|
|
@@ -0,0 +1,505 @@
|
|
|
+<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"
|
|
|
+ >
|
|
|
+ <template #header>
|
|
|
+ <div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
|
|
|
+ 详情
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <el-scrollbar class="ba-table-form-scrollbar">
|
|
|
+ <div class="detail-wrapper">
|
|
|
+ <!-- 用户信息部分 -->
|
|
|
+ <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>
|
|
|
+ <el-descriptions-item label="邮箱">
|
|
|
+ {{ userInfo.email || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="企业">
|
|
|
+ {{ userInfo.company || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="状态">
|
|
|
+ <el-tag :type="userInfo.status === '正常' ? 'success' : 'danger'" size="small">
|
|
|
+ {{ userInfo.status || '-' }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="获得总佣金金额">
|
|
|
+ {{ userInfo.totalCommission || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="当前佣金金额">
|
|
|
+ {{ userInfo.currentCommission || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="推广链接" :span="2">
|
|
|
+ {{ userInfo.promotionLink || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 购买产品部分 -->
|
|
|
+ <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"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ row.remainingPoints || '--' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ prop="remainingTimes"
|
|
|
+ label="剩余次数"
|
|
|
+ align="center"
|
|
|
+ min-width="100"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ row.remainingTimes || '--' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ prop="expirationDate"
|
|
|
+ label="到期时间"
|
|
|
+ align="center"
|
|
|
+ min-width="120"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ row.expirationDate || '--' }}
|
|
|
+ </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"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag
|
|
|
+ :type="row.status === '正常' ? 'success' : 'danger'"
|
|
|
+ size="small"
|
|
|
+ >
|
|
|
+ {{ row.status }}
|
|
|
+ </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"
|
|
|
+ >
|
|
|
+ <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)"
|
|
|
+ />
|
|
|
+ 页
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </el-pagination>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 下级人员部分 -->
|
|
|
+ <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"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag
|
|
|
+ :type="row.status === '正常' ? 'success' : 'danger'"
|
|
|
+ size="small"
|
|
|
+ >
|
|
|
+ {{ row.status }}
|
|
|
+ </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"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ link
|
|
|
+ size="small"
|
|
|
+ @click="handleUnbind(row)"
|
|
|
+ >
|
|
|
+ 解除
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </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"
|
|
|
+ >
|
|
|
+ <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)"
|
|
|
+ />
|
|
|
+ 页
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </el-pagination>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-scrollbar>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button size="large" @click="handleClose">
|
|
|
+ 关闭
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, reactive, watch } from 'vue'
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
+
|
|
|
+interface UserInfo {
|
|
|
+ name: string
|
|
|
+ email: string
|
|
|
+ company: string
|
|
|
+ status: string
|
|
|
+ totalCommission: number | string
|
|
|
+ currentCommission: number | string
|
|
|
+ promotionLink: string
|
|
|
+}
|
|
|
+
|
|
|
+interface PurchasedProduct {
|
|
|
+ productName: string
|
|
|
+ packageType: string
|
|
|
+ remainingPoints?: string
|
|
|
+ remainingTimes?: string | number
|
|
|
+ expirationDate?: string
|
|
|
+ purchaseDate: string
|
|
|
+ status: string
|
|
|
+}
|
|
|
+
|
|
|
+interface SubordinatePersonnel {
|
|
|
+ personnelName: string
|
|
|
+ commissionAmount: number
|
|
|
+ status: string
|
|
|
+ bindingTime: string
|
|
|
+}
|
|
|
+
|
|
|
+const visible = ref(false)
|
|
|
+const userInfo = reactive<UserInfo>({
|
|
|
+ name: '',
|
|
|
+ email: '',
|
|
|
+ company: '',
|
|
|
+ status: '',
|
|
|
+ totalCommission: '',
|
|
|
+ currentCommission: '',
|
|
|
+ promotionLink: '',
|
|
|
+})
|
|
|
+
|
|
|
+// 购买产品相关
|
|
|
+const purchasedProducts = ref<PurchasedProduct[]>([])
|
|
|
+const productsLoading = ref(false)
|
|
|
+const productsPage = ref(1)
|
|
|
+const productsPageSize = ref(10)
|
|
|
+const productsTotal = ref(0)
|
|
|
+
|
|
|
+// 下级人员相关
|
|
|
+const subordinatePersonnel = 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()
|
|
|
+
|
|
|
+ visible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+// 加载购买产品数据
|
|
|
+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 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)
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ subordinatePersonnel.value = mockData
|
|
|
+ subordinatesTotal.value = mockData.length
|
|
|
+ subordinatesLoading.value = false
|
|
|
+ }, 300)
|
|
|
+}
|
|
|
+
|
|
|
+// 购买产品分页变化
|
|
|
+const handleProductsPageChange = (page: number) => {
|
|
|
+ productsPage.value = page
|
|
|
+ loadPurchasedProducts()
|
|
|
+}
|
|
|
+
|
|
|
+// 下级人员分页变化
|
|
|
+const handleSubordinatesPageChange = (page: number) => {
|
|
|
+ subordinatesPage.value = page
|
|
|
+ loadSubordinatePersonnel()
|
|
|
+}
|
|
|
+
|
|
|
+// 下级人员搜索
|
|
|
+const handleSubordinateSearch = () => {
|
|
|
+ subordinatesPage.value = 1
|
|
|
+ loadSubordinatePersonnel()
|
|
|
+}
|
|
|
+
|
|
|
+// 解除绑定
|
|
|
+const handleUnbind = (row: SubordinatePersonnel) => {
|
|
|
+ ElMessageBox.confirm(
|
|
|
+ `确定要解除与"${row.personnelName}"的绑定关系吗?`,
|
|
|
+ '提示',
|
|
|
+ {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning',
|
|
|
+ }
|
|
|
+ )
|
|
|
+ .then(() => {
|
|
|
+ ElMessage.success('解除绑定成功')
|
|
|
+ loadSubordinatePersonnel()
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ // 取消操作
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 关闭弹框
|
|
|
+const handleClose = () => {
|
|
|
+ visible.value = false
|
|
|
+ // 重置数据
|
|
|
+ subordinateSearchName.value = ''
|
|
|
+ productsPage.value = 1
|
|
|
+ subordinatesPage.value = 1
|
|
|
+}
|
|
|
+
|
|
|
+defineExpose({ open })
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.user-detail-dialog {
|
|
|
+ .detail-wrapper {
|
|
|
+ padding: 12px 16px 24px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .detail-section {
|
|
|
+ margin-bottom: 24px;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .section-title {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+
|
|
|
+ .user-info-descriptions {
|
|
|
+ :deep(.el-descriptions__label) {
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-wrapper {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pagination-wrapper {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ margin-top: 12px;
|
|
|
+ padding-top: 12px;
|
|
|
+
|
|
|
+ .pagination-jumper {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #606266;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .dialog-footer {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|