浏览代码

feat: 完善用户管理模块,优化菜单权限

叶静 2 周之前
父节点
当前提交
24c6d0eb1e

+ 6 - 2
src/app/admin/views/auth/access/edit.vue

@@ -27,7 +27,7 @@
         <el-form-item label="类型" prop="type">
           <el-radio-group v-model="form.model.type" @change="changeType">
             <el-radio label="1">菜单</el-radio>
-            <el-radio label="3">页面</el-radio>
+            <el-radio label="2">页面</el-radio>
             <el-radio label="4">动作</el-radio>
             <!-- <el-radio label="3">页面</el-radio> -->
           </el-radio-group>
@@ -38,6 +38,9 @@
         <el-form-item label="标识" prop="name">
           <el-input v-model="form.model.name" placeholder="请输入唯一标识"></el-input>
         </el-form-item>
+        <el-form-item label="短路径" prop="url">
+          <el-input v-model="form.model.url" placeholder="请输入短路径"></el-input>
+        </el-form-item>
         <el-form-item v-if="form.model.type != 'api'" label="图标" prop="icon">
           <sa-uploader v-model="form.model.icon" mode="input" fileType="image"></sa-uploader>
         </el-form-item>
@@ -163,6 +166,7 @@
       title: '',
       type: '1',
       name: '',
+      eName: '',
       icon: '',
       url: '',
       status: 'normal',
@@ -251,7 +255,7 @@
       id: isAdd ? 0 : props.modal.params.id || 0, // 新增时ID为0,编辑时传递原ID
       parentId: formData.parent_id || '',
       name: formData.title || '', // 使用title作为name
-      eName: formData.title || '', // 使用title作为eName
+      eName: formData.eName || '', // 使用title作为eName
       logo: formData.icon || '',
       composingKey: formData.name || '', // 使用name作为composingKey
       type: formData.type, // 转换type格式

+ 4 - 4
src/app/shop/admin/goods/goods.service.js

