detailDialog.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. <template>
  2. <el-dialog
  3. v-model="visible"
  4. class="ba-operate-dialog user-detail-dialog"
  5. width="1200px"
  6. :close-on-click-modal="false"
  7. :destroy-on-close="true"
  8. @close="handleClose"
  9. >
  10. <template #header>
  11. <div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
  12. 详情
  13. </div>
  14. </template>
  15. <el-scrollbar class="ba-table-form-scrollbar">
  16. <div class="detail-wrapper">
  17. <!-- 用户信息部分 -->
  18. <div class="detail-section">
  19. <div class="section-title">用户信息</div>
  20. <el-descriptions :column="2" border size="small" class="user-info-descriptions">
  21. <el-descriptions-item label="用户名称">
  22. {{ userInfo.name || '-' }}
  23. </el-descriptions-item>
  24. <el-descriptions-item label="邮箱">
  25. {{ userInfo.email || '-' }}
  26. </el-descriptions-item>
  27. <el-descriptions-item label="企业">
  28. {{ userInfo.company || '-' }}
  29. </el-descriptions-item>
  30. <el-descriptions-item label="状态">
  31. <el-tag :type="userInfo.status === '正常' ? 'success' : 'danger'" size="small">
  32. {{ userInfo.status || '-' }}
  33. </el-tag>
  34. </el-descriptions-item>
  35. <el-descriptions-item label="获得总佣金金额">
  36. {{ userInfo.totalCommission || '-' }}
  37. </el-descriptions-item>
  38. <el-descriptions-item label="当前佣金金额">
  39. {{ userInfo.currentCommission || '-' }}
  40. </el-descriptions-item>
  41. <el-descriptions-item label="推广链接" :span="2">
  42. {{ userInfo.promotionLink || '-' }}
  43. </el-descriptions-item>
  44. </el-descriptions>
  45. </div>
  46. <!-- 购买产品部分 -->
  47. <div class="detail-section">
  48. <div class="section-title">购买产品</div>
  49. <el-table
  50. :data="purchasedProducts"
  51. border
  52. size="small"
  53. style="width: 100%"
  54. v-loading="productsLoading"
  55. >
  56. <el-table-column
  57. prop="productName"
  58. label="产品名称"
  59. align="center"
  60. min-width="120"
  61. />
  62. <el-table-column
  63. prop="packageType"
  64. label="套餐类型"
  65. align="center"
  66. min-width="140"
  67. />
  68. <el-table-column
  69. prop="remainingPoints"
  70. label="剩余积分"
  71. align="center"
  72. min-width="100"
  73. >
  74. <template #default="{ row }">
  75. {{ row.remainingPoints || '--' }}
  76. </template>
  77. </el-table-column>
  78. <el-table-column
  79. prop="remainingTimes"
  80. label="剩余次数"
  81. align="center"
  82. min-width="100"
  83. >
  84. <template #default="{ row }">
  85. {{ row.remainingTimes || '--' }}
  86. </template>
  87. </el-table-column>
  88. <el-table-column
  89. prop="expirationDate"
  90. label="到期时间"
  91. align="center"
  92. min-width="120"
  93. >
  94. <template #default="{ row }">
  95. {{ row.expirationDate || '--' }}
  96. </template>
  97. </el-table-column>
  98. <el-table-column
  99. prop="purchaseDate"
  100. label="购买时间"
  101. align="center"
  102. min-width="120"
  103. />
  104. <el-table-column
  105. prop="status"
  106. label="状态"
  107. align="center"
  108. min-width="100"
  109. >
  110. <template #default="{ row }">
  111. <el-tag
  112. :type="row.status === '正常' ? 'success' : 'danger'"
  113. size="small"
  114. >
  115. {{ row.status }}
  116. </el-tag>
  117. </template>
  118. </el-table-column>
  119. </el-table>
  120. <!-- 购买产品分页 -->
  121. <div class="pagination-wrapper">
  122. <el-pagination
  123. v-model:current-page="productsPage"
  124. :page-size="productsPageSize"
  125. :total="productsTotal"
  126. layout="prev, pager, next, ->, jumper"
  127. small
  128. @current-change="handleProductsPageChange"
  129. >
  130. <template #jumper>
  131. <span class="pagination-jumper">
  132. 前往
  133. <el-input-number
  134. v-model="productsPage"
  135. :min="1"
  136. :max="Math.ceil(productsTotal / productsPageSize)"
  137. size="small"
  138. controls-position="right"
  139. style="width: 60px; margin: 0 4px"
  140. @change="(val: number | undefined) => val && handleProductsPageChange(val)"
  141. />
  142. 页
  143. </span>
  144. </template>
  145. </el-pagination>
  146. </div>
  147. </div>
  148. <!-- 下级人员部分 -->
  149. <div class="detail-section">
  150. <div class="section-title">下级人员</div>
  151. <div class="search-wrapper">
  152. <el-input
  153. v-model="subordinateSearchName"
  154. placeholder="人员名称"
  155. clearable
  156. size="small"
  157. style="width: 200px"
  158. @input="handleSubordinateSearch"
  159. />
  160. </div>
  161. <el-table
  162. :data="subordinatePersonnel"
  163. border
  164. size="small"
  165. style="width: 100%; margin-top: 12px"
  166. v-loading="subordinatesLoading"
  167. >
  168. <el-table-column
  169. prop="personnelName"
  170. label="人员名称"
  171. align="center"
  172. min-width="120"
  173. />
  174. <el-table-column
  175. prop="commissionAmount"
  176. label="产生分佣金额"
  177. align="center"
  178. min-width="140"
  179. />
  180. <el-table-column
  181. prop="status"
  182. label="状态"
  183. align="center"
  184. min-width="100"
  185. >
  186. <template #default="{ row }">
  187. <el-tag
  188. :type="row.status === '正常' ? 'success' : 'danger'"
  189. size="small"
  190. >
  191. {{ row.status }}
  192. </el-tag>
  193. </template>
  194. </el-table-column>
  195. <el-table-column
  196. prop="bindingTime"
  197. label="绑定时间"
  198. align="center"
  199. min-width="120"
  200. />
  201. <el-table-column
  202. label="操作"
  203. align="center"
  204. min-width="100"
  205. >
  206. <template #default="{ row }">
  207. <el-button
  208. type="danger"
  209. link
  210. size="small"
  211. @click="handleUnbind(row)"
  212. >
  213. 解除
  214. </el-button>
  215. </template>
  216. </el-table-column>
  217. </el-table>
  218. <!-- 下级人员分页 -->
  219. <div class="pagination-wrapper">
  220. <el-pagination
  221. v-model:current-page="subordinatesPage"
  222. :page-size="subordinatesPageSize"
  223. :total="subordinatesTotal"
  224. layout="prev, pager, next, ->, jumper"
  225. small
  226. @current-change="handleSubordinatesPageChange"
  227. >
  228. <template #jumper>
  229. <span class="pagination-jumper">
  230. 前往
  231. <el-input-number
  232. v-model="subordinatesPage"
  233. :min="1"
  234. :max="Math.ceil(subordinatesTotal / subordinatesPageSize)"
  235. size="small"
  236. controls-position="right"
  237. style="width: 60px; margin: 0 4px"
  238. @change="(val: number | undefined) => val && handleSubordinatesPageChange(val)"
  239. />
  240. 页
  241. </span>
  242. </template>
  243. </el-pagination>
  244. </div>
  245. </div>
  246. </div>
  247. </el-scrollbar>
  248. <template #footer>
  249. <div class="dialog-footer">
  250. <el-button size="large" @click="handleClose">
  251. 关闭
  252. </el-button>
  253. </div>
  254. </template>
  255. </el-dialog>
  256. </template>
  257. <script setup lang="ts">
  258. import { ref, reactive, watch } from 'vue'
  259. import { ElMessage, ElMessageBox } from 'element-plus'
  260. interface UserInfo {
  261. name: string
  262. email: string
  263. company: string
  264. status: string
  265. totalCommission: number | string
  266. currentCommission: number | string
  267. promotionLink: string
  268. }
  269. interface PurchasedProduct {
  270. productName: string
  271. packageType: string
  272. remainingPoints?: string
  273. remainingTimes?: string | number
  274. expirationDate?: string
  275. purchaseDate: string
  276. status: string
  277. }
  278. interface SubordinatePersonnel {
  279. personnelName: string
  280. commissionAmount: number
  281. status: string
  282. bindingTime: string
  283. }
  284. const visible = ref(false)
  285. const userInfo = reactive<UserInfo>({
  286. name: '',
  287. email: '',
  288. company: '',
  289. status: '',
  290. totalCommission: '',
  291. currentCommission: '',
  292. promotionLink: '',
  293. })
  294. // 购买产品相关
  295. const purchasedProducts = ref<PurchasedProduct[]>([])
  296. const productsLoading = ref(false)
  297. const productsPage = ref(1)
  298. const productsPageSize = ref(10)
  299. const productsTotal = ref(0)
  300. // 下级人员相关
  301. const subordinatePersonnel = ref<SubordinatePersonnel[]>([])
  302. const subordinatesLoading = ref(false)
  303. const subordinateSearchName = ref('')
  304. const subordinatesPage = ref(1)
  305. const subordinatesPageSize = ref(10)
  306. const subordinatesTotal = ref(0)
  307. // 打开弹框
  308. const open = (row: any) => {
  309. // 设置用户信息
  310. userInfo.name = row.name || '张三'
  311. userInfo.email = row.email || '9854112544@qq.com'
  312. userInfo.company = row.company || '腾达'
  313. userInfo.status = '正常'
  314. userInfo.totalCommission = 500
  315. userInfo.currentCommission = 100
  316. userInfo.promotionLink = 'XXXX.com?from=用户ID'
  317. // 加载购买产品数据
  318. loadPurchasedProducts()
  319. // 加载下级人员数据
  320. loadSubordinatePersonnel()
  321. visible.value = true
  322. }
  323. // 加载购买产品数据
  324. const loadPurchasedProducts = () => {
  325. productsLoading.value = true
  326. // 模拟异步请求
  327. setTimeout(() => {
  328. const mockData: PurchasedProduct[] = [
  329. {
  330. productName: '聚德神笔',
  331. packageType: '日卡',
  332. remainingPoints: '--',
  333. remainingTimes: '--',
  334. expirationDate: '2025-10-11',
  335. purchaseDate: '2025-10-11',
  336. status: '已过期',
  337. },
  338. {
  339. productName: '棱镜',
  340. packageType: '棱镜10次卡',
  341. remainingPoints: '--',
  342. remainingTimes: 10,
  343. expirationDate: '--',
  344. purchaseDate: '2025-10-11',
  345. status: '正常',
  346. },
  347. ]
  348. purchasedProducts.value = mockData
  349. productsTotal.value = mockData.length
  350. productsLoading.value = false
  351. }, 300)
  352. }
  353. // 加载下级人员数据
  354. const loadSubordinatePersonnel = () => {
  355. subordinatesLoading.value = true
  356. // 模拟异步请求
  357. setTimeout(() => {
  358. let mockData: SubordinatePersonnel[] = [
  359. {
  360. personnelName: '王五',
  361. commissionAmount: 0,
  362. status: '正常',
  363. bindingTime: '2025-10-11',
  364. },
  365. {
  366. personnelName: '李四',
  367. commissionAmount: 10,
  368. status: '正常',
  369. bindingTime: '2025-10-11',
  370. },
  371. ]
  372. // 根据搜索条件过滤
  373. if (subordinateSearchName.value) {
  374. mockData = mockData.filter(item =>
  375. item.personnelName.includes(subordinateSearchName.value)
  376. )
  377. }
  378. subordinatePersonnel.value = mockData
  379. subordinatesTotal.value = mockData.length
  380. subordinatesLoading.value = false
  381. }, 300)
  382. }
  383. // 购买产品分页变化
  384. const handleProductsPageChange = (page: number) => {
  385. productsPage.value = page
  386. loadPurchasedProducts()
  387. }
  388. // 下级人员分页变化
  389. const handleSubordinatesPageChange = (page: number) => {
  390. subordinatesPage.value = page
  391. loadSubordinatePersonnel()
  392. }
  393. // 下级人员搜索
  394. const handleSubordinateSearch = () => {
  395. subordinatesPage.value = 1
  396. loadSubordinatePersonnel()
  397. }
  398. // 解除绑定
  399. const handleUnbind = (row: SubordinatePersonnel) => {
  400. ElMessageBox.confirm(
  401. `确定要解除与"${row.personnelName}"的绑定关系吗?`,
  402. '提示',
  403. {
  404. confirmButtonText: '确定',
  405. cancelButtonText: '取消',
  406. type: 'warning',
  407. }
  408. )
  409. .then(() => {
  410. ElMessage.success('解除绑定成功')
  411. loadSubordinatePersonnel()
  412. })
  413. .catch(() => {
  414. // 取消操作
  415. })
  416. }
  417. // 关闭弹框
  418. const handleClose = () => {
  419. visible.value = false
  420. // 重置数据
  421. subordinateSearchName.value = ''
  422. productsPage.value = 1
  423. subordinatesPage.value = 1
  424. }
  425. defineExpose({ open })
  426. </script>
  427. <style scoped lang="scss">
  428. .user-detail-dialog {
  429. .detail-wrapper {
  430. padding: 12px 16px 24px;
  431. }
  432. .detail-section {
  433. margin-bottom: 24px;
  434. &:last-child {
  435. margin-bottom: 0;
  436. }
  437. }
  438. .section-title {
  439. margin-bottom: 12px;
  440. font-weight: 600;
  441. font-size: 14px;
  442. color: #333;
  443. }
  444. .user-info-descriptions {
  445. :deep(.el-descriptions__label) {
  446. font-weight: 500;
  447. }
  448. }
  449. .search-wrapper {
  450. margin-bottom: 12px;
  451. }
  452. .pagination-wrapper {
  453. display: flex;
  454. justify-content: flex-end;
  455. margin-top: 12px;
  456. padding-top: 12px;
  457. .pagination-jumper {
  458. display: inline-flex;
  459. align-items: center;
  460. font-size: 13px;
  461. color: #606266;
  462. }
  463. }
  464. .dialog-footer {
  465. display: flex;
  466. justify-content: flex-end;
  467. align-items: center;
  468. }
  469. }
  470. </style>