Kaynağa Gözat

套餐分配修改

“陈博论” 9 ay önce
ebeveyn
işleme
f11328f434

+ 2 - 4
web/src/api/backend/userCenter/user.ts

@@ -43,12 +43,10 @@ export function removeSubordinate(data: anyObj) {
 }
 
 // 详情
-export function detail(id: number) {
+export function detail(data:anyObj) {
     return createAxios({
         url: '/admin/userCenter.user/detail',
         method: 'get',
-        params: {
-            id: id,
-        },
+        params: data,
     })
 }

+ 443 - 275
web/src/views/backend/userCenter/user/detailDialog.vue

@@ -1,12 +1,6 @@
 <template>
-    <el-dialog
-        v-model="visible"
-        class="ba-operate-dialog user-detail-dialog"
-        width="1200px"
-        :close-on-click-modal="false"
-        :destroy-on-close="true"
-        @close="handleClose"
-    >
+    <el-dialog v-model="visible" class="ba-operate-dialog user-detail-dialog" width="1200px"
+        :close-on-click-modal="false" :destroy-on-close="true" @close="handleClose">
         <template #header>
             <div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
                 详情
@@ -19,28 +13,87 @@
                 <div class="detail-section">
                     <div class="section-title">用户信息</div>
                     <el-descriptions :column="2" border size="small" class="user-info-descriptions">
-                        <el-descriptions-item label="用户名称">
-                            {{ userInfo.name || '-' }}
+                        <el-descriptions-item label="用户ID">
+                            {{ userInfo.id || '-' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="用户名">
+                            {{ userInfo.username || '-' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="昵称">
+                            {{ userInfo.nickname || '-' }}
                         </el-descriptions-item>
                         <el-descriptions-item label="邮箱">
                             {{ userInfo.email || '-' }}
                         </el-descriptions-item>
-                        <el-descriptions-item label="企业">
-                            {{ userInfo.company || '-' }}
+                        <el-descriptions-item label="积分">
+                            {{ userInfo.score || 0 }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="余额">
+                            {{ userInfo.money || '0.00' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="个性签名">
+                            {{ userInfo.motto || '-' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="企业ID">
+                            {{ userInfo.companyId || '-' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="企业名称">
+                            {{ userInfo.companyName || '-' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="邀请码">
+                            {{ userInfo.invitationCode || '-' }}
                         </el-descriptions-item>
                         <el-descriptions-item label="状态">
-                            <el-tag :type="userInfo.status === '正常' ? 'success' : 'danger'" size="small">
-                                {{ userInfo.status || '-' }}
+                            <el-tag :type="userInfo.status === 'enable' ? 'success' : 'danger'" size="small">
+                                {{ userInfo.statusText || '-' }}
                             </el-tag>
                         </el-descriptions-item>
-                        <el-descriptions-item label="获得总佣金金额">
-                            {{ userInfo.totalCommission || '-' }}
+                        <el-descriptions-item label="注册时间">
+                            {{ userInfo.joinTimeStr || '-' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="注册IP">
+                            {{ userInfo.joinIp || '-' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="最后登录时间">
+                            {{ userInfo.lastLoginTimeStr || '-' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="最后登录IP">
+                            {{ userInfo.lastLoginIp || '-' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="登录失败次数">
+                            {{ userInfo.loginFailure || 0 }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="创建时间">
+                            {{ userInfo.createTimeStr || '-' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="更新时间">
+                            {{ userInfo.updateTimeStr || '-' }}
+                        </el-descriptions-item>
+                    </el-descriptions>
+                </div>
+
+                <!-- 佣金信息部分 -->
+                <div class="detail-section">
+                    <div class="section-title">佣金信息</div>
+                    <el-descriptions :column="2" border size="small" class="commission-info-descriptions">
+                        <el-descriptions-item label="总佣金金额">
+                            {{ commissionInfo.totalCommission || '0.00' }}
                         </el-descriptions-item>
                         <el-descriptions-item label="当前佣金金额">
-                            {{ userInfo.currentCommission || '-' }}
+                            {{ commissionInfo.currentCommission || '0.00' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="冻结中佣金">
+                            {{ commissionInfo.frozenCommission || '0.00' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="已提现佣金">
+                            {{ commissionInfo.withdrawnCommission || '0.00' }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="推广链接">
+                            <span class="promotion-link" @click="handleCopyLink(commissionInfo.promotionLink)">{{
+                                commissionInfo.promotionLink || '-' }}</span>
                         </el-descriptions-item>
-                        <el-descriptions-item label="推广链接" :span="2">
-                            {{ userInfo.promotionLink || '-' }}
+                        <el-descriptions-item label="邀请人数">
+                            {{ commissionInfo.invitationCount || 0 }}
                         </el-descriptions-item>
                     </el-descriptions>
                 </div>
@@ -48,99 +101,54 @@
                 <!-- 购买产品部分 -->
                 <div class="detail-section">
                     <div class="section-title">购买产品</div>
-                    <el-table
-                        :data="purchasedProducts"
-                        border
-                        size="small"
-                        style="width: 100%"
-                        v-loading="productsLoading"
-                    >
-                        <el-table-column
-                            prop="productName"
-                            label="产品名称"
-                            align="center"
-                            min-width="120"
-                        />
-                        <el-table-column
-                            prop="packageType"
-                            label="套餐类型"
-                            align="center"
-                            min-width="140"
-                        />
-                        <el-table-column
-                            prop="remainingPoints"
-                            label="剩余积分"
-                            align="center"
-                            min-width="100"
-                        >
+                    <el-table :data="purchasedProducts" border size="small" style="width: 100%"
+                        v-loading="productsLoading">
+                        <el-table-column prop="productName" label="产品名称" align="center" min-width="150" />
+                        <el-table-column prop="displayName" label="套餐名称" align="center" min-width="180" />
+                        <el-table-column prop="cardTypeText" label="卡类型" align="center" min-width="100" />
+                        <el-table-column prop="packageTypeText" label="套餐类型" align="center" min-width="100" />
+                        <el-table-column prop="remainingPoints" label="剩余积分" align="center" min-width="100">
                             <template #default="{ row }">
-                                {{ row.remainingPoints || '--' }}
+                                {{ row.remainingPoints !== undefined && row.remainingPoints !== null ?
+                                row.remainingPoints : '--' }}
                             </template>
                         </el-table-column>
-                        <el-table-column
-                            prop="remainingTimes"
-                            label="剩余次数"
-                            align="center"
-                            min-width="100"
-                        >
+                        <el-table-column prop="remainingTimes" label="剩余次数" align="center" min-width="100">
                             <template #default="{ row }">
-                                {{ row.remainingTimes || '--' }}
+                                {{ row.remainingTimes !== undefined && row.remainingTimes !== null ? row.remainingTimes
+                                : '--' }}
                             </template>
                         </el-table-column>
-                        <el-table-column
-                            prop="expirationDate"
-                            label="到期时间"
-                            align="center"
-                            min-width="120"
-                        >
+                        <el-table-column prop="startTime" label="开始时间" align="center" min-width="160" />
+                        <el-table-column prop="endTime" label="结束时间" align="center" min-width="160" />
+                        <el-table-column prop="expireTime" label="到期时间" align="center" min-width="120" />
+                        <el-table-column prop="buyTime" label="购买时间" align="center" min-width="160" />
+                        <el-table-column prop="lastUseTime" label="最后使用时间" align="center" min-width="160">
                             <template #default="{ row }">
-                                {{ row.expirationDate || '--' }}
+                                {{ row.lastUseTime || '--' }}
                             </template>
                         </el-table-column>
-                        <el-table-column
-                            prop="purchaseDate"
-                            label="购买时间"
-                            align="center"
-                            min-width="120"
-                        />
-                        <el-table-column
-                            prop="status"
-                            label="状态"
-                            align="center"
-                            min-width="100"
-                        >
+                        <el-table-column prop="statusText" label="状态" align="center" min-width="100">
                             <template #default="{ row }">
-                                <el-tag
-                                    :type="row.status === '正常' ? 'success' : 'danger'"
-                                    size="small"
-                                >
-                                    {{ row.status }}
+                                <el-tag :type="row.status === 1 ? 'success' : row.status === 2 ? 'warning' : 'danger'"
+                                    size="small">
+                                    {{ row.statusText }}
                                 </el-tag>
                             </template>
                         </el-table-column>
                     </el-table>
                     <!-- 购买产品分页 -->
                     <div class="pagination-wrapper">
-                        <el-pagination
-                            v-model:current-page="productsPage"
-                            :page-size="productsPageSize"
-                            :total="productsTotal"
-                            layout="prev, pager, next, ->, jumper"
-                            small
-                            @current-change="handleProductsPageChange"
-                        >
+                        <el-pagination v-model:current-page="productsPage" :page-size="productsPageSize"
+                            :total="productsTotal" layout="prev, pager, next, ->, jumper" small
+                            @current-change="handleProductsPageChange">
                             <template #jumper>
                                 <span class="pagination-jumper">
                                     前往
-                                    <el-input-number
-                                        v-model="productsPage"
-                                        :min="1"
-                                        :max="Math.ceil(productsTotal / productsPageSize)"
-                                        size="small"
-                                        controls-position="right"
-                                        style="width: 60px; margin: 0 4px"
-                                        @change="(val: number | undefined) => val && handleProductsPageChange(val)"
-                                    />
+                                    <el-input-number v-model="productsPage" :min="1"
+                                        :max="Math.ceil(productsTotal / productsPageSize)" size="small"
+                                        controls-position="right" style="width: 60px; margin: 0 4px"
+                                        @change="(val: number | undefined) => val && handleProductsPageChange(val)" />
                                     页
                                 </span>
                             </template>
@@ -151,68 +159,30 @@
                 <!-- 下级人员部分 -->
                 <div class="detail-section">
                     <div class="section-title">下级人员</div>
-                    <div class="search-wrapper">
-                        <el-input
-                            v-model="subordinateSearchName"
-                            placeholder="人员名称"
-                            clearable
-                            size="small"
-                            style="width: 200px"
-                            @input="handleSubordinateSearch"
-                        />
-                    </div>
-                    <el-table
-                        :data="subordinatePersonnel"
-                        border
-                        size="small"
-                        style="width: 100%; margin-top: 12px"
-                        v-loading="subordinatesLoading"
-                    >
-                        <el-table-column
-                            prop="personnelName"
-                            label="人员名称"
-                            align="center"
-                            min-width="120"
-                        />
-                        <el-table-column
-                            prop="commissionAmount"
-                            label="产生分佣金额"
-                            align="center"
-                            min-width="140"
-                        />
-                        <el-table-column
-                            prop="status"
-                            label="状态"
-                            align="center"
-                            min-width="100"
-                        >
+                    <el-table :data="subordinatePersonnel" border size="small" style="width: 100%; margin-top: 12px"
+                        v-loading="subordinatesLoading">
+                        <el-table-column prop="name" label="人员名称" align="center" min-width="120" />
+                        <el-table-column prop="username" label="用户名" align="center" min-width="120" />
+                        <el-table-column prop="email" label="邮箱" align="center" min-width="180" />
+                        <el-table-column prop="commissionAmount" label="产生分佣金额" align="center" min-width="140" />
+                        <el-table-column prop="currentCommission" label="当前佣金金额" align="center" min-width="140">
+                            <template #default="{ row }">
+                                {{ row.currentCommission !== null && row.currentCommission !== undefined ?
+                                    row.currentCommission : '0.00' }}
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="statusText" label="状态" align="center" min-width="100">
                             <template #default="{ row }">
-                                <el-tag
-                                    :type="row.status === '正常' ? 'success' : 'danger'"
-                                    size="small"
-                                >
-                                    {{ row.status }}
+                                <el-tag :type="row.statusText === '正常' ? 'success' : 'danger'" size="small">
+                                    {{ row.statusText }}
                                 </el-tag>
                             </template>
                         </el-table-column>
-                        <el-table-column
-                            prop="bindingTime"
-                            label="绑定时间"
-                            align="center"
-                            min-width="120"
-                        />
-                        <el-table-column
-                            label="操作"
-                            align="center"
-                            min-width="100"
-                        >
+                        <el-table-column prop="bindTime" label="绑定时间" align="center" min-width="160" />
+                        <el-table-column prop="createTime" label="创建时间" align="center" min-width="160" />
+                        <el-table-column label="操作" align="center" min-width="100" fixed="right">
                             <template #default="{ row }">
-                                <el-button
-                                    type="danger"
-                                    link
-                                    size="small"
-                                    @click="handleUnbind(row)"
-                                >
+                                <el-button type="danger" link size="small" @click="handleUnbind(row)">
                                     解除
                                 </el-button>
                             </template>
@@ -220,26 +190,16 @@
                     </el-table>
                     <!-- 下级人员分页 -->
                     <div class="pagination-wrapper">
-                        <el-pagination
-                            v-model:current-page="subordinatesPage"
-                            :page-size="subordinatesPageSize"
-                            :total="subordinatesTotal"
-                            layout="prev, pager, next, ->, jumper"
-                            small
-                            @current-change="handleSubordinatesPageChange"
-                        >
+                        <el-pagination v-model:current-page="subordinatesPage" :page-size="subordinatesPageSize"
+                            :total="subordinatesTotal" layout="prev, pager, next, ->, jumper" small
+                            @current-change="handleSubordinatesPageChange">
                             <template #jumper>
                                 <span class="pagination-jumper">
                                     前往
-                                    <el-input-number
-                                        v-model="subordinatesPage"
-                                        :min="1"
-                                        :max="Math.ceil(subordinatesTotal / subordinatesPageSize)"
-                                        size="small"
-                                        controls-position="right"
-                                        style="width: 60px; margin: 0 4px"
-                                        @change="(val: number | undefined) => val && handleSubordinatesPageChange(val)"
-                                    />
+                                    <el-input-number v-model="subordinatesPage" :min="1"
+                                        :max="Math.ceil(subordinatesTotal / subordinatesPageSize)" size="small"
+                                        controls-position="right" style="width: 60px; margin: 0 4px"
+                                        @change="(val: number | undefined) => val && handleSubordinatesPageChange(val)" />
                                     页
                                 </span>
                             </template>
@@ -260,168 +220,353 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, watch } from 'vue'
+import { ref, reactive, computed } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
+import { detail, removeSubordinate } from '/@/api/backend/userCenter/user'
 
 interface UserInfo {
-    name: string
+    id: number
+    username: string
+    nickname: string
     email: string
-    company: string
+    mobile: string
+    avatar: string
+    gender: number
+    genderText: string
+    birthday: string | null
+    money: number | string
+    score: number
+    lastLoginTime: number
+    lastLoginTimeStr: string
+    lastLoginIp: string
+    loginFailure: number
+    joinIp: string
+    joinTime: number
+    joinTimeStr: string
+    motto: string
     status: string
-    totalCommission: number | string
-    currentCommission: number | string
+    statusText: string
+    updateTime: number
+    updateTimeStr: string
+    createTime: number
+    createTimeStr: string
+    invitationCode: string
+    companyId: number
+    companyName: string
+}
+
+interface CommissionInfo {
+    totalCommission: string
+    currentCommission: string
+    frozenCommission: string
+    withdrawnCommission: string
     promotionLink: string
+    invitationCount: number
 }
 
 interface PurchasedProduct {
+    id: number
     productName: string
-    packageType: string
-    remainingPoints?: string
-    remainingTimes?: string | number
-    expirationDate?: string
-    purchaseDate: string
-    status: string
+    packageName: string
+    packageFullName: string
+    displayName: string
+    cardTypeText: string
+    packageType: number
+    packageTypeText: string
+    remainingPoints: number
+    remainingTimes: number
+    startTime: string
+    endTime: string
+    expireTime: string
+    buyTime: string
+    lastUseTime: string | null
+    status: number
+    statusText: string
 }
 
 interface SubordinatePersonnel {
-    personnelName: string
-    commissionAmount: number
-    status: string
-    bindingTime: string
+    id: number
+    username: string
+    nickname: string
+    name: string
+    email: string
+    currentCommission: string | null
+    commissionAmount: string
+    statusText: string
+    bindTime: string
+    createTime: string
 }
 
 const visible = ref(false)
+const loading = ref(false)
+
 const userInfo = reactive<UserInfo>({
-    name: '',
+    id: 0,
+    username: '',
+    nickname: '',
     email: '',
-    company: '',
+    mobile: '',
+    avatar: '',
+    gender: 0,
+    genderText: '',
+    birthday: null,
+    money: 0,
+    score: 0,
+    lastLoginTime: 0,
+    lastLoginTimeStr: '',
+    lastLoginIp: '',
+    loginFailure: 0,
+    joinIp: '',
+    joinTime: 0,
+    joinTimeStr: '',
+    motto: '',
     status: '',
-    totalCommission: '',
-    currentCommission: '',
+    statusText: '',
+    updateTime: 0,
+    updateTimeStr: '',
+    createTime: 0,
+    createTimeStr: '',
+    invitationCode: '',
+    companyId: 0,
+    companyName: '',
+})
+
+const commissionInfo = reactive<CommissionInfo>({
+    totalCommission: '0.00',
+    currentCommission: '0.00',
+    frozenCommission: '0.00',
+    withdrawnCommission: '0.00',
     promotionLink: '',
+    invitationCount: 0,
 })
 
 // 购买产品相关
-const purchasedProducts = ref<PurchasedProduct[]>([])
+const allProducts = ref<PurchasedProduct[]>([])
 const productsLoading = ref(false)
 const productsPage = ref(1)
 const productsPageSize = ref(10)
-const productsTotal = ref(0)
 
 // 下级人员相关
-const subordinatePersonnel = ref<SubordinatePersonnel[]>([])
+const allSubordinates = ref<SubordinatePersonnel[]>([])
 const subordinatesLoading = ref(false)
 const subordinateSearchName = ref('')
 const subordinatesPage = ref(1)
 const subordinatesPageSize = ref(10)
-const subordinatesTotal = ref(0)
 
-// 打开弹框
-const open = (row: any) => {
-    // 设置用户信息
-    userInfo.name = row.name || '张三'
-    userInfo.email = row.email || '9854112544@qq.com'
-    userInfo.company = row.company || '腾达'
-    userInfo.status = '正常'
-    userInfo.totalCommission = 500
-    userInfo.currentCommission = 100
-    userInfo.promotionLink = 'XXXX.com?from=用户ID'
-
-    // 加载购买产品数据
-    loadPurchasedProducts()
-
-    // 加载下级人员数据
-    loadSubordinatePersonnel()
+// 计算属性:分页后的产品列表
+const purchasedProducts = computed(() => {
+    const start = (productsPage.value - 1) * productsPageSize.value
+    const end = start + productsPageSize.value
+    return allProducts.value.slice(start, end)
+})
 
-    visible.value = true
-}
+// 计算属性:产品总数
+const productsTotal = computed(() => allProducts.value.length)
 
-// 加载购买产品数据
-const loadPurchasedProducts = () => {
-    productsLoading.value = true
-    // 模拟异步请求
-    setTimeout(() => {
-        const mockData: PurchasedProduct[] = [
-            {
-                productName: '聚德神笔',
-                packageType: '日卡',
-                remainingPoints: '--',
-                remainingTimes: '--',
-                expirationDate: '2025-10-11',
-                purchaseDate: '2025-10-11',
-                status: '已过期',
-            },
-            {
-                productName: '棱镜',
-                packageType: '棱镜10次卡',
-                remainingPoints: '--',
-                remainingTimes: 10,
-                expirationDate: '--',
-                purchaseDate: '2025-10-11',
-                status: '正常',
-            },
-        ]
-        purchasedProducts.value = mockData
-        productsTotal.value = mockData.length
-        productsLoading.value = false
-    }, 300)
+// 计算属性:过滤后的下级人员列表
+const filteredSubordinates = computed(() => {
+    if (!subordinateSearchName.value) {
+        return allSubordinates.value
+    }
+    const search = subordinateSearchName.value.toLowerCase()
+    return allSubordinates.value.filter(
+        (item) =>
+            item.name?.toLowerCase().includes(search) ||
+            item.username?.toLowerCase().includes(search) ||
+            item.email?.toLowerCase().includes(search)
+    )
+})
+
+// 计算属性:分页后的下级人员列表
+const subordinatePersonnel = computed(() => {
+    const start = (subordinatesPage.value - 1) * subordinatesPageSize.value
+    const end = start + subordinatesPageSize.value
+    return filteredSubordinates.value.slice(start, end)
+})
+
+// 计算属性:下级人员总数
+const subordinatesTotal = computed(() => filteredSubordinates.value.length)
+
+
+// 获取状态文本
+const getStatusText = (status: string): string => {
+    return status === 'enable' ? '启用' : '禁用'
 }
 
-// 加载下级人员数据
-const loadSubordinatePersonnel = () => {
-    subordinatesLoading.value = true
-    // 模拟异步请求
-    setTimeout(() => {
-        let mockData: SubordinatePersonnel[] = [
-            {
-                personnelName: '王五',
-                commissionAmount: 0,
-                status: '正常',
-                bindingTime: '2025-10-11',
-            },
-            {
-                personnelName: '李四',
-                commissionAmount: 10,
-                status: '正常',
-                bindingTime: '2025-10-11',
-            },
-        ]
-
-        // 根据搜索条件过滤
-        if (subordinateSearchName.value) {
-            mockData = mockData.filter(item =>
-                item.personnelName.includes(subordinateSearchName.value)
-            )
-        }
+// 获取套餐类型文本
+const getPackageTypeText = (type: number): string => {
+    return type === 1 ? '个人' : '企业'
+}
 
-        subordinatePersonnel.value = mockData
-        subordinatesTotal.value = mockData.length
-        subordinatesLoading.value = false
-    }, 300)
+// 打开弹框
+const open = (row: TableRow) => {
+    visible.value = true
+    loading.value = true
+
+    detail({ id: row.id })
+        .then((res: anyObj) => {
+            const data = res.data?.data || res.data
+
+            if (data) {
+                // 处理用户信息
+                if (data.user_info) {
+                    const user = data.user_info
+                    Object.assign(userInfo, {
+                        id: user.id || 0,
+                        username: user.username || '',
+                        nickname: user.nickname || '',
+                        email: user.email || '',
+                        mobile: user.mobile || '',
+                        avatar: user.avatar || '',
+                        gender: user.gender || 0,
+                        birthday: user.birthday || null,
+                        money: user.money || 0,
+                        score: user.score || 0,
+                        lastLoginTime: user.last_login_time || 0,
+                        lastLoginTimeStr: user.last_login_time_str || '',
+                        lastLoginIp: user.last_login_ip || '',
+                        loginFailure: user.login_failure || 0,
+                        joinIp: user.join_ip || '',
+                        joinTime: user.join_time || 0,
+                        joinTimeStr: user.join_time_str || '',
+                        motto: user.motto || '',
+                        status: user.status || '',
+                        statusText: getStatusText(user.status || ''),
+                        updateTime: user.update_time || 0,
+                        updateTimeStr: user.update_time_str || '',
+                        createTime: user.create_time || 0,
+                        createTimeStr: user.create_time_str || '',
+                        invitationCode: user.invitation_code || '',
+                        companyId: user.company_id || 0,
+                        companyName: user.company_name || '',
+                    })
+                }
+
+                // 处理佣金信息
+                if (data.commission_info) {
+                    const commission = data.commission_info
+                    Object.assign(commissionInfo, {
+                        totalCommission: commission.total_commission || '0.00',
+                        currentCommission: commission.current_commission || '0.00',
+                        frozenCommission: commission.frozen_commission || '0.00',
+                        withdrawnCommission: commission.withdrawn_commission || '0.00',
+                        promotionLink: commission.promotion_link || '',
+                        invitationCount: commission.invitation_count || 0,
+                    })
+                }
+
+                // 处理产品列表
+                if (data.product_list && Array.isArray(data.product_list)) {
+                    allProducts.value = data.product_list.map((item: any) => ({
+                        id: item.id || 0,
+                        productName: item.product_name || '',
+                        packageName: item.package_name || '',
+                        packageFullName: item.package_full_name || '',
+                        displayName: item.display_name || '',
+                        cardTypeText: item.card_type_text || '',
+                        packageType: item.package_type || 0,
+                        packageTypeText: getPackageTypeText(item.package_type || 0),
+                        remainingPoints: item.remaining_points || 0,
+                        remainingTimes: item.remaining_times || 0,
+                        startTime: item.start_time || '',
+                        endTime: item.end_time || '',
+                        expireTime: item.expire_time || '',
+                        buyTime: item.buy_time || '',
+                        lastUseTime: item.last_use_time || null,
+                        status: item.status || 0,
+                        statusText: item.status_text || '',
+                    }))
+                } else {
+                    allProducts.value = []
+                }
+
+                // 处理下级人员列表
+                if (data.subordinate_list && Array.isArray(data.subordinate_list)) {
+                    allSubordinates.value = data.subordinate_list.map((item: any) => ({
+                        id: item.id || 0,
+                        username: item.username || '',
+                        nickname: item.nickname || '',
+                        name: item.name || '',
+                        email: item.email || '',
+                        currentCommission: item.current_commission,
+                        commissionAmount: item.commission_amount || '0.00',
+                        statusText: item.status_text || '',
+                        bindTime: item.bind_time || '',
+                        createTime: item.create_time || '',
+                    }))
+                } else {
+                    allSubordinates.value = []
+                }
+
+                // 重置分页
+                productsPage.value = 1
+                subordinatesPage.value = 1
+            }
+        })
+        .catch((error: any) => {
+            ElMessage.error(error?.msg || '获取详情失败')
+        })
+        .finally(() => {
+            loading.value = false
+        })
 }
 
 // 购买产品分页变化
 const handleProductsPageChange = (page: number) => {
     productsPage.value = page
-    loadPurchasedProducts()
 }
 
 // 下级人员分页变化
 const handleSubordinatesPageChange = (page: number) => {
     subordinatesPage.value = page
-    loadSubordinatePersonnel()
 }
 
 // 下级人员搜索
 const handleSubordinateSearch = () => {
     subordinatesPage.value = 1
-    loadSubordinatePersonnel()
+}
+
+// 复制推广链接
+const handleCopyLink = (link: string) => {
+    if (!link) {
+        ElMessage.warning('推广链接为空')
+        return
+    }
+
+    // 使用 Clipboard API 复制
+    if (navigator.clipboard) {
+        navigator.clipboard.writeText(link).then(() => {
+            ElMessage.success('复制成功')
+        }).catch(() => {
+            // 降级方案
+            fallbackCopy(link)
+        })
+    } else {
+        fallbackCopy(link)
+    }
+}
+
+// 降级复制方案
+const fallbackCopy = (text: string) => {
+    const textarea = document.createElement('textarea')
+    textarea.value = text
+    textarea.style.position = 'fixed'
+    textarea.style.opacity = '0'
+    document.body.appendChild(textarea)
+    textarea.select()
+    try {
+        document.execCommand('copy')
+        ElMessage.success('复制成功')
+    } catch (err) {
+        ElMessage.error('复制失败')
+    }
+    document.body.removeChild(textarea)
 }
 
 // 解除绑定
 const handleUnbind = (row: SubordinatePersonnel) => {
     ElMessageBox.confirm(
-        `确定要解除与"${row.personnelName}"的绑定关系吗?`,
+        `确定要解除与"${row.name}"的绑定关系吗?`,
         '提示',
         {
             confirmButtonText: '确定',
@@ -430,8 +575,22 @@ const handleUnbind = (row: SubordinatePersonnel) => {
         }
     )
         .then(() => {
-            ElMessage.success('解除绑定成功')
-            loadSubordinatePersonnel()
+            removeSubordinate({ id: row.id })
+                .then(() => {
+                    ElMessage.success('解除绑定成功')
+                    // 从列表中移除
+                    const index = allSubordinates.value.findIndex(item => item.id === row.id)
+                    if (index > -1) {
+                        allSubordinates.value.splice(index, 1)
+                    }
+                    // 如果当前页没有数据了,返回上一页
+                    if (subordinatePersonnel.value.length === 0 && subordinatesPage.value > 1) {
+                        subordinatesPage.value--
+                    }
+                })
+                .catch((error: any) => {
+                    ElMessage.error(error?.msg || '解除绑定失败')
+                })
         })
         .catch(() => {
             // 取消操作
@@ -445,6 +604,8 @@ const handleClose = () => {
     subordinateSearchName.value = ''
     productsPage.value = 1
     subordinatesPage.value = 1
+    allProducts.value = []
+    allSubordinates.value = []
 }
 
 defineExpose({ open })
@@ -471,7 +632,8 @@ defineExpose({ open })
         color: #333;
     }
 
-    .user-info-descriptions {
+    .user-info-descriptions,
+    .commission-info-descriptions {
         :deep(.el-descriptions__label) {
             font-weight: 500;
         }
@@ -501,5 +663,11 @@ defineExpose({ open })
         align-items: center;
     }
 }
-</style>
 
+.promotion-link {
+    color: #409EFF;
+    text-decoration: none;
+    font-size: 14px;
+    cursor: pointer;
+}
+</style>

+ 8 - 8
web/src/views/backend/userCenter/user/index.vue

@@ -65,19 +65,19 @@ const baTable = new baTableClass(
             { label: '编号', prop: 'id', align: 'center', width: 70 },
             {
                 label: '用户名',
-                prop: 'name',
+                prop: 'username',
                 align: 'center',
-                minWidth: 120
+                minWidth: 150
             },
             {
                 label: '邮箱',
                 prop: 'email',
                 align: 'center',
-                minWidth: 170
+                minWidth: 200
             },
             {
                 label: '企业',
-                prop: 'company',
+                prop: 'company_names',
                 align: 'center',
                 minWidth: 160
             },
@@ -86,13 +86,13 @@ const baTable = new baTableClass(
                 prop: 'status',
                 align: 'center',
                 render: 'tag',
-                custom: { '1': 'success', '0': 'danger' },
-                replaceValue: { '1': '启用', '0': '禁用' },
-                minWidth: 150
+                custom: { 'enable': 'success', 'disable': 'danger' },
+                replaceValue: { 'enable': '启用', 'disable': '禁用' },
+                minWidth: 120
             },
             {
                 label: '邀请人',
-                prop: 'inviter',
+                prop: 'inviter_names',
                 align: 'center',
                 minWidth: 120
             },

+ 7 - 7
web/src/views/backend/userCenter/user/popupForm.vue

@@ -29,9 +29,9 @@
                     :rules="rules"
                     v-if="!baTable.form.loading"
                 >
-                    <el-form-item label="姓名" prop="name">
+                    <el-form-item label="姓名" prop="username">
                         <el-input
-                            v-model="baTable.form.items!.name"
+                            v-model="baTable.form.items!.username"
                             placeholder="请输入姓名"
                         />
                     </el-form-item>
@@ -106,7 +106,7 @@ const emit = defineEmits(['success'])
 const { t } = useI18n()
 
 const rules: Partial<Record<string, FormItemRule[]>> = reactive({
-    name: [buildValidatorData({ name: 'required', title: '姓名' })],
+    username: [buildValidatorData({ name: 'required', title: '姓名' })],
     email: [
         buildValidatorData({ name: 'required', title: '邮箱' }),
         buildValidatorData({ name: 'email', title: '邮箱' })
@@ -135,8 +135,8 @@ interface CompanyOption {
 const companyOptions = ref<CompanyOption[]>([])
 
 const statusOptions = {
-    '0': '禁用',
-    '1': '启用',
+    'disable': '禁用',
+    'enable': '启用',
 }
 
 const getCompanyList = () => {
@@ -156,9 +156,9 @@ const btnSubmit = () => {
 
             let params = {
                 id: baTable.form.items!.id,
-                name: baTable.form.items!.name,
+                name: baTable.form.items!.username,
                 email:baTable.form.items!.email,
-                company:baTable.form.items!.company,
+                company_id:baTable.form.items!.company,
                 password:baTable.form.items!.password,
                 status: baTable.form.items!.status,
             }

+ 71 - 95
web/src/views/frontend/components/Me/purchaseHistory.vue

@@ -8,13 +8,13 @@
     </div>
 
     <div class="history-filters">
-      <el-input v-model="searchForm.orderId" placeholder="请输入订单号" clearable class="filter-item order-input">
+      <el-input v-model="searchForm.order_no" placeholder="请输入订单号" clearable class="filter-item order-input">
         <template #prepend>订单号</template>
       </el-input>
 
-      <el-date-picker v-model="searchForm.dateRange" class="filter-item date-picker" style="width: 100px !important;"
-        type="daterange" unlink-panels start-placeholder="下单开始时间" end-placeholder="下单结束时间" range-separator="~"
-        value-format="YYYY-MM-DD" clearable />
+      <el-date-picker v-model="searchForm.dateRange" class="filter-item date-picker" type="daterange"
+                unlink-panels start-placeholder="开始时间" end-placeholder="结束时间" range-separator="~"
+                value-format="YYYY-MM-DD" style="width: 100px !important;" />
 
       <el-button size="small" type="primary" :icon="Search" @click="handleSearch">查询</el-button>
       <el-button size="small" @click="handleReset">重置</el-button>
@@ -28,8 +28,8 @@
     </div>
 
     <div v-loading="loading" class="order-list-wrapper">
-      <div v-if="filteredOrders.length" class="order-list">
-        <div v-for="order in filteredOrders" :key="order.order_no" class="order-card">
+      <div v-if="orders.length" class="order-list">
+        <div v-for="order in orders" :key="order.order_no" class="order-card">
           <div class="order-card__header">
             <div class="header-left">
               <span class="order-id">订单号:{{ order.order_no }}</span>
@@ -94,14 +94,22 @@
 
       <el-empty v-else-if="!loading" class="history-empty" description="暂无符合条件的订单" />
     </div>
+
+    <!-- 分页组件 -->
+    <div v-if="pagination.total > 0" class="pagination-wrapper">
+      <el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
+        :page-sizes="[10, 20, 50, 100]" :total="pagination.total" layout="total, sizes, prev, pager, next, jumper"
+        @size-change="handleSizeChange" @current-change="handleCurrentChange" />
+    </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import { computed, onMounted, reactive, ref } from 'vue'
+import { onMounted, reactive, ref } from 'vue'
 import { Search } from '@element-plus/icons-vue'
 import { ElMessage } from 'element-plus'
 import { purchaseRecordsSimple } from '/@/api/frontend/me'
+import dayjs from 'dayjs'
 
 defineOptions({ name: 'MePurchaseHistory' })
 
@@ -135,13 +143,12 @@ type PurchaseRecordsResponse = {
 
 const activeStatus = ref<OrderTabStatus>('all')
 const loading = ref(false)
-const searchForm = reactive<{ orderId: string; dateRange: string[] }>({
-  orderId: '',
-  dateRange: []
-})
-const appliedFilters = reactive<{ orderId: string; dateRange: string[] }>({
-  orderId: '',
-  dateRange: []
+const searchForm = reactive<{
+  order_no: string
+  dateRange: string[]
+}>({
+  order_no: '',
+  dateRange:[dayjs().format('YYYY-MM-DD'), dayjs().format('YYYY-MM-DD')]
 })
 const statusTabs: { label: string; value: OrderTabStatus }[] = [
   { label: '全部', value: 'all' },
@@ -166,7 +173,22 @@ const pagination = reactive({
 const loadPurchaseRecords = async () => {
   try {
     loading.value = true
-    const res = await purchaseRecordsSimple({})
+    const params: any = {
+      page: pagination.currentPage,
+      pageSize: pagination.pageSize,
+      status: activeStatus.value
+    }
+
+    // 添加搜索条件
+    if (searchForm.order_no) {
+      params.order_no = searchForm.order_no.trim()
+    }
+    if (searchForm.dateRange) {
+      params.star_time = searchForm.dateRange[0]
+      params.end_time = searchForm.dateRange[1]
+    }
+
+    const res = await purchaseRecordsSimple(params)
     const data = res.data as PurchaseRecordsResponse
     orders.value = data.list || []
     stats.value = data.stats || { total_orders: 0, paid_orders: 0, unpaid_orders: 0 }
@@ -178,101 +200,46 @@ const loadPurchaseRecords = async () => {
   }
 }
 
-// 先应用订单号和日期筛选,得到基础筛选结果
-const baseFilteredOrders = computed(() => {
-  let filtered = orders.value
-
-  // 订单号过滤
-  if (appliedFilters.orderId) {
-    const orderId = appliedFilters.orderId.trim()
-    filtered = filtered.filter(order => order.order_no.includes(orderId))
-  }
-
-  // 日期范围过滤
-  if (appliedFilters.dateRange && appliedFilters.dateRange.length == 2) {
-    filtered = filtered.filter(order => {
-      return isWithinRange(order.order_time_formatted, appliedFilters.dateRange)
-    })
-  }
-
-  return filtered
-})
-
-// 再应用状态筛选,得到最终筛选结果
-const filteredOrders = computed(() => {
-  let filtered = baseFilteredOrders.value
-
-  // 状态过滤
-  if (activeStatus.value != 'all') {
-    if (activeStatus.value == 'paid') {
-      filtered = filtered.filter(order => order.pay_status_text == '已支付')
-    } else if (activeStatus.value == 'unpaid') {
-      filtered = filtered.filter(order => order.pay_status_text == '未支付')
-    }
-  }
-
-  return filtered
-})
-
-// 根据基础筛选结果计算各状态的订单数
-const filteredStats = computed(() => {
-  const base = baseFilteredOrders.value
-  return {
-    total_orders: base.length,
-    paid_orders: base.filter(order => order.pay_status_text == '已支付').length,
-    unpaid_orders: base.filter(order => order.pay_status_text == '未支付').length
-  }
-})
-
+// 搜索
 const handleSearch = () => {
-  // 更新应用的筛选条件
-  appliedFilters.orderId = searchForm.orderId || ''
-  // 确保日期范围格式正确
-  if (searchForm.dateRange && Array.isArray(searchForm.dateRange) && searchForm.dateRange.length == 2) {
-    appliedFilters.dateRange = [...searchForm.dateRange]
-  } else {
-    appliedFilters.dateRange = []
-  }
+  pagination.currentPage = 1 // 重置到第一页
+  loadPurchaseRecords()
 }
 
+// 重置
 const handleReset = () => {
-  // 重置搜索表单
-  searchForm.orderId = ''
-  searchForm.dateRange = []
-  // 重置应用的筛选条件
-  appliedFilters.orderId = ''
-  appliedFilters.dateRange = []
+  searchForm.order_no = ''
+  searchForm.dateRange = [dayjs().format('YYYY-MM-DD'), dayjs().format('YYYY-MM-DD')]
+  pagination.currentPage = 1
+  loadPurchaseRecords()
 }
 
+// 标签页切换
 const handleTabClick = (tab: { paneName?: string | number; name?: string | number }) => {
   activeStatus.value = (tab.paneName || tab.name || 'all') as OrderTabStatus
+  pagination.currentPage = 1 // 切换状态时重置到第一页
+  loadPurchaseRecords()
 }
 
+// 获取订单数量(使用后端返回的统计数据)
 const getOrderCount = (status: OrderTabStatus) => {
-  // 使用筛选后的统计结果
-  if (status == 'all') return filteredStats.value.total_orders || 0
-  if (status == 'paid') return filteredStats.value.paid_orders || 0
-  if (status == 'unpaid') return filteredStats.value.unpaid_orders || 0
+  if (status == 'all') return stats.value.total_orders || 0
+  if (status == 'paid') return stats.value.paid_orders || 0
+  if (status == 'unpaid') return stats.value.unpaid_orders || 0
   return 0
 }
 
-const isWithinRange = (orderTime: string, range: string[]) => {
-  if (!range || range.length != 2 || !range[0] || !range[1]) return true
-  const [start, end] = range
-
-  // 确保日期格式正确,处理 YYYY-MM-DD 格式
-  const startDate = new Date(start + ' 00:00:00')
-  const endDate = new Date(end + ' 23:59:59')
-
-  // 订单时间格式通常是 "2025-06-11 15:53:35"
-  const targetDate = new Date(orderTime.replace(/-/g, '/'))
-
-  // 检查日期是否有效
-  if (isNaN(startDate.getTime()) || isNaN(endDate.getTime()) || isNaN(targetDate.getTime())) {
-    return true
-  }
+// 分页大小改变
+const handleSizeChange = (size: number) => {
+  pagination.pageSize = size
+  pagination.currentPage = 1
+  loadPurchaseRecords()
+}
 
-  return targetDate >= startDate && targetDate <= endDate
+// 当前页改变
+const handleCurrentChange = (page: number) => {
+  pagination.currentPage = page
+  loadPurchaseRecords()
 }
 
 // 初始化加载数据
@@ -516,6 +483,15 @@ onMounted(() => {
   border: 1px dashed #c6e2ff;
 }
 
+.pagination-wrapper {
+  display: flex;
+  justify-content: flex-end;
+  padding: 16px;
+  background: #fff;
+  border-radius: 8px;
+  box-shadow: 0 4px 16px rgba(64, 158, 255, 0.12);
+}
+
 @media (max-width: 768px) {
   .history-header {
     flex-direction: column;