@@ -31,7 +31,7 @@ const route = {
 
 const api = {
   goods: {
-    ...CRUD('product'),
+    ...CRUD('mall/product'),
     // 批量上下架
     batchShowStatus: (data) =>
       request({
@@ -42,15 +42,15 @@ const api = {
     // 获取商品状态数量
     getStatusNum: () =>
       request({
-        url: '/product/status/num',
+        url: 'mall/product/status/num',
         method: 'GET',
       }),
   },
   rule: {
-    ...CRUD('product/rule'),
+    ...CRUD('mall/product/rule'),
   },
   category: {
-    ...CRUD('category'),
+    ...CRUD('mall/category'),
     select: (params) => SELECT('shop/admin/category_tag', params),
   },
 };

+ 1 - 3
src/app/shop/admin/goods/goods/select.vue

@@ -125,7 +125,6 @@
     ids: props.modal.params.ids || [],
     selectedGoods: [], // 存储选中的商品完整数据
   });
-
   async function getData(page, searchParams = {}) {
     if (page) pageData.page = page;
     loading.value = true;
@@ -134,9 +133,9 @@
       const params = {
         page: pageData.page,
         size: pageData.size,
+        isShow: 1,
         ...searchParams,
       };
-
       const { code, data } = await api.goods.list(params);
       if (code == '200') {
         table.list = data.list || [];
@@ -158,7 +157,6 @@
 
   // 设置默认选中
   function setDefaultSelected() {
-    console.log(table);
     // 确保 table.list 存在且为数组
     if (!table.list || !Array.isArray(table.list)) {
       console.warn('table.list is not available or not an array:', table.list);

+ 3 - 3
src/app/shop/admin/marketing/marketing.service.js

@@ -24,16 +24,16 @@ const route = {
 const api = {
   // 拼团相关 API
   group: {
-    ...CRUD('flash/activity'),
+    ...CRUD('mall/flash/activity'),
     getActivityProductIds: (activityId) =>
       request({
-        url: `/combination/getActivityProductIds`,
+        url: `mall/combination/getActivityProductIds`,
         method: 'POST',
         params: { activityId },
       }),
     addActivityProduct: (activityId, data) =>
       request({
-        url: `/combination/addActivityProduct`,
+        url: `mall/combination/addActivityProduct`,
         method: 'POST',
         params: { activityId },
         data,

+ 1 - 1
src/app/shop/admin/order/order.service.js

@@ -48,7 +48,7 @@ const route = {
 
 const api = {
   order: {
-    ...CRUD('order'),
+    ...CRUD('mall/order'),
     // 发货
     dispatch: (data) =>
       request({

+ 0 - 1
src/app/shop/admin/order/order/index.vue

@@ -397,7 +397,6 @@
     if (code == '200') {
       table.data = data.list;
       pageData.page = data.pageNum;
-      pageData.size = data.pageSize;
       pageData.total = data.total;
     }
     loading.value = false;

+ 191 - 191
src/app/shop/admin/user/list/detail.vue

@@ -1,10 +1,10 @@
 <template>
-  <el-container class="user-detail">
+  <el-container class="user-detail" v-loading="loading">
     <el-main>
       <!-- 操作按钮 -->
       <div class="action-buttons sa-m-b-2">
-        <el-button @click="onSendSms">发短信</el-button>
-        <el-button @click="onAppPush">APP推送</el-button>
+        <!-- <el-button @click="onSendSms">发短信</el-button> -->
+        <!-- <el-button @click="onAppPush">APP推送</el-button> -->
         <el-button type="primary" @click="onEdit">编辑资料</el-button>
       </div>
 
@@ -24,7 +24,7 @@
               <template #default="{ row }">
                 <div class="avatar-section">
                   <el-image
-                    :src="state.userDetail.avatar"
+                    :src="state.userDetail.headPic || '/static/images/shop/avatar.png'"
                     style="width: 60px; height: 60px; border-radius: 8px"
                     fit="cover"
                   />
@@ -75,31 +75,23 @@
                 <!-- 空白,因为头像已经在上面显示 -->
               </template>
             </el-table-column>
-            <el-table-column label="手机号" align="center">
-              <template #default="{ row }">
-                {{ row.field1 || '--' }}
-              </template>
-            </el-table-column>
-            <el-table-column label="注册时间" align="center">
-              <template #default="{ row }">
-                {{ row.field2 || '--' }}
-              </template>
-            </el-table-column>
-            <el-table-column label="注册时间" align="center">
+            <el-table-column label="收款账户" align="center">
               <template #default="{ row }">
                 {{ row.field3 || '--' }}
               </template>
             </el-table-column>
-            <el-table-column label="收款账户" align="center">
+            <el-table-column label="手机号" align="center">
               <template #default="{ row }">
-                {{ row.field4 || '--' }}
+                {{ row.field1 || '--' }}
               </template>
             </el-table-column>
-            <el-table-column label="账户名称" align="center">
+            <el-table-column label="注册时间" align="center">
               <template #default="{ row }">
-                {{ row.field5 || '--' }}
+                {{ row.field2 || '--' }}
               </template>
             </el-table-column>
+            <el-table-column label="" align="center" />
+            <el-table-column label="" align="center" />
           </el-table>
         </div>
       </div>
@@ -109,44 +101,44 @@
         <div class="sa-title sa-m-b-10 font-bold">统计信息</div>
         <el-table class="sa-table" :data="state.statsTableData" stripe border>
           <el-table-column label="邀请好友数" align="center">
-            <template #default="{ row }">{{ row.inviteFriends || '--' }}</template>
+            <template #default="{ row }">{{ row.inviteNum }}</template>
           </el-table-column>
           <el-table-column label="成功订单数" align="center">
-            <template #default="{ row }">{{ row.successOrders || '--' }}</template>
+            <template #default="{ row }">{{ row.successGroupNum }}</template>
           </el-table-column>
           <el-table-column label="可参团次数" align="center">
-            <template #default="{ row }">{{ row.teamCount || '--' }}</template>
+            <template #default="{ row }">{{ row.groupNum }}</template>
           </el-table-column>
           <el-table-column label="全部佣金总金额" width="150" align="center">
-            <template #default="{ row }">৳ {{ row.totalCommission || '--' }}</template>
+            <template #default="{ row }">৳ {{ row.totalEarnings }}</template>
           </el-table-column>
           <el-table-column label="已结算佣金余额" width="150" align="center">
-            <template #default="{ row }">৳ {{ row.settledCommission || '--' }}</template>
+            <template #default="{ row }">৳ {{ row.settledEarnings }}</template>
           </el-table-column>
           <el-table-column label="充值总金额" min-width="120" align="center">
-            <template #default="{ row }">৳ {{ row.rechargeTotal || '--' }}</template>
+            <template #default="{ row }">৳ {{ row.rechargeAmount }}</template>
           </el-table-column>
         </el-table>
 
         <!-- 第二行统计数据 -->
         <el-table class="sa-table mt-2px" :data="state.statsTableData2" stripe border>
           <el-table-column label="团队人数" align="center">
-            <template #default="{ row }">{{ row.teamMembers || '--' }}</template>
+            <template #default="{ row }">{{ row.teamNum }}</template>
           </el-table-column>
           <el-table-column label="账户余额" align="center">
-            <template #default="{ row }">৳ {{ row.accountBalance || '--' }}</template>
+            <template #default="{ row }">৳ {{ row.walletBalance }}</template>
           </el-table-column>
           <el-table-column label="佣金账户余额" align="center">
-            <template #default="{ row }">৳ {{ row.commissionBalance || '--' }}</template>
+            <template #default="{ row }">৳ {{ row.earningsBalance }}</template>
           </el-table-column>
           <el-table-column label="近7天佣金余额" width="150" align="center">
-            <template #default="{ row }">৳ {{ row.recentCommission || '--' }}</template>
+            <template #default="{ row }">৳ {{ row.l7DEarnings }}</template>
           </el-table-column>
           <el-table-column label="待结算佣金余额" width="150" align="center">
-            <template #default="{ row }">৳ {{ row.pendingCommission || '--' }}</template>
+            <template #default="{ row }">৳ {{ row.pendingEarnings }}</template>
           </el-table-column>
           <el-table-column label="提现总金额" min-width="120" align="center">
-            <template #default="{ row }">৳ {{ row.withdrawTotal || '--' }}</template>
+            <template #default="{ row }">৳ {{ row.withdrawAmount }}</template>
           </el-table-column>
         </el-table>
       </div>
@@ -192,11 +184,15 @@
           <el-tab-pane label="下线用户" name="subordinates">
             <el-skeleton v-if="state.loading.subordinates" :rows="5" animated />
             <el-table v-else class="sa-table" :data="state.subordinates" stripe border>
-              <el-table-column label="昵称" prop="nickname" align="center" />
-              <el-table-column label="手机号" prop="mobile" align="center" />
-              <el-table-column label="注册时间" prop="created_at" align="center" />
+              <el-table-column label="昵称" prop="nickname" align="center">
+                <template #default="{ row }">
+                  {{ row.nickname || '--' }}
+                </template>
+              </el-table-column>
+              <el-table-column label="手机号" prop="phoneNo" align="center" />
+              <el-table-column label="注册时间" prop="createTime" align="center" />
               <el-table-column label="等级" width="100" align="center">
-                <template #default="{ row }"> V{{ row.level || '1' }} </template>
+                <template #default="{ row }"> V{{ row.vipLevel || '1' }} </template>
               </el-table-column>
               <el-table-column label="操作" width="80" align="center">
                 <template #default="{ row }">
@@ -278,16 +274,25 @@
             </el-table>
           </el-tab-pane>
         </el-tabs>
+
+        <!-- 统一分页组件 -->
+        <div class="sa-m-t-20 flex justify-end">
+          <sa-pagination
+            layout="total, prev, pager, next"
+            :pageData="pageData"
+            @updateFn="(page) => loadTabData(state.activeTab, page)"
+          />
+        </div>
       </div>
     </el-main>
   </el-container>
 </template>
 
 <script setup>
-  import { reactive, onMounted } from 'vue';
+  import { ref, reactive, onMounted } from 'vue';
   import { ElMessage } from 'element-plus';
-  import { useModal } from '@/sheep/hooks';
-  import { userDetailMock } from '@/sheep/mock/user';
+  import { useModal, usePagination } from '@/sheep/hooks';
+  import { api } from '../user.service';
   import userEdit from './edit.vue';
   import OrderDetail from '../../order/order/detail.vue';
   import userDetail from './detail.vue';
@@ -302,6 +307,11 @@
     },
   });
 
+  // 分页数据
+  const { pageData } = usePagination();
+  // 设置默认每页5条
+  pageData.size = 5;
+
   const state = reactive({
     userDetail: {},
     basicInfoTableData: [],
@@ -322,160 +332,144 @@
       withdraws: false,
       addresses: false,
     },
-    loadedTabs: new Set(), // 记录已加载的Tab
   });
 
   // 初始化基本信息表格数据
   const initBasicInfoTableData = () => {
     state.basicInfoTableData = [
       {
-        field1: state.userDetail.user_id, // 用户ID
-        field2: state.userDetail.status_text, // 状态
-        field3: state.userDetail.nickname, // 昵称
-        field4: state.userDetail.bank_name, // 收款银行
-        field5: state.userDetail.account_name, // 账户名称
+        field1: state.userDetail.userNo || '--', // 用户ID
+        field2: getStatusText(state.userDetail.status) || '--', // 状态
+        field3: state.userDetail.nickname || '--', // 昵称
+        field4: state.userDetail.bank || '--', // 收款银行
+        field5: state.userDetail.name || '--', // 账户名称
       },
       {
-        field1: state.userDetail.mobile, // 手机号
-        field2: state.userDetail.created_at, // 注册时间
-        field3: state.userDetail.created_at, // 注册时间
-        field4: state.userDetail.account_number, // 收款账户
-        field5: state.userDetail.account_name, // 账户名称
+        field1: state.userDetail.phoneNo || '--', // 手机号
+        field2: state.userDetail.createTime || '--', // 注册时间
+        field3: state.userDetail.bankAccount || '--', // 收款账户
       },
     ];
-
     // 初始化统计信息表格数据
-    const stats = userDetailMock.statsData;
     state.statsTableData = [
       {
-        inviteFriends: stats.invite_friends,
-        successOrders: stats.success_orders,
-        teamCount: stats.team_count,
-        totalCommission: stats.total_commission,
-        settledCommission: stats.settled_commission,
-        rechargeTotal: stats.recharge_total,
+        inviteNum: state.userDetail.inviteNum || 0, // 邀请好友数
+        successGroupNum: state.userDetail.successGroupNum || 0, // 成团数
+        groupNum: state.userDetail.groupNum || 0, // 可参团数
+        totalEarnings: state.userDetail.totalEarnings || 0, // 总收益
+        settledEarnings: state.userDetail.settledEarnings || 0, // 已结算收益
+        rechargeAmount: state.userDetail.rechargeAmount || 0, // 充值
       },
     ];
 
     state.statsTableData2 = [
       {
-        teamMembers: stats.team_members,
-        accountBalance: stats.account_balance,
-        commissionBalance: stats.commission_balance,
-        recentCommission: stats.recent_commission,
-        pendingCommission: stats.pending_commission,
-        withdrawTotal: stats.withdraw_total,
+        teamNum: state.userDetail.teamNum || 0, // 团队人数
+        walletBalance: state.userDetail.walletBalance || 0, // 钱包余额
+        earningsBalance: state.userDetail.earningsBalance || 0, // 收益余额
+        l7DEarnings: state.userDetail.l7DEarnings || 0, // 近7天收益
+        pendingEarnings: state.userDetail.pendingEarnings || 0, // 待结算金额
+        withdrawAmount: state.userDetail.withdrawAmount || 0, // 提现总金额
       },
     ];
   };
 
+  // 获取状态文本
+  function getStatusText(status) {
+    switch (status) {
+      case 1:
+        return '正常';
+      case 2:
+        return '禁止提现';
+      case 3:
+        return '禁止登录';
+      case 4:
+        return '禁止下单';
+      default:
+        return '-';
+    }
+  }
+
   // 获取用户详情
+  const loading = ref(false);
   const getUserDetail = async () => {
     try {
-      // 使用mock数据
-      state.userDetail = userDetailMock.userDetail;
-      initBasicInfoTableData();
+      loading.value = true;
+      const { code, data } = await api.list.userDetail(props.modal.params.id);
+      if (code == '200') {
+        state.userDetail = data;
+        initBasicInfoTableData();
+      }
     } catch (error) {
       console.error('获取用户详情失败:', error);
     }
+    loading.value = false;
   };
 
-  // 获取订单记录
-  const getUserOrders = async () => {
+  // 统一数据获取函数
+  const fetchTabData = async (tabType, page = 1) => {
     try {
-      state.orders = userDetailMock.orders;
-    } catch (error) {
-      console.error('获取订单记录失败:', error);
-    }
-  };
+      const requestData = {
+        page,
+        size: pageData.size,
+      };
 
-  // 获取下线用户
-  const getUserSubordinates = async () => {
-    try {
-      state.subordinates = userDetailMock.subordinates;
-    } catch (error) {
-      console.error('获取下线用户失败:', error);
-    }
-  };
+      let response;
+      let stateKey;
 
-  // 获取佣金记录
-  const getUserCommissions = async () => {
-    try {
-      state.commissions = userDetailMock.commissions;
-    } catch (error) {
-      console.error('获取佣金记录失败:', error);
-    }
-  };
-
-  // 获取充值记录
-  const getUserRecharges = async () => {
-    try {
-      state.recharges = userDetailMock.recharges;
-    } catch (error) {
-      console.error('获取充值记录失败:', error);
-    }
-  };
-
-  // 获取提现记录
-  const getUserWithdraws = async () => {
-    try {
-      state.withdraws = userDetailMock.withdraws;
-    } catch (error) {
-      console.error('获取提现记录失败:', error);
-    }
-  };
+      switch (tabType) {
+        case 'orders':
+          // TODO: 实现订单记录获取
+          state.orders = [];
+          return;
+        case 'subordinates':
+          response = await api.list.myUsers({ ...requestData, type: 1 });
+          stateKey = 'subordinates';
+          break;
+        case 'commissions':
+          // TODO: 实现佣金记录获取
+          state.commissions = [];
+          return;
+        case 'recharges':
+          // TODO: 实现充值记录获取
+          state.recharges = [];
+          return;
+        case 'withdraws':
+          // TODO: 实现提现记录获取
+          state.withdraws = [];
+          return;
+        case 'addresses':
+          // TODO: 实现收货地址获取
+          state.addresses = [];
+          return;
+        default:
+          return;
+      }
 
-  // 获取收货地址
-  const getUserAddresses = async () => {
-    try {
-      state.addresses = userDetailMock.addresses;
+      if (response && response.code == '200') {
+        state[stateKey] = response.data.list || [];
+        // 更新分页信息
+        pageData.page = response.data.pageNum || page;
+        pageData.total = response.data.total || 0;
+      }
     } catch (error) {
-      console.error('获取收货地址失败:', error);
+      console.error(`获取${tabType}数据失败:`, error);
     }
   };
 
   // Tab切换处理
   const handleTabChange = (tabName) => {
     state.activeTab = tabName;
-
-    // 如果该Tab还没有加载过数据,则进行懒加载
-    if (!state.loadedTabs.has(tabName)) {
-      loadTabData(tabName);
-    }
+    // 每次切换都重新加载数据
+    loadTabData(tabName);
   };
 
   // 加载Tab数据
-  const loadTabData = async (tabName) => {
-    if (state.loadedTabs.has(tabName)) return;
-
+  const loadTabData = async (tabName, page = 1) => {
     state.loading[tabName] = true;
 
     try {
-      // 模拟API请求延迟
-      await new Promise((resolve) => setTimeout(resolve, 1000));
-
-      switch (tabName) {
-        case 'orders':
-          await getUserOrders();
-          break;
-        case 'subordinates':
-          await getUserSubordinates();
-          break;
-        case 'commissions':
-          await getUserCommissions();
-          break;
-        case 'recharges':
-          await getUserRecharges();
-          break;
-        case 'withdraws':
-          await getUserWithdraws();
-          break;
-        case 'addresses':
-          await getUserAddresses();
-          break;
-      }
-
-      state.loadedTabs.add(tabName);
+      await fetchTabData(tabName, page);
     } catch (error) {
       console.error(`加载${tabName}数据失败:`, error);
     } finally {
@@ -512,65 +506,71 @@
     );
   };
 
-  // 查看订单详情
-  const viewOrder = (row) => {
-    useModal(OrderDetail, {
-      title: '订单详情',
-      width: '90%',
-      id: row.order_sn,
-    });
-  };
-
-  // 查看下线用户详情
-  const viewSubordinateUser = (row) => {
-    useModal(
-      userDetail,
-      {
+  // 统一模态框打开函数
+  const openDetailModal = (type, row) => {
+    const modalConfigs = {
+      order: {
+        component: OrderDetail,
+        title: '订单详情',
+        width: '90%',
+        id: row.order_sn,
+      },
+      subordinateUser: {
+        component: userDetail,
         title: '用户详情',
-        type: 'view',
         width: '1200px',
+        type: 'view',
         id: row.id,
+        hasConfirm: true,
       },
-      {
-        confirm: () => {
-          getData();
-        },
+      commission: {
+        component: CommissionEdit,
+        title: '佣金详情',
+        width: '80%',
+        id: row.commission_id,
       },
-    );
-  };
+      recharge: {
+        component: RechargeEdit,
+        title: '充值详情',
+        width: '80%',
+        id: row.order_sn,
+      },
+      withdraw: {
+        component: WithdrawEdit,
+        title: '提现详情',
+        width: '80%',
+        id: row.order_sn,
+      },
+    };
 
-  // 查看佣金记录详情
-  const viewCommission = (row) => {
-    useModal(CommissionEdit, {
-      title: '佣金详情',
-      width: '80%',
-      id: row.commission_id,
-    });
-  };
+    const config = modalConfigs[type];
+    if (!config) return;
 
-  // 查看充值详情
-  const viewRecharge = (row) => {
-    useModal(RechargeEdit, {
-      title: '充值详情',
-      width: '80%',
-      id: row.order_sn,
-    });
-  };
+    const { component, hasConfirm, ...modalOptions } = config;
 
-  // 查看提现详情
-  const viewWithdraw = (row) => {
-    useModal(WithdrawEdit, {
-      title: '提现详情',
-      width: '80%',
-      id: row.order_sn,
-    });
+    if (hasConfirm) {
+      useModal(component, modalOptions, {
+        confirm: () => getData(),
+      });
+    } else {
+      useModal(component, modalOptions);
+    }
   };
 
+  // 各类详情查看函数(保持向后兼容)
+  const viewOrder = (row) => openDetailModal('order', row);
+  const viewSubordinateUser = (row) => openDetailModal('subordinateUser', row);
+  const viewCommission = (row) => openDetailModal('commission', row);
+  const viewRecharge = (row) => openDetailModal('recharge', row);
+  const viewWithdraw = (row) => openDetailModal('withdraw', row);
+
   onMounted(() => {
+    // 先初始化基本信息表格数据(防止模板报错)
+    initBasicInfoTableData();
     // 初始化用户详情和基本信息
     getUserDetail();
-    // 默认加载订单记录
-    loadTabData('orders');
+    // 默认加载当前激活的tab数据
+    loadTabData(state.activeTab);
   });
 </script>
 

+ 50 - 68
src/app/shop/admin/user/list/edit.vue

@@ -1,18 +1,24 @@
 <template>
   <el-container>
     <el-main>
-      <el-form :model="form.model" :rules="form.rules" ref="formRef" label-width="100px">
+      <el-form
+        :model="form.model"
+        :rules="form.rules"
+        ref="formRef"
+        label-width="100px"
+        v-loading="loading"
+        element-loading-text="加载中..."
+      >
+        <el-form-item label="用户手机" prop="phoneNo">
+          <el-input v-model="form.model.phoneNo" placeholder="请输入用户手机"></el-input>
+        </el-form-item>
         <el-form-item label="用户昵称" prop="nickname">
           <el-input v-model="form.model.nickname" placeholder="请输入用户昵称"></el-input>
         </el-form-item>
 
-        <el-form-item label="用户手机" prop="mobile">
-          <el-input v-model="form.model.mobile" placeholder="请输入用户手机"></el-input>
-        </el-form-item>
-
-        <el-form-item label="用户头像" prop="avatar">
+        <el-form-item label="用户头像" prop="headPic">
           <sa-upload-image
-            v-model="form.model.avatar"
+            v-model="form.model.headPic"
             :max-count="1"
             :accept="['jpg', 'png']"
             :max-size="5"
@@ -22,39 +28,30 @@
           />
         </el-form-item>
 
-        <el-form-item label="收款银行" prop="bank_name">
-          <el-input v-model="form.model.bank_name" placeholder="请输入收款银行"></el-input>
-        </el-form-item>
-
-        <el-form-item label="账户名称" prop="account_name">
-          <el-input v-model="form.model.account_name" placeholder="请输入账户名称"></el-input>
+        <el-form-item label="收款银行" prop="bank">
+          <el-input v-model="form.model.bank" placeholder="请输入收款银行"></el-input>
         </el-form-item>
 
-        <el-form-item label="收款账户" prop="account_number">
-          <el-input v-model="form.model.account_number" placeholder="请输入收款账户"></el-input>
+        <el-form-item label="账户名称" prop="name">
+          <el-input v-model="form.model.name" placeholder="请输入账户名称"></el-input>
         </el-form-item>
 
-        <el-form-item label="登录密码" prop="password">
-          <el-input
-            v-model="form.model.password"
-            type="password"
-            placeholder="请输入登录密码"
-            show-password
-          ></el-input>
+        <el-form-item label="收款账户" prop="bankAccount">
+          <el-input v-model="form.model.bankAccount" placeholder="请输入收款账户"></el-input>
         </el-form-item>
 
-        <el-form-item label="权限" prop="permissions">
-          <el-checkbox-group v-model="form.model.permissions">
-            <el-checkbox label="normal">正常</el-checkbox>
-            <el-checkbox label="withdraw_disabled">禁止提现</el-checkbox>
-            <el-checkbox label="login_disabled">禁止登录</el-checkbox>
-            <el-checkbox label="order_disabled">禁止下单</el-checkbox>
-          </el-checkbox-group>
+        <el-form-item label="状态" prop="status">
+          <el-radio-group v-model="form.model.status">
+            <el-radio :label="1">正常</el-radio>
+            <el-radio :label="2">禁止提现</el-radio>
+            <el-radio :label="3">禁止登录</el-radio>
+            <el-radio :label="4">禁止下单</el-radio>
+          </el-radio-group>
         </el-form-item>
 
-        <el-form-item label="备注" prop="remark">
+        <el-form-item label="备注" prop="memo">
           <el-input
-            v-model="form.model.remark"
+            v-model="form.model.memo"
             type="textarea"
             :rows="4"
             maxlength="100"
@@ -74,6 +71,7 @@
   import { cloneDeep } from 'lodash';
   import { onMounted, reactive, ref, unref } from 'vue';
   import { api } from '../user.service';
+  import { ElMessage } from 'element-plus';
   const emit = defineEmits(['modalCallBack']);
   const props = defineProps({
     modal: {
@@ -85,46 +83,35 @@
   const form = reactive({
     model: {
       nickname: '',
-      mobile: '',
-      avatar: [],
-      bank_name: '',
-      account_name: '',
-      account_number: '',
-      password: '',
-      permissions: ['normal'],
-      remark: '',
+      phoneNo: '',
+      headPic: [],
+      bank: '',
+      name: '',
+      bankAccount: '',
+      status: 1,
+      memo: '',
     },
     rules: {
-      nickname: [{ required: true, message: '请填写用户昵称', trigger: 'blur' }],
-      mobile: [{ required: true, message: '请填写手机号', trigger: 'blur' }],
-      avatar: [{ required: true, message: '请上传用户头像', trigger: 'change' }],
-      password: [
-        { required: true, message: '请填写登录密码', trigger: 'blur' },
-        { min: 6, max: 20, message: '密码长度为6-20位', trigger: 'blur' },
-      ],
-      permissions: [{ required: true, message: '请选择用户权限', trigger: 'change' }],
-      remark: [{ max: 100, message: '备注不能超过100字', trigger: 'blur' }],
+      // nickname: [{ required: true, message: '请填写用户昵称', trigger: 'blur' }],
+      phoneNo: [{ required: true, message: '请填写手机号', trigger: 'blur' }],
+      // headPic: [{ required: true, message: '请上传用户头像', trigger: 'change' }],
+      status: [{ required: true, message: '请选择用户状态', trigger: 'change' }],
+      memo: [{ max: 100, message: '备注不能超过100字', trigger: 'blur' }],
     },
   });
   const loading = ref(false);
   // 获取详情
   async function getDetail(id) {
     loading.value = true;
-    const { code, data } = await api.list.detail(id);
+    const { code, data } = await api.list.userDetail(id);
     if (code == 200) {
       // 处理头像数据
-      if (data.avatar) {
-        form.model.avatar = Array.isArray(data.avatar) ? data.avatar : [data.avatar];
-      }
-      // 处理权限数据
-      if (data.permissions) {
-        form.model.permissions = Array.isArray(data.permissions)
-          ? data.permissions
-          : [data.permissions];
+      if (data.headPic) {
+        form.model.headPic = Array.isArray(data.headPic) ? data.headPic : [data.headPic];
       }
       // 其他字段直接赋值
       Object.keys(form.model).forEach((key) => {
-        if (key !== 'avatar' && key !== 'permissions' && data[key] !== undefined) {
+        if (key !== 'headPic' && data[key] !== undefined) {
           form.model[key] = data[key];
         }
       });
@@ -139,23 +126,18 @@
       let submitForm = cloneDeep(form.model);
 
       // 处理头像数据 - 只提交第一张图片的URL
-      if (submitForm.avatar && submitForm.avatar.length > 0) {
-        submitForm.avatar = submitForm.avatar[0];
+      if (submitForm.headPic && submitForm.headPic.length > 0) {
+        submitForm.headPic = submitForm.headPic[0];
       } else {
-        submitForm.avatar = '';
-      }
-
-      // 处理权限数据 - 转换为字符串或保持数组格式
-      if (submitForm.permissions && submitForm.permissions.length > 0) {
-        // 根据后端需要的格式调整,这里保持数组格式
-        submitForm.permissions = submitForm.permissions;
+        submitForm.headPic = '';
       }
 
       const { code } =
         props.modal.params.type == 'add'
           ? await api.list.add(submitForm)
-          : await api.list.edit(props.modal.params.id, submitForm);
+          : await api.list.edit({ id: props.modal.params.id, ...submitForm });
       if (code == '200') {
+        ElMessage.success('保存成功');
         emit('modalCallBack', { event: 'confirm' });
       }
     });

+ 83 - 52
src/app/shop/admin/user/list/index.vue

@@ -26,10 +26,11 @@
         </sa-search-simple>
       </div>
       <el-tabs class="sa-tabs" v-model="activeTab" @tab-change="handleTabChange">
-        <el-tab-pane label="全部" name="all"></el-tab-pane>
-        <el-tab-pane label="正常" name="normal"></el-tab-pane>
-        <el-tab-pane label="禁止提现" name="withdraw_disabled"></el-tab-pane>
-        <el-tab-pane label="禁止登录" name="login_disabled"></el-tab-pane>
+        <el-tab-pane label="全部" :name="0"></el-tab-pane>
+        <el-tab-pane label="正常" :name="1"></el-tab-pane>
+        <el-tab-pane label="禁止提现" :name="2"></el-tab-pane>
+        <el-tab-pane label="禁止登录" :name="3"></el-tab-pane>
+        <el-tab-pane label="禁止下单" :name="4"></el-tab-pane>
       </el-tabs>
       <div class="sa-title sa-flex sa-row-between">
         <div class="label sa-flex">
@@ -64,74 +65,63 @@
           <el-table-column label="用户昵称" min-width="140">
             <template #default="scope">
               <div class="user-info">
-                <el-link type="primary" @click="viewDetail(scope.row)">
-                  {{ scope.row.username || '-' }}
-                </el-link>
+                {{ scope.row.name || '-' }}
               </div>
             </template>
           </el-table-column>
           <el-table-column label="手机号码" min-width="140">
             <template #default="scope">
-              {{ scope.row.mobile || '-' }}
+              {{ scope.row.phoneNo || '-' }}
             </template>
           </el-table-column>
           <el-table-column label="用户等级" min-width="100" align="center">
             <template #default="scope">
-              <el-tag type="info" size="small">{{ scope.row.level || '-' }}</el-tag>
+              <el-tag type="primary" size="small">V{{ scope.row.vipLevel || '-' }}</el-tag>
             </template>
           </el-table-column>
           <el-table-column label="邀请好友数" min-width="120" align="center">
             <template #default="scope">
-              {{ scope.row.invite_count || 0 }}
+              {{ scope.row.inviteNum || 0 }}
             </template>
           </el-table-column>
           <el-table-column label="团队人数" min-width="120" align="center">
             <template #default="scope">
-              {{ scope.row.team_count || 0 }}
+              {{ scope.row.teamNum || 0 }}
             </template>
           </el-table-column>
           <el-table-column label="成功订单" min-width="120" align="center">
             <template #default="scope">
-              {{ scope.row.success_orders || 0 }}
+              {{ scope.row.successGroupNum || 0 }}
             </template>
           </el-table-column>
           <el-table-column label="账户余额" min-width="120" align="center">
             <template #default="scope">
-              <span class="amount">{{ scope.row.balance || '৳0' }}</span>
+              <span class="amount">{{ scope.row.walletBalance || '৳0' }}</span>
             </template>
           </el-table-column>
           <el-table-column label="佣金余额" min-width="120" align="center">
             <template #default="scope">
-              <span class="amount">{{ scope.row.commission_balance || '৳0' }}</span>
+              <span class="amount">{{ scope.row.earningsBalance || '৳0' }}</span>
             </template>
           </el-table-column>
           <el-table-column label="状态" min-width="120" align="center">
             <template #default="scope">
-              <el-tag
-                :type="
-                  scope.row.status === 'normal'
-                    ? 'success'
-                    : scope.row.status === 'withdraw_disabled'
-                      ? 'warning'
-                      : 'danger'
-                "
-                size="small"
-              >
-                {{ scope.row.status_text || '-' }}
+              <el-tag :type="getStatusType(scope.row.status)" size="small">
+                {{ getStatusText(scope.row.status) }}
               </el-tag>
             </template>
           </el-table-column>
           <el-table-column label="备注" min-width="120">
             <template #default="scope">
-              {{ scope.row.remark || '--' }}
+              {{ scope.row.memo || '--' }}
             </template>
           </el-table-column>
           <el-table-column label="注册时间" min-width="160">
             <template #default="scope">
-              {{ scope.row.register_time || '-' }}
+              {{ scope.row.createTime || '-' }}
             </template>
           </el-table-column>
-          <el-table-column fixed="right" label="操作" min-width="140">
+          <el-table-column fixed="right" label="操作" min-width="200">
             <template #default="scope">
               <el-button class="is-link" type="primary" @click="viewDetail(scope.row)">
                 详情
@@ -139,6 +129,9 @@
               <el-button class="is-link" type="primary" @click="editRow(scope.row)">
                 编辑
               </el-button>
+              <el-button class="is-link" type="warning" @click="changePassword(scope.row)">
+                改密码
+              </el-button>
             </template>
           </el-table-column>
         </el-table>
@@ -161,22 +154,23 @@
 <script setup>
   import { onMounted, reactive, ref } from 'vue';
   import { api } from '../user.service';
-  import { ElMessageBox } from 'element-plus';
+  import { ElMessageBox, ElMessage } from 'element-plus';
   import { useModal } from '@/sheep/hooks';
   import { usePagination } from '@/sheep/hooks';
   import userEdit from './edit.vue';
   import userDetail from './detail.vue';
+  import userPassword from './password.vue';
   const { pageData } = usePagination();
 
   // 搜索字段配置
   const searchFields = reactive({
-    username: {
+    name: {
       type: 'input',
       label: '账号',
       placeholder: '请输入用户名或昵称',
       width: 200,
     },
-    mobile: {
+    phone: {
       type: 'input',
       label: '手机号',
       placeholder: '请输入手机号',
@@ -186,13 +180,13 @@
 
   // 默认搜索值
   const defaultSearchValues = reactive({
-    username: '',
-    mobile: '',
+    name: '',
+    phone: '',
     dateRange: [],
   });
 
   // 当前激活的 Tab
-  const activeTab = ref('all');
+  const activeTab = ref(0);
   // 列表
   const table = reactive({
     data: [],
@@ -207,37 +201,58 @@
     if (page) pageData.page = page;
     loading.value = true;
 
-    // 构建查询参数
-    const params = {
+    // 构建请求参数
+    const requestData = {
       page: pageData.page,
       size: pageData.size,
-      order: table.order,
-      sort: table.sort,
-      status: activeTab.value === 'all' ? '' : activeTab.value,
+      type: 0,
       ...searchParams,
     };
-
-    // 处理日期范围
+    // 处理时间范围搜索
     if (searchParams.dateRange && searchParams.dateRange.length === 2) {
-      params.start_time = searchParams.dateRange[0];
-      params.end_time = searchParams.dateRange[1];
+      requestData.startTime = searchParams.dateRange[0];
+      requestData.endTime = searchParams.dateRange[1];
+      delete requestData.dateRange;
     }
 
-    const { code, data } = await api.list.list(params);
-    console.log('API 响应:', error, data);
-    if (code == 200) {
-      table.data = data.data;
-      pageData.page = data.current_page;
-      pageData.size = data.per_page;
+    const { code, data } = await api.list.list(requestData, false);
+
+    if (code == '200') {
+      table.data = data.list;
+      pageData.page = data.pageNum;
+      pageData.size = data.pageSize;
       pageData.total = data.total;
     }
     loading.value = false;
   }
 
   // Tab 切换处理
-  function handleTabChange(tabName) {
-    activeTab.value = tabName;
-    getData(1);
+  function handleTabChange(status) {
+    activeTab.value = status;
+    // 如果是全部(0),不传status参数;否则传递对应的状态值
+    const searchParams = status === 0 ? {} : { status };
+    getData(1, searchParams);
+  }
+
+  // 获取状态标签类型
+  function getStatusType(status) {
+    return status === 1 ? 'success' : status === 2 ? 'warning' : 'danger';
+  }
+
+  // 获取状态文本
+  function getStatusText(status) {
+    switch (status) {
+      case 1:
+        return '正常';
+      case 2:
+        return '禁止提现';
+      case 3:
+        return '禁止登录';
+      case 4:
+        return '禁止下单';
+      default:
+        return '-';
+    }
   }
   // table 字段排序
   function fieldFilter({ prop, order }) {
@@ -303,6 +318,22 @@
       },
     );
   }
+
+  function changePassword(row) {
+    useModal(
+      userPassword,
+      {
+        title: '修改密码',
+        width: '500px',
+        id: row.id,
+      },
+      {
+        confirm: () => {
+          // 密码修改成功后不需要刷新列表
+        },
+      },
+    );
+  }
   // 删除api 单独批量可以直接调用
   async function deleteApi(id) {
     await api.list.delete(id);

+ 104 - 0
src/app/shop/admin/user/list/password.vue

@@ -0,0 +1,104 @@
+<template>
+  <el-container>
+    <el-main>
+      <el-form
+        ref="formRef"
+        :model="form.model"
+        :rules="form.rules"
+        label-width="100px"
+        v-loading="loading"
+        element-loading-text="提交中..."
+      >
+        <el-form-item label="新密码" prop="password">
+          <el-input
+            v-model="form.model.password"
+            type="password"
+            placeholder="请输入新密码"
+            show-password
+          ></el-input>
+        </el-form-item>
+
+        <el-form-item label="确认密码" prop="confirmPassword">
+          <el-input
+            v-model="form.model.confirmPassword"
+            type="password"
+            placeholder="请再次输入新密码"
+            show-password
+          ></el-input>
+        </el-form-item>
+      </el-form>
+    </el-main>
+    <el-footer class="sa-footer--submit">
+      <el-button type="primary" @click="onConfirm">更新</el-button>
+    </el-footer>
+  </el-container>
+</template>
+
+<script setup>
+  import { reactive, ref } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import { api } from '../user.service';
+  const emit = defineEmits(['modalCallBack']);
+  const props = defineProps({
+    modal: {
+      type: Object,
+      default: () => ({}),
+    },
+  });
+
+  const formRef = ref();
+  const loading = ref(false);
+
+  const form = reactive({
+    model: {
+      password: '',
+      confirmPassword: '',
+    },
+    rules: {
+      password: [
+        { required: true, message: '请输入新密码', trigger: 'blur' },
+        { min: 6, max: 20, message: '密码长度为6-20位', trigger: 'blur' },
+      ],
+      confirmPassword: [
+        { required: true, message: '请再次输入新密码', trigger: 'blur' },
+        {
+          validator: (rule, value, callback) => {
+            if (value !== form.model.password) {
+              callback(new Error('两次输入的密码不一致'));
+            } else {
+              callback();
+            }
+          },
+          trigger: 'blur',
+        },
+      ],
+    },
+  });
+
+  // 提交表单
+  const onConfirm = async () => {
+    try {
+      await formRef.value.validate();
+
+      loading.value = true;
+      const { code } = await api.list.edit({
+        id: props.modal.params.id,
+        pwd: form.model.password,
+      });
+
+      if (code == '200') {
+        ElMessage.success('密码修改成功');
+        emit('modalCallBack', { event: 'confirm' });
+      }
+    } catch (error) {
+      console.error('修改密码失败:', error);
+      return false;
+    } finally {
+      loading.value = false;
+    }
+  };
+
+  defineExpose({
+    onConfirm,
+  });
+</script>

+ 9 - 8
src/app/shop/admin/user/user.service.js

@@ -39,22 +39,23 @@ const route = {
 };
 
 const api = {
+  // 分离分页参数和其他参数
   // 用户列表相关 API
   list: {
-    ...CRUD('shop/admin/user/list'),
-    export: (params) =>
+    ...CRUD('/cif/user'),
+    userDetail: (userId) =>
       request({
-        url: 'shop/admin/user/list/export',
+        url: '/cif/user/detail',
         method: 'GET',
-        params,
+        params: { userId },
       }),
-    statistics: () =>
+    myUsers: (data) =>
       request({
-        url: 'shop/admin/user/list/statistics',
-        method: 'GET',
+        url: '/cif/user/myUsers',
+        method: 'POST',
+        data,
       }),
   },
-
   // 用户详情相关 API
   getUserDetail: (id) =>
     request({

+ 16 - 6
src/sheep/request/crud.js

@@ -1,16 +1,25 @@
 import { request } from './index';
 
 // 查看列表
-export const LIST = (url, data) => {
+export const LIST = (url, data, pageInParams = true) => {
   // 分离分页参数和其他参数
   const { page, size, ...otherParams } = data;
 
-  return request({
+  const requestConfig = {
     url: url + `/list`,
     method: 'POST',
-    params: { page, size }, // 分页参数放在params中
-    data: otherParams, // 其他参数放在data中
-  });
+  };
+
+  if (pageInParams) {
+    // 分页参数放在params中(默认行为)
+    requestConfig.params = { page, size };
+    requestConfig.data = otherParams;
+  } else {
+    // 分页参数放在data中
+    requestConfig.data = { page, size, ...otherParams };
+  }
+
+  return request(requestConfig);
 };
 
 // 查看详情
@@ -87,7 +96,8 @@ export const RESTORE = (url, id) =>
 // 通用增删改查
 export const CRUD = (url, methods = ['list', 'detail', 'add', 'edit', 'delete']) => {
   const apis = {};
-  if (methods.includes('list')) apis.list = (params) => LIST(url, params);
+  if (methods.includes('list'))
+    apis.list = (params, pageInParams = true) => LIST(url, params, pageInParams);
   if (methods.includes('detail')) apis.detail = (id) => DETAIL(url, id);
   if (methods.includes('add')) apis.add = (data) => ADD(url, data);
   if (methods.includes('edit')) apis.edit = (id, data) => EDIT(url, id, data);