183 Commits

Author SHA1 Message Date
lihui b84a4ee0fb refactor(coinConsumeDetail): 修改消耗总新币为消耗新币 3 days ago
ZhangYong c7ed9efc5b 金币导出统计取消/100 3 days ago
lihui f410363578 Merge branch 'refs/heads/lihui/feature-20250728114233-金币前端三期' into milestone-20250728-金币前端三期 3 days ago
lihui 1be2be64e2 fix(permissions): 重复调用 3 days ago
lihui 9c5deb22af Merge branch 'refs/heads/lihui/feature-20250728114233-金币前端三期' into milestone-20250728-金币前端三期 3 days ago
lihui f2f6870e56 fix(permissions): 重复调用 3 days ago
ZhangYong 9091970c8d Merge branch 'milestone-20250728-金币前端三期' of http://39.101.133.168:8807/huangqizhen/gold-vue into milestone-20250728-金币前端三期 4 days ago
ZhangYong fd5af5a229 金币取消/100 4 days ago
lihui 0eece10204 fix(audit): 时间显示逻辑 4 days ago
lihui 00dc0fa15a refactor(audit): 优化审核时间筛选显示 4 days ago
lihui 5187f704df feat(audit): 添加货币名称和充值金额 4 days ago
lihui 71a951e0ab fix: 总金额 => 总新币 4 days ago
lihui e4bb3dd8e9 fix: 优化时间筛选功能 4 days ago
lihui 5654c1cd4f refactor(audit): 修改退款审核中的管理员 ID 获取方式 5 days ago
ZhangYong ce3e5eff37 金币新增明细时间修改 5 days ago
lihui bce68e4b43 fix: 空值导致总计为空 5 days ago
lihui 795651239c fix: 空值导致总计为空 5 days ago
lihui 661f209703 fix(usergold): 修复金币数量显示逻辑 6 days ago
lihui 1aa365d60c fix(usergold): 修复金币数量显示错误 6 days ago
lihui f2ab8102c4 refactor(src): 全局拦截过期 6 days ago
ZhangYong b4a97d65e4 金币充值输入优化 6 days ago
ZhangYong c7b746dd6a Merge branch 'milestone-20250728-金币前端三期' of http://39.101.133.168:8807/huangqizhen/gold-vue into zhangyong/feature-20250716164232-金币前端 6 days ago
ZhangYong 425575c1f8 金豆输入校验优化 6 days ago
lihuilin a0a87b3dad 样式修改,表单精简 1 week ago
lihuilin 9888be00af 样式修改,表单精简 1 week ago
lihuilin 1e28905359 重置调方法+调整布局 1 week ago
ZhangYong 5d3adfa6d8 新增消耗修改 1 week ago
lihui 6e5d1070b8 feat(components): 密码跳转 1 week ago
lihuilin 3769e8a6c5 大数字加逗号Merge branch 'lihuilin/feature-20250730114922-金币三期' into milestone-20250728-金币前端三期 1 week ago
lihuilin a72bbebbf9 大数字加逗号 1 week ago
ZhangYong 421c509495 金豆明细序号 1 week ago
ZhangYong 8f3ffc3b28 金豆客户明细序号 1 week ago
lihui 505c3b0140 fix:金币余额输入筛选项后点击重置,再点击查询,除了筛选项包含地区外,点击查询页面没有反应 1 week ago
lihuilin 953d067c53 这是一次拉取branch 'milestone-20250728-金币前端三期' of http://39.101.133.168:8807/huangqizhen/gold-vue into lihuilin/feature-20250730114922-金币三期 1 week ago
lihui 7a9bed3adf fix:金币明细输入筛选项后点击重置,再点击查询,除了筛选项包含地区外,点击查询页面没有反应 1 week ago
lihuilin 9fb9a2075f 付费免费 1 week ago
ZhangYong 877fbac479 Merge branch 'zhangyong/feature-20250716164232-金币前端' into milestone-20250728-金币前端三期 1 week ago
ZhangYong 52168b3ef3 金豆字段修改 1 week ago
lihuilin 3f19a6e061 这是一次拉取 1 week ago
lihuilin 36afd2793f 付费金豆 1 week ago
lihuilin 13194bb736 付费哈哈哈哈 1 week ago
lihui 18d4051f69 永久金币和免费金币不能同时为0 1 week ago
lihui 7f358ccb26 永久金币和付费金币不能同时为0 1 week ago
ZhangYong 278329cd03 Merge branch 'zhangyong/feature-20250716164232-金币前端' into milestone-20250728-金币前端三期 1 week ago
ZhangYong 0da3fece75 金豆增减优化 1 week ago
ZhangYong 5d57fb5f03 Merge branch 'zhangyong/feature-20250716164232-金币前端' into milestone-20250728-金币前端三期 1 week ago
ZhangYong 78ed389236 金豆增减优化 1 week ago
ZhangYong a6b12231c6 Merge branch 'zhangyong/feature-20250716164232-金币前端' into milestone-20250728-金币前端三期 1 week ago
ZhangYong 64edd1a4e8 金豆增减优化 1 week ago
lihui ad659069d6 Merge branch 'refs/heads/lihui/feature-20250728114233-金币前端三期' into milestone-20250728-金币前端三期 1 week ago
lihui 213d1a075c fix:金币充值默认为0 1 week ago
lihuilin bbd4518488 Merge branch 'lihuilin/feature-20250730114922-金币三期' of http://39.101.133.168:8807/huangqizhen/gold-vue into milestone-20250728-金币前端三期 1 week ago
lihuilin 2bc8736bf4 接口,切换页面清排序 1 week ago
ZhangYong 1276e030c5 线上充值地区接口 1 week ago
lihuilin 92110e6dbd 1 week ago
lihuilin c34718bc50 研发/总部地区传空 1 week ago
zhangrenyuan d496402c8a fix reset of articleVideo.vue 1 week ago
zhangrenyuan 7837b6f597 Merge branch 'zhangrenyuan/feature-20250728113353-金币前端三期' into milestone-20250728-金币前端三期 1 week ago
zhangrenyuan 431c204874 fix bug 1 week ago
lihuilin 61c611b2cc 金豆审核做级联 1 week ago
zhangrenyuan 01d06e1842 Merge branch 'milestone-20250728-金币前端三期' of http://39.101.133.168:8807/huangqizhen/gold-vue into milestone-20250728-金币前端三期 1 week ago
zhangrenyuan d11ae90795 fix beanConsume card 1 week ago
lihui ac766ff0e2 fix: 近七天逻辑 1 week ago
zhangrenyuan 02f0171020 fix .env.dev and bug 1 week ago
zhangrenyuan 3b6cc29047 Merge branch 'milestone-20250728-金币前端三期' into zhangrenyuan/feature-20250728113353-金币前端三期 1 week ago
zhangrenyuan 7bada1f770 fix isBackpack column 1 week ago
zhangrenyuan 0ceea3fe11 Merge branch 'zhangrenyuan/feature-20250728113353-金币前端三期' into milestone-20250728-金币前端三期 1 week ago
zhangrenyuan 4e811c3f25 fix consume bug and feat backpack column 1 week ago
ZhangYong ddf3001fcb 金豆审核防抖 1 week ago
zhangrenyuan 78ef4f2b5e Merge branch 'zhangrenyuan/feature-20250728113353-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
zhangrenyuan e1faf8b3e3 fix consume 2 weeks ago
ZhangYong 53ff53d0f7 Merge branch 'zhangyong/feature-20250716164232-金币前端' into milestone-20250728-金币前端三期 2 weeks ago
ZhangYong 8db66e5f28 金豆充值优化 2 weeks ago
zhangrenyuan dd42ba3211 Merge branch 'milestone-20250728-金币前端三期' into zhangrenyuan/feature-20250728113353-金币前端三期 2 weeks ago
zhangrenyuan bf6890bab9 fix id2 and beanConsume 2 weeks ago
zhangrenyuan 1dbc4184b2 Merge branch 'zhangrenyuan/feature-20250728113353-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
zhangrenyuan 33b1890e92 fix id 2 weeks ago
lihui 8592749584 Merge branch 'refs/heads/milestone-20250728-金币前端三期' into lihui/feature-20250728114233-金币前端三期 2 weeks ago
lihui b3e5f31718 refactor(jwcode): 优化精网号验证逻辑 2 weeks ago
ZhangYong 3a6e6297f4 Merge branch 'zhangyong/feature-20250716164232-金币前端' into milestone-20250728-金币前端三期 2 weeks ago
ZhangYong adf214087a 金豆充值优化 2 weeks ago
lihui 65e622172d fix(menu): 菜单高亮功能 2 weeks ago
ZhangYong 8e38d899a4 Merge branch 'zhangyong/feature-20250716164232-金币前端' into milestone-20250728-金币前端三期 2 weeks ago
ZhangYong 64552afa85 金豆查询格式校验 2 weeks ago
lihui 7a45fb126f fix:格式校验OA 20位 精网 9位 2 weeks ago
lihui 4d09c15b01 Merge remote-tracking branch 'origin/milestone-20250728-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
lihui 6f3d8ebdc5 fix:格式校验 2 weeks ago
ZhangYong a3c54e13dc 金豆查询格式校验 2 weeks ago
lihui 0df94f49e7 Merge branch 'refs/heads/lihui/feature-20250728114233-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
lihui 633e3d2b7b fix:时间边界 2 weeks ago
ZhangYong 082653d8b8 系统充值金币总数问题修改 2 weeks ago
ZhangYong f87e2b08c7 Merge branch 'milestone-20250728-金币前端三期' of http://39.101.133.168:8807/huangqizhen/gold-vue into zhangyong/feature-20250716164232-金币前端 2 weeks ago
lihui a78aa9f781 fix:时间边界 2 weeks ago
lihui 97a4d06ec1 refactor:删除汇率的提交人 2 weeks ago
lihui ffe7138a27 fix:筛选问题 时间边界问题 2 weeks ago
ZhangYong a685302a17 Merge branch 'milestone-20250728-金币前端三期' of http://39.101.133.168:8807/huangqizhen/gold-vue into zhangyong/feature-20250716164232-金币前端 2 weeks ago
ZhangYong 740eef6951 huanjing wenjian 2 weeks ago
lihui d512c4f609 fix:权限金豆相关问题 2 weeks ago
lihui 6d2861bce2 refactor:优化金豆充值跳转 2 weeks ago
lihui e1a17f3637 fix: 重置 2 weeks ago
lihui e8110242c8 perf:打包不带日志 2 weeks ago
lihui 349e3f7aa7 地区 查询问题 2 weeks ago
lihui 4499310a19 refactor: 优化 地区筛选 总部 、 研发 置空 2 weeks ago
ZhangYong 7cfbe4707b Merge branch 'zhangyong/feature-20250716164232-金币前端' into milestone-20250728-金币前端三期 2 weeks ago
ZhangYong c1b26fc0ab youhua 2 weeks ago
zhangrenyuan 1de3ea9f77 Merge branch 'zhangrenyuan/feature-20250728113353-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
zhangrenyuan 34a5be0ee1 fix 注释annotation 2 weeks ago
ZhangYong 3ed3962eea 拉代码 2 weeks ago
ZhangYong d516291cba 充值导出修复 2 weeks ago
lihui 50c44e8fa7 Merge branch 'refs/heads/lihui/feature-20250728114233-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
lihui 6ea5157e98 refactor: 优化地区选择 2 weeks ago
lihui fcfe17d327 fix:金币充值审核的级联选择,工作台的优化 2 weeks ago
zhangrenyuan 68297c6188 fix fuzzy query 2 weeks ago
zhangrenyuan 07f5230473 修改.env.dev 2 weeks ago
zhangrenyuan 7ef0a2cc0e Merge branch 'zhangrenyuan/feature-20250728113353-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
zhangrenyuan 2b5e9f56d1 feat export of consume 2 weeks ago
lihui c3cf6ca467 Merge branch 'refs/heads/milestone-20250728-金币前端三期' into lihui/feature-20250728114233-金币前端三期 2 weeks ago
lihui 13e6d4106a refactor:删掉各个详情页面前端的映射,权限加上了金豆的过滤,只有总部可以点击 2 weeks ago
ZhangYong 728fa83dc6 Merge branch 'zhangyong/feature-20250716164232-金币前端' into milestone-20250728-金币前端三期 2 weeks ago
lihui 13467ba2ff refactor:地区级联 2 weeks ago
ZhangYong 14b014c202 导出 2 weeks ago
ZhangYong 16bdcc5fbb 导出弹窗列表样式优化 2 weeks ago
ZhangYong 8b31de3f6c Merge branch 'milestone-20250728-金币前端三期' of http://39.101.133.168:8807/huangqizhen/gold-vue into zhangyong/feature-20250716164232-金币前端 2 weeks ago
zhangrenyuan 9646e0b170 merge success liveStream, dieHardFan and articleVideo's page 2 weeks ago
zhangrenyuan b00cf54a89 completed liveStream dieHardFan and articleVidel's page 2 weeks ago
lihuilin ecd168d14b 审核人ok 2 weeks ago
lihuilin 9ea40369e0 合计数ok 2 weeks ago
lihui df77028a2e refactor:金币相关的时间选择器,修改选择也是到59秒的 2 weeks ago
lihui 9ef040d660 feat(noPermissionPage): 优化无权限页面 2 weeks ago
lihui 1d56545498 refactor(views):删除提交人展示 2 weeks ago
lihui ecd6876745 增加与tab的间距 2 weeks ago
ZhangYong fc98d6f78e 金豆新增界面分页,导航问题修复 2 weeks ago
lihui 4a147c9f43 refactor(consume): 单文件 2 weeks ago
lihui 0e7609e5c0 refactor(consume): 金豆消耗日期 2 weeks ago
zhangrenyuan 3a0565ec05 Merge branch 'zhangrenyuan/feature-20250728113353-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
zhangrenyuan 4c3fe8ed10 optimized today yesterday and last week's button reactive 2 weeks ago
ZhangYong c1ee4d2db8 系统,线上新增界面接口对接 2 weeks ago
ZhangYong 7a61868f7f 系统,线上新增界面接口对接 2 weeks ago
lihui 23326d4e6a Merge branch 'refs/heads/lihui/feature-20250728114233-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
lihui c6e7f101e3 refactor(beanConsume): 金豆消耗跳转 2 weeks ago
lihui cd37501a8c Merge branch 'refs/heads/lihui/feature-20250728114233-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
lihui 818ee9ce0b feat(permission): 添加汇率修改权限控制 2 weeks ago
lihui d626684070 Merge branch 'refs/heads/lihui/feature-20250728114233-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
lihui ae97b40e25 refactor:时间改为00:00:00-23:59:59 2 weeks ago
zhangrenyuan 235def1026 consume merge success 2 weeks ago
zhangrenyuan 700f4c1ec0 fix .env.dev 2 weeks ago
zhangrenyuan 073a21b246 fix liveStream dieHardFan articleVideo 2 weeks ago
lihui 06ef34d3cf feat 拆权限页面,无权限tab切换禁用 2 weeks ago
lihui c2f41d78cf feat(router): 添加权限ID配置 2 weeks ago
lihui ab93c31242 feat(router): 添加权限ID配置 2 weeks ago
lihui 9ba171db27 refactor:通过映射id 判断页面 2 weeks ago
lihui dc98a0ba29 feat:文件划分 2 weeks ago
lihui 7c6bc2b1d0 feat:对通过驳回加上权限验证 2 weeks ago
zhangrenyuan 8a9767f181 Merge branch 'zhangrenyuan/feature-20250728113353-金币前端三期' into milestone-20250728-金币前端三期 2 weeks ago
zhangrenyuan 0e01884eab feat 直播liveStream 铁粉dieHardFan all的接口interface and 文章视频articleVideo optimize 2 weeks ago
ZhangYong 2cc6066779 系统线上新增界面 2 weeks ago
ZhangYong 2a4404e7ea 线上以及系统新增界面 2 weeks ago
lihui 5e7ae67155 feat:级联 改了剩下的几个 ,还没改权限的 3 weeks ago
lihui a0aa79dde6 feat(recharge):级联 改了充值的,优化映射为动态映射 3 weeks ago
lihui ed438fb656 Merge branch 'refs/heads/milestone-20250728-金币前端三期' into lihui/feature-20250728114233-金币前端三期 3 weeks ago
ZhangYong f9c3de4234 金豆系统充值界面 3 weeks ago
zhangrenyuan 9453ba8e9c merge 3 weeks ago
zhangrenyuan 9aa5cb155c Merge branch 'zhangrenyuan/feature-20250728113353-金币前端三期' into milestone-20250728-金币前端三期 3 weeks ago
zhangrenyuan 07b5d4793b feat liveStream dieHardFan articleVideo searchTable 3 weeks ago
ZhangYong e2624383ff 金豆新增充值 3 weeks ago
ZhangYong 8eeaf604db 金豆新增充值 3 weeks ago
lihui 083b74675c feat: 将地区选择改为级联下拉框 3 weeks ago
zhangrenyuan bd62773cc3 Merge branch 'zhangrenyuan/feature-20250728113353-金币前端三期' into milestone-20250728-金币前端三期 3 weeks ago
zhangrenyuan 6d8ce3bc77 feat beanConsume liveStream 3 weeks ago
lihui dcd335b0ac fix:路由映射 3 weeks ago
lihui 0ecfcd0d23 refactor(login): 跳转第一个页面逻辑 3 weeks ago
lihui e2211fa360 Merge remote-tracking branch 'origin/milestone-20250728-金币前端三期' into milestone-20250728-金币前端三期 3 weeks ago
lihui 0baa0cd622 refactor(login): 跳转第一个页面逻辑 3 weeks ago
ZhangYong 8c99d3fc8a 新增金豆界面创建初始化 3 weeks ago
zhangrenyuan e1f5cb249f Merge branch 'zhangrenyuan/feature-20250728113353-金币前端三期' into milestone-20250728-金币前端三期 3 weeks ago
zhangrenyuan b1c40f2d77 feat(beanConsume):gold consume router include add,liveStream,dieHardFan,articleVideo 3 weeks ago
ZhangYong f939a3e6b1 新增界面创建 3 weeks ago
lihui 8d492ef8cb feat(权限管理): 权限控制逻辑 3 weeks ago
zhangrenyuan fbf6325e73 feat env.dev and merge 3 weeks ago
zhangrenyuan 79778be4cb feat:market tree-select 3 weeks ago
ZhangYong 210c397f25 角色编辑 3 weeks ago
ZhangYong 90056f7fc3 角色编辑修改 3 weeks ago
lihui 7a9056ce20 feat(menu): 更新菜单项和路由配置 3 weeks ago
  1. 13
      .env.development
  2. BIN
      src/assets/403.png
  3. 5
      src/components/PasswordSuccess.vue
  4. 25
      src/main.ts
  5. 152
      src/router/index.js
  6. 16
      src/store/index.js
  7. 14
      src/util/request.js
  8. 14
      src/utils/marketMap.js
  9. 100
      src/utils/menuTreePermission.js
  10. 64
      src/utils/menuUtils.js
  11. 452
      src/views/audit/beanAudit.vue
  12. 34
      src/views/audit/gold/audit.vue
  13. 621
      src/views/audit/gold/rechargeAudit.vue
  14. 337
      src/views/audit/gold/refundAudit.vue
  15. 691
      src/views/audit/rechargeAudit.vue
  16. 311
      src/views/consume/bean/addBeanConsume.vue
  17. 598
      src/views/consume/bean/articleVideo.vue
  18. 110
      src/views/consume/bean/beanConsume.vue
  19. 581
      src/views/consume/bean/dieHardFan.vue
  20. 627
      src/views/consume/bean/liveStream.vue
  21. 4
      src/views/consume/beanConsume.vue
  22. 101
      src/views/consume/coinConsume.vue
  23. 204
      src/views/consume/gold/addCoinConsume.vue
  24. 89
      src/views/consume/gold/coinConsume.vue
  25. 209
      src/views/consume/gold/coinConsumeDetail.vue
  26. 34
      src/views/home.vue
  27. 51
      src/views/login.vue
  28. 224
      src/views/managerecharge/rate.vue
  29. 111
      src/views/noPermissionPage.vue
  30. 1641
      src/views/permissions/permission.vue
  31. 89
      src/views/permissions/permissions.vue
  32. 868
      src/views/permissions/rolePermission.vue
  33. 1176
      src/views/permissions/userPermission.vue
  34. 297
      src/views/recharge/addBeanRecharge.vue
  35. 489
      src/views/recharge/beanOnlineRecharge.vue
  36. 97
      src/views/recharge/beanRecharge.vue
  37. 452
      src/views/recharge/beanSystemRecharge.vue
  38. 101
      src/views/recharge/coinRecharge.vue
  39. 210
      src/views/recharge/gold/addCoinRecharge.vue
  40. 89
      src/views/recharge/gold/coinRecharge.vue
  41. 263
      src/views/recharge/gold/coinRechargeDetail.vue
  42. 101
      src/views/refund/coinRefund.vue
  43. 170
      src/views/refund/gold/addCoinRefund.vue
  44. 89
      src/views/refund/gold/coinRefund.vue
  45. 342
      src/views/refund/gold/coinRefundDetail.vue
  46. 101
      src/views/usergold/clientCount.vue
  47. 89
      src/views/usergold/gold/clientCount.vue
  48. 205
      src/views/usergold/gold/clientCountBalance.vue
  49. 210
      src/views/usergold/gold/clientCountDetail.vue
  50. 199
      src/views/usergold/userbean.vue
  51. 117
      src/views/workspace/index.vue
  52. 2
      stats.html
  53. 3
      vite.config.ts

13
.env.development

@ -1,9 +1,16 @@
VITE_API_BASE='https://hwjb.homilychart.com/dev/admin' VITE_API_BASE='https://hwjb.homilychart.com/dev/admin'
# 测试环境 # 测试环境
# VITE_API_BASE='http://18.143.76.3:10704/'
# VITE_API_BASE='http://192.168.9.52:10705/'
# VITE_API_BASE='http://54.255.212.181:10704/'
# 正式环境
# VITE_API_BASE='http://54.255.212.181:10705/'
VITE_UPLOAD_URL=http://39.101.133.168:8828/hljw/api/aws/upload VITE_UPLOAD_URL=http://39.101.133.168:8828/hljw/api/aws/upload
# VITE_API_BASE='http://192.168.9.28:8081/' # VITE_API_BASE='http://192.168.9.28:8081/'
# 孙加倍
# sunjiabei
# VITE_API_BASE='http://192.168.9.28:8081/' # VITE_API_BASE='http://192.168.9.28:8081/'
# VITE_API_BASE='http://192.168.5.92:8081/' # VITE_API_BASE='http://192.168.5.92:8081/'
# zhangyong
# VITE_API_BASE='http://192.168.3.83:8081/'
# 本地
# VITE_API_BASE='http://localhost:8081/'
# sunjiabei
# VITE_API_BASE='http://192.168.0.113:8081/'

BIN
src/assets/403.png

After

Width: 1024  |  Height: 1024  |  Size: 17 KiB

5
src/components/PasswordSuccess.vue

@ -19,6 +19,7 @@ import {ElIcon, ElButton} from 'element-plus';
import {SuccessFilled} from '@element-plus/icons-vue'; import {SuccessFilled} from '@element-plus/icons-vue';
import {useRouter} from 'vue-router'; import {useRouter} from 'vue-router';
const machineId = localStorage.getItem("machineId");
const countdown = ref(3); const countdown = ref(3);
const router = useRouter(); const router = useRouter();
@ -32,7 +33,7 @@ const startCountdown = () => {
// //
// 使 window.location.href // 使 window.location.href
// window.location.href = '/login'; // window.location.href = '/login';
router.replace ('/login');
router.replace(`/login?machineId=${machineId || ''}`);
// router.push('/login'); // router.push('/login');
} }
}, 1000); }, 1000);
@ -41,7 +42,7 @@ const startCountdown = () => {
const immediateJump = () => { const immediateJump = () => {
// window.location.href = '/login'; // window.location.href = '/login';
// //
router.replace ('/login');
router.replace(`/login?machineId=${machineId || ''}`);
}; };
onMounted(() => { onMounted(() => {

25
src/main.ts

@ -12,16 +12,24 @@ import VxeUI from 'vxe-pc-ui'
import 'vxe-pc-ui/lib/style.css' import 'vxe-pc-ui/lib/style.css'
import VxeUITable from 'vxe-table' import VxeUITable from 'vxe-table'
import 'vxe-table/lib/style.css' import 'vxe-table/lib/style.css'
const a = createApp(App)
import { useAdminStore } from '../src/store'
// 修正导入路径
import { useAdminStore } from './store'
import request from "@/util/request";
const app = createApp(App)
const pinia = createPinia()
// 全局注册 ElementPlus 图标 // 全局注册 ElementPlus 图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) { for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
a.component(key, component)
app.component(key, component)
} }
const pinia = createPinia()
// 使用 ElementPlus 和路由器
a.use(ElementPlus, {
// 先注册组件再挂载
app.component('downloadExcel', JsonExcel)
app.config.globalProperties.$http = request
// 使用各种插件
app.use(ElementPlus, {
locale: zhCn locale: zhCn
}) })
.use(router) .use(router)
@ -30,9 +38,6 @@ a.use(ElementPlus, {
.use(pinia) .use(pinia)
.mount('#app') .mount('#app')
// 恢复localStorage数据
// 在 app 挂载之后再使用 store
const adminStore = useAdminStore() const adminStore = useAdminStore()
adminStore.initFromLocalStorage() adminStore.initFromLocalStorage()
// 注册 JsonExcel 组件
a.component('downloadExcel', JsonExcel)

152
src/router/index.js

@ -33,7 +33,7 @@ const routes = [
{ {
path: '/audit', path: '/audit',
name: "audit", name: "audit",
component: () => import("../views/audit/audit.vue"),
component: () => import("../views/audit/gold/audit.vue"),
meta: {permissionId: 40}, meta: {permissionId: 40},
// redirect: '/index', // redirect: '/index',
children: [ children: [
@ -41,17 +41,22 @@ const routes = [
{ {
path: 'rechargeAudit', path: 'rechargeAudit',
name: "rechargeAudit", name: "rechargeAudit",
component: () => import("../views/audit/rechargeAudit.vue"),
component: () => import("../views/audit/gold/rechargeAudit.vue"),
meta: {permissionId: [11, 12]} // 对应"查看充值审核"id=11、"充值审批"id=12 meta: {permissionId: [11, 12]} // 对应"查看充值审核"id=11、"充值审批"id=12
}, },
// 退款审核 // 退款审核
{ {
path: 'refundAudit', path: 'refundAudit',
name: "refundAudit", name: "refundAudit",
component: () => import("../views/audit/refundAudit.vue"),
component: () => import("../views/audit/gold/refundAudit.vue"),
meta: {permissionId: [13, 14]} // 对应"查看退款审核"id=13、"退款审批"id=14 meta: {permissionId: [13, 14]} // 对应"查看退款审核"id=13、"退款审批"id=14
}, },
] ]
},{
path: 'beanAudit',
name: "beanAudit",
component: () => import("../views/audit/beanAudit.vue"),
meta: {permissionId: [54,55]}
}, },
@ -59,7 +64,7 @@ const routes = [
{ {
path: '/coinConsume', path: '/coinConsume',
name: "coinConsume", name: "coinConsume",
component: () => import("../views/consume/coinConsume.vue"),
component: () => import("../views/consume/gold/coinConsume.vue"),
// redirect: '/coinConsume/add', // redirect: '/coinConsume/add',
meta: {permissionId: 6}, meta: {permissionId: 6},
children: [ children: [
@ -67,18 +72,56 @@ const routes = [
{ {
path: 'add', path: 'add',
name: "addCoinConsume", name: "addCoinConsume",
component: () => import("../views/consume/addCoinConsume.vue"),
component: () => import("../views/consume/gold/addCoinConsume.vue"),
meta: {permissionId: 19} // 对应"提交金币消耗"id=19 meta: {permissionId: 19} // 对应"提交金币消耗"id=19
}, },
// 金币消耗明细详情 // 金币消耗明细详情
{ {
path: 'detail', path: 'detail',
name: "coinConsumeDetail", name: "coinConsumeDetail",
component: () => import("../views/consume/coinConsumeDetail.vue"),
component: () => import("../views/consume/gold/coinConsumeDetail.vue"),
meta: {permissionId: 20} // 对应"查看金币消耗明细"id=20 meta: {permissionId: 20} // 对应"查看金币消耗明细"id=20
} }
] ]
}, },
// 金豆消耗
{
path: '/beanConsume',
name: "beanConsume",
component: () => import("../views/consume/bean/beanConsume.vue"),
meta: {permissionId: 42},
children: [
// 金豆新增消耗
{
path: 'add',
name: "addBeanConsume",
component: () => import("../views/consume/bean/addBeanConsume.vue"),
meta: {permissionId: 49} // 对应"提交金豆消耗"id=49
},
// 直播
{
path: 'live',
name: "liveStream",
component: () => import("../views/consume/bean/liveStream.vue"),
meta: {permissionId: 50} // 对应"直播"id=50
},
// 铁粉
{
path: 'fan',
name: "dieHardFan",
component: () => import("../views/consume/bean/dieHardFan.vue"),
meta: {permissionId: 51} // 对应"铁粉"id=51
},
// 文章视频
{
path: 'article',
name: "articleVideo",
component: () => import("../views/consume/bean/articleVideo.vue"),
meta: {permissionId: 52} // 对应"文章视频"id=52
}
]
},
// 汇率管理 // 汇率管理
{ {
@ -91,31 +134,62 @@ const routes = [
{ {
path: '/coinRecharge', path: '/coinRecharge',
name: "coinRecharge", name: "coinRecharge",
component: () => import("../views/recharge/coinRecharge.vue"),
component: () => import("../views/recharge/gold/coinRecharge.vue"),
// redirect: '/coinRecharge/add', // redirect: '/coinRecharge/add',
children: [ children: [
// 金币新增充值 // 金币新增充值
{ {
path: 'add', path: 'add',
name: "addCoinRecharge", name: "addCoinRecharge",
component: () => import("../views/recharge/addCoinRecharge.vue"),
component: () => import("../views/recharge/gold/addCoinRecharge.vue"),
meta: {permissionId: 17} // 对应"提交金币充值"id=17 meta: {permissionId: 17} // 对应"提交金币充值"id=17
}, },
// 金币充值明细详情 // 金币充值明细详情
{ {
path: 'detail', path: 'detail',
name: "coinRechargeDetail", name: "coinRechargeDetail",
component: () => import("../views/recharge/coinRechargeDetail.vue"),
component: () => import("../views/recharge/gold/coinRechargeDetail.vue"),
meta: {permissionId: 18} // 对应"查看金币充值明细"id=18 meta: {permissionId: 18} // 对应"查看金币充值明细"id=18
} }
] ]
}, },
// 金豆充值
{
path: '/beanRecharge',
name: "beanRecharge",
component: () => import("../views/recharge/beanRecharge.vue"),
// redirect: '/coinRecharge/add',
children: [
// 金豆新增充值
{
path: 'add',
name: "addBeanRecharge",
component: () => import("../views/recharge/addBeanRecharge.vue"),
meta: {permissionId: 46} // 对应"提交金豆充值"id=46
},
// 金豆系统充值
{
path: 'system',
name: "beanSystemRecharge",
component: () => import("../views/recharge/beanSystemRecharge.vue"),
meta: {permissionId: 47} // 对应"查看金豆系统充值明细"id=47
},
// 金豆线上充值
{
path: 'online',
name: "beanOnlineRecharge",
component: () => import("../views/recharge/beanOnlineRecharge.vue"),
meta: {permissionId: 48} // 对应"查看金豆线上充值明细"id=48
}
]
},
// 金币退款 // 金币退款
{ {
path: '/coinRefund', path: '/coinRefund',
name: "coinRefund", name: "coinRefund",
component: () => import("../views/refund/coinRefund.vue"),
component: () => import("../views/refund/gold/coinRefund.vue"),
// redirect: '/coinRefund/add', // redirect: '/coinRefund/add',
meta: {permissionId: 7}, meta: {permissionId: 7},
children: [ children: [
@ -123,14 +197,14 @@ const routes = [
{ {
path: 'add', path: 'add',
name: "addCoinRefund", name: "addCoinRefund",
component: () => import("../views/refund/addCoinRefund.vue"),
component: () => import("../views/refund/gold/addCoinRefund.vue"),
meta: {permissionId: 21} // 对应"提交金币退款"id=21 meta: {permissionId: 21} // 对应"提交金币退款"id=21
}, },
// 金币退款明细详情 // 金币退款明细详情
{ {
path: 'detail', path: 'detail',
name: "coinRefundDetail", name: "coinRefundDetail",
component: () => import("../views/refund/coinRefundDetail.vue"),
component: () => import("../views/refund/gold/coinRefundDetail.vue"),
meta: {permissionId: 22} // 对应"查看金币退款明细"id=22 meta: {permissionId: 22} // 对应"查看金币退款明细"id=22
} }
] ]
@ -140,7 +214,7 @@ const routes = [
{ {
path: '/usergold', path: '/usergold',
name: "usergold", name: "usergold",
component: () => import("../views/usergold/clientCount.vue"),
component: () => import("../views/usergold/gold/clientCount.vue"),
// redirect: '/usergold/detail', // redirect: '/usergold/detail',
meta: {permissionId: 8}, meta: {permissionId: 8},
children: [ children: [
@ -148,25 +222,49 @@ const routes = [
{ {
path: 'detail', path: 'detail',
name: "clientCountDetail", name: "clientCountDetail",
component: () => import("../views/usergold/clientCountDetail.vue"),
component: () => import("../views/usergold/gold/clientCountDetail.vue"),
meta: {permissionId: 23} // 对应"查看金币明细"id=23 meta: {permissionId: 23} // 对应"查看金币明细"id=23
}, },
// 金币余额 // 金币余额
{ {
path: 'balance', path: 'balance',
name: "clientCountBalance", name: "clientCountBalance",
component: () => import("../views/usergold/clientCountBalance.vue"),
component: () => import("../views/usergold/gold/clientCountBalance.vue"),
meta: {permissionId: 24} // 对应"查看金币余额"id=24 meta: {permissionId: 24} // 对应"查看金币余额"id=24
}, },
] ]
}, },
{
path: 'userbean',
name: "userbean",
component: () => import("../views/usergold/userbean.vue"),
meta:{ permissionId: 45 }
},
// 权限管理 // 权限管理
{ {
path: '/permissions', path: '/permissions',
name: "permissions", name: "permissions",
component: () => import("../views/permissions/permission.vue"),
meta: {permissionId: [25, 26, 27, 28, 29]} // 对应权限管理下的所有操作
component: () => import("../views/permissions/permissions.vue"),
meta: {permissionId: 9},
children: [
// 用户权限
{
path: 'userPermission',
name: "userPermission",
component: () => import("../views/permissions/userPermission.vue"),
meta: {permissionId: 25}
}, },
// 角色权限
{
path: 'rolePermission',
name: "rolePermission",
component: () => import("../views/permissions/rolePermission.vue"),
meta: {permissionId: 30}
},
]
},
// 没有权限 // 没有权限
{ {
path: '/noPermission', path: '/noPermission',
@ -189,23 +287,6 @@ const router = createRouter({
routes routes
}); });
// 全局拦截器:token过期处理
axios.interceptors.response.use(
response => response,
error => {
if (error.response && error.response.status === 401) {
localStorage.removeItem('token');
router.push({
name: 'login',
query: {
machineId: localStorage.getItem('machineId'),
expired: true
}
});
}
return Promise.reject(error);
}
);
// 工具函数:从菜单树提取所有权限ID // 工具函数:从菜单树提取所有权限ID
const getAllPermissionIds = (menuTree) => { const getAllPermissionIds = (menuTree) => {
@ -223,7 +304,6 @@ const getAllPermissionIds = (menuTree) => {
}; };
// 全局路由守卫 // 全局路由守卫
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
@ -244,7 +324,7 @@ router.beforeEach(async (to, from, next) => {
// 获取管理员信息 // 获取管理员信息
let roleId = null; let roleId = null;
console.log('adminData:', adminData)
console.log('路由的adminData:', adminData.value)
try { try {
roleId = adminData.value.roleId; roleId = adminData.value.roleId;
if (!roleId) { if (!roleId) {

16
src/store/index.js

@ -4,7 +4,8 @@ import { defineStore } from 'pinia'
export const useAdminStore = defineStore('admin', { export const useAdminStore = defineStore('admin', {
state: () => ({ state: () => ({
adminData: null, // 用户信息 adminData: null, // 用户信息
menuTree: null, // 菜单权限树
menuTree: [], // 菜单权限树
marketList: {}, // 市场列表
}), }),
actions: { actions: {
// 设置用户信息并同步到localStorage // 设置用户信息并同步到localStorage
@ -18,11 +19,16 @@ export const useAdminStore = defineStore('admin', {
this.menuTree = tree this.menuTree = tree
localStorage.setItem('menuTree', JSON.stringify(tree)) localStorage.setItem('menuTree', JSON.stringify(tree))
}, },
setMarketList(list) {
this.marketList = list
localStorage.setItem('marketList', JSON.stringify(list))
},
// 从localStorage初始化数据 // 从localStorage初始化数据
initFromLocalStorage() { initFromLocalStorage() {
const adminData = localStorage.getItem('adminData') const adminData = localStorage.getItem('adminData')
const menuTree = localStorage.getItem('menuTree') const menuTree = localStorage.getItem('menuTree')
const marketList = localStorage.getItem('marketList')
if (adminData) { if (adminData) {
this.adminData = JSON.parse(adminData) this.adminData = JSON.parse(adminData)
@ -31,14 +37,20 @@ export const useAdminStore = defineStore('admin', {
if (menuTree) { if (menuTree) {
this.menuTree = JSON.parse(menuTree) this.menuTree = JSON.parse(menuTree)
} }
if (marketList) {
this.marketList = JSON.parse(marketList)
}
}, },
// 清空状态并移除localStorage数据 // 清空状态并移除localStorage数据
clearState() { clearState() {
this.adminData = null this.adminData = null
this.menuTree = null
this.menuTree = []
this.marketList = {}
localStorage.removeItem('adminData') localStorage.removeItem('adminData')
localStorage.removeItem('menuTree') localStorage.removeItem('menuTree')
localStorage.removeItem('marketList')
// localStorage.removeItem('token') // localStorage.removeItem('token')
} }
} }

14
src/util/request.js

@ -44,13 +44,13 @@ service.interceptors.response.use(
return response return response
}, },
error => { error => {
// const { response } = error
// if (response && response.status === 401) {
// const machineId = localStorage.getItem('machineId')
// localStorage.removeItem('token')
// window.location.href = `/login?machineId=${machineId}`
// return Promise.resolve({ needsLogin: true })
// }
const { response } = error
if (response && response.status === 401) {
const machineId = localStorage.getItem('machineId')
localStorage.removeItem('token')
window.location.href = `#/login?machineId=${machineId}`
return Promise.resolve({ needsLogin: true })
}
return Promise.reject(error) return Promise.reject(error)
} }
) )

14
src/utils/marketMap.js

@ -0,0 +1,14 @@
import {storeToRefs} from 'pinia';
import {useAdminStore} from '@/store/index.js';
const adminStore = useAdminStore();
const {marketList} = storeToRefs(adminStore);
// 地区映射
export const marketMapping = marketList.value;
console.log('marketList:', marketList)
// 反向映射
export const reverseMarketMapping = Object.entries(marketMapping).reduce((acc, [key, value]) => {
acc[value] = key;
return acc;
}, {});

100
src/utils/menuTreePermission.js

@ -0,0 +1,100 @@
// 菜单权限映射(按 menu_type 排序)
export const permissionMapping = {
System_Management: 1, // 系统管理
// menu_type 2: 主功能菜单
Workbench: 2, // 工作台
Financial_Audit: 3, // 财务审核
Exchange_Rate_Management: 4, // 汇率管理
Recharge_Management: 5, // 充值管理
Consumption_Management: 6, // 消耗管理
Refund_Management: 7, // 退款管理
Customer_Account_Details: 8, // 客户账户明细
Permission_Management: 9, // 权限管理
// menu_type 3: 子功能菜单
Gold_Coin_Recharge: 34, // 金币充值
Gold_Coin_Consumption: 35, // 金币消耗
Gold_Coin_Refund: 37, // 金币退款
Gold_Coin_Audit: 40, // 金币审核
Golden_Bean_Recharge: 41, // 金豆充值
Golden_Bean_Consumption: 42, // 金豆消耗
Golden_Bean_Audit: 43, // 金豆审核
Gold_Coin_Customer_Account_Details: 44, // 金币客户账户明细
Golden_Bean_Customer_Account_Details: 45, // 金豆客户账户明细
// menu_type 4: 功能操作权限
Workbench_Display: 10, // 工作台展示
View_Recharge_Audit: 11, // 查看充值审核 // 有这个页面权限的就有
Recharge_Approval: 12, // 充值审批 //细致划分
View_Refund_Audit: 13, // 查看退款审核 // 有这个页面权限的就有
Refund_Approval: 14, // 退款审批 //细致划分
Exchange_Rate_View: 15, // 汇率查看
Exchange_Rate_Modification: 16, // 汇率修改
Submit_Gold_Coin_Recharge: 17, // 提交金币充值 // coinRecharge页面
View_Gold_Coin_Recharge_Details: 18, // 查看金币充值明细 // coinRecharge页面
Submit_Gold_Coin_Consumption: 19, // 提交金币消耗 // coinConsume页面
View_Gold_Coin_Consumption_Details: 20, // 查看金币消耗明细 // coinConsume页面
Submit_Gold_Coin_Refund: 21, // 提交金币退款 // coinRefund页面
View_Gold_Coin_Refund_Details: 22, // 查看金币退款明细 // coinRefund页面
View_Gold_Coin_Details: 23, // 查看金币明细 //usergold页面
View_Gold_Coin_Balance: 24, // 查看金币余额 //usergold页面
View_Permission: 25, // 查看权限
Add_User: 26, // 新增用户
Change_Status: 27, // 改变状态
Modify_Permission: 28, // 修改权限
Delete_User: 29, // 删除用户
View_Role: 30, // 查看角色
Edit_Role: 36, // 编辑角色
Recharge_Audit: 31, // 充值审核(金币) // audit页面
Refund_Audit: 32, // 退款审核(金币) // audit页面,
// 新增的金豆相关权限
Submit_Golden_Bean_Recharge: 46, // 提交金豆充值
View_Golden_Bean_System_Recharge_Details: 47, // 查看金豆系统充值明细
View_Golden_Bean_Online_Recharge_Details: 48, // 查看金豆线上充值明细
Submit_Golden_Bean_Consumption: 49, // 提交金豆消耗
View_Golden_Bean_Live_Consumption_Details: 50, // 查看金豆直播消耗明细
View_Golden_Bean_Fan_Consumption_Details: 51, // 查看金豆铁粉消耗明细
View_Golden_Bean_Article_Video_Consumption_Details: 52, // 查看金豆文章/视频消耗明细
View_Golden_Bean_Balance: 53, // 查看金豆余额
View_Golden_Bean_Recharge_Audit: 54, // 查看金豆充值审核
Golden_Bean_Recharge_Approval: 55 // 金豆充值审批
};
// 递归查找菜单中是否存在目标id
export const findMenuById = (menuList, targetId) => {
for (const menu of menuList) {
if (menu.id === targetId) {
return true; // 找到目标菜单
}
// 如果有子菜单,递归查找
if (menu.children && menu.children.length > 0) {
const found = findMenuById(menu.children, targetId);
if (found) return true;
}
}
return false;
};
// 递归判断某个 menuId 是否存在
export const hasMenuPermission = (tree, targetId) => {
for (const node of tree) {
console.log(node.id)
if (node.id === targetId) return true;
if (node.children && hasMenuPermission(node.children, targetId)) return true;
}
return false;
};

64
src/utils/menuUtils.js

@ -7,56 +7,68 @@ export function filterMenu(menuList) {
...menu, ...menu,
children: menu.children ? filterMenu(menu.children) : [] children: menu.children ? filterMenu(menu.children) : []
})) }))
.sort((a, b) => a.priority - b.priority); // 按 id 升序
.sort((a, b) => a.id - b.id); // 按 id 升序
}
// 过滤 只获得第三级的菜单
export function filterFirstMenu(menuList) {
return menuList
.map(menu => ({
...menu,
children: menu.children ? filterMenu(menu.children) : []
}))
.sort((a, b) => a.id - b.id); // 按 id 升序
} }
// 辅助函数:查找第一个可访问的菜单项 // 辅助函数:查找第一个可访问的菜单项
export function findFirstAccessibleMenu(menuList) {
if (!menuList || menuList.length === 0) return null
export function findFirstThirdLevelMenu(menuList) {
if (!menuList || menuList.length === 0) return null;
for (const menu of menuList) { for (const menu of menuList) {
if (menu.menuType === 1) { // 根
const childResult = findFirstAccessibleMenu(menu.children)
if (childResult) return childResult
} else if (menu.menuType === 2) { // 目录
return menu
} else if (menu.menuType === 3) { // 菜单
return menu
// 先检查当前菜单是否为三级菜单
if (menu.menuType === 3) {
return menu;
}
// 若不是,递归查找其子菜单(无论当前菜单是几级,都深入子菜单找三级)
const childResult = findFirstThirdLevelMenu(menu.children);
if (childResult) {
return childResult;
} }
} }
return null
return null;
} }
// 路由映射
// 路由映射(左侧菜单栏)
export const getRoutePath = (menu) => { export const getRoutePath = (menu) => {
// 路由映射表:key为接口menuName,value为对应路由路径 // 路由映射表:key为接口menuName,value为对应路由路径
const routeMap = { const routeMap = {
'工作台': '/workspace',
'审核页面': '/audit',
'财务审核': '/audit',
'工作台展示': '/workspace',
'充值审核': '/audit/rechargeAudit',
'退款审核': '/audit/refundAudit',
'金币审核': '/audit',
'金豆审核': '/beanAudit',
'汇率管理': '/rate', '汇率管理': '/rate',
'消耗管理': '/coinConsume',
'消耗页面': '/coinConsume',
'权限管理': '/permissions',
'金币充值': '/coinRecharge',
'金豆充值': '/beanRecharge',
'充值管理': '/coinRecharge',
'金币消耗': '/coinConsume',
'金豆消耗': '/beanConsume',
'充值页面': '/coinRecharge',
'退款管理': '/coinRefund',
'退款页面': '/coinRefund',
'金币退款': '/coinRefund',
// '金豆退款': '/beanRefund',
'权限管理': '/permissions',
'客户账户明细': '/usergold',
'金币客户账户明细': '/usergold',
'金豆客户账户明细': '/userbean',
}; };
// 未匹配的菜单默认使用id作为路由(可根据实际需求调整) // 未匹配的菜单默认使用id作为路由(可根据实际需求调整)
return routeMap[menu.menuName] || '/noPermissionPage'
return routeMap[menu.menuName] || '/noPermission'
} }

452
src/views/audit/beanAudit.vue

@ -0,0 +1,452 @@
<template>
<el-card style="margin-bottom: 0.5vh;width:82.5vw">
<el-col style="margin-bottom: 1vh">
<el-text size="large">精网号</el-text>
<el-input v-model="searchForm.jwcode" placeholder="请输入精网号" style="width: 12vw;margin-right:1vw" clearable />
<el-text class="mx-1" size="large">所属地区</el-text>
<el-cascader v-model="selectedMarkets" :options="marketOptions" placeholder="请选择所属地区" clearable
style="width: 12vw" @change="handleMarketChange" />
</el-col>
<el-col>
<el-text size="large" style="width: 80px">充值时间</el-text>
<el-date-picker v-model="dateRange" type="datetimerange" range-separator="" start-placeholder="开始时间"
end-placeholder="结束时间" style="width: 25vw;margin-right:1vw" @change="handleDatePickerChange"
:default-time="defaultTime" />
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''"></el-button>
<el-button @click="getYesterday()" :type="activeTimeRange === 'yesterday' ? 'primary' : ''"></el-button>
<el-button @click="get7Days()" :type="activeTimeRange === '7days' ? 'primary' : ''">近7天</el-button>
<el-button type="success" @click="resetSearch">重置</el-button>
<el-button type="primary" @click="handleSearch">查询</el-button>
</el-col>
</el-card>
<el-card>
<el-tabs v-model="activeName" type="card" @tab-click="handleClick">
<el-tab-pane label="待审核" name="wait"></el-tab-pane>
<el-tab-pane label="已通过" name="pass"></el-tab-pane>
<el-tab-pane label="已驳回" name="reject"></el-tab-pane>
</el-tabs>
<div>
总条数{{ format3(stats.num) }}&nbsp;&nbsp;&nbsp;&nbsp;
总金豆数{{ format3(stats.beanNum) }}金豆&nbsp;&nbsp;&nbsp;&nbsp;
付费金豆{{ format3(stats.permanentBean) }}金豆&nbsp;&nbsp;&nbsp;&nbsp;
免费金豆{{ format3(stats.freeBean) }}金豆
</div>
<el-table :data="tableData" height="540px" @sort-change="handleSortChange">
<el-table-column prop="id" label="序号" width="80" />
<el-table-column prop="name" label="姓名" width="120" show-overflow-tooltip />
<el-table-column prop="jwcode" label="精网号" width="120" />
<el-table-column prop="market" label="所属地区" width="120" />
<el-table-column prop="permanentBean" label="付费金豆" width="120" sortable="custom" />
<el-table-column prop="freeBean" label="免费金豆" width="120" sortable="custom" />
<el-table-column prop="remark" label="备注" width="150" show-overflow-tooltip />
<el-table-column prop="submitName" label="提交人" width="120" />
<el-table-column v-if="checkTab === 'reject'" prop="reason" label="驳回理由" width="120" show-overflow-tooltip />
<el-table-column v-if="checkTab !== 'pending'" prop="auditName" label="审核人" width="120" />
<el-table-column prop="createTime" label="提交时间" width="180" sortable="custom">
<template #default="{ row }">
{{ moment(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column v-if="checkTab !== 'pending'" prop="auditTime" label="审核时间" width="240" sortable="custom">
<template #default="{ row }">
{{ row.auditTime ? moment(row.auditTime).format('YYYY-MM-DD HH:mm:ss') : '--' }}
</template>
</el-table-column>
<el-table-column v-if="checkTab === 'pending'" fixed="right" prop="operation" label="操作" width="400px">
<template #default="scope">
<div class="operation">
<el-popconfirm title="确定要通过此条记录吗?" @confirm="throttledHandleApprove(scope.row)">
<template #reference>
<el-button :disabled="scope.row.status === 1 || scope.row.status === 2" type="primary" text>
通过
</el-button>
</template>
</el-popconfirm>
<el-button :disabled="scope.row.status === 1 || scope.row.status === 2" type="primary" text
@click="showRejectDialog(scope.row)">
驳回
</el-button>
</div>
</template>
</el-table-column>
</el-table>
<el-pagination style="margin-top:20px" v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="stats.num"
@size-change="handlePageSizeChange" @current-change="handleCurrentChange"></el-pagination>
</el-card>
<el-dialog v-model="rejectVisible" title="驳回理由" width="500px">
<el-form>
<el-form-item label="驳回理由" required>
<el-input v-model="reason" type="textarea" :rows="4" placeholder="请输入驳回理由" maxlength="200" show-word-limit />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="rejectVisible = false">取消</el-button>
<el-button type="primary" @click="handleReject()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import API from '@/util/http'
import moment from 'moment'
import { useAdminStore } from "@/store/index.js";
import { storeToRefs } from "pinia";
import _ from 'lodash'
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
const adminStore = useAdminStore();
const { adminData, menuTree } = storeToRefs(adminStore);
import { permissionMapping, findMenuById } from "@/utils/menuTreePermission.js"
import dayjs from "dayjs";
const tableData = ref([])
const marketOptions = ref("")
const dateRange = ref([])
const activeTimeRange = ref('')
const activeName = ref('wait')
const sortField = ref('')
const sortOrder = ref('')
const checkTab = ref('pending')
const rejectVisible = ref(false)
const reason = ref('')
const rejectRow = ref({
id: null
})//
//
const STATUS = {
PENDING: 0, //
APPROVED: 1, //
REJECTED: 2 //
}
const searchForm = ref({
jwcode: '',
market: '',
createStartTime: '',
createEndTime: '',
status: STATUS.PENDING,
auditStartTime: '',
auditEndTime: ''
})
const pagination = ref({
pageNum: 1,
pageSize: 50
})
//
const stats = ref({
num: 0,
beanNum: 0,
permanentBean: 0,
freeBean: 0
})
//
const selectedMarkets = ref("")
const handleSortChange = (column) => {
if (column.prop === 'permanentBean') {
sortField.value = 'permanentBean'
} else if (column.prop === 'freeBean') {
sortField.value = 'freeBean'
} else if (column.prop === 'createTime') {
sortField.value = 'createTime'
} else if (column.prop === 'auditTime') {
sortField.value = 'auditTime'
} else {
sortField.value = ''
}
sortOrder.value = column.order === 'ascending' ? 'ASC' : 'DESC'
console.log('排序字段:', sortField.value)
console.log('排序方式:', sortOrder.value)
get()
}
const handleSearch = function () {
trimJwCode()
if (searchForm.value.jwcode) {
const numRef = /^\d{1,9}$/;
if (!numRef.test(searchForm.value.jwcode)) {
ElMessage.error('请检查精网号格式')
return
}
}
get()
getStats()
}
const get = async function () {
if (findMenuById(menuTree.value, permissionMapping.View_Golden_Bean_Recharge_Audit)) {
try {
if (dateRange.value && dateRange.value.length === 2) {
searchForm.value.createStartTime = moment(dateRange.value[0]).format('YYYY-MM-DD HH:mm:ss')
searchForm.value.createEndTime = moment(dateRange.value[1]).format('YYYY-MM-DD HH:mm:ss')
} else {
searchForm.value.createStartTime = ''
searchForm.value.createEndTime = ''
}
if (searchForm.value.market === '总部' || searchForm.value.market === '研发部') {
searchForm.value.market = '';
}
const params = {
pageNum: pagination.value.pageNum,//
pageSize: pagination.value.pageSize,//
beanAuditInfo: {
jwcode: searchForm.value.jwcode,
status: searchForm.value.status,
market: searchForm.value.market,
createStartTime: searchForm.value.createStartTime,
createEndTime: searchForm.value.createEndTime,
auditStartTime: searchForm.value.auditStartTime,
auditEndTime: searchForm.value.auditEndTime,
sortField: sortField.value,
sortOrder: sortOrder.value
}
}
console.log('看看传给后端的参数:', params)
const res = await API({ url: '/beanAudit/selectBy', data: params })
tableData.value = res.data.list || []
} catch (error) {
console.error('获取数据失败', error)
}
} else {
ElMessage.error('无此权限')
}
}
const getStats = async () => {
if (findMenuById(menuTree.value, permissionMapping.View_Golden_Bean_Recharge_Audit)) {
try {
const params = {
jwcode: searchForm.value.jwcode,
status: searchForm.value.status,
market: searchForm.value.market,
createStartTime: searchForm.value.createStartTime,
createEndTime: searchForm.value.createEndTime,
auditStartTime: searchForm.value.auditStartTime,
auditEndTime: searchForm.value.auditEndTime
}
const res = await API({
url: '/beanAudit/statsBean',
data: params
})
stats.value.num = res.data.num
stats.value.permanentBean = res.data.permanentBean
stats.value.freeBean = res.data.freeBean
stats.value.beanNum = res.data.beanNum
console.log('see see stats和搜索对象', stats.value, params)
} catch (error) {
console.log('请求失败', error)
}
} else {
ElMessage.error('无此权限')
}
}
//
const handleApprove = async (row) => {
if (findMenuById(menuTree.value, permissionMapping.Golden_Bean_Recharge_Approval)) {
try {
const params = {
id: row.id,
auditName: adminData.value.adminName
}
await API({ url: '/beanAudit/status1', data: params })
ElMessage.success('审核通过成功')
get()
getStats()
} catch (error) {
console.error('审核通过失败', error)
ElMessage.error('操作失败')
}
} else {
ElMessage.error('无此权限')
}
}
//
const handleReject = async () => {
if (findMenuById(menuTree.value, permissionMapping.Golden_Bean_Recharge_Approval)) {
if (!reason.value.trim()) {
ElMessage.warning('请输入驳回理由')
return
}
try {
const params = {
id: rejectRow.value.id,
auditName: adminData.value.adminName,
reason: reason.value
}
await API({ url: '/beanAudit/status2', data: params })
ElMessage.success('驳回成功')
rejectVisible.value = false
get()
getStats()
} catch (error) {
console.error('驳回失败', error)
ElMessage.error('操作失败')
}
} else {
ElMessage.error('无此权限')
}
}
const getToday = function () {
const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime]
console.log('dateRange', dateRange.value)
activeTimeRange.value = 'today'
get()
getStats()
}
const getYesterday = function () {
const today = dayjs()
const startTime = today.subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime]
console.log('dateRange', dateRange.value)
activeTimeRange.value = 'yesterday'
get()
getStats()
}
const get7Days = function () {
const today = dayjs()
const startTime = today.subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime]
console.log('dateRange', dateRange.value)
activeTimeRange.value = '7days'
get()
getStats()
}
const resetSearch = function () {
const status = searchForm.value.status;
searchForm.value = {
jwcode: '',
market: '',
createStartTime: '',
createEndTime: '',
status: status,
auditStartTime: '',
auditEndTime: ''
}
selectedMarkets.value = []
dateRange.value = []
activeTimeRange.value = ''
get()
getStats()
}
const handleClick = function (tab) {
resetSearch()
activeName.value = tab.props.name
if (tab.props.name === 'wait') {
adminWait()
} else if (tab.props.name === 'pass') {
adminPass()
} else if (tab.props.name === 'reject') {
adminReject()
}
}
//
const adminWait = async function () {
checkTab.value = 'pending'
searchForm.value.status = STATUS.PENDING
await get()
await getStats()
console.log('切换页面后:', checkTab.value, sortField.value, sortOrder.value)
}
//
const adminPass = async function () {
checkTab.value = 'pass'
searchForm.value.status = STATUS.APPROVED
await get()
await getStats()
console.log('切换页面后:', checkTab.value, sortField.value, sortOrder.value)
}
//
const adminReject = async function () {
checkTab.value = 'reject'
searchForm.value.status = STATUS.REJECTED
await get()
await getStats()
console.log('切换页面后:', checkTab.value, sortField.value, sortOrder.value)
}
const handleMarketChange = (value) => {
if (value && value.length > 0) {
searchForm.value.market = value[value.length - 1]
} else {
searchForm.value.market = ''
}
}
const handleDatePickerChange = () => {
activeTimeRange.value = ''
}
const handlePageSizeChange = function (val) {
pagination.value.pageSize = val
get()
}
const handleCurrentChange = function (val) {
pagination.value.pageNum = val
get()
}
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
const throttledHandleApprove = _.throttle(handleApprove, 5000, {
trailing: false
})
const showRejectDialog = (row) => {
rejectRow.value.id = row.id
reason.value = ''
rejectVisible.value = true
}
//
const getmarkets = async function () {
try {
const result = await API({
url: '/market/selectMarket',
});
console.log('请求成功', result)
//
const transformTree = (nodes) => {
//
const allChildren = nodes.flatMap(node => node.children || []);
return allChildren.map(child => {
const grandchildren = child.children && child.children.length
? transformTree([child]) //
: null;
return {
value: child.name,
label: child.name,
children: grandchildren
}
})
}
marketOptions.value = transformTree(result.data)
console.log('转换后的地区树==============', marketOptions.value)
} catch (error) {
console.log('请求失败', error)
}
}
const trimJwCode = () => {
if (searchForm.value.jwcode) {
searchForm.value.jwcode = searchForm.value.jwcode.replace(/\s/g, '');
}
}
onMounted(async () => {
getmarkets()
get()
getStats()
console.log("看看通信来的用户身份", adminData.value)
})
</script>
<style scoped></style>

34
src/views/audit/audit.vue → src/views/audit/gold/audit.vue

@ -4,12 +4,14 @@
<el-button <el-button
:type="activeTab === 'rechargeAudit' ? 'primary' : 'default'" :type="activeTab === 'rechargeAudit' ? 'primary' : 'default'"
@click="navigateTo('rechargeAudit')" @click="navigateTo('rechargeAudit')"
:disabled="!hasRecharge"
> >
充值审核 充值审核
</el-button> </el-button>
<el-button <el-button
:type="activeTab === 'refundAudit' ? 'primary' : 'default'" :type="activeTab === 'refundAudit' ? 'primary' : 'default'"
@click="navigateTo('refundAudit')" @click="navigateTo('refundAudit')"
:disabled="!hasRefund"
> >
退款审核 退款审核
</el-button> </el-button>
@ -23,6 +25,7 @@ import {ref, watch, onMounted} from 'vue';
import {useRouter, useRoute} from 'vue-router'; import {useRouter, useRoute} from 'vue-router';
import {storeToRefs} from 'pinia'; import {storeToRefs} from 'pinia';
import {useAdminStore} from '@/store/index.js'; import {useAdminStore} from '@/store/index.js';
import {hasMenuPermission, permissionMapping} from "@/utils/menuTreePermission.js";
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
@ -30,31 +33,34 @@ const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore); const {menuTree} = storeToRefs(adminStore);
const activeTab = ref(''); const activeTab = ref('');
const hasRecharge = ref(false);
const hasRefund = ref(false);
// //
const navigateTo = (name) => { const navigateTo = (name) => {
activeTab.value = name; activeTab.value = name;
router.push({name}); router.push({name});
}; };
// menuName
const hasMenuPermission = (tree, targetName) => {
for (const node of tree) {
if (node.menuName === targetName) return true;
if (node.children && hasMenuPermission(node.children, targetName)) return true;
}
return false;
//
const initPermissions = () => {
if (!menuTree.value || !menuTree.value.length) return;
hasRecharge.value = hasMenuPermission(menuTree.value, permissionMapping.Recharge_Audit);
hasRefund.value = hasMenuPermission(menuTree.value, permissionMapping.Refund_Audit);
}; };
//
//
const getDefaultAuditRoute = () => { const getDefaultAuditRoute = () => {
if (!menuTree.value) return 'rechargeAudit';
const hasRecharge = hasMenuPermission(menuTree.value, '充值审核');
return hasRecharge ? 'rechargeAudit' : 'refundAudit';
initPermissions();
if (hasRecharge.value) return 'rechargeAudit';
if (hasRefund.value) return 'refundAudit';
return 'rechargeAudit';
}; };
// //
watch(() => route.name, (newName) => { watch(() => route.name, (newName) => {
initPermissions()
if (newName === 'rechargeAudit' || newName === 'refundAudit') { if (newName === 'rechargeAudit' || newName === 'refundAudit') {
activeTab.value = newName; activeTab.value = newName;
} else if (newName === 'audit') { } else if (newName === 'audit') {
@ -64,9 +70,9 @@ watch(() => route.name, (newName) => {
} }
}); });
// //
onMounted(() => { onMounted(() => {
initPermissions()
if (route.name === 'audit') { if (route.name === 'audit') {
const defaultRoute = getDefaultAuditRoute(); const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute); navigateTo(defaultRoute);

621
src/views/audit/gold/rechargeAudit.vue

@ -0,0 +1,621 @@
<template>
<el-card style="margin-bottom: 0.5vh;margin-top: 0.5vh">
<el-col style="margin-bottom: 0.5vh">
<el-text size="large">精网号</el-text>
<el-input v-model="rechargeAudit.jwcode" placeholder="请输入精网号" style="width: 12vw;margin-right:1vw"
clearable/>
<el-text size="large">活动名称</el-text>
<el-select v-model="rechargeAudit.activity" placeholder="请选择活动名称" style="width: 12vw;margin-right:1vw"
clearable>
<el-option v-for="item in activity" :key="item" :label="item" :value="item"/>
</el-select>
<el-text size="large">支付方式</el-text>
<el-select v-model="rechargeAudit.payModel" placeholder="请选择支付方式" style="width: 12vw;margin-right:1vw"
clearable>
<el-option v-for="item in payModel" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
<el-text size="large">所属地区</el-text>
<el-cascader v-model="selectedMarketPath" :options="market" placeholder="请选择所属地区" clearable
style="width:12vw"
@change="handleMarketChange"/>
</el-col>
<el-col>
<el-text size="large">
{{ activeName === 'wait' ? '提交时间:' : '审核时间:' }}
</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" class="time-controls" style="margin-right:1vw;width:25vw"
@change="handleDatePickerChange"
:default-time="defaultTime"/>
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''"></el-button>
<el-button @click="getYesterday()" :type="activeTimeRange === 'yesterday' ? 'primary' : ''"></el-button>
<el-button @click="get7Days()" :type="activeTimeRange === '7days' ? 'primary' : ''">近7天</el-button>
<el-button @click="resetSearch" type="success">重置</el-button>
<el-button @click="handleSearch" type="primary">查询</el-button>
</el-col>
</el-card>
<el-card>
<el-tabs v-model="activeName" type="card" @tab-click="handleClick">
<el-tab-pane label="待审核" name="wait"></el-tab-pane>
<el-tab-pane label="已通过" name="pass"></el-tab-pane>
<el-tab-pane label="已驳回" name="reject"></el-tab-pane>
<div>
总条数{{ format3(stats.totalNum) }}&nbsp;&nbsp;&nbsp;&nbsp;
充值新币{{ format3(stats.permanentGolds) }}新币&nbsp;&nbsp;&nbsp;&nbsp;
总金币数{{ format3(stats.permanentGolds + stats.freeGolds + stats.taskGolds) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ format3(stats.permanentGolds) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ format3(stats.freeGolds) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
</div>
</el-tabs>
<el-table :data="tableData" style="width: 100vw;height:55vh" @sort-change="handleSortChange"
:row-style="{ height: '50px' }">
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope">
<span>{{ scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize }}</span>
</template>
</el-table-column>
<el-table-column fixed="left" prop="name" label="姓名" width="150px"/>
<el-table-column fixed="left" prop="jwcode" label="精网号" width="110px"/>
<el-table-column prop="market" label="所属地区" width="100px"/>
<el-table-column prop="activity" label="活动名称" width="100px" show-overflow-tooltip/>
<el-table-column prop="rateName" label="货币名称" width="110px"/>
<el-table-column prop="money" sortable="custom" label="充值金额" width="110px"/>
<el-table-column prop="money" label="充值金额" sortable="custom" width="110px">
<template #default="scope">{{ scope.row.permanentGold / 100 }}</template>
</el-table-column>
<el-table-column prop="permanentGold" label="永久金币" width="110px" sortable="custom">
<template #default="scope">{{ scope.row.permanentGold / 100 }}</template>
</el-table-column>
<el-table-column prop="freeGold" label="免费金币" sortable="custom" width="110px">
<template #default="scope">{{ (scope.row.freeGold) / 100 }}</template>
</el-table-column>
<el-table-column prop="remark" label="备注" width="200px" show-overflow-tooltip/>
<el-table-column prop="payModel" label="支付方式" width="110px"/>
<el-table-column prop="voucher" label="支付凭证" width="110px">
<template #default="scope">
<div v-if="scope.row.voucher"
style="display: flex; justify-content: center; align-items: center; cursor: pointer;"
@click="previewImage(scope.row.voucher)">
<img :src="scope.row.voucher" alt="支付凭证" style="width: auto; height: 40px;">
</div>
<div v-else style="display: flex; justify-content: center; align-items: center; height: 40px;">--</div>
</template>
</el-table-column>
<el-table-column prop="adminName" label="提交人" width="100px"/>
<el-table-column prop="rejectReason" v-if="activeName === 'reject'" label="驳回理由" width="200px"
show-overflow-tooltip/>
<el-table-column v-if="activeName !== 'wait'" prop="auditName" label="审核人" width="100px"/>
<el-table-column prop="payTime" sortable="custom" label="付款时间" width="200px">
<template #default="scope">
{{ moment(scope.row.payTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column prop="createTime" sortable="custom" label="提交时间" width="200px">
<template #default="scope">
<!-- {{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}-->
{{
activeName === 'wait'
? moment(scope.row.auditTime).format('YYYY-MM-DD HH:mm:ss')
: moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss')
}}
</template>
</el-table-column>
<el-table-column v-if="activeName !== 'wait'" prop="auditTime" label="审核时间" width="200px">
<template #default="scope">
{{ moment(scope.row.auditTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column v-if="activeName === 'wait'" fixed="right" prop="operation" label="操作" width="150px">
<template #default="scope">
<div class="operation">
<el-popconfirm title="确定要通过此条记录吗?" @confirm="handleApprove(scope.row)">
<template #reference>
<el-button :disabled="scope.row.auditStatus === 1 || scope.row.auditStatus === 2" type="primary" text>
通过
</el-button>
</template>
</el-popconfirm>
<el-button :disabled="scope.row.auditStatus === 1 || scope.row.auditStatus === 2" type="primary" text
@click="showRejectDialog(scope.row)">
驳回
</el-button>
</div>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="handlePagination('size', $event)"
@current-change="handlePagination('page', $event)"></el-pagination>
</div>
</el-card>
<el-dialog v-model="rejectDialogVisible" title="驳回理由" width="500px">
<el-form>
<el-form-item label="驳回理由" required>
<el-input v-model="rejectReason" type="textarea" :rows="4" placeholder="请输入驳回理由" maxlength="200"
show-word-limit/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="rejectDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleReject">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {onMounted, reactive, ref} from 'vue'
import {ElMessage} from 'element-plus'
import request from '@/util/http.js'
import API from '@/util/http.js'
import moment from 'moment'
import {useAdminStore} from "@/store/index.js";
import {storeToRefs} from "pinia";
const adminStore = useAdminStore();
const {adminData, menuTree} = storeToRefs(adminStore);
import {findMenuById, permissionMapping} from "@/utils/menuTreePermission.js"
import dayjs from "dayjs";
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
const tableData = ref([])
//
const activeTimeRange = ref('')
const total = ref(50)
//
const getTime = ref([])
const activity = ref([])
const market = ref("")
const rejectDialogVisible = ref(false)
const rejectReason = ref('')
//
const currentRecord = ref(null)
//
const activeName = ref('wait')
const sortField = ref('')
const sortOrder = ref('')
//
const selectedMarketPath = ref("")
//
const rechargeAudit = ref({
jwcode: "", //
activity: "", //
payModel: "", //
startTime: "", //
endTime: "", //
market: "", //
auditStatus: "0",
})
//
const getObj = ref({
pageNum: 1,
pageSize: 50
})
//
const payModel = [
{
value: '现金',
label: '现金'
},
{
value: '支票',
label: '支票'
},
{
value: '刷卡',
label: '刷卡'
},
{
value: '其他(各地区电子支付)',
label: '其他(各地区电子支付)'
},
]
//
const stats = ref({
totalNum: 0,
totalCoins: 0,
permanentGolds: 0,
freeGolds: 0,
taskGolds: 0
})
//
const rules = reactive({
rejectReason: [{required: true, message: '请输入驳回理由', trigger: 'blur'}]
})
//
const getRecharge = async function (val) {
try {
if (getTime.value && getTime.value.length === 2) {
rechargeAudit.value.startTime = formatTime(getTime.value[0])
rechargeAudit.value.endTime = formatTime(getTime.value[1])
} else {
rechargeAudit.value.startTime = ''
rechargeAudit.value.endTime = ''
}
if (rechargeAudit.value.market === '总部' || rechargeAudit.value.market === '研发部') {
rechargeAudit.value.market = '';
}
//
if (rechargeAudit.value.jwcode) {
//
const numberRegex = /^\d{1,9}$/;
//
if (!numberRegex.test(rechargeAudit.value.jwcode)) {
ElMessage.error('请检查精网号格式')
return
}
}
const result = await request({
url: '/audit/selectRecharge',
data: {
pageNum: getObj.value.pageNum,
pageSize: getObj.value.pageSize,
rechargeAudit: {
...rechargeAudit.value,
sortField: sortField.value,
sortOrder: sortOrder.value
}
}
})
tableData.value = result.list
total.value = result.total
} catch (error) {
console.log('请求失败', error)
}
}
const getStats = async () => {
try {
const params = {
pageNum: getObj.value.pageNum,
pageSize: getObj.value.pageSize,
rechargeAudit: rechargeAudit.value
}
//
if (rechargeAudit.value.jwcode) {
//
const numberRegex = /^\d{1,9}$/;
//
if (!numberRegex.test(rechargeAudit.value.jwcode)) {
// ElMessage.error('')
//
return
}
}
const res = await API({
url: '/audit/sumRechargeGold',
data: params
})
stats.value.totalNum = res.totalNum
stats.value.permanentGolds = res.permanentGolds / 100
stats.value.freeGolds = res.freeGolds / 100
stats.value.taskGolds = res.taskGolds / 100
console.log('see see stats和搜索对象', stats.value, params)
} catch (error) {
console.log('请求失败', error)
}
}
//
const handleSearch = function () {
trimJwCode();
getObj.value.pageNum = 1
getRecharge()
getStats()
}
//
const resetSearch = function () {
rechargeAudit.value = {
jwcode: "",
activity: "",
payModel: "",
startTime: "",
endTime: "",
market: "",
auditStatus: rechargeAudit.value.auditStatus
}
selectedMarketPath.value = []
getTime.value = []
activeTimeRange.value = '' //
getRecharge()
getStats()
}
//
const getToday = function () {
const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = 'today' //
getRecharge()
getStats()
}
//
const getYesterday = function () {
const today = dayjs()
const startTime = today.subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = 'yesterday' //
getRecharge()
getStats()
}
// 7
const get7Days = function () {
const today = dayjs()
const startTime = today.subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = '7days' //
getRecharge()
getStats()
}
//
const adminWait = async function () {
rechargeAudit.value.auditStatus = "0"
getObj.value.pageNum = 1
await getRecharge()
await getStats()
console.log('adminWait,这是点击待审核调用')
}
//
const adminPass = async function () {
rechargeAudit.value.auditStatus = "1"
getObj.value.pageNum = 1
await getRecharge()
await getStats()
console.log('adminWait,这是点击已通过调用')
}
//
const adminReject = async function () {
rechargeAudit.value.auditStatus = "2"
getObj.value.pageNum = 1
await getRecharge()
await getStats()
console.log('adminWait,这是点击已驳回调用')
}
const handleClick = function (tab, event) {
activeName.value = tab.props.name
if (tab.props.name === 'wait') {
adminWait()
} else if (tab.props.name === 'pass') {
adminPass()
} else if (tab.props.name === 'reject') {
adminReject()
}
}
const getActivity = async function () {
try {
const result = await request({
url: '/general/activity',
data: {}
})
activity.value = result.data
console.log('activity', activity.value)
} catch (error) {
console.log('请求失败', error)
}
}
const handlePagination = (type, val) => {
if (type === 'size') {
getObj.value.pageSize = val
} else {
getObj.value.pageNum = val
}
getRecharge()
getStats()
}
//
const handleApprove = async (row) => {
if (findMenuById(menuTree.value, permissionMapping.Recharge_Approval)) {
try {
const params = {
orderCode: row.orderCode,
auditId: adminData.value.id,
action: 1,
rejectReason: ''
}
await request({url: '/audit/audit', data: params})
ElMessage.success('审核通过成功')
await getRecharge()
await getStats()
} catch (error) {
console.error('审核通过失败', error)
ElMessage.error('操作失败')
}
} else {
ElMessage.error('无权限')
}
}
const showRejectDialog = (row) => {
currentRecord.value = row
rejectReason.value = ''
if (findMenuById(menuTree.value, permissionMapping.Recharge_Approval)) {
rejectDialogVisible.value = true
} else {
ElMessage.error('无权限')
}
}
//
const handleReject = async () => {
if (findMenuById(menuTree.value, permissionMapping.Recharge_Approval)) {
if (!rejectReason.value.trim()) {
ElMessage.warning('请输入驳回理由')
return
}
try {
const params = {
orderCode: currentRecord.value.orderCode,
auditId: adminData.value.id,
action: 2,
rejectReason: rejectReason.value
}
await request({url: '/audit/audit', data: params})
ElMessage.success('驳回操作成功')
rejectDialogVisible.value = false
await getRecharge()
await getStats()
} catch (error) {
console.error('驳回操作失败', error)
ElMessage.error('操作失败')
}
} else {
ElMessage.error('无权限')
}
}
//
const handleSortChange = (column) => {
console.log('排序字段:', column.prop)
console.log('排序方式:', column.order)
if (column.prop === 'money') {
sortField.value = 'permanent_gold'
} else if (column.prop === 'permanentGold') {
sortField.value = 'permanent_gold'
} else if (column.prop === 'freeGold') {
sortField.value = 'freeGold'
} else if (column.prop === 'createTime') {
sortField.value = 'create_time'
} else if (column.prop === 'payTime') {
sortField.value = 'pay_time'
} else if (column.prop === 'auditTime') {
sortField.value = 'audit_time'
}
sortOrder.value = column.order === 'ascending' ? 'asc' : 'desc'
getRecharge()
getStats()
}
//
const previewImage = (imageUrl) => {
// 使 element-plus el-image
const imageElement = document.createElement('img');
imageElement.src = imageUrl;
imageElement.style.maxWidth = '80vw';
imageElement.style.maxHeight = '80vh';
const viewer = document.createElement('div');
viewer.style.position = 'fixed';
viewer.style.top = '0';
viewer.style.left = '0';
viewer.style.width = '100vw';
viewer.style.height = '100vh';
viewer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
viewer.style.display = 'flex';
viewer.style.justifyContent = 'center';
viewer.style.alignItems = 'center';
viewer.style.zIndex = '9999';
viewer.style.overflow = 'auto';
viewer.appendChild(imageElement);
document.body.appendChild(viewer);
viewer.addEventListener('click', () => {
document.body.removeChild(viewer);
});
};
const handleMarketChange = (value) => {
if (value && value.length > 0) {
rechargeAudit.value.market = value[value.length - 1]
} else {
rechargeAudit.value.market = ''
}
}
const props = {multiple: true}
//
const getMarket = async function () {
try {
const result = await API({
url: '/market/selectMarket',
});
console.log('请求成功', result)
//
const transformTree = (nodes) => {
//
const allChildren = nodes.flatMap(node => node.children || []);
return allChildren.map(child => {
const grandchildren = child.children && child.children.length
? transformTree([child]) //
: null;
return {
value: child.name,
label: child.name,
children: grandchildren
};
});
};
market.value = transformTree(result.data)
console.log('转换后的地区树==============', market.value)
} catch (error) {
console.log('请求失败', error)
}
}
//
const format3 = (num) => {
return num.toLocaleString('en-US')
}
//
const handleDatePickerChange = () => {
activeTimeRange.value = ''
}
//
const trimJwCode = () => {
if (rechargeAudit.value.jwcode) {
rechargeAudit.value.jwcode = rechargeAudit.value.jwcode.replace(/\s/g, '');
}
}
const formatTime = (val) => val ? moment(val).format('YYYY-MM-DD HH:mm:ss') : ''
//
onMounted(async function () {
await getActivity()
await getMarket()
await getRecharge()
console.log("看看通信来的用户身份", adminData.value)
await getStats()
})
</script>
<style scoped>
.pagination {
display: flex;
margin-top: 0.5vh;
}
.operation {
display: flex;
}
.time-controls {
display: flex;
align-items: center;
display: flex;
align-items: center;
gap: 10px;
}
</style>

337
src/views/audit/refundAudit.vue → src/views/audit/gold/refundAudit.vue

@ -1,53 +1,41 @@
<template> <template>
<el-row>
<el-col>
<div>
<el-card style="margin-bottom: 5px">
<el-row style="margin-bottom: 5px">
<el-col :span="6">
<el-card style="margin-bottom: 0.5vh;margin-top:0.5vh;width:82.5vw">
<el-col style="margin-bottom: 0.5vh">
<el-text size="large">精网号</el-text> <el-text size="large">精网号</el-text>
<el-input v-model="searchForm.jwcode" placeholder="请输入精网号" style="width: 240px" clearable />
</el-col>
<el-col :span="6">
<el-input v-model="searchForm.jwcode" placeholder="请输入精网号" style="width: 12vw;margin-right:1vw" clearable/>
<el-text size="large">商品名</el-text> <el-text size="large">商品名</el-text>
<el-select v-model="searchForm.goodsName" placeholder="请输入商品名" style="width: 240px" clearable>
<el-select v-model="searchForm.goodsName" placeholder="请输入商品名" style="width: 12vw;margin-right:1vw"
clearable>
<el-option v-for="item in refundGoodsOptions" :key="item" :label="item" :value="item"></el-option> <el-option v-for="item in refundGoodsOptions" :key="item" :label="item" :value="item"></el-option>
</el-select> </el-select>
</el-col>
<el-col :span="6">
<el-text size="large">退款方式</el-text> <el-text size="large">退款方式</el-text>
<el-select v-model="searchForm.refundModel" placeholder="请选择" style="width: 240px" clearable>
<el-select v-model="searchForm.refundModel" placeholder="请选择" style="width: 12vw;margin-right:1vw" clearable>
<el-option label="全部退款" value="0"/> <el-option label="全部退款" value="0"/>
<el-option label="部分退款" value="1"/> <el-option label="部分退款" value="1"/>
</el-select> </el-select>
</el-col>
<el-col :span="6">
<el-text size="large">所属地区</el-text> <el-text size="large">所属地区</el-text>
<el-select v-model="searchForm.market" placeholder="请选择" style="width: 240px" clearable>
<el-option v-for="item in marketOptions" :key="item" :label="item" :value="item" />
</el-select>
<el-cascader v-model="selectedMarketPath" :options="market" placeholder="请选择所属地区" clearable
style="width:12vw"
@change="handleMarketChange"/>
</el-col> </el-col>
</el-row>
<el-row>
<el-col :span="15">
<div class="time-controls">
<div class="time-group">
<el-text size="large" style="width: 80px">提交时间</el-text>
<el-col>
<el-text size="large">
{{ activeName === 'wait' ? '提交时间:' : '审核时间:' }}
</el-text>
<el-date-picker v-model="dateRange" type="datetimerange" range-separator="" start-placeholder="开始时间" <el-date-picker v-model="dateRange" type="datetimerange" range-separator="" start-placeholder="开始时间"
end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange"/>
<el-button @click="getToday()" style="margin-left: 10px" :type="activeTimeRange === 'today' ? 'primary' : ''"> </el-button>
<el-button @click="getYesterday()" style="margin-left: 10px" :type="activeTimeRange === 'yesterday' ? 'primary' : ''"> </el-button>
<el-button @click="get7Days()" style="margin-left: 10px" :type="activeTimeRange === '7days' ? 'primary' : ''"> 近7天</el-button>
class="time-controls"
end-placeholder="结束时间" style="margin-right:1vw;width:25vw" @change="handleDatePickerChange"
:default-time="defaultTime"/>
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''"></el-button>
<el-button @click="getYesterday()" :type="activeTimeRange === 'yesterday' ? 'primary' : ''"></el-button>
<el-button @click="get7Days()" :type="activeTimeRange === '7days' ? 'primary' : ''">近7天</el-button>
<el-button type="success" @click="resetSearch">重置</el-button> <el-button type="success" @click="resetSearch">重置</el-button>
<el-button type="primary" @click="handleSearch">查询</el-button> <el-button type="primary" @click="handleSearch">查询</el-button>
</div>
</div>
</el-col> </el-col>
</el-row>
</el-card> </el-card>
</div>
</el-col>
</el-row>
<el-card> <el-card>
<el-tabs v-model="activeName" type="card" @tab-click="handleClick"> <el-tabs v-model="activeName" type="card" @tab-click="handleClick">
@ -57,18 +45,19 @@
</el-tabs> </el-tabs>
<div> <div>
总条数{{ stats.totalNum }}&nbsp;&nbsp;&nbsp;&nbsp;
退款总金币数{{ (stats.permanentGolds + stats.freeGolds + stats.taskGolds).toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ stats.permanentGolds.toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ stats.freeGolds.toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
任务金币{{ stats.taskGolds.toFixed(2) }}金币
总条数{{ format3(stats.totalNum) }}&nbsp;&nbsp;&nbsp;&nbsp;
退款总金币数{{ format3(stats.permanentGolds + stats.freeGolds + stats.taskGolds) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ format3(stats.permanentGolds) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ format3(stats.freeGolds.toFixed(2)) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
任务金币{{ format3(stats.taskGolds.toFixed(2)) }}金币
</div> </div>
<el-table :data="tableData" height="540px" @sort-change="handleSortChange">
<el-table :data="tableData" style="height:55vh" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="60"/> <el-table-column type="index" label="序号" width="60"/>
<el-table-column prop="name" label="姓名" width="120"/> <el-table-column prop="name" label="姓名" width="120"/>
<el-table-column prop="jwcode" label="精网号" width="120"/> <el-table-column prop="jwcode" label="精网号" width="120"/>
<el-table-column prop="market" label="所属地区" width="120"/> <el-table-column prop="market" label="所属地区" width="120"/>
<el-table-column prop="refundType" label="退款类型" width="120"/> <el-table-column prop="refundType" label="退款类型" width="120"/>
<el-table-column prop="refundModel" label="退款方式" width="120"> <el-table-column prop="refundModel" label="退款方式" width="120">
<template #default="{ row }"> <template #default="{ row }">
@ -96,13 +85,20 @@
{{ row.taskGold / 100 }} {{ row.taskGold / 100 }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="remark" label="备注" width="150" show-overflow-tooltip /><!-- 当内容过长被隐藏时显示 tooltip -->
<el-table-column prop="remark" label="备注" width="150" show-overflow-tooltip/>
<el-table-column prop="adminName" label="提交人" width="120"/> <el-table-column prop="adminName" label="提交人" width="120"/>
<el-table-column v-if="checkTab === 'reject'" prop="rejectReason" label="驳回理由" width="150" show-overflow-tooltip />
<el-table-column v-if="checkTab === 'reject'" prop="rejectReason" label="驳回理由" width="150"
show-overflow-tooltip/>
<el-table-column v-if="checkTab !== 'pending'" prop="auditName" label="审核人" width="120"/> <el-table-column v-if="checkTab !== 'pending'" prop="auditName" label="审核人" width="120"/>
<el-table-column prop="createTime" label="提交时间" width="180" sortable="custom"> <el-table-column prop="createTime" label="提交时间" width="180" sortable="custom">
<template #default="{ row }"> <template #default="{ row }">
{{ moment(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
<!-- {{ moment(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}-->
{{
checkTab === 'pending'
? moment(row.auditTime).format('YYYY-MM-DD HH:mm:ss')
: moment(row.createTime).format('YYYY-MM-DD HH:mm:ss')
}}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column v-if="checkTab !== 'pending'" prop="auditTime" label="审核时间" width="180" sortable="custom"> <el-table-column v-if="checkTab !== 'pending'" prop="auditTime" label="审核时间" width="180" sortable="custom">
@ -129,14 +125,16 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-pagination class="pagination" v-model:current-page="pagination.pageNum" v-model:page-size="pagination.pageSize" <el-pagination class="pagination" v-model:current-page="pagination.pageNum" v-model:page-size="pagination.pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" @size-change="handlePageSizeChange"
layout="total, sizes, prev, pager, next, jumper" :total="pagination.total"
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination> @current-change="handleCurrentChange"></el-pagination>
</el-card> </el-card>
<el-dialog v-model="rejectDialogVisible" title="驳回理由" width="500px"> <el-dialog v-model="rejectDialogVisible" title="驳回理由" width="500px">
<el-form> <el-form>
<el-form-item label="驳回理由" required> <el-form-item label="驳回理由" required>
<el-input v-model="rejectReason" type="textarea" :rows="4" placeholder="请输入驳回理由" maxlength="200" show-word-limit/>
<el-input v-model="rejectReason" type="textarea" :rows="4" placeholder="请输入驳回理由" maxlength="200"
show-word-limit/>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@ -151,19 +149,24 @@
<script setup> <script setup>
import {onMounted, reactive, ref} from 'vue' import {onMounted, reactive, ref} from 'vue'
import {ElMessage} from 'element-plus' import {ElMessage} from 'element-plus'
import API from '@/util/http'
import API from '@/util/http.js'
import moment from 'moment' import moment from 'moment'
import {useAdminStore} from "@/store/index.js";
import {storeToRefs} from "pinia";
import {findMenuById, permissionMapping} from "@/utils/menuTreePermission.js"
import dayjs from "dayjs";
//
const adminStore = useAdminStore();
const {adminData, menuTree} = storeToRefs(adminStore);
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
//
const activeTimeRange = ref('') const activeTimeRange = ref('')
//
const handleDatePickerChange = () => {
activeTimeRange.value = ''
}
const scopeValue = ref(null) // const scopeValue = ref(null) //
const rejectDialogVisible = ref(false) //
const rejectReason = ref('') //
const rejectDialogVisible = ref(false)
const rejectReason = ref('')
// //
const STATUS = { const STATUS = {
PENDING: 0, // PENDING: 0, //
@ -175,13 +178,12 @@ const searchForm = ref({
jwcode: '', jwcode: '',
refundModel: '', refundModel: '',
goodsName: '', goodsName: '',
market: '',
market: "",
startTime: '', startTime: '',
endTime: '', endTime: '',
auditStatus: '0' auditStatus: '0'
}) })
const checkTab = ref('pending') // STATUS012statusInteger const checkTab = ref('pending') // STATUS012statusInteger
//
const dateRange = ref([]) const dateRange = ref([])
const pagination = ref({ const pagination = ref({
pageNum: 1, pageNum: 1,
@ -192,6 +194,7 @@ const tableData = ref([])
const marketOptions = ref([]) const marketOptions = ref([])
const refundGoodsOptions = ref([]) const refundGoodsOptions = ref([])
const adminInfo = ref({}) const adminInfo = ref({})
// //
const stats = ref({ const stats = ref({
totalNum: 0, totalNum: 0,
@ -200,43 +203,14 @@ const stats = ref({
freeGolds: 0, freeGolds: 0,
taskGolds: 0 taskGolds: 0
}) })
//
const rejectVisible = ref(false) const rejectVisible = ref(false)
//
const rejectObj = ref({}) const rejectObj = ref({})
//
const passObj = ref({}) const passObj = ref({})
// //
const activeName = ref('wait') const activeName = ref('wait')
// ref
const Ref = ref(null)
//
const sortField = ref('') const sortField = ref('')
const sortOrder = ref('') const sortOrder = ref('')
//
const rules = reactive({
reason: [{ required: true, message: '请输入驳回理由', trigger: 'blur' }]
})
const getAdminData = async function () {
try {
const result = await API({ url: '/admin/userinfo', data: {} })
adminInfo.value = result
console.log('请求成功', result)
console.log('用户信息', adminInfo.value)
} catch (error) {
console.log('请求失败', error)
}
}
const handlePageSizeChange = function (val) {
pagination.value.pageSize = val
get()
console.log('aaaaaaaaaaaaaaaaaa这是pageSize改变调用')
}
const handleCurrentChange = function (val) {
pagination.value.pageNum = val
get()
console.log('aaaaaaaaaaaaaaa这是pageNum改变调用')
}
const market = ref("")
// //
const handleSortChange = (column) => { const handleSortChange = (column) => {
if (column.prop === 'sumGold') { if (column.prop === 'sumGold') {
@ -265,17 +239,6 @@ const showRejectDialog = (row) => {
rejectReason.value = '' rejectReason.value = ''
rejectDialogVisible.value = true rejectDialogVisible.value = true
} }
//
const getmarkets = async () => {
try {
const result = await API({ url: '/general/adminMarkets', data: {
account: adminInfo.value.account,
} })
marketOptions.value = result.data || []
} catch (error) {
console.error('获取地区列表失败', error)
}
}
// //
const getRefundGoods = async () => { const getRefundGoods = async () => {
try { try {
@ -299,6 +262,9 @@ const get = async function (val) {
searchForm.value.startTime = '' searchForm.value.startTime = ''
searchForm.value.endTime = '' searchForm.value.endTime = ''
} }
if (searchForm.value.market === '总部' || searchForm.value.market === '研发部') {
searchForm.value.market = '';
}
const params = { const params = {
pageNum: pagination.value.pageNum, pageNum: pagination.value.pageNum,
pageSize: pagination.value.pageSize, pageSize: pagination.value.pageSize,
@ -309,6 +275,18 @@ const get = async function (val) {
} }
} }
console.log('看看传给后端的参数:', params) console.log('看看传给后端的参数:', params)
//
if (searchForm.value.jwcode) {
//
const numberRegex = /^\d{1,9}$/;
//
if (!numberRegex.test(searchForm.value.jwcode)) {
ElMessage.error('请检查精网号格式')
//
return
}
}
const res = await API({url: '/audit/selectRefund', data: params}) const res = await API({url: '/audit/selectRefund', data: params})
tableData.value = res.list || [] tableData.value = res.list || []
pagination.value.total = res.total || 0 pagination.value.total = res.total || 0
@ -319,26 +297,32 @@ const get = async function (val) {
} }
// //
const handleApprove = async (row) => { const handleApprove = async (row) => {
if (findMenuById(menuTree.value, permissionMapping.Refund_Approval)) {
try { try {
const params = { const params = {
orderCode: row.orderCode, orderCode: row.orderCode,
auditId: adminInfo.value.id,
auditId: adminData.value.id,
action: 1,// action1,2 action: 1,// action1,2
rejectReason: '' rejectReason: ''
} }
await API({ url: '/audit/audit', data: params })//
await API({url: '/audit/audit', data: params})
ElMessage.success('审核通过成功') ElMessage.success('审核通过成功')
get() get()
getStats() getStats()
console.log('aaaaaaaaaa这是通过调用')
} catch (error) { } catch (error) {
console.error('审核通过失败', error) console.error('审核通过失败', error)
ElMessage.error('操作失败') ElMessage.error('操作失败')
} }
} else {
ElMessage.warning('没有权限')
}
} }
// //
const handleReject = async () => { const handleReject = async () => {
if (findMenuById(menuTree.value, permissionMapping.Refund_Approval)) {
if (!rejectReason.value.trim()) { if (!rejectReason.value.trim()) {
ElMessage.warning('请输入驳回理由') ElMessage.warning('请输入驳回理由')
return return
@ -346,7 +330,7 @@ const handleReject = async () => {
try { try {
const params = { const params = {
orderCode: scopeValue.value.orderCode, orderCode: scopeValue.value.orderCode,
auditId: adminInfo.value.id,
auditId: adminData.value.id,
action: 2, action: 2,
rejectReason: rejectReason.value rejectReason: rejectReason.value
} }
@ -355,11 +339,14 @@ const handleReject = async () => {
rejectDialogVisible.value = false rejectDialogVisible.value = false
get() get()
getStats() getStats()
console.log('aaaaaaaaaa这是驳回调用',params)
console.log('看看驳回参数', params)
} catch (error) { } catch (error) {
console.error('驳回失败', error) console.error('驳回失败', error)
ElMessage.error('操作失败') ElMessage.error('操作失败')
} }
} else {
ElMessage.warning('没有权限')
}
} }
const getStats = async () => { const getStats = async () => {
@ -371,6 +358,17 @@ const getStats = async () => {
...searchForm.value ...searchForm.value
} }
} }
if (searchForm.value.jwcode) {
//
const numberRegex = /^\d{1,9}$/;
//
if (!numberRegex.test(searchForm.value.jwcode)) {
// ElMessage.error('')
//
return
}
}
const res = await API({ const res = await API({
url: '/audit/sumRefundGold', url: '/audit/sumRefundGold',
data: params data: params
@ -385,15 +383,12 @@ const getStats = async () => {
console.log('请求失败', error) console.log('请求失败', error)
} }
} }
// //
const handleSearch = function () { const handleSearch = function () {
// pagination.value.pageNum = 1
trimJwCode()
get() get()
getStats() getStats()
console.log('aaaaaaaaaa这是搜索按钮调用')
} }
// //
const resetSearch = function () { const resetSearch = function () {
const auditStatus = searchForm.value.auditStatus; const auditStatus = searchForm.value.auditStatus;
@ -401,7 +396,7 @@ const resetSearch = function () {
jwcode: '', jwcode: '',
refundType: '', refundType: '',
goodsName: '', goodsName: '',
market: '',
market: "",
startTime: '', startTime: '',
endTime: '', endTime: '',
sortField: '', sortField: '',
@ -410,53 +405,43 @@ const resetSearch = function () {
} }
dateRange.value = [] dateRange.value = []
activeTimeRange.value = '' // activeTimeRange.value = '' //
selectedMarketPath.value = []
get()
getStats()
} }
// //
const getToday = function () { const getToday = function () {
const today = new Date()
const startTime = new Date(today.getFullYear(), today.getMonth(), today.getDate())
const endTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1)
const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime] dateRange.value = [startTime, endTime]
console.log('dateRange', dateRange.value) console.log('dateRange', dateRange.value)
activeTimeRange.value = 'today' //
activeTimeRange.value = 'today'
get() get()
getStats() getStats()
console.log('aaaaaaaaaa这是今天调用')
} }
// //
const getYesterday = function () { const getYesterday = function () {
const yesterday = new Date()
yesterday.setDate(yesterday.getDate() - 1)
const startTime = new Date(yesterday.getFullYear(), yesterday.getMonth(), yesterday.getDate())
const endTime = new Date(yesterday.getFullYear(), yesterday.getMonth(), yesterday.getDate() + 1)
const today = dayjs()
const startTime = today.subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime] dateRange.value = [startTime, endTime]
console.log('dateRange', dateRange.value) console.log('dateRange', dateRange.value)
activeTimeRange.value = 'yesterday' //
activeTimeRange.value = 'yesterday'
get() get()
getStats() getStats()
console.log('aaaaaaaaaa这是昨天调用')
} }
// 7 // 7
const get7Days = function () { const get7Days = function () {
const today = new Date()
const startTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 6)
const endTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1)
const today = dayjs()
const startTime = today.subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime] dateRange.value = [startTime, endTime]
console.log('dateRange', dateRange.value) console.log('dateRange', dateRange.value)
activeTimeRange.value = '7days' //
activeTimeRange.value = '7days'
get() get()
getStats() getStats()
console.log('aaaaaaaaaa这是近七天调用')
} }
//
const handleClick = function (tab, event) { const handleClick = function (tab, event) {
activeName.value = tab.props.name activeName.value = tab.props.name
if (tab.props.name === 'wait') { if (tab.props.name === 'wait') {
@ -467,7 +452,6 @@ const handleClick = function (tab, event) {
adminReject() adminReject()
} }
} }
// //
const getCurrentStatus = () => { const getCurrentStatus = () => {
switch (activeName.value) { switch (activeName.value) {
@ -481,50 +465,104 @@ const getCurrentStatus = () => {
return '' return ''
} }
} }
//
//
const adminWait = async function () { const adminWait = async function () {
checkTab.value = 'pending' checkTab.value = 'pending'
searchForm.value.auditStatus = STATUS.PENDING searchForm.value.auditStatus = STATUS.PENDING
// pagination.value.pageNum = 1
await get() await get()
await getStats() await getStats()
console.log('aaaaaaaaaaaaaaaaa看看checkTab,这是点击待审核调用', checkTab.value)
} }
//
//
const adminPass = async function () { const adminPass = async function () {
checkTab.value = 'pass' checkTab.value = 'pass'
searchForm.value.auditStatus = STATUS.APPROVED searchForm.value.auditStatus = STATUS.APPROVED
// pagination.value.pageNum = 1
await get() await get()
await getStats() await getStats()
console.log('aaaaaaaaaaaaaaaaaaaaa看看checkTab,这是点击已通过调用', checkTab.value)
} }
//
//
const adminReject = async function () { const adminReject = async function () {
checkTab.value = 'reject' checkTab.value = 'reject'
searchForm.value.auditStatus = STATUS.REJECTED searchForm.value.auditStatus = STATUS.REJECTED
// pagination.value.pageNum = 1
await get() await get()
await getStats() await getStats()
console.log('aaaaaaaaaaaaaaaaaa看看checkTab,这是点击已驳回调用', checkTab.value)
} }
const selectedMarketPath = ref("")
const handleMarketChange = (value) => {
if (value && value.length > 0) {
searchForm.value.market = value[value.length - 1]
} else {
searchForm.value.market = ''
}
}
//
const getMarket = async function () {
try {
const result = await API({
url: '/market/selectMarket',
})
console.log('请求成功', result)
//
const transformTree = (nodes) => {
//
const allChildren = nodes.flatMap(node => node.children || []);
return allChildren.map(child => {
const grandchildren = child.children && child.children.length
? transformTree([child]) //
: null;
return {
value: child.name,
label: child.name,
children: grandchildren
};
});
};
market.value = transformTree(result.data)
console.log('转换后的地区树==============', market.value)
} catch (error) {
console.log('请求失败', error)
}
}
const trimJwCode = () => {
if (searchForm.value.jwcode) {
searchForm.value.jwcode = searchForm.value.jwcode.replace(/\s/g, '');
}
}
//
const handleDatePickerChange = () => {
activeTimeRange.value = ''
}
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
//
const rules = reactive({
reason: [{required: true, message: '请输入驳回理由', trigger: 'blur'}]
})
const handlePageSizeChange = function (val) {
pagination.value.pageSize = val
get()
}
const handleCurrentChange = function (val) {
pagination.value.pageNum = val
get()
}
onMounted(async () => { onMounted(async () => {
await getAdminData()
getRefundGoods() getRefundGoods()
getmarkets()
await getMarket()
await get() await get()
await getStats() await getStats()
console.log('aaaaaaaaaa这是挂载后调用')
}) })
</script> </script>
<style scoped> <style scoped>
.pagination { .pagination {
display: flex; display: flex;
margin-top: 0.5vh;
} }
.operation { .operation {
@ -534,9 +572,6 @@ onMounted(async () => {
.time-controls { .time-controls {
display: flex; display: flex;
align-items: center; align-items: center;
}
.time-group {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;

691
src/views/audit/rechargeAudit.vue

@ -1,691 +0,0 @@
<!-- 支付凭证 -->
<template>
<el-row>
<el-col>
<el-card style="margin-bottom: 5px">
<el-row style="margin-bottom: 5px">
<el-col :span="6">
<el-text size="large">精网号</el-text>
<el-input v-model="rechargeAudit.jwcode" placeholder="请输入精网号" style="width: 240px" clearable/>
</el-col>
<el-col :span="6">
<el-text size="large">活动名称</el-text>
<el-select v-model="rechargeAudit.activity" placeholder="请选择活动名称" style="width: 240px" clearable>
<el-option v-for="item in activity" :key="item" :label="item" :value="item"/>
</el-select>
</el-col>
<el-col :span="6">
<el-text size="large">支付方式</el-text>
<el-select v-model="rechargeAudit.payModel" placeholder="请选择支付方式" style="width: 240px" clearable>
<el-option v-for="item in payModel" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
</el-col>
<el-col :span="6">
<div class="head-card-element">
<el-text size="large">所属地区</el-text>
<el-select v-model="rechargeAudit.market" placeholder="请选择所属地区" style="width: 240px" clearable>
<el-option v-for="item in market" :key="item" :label="item" :value="item"/>
</el-select>
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<div class="time-controls">
<div class="time-group">
<el-text size="large" style="width: 80px">充值时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange"/>
<el-button @click="getToday()" style="margin-left: 10px" :type="activeTimeRange === 'today' ? 'primary' : ''"> </el-button>
<el-button @click="getYesterday()" style="margin-left: 10px" :type="activeTimeRange === 'yesterday' ? 'primary' : ''"> </el-button>
<el-button @click="get7Days()" style="margin-left: 10px" :type="activeTimeRange === '7days' ? 'primary' : ''"> 近7天</el-button>
<el-button @click="resetSearch" type="success">重置</el-button>
<el-button type="primary" @click="handleSearch">查询</el-button>
</div>
</div>
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
<el-row>
<el-col>
<el-card>
<el-tabs v-model="activeName" type="card" @tab-click="handleClick">
<el-tab-pane label="待审核" name="wait"></el-tab-pane>
<el-tab-pane label="已通过" name="pass"></el-tab-pane>
<el-tab-pane label="已驳回" name="reject"></el-tab-pane>
<div>
总条数{{ stats.totalNum }}&nbsp;&nbsp;&nbsp;&nbsp;
总金币数{{ (stats.permanentGolds + stats.freeGolds + stats.taskGolds).toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ stats.permanentGolds.toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ stats.freeGolds.toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
<!-- 任务金币{{ stats.taskGolds.toFixed(2) }}金币-->
</div>
</el-tabs>
<!--表格-->
<div style="height: 540px; overflow-y: auto">
<el-table :data="tableData" style="width: 100%" height="540px" @sort-change="handleSortChange"
:row-style="{ height: '50px' }">
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope">
<span>{{ scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize }}</span>
</template>
</el-table-column>
<el-table-column fixed="left" prop="name" label="姓名" width="150px"/>
<el-table-column fixed="left" prop="jwcode" label="精网号" width="110px"/>
<el-table-column prop="market" label="所属地区" width="100px"/>
<el-table-column prop="activity" label="活动名称" width="100px" show-overflow-tooltip/>
<el-table-column prop="money" label="充值金额" sortable="custom" width="110px">
<template #default="scope">{{ scope.row.permanentGold / 100 }}</template>
</el-table-column>
<el-table-column prop="permanentGold" label="永久金币" width="110px" sortable="custom">
<template #default="scope">{{ scope.row.permanentGold / 100 }}</template>
</el-table-column>
<el-table-column prop="freeGold" label="免费金币" sortable="custom" width="110px">
<template #default="scope">{{ (scope.row.freeGold) / 100 }}</template>
</el-table-column>
<el-table-column prop="remark" label="备注" width="200px" show-overflow-tooltip/>
<el-table-column prop="payModel" label="支付方式" width="110px"/>
<el-table-column prop="voucher" label="支付凭证" width="110px">
<template #default="scope">
<div v-if="scope.row.voucher" style="display: flex; justify-content: center; align-items: center; cursor: pointer;" @click="previewImage(scope.row.voucher)">
<img :src="scope.row.voucher" alt="支付凭证" style="width: auto; height: 40px;">
</div>
<div v-else style="display: flex; justify-content: center; align-items: center; height: 40px;">--</div>
</template>
</el-table-column>
<el-table-column prop="adminName" label="提交人" width="100px"/>
<el-table-column prop="rejectReason" v-if="activeName === 'reject'" label="驳回理由" width="200px"
show-overflow-tooltip/>
<el-table-column v-if="activeName !== 'wait'" prop="auditName" label="审核人" width="100px"/>
<el-table-column prop="payTime" sortable="custom" label="付款时间" width="200px">
<template #default="scope">
{{ moment(scope.row.payTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column prop="createTime" sortable="custom" label="提交时间" width="200px">
<template #default="scope">
{{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column v-if="activeName !== 'wait'" prop="auditTime" label="审核时间" width="200px">
<template #default="scope">
{{ moment(scope.row.auditTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column v-if="activeName === 'wait'" fixed="right" prop="operation" label="操作" width="150px">
<template #default="scope">
<div class="operation">
<el-popconfirm title="确定要通过此条记录吗?" @confirm="handleApprove(scope.row)">
<template #reference>
<el-button :disabled="scope.row.auditStatus === 1 || scope.row.auditStatus === 2" type="primary"
text>
通过
</el-button>
</template>
</el-popconfirm>
<el-button :disabled="scope.row.auditStatus === 1 || scope.row.auditStatus === 2" type="primary" text
@click="showRejectDialog(scope.row)">
驳回
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="handlePagination('size', $event)"
@current-change="handlePagination('page', $event)"></el-pagination>
</div>
</el-card>
</el-col>
</el-row>
<el-dialog v-model="rejectDialogVisible" title="驳回理由" width="500px">
<el-form>
<el-form-item label="驳回理由" required>
<el-input v-model="rejectReason" type="textarea" :rows="4" placeholder="请输入驳回理由" maxlength="200"
show-word-limit/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="rejectDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleReject">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {computed, onMounted, reactive, ref} from 'vue'
import {ElMessage} from 'element-plus'
import request from '@/util/http'
import moment from 'moment'
import API from '@/util/http'
//
const trimJwCode = () => {
if (rechargeAudit.value.jwcode) {
rechargeAudit.value.jwcode = rechargeAudit.value.jwcode.replace(/\s/g, '');
}
}
const formatTime = (val) => val ? moment(val).format('YYYY-MM-DD HH:mm:ss') : ''
//
const adminData = ref({})
//
const tableData = ref([])
//
const activeTimeRange = ref('')
//
const handleDatePickerChange = () => {
activeTimeRange.value = ''
}
//
const rechargeAudit = ref({
jwcode: "", //
activity: "", //
payModel: "", //
startTime: "", //
endTime: "", //
market: "", //
auditStatus : "0",
})
//
const getObj = ref({
pageNum: 1,
pageSize: 50
})
//
const total = ref(50)
//
const getTime = ref([])
const activity = ref([])
//
const market = ref([])
//
const rejectDialogVisible = ref(false)
//
const rejectReason = ref('')
//
const currentRecord = ref(null)
//
const activeName = ref('wait')
//
const payModel = [
//
{
value: '现金',
label: '现金'
},
{
value: '支票',
label: '支票'
},
{
value: '刷卡',
label: '刷卡'
},
{
value: '其他(各地区电子支付)',
label: '其他(各地区电子支付)'
},
]
// ref
const Ref = ref(null)
//
const stats = ref({
totalNum: 0,
totalCoins: 0,
permanentGolds: 0,
freeGolds: 0,
taskGolds: 0
})
//
const sortField = ref('')
const sortOrder = ref('')
//
const rules = reactive({
rejectReason: [{required: true, message: '请输入驳回理由', trigger: 'blur'}]
})
const getAdminData = async function () {
try {
const result = await request({
url: '/admin/userinfo',
data: {}
})
adminData.value = result
console.log('用户信息', adminData.value)
} catch (error) {
console.log('请求失败', error)
}
}
//
const getRecharge = async function (val) {
try {
//
if (typeof val === 'number') {
getObj.value.pageNum = val
}
//
if (getTime.value && getTime.value.length === 2) {
rechargeAudit.value.startTime = formatTime(getTime.value[0])
rechargeAudit.value.endTime = formatTime(getTime.value[1])
} else {
rechargeAudit.value.startTime = ''
rechargeAudit.value.endTime = ''
}
console.log('搜索参数', getObj.value)
const result = await request({
url: '/audit/selectRecharge',
data: {
pageNum: getObj.value.pageNum,
pageSize: getObj.value.pageSize,
rechargeAudit: {
...rechargeAudit.value,
sortField: sortField.value,
sortOrder: sortOrder.value
}
}
})
//
tableData.value = result.list
//
total.value = result.total
} catch (error) {
console.log('请求失败', error)
}
}
const getStats = async () => {
try {
const params = {
pageNum: getObj.value.pageNum,
pageSize: getObj.value.pageSize,
rechargeAudit: rechargeAudit.value
}
const res = await API({
url: '/audit/sumRechargeGold',
data: params
})
stats.value.totalNum = res.totalNum
stats.value.permanentGolds = res.permanentGolds / 100
stats.value.freeGolds = res.freeGolds / 100
stats.value.taskGolds = res.taskGolds / 100
console.log('see see stats和搜索对象', stats.value, params)
} catch (error) {
console.log('请求失败', error)
}
}
//
const handleSearch = function () {
trimJwCode();
getObj.value.pageNum = 1
getRecharge()
getStats()
}
//
const resetSearch = function () {
rechargeAudit.value = {
jwcode: "",
activity: "",
payModel: "",
startTime: "",
endTime: "",
market: "",
auditStatus: rechargeAudit.value.auditStatus
}
getTime.value = []
activeTimeRange.value = '' //
getRecharge()
getStats()
}
//
const getToday = function () {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate()
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + 1
)
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = 'today' //
getRecharge()
getStats()
}
//
const getYesterday = function () {
const yesterday = new Date()
yesterday.setDate(yesterday.getDate() - 1)
const startTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate()
)
const endTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate() + 1
)
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = 'yesterday' //
getRecharge()
getStats()
}
// 7
const get7Days = function () {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() - 6
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + 1
)
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = '7days' //
getRecharge()
getStats()
}
//
const adminWait = async function () {
rechargeAudit.value.auditStatus = "0"
getObj.value.pageNum = 1
await getRecharge()
await getStats()
console.log('adminWait,这是点击待审核调用')
}
//
const adminPass = async function () {
rechargeAudit.value.auditStatus = "1"
getObj.value.pageNum = 1
await getRecharge()
await getStats()
console.log('adminWait,这是点击已通过调用')
}
//
const adminReject = async function () {
rechargeAudit.value.auditStatus = "2"
getObj.value.pageNum = 1
await getRecharge()
await getStats()
console.log('adminWait,这是点击已驳回调用')
}
const handleClick = function (tab, event) {
activeName.value = tab.props.name
if (tab.props.name === 'wait') {
adminWait()
} else if (tab.props.name === 'pass') {
adminPass()
} else if (tab.props.name === 'reject') {
adminReject()
}
}
const getActivity = async function () {
try {
const result = await request({
url: '/general/activity',
data: {}
})
activity.value = result.data
console.log('activity', activity.value)
} catch (error) {
console.log('请求失败', error)
}
}
//
const getMarket = async function () {
try {
const result = await request({
url: '/general/adminMarkets',
data: {
account: adminData.value.account
}
})
market.value = result.data
console.log('地区', market.value)
} catch (error) {
console.log('请求失败', error)
}
}
const handlePagination = (type, val) => {
if (type === 'size') {
getObj.value.pageSize = val
} else {
getObj.value.pageNum = val
}
getRecharge()
getStats()
}
//
const handleApprove = async (row) => {
try {
const params = {
orderCode: row.orderCode,
auditId: adminData.value.id,
action: 1,
rejectReason: ''
}
await request({url: '/audit/audit', data: params})
ElMessage.success('审核通过成功')
await getRecharge()
await getStats()
} catch (error) {
console.error('审核通过失败', error)
ElMessage.error('操作失败')
}
}
//
const showRejectDialog = (row) => {
currentRecord.value = row
rejectReason.value = '' //
rejectDialogVisible.value = true
}
//
const handleReject = async () => {
if (!rejectReason.value.trim()) {
ElMessage.warning('请输入驳回理由')
return
}
try {
const params = {
orderCode: currentRecord.value.orderCode,
auditId: adminData.value.id,
action: 2,
rejectReason: rejectReason.value
}
await request({url: '/audit/audit', data: params})
ElMessage.success('驳回操作成功')
rejectDialogVisible.value = false
await getRecharge()
await getStats()
} catch (error) {
console.error('驳回操作失败', error)
ElMessage.error('操作失败')
}
}
//
const handleSortChange = (column) => {
console.log('排序字段:', column.prop)
console.log('排序方式:', column.order)
if (column.prop === 'money') {
sortField.value = 'permanent_gold'
} else if (column.prop === 'permanentGold') {
sortField.value = 'permanent_gold'
} else if (column.prop === 'freeGold') {
sortField.value = 'freeGold'
} else if (column.prop === 'createTime') {
sortField.value = 'create_time'
} else if (column.prop === 'payTime') {
sortField.value = 'pay_time'
} else if (column.prop === 'auditTime') {
sortField.value = 'audit_time'
}
sortOrder.value = column.order === 'ascending' ? 'asc' : 'desc'
getRecharge()
getStats()
}
//
const previewImage = (imageUrl) => {
// 使 element-plus el-image
const imageElement = document.createElement('img');
imageElement.src = imageUrl;
imageElement.style.maxWidth = '80vw';
imageElement.style.maxHeight = '80vh';
const viewer = document.createElement('div');
viewer.style.position = 'fixed';
viewer.style.top = '0';
viewer.style.left = '0';
viewer.style.width = '100vw';
viewer.style.height = '100vh';
viewer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
viewer.style.display = 'flex';
viewer.style.justifyContent = 'center';
viewer.style.alignItems = 'center';
viewer.style.zIndex = '9999';
viewer.style.overflow = 'auto';
viewer.appendChild(imageElement);
document.body.appendChild(viewer);
viewer.addEventListener('click', () => {
document.body.removeChild(viewer);
});
};
//
onMounted(async function () {
await getAdminData()
await getActivity()
await getMarket()
await getRecharge()
console.log('111')
await getStats()
})
</script>
<style scoped>
.pagination {
display: flex;
}
.operation {
display: flex;
}
.green-dot {
background-color: #67C23A;
}
.grey-dot {
background-color: #909399;
}
.red-dot {
background-color: #F56C6C;
}
.time-controls {
display: flex;
align-items: center;
}
.time-group {
display: flex;
align-items: center;
gap: 10px;
}
.quick-buttons {
display: flex;
align-items: center;
}
.status {
display: flex;
align-items: center;
/* 确保子元素垂直居中对齐 */
gap: 6px;
/* 设置圆点和文字之间的间距 */
}
.green-dot,
.grey-dot,
.red-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
/* 防止圆点在空间不足时缩小 */
margin: 0;
/* 移除原有的 margin-right */
}
/* 备注列样式 */
.remark-cell {
display: block;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 设置单元格内容溢出隐藏 */
.el-table .el-table__cell {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

311
src/views/consume/bean/addBeanConsume.vue

@ -0,0 +1,311 @@
<script setup>
import { ref, onMounted, reactive, computed, watch, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import axios from 'axios'
import { ElMessageBox } from 'element-plus'
import API from '@/util/http.js'
import { uploadFile } from '@/util/request.js';
import request from '@/util/http.js'
import moment from 'moment'
import { e, range, re } from 'mathjs'
import { utils, read } from 'xlsx'
import throttle from 'lodash/throttle'
import { useAdminStore } from "@/store/index.js";
import { storeToRefs } from "pinia";
import _ from 'lodash'
const user = ref({})
const getUser = async function (jwcode) {
if (consumeForm.value.jwcode) {
consumeForm.value.jwcode = consumeForm.value.jwcode.replace(/\s/g, '');
} else {
ElMessage.error('请先输入精网号')
return false
}
try {
const result = await API({
url: '/beanUser/userCard',
data: {
jwcode: consumeForm.value.jwcode
}
})
if (result.code === 0) {
ElMessage.error(result.msg);
} else if (result.data === null) {
ElMessage.error("用户不存在");
} else {
user.value = result.data;
console.log("用户信息", user.value);
ElMessage.success("查询成功");
}
} catch (error) {
console.log("请求失败", error);
ElMessage.error("精网号错误");
}
}
const consumeForm = ref({
jwcode: '',
permanentBean: '',
freeBean: '',
remark: '',
adminName: ''
})
const formRef = ref(null)
const adminStore = useAdminStore()
const { adminData } = storeToRefs(adminStore)
const rules = reactive({
jwcode: [
{ required: true, message: '请输入精网号', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (!value) {
callback(new Error('精网号不能为空'));
return;
}
if (/[^0-9]/.test(value)) {
callback(new Error('精网号只能包含数字'));
return;
}
callback();
}, trigger: 'blur'
}],
permanentBean: [
{ required: true, message: '请输入付费金豆数', trigger: 'change' },
{
validator: (rule, value, callback) => {
if(!value){
value = 0
}
//
if (!/^\d+$/.test(value)) {
callback(new Error('请输入非负整数'));
return;
}
//
if (value.length > 6) {
callback(new Error('整数位数不能超过6位'));
return;
}
callback();
},
trigger: 'blur'
}
],
freeBean: [
{ required: true, message: '请输入免费金豆数', trigger: 'change' },
{
validator: (rule, value, callback) => {
if(!value){
value = 0
}
//
if (!/^\d+$/.test(value)) {
callback(new Error('请输入非负整数'));
return;
}
//
if (value.length > 6) {
callback(new Error('整数位数不能超过6位'));
return;
}
callback();
},
trigger: 'blur'
}
],
remark: [
{ required: true, message: '请输入备注', trigger: 'blur' }
]
});
//
const deleteConsumeForm = function () {
consumeForm.value = {
jwcode: '',
permanentBean: '',
freeBean: '',
remark: '',
adminName: ''
}
}
const handleConsumeForm = async () => {
try {
if(!consumeForm.value.permanentBean ){
consumeForm.value.permanentBean = 0
}
if(!consumeForm.value.freeBean ){
consumeForm.value.freeBean = 0
}
await new Promise((resolve, reject) => {
formRef.value.validate((valid) => {
if (valid) {
if (Number(consumeForm.value.permanentBean) === 0 && Number(consumeForm.value.freeBean) === 0) {
reject(new Error('付费金豆和免费金豆不能同时为0'));
}
resolve(); //
} else {
reject(new Error('请检查并完善表单信息')); //
}
});
});
console.log('adminData', adminData.value);
//
const inputPermanentBean = Number(consumeForm.value.permanentBean);
const inputFreeBean = Number(consumeForm.value.freeBean);
const userPermanentBean = Number(user.value.permanentBean) || 0;
const userFreeBean = Number(user.value.freeBean) || 0;
// if (inputPermanentBean > userPermanentBean) {
// throw new Error('');
// }
// if (inputFreeBean > userFreeBean) {
// throw new Error('');
// }
await ElMessageBox.confirm(
'确认消耗吗?',
'提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: "primary",
lockScroll: false,
}
)
const result = await request({
url: '/beanConsume/reduce',
data: {
jwcode: consumeForm.value.jwcode,
permanentBean: consumeForm.value.permanentBean,
freeBean: consumeForm.value.freeBean,
remark: consumeForm.value.remark,
adminName: adminData.value.adminName
}
})
if (result.code == 200) {
ElMessage.success('新增成功')
consumeForm.value = {
jwcode: "",
permanentBean: '',
freeBean: '',
remark: '',
adminName: ''
}
}else{
ElMessage.error(result.msg)
}
} catch (error) {
console.log('金豆新增充值失败');
ElMessage.error(error.message || '操作失败');
}
}
const throttledHandleConsumeFormt = _.throttle(handleConsumeForm, 5000, {
trailing: false
})
</script>
<template>
<div>
<el-form :model="consumeForm" :rules="rules" ref="formRef" label-width="auto" style="max-width: 600px" class="consume-form">
<el-form-item prop="jwcode" label="精网号" label-position="left">
<el-input v-model="consumeForm.jwcode" style="width: 220px" />
<el-button type="primary" @click="getUser(consumeForm.jwcode)" style="margin-left: 20px">查询</el-button>
</el-form-item>
<el-form-item prop="permanentBean" label="付费金豆" label-position="left">
<el-input v-model="consumeForm.permanentBean" placeholder="0" style="width: 100px" />
</el-form-item>
<el-form-item prop="freeBean" label="免费金豆" label-position="left">
<el-input v-model="consumeForm.freeBean" placeholder="0" style="width: 100px" />
</el-form-item>
<el-form-item prop="remark" label="备注" label-position="left">
<el-input v-model="consumeForm.remark" style="width: 300px" :rows="5" maxlength="100" show-word-limit
type="textarea" />
</el-form-item>
<!-- <el-form-item prop="adminName" label="提交人">
<el-input style="width: 300px" :value="adminData.adminName" disabled placeholder="提交人姓名" />
</el-form-item> -->
<el-button @click="deleteConsumeForm" style="margin-left: 280px" type="success">重置</el-button>
<el-button type="primary" @click="handleConsumeForm"> 提交 </el-button>
</el-form>
<!-- 客户信息栏 -->
<el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info">
<el-form :model="user" label-width="auto" style="max-width: 1000px" label-position="left">
<el-text size="large" style="margin-left: 20px">客户信息</el-text>
<!-- 第一行姓名 + 当前付费金豆 -->
<el-row style="margin-top: 20px">
<el-col :span="9">
<el-form-item label="姓名:">
<p style="color: #2fa1ff; margin-right: 5px">{{ user.name }}</p>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="当前付费金豆:">
<p style="color: #2fa1ff; margin-right: 5px" v-if="!isNaN(Number(user.permanentBean))">
{{ Number(user.permanentBean) }}
</p>
<!-- 如果不是有效的数字显示默认值 -->
<p v-else></p>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行精网号 + 免费金豆 -->
<el-row >
<el-col :span="9">
<el-form-item label="精网号">
<p style="color: #2fa1ff; margin-right: 5px">{{ user.jwcode }}</p>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="当前免费金豆:">
<span style="color: #2fa1ff; margin-right: 5px" v-if="user.freeBean !== undefined">{{ user.freeBean }}
</span>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行消费次数 + 所属门店 -->
<el-row>
<el-col :span="9">
<el-form-item label="所属门店">
<p style="color: #2fa1ff">{{ user.market }}</p>
</el-form-item>
</el-col>
<el-col :span="9">
<el-form-item label="消耗金豆总数">
<p style="color: #2fa1ff; margin-right: 5px" v-if="user.consumeSum!=null">{{ user.consumeSum }}</p>
<p style="color: #2fa1ff; margin-right: 5px" v-else>{{ 0 }}</p>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</template>
<style scoped>
.consume-form {
margin-top: 50px;
max-width: 50%;
float: left;
}
.customer-info {
max-width: 60%;
}
p {
margin: 0px;
}
.el-form-item {
margin-left: 50px;
}
</style>

598
src/views/consume/bean/articleVideo.vue

@ -0,0 +1,598 @@
<script setup>
import { computed, onMounted, ref } from 'vue'
import { dayjs, ElMessage } from 'element-plus'
import request from '@/util/http.js'
import API from '@/util/http.js'
import moment from 'moment'
import { ar } from 'element-plus/es/locales.mjs'
//
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
/*
====================工具方法==============================
*/
//
const formatTime = (val) => val ? dayjs(val).format('YYYY-MM-DD HH:mm:ss') : ''
/*
====================数据=================================
*/
//
const adminData = ref({})
//
const tableData = ref([])
// articleVideo
const beanConsumeArticle = ref({
jwcode: null,
dept: "",
type: "",
payMode: "",
articleId: "",
articleName: "",
author: "",
startTime: '',
endTime: '',
})
//
const channels = ref([])
//
const consumeTypes = ref([
{ label: '打赏', value: 9 },
{ label: '打赏', value: 10 },
{ label: '付费购买', value: 11 },
])
// payMode
const handlePayModeChange = (value) => {
beanConsumeArticle.value.payMode = value;
//
ConsumeSelectBy();
}
//------------------------
//
const activeTimeRange = ref('')
//
const handleDatePickerChange = () => {
activeTimeRange.value = ''
}
//
const getObj = ref({
pageNum: 1,
pageSize: 50
})
//
const total = ref(100)
//
const getTime = ref({
startTime: '',
endTime: ''
})
//
const dept = ref([])
//
const getDept = async function () {
try {
//
const result = await request({
// url: '/general/dept',
url: '/beanConsume/getDept', // todo
data: { account: adminData.value.account }
})
console.log('请求地区列表成功', result)
//
dept.value = result.data
console.log('地区数据', dept.value)
} catch (error) {
console.log('请求地区列表失败', error)
ElMessage({
type: 'error',
message: '获取地区列表失败,请稍后重试'
})
}
}
// //
// const filterChannel = (query) => {
// if (query) {
// return channels.value.filter(item => item.toLowerCase().includes(query.toLowerCase()));
// }
// return channels.value;
// };
//
const sortField = ref('')
const sortOrder = ref('')
//
const permanentBean = ref(0)
const freeBean = ref(0)
const totalNum = ref(0)
/*
====================方法=================================
*/
//
const getAdminData = async function () {
try {
const result = await request({
url: '/admin/userinfo',
data: {}
})
adminData.value = result
console.log('请求成功', result)
console.log('用户信息', adminData.value)
} catch (error) {
console.log('请求失败', error)
}
}
const ConsumeSelectBy = async function (val) {
try {
//
if (typeof val === 'number') {
getObj.value.pageNum = val
}
//
if (getTime.value != null) {
if (getTime.value.startTime != '' && getTime.value.endTime != '') {
beanConsumeArticle.value.startTime = formatTime(getTime.value[0])
beanConsumeArticle.value.endTime = formatTime(getTime.value[1])
}
} else {
beanConsumeArticle.value.startTime = ''
beanConsumeArticle.value.endTime = ''
}
beanConsumeArticle.value.sortField = sortField.value
beanConsumeArticle.value.sortOrder = sortOrder.value
console.log('搜索参数_时间', beanConsumeArticle.value.startTime)
console.log('搜索参数1', getObj.value)
console.log('搜索参数2', beanConsumeArticle.value)
// POST
const result = await request({
url: '/beanConsume/selectArticleBy',
data: {
pageNum: getObj.value.pageNum,
pageSize: getObj.value.pageSize,
beanConsumeArticle: {
...beanConsumeArticle.value,
jwcode: beanConsumeArticle.value.jwcode,
dept: beanConsumeArticle.value.dept,
payMode: beanConsumeArticle.value.payMode,
articleId: beanConsumeArticle.value.articleId,
articleName: beanConsumeArticle.value.articleName,
author: beanConsumeArticle.value.author,
startTime: beanConsumeArticle.value.startTime,
endTime: beanConsumeArticle.value.endTime,
sortField: beanConsumeArticle.value.sortField,
sortOrder: beanConsumeArticle.value.sortOrder,
}
}
})
console.log('请求成功4', sortField)
console.log('接口响应结果', result); //
if (result.code === 200 && result.data && result.data.list) {
// type payMode
const filteredList = result.data.list.filter(item => {
if (beanConsumeArticle.value.payMode === '0') {
return [9, 10].includes(Number(item.type));
} else if (beanConsumeArticle.value.payMode === '1') {
return Number(item.type) === 11;
}
return true;
});
tableData.value = filteredList;
}
//
// beanConsumeArticle.value payType 8
const sumConsumeParams = {
payType: 8, // payType 8
beanConsumeArticle: {
...beanConsumeArticle.value,
}
};
// POST
const resultTotalGold = await request({
url: '/beanConsume/sumConsumeGold',
data: sumConsumeParams
});
console.log("总计", resultTotalGold);
const data = resultTotalGold.data || resultTotalGold;
console.log('请求成功3', resultTotalGold.data)
console.log('permanentBean3', data.permanentBean)
// permanentBeanfreeBeantotalNum
permanentBean.value = Number(data.permanentBean) || 0;
freeBean.value = Number(data.freeBean) || 0;
totalNum.value = Number(data.totalNum) || 0;
//
total.value = result.data.total
console.log('total', total.value)
} catch (error) {
console.log('请求失败', error)
}
}
//
const search = function () {
getObj.value.pageNum = 1
if (beanConsumeArticle.value.jwcode) {
const numRef = /^\d{1,9}$/;
if (!numRef.test(beanConsumeArticle.value.jwcode)) {
ElMessage.error('请检查精网号格式')
return
}
}
if (beanConsumeArticle.value.articleId) {
const numRef = /^\d{1,9}$/;
if (!numRef.test(beanConsumeArticle.value.articleId)) {
ElMessage.error('请检查文章ID格式')
return
}
}
ConsumeSelectBy()
}
//
const reset = function () {
console.log('文章/视频的重置')
beanConsumeArticle.value.jwcode = null
beanConsumeArticle.value.dept = ''
beanConsumeArticle.value.type = ''
beanConsumeArticle.value.payMode = ''
beanConsumeArticle.value.articleId = ''
beanConsumeArticle.value.articleName = ''
beanConsumeArticle.value.author = ''
beanConsumeArticle.value.startTime = ''
beanConsumeArticle.value.endTime = ''
sortField.value = ''
sortOrder.value = ''
getTime.value = {}
activeTimeRange.value = '' //
//
ConsumeSelectBy()
console.log(' beanConsumeArticle', beanConsumeArticle.value)
}
//
const getToday = function () {
const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = 'today' //
ConsumeSelectBy()
}
//
const getYesterday = function () {
const today = dayjs()
const startTime = today.subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = 'yesterday' //
ConsumeSelectBy()
}
// 7
const get7Days = function () {
const today = dayjs()
const startTime = today.subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = '7days' //
ConsumeSelectBy()
}
//
const handleSortChange = (column) => {
console.log('排序字段:', column.prop)
console.log('排序方式:', column.order)
if (column.prop === 'beanNum') {
sortField.value = 'beanNum'
} else if (column.prop === 'consumeTime') {
sortField.value = 'consumeTime'
} else if (column.prop === 'buyBean') {
sortField.value = 'buyBean'
} else if (column.prop === 'freeBean') {
sortField.value = 'freeBean'
}
sortOrder.value = column.order === 'ascending' ? 'DESC' : 'ASC'
ConsumeSelectBy()
}
const handlePageSizeChange = function (val) {
getObj.value.pageSize = val
ConsumeSelectBy()
}
const handleCurrentChange = function (val) {
getObj.value.pageNum = val
ConsumeSelectBy()
}
/*
====================计算属性=================================
*/
//
// const totalBean = computed(() => permanentBean.value + freeBean.value)
/*
====================监听=================================
*/
/*
====================挂载=================================
*/
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
onMounted(async function () {
await getAdminData()
await ConsumeSelectBy()
await getDept()
})
const exportExcel = async function () {
const params = { //
...getObj.value,
"beanConsumeArticle": {
...beanConsumeArticle.value,
sortField: sortField.value,
sortOrder: sortOrder.value,
},
}
const res = await API({ url: '/export/exportArticle', data: params })
if (res.code === 200) {
ElMessage.success('导出成功')
}
}
const exportListVisible = ref(false)
//
const openExportList = () => {
getExportList()
exportListVisible.value = true
}
//
const exportList = ref([])
//
const exportListLoading = ref(false)
//
const getExportList = async () => {
exportListLoading.value = true
try {
const result = await API({ url: '/export/export' })
if (result.code === 200) {
const filteredData = result.data.filter(item => {
return item.type === 8; //4 // todo type 8/
});
exportList.value = filteredData
} else {
ElMessage.error(result.msg || '获取导出列表失败')
}
} catch (error) {
console.error('获取导出列表出错:', error)
ElMessage.error('获取导出列表失败,请稍后重试')
} finally {
exportListLoading.value = false
}
}
//
const downloadExportFile = (item) => {
if (item.state === 2) {
const link = document.createElement('a')
link.href = item.url
link.download = item.fileName
link.click()
} else {
ElMessage.warning('文件还在导出中,请稍后再试')
}
}
//
const getTagType = (state) => {
switch (state) {
case 0:
return 'info';
case 1:
return 'primary';
case 2:
return 'success';
case 3:
return 'danger';
default:
return 'info';
}
}
//
const getTagText = (state) => {
switch (state) {
case 0:
return '待执行';
case 1:
return '执行中';
case 2:
return '执行完成';
case 3:
return '执行出错';
default:
return '未知状态';
}
}
</script>
<template>
<el-card style="margin-bottom: 10px;margin-top:10px">
<el-col style="margin-bottom: 10px">
<el-text>精网号</el-text>
<el-input v-model="beanConsumeArticle.jwcode" placeholder="请输入精网号" style="width: 200px;margin-right: 20px" clearable />
<el-text>地区</el-text>
<el-select v-model="beanConsumeArticle.dept" placeholder="请选择地区" style="width: 200px;margin-right: 20px" clearable>
<el-option v-for="(item, index) in dept" :key="index" :label="item" :value="item" />
</el-select>
<el-text>类型</el-text>
<el-select v-model="beanConsumeArticle.payMode" placeholder="请选择类型" style="width: 200px;margin-right: 20px" clearable
@change="handlePayModeChange">
<el-option label="打赏" value="0" />
<el-option label="付费购买" value="1" />
<el-option label="其他" value="2" />
</el-select>
<el-text>文章/视频ID</el-text>
<el-input v-model="beanConsumeArticle.articleId" placeholder="请输入文章/视频ID" style="width: 200px;margin-right: 20px" clearable />
<el-text>文章/视频标题</el-text>
<el-input v-model="beanConsumeArticle.articleName" placeholder="请输入文章/视频标题" style="width: 200px" clearable />
</el-col>
<el-col>
<el-text>作者</el-text>
<el-input v-model="beanConsumeArticle.author" placeholder="请输入作者" style="width: 200px;margin-right: 20px" clearable />
<el-text>付费时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 400px;margin-right: 10px" @change="handleDatePickerChange"
value-format="YYYY-MM-DD HH:mm:ss" :default-time="defaultTime" />
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''"></el-button>
<el-button @click="getYesterday()" :type="activeTimeRange === 'yesterday' ? 'primary' : ''"></el-button>
<el-button @click="get7Days()" :type="activeTimeRange === '7days' ? 'primary' : ''">近7天</el-button>
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button>
<el-button type="primary" @click="exportExcel()">导出Excel</el-button>
<el-button type="primary" @click="openExportList">查看导出列表</el-button>
</el-col>
</el-card>
<el-card>
<div>
金豆总数{{ format3(Math.abs(permanentBean + freeBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
付费金豆数{{ format3(Math.abs(permanentBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金豆数{{ format3(Math.abs(freeBean)) }}
</div>
<div style="overflow-y: auto">
<el-table :data="tableData" style="width: 100%" height="550px" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<!-- 固定姓名列 -->
<el-table-column prop="name" label="姓名" width="150px" fixed="left" />
<!-- 固定精网号列 -->
<el-table-column prop="jwcode" label="精网号" width="110px" fixed="left" />
<el-table-column prop="dept" label="地区" width="110px" />
<el-table-column prop="type" label="类型" width="120px">
<template #default="scope">
{{
Array.isArray(consumeTypes)
? consumeTypes.find(item => item.value === Number(scope.row.type))?.label || '未知类型'
: '未知类型'
}}
</template>
</el-table-column>
<el-table-column prop="beanNum" label="金豆总数" sortable="custom" width="120px" />
<el-table-column prop="buyBean" label="付费金豆数" sortable="custom" width="120px" />
<el-table-column prop="freeBean" label="免费金豆数" sortable="custom" width="120px" />
<el-table-column prop="articleId" label="文章/视频ID" width="150px" />
<el-table-column prop="articleName" label="文章/视频标题" width="150px" show-overflow-tooltip />
<el-table-column prop="author" label="作者" width="120px" />
<el-table-column prop="consumeTime" label="付费时间" sortable="custom" width="180px">
<template #default="scope">
{{ formatTime(scope.row.consumeTime) }}
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</div>
</el-card>
<!-- 导出弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" label="文件名" />
<el-table-column prop="state" label="状态">
<template #default="scope">
<el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
{{ getTagText(scope.row.state) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template #default="scope">
{{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" size="small" @click="downloadExportFile(scope.row)"
:disabled="scope.row.state !== 2">
下载
</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button text @click="exportListVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped>
.pagination {
display: flex;
margin-top: 20px;
}
</style>

110
src/views/consume/bean/beanConsume.vue

@ -0,0 +1,110 @@
<template>
<div>
<el-button-group>
<el-button
:type="activeTab === 'addBeanConsume' ? 'primary' : 'default'"
@click="navigateTo('addBeanConsume')"
:disabled="!hasAdd"
style="width: 120px;"
>
新增消耗
</el-button>
<el-button
:type="activeTab === 'liveStream' ? 'primary' : 'default'"
@click="navigateTo('liveStream')"
:disabled="!hasLive"
style="width: 120px;"
>
直播
</el-button>
<el-button
:type="activeTab === 'dieHardFan' ? 'primary' : 'default'"
@click="navigateTo('dieHardFan')"
:disabled="!hasFan"
style="width: 120px;"
>
铁粉
</el-button>
<el-button
:type="activeTab === 'articleVideo' ? 'primary' : 'default'"
@click="navigateTo('articleVideo')"
:disabled="!hasArticleVideo"
style="width: 120px;"
>
文章/视频
</el-button>
</el-button-group>
<router-view></router-view>
</div>
</template>
<script setup>
import {ref, watch, onMounted} from 'vue';
import {useRouter, useRoute} from 'vue-router';
import {storeToRefs} from 'pinia';
import {useAdminStore} from '@/store/index.js';
import {hasMenuPermission, permissionMapping} from "@/utils/menuTreePermission.js";
const router = useRouter();
const route = useRoute();
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const activeTab = ref('');
const hasAdd = ref(false);
const hasLive = ref(false);
const hasFan = ref(false);
const hasArticleVideo = ref(false);
//
const navigateTo = (name) => {
activeTab.value = name;
router.push({name});
};
//
const initPermissions = () => {
if (!menuTree.value || !menuTree.value.length) return;
hasAdd.value = hasMenuPermission(menuTree.value, permissionMapping.Submit_Golden_Bean_Consumption);
hasLive.value = hasMenuPermission(menuTree.value, permissionMapping.View_Golden_Bean_Live_Consumption_Details);
hasFan.value = hasMenuPermission(menuTree.value, permissionMapping.View_Golden_Bean_Fan_Consumption_Details);
hasArticleVideo.value = hasMenuPermission(menuTree.value, permissionMapping.View_Golden_Bean_Article_Video_Consumption_Details);
};
//
const getDefaultAuditRoute = () => {
initPermissions();
if (hasAdd.value) return 'addBeanConsume';
if (hasLive.value) return 'liveStream';
if (hasFan.value) return 'dieHardFan';
if (hasArticleVideo.value) return 'articleVideo';
return 'addBeanConsume';
};
//
watch(() => route.name, (newName) => {
initPermissions()
if (newName=== 'addBeanConsume' || newName === 'liveStream' || newName === 'dieHardFan' || newName === 'articleVideo') {
activeTab.value = newName;
} else if (newName === 'beanConsume') {
// 访 /beanConsume
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
}
});
//
onMounted(() => {
initPermissions()
if (route.name === 'beanConsume') {
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
} else {
//
if (route.name=== 'addBeanConsume' || route.name === 'liveStream' || route.name === 'dieHardFan' || route.name === 'articleVideo') {
activeTab.value = route.name;
}
}
});
</script>

581
src/views/consume/bean/dieHardFan.vue

@ -0,0 +1,581 @@
<script setup>
import { computed, onMounted, ref } from 'vue'
import { dayjs, ElMessage } from 'element-plus'
import request from '@/util/http.js'
import API from '@/util/http.js'
import moment from 'moment'
//
/*
====================工具方法==============================
*/
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
//
const formatTime = (val) => val ? dayjs(val).format('YYYY-MM-DD HH:mm:ss') : ''
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
/*
====================数据=================================
*/
//
const adminData = ref({})
//
const tableData = ref([])
// beanConsumeFan
const beanConsumeFan = ref({
jwcode: null,
dept: "",
type: "",
gift: "",
channel: "",
liveRoom: "",
startTime: '',
endTime: '',
})
//
const channels = ref([])
//
const getChannel = async function () {
try {
const result = await request({
url: '/beanConsume/getLiveChannel', // todo
data: { account: adminData.value.account }
})
console.log('请求频道列表成功', result)
//
channels.value = result.data
console.log('频道数据', channels.value)
} catch (error) {
console.log('请求频道列表失败', error)
ElMessage({
type: 'error',
message: '获取频道列表失败,请稍后重试'
})
}
}
// //
// const filterChannel = (query) => {
// if (query) {
// return channels.value.filter(item => item.toLowerCase().includes(query.toLowerCase()));
// }
// return channels.value;
// };
//
const consumeTypes = ref([
{ label: '发礼物', value: 1 },
{ label: '发红包', value: 2 },
{ label: '发福袋', value: 3 },
{ label: '付费直播', value: 4 },
{ label: '加入粉丝团', value: 5 },
{ label: '发弹幕', value: 6 },
{ label: '单次付费', value: 7 },
{ label: '连续包月', value: 8 }
])
// //
// const handleTypeChange = (value) => {
// if (value !== 1) {
// beanConsumeFan.value.gift = ''
// }
// }
//------------------------
//
const activeTimeRange = ref('')
//
const handleDatePickerChange = () => {
activeTimeRange.value = ''
}
//
const getObj = ref({
pageNum: 1,
pageSize: 50
})
//
const total = ref(100)
//
const getTime = ref({
startTime: '',
endTime: ''
})
//
// const activity = ref([])
//
const dept = ref([])
//
const sortField = ref('')
const sortOrder = ref('')
//
const permanentBean = ref(0)
const freeBean = ref(0)
const totalNum = ref(0)
/*
====================方法=================================
*/
//
const getAdminData = async function () {
try {
const result = await request({
url: '/admin/userinfo',
data: {}
})
adminData.value = result
console.log('请求成功', result)
console.log('用户信息', adminData.value)
} catch (error) {
console.log('请求失败', error)
}
}
const ConsumeSelectBy = async function (val) {
try {
//
if (typeof val === 'number') {
getObj.value.pageNum = val
}
//
if (getTime.value != null) {
if (getTime.value.startTime != '' && getTime.value.endTime != '') {
beanConsumeFan.value.startTime = formatTime(getTime.value[0])
beanConsumeFan.value.endTime = formatTime(getTime.value[1])
}
} else {
beanConsumeFan.value.startTime = ''
beanConsumeFan.value.endTime = ''
}
beanConsumeFan.value.sortField = sortField.value
beanConsumeFan.value.sortOrder = sortOrder.value
console.log('搜索参数_时间', beanConsumeFan.value.startTime)
console.log('搜索参数1', getObj.value)
console.log('搜索参数2', beanConsumeFan.value)
// POST
const result = await request({
url: '/beanConsume/selectFanBy',
data: {
pageNum: getObj.value.pageNum,
pageSize: getObj.value.pageSize,
beanConsumeFan: {
...beanConsumeFan.value,
jwcode: beanConsumeFan.value.jwcode ? String(beanConsumeFan.value.jwcode) : '',
dept: beanConsumeFan.value.dept || '',
channel: beanConsumeFan.value.channel || '',
startTime: beanConsumeFan.value.startTime || '',
endTime: beanConsumeFan.value.endTime || '',
sortField: beanConsumeFan.value.sortField || 'consumeTime',
sortOrder: beanConsumeFan.value.sortOrder || 'desc'
}
}
})
console.log('请求成功3', sortField)
console.log('接口响应结果', result);
if (result.code === 200 && result.data && result.data.list) {
tableData.value = result.data.list;
total.value = result.data.total;
}
//
// beanConsumeFan.value payType 1 7
const sumConsumeParams = {
payType: 7, // payType 7
beanConsumeFan: {
...beanConsumeFan.value,
}
};
// POST
const resultTotalGold = await request({
url: '/beanConsume/sumConsumeGold',
data: sumConsumeParams
});
console.log("总计2", resultTotalGold);
const data = resultTotalGold.data || resultTotalGold;
console.log('请求成功2', resultTotalGold.data)
console.log('permanentBean2', data.permanentBean)
// permanentBeanfreeBeantotalNum
permanentBean.value = Number(data.permanentBean) || 0;
freeBean.value = Number(data.freeBean) || 0;
totalNum.value = Number(data.totalNum) || 0;
//
total.value = result.data.total
console.log('total', total.value)
} catch (error) {
console.log('请求失败', error)
}
}
//
const search = function () {
getObj.value.pageNum = 1
if (beanConsumeFan.value.jwcode) {
const numRef = /^\d{1,9}$/;
if (!numRef.test(beanConsumeFan.value.jwcode)) {
ElMessage.error('请检查精网号格式')
return
}
}
ConsumeSelectBy()
}
//
const reset = function () {
console.log('兄弟,你点了重置')
beanConsumeFan.value.jwcode = null
beanConsumeFan.value.type = ''
beanConsumeFan.value.gift = ''
beanConsumeFan.value.channel = ''
beanConsumeFan.value.liveRoom = ''
beanConsumeFan.value.dept = ''
beanConsumeFan.value.startTime = ''
beanConsumeFan.value.endTime = ''
sortField.value = ''
sortOrder.value = ''
getTime.value = {}
activeTimeRange.value = '' //
//
ConsumeSelectBy()
console.log(' beanConsumeFan', beanConsumeFan.value)
}
//
const getToday = function () {
const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = 'today' //
ConsumeSelectBy()
}
//
const getYesterday = function () {
const today = dayjs()
const startTime = today.subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = 'yesterday' //
console.log('昨', getTime.value)
ConsumeSelectBy()
}
// 7
const get7Days = function () {
const today = dayjs()
const startTime = today.subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = '7days' //
ConsumeSelectBy()
}
//
const getDept = async function () {
try {
//
const result = await request({
// url: '/general/dept',
url: '/beanConsume/getDept', // todo
data: { account: adminData.value.account }
})
console.log('请求地区列表成功', result)
//
dept.value = result.data
console.log('地区数据', dept.value)
} catch (error) {
console.log('请求地区列表失败', error)
ElMessage({
type: 'error',
message: '获取地区列表失败,请稍后重试'
})
}
}
//
const handleSortChange = (column) => {
console.log('排序字段:', column.prop)
console.log('排序方式:', column.order)
if (column.prop === 'beanNum') {
sortField.value = 'beanNum'
} else if (column.prop === 'consumeTime') {
sortField.value = 'consumeTime'
} else if (column.prop === 'buyBean') {
sortField.value = 'buyBean'
} else if (column.prop === 'freeBean') {
sortField.value = 'freeBean'
}
sortOrder.value = column.order === 'ascending' ? 'DESC' : 'ASC'
ConsumeSelectBy()
}
const handlePageSizeChange = function (val) {
getObj.value.pageSize = val
ConsumeSelectBy()
}
const handleCurrentChange = function (val) {
getObj.value.pageNum = val
ConsumeSelectBy()
}
/*
====================计算属性=================================
*/
//
// const totalBean = computed(() => permanentBean.value + freeBean.value)
/*
====================监听=================================
*/
/*
====================挂载=================================
*/
onMounted(async function () {
await getAdminData()
await ConsumeSelectBy()
await getChannel()
await getDept()
})
const exportExcel = async function () {
const params = { //
...getObj.value,
"beanConsumeFan": {
...beanConsumeFan.value,
sortField: sortField.value,
sortOrder: sortOrder.value,
},
}
const res = await API({ url: '/export/exportFan', data: params })
if (res.code === 200) {
ElMessage.success('导出成功')
}
}
const exportListVisible = ref(false)
//
const openExportList = () => {
getExportList()
exportListVisible.value = true
}
//
const exportList = ref([])
//
const exportListLoading = ref(false)
//
const getExportList = async () => {
exportListLoading.value = true
try {
const result = await API({ url: '/export/export' })
if (result.code === 200) {
const filteredData = result.data.filter(item => {
return item.type === 7; //4 // todo type 7
});
exportList.value = filteredData
} else {
ElMessage.error(result.msg || '获取导出列表失败')
}
} catch (error) {
console.error('获取导出列表出错:', error)
ElMessage.error('获取导出列表失败,请稍后重试')
} finally {
exportListLoading.value = false
}
}
//
const downloadExportFile = (item) => {
if (item.state === 2) {
const link = document.createElement('a')
link.href = item.url
link.download = item.fileName
link.click()
} else {
ElMessage.warning('文件还在导出中,请稍后再试')
}
}
//
const getTagType = (state) => {
switch (state) {
case 0:
return 'info';
case 1:
return 'primary';
case 2:
return 'success';
case 3:
return 'danger';
default:
return 'info';
}
}
//
const getTagText = (state) => {
switch (state) {
case 0:
return '待执行';
case 1:
return '执行中';
case 2:
return '执行完成';
case 3:
return '执行出错';
default:
return '未知状态';
}
}
</script>
<template>
<el-card style="margin-bottom: 20px;margin-top:10px">
<el-col style="margin-bottom: 10px">
<el-text>精网号</el-text>
<el-input v-model="beanConsumeFan.jwcode" placeholder="请输入精网号" style="width: 200px;margin-right: 20px"
clearable />
<el-text>地区</el-text>
<el-select v-model="beanConsumeFan.dept" placeholder="请选择地区" style="width: 200px;margin-right: 20px" clearable>
<el-option v-for="(item, index) in dept" :key="index" :label="item" :value="item" />
</el-select>
<el-text>频道</el-text>
<el-select v-model="beanConsumeFan.channel" placeholder="请选择频道" style="width: 200px" clearable filterable allow-create>
<el-option v-for="(item, index) in channels" :key="index" :label="item" :value="item" />
</el-select>
</el-col>
<el-col>
<el-text>消费时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 400px;margin-right:20px" @change="handleDatePickerChange"
value-format="YYYY-MM-DD HH:mm:ss" :default-time="defaultTime" />
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''"></el-button>
<el-button @click="getYesterday()" :type="activeTimeRange === 'yesterday' ? 'primary' : ''"></el-button>
<el-button @click="get7Days()" :type="activeTimeRange === '7days' ? 'primary' : ''">近7天</el-button>
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button>
<el-button type="primary" @click="exportExcel()">导出Excel</el-button>
<el-button type="primary" @click="openExportList">查看导出列表</el-button>
</el-col>
</el-card>
<el-card>
<div>
金豆总数{{ format3(Math.abs(permanentBean + freeBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
付费金豆数{{ format3(Math.abs(permanentBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金豆数{{ format3(Math.abs(freeBean)) }}
</div>
<div style="overflow-y: auto">
<el-table :data="tableData" style="width: 100%" height="550px" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<!-- 固定姓名列 -->
<el-table-column prop="name" label="姓名" width="150px" fixed="left" />
<!-- 固定精网号列 -->
<el-table-column prop="jwcode" label="精网号" width="110px" fixed="left" />
<el-table-column prop="dept" label="地区" width="110px" />
<el-table-column prop="beanNum" label="金豆数量" sortable="custom" width="120px" />
<el-table-column prop="buyBean" label="付费金豆数" sortable="custom" width="120px" />
<el-table-column prop="freeBean" label="免费金豆数" sortable="custom" width="120px" />
<el-table-column prop="channel" label="频道" width="190px" />
<el-table-column prop="type" label="会员类型" width="120px">
<template #default="scope">
{{consumeTypes.find(item => item.value === Number(scope.row.type))?.label || '未知类型'}}
</template>
</el-table-column>
<el-table-column prop="consumeTime" label="加入时间" sortable="custom" width="180px" />
</el-table>
</div>
<!-- 分页 -->
<div class="pagination">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</div>
</el-card>
<!-- 导出弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" label="文件名" />
<el-table-column prop="state" label="状态">
<template #default="scope">
<el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
{{ getTagText(scope.row.state) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template #default="scope">
{{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" size="small" @click="downloadExportFile(scope.row)"
:disabled="scope.row.state !== 2">
下载
</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button text @click="exportListVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped>
.pagination {
display: flex;
margin-top: 20px;
}
</style>

627
src/views/consume/bean/liveStream.vue

@ -0,0 +1,627 @@
<script setup>
import { computed, onMounted, ref } from 'vue'
import { dayjs, ElMessage } from 'element-plus'
import request from '@/util/http.js'
import API from '@/util/http.js'
import moment from 'moment'
//
/*
====================工具方法==============================
*/
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
//
const formatTime = (val) => val ? dayjs(val).format('YYYY-MM-DD HH:mm:ss') : ''
/*
====================数据=================================
*/
//
const adminData = ref({})
//
const tableData = ref([])
// beanConsumeLive
const beanConsumeLive = ref({
jwcode: null,
dept: "",
type: "",
gift: "",
liveChannel: "",
liveName: "",
startTime: '',
endTime: '',
sortField: '',
sortOrder: ''
})
//
const gifts = ref([])
//
const getGift = async function () {
try {
const result = await request({
url: '/beanConsume/getLiveGift', // todo
data: { account: adminData.value.account }
})
console.log('请求礼物列表成功', result)
//
gifts.value = result.data
console.log('礼物数据', gifts.value)
} catch (error) {
console.log('请求礼物列表失败', error)
ElMessage({
type: 'error',
message: '获取礼物列表失败,请稍后重试'
})
}
}
//
const channels = ref([])
//
const getChannel = async function () {
try {
const result = await request({
url: '/beanConsume/getLiveChannel', // todo
data: { account: adminData.value.account }
})
console.log('请求频道列表成功', result)
//
channels.value = result.data
console.log('频道数据', channels.value)
} catch (error) {
console.log('请求频道列表失败', error)
ElMessage({
type: 'error',
message: '获取频道列表失败,请稍后重试'
})
}
}
//
const consumeTypes = ref([
{ label: '发礼物', value: 1 },
{ label: '发红包', value: 2 },
{ label: '发福袋', value: 3 },
{ label: '付费直播', value: 4 },
{ label: '加入粉丝团', value: 5 },
{ label: '发弹幕', value: 6 }
])
//
const handleTypeChange = (value) => {
if (value !== 1) {
beanConsumeLive.value.gift = ''
}
}
//------------------------
//
const activeTimeRange = ref('')
//
const handleDatePickerChange = () => {
activeTimeRange.value = ''
}
//
const getObj = ref({
pageNum: 1,
pageSize: 50
})
//
const total = ref(100)
//
const getTime = ref({
startTime: '',
endTime: ''
})
//
const dept = ref([])
//
const getDept = async function () {
try {
//
const result = await request({
// url: '/general/dept',
url: '/beanConsume/getDept', // todo
data: { account: adminData.value.account }
})
console.log('请求地区列表成功', result)
//
dept.value = result.data
console.log('地区数据', dept.value)
} catch (error) {
console.log('请求地区列表失败', error)
ElMessage({
type: 'error',
message: '获取地区列表失败,请稍后重试'
})
}
}
//
const sortField = ref('')
const sortOrder = ref('')
//
const permanentBean = ref(0)
const freeBean = ref(0)
const totalNum = ref(0)
/*
====================方法=================================
*/
//
const getAdminData = async function () {
try {
const result = await request({
url: '/admin/userinfo',
data: {}
})
adminData.value = result
console.log('请求成功', result)
console.log('用户信息', adminData.value)
} catch (error) {
console.log('请求失败', error)
}
}
const selectLiveBy = async function (val) {
try {
//
if (typeof val === 'number') {
getObj.value.pageNum = val
}
//
//
if (Array.isArray(getTime.value) && getTime.value.length === 2) {
beanConsumeLive.value.startTime = formatTime(getTime.value[0])
beanConsumeLive.value.endTime = formatTime(getTime.value[1])
} else {
beanConsumeLive.value.startTime = ''
beanConsumeLive.value.endTime = ''
}
//
beanConsumeLive.value.sortField = sortField.value
beanConsumeLive.value.sortOrder = sortOrder.value
console.log('搜索参数_时间', beanConsumeLive.value.startTime)
console.log('搜索参数1', getObj.value)
console.log('搜索参数2', beanConsumeLive.value)
// POST
const result = await request({
url: '/beanConsume/selectLiveBy',
data: {
pageNum: getObj.value.pageNum,
pageSize: getObj.value.pageSize,
beanConsumeLive: {
...beanConsumeLive.value,
jwcode: beanConsumeLive.value.jwcode ? String(beanConsumeLive.value.jwcode) : '',
dept: beanConsumeLive.value.dept || '',
type: beanConsumeLive.value.type || '',
gift: beanConsumeLive.value.gift || '',
beanNum: beanConsumeLive.value.beanNum || '',
isBackpack: beanConsumeLive.value.isBackpack || '',
liveChannel: beanConsumeLive.value.liveChannel || '',
liveName: beanConsumeLive.value.liveName || '',
startTime: beanConsumeLive.value.startTime || '',
endTime: beanConsumeLive.value.endTime || '',
sortField: beanConsumeLive.value.sortField || 'consumeTime',
sortOrder: beanConsumeLive.value.sortOrder || 'desc'
}
}
})
console.log('请求成功2', sortField)
console.log('接口响应结果', result); //
if (result.code === 200 && result.data && result.data.list) {
tableData.value = result.data.list;
total.value = result.data.total;
}
// beanConsumeLive.value payType 1
const sumConsumeParams = {
payType: 1, // payType 1
beanConsumeLive: {
...beanConsumeLive.value,
}
};
// POST
const resultTotalGold = await request({
url: '/beanConsume/sumConsumeGold',
data: sumConsumeParams
});
console.log("总计", resultTotalGold);
const data = resultTotalGold.data || resultTotalGold;
console.log('请求成功1', resultTotalGold.data) //undifined
console.log('permanentBean1', data.permanentBean)
// permanentBeanfreeBeantotalNum
permanentBean.value = Number(data.permanentBean) || 0;
freeBean.value = Number(data.freeBean) || 0;
totalNum.value = Number(data.totalNum) || 0;
//
total.value = result.data.total
console.log('total', total.value)
} catch (error) {
console.log('请求失败', error)
}
}
//
const search = function () {
getObj.value.pageNum = 1
if (beanConsumeLive.value.jwcode) {
const numRef = /^\d{1,9}$/;
if (!numRef.test(beanConsumeLive.value.jwcode)) {
ElMessage.error('请检查精网号格式')
return
}
}
selectLiveBy()
}
//
const reset = function () {
console.log('直播的重置')
beanConsumeLive.value.jwcode = null
beanConsumeLive.value.type = ''
beanConsumeLive.value.gift = ''
beanConsumeLive.value.liveChannel = ''
beanConsumeLive.value.liveName = ''
beanConsumeLive.value.dept = ''
beanConsumeLive.value.startTime = ''
beanConsumeLive.value.endTime = ''
sortField.value = ''
sortOrder.value = ''
getTime.value = {}
activeTimeRange.value = '' //
//
selectLiveBy()
console.log(' beanConsumeLive', beanConsumeLive.value)
}
//
const getToday = function () {
const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = 'today' //
selectLiveBy()
}
//
const getYesterday = function () {
const today = dayjs()
const startTime = today.subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = 'yesterday' //
selectLiveBy()
}
// 7
const get7Days = function () {
const today = dayjs()
const startTime = today.subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime]
console.log('getTime', getTime.value)
activeTimeRange.value = '7days' //
selectLiveBy()
}
//
const handleSortChange = (column) => {
console.log('排序字段:', column.prop)
console.log('排序方式:', column.order)
if (column.prop === 'beanNum') {
sortField.value = 'beanNum'
} else if (column.prop === 'consumeTime') {
sortField.value = 'consumeTime'
} else if (column.prop === 'buyBean') {
sortField.value = 'buyBean'
} else if (column.prop === 'freeBean') {
sortField.value = 'freeBean'
}
sortOrder.value = column.order === 'ascending' ? 'DESC' : 'ASC'
selectLiveBy()
}
const handlePageSizeChange = function (val) {
getObj.value.pageSize = val
selectLiveBy()
}
const handleCurrentChange = function (val) {
getObj.value.pageNum = val
selectLiveBy()
}
/*
====================计算属性=================================
*/
//
// const totalBean = computed(() => permanentBean.value + freeBean.value)
/*
====================监听=================================
*/
/*
====================挂载=================================
*/
onMounted(async function () {
await getAdminData()
await selectLiveBy()
await getDept()
await getGift()
await getChannel()
})
const exportExcel = async function () {
console.log('1')
const params = {
...getObj.value,
"beanConsumeLive": {
...beanConsumeLive.value,
jwcode: beanConsumeLive.value.jwcode ? String(beanConsumeLive.value.jwcode) : '',
dept: beanConsumeLive.value.dept || '',
type: beanConsumeLive.value.type || '',
gift: beanConsumeLive.value.gift || '',
liveChannel: beanConsumeLive.value.liveChannel || '',
liveName: beanConsumeLive.value.liveName || '',
startTime: beanConsumeLive.value.startTime || '',
endTime: beanConsumeLive.value.endTime || '',
sortField: sortField.value || 'consumeTime',
sortOrder: sortOrder.value || 'desc'
}
}
// 便
console.log('导出请求参数:', params);
try {
console.log('2')
const res = await API({ url: '/export/exportLive', data: params });
console.log('导出请求响应:', res);
if (res.code === 200) {
ElMessage.success('导出成功');
} else {
ElMessage.error(res.message || '导出失败,请稍后重试');
}
} catch (error) {
console.error('导出请求出错:', error);
ElMessage.error('导出失败,请稍后重试');
}
}
const exportListVisible = ref(false)
//
const openExportList = () => {
getExportList()
exportListVisible.value = true
}
//
const exportList = ref([])
//
const exportListLoading = ref(false)
//
const getExportList = async () => {
exportListLoading.value = true
try {
const result = await API({ url: '/export/export' })
if (result.code === 200) {
const filteredData = result.data.filter(item => {
return item.type === 6; //4 // todo type 6
});
exportList.value = filteredData
} else {
ElMessage.error(result.msg || '获取导出列表失败')
}
} catch (error) {
console.error('获取导出列表出错:', error)
ElMessage.error('获取导出列表失败,请稍后重试')
} finally {
exportListLoading.value = false
}
}
//
const downloadExportFile = (item) => {
if (item.state === 2) {
const link = document.createElement('a')
link.href = item.url
link.download = item.fileName
link.click()
} else {
ElMessage.warning('文件还在导出中,请稍后再试')
}
}
//
const getTagType = (state) => {
switch (state) {
case 0:
return 'info';
case 1:
return 'primary';
case 2:
return 'success';
case 3:
return 'danger';
default:
return 'info';
}
}
//
const getTagText = (state) => {
switch (state) {
case 0:
return '待执行';
case 1:
return '执行中';
case 2:
return '执行完成';
case 3:
return '执行出错';
default:
return '未知状态';
}
}
</script>
<template>
<el-card style="margin-bottom: 20px;margin-top:10px">
<el-col style="margin-bottom: 10px">
<el-text>精网号</el-text>
<el-input v-model="beanConsumeLive.jwcode" placeholder="请输入精网号" style="width: 200px;margin-right: 20px"
clearable />
<el-text>地区</el-text>
<el-select v-model="beanConsumeLive.dept" placeholder="请选择地区" style="width: 200px;margin-right: 20px" clearable>
<el-option v-for="(item, index) in dept" :key="index" :label="item" :value="item" />
</el-select>
<el-text>礼物名称</el-text>
<el-select v-model="beanConsumeLive.gift" placeholder="请选择礼物名称" style="width: 200px;margin-right: 20px" clearable
filterable allow-create default-first-option>
<el-option v-for="(item, index) in gifts" :key="index" :label="item" :value="item" />
</el-select>
<el-text>频道</el-text>
<el-select v-model="beanConsumeLive.liveChannel" placeholder="请选择频道" style="width: 200px;margin-right: 20px"
clearable filterable allow-create default-first-option>
<el-option v-for="(item, index) in channels" :key="index" :label="item" :value="item" />
</el-select>
<el-text>直播间</el-text>
<el-input v-model="beanConsumeLive.liveName" placeholder="请输入直播间" style="width: 200px;margin-right: 20px"
clearable />
</el-col>
<el-col>
<el-text>消费时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 400px;margin-right: 20px;" @change="handleDatePickerChange"
:default-time="defaultTime" />
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''"> </el-button>
<el-button @click="getYesterday()" :type="activeTimeRange === 'yesterday' ? 'primary' : ''"> </el-button>
<el-button @click="get7Days()" :type="activeTimeRange === '7days' ? 'primary' : ''"> 近7天</el-button>
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button>
<el-button type="primary" @click="exportExcel()">导出excel</el-button>
<el-button type="primary" @click="openExportList">查看导出列表</el-button>
</el-col>
</el-card>
<el-card>
<div>
金豆总数{{ format3(Math.abs(permanentBean + freeBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
付费金豆数{{ format3(Math.abs(permanentBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金豆数{{ format3(Math.abs(freeBean)) }}
</div>
<!-- 设置表格容器的高度和滚动样式 -->
<div style="overflow-y: auto">
<el-table :data="tableData" style="width: 100%" height="550px" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<!-- 固定姓名列 -->
<el-table-column prop="name" label="姓名" width="150px" fixed="left" />
<!-- 固定精网号列 -->
<el-table-column prop="jwcode" label="精网号" width="110px" fixed="left" />
<el-table-column prop="dept" label="地区" width="110px" />
<el-table-column prop="gift" label="礼物" width="140px">
</el-table-column>
<el-table-column prop="beanNum" label="金豆数量" sortable="custom" width="120px" />
<el-table-column prop="isBackpack" label="背包礼物" width="120px">
<template #default="scope">
{{ scope.row.isBackpack == 1 ? '是' : '否' }}
</template>
</el-table-column>
<el-table-column prop="buyBean" label="付费金豆数" sortable="custom" width="120px" />
<el-table-column prop="freeBean" label="免费金豆数" sortable="custom" width="120px" />
<el-table-column prop="liveChannel" label="频道" width="120px" />
<el-table-column prop="liveName" label="直播间名称" width="160px" show-overflow-tooltip />
<el-table-column prop="consumeTime" label="消费时间" sortable="custom" width="180px" />
</el-table>
</div>
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]" style="margin-top: 20px;"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</el-card>
<!-- 导出弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" label="文件名" />
<el-table-column prop="state" label="状态">
<template #default="scope">
<el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
{{ getTagText(scope.row.state) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template #default="scope">
{{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" size="small" @click="downloadExportFile(scope.row)"
:disabled="scope.row.state !== 2">
下载
</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button text @click="exportListVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped>
.pagination {
display: flex;
margin-top: 20px;
}
</style>

4
src/views/consume/beanConsume.vue

@ -1,4 +0,0 @@
<template>
</template>
<script>
</script>

101
src/views/consume/coinConsume.vue

@ -1,101 +0,0 @@
<template>
<div>
<!-- 这里放置标签切换的按钮 -->
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
<el-button
:type="activeTab === 'add' ? 'primary' : 'default'"
@click="goToAdd"
>
新增消耗
</el-button>
<el-button
:type="activeTab === 'detail' ? 'primary' : 'default'"
@click="goToDetail"
>
金币消耗明细
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
<router-view></router-view>
</div>
</template>
<script setup>
import {onMounted, ref, watch} from 'vue';
import {useRouter, useRoute} from 'vue-router';
import {storeToRefs} from "pinia";
import {useAdminStore} from "@/store/index.js";
const router = useRouter();//
const route = useRoute();//
// activeTab
const activeTab = ref(route.name === 'coinConsumeDetail' ? 'detail' : 'add');
//coinConsumeDetaildetailadd
//coinConsumeadd
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const goToAdd = () => {
// activeTab add
activeTab.value = 'add';
router.push({name: 'addCoinConsume'});
};
const goToDetail = () => {
// activeTab detail
activeTab.value = 'detail';
router.push({name: 'coinConsumeDetail'});
};
//
const navigateTo = (name) => {
activeTab.value = name;
if (name === 'add') {
router.push({name: 'addCoinConsume'});
} else if (name === 'detail') {
router.push({name: 'coinConsumeDetail'});
}
};
// menuName
const hasMenuPermission = (tree, targetName) => {
for (const node of tree) {
if (node.menuName === targetName) return true;
if (node.children && hasMenuPermission(node.children, targetName)) return true;
}
return false;
};
//
const getDefaultRoute = () => {
if (!menuTree.value) return 'add';
const hasRecharge = hasMenuPermission(menuTree.value, '提交金币消耗');
return hasRecharge ? 'add' : 'detail';
};
//
watch(() => route.name, (newName) => {
if (newName === 'add' || newName === 'detail') {
activeTab.value = newName;
} else if (newName === 'coinConsume') {
const defaultRoute = getDefaultRoute();
navigateTo(defaultRoute);
}
});
//
onMounted(() => {
if (route.name === 'coinConsume') {
const defaultRoute = getDefaultRoute();
navigateTo(defaultRoute);
} else {
//
if (route.name === 'add' || route.name === 'detail') {
activeTab.value = route.name;
}
}
});
</script>

204
src/views/consume/addCoinConsume.vue → src/views/consume/gold/addCoinConsume.vue

@ -2,11 +2,7 @@
import { onMounted, reactive, ref, watch } from "vue"; import { onMounted, reactive, ref, watch } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import moment from "moment"; import moment from "moment";
import request from "@/util/http";
/*
====================工具方法==============================
*/
import request from "@/util/http.js"
// //
const trimJwCode = () => { const trimJwCode = () => {
if (addConsume.value.jwcode) { if (addConsume.value.jwcode) {
@ -351,8 +347,8 @@ const getUser = async function (jwcode) {
} }
// //
if (!/^\d+$/.test(jwcode)) {
ElMessage.warning('精网号必须为数字');
if (!/^\d{1,9}$/.test(jwcode)) {
ElMessage.warning('精网号必须为数字且不超过九位');
resetForm() resetForm()
return; return;
} }
@ -467,135 +463,64 @@ onMounted(async function () {
</script> </script>
<template> <template>
<div> <div>
<!-- 根据activeTab切换显示内容 -->
<!-- 新增消耗的布局---------------------------------------------------------- -->
<!-- <div v-if="activeTab === 'addConsume'"> -->
<!-- <div style="margin-bottom: 20px; font-weight: bolder">新增消费</div> -->
<el-form
:model="addConsume"
ref="Ref"
:rules="rules"
label-width="auto"
style="max-width: 750px;"
class="form-style"
>
<el-form-item prop="jwcode" label="精网号">
<el-input
v-model="addConsume.jwcode"
style="width: 220px"
/>
<el-button
type="primary"
@click="getUser(addConsume.jwcode)"
style="margin-left: 20px"
>查询
</el-button
>
<el-form :model="addConsume" ref="Ref" :rules="rules" style="max-width: 750px;">
<div style="width:25vw">
<el-form-item prop="jwcode" label="精网号" style="margin-top: 50px">
<el-input v-model="addConsume.jwcode" style="width: 10vw;margin-left:45px" />
<el-button type="primary" @click="getUser(addConsume.jwcode)" style="margin-left: 10px">查询
</el-button>
</el-form-item> </el-form-item>
<div style="display: flex; align-items: center; gap: 20px;">
</div>
<div style="width:25vw">
<el-form-item prop="goodsName" label="商品名称" style="flex: 1; margin-right: 0px"> <el-form-item prop="goodsName" label="商品名称" style="flex: 1; margin-right: 0px">
<el-select
v-model="addConsume.goodsName"
placeholder="请选择商品"
style="width: 450px"
>
<el-option
v-for="item in goods"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="addConsume.goodsName" placeholder="请选择商品" style="width: 10vw;margin-left:30px">
<el-option v-for="item in goods" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</div> </div>
<div style="width:25vw">
<el-form-item prop="sumGold" label="消耗金币总数"> <el-form-item prop="sumGold" label="消耗金币总数">
<el-input
v-model="addConsume.sumGold"
style="width: 100px"
@input="validateInput()"
@change="calculateCoins(addConsume.sumGold)"
/>
<el-input v-model="addConsume.sumGold" style="width: 10vw;margin-left:2px" @input="validateInput()"
@change="calculateCoins(addConsume.sumGold)" />
</el-form-item> </el-form-item>
</div>
<!-- 三类金币自动计算禁用状态不可编辑 --> <!-- 三类金币自动计算禁用状态不可编辑 -->
<div style="display: flex; align-items: center">
<el-form-item prop="permanentGold" label="永久金币" style="float: left">
<el-input
v-model="addConsume.permanentGold"
disabled
style="width: 100px; margin-left: -5px"
>
<div style="width:25vw">
<el-form-item prop="permanentGold" label="永久金币">
<el-input v-model="addConsume.permanentGold" disabled style="width: 10vw;margin-left:40px">
<template #default="scope">{{ scope.row.permanentGold }}</template> <template #default="scope">{{ scope.row.permanentGold }}</template>
</el-input> </el-input>
<p style="margin-right: 0px"></p>
<p style="margin-right: 0px">&nbsp;&nbsp;</p>
</el-form-item> </el-form-item>
<el-form-item
prop="freeCoin"
label="免费金币"
style="float: left; margin-left: -20px"
>
<el-input
disabled
v-model="addConsume.freeGold"
style="width: 100px; margin-left: -5px"
/>
<p style="margin-right: 0px"></p>
</div>
<div style="width:25vw">
<el-form-item prop="freeCoin" label="免费金币">
<el-input disabled v-model="addConsume.freeGold" style="width: 10vw;margin-left:40px" />
<p style="margin-right: 0px">&nbsp;&nbsp;</p>
</el-form-item> </el-form-item>
<el-form-item prop="taskGold" label="任务金币" style="margin-left: -20px">
<el-input
disabled
v-model="addConsume.taskGold"
style="width: 100px; margin-left: -5px"
/>
<p style="margin-right: 20px"></p>
</div>
<div style="width:25vw">
<el-form-item prop="taskGold" label="任务金币">
<el-input disabled v-model="addConsume.taskGold" style="width: 10vw;margin-left:40px" />
<p style="margin-right: 20px">&nbsp;&nbsp;</p>
</el-form-item> </el-form-item>
</div> </div>
<div style="width:25vw">
<el-form-item prop="remark" label="备注"> <el-form-item prop="remark" label="备注">
<el-input
v-model="addConsume.remark"
style="width: 300px"
:rows="2"
maxlength="100"
show-word-limit
type="textarea"
/>
</el-form-item>
<el-form-item prop="commitName" label="提交人">
<el-input
style="width: 300px"
:value="adminData.adminName"
disabled
placeholder="提交人姓名"
/>
<el-input v-model="addConsume.remark" style="width: 13.5vw;margin-left:70px" :rows="4" maxlength="100" show-word-limit
type="textarea" />
</el-form-item> </el-form-item>
<el-button type="success" @click="resetForm()" style="margin-left: 280px">重置</el-button>
<el-button type="primary" @click="addBefore"> 提交</el-button>
</div>
<el-button type="success" @click="resetForm()" style="margin-left: 200px;margin-top:10px">重置</el-button>
<el-button type="primary" @click="addBefore" style="margin-top:10px"> 提交</el-button>
</el-form> </el-form>
<!-- 客户信息栏 --> <!-- 客户信息栏 -->
<el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info"> <el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info">
<el-form
:model="user"
label-width="auto"
style="max-width: 1000px"
label-position="left"
>
<el-form :model="user" label-width="auto" style="max-width: 1000px" label-position="left">
<el-text size="large" style="margin-left: 20px">客户信息</el-text> <el-text size="large" style="margin-left: 20px">客户信息</el-text>
<!-- 第一行姓名 + 历史金币 --> <!-- 第一行姓名 + 历史金币 -->
@ -616,10 +541,8 @@ onMounted(async function () {
<p v-else></p> <p v-else></p>
</el-form-item> </el-form-item>
<el-form-item style="margin-top: -23px"> <el-form-item style="margin-top: -23px">
<span
style="display: inline; white-space: nowrap; color: #b1b1b1"
v-if="user.historyPermanentGold !== undefined"
>(永久金币:{{ user.historyPermanentGold }};免费金币:{{
<span style="display: inline; white-space: nowrap; color: #b1b1b1"
v-if="user.historyPermanentGold !== undefined">(永久金币:{{ user.historyPermanentGold }};免费金币:{{
(user.historyFreeGold) (user.historyFreeGold)
}};任务金币:{{ user.historyTaskGold }})</span> }};任务金币:{{ user.historyTaskGold }})</span>
</el-form-item> </el-form-item>
@ -635,17 +558,13 @@ onMounted(async function () {
</el-col> </el-col>
<el-col :span="14"> <el-col :span="14">
<el-form-item label="当前金币总数" style="width: 500px"> <el-form-item label="当前金币总数" style="width: 500px">
<span
style="color: #2fa1ff; margin-right: 5px"
v-if="user.nowSumGold !== undefined"
>{{ user.nowSumGold }}</span>
<span style="color: #2fa1ff; margin-right: 5px" v-if="user.nowSumGold !== undefined">{{ user.nowSumGold
}}</span>
</el-form-item> </el-form-item>
<!-- 金币详情独立显示 --> <!-- 金币详情独立显示 -->
<el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 --> <el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
<span
style="color: #b1b1b1; margin-left: 0px"
v-if="user.nowPermanentGold !== undefined"
>(永久金币:{{ user.nowPermanentGold }};
<span style="color: #b1b1b1; margin-left: 0px" v-if="user.nowPermanentGold !== undefined">(永久金币:{{
user.nowPermanentGold }};
免费金币:{{ user.nowFreeGold }}; 免费金币:{{ user.nowFreeGold }};
任务金币:{{ user.nowTaskGold }})</span> 任务金币:{{ user.nowTaskGold }})</span>
</el-form-item> </el-form-item>
@ -713,39 +632,4 @@ p {
</style> </style>
<style> <style>
.avatar-uploader .el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader .el-upload:hover {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 50px;
height: 50px;
text-align: center;
}
.form-style {
margin-top: 50px;
max-width: 50%;
float: left;
}
.form-style2 {
max-width: 60%;
}
p {
font-size: 13px;
transform: scale(1);
}
</style> </style>

89
src/views/consume/gold/coinConsume.vue

@ -0,0 +1,89 @@
<template>
<div>
<!-- 这里放置标签切换的按钮 -->
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
<el-button
:type="activeTab === 'addCoinConsume' ? 'primary' : 'default'"
@click="navigateTo('addCoinConsume')"
:disabled="!hasAdd"
>
新增消耗
</el-button>
<el-button
:type="activeTab === 'coinConsumeDetail' ? 'primary' : 'default'"
@click="navigateTo('coinConsumeDetail')"
:disabled="!hasDetail"
>
金币消耗明细
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
<router-view></router-view>
</div>
</template>
<script setup>
import {onMounted, ref, watch} from 'vue';
import {useRoute, useRouter} from 'vue-router';
import {storeToRefs} from "pinia";
import {useAdminStore} from "@/store/index.js";
import {hasMenuPermission, permissionMapping} from "@/utils/menuTreePermission.js";
const router = useRouter();
const route = useRoute();
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const activeTab = ref('');
const hasAdd = ref(false);
const hasDetail = ref(false);
//
const navigateTo = (name) => {
activeTab.value = name;
router.push({name});
};
//
const initPermissions = () => {
if (!menuTree.value || !menuTree.value.length) return;
hasAdd.value = hasMenuPermission(menuTree.value, permissionMapping.Submit_Gold_Coin_Consumption);
hasDetail.value = hasMenuPermission(menuTree.value, permissionMapping.View_Gold_Coin_Consumption_Details);
};
//
const getDefaultAuditRoute = () => {
initPermissions();
if (hasAdd.value) return 'addCoinConsume';
if (hasDetail.value) return 'coinConsumeDetail';
return 'addCoinConsume';
};
//
watch(() => route.name, (newName) => {
initPermissions()
if (newName === 'addCoinConsume' || newName === 'coinConsumeDetail') {
activeTab.value = newName;
} else if (newName === 'coinConsume') {
// 访 /coinConsume
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
}
});
//
onMounted(() => {
initPermissions()
if (route.name === 'coinConsume') {
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
} else {
//
if (route.name === 'addCoinConsume' || route.name === 'coinConsumeDetail') {
activeTab.value = route.name;
}
}
});
</script>

209
src/views/consume/coinConsumeDetail.vue → src/views/consume/gold/coinConsumeDetail.vue

@ -1,18 +1,33 @@
<script setup> <script setup>
import {computed, onMounted, ref} from 'vue' import {computed, onMounted, ref} from 'vue'
import {dayjs, ElMessage} from 'element-plus' import {dayjs, ElMessage} from 'element-plus'
import request from '@/util/http'
import API from '@/util/http'
import request from '@/util/http.js'
import API from '@/util/http.js'
import moment from 'moment' import moment from 'moment'
import {reverseMarketMapping} from "@/utils/marketMap.js";
// //
/* /*
====================工具方法============================== ====================工具方法==============================
*/ */
const trimJwCode = () => {
if (consumeUser.value.jwcode) {
consumeUser.value.jwcode = consumeUser.value.jwcode.replace(/\s/g, '');
}
}
const format3 = (num) => {
if (!num) return '0';
const parts = Number(num).toFixed(2).split('.');
//
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return parts.join('.');
}
// //
const formatTime = (val) => val ? dayjs(val).format('YYYY-MM-DD HH:mm:ss') : '' const formatTime = (val) => val ? dayjs(val).format('YYYY-MM-DD HH:mm:ss') : ''
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
/* /*
====================数据================================= ====================数据=================================
*/ */
@ -59,7 +74,7 @@ const getTime = ref({
const activity = ref([]) const activity = ref([])
// //
const market = ref([])
const market = ref("")
// //
const sortField = ref('') const sortField = ref('')
@ -162,9 +177,22 @@ const ConsumeSelectBy = async function (val) {
console.log('搜索参数1', getObj.value) console.log('搜索参数1', getObj.value)
console.log('搜索参数2', consumeUser.value) console.log('搜索参数2', consumeUser.value)
// POST // POST
if (consumeUser.value.market === '9' || consumeUser.value.market === '9999') {
consumeUser.value.market = '';
}
if (consumeUser.value.jwcode) {
//
const numberRegex = /^\d{1,9}$/;
//
if (!numberRegex.test(consumeUser.value.jwcode)) {
ElMessage.error('请检查精网号格式')
return
}
}
const result = await request({ const result = await request({
url: '/consume/selectBy', url: '/consume/selectBy',
data: { data: {
pageNum: getObj.value.pageNum, pageNum: getObj.value.pageNum,
@ -254,6 +282,7 @@ const ConsumeSelectBy = async function (val) {
} }
// //
const search = function () { const search = function () {
trimJwCode()
getObj.value.pageNum = 1 getObj.value.pageNum = 1
ConsumeSelectBy() ConsumeSelectBy()
} }
@ -263,7 +292,7 @@ const reset = function () {
consumeUser.value.goodsName = '' consumeUser.value.goodsName = ''
consumeUser.value.market = ''
consumeUser.value.market = ""
consumeUser.value.payPlatform = '' consumeUser.value.payPlatform = ''
@ -277,6 +306,7 @@ const reset = function () {
sortOrder.value = '' sortOrder.value = ''
getTime.value = {} getTime.value = {}
activeTimeRange.value = '' // activeTimeRange.value = '' //
selectedMarketPath.value = []
// //
@ -285,17 +315,9 @@ const reset = function () {
} }
// //
const getToday = function () { const getToday = function () {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate()
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + 1
)
const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
console.log('getTime', getTime.value) console.log('getTime', getTime.value)
activeTimeRange.value = 'today' // activeTimeRange.value = 'today' //
@ -304,18 +326,9 @@ const getToday = function () {
} }
// //
const getYesterday = function () { const getYesterday = function () {
const yesterday = new Date()
yesterday.setDate(yesterday.getDate() - 1)
const startTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate()
)
const endTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate() + 1
)
const today = dayjs()
const startTime = today.subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
console.log('getTime', getTime.value) console.log('getTime', getTime.value)
activeTimeRange.value = 'yesterday' // activeTimeRange.value = 'yesterday' //
@ -324,17 +337,9 @@ const getYesterday = function () {
} }
// 7 // 7
const get7Days = function () { const get7Days = function () {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() - 6
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + 1
)
const today = dayjs()
const startTime = today.subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
console.log('getTime', getTime.value) console.log('getTime', getTime.value)
activeTimeRange.value = '7days' // activeTimeRange.value = '7days' //
@ -360,29 +365,6 @@ const getGoods = async function () {
} }
} }
//
const getMarket = async function () {
try {
//
const result = await request({
// url: '/general/market',
url: '/general/adminMarkets', // todo
data: {account:adminData.value.account}
})
console.log('请求地区列表成功', result)
//
market.value = result.data
console.log('地区数据', market.value)
} catch (error) {
console.log('请求地区列表失败', error)
ElMessage({
type: 'error',
message: '获取地区列表失败,请稍后重试'
})
}
}
// //
const handleSortChange = (column) => { const handleSortChange = (column) => {
console.log('排序字段:', column.prop) console.log('排序字段:', column.prop)
@ -439,7 +421,7 @@ const exportExcel = async function () {
consumeUser: { consumeUser: {
jwcode: consumeUser.value.jwcode || '', jwcode: consumeUser.value.jwcode || '',
payPlatform: consumeUser.value.payPlatform || '', payPlatform: consumeUser.value.payPlatform || '',
market: consumeUser.value.market || '',
marketss: consumeUser.value.market || "",
startTime: consumeUser.value.startTime || '', startTime: consumeUser.value.startTime || '',
endTime: consumeUser.value.endTime || '', endTime: consumeUser.value.endTime || '',
goodsName: consumeUser.value.goodsName || '', goodsName: consumeUser.value.goodsName || '',
@ -525,6 +507,53 @@ const getTagText = (state) => {
return '未知状态'; return '未知状态';
} }
} }
//
const selectedMarketPath = ref("")
const handleMarketChange = (value) => {
if (value && value.length > 0) {
const lastValue = value[value.length - 1]
consumeUser.value.market = reverseMarketMapping[lastValue]
} else {
consumeUser.value.market = ''
}
}
//
const getMarket = async function () {
try {
// POST
const result = await API({
url: '/market/selectMarket',
});
//
console.log('请求成功', result)
//
const transformTree = (nodes) => {
//
const allChildren = nodes.flatMap(node => node.children || []);
return allChildren.map(child => {
const grandchildren = child.children && child.children.length
? transformTree([child]) //
: null;
return {
value: child.name,
label: child.name,
children: grandchildren
};
});
};
//
market.value = transformTree(result.data)
console.log('转换后的地区树==============', market.value)
} catch (error) {
console.log('请求失败', error)
}
}
</script> </script>
<template> <template>
@ -541,7 +570,7 @@ const getTagText = (state) => {
<el-col :span="6"> <el-col :span="6">
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1" size="large">商品名称</el-text> <el-text class="mx-1" size="large">商品名称</el-text>
<el-select v-model="consumeUser.goodsName" placeholder="请选择商品名称" size="large" style="width: 180px"
<el-select v-model="consumeUser.goodsName" placeholder="请选择商品名称" style="width: 180px"
clearable> clearable>
<!-- 修改 v-for 绑定逻辑 --> <!-- 修改 v-for 绑定逻辑 -->
<el-option v-for="(item, index) in goods" :key="index" :label="item" :value="item"/> <el-option v-for="(item, index) in goods" :key="index" :label="item" :value="item"/>
@ -549,17 +578,21 @@ const getTagText = (state) => {
</div> </div>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<div class="head-card-element">
<el-text class="mx-1" size="large">所属地区</el-text> <el-text class="mx-1" size="large">所属地区</el-text>
<el-select v-model="consumeUser.market" placeholder="请选择所属地区" size="large" style="width: 180px" clearable>
<el-option v-for="(item, index) in market" :key="index" :label="item" :value="item" />
</el-select>
</div>
<el-cascader
v-model="selectedMarketPath"
:options="market"
placeholder="请选择所属地区"
clearable
style="width:180px"
@change="handleMarketChange"
/>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1" size="large">消耗平台</el-text> <el-text class="mx-1" size="large">消耗平台</el-text>
<el-select v-model="consumeUser.payPlatform" placeholder="请选择消耗平台" size="large" style="width: 180px"
<el-select v-model="consumeUser.payPlatform" placeholder="请选择消耗平台"
style="width: 180px"
clearable> clearable>
<el-option v-for="item in consumePlatform" :key="item.value" :label="item.label" :value="item.value"/> <el-option v-for="item in consumePlatform" :key="item.value" :label="item.label" :value="item.value"/>
</el-select> </el-select>
@ -571,13 +604,17 @@ const getTagText = (state) => {
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1" size="large">消耗时间</el-text> <el-text class="mx-1" size="large">消耗时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间" <el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange" />
end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange"
:default-time="defaultTime"/>
<el-button @click="getToday()" style="margin-left: 10px" <el-button @click="getToday()" style="margin-left: 10px"
:type="activeTimeRange === 'today' ? 'primary' : ''"> </el-button>
:type="activeTimeRange === 'today' ? 'primary' : ''">
</el-button>
<el-button @click="getYesterday()" style="margin-left: 10px" <el-button @click="getYesterday()" style="margin-left: 10px"
:type="activeTimeRange === 'yesterday' ? 'primary' : ''"> </el-button>
:type="activeTimeRange === 'yesterday' ? 'primary' : ''">
</el-button>
<el-button @click="get7Days()" style="margin-left: 10px" <el-button @click="get7Days()" style="margin-left: 10px"
:type="activeTimeRange === '7days' ? 'primary' : ''"> 近7天</el-button>
:type="activeTimeRange === '7days' ? 'primary' : ''"> 近7天
</el-button>
<!-- </div> <!-- </div>
</el-col> </el-col>
<el-col :span="3"> <el-col :span="3">
@ -596,9 +633,10 @@ const getTagText = (state) => {
<el-col> <el-col>
<el-card> <el-card>
<div> <div>
消耗总金额{{ Math.abs(permanentGolds) / 100 }}新币永久金币{{ Math.abs(permanentGolds) / 100 }}免费金币{{
Math.abs(freeGolds) / 100
}}任务金币{{ Math.abs(taskGolds) / 100 }}
消耗新币{{ format3(Math.abs(permanentGolds) ) }}新币&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ format3(Math.abs(permanentGolds) ) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ format3(Math.abs(freeGolds) ) }}&nbsp;&nbsp;&nbsp;&nbsp;
任务金币{{ format3(Math.abs(taskGolds) ) }}
</div> </div>
<!-- 设置表格容器的高度和滚动样式 --> <!-- 设置表格容器的高度和滚动样式 -->
<div style="height: 576px; overflow-y: auto"> <div style="height: 576px; overflow-y: auto">
@ -634,24 +672,24 @@ const getTagText = (state) => {
{{ {{
(scope.row.taskGold + (scope.row.taskGold +
scope.row.freeGold + scope.row.freeGold +
scope.row.permanentGold) / 100
scope.row.permanentGold)
}} }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="permanentGold" label="永久金币" sortable="“custom”" width="110px"> <el-table-column prop="permanentGold" label="永久金币" sortable="“custom”" width="110px">
<template #default="scope"> <template #default="scope">
{{ scope.row.permanentGold / 100 }}
{{ scope.row.permanentGold}}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="freeGold" label="免费金币" sortable="“custom”" width="110px"> <el-table-column prop="freeGold" label="免费金币" sortable="“custom”" width="110px">
<template #default="scope"> <template #default="scope">
{{ scope.row.freeGold / 100 }}
{{ scope.row.freeGold }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="taskGold" label="任务金币" sortable="“custom”" width="110px"> <el-table-column prop="taskGold" label="任务金币" sortable="“custom”" width="110px">
<template #default="scope"> <template #default="scope">
{{ scope.row.taskGold / 100 }}
{{ scope.row.taskGold }}
</template> </template>
</el-table-column> </el-table-column>
@ -664,7 +702,8 @@ const getTagText = (state) => {
<!-- 分页 --> <!-- 分页 -->
<div class="pagination"> <div class="pagination">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]" <el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination> @current-change="handleCurrentChange"></el-pagination>
</div> </div>
</el-card> </el-card>
@ -673,7 +712,7 @@ const getTagText = (state) => {
<!-- 导出弹窗 --> <!-- 导出弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%"> <el-dialog v-model="exportListVisible" title="导出列表" width="80%">
<el-table :data="exportList" style="width: 100%" :loading="exportListLoading">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" label="文件名"/> <el-table-column prop="fileName" label="文件名"/>
<el-table-column prop="state" label="状态"> <el-table-column prop="state" label="状态">
<template #default="scope"> <template #default="scope">

34
src/views/home.vue

@ -1,7 +1,7 @@
<script setup> <script setup>
// //
import {ref} from 'vue'
import {useRouter} from 'vue-router'
import {computed, ref} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {ElMessage} from 'element-plus' import {ElMessage} from 'element-plus'
import dmmn from '../assets/link.png' import dmmn from '../assets/link.png'
import ChangePassword from '@/components/changePassword.vue' import ChangePassword from '@/components/changePassword.vue'
@ -21,7 +21,35 @@ const {adminData, menuTree} = storeToRefs(adminStore)
menuList.value = filterMenu(menuTree.value) menuList.value = filterMenu(menuTree.value)
console.log("menuList", menuList.value) console.log("menuList", menuList.value)
//
const route = useRoute()
// index
function findBestMatch(menuList, path) {
let bestMatch = ''
function traverse(menus) {
for (const item of menus) {
const itemPath = getRoutePath(item)
// path
if (path.startsWith(itemPath) && itemPath.length > bestMatch.length) {
bestMatch = itemPath
}
if (item.children && item.children.length > 0) {
traverse(item.children)
}
}
}
traverse(menuList)
return bestMatch || path // fallback
}
//
const activeMenu = computed(() => {
return findBestMatch(menuList.value, route.path)
})
const router = useRouter() const router = useRouter()
const imgrule1 = dmmn const imgrule1 = dmmn
const messageVisible = ref(false) const messageVisible = ref(false)
@ -81,7 +109,7 @@ function logout() {
<el-menu <el-menu
:router="true" :router="true"
class="el-menu-vertical-demo" class="el-menu-vertical-demo"
:default-active="$route.path"
:default-active="activeMenu"
> >
<!-- 递归渲染菜单层级 --> <!-- 递归渲染菜单层级 -->
<template v-for="menu in menuList" :key="menu.id"> <template v-for="menu in menuList" :key="menu.id">

51
src/views/login.vue

@ -6,7 +6,8 @@ import {useRouter} from 'vue-router'
import API from "@/util/http.js"; import API from "@/util/http.js";
import {useAdminStore} from '@/store' import {useAdminStore} from '@/store'
// //
import {filterMenu, findFirstAccessibleMenu, getRoutePath} from "../utils/menuUtils.js"
import {filterFirstMenu, findFirstThirdLevelMenu, getRoutePath,} from "../utils/menuUtils.js"
import {storeToRefs} from "pinia";
const router = useRouter() // const router = useRouter() //
// //
@ -62,15 +63,22 @@ const login = async function () {
adminStore.setMenuTree(menuTree) adminStore.setMenuTree(menuTree)
// //
const filteredMenu = filterMenu(adminStore.menuTree)
const filteredMenu = filterFirstMenu(adminStore.menuTree)
console.log('过滤后的菜单树', filteredMenu)
// 访 // 访
const firstMenu = findFirstAccessibleMenu(filteredMenu)
const firstMenu = findFirstThirdLevelMenu(filteredMenu)
console.log('获取到的第一个可访问的菜单', firstMenu)
// 访 path // 访 path
const redirectPath = firstMenu ? getRoutePath(firstMenu) : '/noPermission' const redirectPath = firstMenu ? getRoutePath(firstMenu) : '/noPermission'
// //
router.push(redirectPath) router.push(redirectPath)
ElMessage.success('登录成功') ElMessage.success('登录成功')
//
await selectMarket()
console.log('请求成功', result) console.log('请求成功', result)
} else { } else {
form.value.password = '' form.value.password = ''
@ -97,6 +105,43 @@ const getMenuTree = async function () {
} }
} }
//
const selectMarket = async function () {
try {
const selectMarketResult = await API({ url: '/market/selectMarket' });
const marketList = {};
//
const traverseTree = (nodes) => {
nodes.forEach(node => {
// id name
marketList[node.id] = node.name;
//
if (node.children && node.children.length > 0) {
traverseTree(node.children);
}
});
};
//
// selectMarketResult.data
selectMarketResult.data.forEach(rootNode => {
//
if (rootNode.children && rootNode.children.length > 0) {
traverseTree(rootNode.children);
}
});
console.log('排除第一级后的地区列表:', marketList);
adminStore.setMarketList(marketList);
// return marketList;
} catch (error) {
console.error('获取地区树失败:', error);
return {};
}
};
onMounted(() => { onMounted(() => {
getMachineId() getMachineId()

224
src/views/managerecharge/rate.vue

@ -2,40 +2,21 @@
import { onMounted, reactive, ref } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import request from '@/util/http' import request from '@/util/http'
import { useAdminStore } from "@/store/index.js";
import { storeToRefs } from "pinia";
import { findMenuById, permissionMapping } from "@/utils/menuTreePermission.js"
const adminStore = useAdminStore()
const { adminData, menuTree } = storeToRefs(adminStore)
/*
====================工具方法=========================
*/
/*
====================数据===================================
*/
//
const adminData = ref({
id: '',
adminName: ''
})
//
const regeEdit = ref(false) const regeEdit = ref(false)
// (id )
const editFormRef = ref(null) const editFormRef = ref(null)
//
const tableData = ref([]) const tableData = ref([])
// //
const getObj = ref({ const getObj = ref({
pageNum: 1, pageNum: 1,
pageSize: 10 pageSize: 10
}) })
const total = ref(0) const total = ref(0)
//
const rateEdit = ref({ const rateEdit = ref({
id: null, id: null,
rateName: '', rateName: '',
@ -94,41 +75,12 @@ const checkFreeGoldRadio = function (rule, value, callback) {
} }
// //
const rules = reactive({ const rules = reactive({
// rateName: [{required: true, message: '', trigger: 'blur'}],
num: [{ validator: checkFreeGoldRadio, trigger: 'blur' }], num: [{ validator: checkFreeGoldRadio, trigger: 'blur' }],
}) })
// //
const formSize = ref('default') const formSize = ref('default')
/*
====================方法=========================
*/
//
const getAdminData = async function () {
try {
const result = await request({
url: '/admin/userinfo',
data: {}
})
adminData.value = result
rateEdit.value.adminId = adminData.value.id
console.log('请求成功', result)
} catch (error) {
console.log('请求失败', error)
}
}
//
const getAllRate = async function (val) { const getAllRate = async function (val) {
try { try {
//
if (typeof val === 'number') {
getObj.value.pageNum = val;
}
// POST
const result = await request({ const result = await request({
url: '/rate/selectAll', url: '/rate/selectAll',
method: 'POST', method: 'POST',
@ -136,79 +88,60 @@ const getAllRate = async function (val) {
pageNum: getObj.value.pageNum, pageNum: getObj.value.pageNum,
pageSize: getObj.value.pageSize, pageSize: getObj.value.pageSize,
} }
});
//
console.log('这是汇率列表 请求成功', result);
//
tableData.value = result.data.list;
//
total.value = result.data.total;
})
console.log('这是汇率列表 请求成功', result)
tableData.value = result.data.list
total.value = result.data.total
} catch (error) { } catch (error) {
console.log('请求失败', error); console.log('请求失败', error);
ElMessage.error('请求失败'); ElMessage.error('请求失败');
} }
} }
//
const handlePageSizeChange = function (val) { const handlePageSizeChange = function (val) {
getObj.value.pageSize = val getObj.value.pageSize = val
getAllRate() getAllRate()
} }
//
const handleCurrentChange = function (val) { const handleCurrentChange = function (val) {
getObj.value.pageNum = val getObj.value.pageNum = val
getAllRate() getAllRate()
} }
//id
const getEditData = async function (row) { const getEditData = async function (row) {
try { try {
console.log('搜索参数', getObj.value) console.log('搜索参数', getObj.value)
// POST
const result = await request({ const result = await request({
url: '/rate/selectById', url: '/rate/selectById',
data: { id: row.id } data: { id: row.id }
}) })
//
console.log('根据id查 请求成功', result) console.log('根据id查 请求成功', result)
//
// rateEdit.value = result.data
//
rateEdit.value.id = row.id rateEdit.value.id = row.id
rateEdit.value.rateName = row.rateName rateEdit.value.rateName = row.rateName
rateEdit.value.num = row.num rateEdit.value.num = row.num
console.log('根据id获取的数据', rateEdit.value) console.log('根据id获取的数据', rateEdit.value)
rateEdit.value.adminId = adminData.value.id rateEdit.value.adminId = adminData.value.id
} catch (error) { } catch (error) {
console.log('请求失败', error) console.log('请求失败', error)
} }
} }
// //
const editRate = async function () { const editRate = async function () {
if (findMenuById(menuTree.value, permissionMapping.Exchange_Rate_Modification)) {
// //
rateEdit.value.num = parseFloat(rateEdit.value.num); rateEdit.value.num = parseFloat(rateEdit.value.num);
try { try {
console.log('搜索参数', rateEdit.value) console.log('搜索参数', rateEdit.value)
// POST
const result = await request({ const result = await request({
url: '/rate/update', url: '/rate/update',
data: rateEdit.value data: rateEdit.value
}) })
//
console.log('请求成功', result) console.log('请求成功', result)
await getAllRate() await getAllRate()
} catch (error) { } catch (error) {
console.log('请求失败', error) console.log('请求失败', error)
} }
} else {
ElMessage.error('没有权限')
}
} }
// //
const edit = () => { const edit = () => {
editFormRef.value.validate(async (valid) => { editFormRef.value.validate(async (valid) => {
@ -226,24 +159,18 @@ const edit = () => {
ElMessage({ ElMessage({
type: "error", type: "error",
message: "请检查输入内容", message: "请检查输入内容",
});
})
}
})
} }
});
};
//
const cancelEdit = () => { const cancelEdit = () => {
regeEdit.value = false regeEdit.value = false
} }
//
const handleEditDialogClose = () => { const handleEditDialogClose = () => {
if (editFormRef.value) { if (editFormRef.value) {
getAllRate() getAllRate()
} }
} }
// //
function formatDate(value) { function formatDate(value) {
if (!value) return '' if (!value) return ''
@ -256,8 +183,6 @@ function formatDate(value) {
const seconds = date.getSeconds().toString().padStart(2, '0') const seconds = date.getSeconds().toString().padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
} }
// //
function handleInput(value) { function handleInput(value) {
// 使 // 使
@ -265,21 +190,17 @@ function handleInput(value) {
ElMessage.warning('请输入正确的符号'); ElMessage.warning('请输入正确的符号');
// value = value.replace('', '.'); // value = value.replace('', '.');
} }
// //
const parts = value.split('.'); const parts = value.split('.');
if (parts.length > 2) { if (parts.length > 2) {
value = parts[0] + '.' + parts.slice(1).join(''); value = parts[0] + '.' + parts.slice(1).join('');
ElMessage.warning('只能包含一个小数点'); ElMessage.warning('只能包含一个小数点');
} }
// //
if (value.startsWith('-')) { if (value.startsWith('-')) {
ElMessage.warning('不允许输入负数'); ElMessage.warning('不允许输入负数');
value = value.substring(1); value = value.substring(1);
} }
// //
if (value.includes('.')) { if (value.includes('.')) {
const parts = value.split('.') const parts = value.split('.')
@ -303,7 +224,6 @@ function handleInput(value) {
ElMessage.info('整数部分最多允许六位') ElMessage.info('整数部分最多允许六位')
} }
} }
// 0 // 0
if (value.startsWith('.')) { if (value.startsWith('.')) {
value = '0' + value; value = '0' + value;
@ -311,46 +231,22 @@ function handleInput(value) {
// ElMessage.info('0'); // ElMessage.info('0');
} }
// //
rateEdit.value.num = value;
rateEdit.value.num = value
return value;
return value
} }
/*
====================监听=========================
*/
/*
====================挂载=========================
*/
//
onMounted(async function () { onMounted(async function () {
await getAllRate() await getAllRate()
await getAdminData()
}) })
</script> </script>
<template> <template>
<!-- 这是主页面 -->
<el-row> <el-row>
<el-col> <el-col>
<el-card class="box-card" style="max-width: 100%"> <el-card class="box-card" style="max-width: 100%">
<!-- 表格 -->
<div> <div>
<el-table
:data="tableData"
v-if="(tableData.flag = 1)"
>
<el-table-column
type="index"
label="序号"
width="100px"
fixed="left"
>
<el-table :data="tableData" v-if="(tableData.flag = 1)">
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope"> <template #default="scope">
<span>{{ <span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
@ -373,17 +269,12 @@ onMounted(async function () {
</el-table-column> </el-table-column>
<el-table-column label="操作" :span="3"> <el-table-column label="操作" :span="3">
<template #default="scope"> <template #default="scope">
<el-button
type="text"
@click="
() => {
<el-button type="text" @click=" () => {
regeEdit = true regeEdit = true
getEditData(scope.row) getEditData(scope.row)
} }
"
>编辑
</el-button
>
">编辑
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -391,54 +282,25 @@ onMounted(async function () {
<!-- 分页 --> <!-- 分页 -->
<div class="pagination"> <div class="pagination">
<el-pagination
background
:page-size="getObj.pageSize"
:page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"
></el-pagination>
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
<!-- 这是编辑弹窗 --> <!-- 这是编辑弹窗 -->
<el-dialog
v-model="regeEdit"
title="修改汇率"
width="500"
:close-on-click-modal="false"
@close="handleEditDialogClose"
>
<el-dialog v-model="regeEdit" title="修改汇率" width="500" :close-on-click-modal="false" @close="handleEditDialogClose">
<template #footer> <template #footer>
<el-form
ref="editFormRef"
style="max-width: 600px"
:model="rateEdit"
:rules="rules"
label-width="auto"
class="demo-ruleForm"
:size="formSize"
status-icon
>
<el-form ref="editFormRef" style="max-width: 600px" :model="rateEdit" :rules="rules" label-width="auto"
class="demo-ruleForm" :size="formSize" status-icon>
<el-form-item prop="rateName" label="货币名称:"> <el-form-item prop="rateName" label="货币名称:">
<el-input
v-model="rateEdit.rateName"
disabled
style="width: 240px"
/>
<el-input v-model="rateEdit.rateName" disabled style="width: 240px" />
</el-form-item> </el-form-item>
<el-form-item prop="num" label="汇率:"> <el-form-item prop="num" label="汇率:">
<el-input
v-model="rateEdit.num"
@update:modelValue="handleInput"
style="width: 120px"
/>
<el-input v-model="rateEdit.num" @update:modelValue="handleInput" style="width: 120px" />
<span class="unit">:1</span> <span class="unit">:1</span>
<span class="rate-tip"> <span class="rate-tip">
(提示当前规则每 (提示当前规则每
@ -446,9 +308,7 @@ onMounted(async function () {
<span>{{ rateEdit.rateName }}</span>可兑换 1 新币) <span>{{ rateEdit.rateName }}</span>可兑换 1 新币)
</span> </span>
</el-form-item> </el-form-item>
<el-form-item label="提交人:">
<el-input disabled :value="adminData.adminName" style="width: 240px"/>
</el-form-item>
<el-form-item> <el-form-item>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button type="primary" @click="edit">修改</el-button> <el-button type="primary" @click="edit">修改</el-button>
@ -471,30 +331,10 @@ onMounted(async function () {
margin-top: 20px; margin-top: 20px;
} }
.button-item {
margin-left: 10px;
}
.add-item {
margin-bottom: 10px;
}
.unit { .unit {
margin-left: 10px; margin-left: 10px;
} }
.el-card {
padding: 0px;
}
.pagination {
display: flex;
}
.status {
display: flex;
}
.rate-tip { .rate-tip {
hyphens: auto; hyphens: auto;
} }

111
src/views/noPermissionPage.vue

@ -1,24 +1,105 @@
<script setup> <script setup>
import { ref, onMounted, reactive, computed, nextTick } from "vue";
const image = "../src/assets/hqz大拇指.png";
import image_403 from "@/assets/403.png"
</script> </script>
<template> <template>
<el-row>
<el-col>
<div>
<p style="font-size: 50px; font-weight: bold; text-align: center;">
暂无权限
</p>
<p style="font-size: 60px; font-weight: bold; text-align: center;">
请联系管理员添加权限
</p>
<div class="error-page">
<!-- 背景装饰元素 -->
<div class="bg-decoration"></div>
<el-row class="error-container">
<el-col :span="24" class="error-content">
<!-- 错误图标/图片区域 -->
<div class="error-img">
<img :src="image_403" alt="无权限访问" class="error-image">
</div>
<!-- 文本信息区域 -->
<div class="error-message">
<h2>暂无权限</h2>
<p>您当前的账号没有访问该页面的权限请联系管理员添加权限</p>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
</div>
</template> </template>
<style scoped></style>
<style scoped>
.error-page {
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
}
/* 背景装饰 */
.bg-decoration {
position: absolute;
width: 100%;
z-index: 0;
}
.error-container {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 2rem;
position: relative;
z-index: 1;
}
.error-content {
max-width: 800px;
width: 100%;
text-align: center;
animation: fadeIn 0.8s ease-out;
}
/* 错误图片区域 */
.error-img {
position: relative;
margin-bottom: 2.5rem;
}
.error-image {
max-width: 280px;
width: 100%;
height: auto;
transition: transform 0.3s ease;
}
.error-image:hover {
transform: scale(1.03);
}
/* 文本信息区域 */
.error-message {
margin-bottom: 2.5rem;
}
.error-message h2 {
font-size: 2.2rem;
margin-bottom: 1rem;
font-weight: 600;
}
.error-message p {
font-size: 1.1rem;
max-width: 600px;
margin: 0 auto;
line-height: 1.6;
}
/* 操作按钮区域 */
.error-actions {
display: flex;
justify-content: center;
gap: 1rem;
margin-bottom: 2rem;
}
</style>

1641
src/views/permissions/permission.vue
File diff suppressed because it is too large
View File

89
src/views/permissions/permissions.vue

@ -0,0 +1,89 @@
<template>
<div>
<!-- 这里放置标签切换的按钮 -->
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
<el-button
:type="activeTab === 'userPermission' ? 'primary' : 'default'"
@click="navigateTo('userPermission')"
:disabled="!hasDetail"
>
用户管理
</el-button>
<el-button
:type="activeTab === 'rolePermission' ? 'primary' : 'default'"
@click="navigateTo('rolePermission')"
:disabled="!hasBalance"
>
角色管理
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
<router-view></router-view>
</div>
</template>
<script setup>
import {onMounted, ref, watch} from 'vue';
import {useRoute, useRouter} from 'vue-router';
import {storeToRefs} from "pinia";
import {useAdminStore} from "@/store/index.js";
import {hasMenuPermission, permissionMapping} from "@/utils/menuTreePermission.js";
const router = useRouter();
const route = useRoute();
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const activeTab = ref('');
const hasDetail = ref(false);
const hasBalance = ref(false);
//
const navigateTo = (name) => {
activeTab.value = name;
router.push({name});
};
//
const initPermissions = () => {
if (!menuTree.value || !menuTree.value.length) return;
hasDetail.value = hasMenuPermission(menuTree.value, permissionMapping.View_Permission);
hasBalance.value = hasMenuPermission(menuTree.value, permissionMapping.View_Role);
};
//
const getDefaultAuditRoute = () => {
initPermissions();
if (hasDetail.value) return 'userPermission';
if (hasBalance.value) return 'rolePermission';
return 'userPermission';
};
//
watch(() => route.name, (newName) => {
initPermissions()
if (newName === 'userPermission' || newName === 'rolePermission') {
activeTab.value = newName;
} else if (newName === 'permissions') {
// 访 /permissions
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
}
});
//
onMounted(() => {
initPermissions()
if (route.name === 'permissions') {
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
} else {
//
if (route.name === 'userPermission' || route.name === 'rolePermission') {
activeTab.value = route.name;
}
}
});
</script>

868
src/views/permissions/rolePermission.vue

@ -0,0 +1,868 @@
<script setup>
import {nextTick, onMounted, reactive, ref} from 'vue'
import {ElMessage} from 'element-plus'
import _ from 'lodash'
import request from '@/util/http'
import API from '@/util/http'
//
const tableData = ref([])
const roleData = ref([])
const total = ref(100)
const roleTotal = ref(100)//
// admin
const admin = ref({
account: '',
market: '',
postiton: ''
})
//
const role = ref({
name: ''
})
//
const getObj = ref({
pageNum: 1,
pageSize: 10
})
//
const getRoleObj = ref({
pageNum: 1,
pageSize: 10
})
//
const market = ref([])
//
const postiton = ref([])
//
const permissionAddVisible = ref(false)
//
const addRole = ref({
roleName: '',
parentId: null,
checkedKeys: [],
grade: '',
market: ''
})
const addRoleMarket = ref([])
//
const adminData = ref({})
const getAdminData = async function () {
try {
const result = await API({url: '/admin/userinfo', data: {}})
adminData.value = result
console.log('管理员用户信息', adminData.value)
} catch (error) {
console.log('请求失败', error)
}
}
const viewRole = ref([])
const getRolePermission = async function () {
const result = await request({
url: '/menu/tree',
data: {
"id": adminData.value.roleId
}
})
viewRole.value = collectIds(result.data)
console.log('result111', viewRole.value);
}
const get = async function (val) {
try {
if (typeof val === 'number') {
getObj.value.pageNum = val
}
console.log('搜索参数', getObj.value, admin.value)
const result = await request({
url: '/permission/getPermission',
data: {
...getObj.value,
permission: {
...admin.value
}
}
})
tableData.value = result.data.list
console.log('tableData', tableData.value)
total.value = result.data.total
} catch (error) {
console.log('请求失败', error)
}
}
const getRoleList = async function (val) {
try {
if (typeof val === 'number') {
getObj.value.pageNum = val
}
console.log('搜索参数', getObj.value, role.value)
const result = await request({
url: '/role/selectBy',
data: {
...getRoleObj.value,
roleVo: {
roleName: role.value.name
}
}
})
roleData.value = result.data.list
console.log('roleData', roleData.value)
roleTotal.value = result.data.total
} catch (error) {
console.log('请求失败', error)
}
}
// D
const formatPermissions = (tree) => {
if (!tree || tree.length === 0) return '';
return tree.map(menu => {
const mainMenu = menu.menuName;
const subMenus = menu.children?.map(child => child.menuName) || [];
// 2
if (subMenus.length > 0) {
const maxSub = Math.min(2, subMenus.length);
const subText = subMenus.slice(0, maxSub).join('、');
const moreText = subMenus.length > maxSub ? '...' : '';
return `${mainMenu}+${subText}${moreText}`;
}
//
return mainMenu;
}).join('+');
};
const trimJwCode = () => {
if (admin.value.account) {
admin.value.account = admin.value.account.replace(/\s/g, '');
}
}
const searchRole = function () {
trimJwCode();
getObj.value.pageNum = 1
getRoleList()
}
//
const reset = function () {
admin.value = {}
role.value.name = ''
get()
getRoleList()
}
const RoleArea = ref([])
const getRoleArea = async function () {
try {
const result = await request({
url: '/general/allRoleMarket',
data: {}
})
RoleArea.value = result.data
} catch (error) {
console.log('请求失败', error)
}
}
const getArea = async function () {
try {
const result = await request({
url: '/general/adminMarkets',
data: {account: adminData.value.account}
})
market.value = result.data
} catch (error) {
console.log('请求失败', error)
}
}
//
const getStore = async function () {
try {
const result = await request({
url: '/permission/getposition',
data: {}
})
postiton.value = result.data
} catch (error) {
console.log('请求失败', error)
}
}
//
const openPermissionAddVisible = function () {
permissionAddVisible.value = true
getRoles()
getLists()
}
const closePermissionAddVisible = function () {
permissionAddVisible.value = false
Ref.value.resetFields();
getRoleList()
}
//
const permissionAddInit = function () {
openPermissionAddVisible()
}
const handleDialogClose = function(){
closePermissionAddVisible()
console.log('hhh');
}
// ref
const Ref = ref(null)
//
const permissionList = ref([])
const getRoles = async function () {
try {
const res = await API({url: '/role/selectAll'})
permissionList.value = res.data.map(item => ({
label: item.roleName,
value: item.id
}))
console.log('权限列表:', permissionList.value)
} catch (error) {
console.error('获取权限列表失败:', error)
}
}
const collectIds = (tree) => {
let ids = [];
tree.forEach((node) => {
ids.push(node.id);
if (node.children && node.children.length > 0) {
ids = ids.concat(collectIds(node.children));
}
});
return ids;
};
//datadisabled
function processTreeData(data) {
return data.map(item => ({
...item,
disabled: item.id != null || item.menuName.includes("敏感权限"), //
children: item.children ? processTreeData(item.children) : []
}));
}
const handleAddRole = async function () {
try {
await new Promise((resolve, reject) => {
Ref.value.validate((valid) => {
if (valid) {
resolve(); //
} else {
reject(new Error('请检查并完善表单信息')); //
}
});
});
addRole.value.roleName = addRole.value.roleName.replace(/\s+/g, '');
console.log('去除角色名空格:', addRole.value.roleName);
// ID
const finalCheckedKeys = addRole.value.checkedKeys || [];
const res = await API({
url: '/role/add',
data: {
"roleName": addRole.value.roleName,
"menuIds": finalCheckedKeys,
"priority": addRole.value.grade,
"fatherId": addRole.value.parentId,
"market": addRole.value.market
}
})
if (res.code === 200) {
ElMessage.success('角色' + addRole.value.roleName + '添加成功')
console.log('成功了,看看addRole', addRole.value)
console.log('提交的权限ID列表:', finalCheckedKeys);
closePermissionAddVisible()
} else {
ElMessage.error(res.msg)
}
} catch (error) {
console.log('请求失败', error)
console.log('失败,看看addRole', addRole.value);
}
}
const handleRolePageSizeChange = (val) => {
getRoleObj.value.pageSize = val
getRoleList() //
}
// -
const handleRoleCurrentChange = (val) => {
getRoleObj.value.pageNum = val
getRoleList() //
}
const data = ref([])
const getLists = async function () {
try {
console.log('addRole.value.roleId', addRole.value.roleId);
let roleId = addRole.value.parentId
if (addRole.value.parentId === null || addRole.value.parentId === undefined) {
roleId = 2
}
const res = await API({
url: '/menu/tree',
data: {id: roleId}
})
data.value = res.data
let originalData = res.data.filter(item => item.id !== 9); //
//
data.value = filterGoldenBeanMenus(originalData);
// data.value = data.value.filter(item => item.id !== 9);
console.log('看看data', data.value)
console.log('parentID:', addRole.value.parentId, 'roleId:', roleId)
/* // 根据地区过滤金豆菜单
if (addRole.value.market !== '总部') {
//
originalData = filterGoldenBeanMenus(originalData);
}
data.value = originalData; // */
if (addRole.value.parentId && addRole.value.parentId !== 2) {
const result = await API({
url: '/general/roleMarket',
data: {id: addRole.value.parentId}
})
if (result.code === 200) {
if (typeof result.data === 'string' && result.data) {
addRoleMarket.value = result.data.split(',');
addRole.value.market = ''
} else if (Array.isArray(result.data)) {
addRoleMarket.value = result.data
addRole.value.market = ''
} else {
addRoleMarket.value = [];
addRole.value.market = ''
}
} else {
ElMessage.error('该上级角色无所属地区')
console.log('该上级角色无所属地区');
}
console.log('addRoleMarket.value', addRoleMarket.value);
} else {
addRoleMarket.value = RoleArea.value
console.log('elseRoleArea', RoleArea);
}
} catch (error) {
console.log('请求失败', error)
}
}
//
const goldenBeanMenuIds = new Set([
43, 55, 54, // ->
41, 47, 46, 48, // ->
42, 50, 49, 52, 51, // ->
45, 53 // ->
]);
const filterGoldenBeanMenus = (tree) => {
return tree
.filter(item => {
//
if (goldenBeanMenuIds.has(item.id)) {
return false;
}
//
if (item.children && item.children.length > 0) {
item.children = filterGoldenBeanMenus(item.children);
}
return true;
});
};
const treeRef = ref(null)
//
const handleEditRolePermissionCheck = (checkedNodes, checkedInfo) => {
const {checkedKeys, checkedNodes: allCheckedNodes} = checkedInfo;
//
if (allCheckedNodes.length === 0) {
permissionEditRoleObj.value.checkedKeys = [];
return;
}
// check-strictly="false"Element Plus
// 使 checkedKeysID
permissionEditRoleObj.value.checkedKeys = checkedKeys;
console.log('编辑角色选中的权限ID:', checkedKeys);
console.log('选中的节点数量:', allCheckedNodes.length);
};
const handleCheckChange = async (checkedNodes, checkedInfo) => {
const {checkedKeys, checkedNodes: allCheckedNodes} = checkedInfo;
//
if (allCheckedNodes.length === 0) {
addRole.value.checkedKeys = [];
return;
}
// SetID
const allKeys = new Set(checkedKeys);
//
allCheckedNodes.forEach(node => {
//
selectParentNodes(data.value, node.id, allKeys);
});
// Set
addRole.value.checkedKeys = Array.from(allKeys);
console.log('新增角色包含所有父级的选中项:', addRole.value.checkedKeys);
};
const selectParentNodes = (treeData, nodeId, checkedKeys) => {
if (!Array.isArray(treeData)) return false;
for (const item of treeData) {
//
if (item.children && item.children.length > 0) {
const foundInChildren = selectParentNodes(item.children, nodeId, checkedKeys);
if (foundInChildren) {
//
checkedKeys.add(item.id);
return true;
}
}
//
if (item.id === nodeId) {
return true;
}
}
return false;
};
//
const menuTreeVisible = ref(false);
const currentRoleMenuTree = ref([]);
const currentRoleName = ref('');
const Rolecheckedkeys = ref([])
const showMenuTree = (treeData, roleName) => {
currentRoleMenuTree.value = processTreeData(treeData) || [];
console.log('currentRoleMenuTree.value', currentRoleMenuTree.value);
Rolecheckedkeys.value = collectIds(treeData)
console.log('Rolecheckedkeys', Rolecheckedkeys.value);
currentRoleName.value = roleName || '权限详情';
menuTreeVisible.value = true;
};
//
const permissionEditRoleObj = ref({
id: null,
roleName: '',
market: '',
parentId: null,
parentName: '',
checkedKeys: [],
grade: '',
});
//
const permissionEditRoleVisible = ref(false);
const collectIds2 = (tree) => {
let ids = [];
tree.forEach((node) => {
// children children
if (!node.children || node.children.length === 0) {
ids.push(node.id);
} else {
// children
ids = ids.concat(collectIds2(node.children));
}
});
return ids;
};
//
const permissionEditRoleInit = async function (row) {
console.log('row', row);
console.log('row.tree', row.tree);
permissionEditRoleObj.value = {};
permissionEditRoleObj.value.id = row.id;
permissionEditRoleObj.value.roleName = row.roleName;
permissionEditRoleObj.value.market = row.market;
permissionEditRoleObj.value.parentId = row.fatherId;
permissionEditRoleObj.value.parentName = row.fatherName;
permissionEditRoleObj.value.grade = row.priority;
try {
let roleId = permissionEditRoleObj.value.parentId;
// id
if (permissionEditRoleObj.value.parentId === null || permissionEditRoleObj.value.parentId === undefined) {
roleId = 2;
}
// /tree 使 ID
const res = await API({
url: '/menu/tree',
data: {id: roleId}
});
data.value = res.data;
data.value = data.value.filter(item => item.id !== 9);
data.value = filterGoldenBeanMenus(data.value);
// id
if (row.tree && row.tree.length > 0) {
const leafIds = collectIds2(row.tree);
permissionEditRoleObj.value.checkedKeys = leafIds;
console.log('编辑角色初始化时的权限列表', permissionEditRoleObj.value.checkedKeys);
} else {
permissionEditRoleObj.value.checkedKeys = [];
}
} catch (error) {
console.log('根据上级角色获取权限列表失败', error);
data.value = [];
permissionEditRoleObj.value.checkedKeys = [];
}
console.log('编辑角色', permissionEditRoleObj.value);
permissionEditRoleVisible.value = true;
// DOM
await nextTick();
if (treeRef.value && permissionEditRoleObj.value.checkedKeys.length > 0) {
treeRef.value.setCheckedKeys(permissionEditRoleObj.value.checkedKeys);
console.log('手动设置树的选中状态:', permissionEditRoleObj.value.checkedKeys);
}
};
//
const permissionEditRole = async function () {
try {
await new Promise((resolve, reject) => {
Ref.value.validate((valid) => {
if (valid) {
resolve();
} else {
reject(new Error('请检查并完善表单信息'));
}
});
});
// ID
let finalCheckedKeys = permissionEditRoleObj.value.checkedKeys || [];
// ID
const allKeys = new Set(finalCheckedKeys);
finalCheckedKeys.forEach(nodeId => {
selectParentNodesForSubmit(data.value, nodeId, allKeys);
});
finalCheckedKeys = Array.from(allKeys);
const res = await API({
url: '/menu/update',
data: {
"id": permissionEditRoleObj.value.id,
"roleName": permissionEditRoleObj.value.roleName,
"menuIds": finalCheckedKeys,
"priority": permissionEditRoleObj.value.grade,
"fatherId": permissionEditRoleObj.value.parentId,
"market": permissionEditRoleObj.value.market
}
});
if (res.code === 200) {
console.log('编辑角色成功', permissionEditRoleObj.value);
console.log('提交的权限ID列表:', finalCheckedKeys);
permissionEditRoleVisible.value = false;
getRoleList();
ElMessage.success('编辑角色成功');
} else if (res.code === 0) {
console.log('角色名重复', permissionEditRoleObj.value);
ElMessage.error('角色名重复');
} else {
console.log('编辑角色失败', res);
ElMessage.error('编辑角色失败');
}
} catch (error) {
console.log('编辑角色失败', error);
console.log('失败,看看permissionEditRoleObj', permissionEditRoleObj.value);
}
};
//
const selectParentNodesForSubmit = (treeData, nodeId, checkedKeys) => {
if (!Array.isArray(treeData)) return false;
for (const item of treeData) {
//
if (item.children && item.children.length > 0) {
const foundInChildren = selectParentNodesForSubmit(item.children, nodeId, checkedKeys);
if (foundInChildren) {
//
checkedKeys.add(item.id);
return true;
}
}
//
if (item.id === nodeId) {
return true;
}
}
return false;
};
const Rolerules = reactive({
roleName: [
{required: true, message: '请输入角色名称', trigger: 'blur'},
{min: 2, max: 20, message: '角色名称长度应在2-20个字符之间', trigger: 'blur'}
],
market: [
{required: true, message: '请选择所属地区', trigger: 'change'}
],
grade: [
{required: true, message: '请输入优先级', trigger: 'blur'},
{pattern: /^[1-9]\d{0,2}$/, message: '优先级应为1-999的数字', trigger: 'blur'}
],
checkedKeys: [
{
required: true,
message: '请选择权限列表',
trigger: 'change', //
validator: (rule, value, callback) => {
if (value && value.length > 0) {
callback(); //
} else {
callback(new Error('请选择权限列表')); //
}
}
}
]
});
const throttledHandleAddRole = _.throttle(handleAddRole, 5000, {
trailing: false
})
//
onMounted(async function () {
await getAdminData()
await get()
await getArea()
await getStore()
await getRoleList()
await getRolePermission()
await getRoleArea()
})
</script>
<template>
<div>
<!-- 角色搜索 -->
<el-card style="margin-bottom: 20px;margin-top:10px">
<div class="head-card">
<el-text class="mx-1" size="large">角色名称</el-text>
<el-input v-model="role.name" style="width: 240px" placeholder="请输入角色名称" clearable/>
<div class="head-card-btn">
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="searchRole()">查询</el-button>
</div>
</div>
</el-card>
<!-- 展示表单 -->
<el-card>
<div class="add-item">
<el-button style="color: #048efb; border: 1px solid #048efb" @click="permissionAddInit()">新增角色</el-button>
</div>
<div>
<el-table :data="roleData" style="width: 100%" show-overflow-tooltip>
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getRoleObj.pageNum - 1) * getRoleObj.pageSize
}}</span>
</template>
</el-table-column>
<el-table-column prop="roleName" label="角色名称"/>
<el-table-column prop="fatherName" label="上级角色"/>
<el-table-column prop="priority" label="优先级"/>
<el-table-column label="权限范围" show-overflow-tooltip>
<template #default="scope">
<div class="permission-cell" @click="showMenuTree(scope.row.tree, scope.row.roleName)">
{{ formatPermissions(scope.row.tree) }}
</div>
</template>
</el-table-column>
<!-- <el-table-column prop="operation" label="操作" width="200px">
<template #default="scope">
<el-popconfirm title="确定将此角色删除吗?" @confirm="delRoleConfirm">
<template #reference>
<el-button type="danger" text @click="delRole(scope.row)"
:disabled="scope.row.id === 1 || scope.row.id === 2">
删除
</el-button>
</template>
<template #actions="{ confirm, cancel }">
<el-button size="small" @click="cancel">取消</el-button>
<el-button type="primary" size="small" @click="confirm">
确定
</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column> -->
<el-table-column prop="operation" label="操作" width="200px">
<template #default="scope">
<el-button type="warning" text @click="permissionEditRoleInit(scope.row)" :disabled="scope.row.id === 2">
编辑
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination" style="margin-top: 20px">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="roleTotal"
@size-change="handleRolePageSizeChange"
@current-change="handleRoleCurrentChange"></el-pagination>
</div>
</el-card>
</div>
<!-- 角色菜单树展示 -->
<el-dialog v-model="menuTreeVisible" :title='`权限详情:${currentRoleName}`' width="600px">
<el-tree :data="currentRoleMenuTree" node-key="id" :props="{ label: 'menuName', children: 'children' }"
show-checkbox check-strictly :expand-on-click-node="false"
:default-expanded-keys="currentRoleMenuTree.map(item => item.id)" :default-checked-keys="Rolecheckedkeys"/>
<template #footer>
<el-button @click="menuTreeVisible = false" type="primary">关闭</el-button>
</template>
</el-dialog>
<!-- 新增角色 -->
<el-dialog v-model="permissionAddVisible" title="新增角色" width="800px" :close-on-click-modal="false" @close="handleDialogClose ">
<template #footer>
<el-form ref="Ref" :rules="Rolerules" :model="addRole" label-width="auto"
style="max-width: 600px; align-items: center">
<el-form-item prop="roleName" label="角色名称:" required>
<el-input v-model="addRole.roleName" placeholder="请输入角色名称" style="width: 220px"/>
</el-form-item>
<el-form-item prop="parentName" label="上级角色:">
<el-select v-model="addRole.parentId" placeholder="请选择上级角色" style="width: 220px" @change="getLists"
clearable>
<el-option v-for="item in permissionList" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item prop="market" label="所属地区:" required>
<el-select v-model="addRole.market" placeholder="请选择所属地区" style="width: 220px" clearable >
<el-option v-for="item in addRoleMarket" :key="item" :label="item" :value="item"/>
</el-select>
</el-form-item>
<el-form-item prop="checkedKeys" label="权限列表:" required>
<el-tree v-if="data.length > 0" :data="data" show-checkbox node-key="id"
:props="{ label: 'menuName', children: 'children' }" :checked-keys="addRole.checkedKeys"
:check-strictly="false"
@check="handleCheckChange">
<template #default="{ node, data }">
<span>{{ node.label }}</span>
</template>
</el-tree>
<div v-else style="display: flex; align-items: center; gap: 8px;">
<span style="color: #999;">暂无数据</span>
</div>
</el-form-item>
<el-form-item prop="grade" label="优先级:" required>
<el-input v-model="addRole.grade" placeholder="数字1~999" style="width: 220px"/>
</el-form-item>
</el-form>
<div>
<el-button @click="closePermissionAddVisible()">取消</el-button>
<el-button type="primary" @click="throttledHandleAddRole">
提交
</el-button>
</div>
</template>
</el-dialog>
<!-- 編輯角色彈窗 -->
<el-dialog v-model="permissionEditRoleVisible" title="编辑角色" width="800px" :close-on-click-modal="false">
<template #footer>
<el-form ref="Ref" :rules="Rolerules" :model="permissionEditRoleObj" label-width="auto"
style="max-width: 600px; align-items: center">
<el-form-item prop="roleName" label="角色名称:" required>
<el-input v-model="permissionEditRoleObj.roleName" placeholder="请输入角色名称" style="width: 220px"/>
</el-form-item>
<el-form-item prop="parentName" label="上级角色:">
<el-input v-model="permissionEditRoleObj.parentName" placeholder="无上级角色" disabled style="width: 220px">
<el-option v-for="item in permissionList" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-input>
</el-form-item>
<el-form-item prop="market" label="地区" required>
<el-input v-model="permissionEditRoleObj.market" placeholder="请输入所属地区" style="width: 220px" disabled/>
</el-form-item>
<el-form-item prop="checkedKeys" label="权限列表:" required>
<el-tree v-if="data.length > 0" :data="data" show-checkbox node-key="id" ref="treeRef"
:props="{ label: 'menuName', children: 'children' }"
:default-checked-keys="permissionEditRoleObj.checkedKeys"
:check-strictly="false"
@check="handleEditRolePermissionCheck">
<template #default="{ node, data }">
<span>{{ node.label }}</span>
</template>
</el-tree>
<div v-else style="display: flex; align-items: center; gap: 8px;">
<span style="color: #999;">暂无数据</span>
</div>
</el-form-item>
<!-- <el-form-item prop="grade" label="优先级:" required>
<el-input v-model="permissionEditRoleObj.grade" placeholder="数字1~999" style="width: 220px" />
</el-form-item> -->
</el-form>
<div>
<el-button @click="permissionEditRoleVisible = false">取消</el-button>
<el-button type="primary" @click="permissionEditRole">
提交
</el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped>
.pagination {
display: flex;
}
.head-card {
display: flex;
}
.head-card-btn {
margin-left: auto;
}
.permission-cell {
cursor: pointer;
color: #409eff;
/* 蓝色文字,提示可点击 */
}
</style>

1176
src/views/permissions/userPermission.vue
File diff suppressed because it is too large
View File

297
src/views/recharge/addBeanRecharge.vue

@ -0,0 +1,297 @@
<script setup>
import { ref, onMounted, reactive, computed, watch, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import axios from 'axios'
import { ElMessageBox } from 'element-plus'
import API from '@/util/http'
import { uploadFile } from '@/util/request';
import request from '@/util/http'
import moment from 'moment'
import { e, range, re } from 'mathjs'
import { utils, read } from 'xlsx'
import throttle from 'lodash/throttle'
import { useAdminStore } from "@/store/index.js";
import { storeToRefs } from "pinia";
import _ from 'lodash'
const user = ref({})
const getUser = async function (jwcode) {
if (addForm.value.jwcode) {
addForm.value.jwcode = addForm.value.jwcode.replace(/\s/g, '');
} else {
ElMessage.error('请先输入精网号')
return false
}
try {
const result = await API({
url: '/beanUser/userCard',
data: {
jwcode: addForm.value.jwcode
}
})
if (result.code === 0) {
ElMessage.error(result.msg);
} else if (result.data === null) {
ElMessage.error("用户不存在");
} else {
user.value = result.data;
console.log("用户信息", user.value);
ElMessage.success("查询成功");
}
} catch (error) {
console.log("请求失败", error);
ElMessage.error("精网号错误");
}
}
const addForm = ref({
jwcode: '',
permanentBean: '',
freeBean: '',
remark: '',
adminName: ''
})
const formRef = ref(null)
const adminStore = useAdminStore()
const { adminData } = storeToRefs(adminStore)
const rules = reactive({
jwcode: [
{ required: true, message: '请输入精网号', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (!value) {
callback(new Error('精网号不能为空'));
return;
}
if (/[^0-9]/.test(value)) {
callback(new Error('精网号只能包含数字'));
return;
}
callback();
}, trigger: 'blur'
}],
permanentBean: [
{ required: true, message: '请输入付费金豆数', trigger: 'change' },
{
validator: (rule, value, callback) => {
if(!value){
value = 0
}
//
if (!/^\d+$/.test(value)) {
callback(new Error('请输入非负整数'));
return;
}
//
if (value.length > 6) {
callback(new Error('整数位数不能超过6位'));
return;
}
callback();
},
trigger: 'blur'
}
],
freeBean: [
{ required: true, message: '请输入免费金豆数', trigger: 'change' },
{
validator: (rule, value, callback) => {
if(!value){
value = 0
}
//
if (!/^\d+$/.test(value)) {
callback(new Error('请输入非负整数'));
return;
}
//
if (value.length > 6) {
callback(new Error('整数位数不能超过6位'));
return;
}
callback();
},
trigger: 'blur'
}
],
remark: [
{ required: true, message: '请输入备注', trigger: 'blur' }
]
});
//
const deleteAddForm = function () {
addForm.value = {
jwcode: '',
permanentBean: '',
freeBean: '',
remark: '',
adminName: ''
}
}
const handleAddForm = async () => {
try {
if(!addForm.value.permanentBean ){
addForm.value.permanentBean = 0
}
if(!addForm.value.freeBean ){
addForm.value.freeBean = 0
}
await new Promise((resolve, reject) => {
formRef.value.validate((valid) => {
if (valid) {
if (Number(addForm.value.permanentBean) === 0 && Number(addForm.value.freeBean) === 0) {
reject(new Error('付费金豆和免费金豆不能同时为0'));
}
resolve(); //
} else {
reject(new Error('请检查并完善表单信息')); //
}
});
});
console.log('adminData', adminData.value);
await ElMessageBox.confirm(
'确认充值吗?',
'提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: "primary",
lockScroll: false,
}
)
const result = await request({
url: '/beanRecharge/add',
data: {
jwcode: addForm.value.jwcode,
permanentBean: addForm.value.permanentBean,
freeBean: addForm.value.freeBean,
remark: addForm.value.remark,
adminName: adminData.value.adminName
}
})
if (result.code == 200) {
ElMessage.success('新增成功')
addForm.value = {
jwcode: '',
permanentBean: '',
freeBean: '',
remark: '',
adminName: ''
}
}else{
ElMessage.error(result.msg)
}
} catch (error) {
console.log('金豆新增充值失败');
ElMessage.error(error.message || '操作取消');
}
}
const throttledHandleAddFormt = _.throttle(handleAddForm, 5000, {
trailing: false
})
</script>
<template>
<div>
<el-form :model="addForm" :rules="rules" ref="formRef" label-width="auto" style="max-width: 600px" class="add-form">
<el-form-item prop="jwcode" label="精网号" label-position="left">
<el-input v-model="addForm.jwcode" style="width: 220px" />
<el-button type="primary" @click="getUser(addForm.jwcode)" style="margin-left: 20px">查询</el-button>
</el-form-item>
<el-form-item prop="permanentBean" label="付费金豆" label-position="left">
<el-input v-model="addForm.permanentBean" placeholder="0" style="width: 100px" />
</el-form-item>
<el-form-item prop="freeBean" label="免费金豆" label-position="left">
<el-input v-model="addForm.freeBean" placeholder="0" style="width: 100px" />
</el-form-item>
<el-form-item prop="remark" label="备注" label-position="left">
<el-input v-model="addForm.remark" style="width: 300px" :rows="5" maxlength="100" show-word-limit
type="textarea" />
</el-form-item>
<!-- <el-form-item prop="adminName" label="提交人">
<el-input style="width: 300px" :value="adminData.adminName" disabled placeholder="提交人姓名" />
</el-form-item> -->
<el-button @click="deleteAddForm" style="margin-left: 280px" type="success">重置</el-button>
<el-button type="primary" @click="throttledHandleAddFormt"> 提交 </el-button>
</el-form>
<!-- 客户信息栏 -->
<el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info">
<el-form :model="user" label-width="auto" style="max-width: 1000px" label-position="left">
<el-text size="large" style="margin-left: 20px">客户信息</el-text>
<!-- 第一行姓名 + 当前付费金豆 -->
<el-row style="margin-top: 20px">
<el-col :span="9">
<el-form-item label="姓名:">
<p style="color: #2fa1ff; margin-right: 5px">{{ user.name }}</p>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="当前付费金豆:">
<p style="color: #2fa1ff; margin-right: 5px" v-if="!isNaN(Number(user.permanentBean))">
{{ Number(user.permanentBean) }}
</p>
<!-- 如果不是有效的数字显示默认值 -->
<p v-else></p>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行精网号 + 免费金豆 -->
<el-row>
<el-col :span="9">
<el-form-item label="精网号:">
<p style="color: #2fa1ff; margin-right: 5px">{{ user.jwcode }}</p>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="当前免费金豆:">
<span style="color: #2fa1ff; margin-right: 5px" v-if="user.freeBean !== undefined">{{ user.freeBean }}
</span>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行消费次数 + 所属门店 -->
<el-row>
<el-col :span="9">
<el-form-item label="所属门店:">
<p style="color: #2fa1ff">{{ user.market }}</p>
</el-form-item>
</el-col>
<el-col :span="9">
<el-form-item label="消耗金豆总数:">
<p style="color: #2fa1ff; margin-right: 5px" v-if="user.consumeSum != null">{{ user.consumeSum }}</p>
<p style="color: #2fa1ff; margin-right: 5px" v-else>{{ 0 }}</p>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</template>
<style scoped>
.add-form {
margin-top: 50px;
max-width: 50%;
float: left;
}
.customer-info {
max-width: 60%;
}
p {
margin: 0px;
}
.el-form-item {
margin-left: 50px;
}
</style>

489
src/views/recharge/beanOnlineRecharge.vue

@ -0,0 +1,489 @@
<script setup>
import { ref, onMounted, reactive, computed } from 'vue'
import ElementPlus from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { AiFillRead } from 'vue-icons-plus/ai'
import axios from 'axios'
import moment from 'moment'
import API from '@/util/http'
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
const selectData = ref({
jwcode: '',
market: '',
startTime: '',
endTime: '',
})
const permanentBeans = ref(0)
const beanNum = ref(0)
const money = ref(0)
//
const getTotalBeans = async () => {
try {
const result = await API({
url: '/beanRecharge/statsOnlineBean',
data: {
...selectData.value,
}
})
if (result.code == 200) {
permanentBeans.value = result.data.permanentBean
beanNum.value = result.data.beanNum
money.value = result.data.money
console.log('金豆总数获取成功');
}
console.log('获取金豆总数失败:', result.msg);
} catch (error) {
console.log('获取金豆总数出错');
}
}
//
const formatTime = (val) => val ? moment(val).format('YYYY-MM-DD HH:mm:ss') : ''
const tableData = ref([])
//
const market = ref()
const getTime = ref([])
//
const activeTimeRange = ref('')
const handleDatePickerChange = () => {
activeTimeRange.value = ''
console.log('当前选中时间范围', getTime.value);
}
//
const getArea = async () => {
const result = await API({
url: '/beanRecharge/onlineMarket',
data: {}
})
if (result.code == 200) {
market.value = result.data
console.log('线上充值地区获取成功', market.value)
} else {
ElMessage.error('线上充值地区获取失败')
}
}
//
const getToday = () => {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate(),
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate(),
23, 59, 59
)
getTime.value = [startTime, endTime]
activeTimeRange.value = 'today'
search();
}
const getYesterday = () => {
const yesterday = new Date()
yesterday.setDate(yesterday.getDate() - 1)
const startTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate()
)
const endTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate(),
23, 59, 59
)
getTime.value = [startTime, endTime]
activeTimeRange.value = 'yesterday'
search();
}
const get7Days = function () {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() - 6
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate(),
23, 59, 59
)
getTime.value = [startTime, endTime]
activeTimeRange.value = '7days'
search();
}
const reset = () => {
selectData.value = {
jwcode: '',
market: '',
startTime: '',
endTime: '',
}
getTime.value = []
activeTimeRange.value = ''
search()
}
//
const search = () => {
getObj.value.pageNum = 1
//
getObj.value.pageNum = 1
if (selectData.value.jwcode) {
const numRef = /^\d{1,9}$/;
if (!numRef.test(selectData.value.jwcode)) {
ElMessage.error('请检查精网号格式')
return
}
}
get()
getTotalBeans()
}
//
const get = async () => {
try {
if (getTime.value != null) {
selectData.value.startTime = formatTime(getTime.value[0])
selectData.value.endTime = formatTime(getTime.value[1])
} else {
selectData.value.startTime = ''
selectData.value.endTime = ''
}
const data = {
...getObj.value,
beanOnlineRechargeInfo: {
...selectData.value,
sortField: sortField.value,
sortOrder: sortOrder.value,
},
}
console.log('请求参数:', data);
const result = await API({
url: '/beanRecharge/selectByOnline',
data: data
})
if (result.code == 200) {
tableData.value = result.data.list
total.value = result.data.total
} else {
ElMessage.error(result.message)
}
} catch (error) {
console.log('搜索失败', error);
}
}
//
const platform = [
{
value: 1,
label: 'PC'
},
{
value: 2,
label: '手机'
}
]
//
//
const sortField = ref('')
const sortOrder = ref('')
const handleSortChange = (column) => {
console.log('排序字段:', column.prop)
console.log('排序方式:', column.order)
// 使
const allowedFields = ['money', 'num'];
if (allowedFields.includes(column.prop)) {
sortField.value = column.prop;
}
sortOrder.value = column.order === 'ascending' ? 'ASC' : 'DESC';
console.log('传递给后端的排序字段:', sortField.value)
console.log('传递给后端的排序方式:', sortOrder.value)
get();
}
//===================================================
//
const total = ref(0)
const getObj = ref({
pageNum: 1,
pageSize: 50
})
const handlePageSizeChange = (value) => {
getObj.value.pageSize = value
get()
}
const handleCurrentChange = (value) => {
getObj.value.pageNum = value
get()
}
//=============================================
const exportExcel = async () => {
const params = {
...getObj.value,
beanOnlineRechargeInfo: {
...selectData.value,
sortField: sortField.value,
sortOrder: sortOrder.value,
},
}
try {
const res = await API({ url: '/export/exportol', data: params })
if (res.code === 200) {
ElMessage.success('导出成功')
} else {
ElMessage.error(res.message || '导出失败,请稍后重试')
}
} catch (error) {
console.log('请求失败', error)
ElMessage.error('导出失败,请稍后重试')
}
}
const exportListVisible = ref(false)
//
const openExportList = () => {
getExportList()
exportListVisible.value = true
}
//
const exportList = ref([])
//
const exportListLoading = ref(false)
//
const getExportList = async () => {
exportListLoading.value = true
try {
const result = await API({ url: '/export/export' })
if (result.code === 200) {
const filteredData = result.data.filter(item => {
return item.type === 10;
});
exportList.value = filteredData
} else {
ElMessage.error(result.msg || '获取导出列表失败')
}
} catch (error) {
console.error('获取导出列表出错:', error)
ElMessage.error('获取导出列表失败,请稍后重试')
} finally {
exportListLoading.value = false
}
}
//
const downloadExportFile = (item) => {
if (item.state === 2) {
const link = document.createElement('a')
link.href = item.url
link.download = item.fileName
link.click()
} else {
ElMessage.warning('文件还在导出中,请稍后再试')
}
}
//
const getTagType = (state) => {
switch (state) {
case 0:
return 'info';
case 1:
return 'primary';
case 2:
return 'success';
case 3:
return 'danger';
default:
return 'info';
}
}
//
const getTagText = (state) => {
switch (state) {
case 0:
return '待执行';
case 1:
return '执行中';
case 2:
return '执行完成';
case 3:
return '执行出错';
default:
return '未知状态';
}
}
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
onMounted(async function () {
await get()
await getArea()
await getTotalBeans()
})
</script>
<template>
<el-row>
<el-col>
<el-card style="margin-bottom: 20px;margin-top: 10px">
<el-row style="margin-bottom: 10px">
<el-col :span="5">
<div class="head-card-element">
<el-text class="mx-1" size="large">精网号</el-text>
<el-input v-model="selectData.jwcode" placeholder="请输入精网号" style="width: 150px" clearable />
</div>
</el-col>
<el-col :span="6">
<div class="head-card-element">
<el-text class="mx-1" size="large">所属地区</el-text>
<el-select v-model="selectData.market" placeholder="请选择所属地区" clearable style="width:150px">
<el-option v-for="item in market" :key="item" :label="item" :value="item" />
</el-select>
</div>
</el-col>
<el-col :span="5">
<div class="head-card-element">
<el-text class="mx-1" size="large">订单号</el-text>
<el-input v-model="selectData.orderNo" placeholder="请输入订单号" style="width: 150px"
clearable />
</div>
</el-col>
<el-col :span="6">
<div class="head-card-element">
<el-text class="mx-1" size="large">充值平台</el-text>
<el-select v-model="selectData.platform" placeholder="请选择充值平台" clearable
style="width:150px">
<el-option v-for="item in platform" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<div class="head-card-element">
<el-text class="mx-1" size="large">充值时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator=""
start-placeholder="起始时间" end-placeholder="结束时间" style="width: 350px"
@change="handleDatePickerChange" :default-time="defaultTime" />
<el-button @click="getToday()" style="margin-left: 10px"
:type="activeTimeRange === 'today' ? 'primary' : ''">
</el-button>
<el-button @click="getYesterday()" style="margin-left: 10px"
:type="activeTimeRange === 'yesterday' ? 'primary' : ''">
</el-button>
<el-button @click="get7Days()" style="margin-left: 10px"
:type="activeTimeRange === '7days' ? 'primary' : ''"> 近7天
</el-button>
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button>
<el-button type="primary" @click="exportExcel()">导出Excel</el-button>
<el-button type="primary" @click="openExportList">查看导出列表</el-button>
</div>
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
<el-row>
<el-col>
<el-card>
<div class="bean-info">
充值金豆数{{ format3(beanNum) }}, 合计新币数{{ format3(money) }}
</div>
<!-- 设置表格容器的高度和滚动样式 -->
<div style="height: 520px; overflow-y: auto;margin-top: 10px;">
<el-table :data="tableData" style="width: 100%" height="520px" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<el-table-column fixed="left" prop="name" label="姓名" min-width="120" />
<el-table-column fixed="left" prop="jwcode" label="精网号" min-width="110px" />
<el-table-column prop="market" label="所属地区" min-width="100px" />
<el-table-column prop="orderNo" header-align="center" align="center" label="订单号"
min-width="210px" />
<el-table-column prop="num" label="数量" sortable="custom" min-width="110px" />
<el-table-column prop="money" label="金额" sortable="custom" min-width="150px"
show-overflow-tooltip />
<el-table-column prop="platform" label="充值平台" min-width="150px" show-overflow-tooltip>
<template #default=scope>
<span v-if="scope.row.platform == 1">PC</span>
<span v-else-if="scope.row.platform == 2">手机</span>
<span v-else>其他</span>
</template>
</el-table-column>
<el-table-column prop="rechargeTime" label="充值时间" min-width="200px">
<template #default="scope">
{{ moment(scope.row.rechargeTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination" style="margin-top: 20px">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="handlePageSizeChange" @current-change="handleCurrentChange"></el-pagination>
</div>
</el-card>
</el-col>
</el-row>
<!-- 导出弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%" class="custom-height-dialog">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" label="文件名" />
<el-table-column prop="state" label="状态">
<template #default="scope">
<el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
{{ getTagText(scope.row.state) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template #default="scope">
{{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" size="small" @click="downloadExportFile(scope.row)"
:disabled="scope.row.state !== 2">
下载
</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button text @click="exportListVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped></style>

97
src/views/recharge/beanRecharge.vue

@ -1,4 +1,99 @@
<template> <template>
<div>
<el-button-group>
<el-button
:type="activeTab === 'addBeanRecharge' ? 'primary' : 'default'"
@click="navigateTo('addBeanRecharge')"
:disabled="!hasAdd"
style="width: 120px;"
>
新增充值
</el-button>
<el-button
:type="activeTab === 'beanSystemRecharge' ? 'primary' : 'default'"
@click="navigateTo('beanSystemRecharge')"
:disabled="!hasSystem"
style="width: 120px;"
>
系统充值
</el-button>
<el-button
:type="activeTab === 'beanOnlineRecharge' ? 'primary' : 'default'"
@click="navigateTo('beanOnlineRecharge')"
:disabled="!hasOnline"
style="width: 120px;"
>
线上充值
</el-button>
</el-button-group>
<router-view></router-view>
</div>
</template> </template>
<script>
<script setup>
import {ref, watch, onMounted} from 'vue';
import {useRouter, useRoute} from 'vue-router';
import {storeToRefs} from 'pinia';
import {useAdminStore} from '@/store/index.js';
import {hasMenuPermission, permissionMapping} from "@/utils/menuTreePermission.js";
const router = useRouter();
const route = useRoute();
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const activeTab = ref('');
const hasAdd = ref(false);
const hasSystem = ref(false);
const hasOnline = ref(false);
//
const navigateTo = (name) => {
activeTab.value = name;
router.push({name});
};
//
const initPermissions = () => {
if (!menuTree.value || !menuTree.value.length) return;
hasAdd.value = hasMenuPermission(menuTree.value, permissionMapping.Submit_Golden_Bean_Recharge);
hasSystem.value = hasMenuPermission(menuTree.value, permissionMapping.View_Golden_Bean_System_Recharge_Details);
hasOnline.value = hasMenuPermission(menuTree.value, permissionMapping.View_Golden_Bean_Online_Recharge_Details);
};
//
const getDefaultAuditRoute = () => {
initPermissions();
if (hasAdd.value) return 'addBeanRecharge';
if (hasSystem.value) return 'beanSystemRecharge';
if (hasOnline.value) return 'beanOnlineRecharge';
return 'addBeanRecharge';
};
//
watch(() => route.name, (newName) => {
initPermissions()
if (newName=== 'addBeanRecharge' || newName === 'beanSystemRecharge' || newName === 'beanOnlineRecharge') {
activeTab.value = newName;
} else if (newName === 'beanRecharge') {
// 访 /beanConsume
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
}
});
//
onMounted(() => {
initPermissions()
if (route.name === 'beanRecharge') {
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
} else {
//
if (route.name=== 'addBeanRecharge' || route.name === 'beanSystemRecharge' || route.name === 'beanOnlineRecharge') {
activeTab.value = route.name;
}
}
});
</script> </script>

452
src/views/recharge/beanSystemRecharge.vue

@ -0,0 +1,452 @@
<script setup>
import { ref, onMounted, reactive, computed } from 'vue'
import ElementPlus from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { AiFillRead } from 'vue-icons-plus/ai'
import axios from 'axios'
import moment from 'moment'
import API from '@/util/http'
const selectData = ref({
jwcode: '',
market: '',
startTime: '',
endTime: '',
})
const permanentBeans = ref(0)
const freeBean = ref(0)
const beanNum = ref(0)
const money = ref(0)
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
//
const formatTime = (val) => val ? moment(val).format('YYYY-MM-DD HH:mm:ss') : ''
const tableData = ref([])
//
const market = ref([])
const getTime = ref([])
//
const activeTimeRange = ref('')
const handleDatePickerChange = () => {
activeTimeRange.value = ''
}
//
const getTotalBeans = async () => {
try {
const result = await API({
url: '/beanRecharge/statsSystemBean',
data: {
...selectData.value,
}
})
if (result.code == 200) {
permanentBeans.value = result.data.permanentBean
freeBean.value = result.data.freeBean
beanNum.value = result.data.beanNum
money.value = result.data.money
console.log('金豆总数获取成功');
}
console.log('获取金豆总数失败:', result.msg);
} catch (error) {
console.log('获取金豆总数出错');
}
}
//
const getArea = async () => {
const result = await API({
url: '/beanRecharge/systemMarket',
data: {}
})
if (result.code == 200) {
market.value = result.data
console.log('系统充值地区获取成功', market.value)
} else {
ElMessage.error('系统充值地区获取失败')
}
}
//
const getToday = () => {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate(),
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate(),
23, 59, 59
)
getTime.value = [startTime, endTime]
activeTimeRange.value = 'today'
search()
}
const getYesterday = () => {
const yesterday = new Date()
yesterday.setDate(yesterday.getDate() - 1)
const startTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate()
)
const endTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate(),
23, 59, 59
)
getTime.value = [startTime, endTime]
activeTimeRange.value = 'yesterday'
search()
}
const get7Days = function () {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() - 6
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate(),
23, 59, 59
)
getTime.value = [startTime, endTime]
activeTimeRange.value = '7days'
search()
}
const reset = () => {
selectData.value = {
jwcode: '',
market: '',
startTime: '',
endTime: '',
}
getTime.value = []
activeTimeRange.value = ''
search()
}
//
const search = () => {
//
getObj.value.pageNum = 1
if (selectData.value.jwcode) {
const numRef = /^\d{1,9}$/;
if (!numRef.test(selectData.value.jwcode)) {
ElMessage.error('请检查精网号格式')
return
}
}
get()
getTotalBeans()
}
//
const get = async () => {
try {
if (getTime.value != null) {
selectData.value.startTime = formatTime(getTime.value[0])
selectData.value.endTime = formatTime(getTime.value[1])
} else {
selectData.value.startTime = ''
selectData.value.endTime = ''
}
const data = {
...getObj.value,
"beanSystemRechargeInfo": {
...selectData.value,
sortField: sortField.value,
sortOrder: sortOrder.value,
},
}
console.log('请求参数:', data);
const result = await API({
url: '/beanRecharge/selectBySystem',
data: data
})
if (result.code == 200) {
tableData.value = result.data.list
total.value = result.data.total
} else {
ElMessage.error(result.message)
}
} catch (error) {
console.log('搜索失败', error);
}
}
//
//
const sortField = ref('')
const sortOrder = ref('')
const handleSortChange = (column) => {
console.log('排序字段:', column.prop)
console.log('排序方式:', column.order)
// 使
const allowedFields = ['money', 'freeBean', 'rechargeTime', 'createTime', 'permanentBean'];
if (allowedFields.includes(column.prop)) {
sortField.value = column.prop;
}
sortOrder.value = column.order === 'ascending' ? 'ASC' : 'DESC';
console.log('传递给后端的排序字段:', sortField.value)
console.log('传递给后端的排序方式:', sortOrder.value)
get();
}
//===================================================
//
const total = ref(0)
const getObj = ref({
pageNum: 1,
pageSize: 50
})
const handlePageSizeChange = (value) => {
getObj.value.pageSize = value
get()
}
const handleCurrentChange = (value) => {
getObj.value.pageNum = value
get()
}
//=================================================
const exportExcel = async () => {
const params = {
...getObj.value,
"beanSystemRechargeInfo": {
...selectData.value,
sortField: sortField.value,
sortOrder: sortOrder.value,
},
}
try {
const res = await API({ url: '/export/exportBean', data: params })
console.log('系统充值导出的参数为:', params);
if (res.code === 200) {
ElMessage.success('导出成功')
} else {
ElMessage.error(res.message || '导出失败,请稍后重试')
}
} catch (error) {
console.log('请求失败', error)
ElMessage.error('导出失败,请稍后重试')
}
}
const exportListVisible = ref(false)
//
const openExportList = () => {
getExportList()
exportListVisible.value = true
}
//
const exportList = ref([])
//
const exportListLoading = ref(false)
//
const getExportList = async () => {
exportListLoading.value = true
try {
const result = await API({ url: '/export/export' })
if (result.code === 200) {
const filteredData = result.data.filter(item => {
return item.type === 9;
});
exportList.value = filteredData
} else {
ElMessage.error(result.msg || '获取导出列表失败')
}
} catch (error) {
console.error('获取导出列表出错:', error)
ElMessage.error('获取导出列表失败,请稍后重试')
} finally {
exportListLoading.value = false
}
}
//
const downloadExportFile = (item) => {
if (item.state === 2) {
const link = document.createElement('a')
link.href = item.url
link.download = item.fileName
link.click()
} else {
ElMessage.warning('文件还在导出中,请稍后再试')
}
}
//
const getTagType = (state) => {
switch (state) {
case 0:
return 'info';
case 1:
return 'primary';
case 2:
return 'success';
case 3:
return 'danger';
default:
return 'info';
}
}
//
const getTagText = (state) => {
switch (state) {
case 0:
return '待执行';
case 1:
return '执行中';
case 2:
return '执行完成';
case 3:
return '执行出错';
default:
return '未知状态';
}
}
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
onMounted(async function () {
await get()
await getArea()
await getTotalBeans()
})
</script>
<template>
<el-row>
<el-col>
<el-card style="margin-bottom: 20px;margin-top: 10px">
<el-row style="margin-bottom: 10px">
<el-col :span="3">
<div class="head-card-element">
<el-text class="mx-1" size="large">精网号</el-text>
<el-input v-model="selectData.jwcode" placeholder="请输入精网号" style="width: 115px" clearable />
</div>
</el-col>
<el-col :span="4">
<div class="head-card-element">
<el-text class="mx-1" size="large">所属地区</el-text>
<el-select v-model="selectData.market" placeholder="请选择所属地区" clearable style="width:150px">
<el-option v-for="item in market" :key="item" :label="item" :value="item" />
</el-select>
</div>
</el-col>
<el-col :span="17">
<div class="head-card-element">
<el-text class="mx-1" size="large">充值时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator=""
start-placeholder="起始时间" end-placeholder="结束时间" style="width: 350px"
@change="handleDatePickerChange" :default-time="defaultTime" />
<el-button @click="getToday()" style="margin-left: 10px"
:type="activeTimeRange === 'today' ? 'primary' : ''">
</el-button>
<el-button @click="getYesterday()" style="margin-left: 10px"
:type="activeTimeRange === 'yesterday' ? 'primary' : ''">
</el-button>
<el-button @click="get7Days()" style="margin-left: 10px"
:type="activeTimeRange === '7days' ? 'primary' : ''"> 近7天
</el-button>
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button>
<el-button type="primary" style="width: 80px;" @click="exportExcel()">导出Excel</el-button>
<el-button type="primary" style="width: 95px;" @click="openExportList">查看导出列表</el-button>
</div>
</el-col>
</el-row>
<el-row>
</el-row>
</el-card>
</el-col>
</el-row>
<el-row>
<el-col>
<el-card>
<div class="bean-info">
<!-- 汉字用 <strong> 加粗动态数据用 <span> 包一层单独改色 -->
金豆总数{{ format3(beanNum) }}&nbsp;&nbsp;&nbsp;&nbsp;
付费金豆数{{ format3(permanentBeans) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金豆数{{ format3(freeBean) }}
</div>
<!-- 设置表格容器的高度和滚动样式 -->
<div style="height: 520px; overflow-y: auto;margin-top: 10px;">
<el-table :data="tableData" style="width: 100%" height="520px" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<el-table-column fixed="left" prop="name" label="姓名" min-width="100" />
<el-table-column fixed="left" prop="jwcode" label="精网号" min-width="110px" />
<el-table-column prop="market" label="所属地区" min-width="100px" />
<el-table-column prop="permanentBean" label="付费金豆" sortable="custom" min-width="110px" />
<el-table-column prop="freeBean" label="免费金豆" sortable="custom" min-width="110px" />
<el-table-column prop="remark" label="备注" min-width="150px" show-overflow-tooltip />
<el-table-column prop="rechargeTime" label="充值时间" min-width="200px">
<template #default="scope">
{{ moment(scope.row.rechargeTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination" style="margin-top: 20px">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="handlePageSizeChange" @current-change="handleCurrentChange"></el-pagination>
</div>
</el-card>
</el-col>
</el-row>
<!-- 导出弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" label="文件名" />
<el-table-column prop="state" label="状态">
<template #default="scope">
<el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
{{ getTagText(scope.row.state) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template #default="scope">
{{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" size="small" @click="downloadExportFile(scope.row)"
:disabled="scope.row.state !== 2">
下载
</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button text @click="exportListVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped></style>

101
src/views/recharge/coinRecharge.vue

@ -1,101 +0,0 @@
<template>
<div>
<!-- 这里放置标签切换的按钮 -->
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
<el-button
:type="activeTab === 'add' ? 'primary' : 'default'"
@click="goToAdd"
>
新增充值
</el-button>
<el-button
:type="activeTab === 'detail' ? 'primary' : 'default'"
@click="goToDetail"
>
金币充值明细
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
<router-view></router-view>
</div>
</template>
<script setup>
import {onMounted, ref, watch} from 'vue';
import {useRouter, useRoute} from 'vue-router';
import {storeToRefs} from "pinia";
import {useAdminStore} from "@/store/index.js";
const router = useRouter();//
const route = useRoute();//
// activeTab
const activeTab = ref(route.name === 'coinRechargeDetail' ? 'detail' : 'add');
//coinConsumeDetaildetailadd
//coinConsumeadd
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const goToAdd = () => {
// activeTab add
activeTab.value = 'add';
router.push({name: 'addCoinRecharge'});
};
const goToDetail = () => {
// activeTab detail
activeTab.value = 'detail';
router.push({name: 'coinRechargeDetail'});
};
//
const navigateTo = (name) => {
activeTab.value = name;
if (name === 'add') {
router.push({name: 'addCoinRecharge'});
} else if (name === 'detail') {
router.push({name: 'coinRechargeDetail'});
}
};
// menuName
const hasMenuPermission = (tree, targetName) => {
for (const node of tree) {
if (node.menuName === targetName) return true;
if (node.children && hasMenuPermission(node.children, targetName)) return true;
}
return false;
};
//
const getDefaultRoute = () => {
if (!menuTree.value) return 'add';
const hasRecharge = hasMenuPermission(menuTree.value, '提交金币充值');
return hasRecharge ? 'add' : 'detail';
};
//
watch(() => route.name, (newName) => {
if (newName === 'add' || newName === 'detail') {
activeTab.value = newName;
} else if (newName === 'coinRecharge') {
const defaultRoute = getDefaultRoute();
navigateTo(defaultRoute);
}
});
//
onMounted(() => {
if (route.name === 'coinRecharge') {
const defaultRoute = getDefaultRoute();
navigateTo(defaultRoute);
} else {
//
if (route.name === 'add' || route.name === 'detail') {
activeTab.value = route.name;
}
}
});
</script>

210
src/views/recharge/addCoinRecharge.vue → src/views/recharge/gold/addCoinRecharge.vue

@ -4,8 +4,8 @@ import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import axios from 'axios' import axios from 'axios'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import API from '@/util/http'
import { uploadFile } from '@/util/request';
import API from '@/util/http.js'
import { uploadFile } from '@/util/request.js';
import moment from 'moment' import moment from 'moment'
import { range, re } from 'mathjs' import { range, re } from 'mathjs'
import { utils, read } from 'xlsx' import { utils, read } from 'xlsx'
@ -63,9 +63,9 @@ const recharge = ref({
activity: '', // activity activity: '', // activity
voucher: '', voucher: '',
rechargeWay: '客服充值', rechargeWay: '客服充值',
freeGold: '',
freeGold: "",
money: null, money: null,
permanentGold: '',
permanentGold: "",
rateName: null, rateName: null,
rateId: null, rateId: null,
payModel: '', // payModel payModel: '', // payModel
@ -83,6 +83,7 @@ const add = async function () {
if (formattedRecharge.permanentGold) { if (formattedRecharge.permanentGold) {
formattedRecharge.permanentGold = Number(formattedRecharge.permanentGold) * 100; formattedRecharge.permanentGold = Number(formattedRecharge.permanentGold) * 100;
} }
if (formattedRecharge.freeGold) { if (formattedRecharge.freeGold) {
formattedRecharge.freeGold = Number(formattedRecharge.freeGold) * 100; formattedRecharge.freeGold = Number(formattedRecharge.freeGold) * 100;
} }
@ -130,22 +131,21 @@ const add = async function () {
// //
const addBefore = () => { const addBefore = () => {
if(!recharge.value.permanentGold){
recharge.value.permanentGold ='0'
}
if(!recharge.value.freeGold){
recharge.value.freeGold ='0'
}
Ref.value.validate(async (valid) => { Ref.value.validate(async (valid) => {
if (valid) { if (valid) {
// add getUser
//
// try {
// await getUser(recharge.value.jwcode);
// if (!user.value.jwcode) {
// ElMessage.error('');
// return;
// }
// } catch (error) {
// ElMessage.error('');
// return;
// }
if (Number(recharge.value.permanentGold) === 0 && Number(recharge.value.freeGold) === 0) {
ElMessage({
type: 'error',
message: '永久金币和免费金币不能同时为0'
})
return
}
if (recharge.value.rateName == null || recharge.value.rateName == '' || recharge.value.rateName == undefined) { if (recharge.value.rateName == null || recharge.value.rateName == '' || recharge.value.rateName == undefined) {
ElMessage({ ElMessage({
@ -187,8 +187,9 @@ const addBefore = () => {
// //
// //
const Ref = ref(null) const Ref = ref(null)
const validateJwCode = (rule, value, callback) => {
const rules = reactive({
jwcode: [{
required: true, validator: (rule, value, callback) => {
if (!value) { if (!value) {
callback(new Error('精网号不能为空')); callback(new Error('精网号不能为空'));
return; return;
@ -198,15 +199,16 @@ const validateJwCode = (rule, value, callback) => {
return; return;
} }
callback(); callback();
};
const rules = reactive({
jwcode: [{ required: true, validator: validateJwCode, trigger: 'blur' }],
}, trigger: 'blur'
}],
activity: [{ required: true, message: '请选择活动名称', trigger: 'blur' }], activity: [{ required: true, message: '请选择活动名称', trigger: 'blur' }],
permanentGold: [ permanentGold: [
{ required: true, message: '请输入永久金币数', trigger: 'blur' },
{ required: true, message: '请输入永久金币数', trigger: 'change' },
{ {
validator: (rule, value, callback) => { validator: (rule, value, callback) => {
if(!value){
value = '0'
}
// //
if (/[^0-9.]/.test(value)) { if (/[^0-9.]/.test(value)) {
callback(new Error('不能包含特殊符号或负数')); callback(new Error('不能包含特殊符号或负数'));
@ -242,9 +244,12 @@ const rules = reactive({
} }
], ],
freeGold: [ freeGold: [
{ required: true, message: '请输入免费金币数', trigger: 'blur' },
{ required: true, message: '请输入免费金币数', trigger: 'change' },
{ {
validator: (rule, value, callback) => { validator: (rule, value, callback) => {
if(!value){
value = '0'
}
// //
if (/[^0-9.]/.test(value)) { if (/[^0-9.]/.test(value)) {
callback(new Error('不能包含特殊符号或负数')); callback(new Error('不能包含特殊符号或负数'));
@ -331,9 +336,20 @@ const rules = reactive({
const user = ref({}) const user = ref({})
const getUser = async function (jwcode) { const getUser = async function (jwcode) {
trimJwCode(); trimJwCode();
try {
//
if (!jwcode) {
ElMessage.warning('精网号不能为空');
return;
}
//
if (!/^\d{1,9}$/.test(jwcode)) {
ElMessage.warning('精网号必须为数字且不超过九位');
deleteRecharge()
return;
}
try {
const result = await API({ const result = await API({
url: '/user/selectUser', url: '/user/selectUser',
data: { data: {
@ -342,6 +358,7 @@ const getUser = async function (jwcode) {
}) })
if (result.code === 0) { if (result.code === 0) {
ElMessage.error(result.msg); ElMessage.error(result.msg);
} else if (result.data === null) { } else if (result.data === null) {
@ -515,9 +532,9 @@ const deleteRecharge = function () {
market: adminData.value.market, market: adminData.value.market,
voucher: '', voucher: '',
rechargeWay: '客服充值', rechargeWay: '客服充值',
freeGold: Number(),
freeGold: '',
money: null, money: null,
permanentGold: Number(),
permanentGold: '',
rateId: null rateId: null
} }
imageUrl.value = '' imageUrl.value = ''
@ -539,23 +556,10 @@ onMounted(() => {
<template> <template>
<div> <div>
<el-form
:model="recharge"
ref="Ref"
:rules="rules"
label-width="auto"
style="max-width: 600px"
class="add-form"
>
<el-form :model="recharge" ref="Ref" :rules="rules" label-width="auto" style="max-width: 600px" class="add-form">
<el-form-item prop="jwcode" label="精网号"> <el-form-item prop="jwcode" label="精网号">
<el-input v-model="recharge.jwcode" style="width: 220px" /> <el-input v-model="recharge.jwcode" style="width: 220px" />
<el-button
type="primary"
@click="getUser(recharge.jwcode)"
style="margin-left: 20px"
>查询</el-button
>
<el-button type="primary" @click="getUser(recharge.jwcode)" style="margin-left: 20px">查询</el-button>
</el-form-item> </el-form-item>
<!-- <el-form-item prop="activity" label="活动名称"> <!-- <el-form-item prop="activity" label="活动名称">
<el-select <el-select
@ -573,20 +577,16 @@ onMounted(() => {
</el-select> </el-select>
</el-form-item> --> </el-form-item> -->
<el-form-item prop="activity" label="活动名称"> <el-form-item prop="activity" label="活动名称">
<el-input
v-model="recharge.activity"
placeholder="请输入活动名称"
style="width: 300px"
/>
<el-input v-model="recharge.activity" placeholder="请输入活动名称" style="width: 300px" />
</el-form-item> </el-form-item>
<el-form-item prop="permanentGold" label="永久金币"> <el-form-item prop="permanentGold" label="永久金币">
<el-input v-model="recharge.permanentGold" style="width: 100px" />
<el-input v-model="recharge.permanentGold" placeholder="0" style="width: 100px" />
<p></p> <p></p>
</el-form-item> </el-form-item>
<el-form-item prop="freeGold" label="免费金币"> <el-form-item prop="freeGold" label="免费金币">
<el-input v-model="recharge.freeGold" style="width: 100px" />
<el-input v-model="recharge.freeGold" placeholder="0" style="width: 100px" />
<p></p> <p></p>
</el-form-item> </el-form-item>
<!-- <el-form-item label="充值金额"> <!-- <el-form-item label="充值金额">
@ -610,17 +610,8 @@ onMounted(() => {
<el-form-item label="充值金额" required> <el-form-item label="充值金额" required>
<!-- 货币名称 --> <!-- 货币名称 -->
<el-form-item prop="rateName" style="display: inline-block; margin-left:0;"> <el-form-item prop="rateName" style="display: inline-block; margin-left:0;">
<el-select
v-model="recharge.rateName"
placeholder="货币名称"
style="width: 100px"
>
<el-option
v-for="item in rateName"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="recharge.rateName" placeholder="货币名称" style="width: 100px">
<el-option v-for="item in rateName" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -631,50 +622,19 @@ onMounted(() => {
</el-form-item> </el-form-item>
<el-form-item prop="payModel" label="收款方式"> <el-form-item prop="payModel" label="收款方式">
<el-select
v-model="recharge.payModel"
placeholder="请选择"
style="width: 300px"
>
<el-option
v-for="item in payModel"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="recharge.payModel" placeholder="请选择" style="width: 300px">
<el-option v-for="item in payModel" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item prop="payTime" label="交款时间"> <el-form-item prop="payTime" label="交款时间">
<!-- 修改 type 属性为 datetime 以支持时分秒选择 --> <!-- 修改 type 属性为 datetime 以支持时分秒选择 -->
<el-date-picker
v-model="recharge.payTime"
type="datetime"
style="width: 300px"
/>
<el-date-picker v-model="recharge.payTime" type="datetime" style="width: 300px" />
</el-form-item> </el-form-item>
<el-form-item
prop="voucher"
label="交款凭证"
style="margin-bottom: 5px"
>
<el-upload
:http-request="customUpload"
class="avatar-uploader"
:show-file-list="false"
:before-upload="beforeAvatarUpload"
style="width: 100px; height: 115px"
>
<img
v-if="imageUrl"
:src="imageUrl"
class="avatar"
style="width: 100px; height: 115px"
/>
<el-icon
v-else
class="avatar-uploader-icon"
style="width: 100px; height: 100px"
>
<el-form-item prop="voucher" label="交款凭证" style="margin-bottom: 5px">
<el-upload :http-request="customUpload" class="avatar-uploader" :show-file-list="false"
:before-upload="beforeAvatarUpload" style="width: 100px; height: 115px">
<img v-if="imageUrl" :src="imageUrl" class="avatar" style="width: 100px; height: 115px" />
<el-icon v-else class="avatar-uploader-icon" style="width: 100px; height: 100px">
<Plus /> <Plus />
</el-icon> </el-icon>
</el-upload> </el-upload>
@ -683,37 +643,17 @@ onMounted(() => {
</p> </p>
</el-form-item> </el-form-item>
<el-form-item prop="remark" label="备注"> <el-form-item prop="remark" label="备注">
<el-input
v-model="recharge.remark"
style="width: 300px"
:rows="2"
maxlength="100"
show-word-limit
type="textarea"
/>
<el-input v-model="recharge.remark" style="width: 300px" :rows="2" maxlength="100" show-word-limit
type="textarea" />
</el-form-item> </el-form-item>
<el-form-item prop="adminName" label="提交人">
<el-input
style="width: 300px"
:value="adminData.adminName"
disabled
placeholder="提交人姓名"
/>
</el-form-item>
<el-button @click="deleteRecharge" style="margin-left: 280px" type="success"
>重置</el-button
>
<el-button @click="deleteRecharge" style="margin-left: 280px" type="success">重置</el-button>
<el-button type="primary" @click="addBefore"> 提交 </el-button> <el-button type="primary" @click="addBefore"> 提交 </el-button>
</el-form> </el-form>
<!-- 客户信息栏 --> <!-- 客户信息栏 -->
<el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info"> <el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info">
<el-form
:model="user"
label-width="auto"
style="max-width: 1000px"
label-position="left"
>
<el-form :model="user" label-width="auto" style="max-width: 1000px" label-position="left">
<el-text size="large" style="margin-left: 20px">客户信息</el-text> <el-text size="large" style="margin-left: 20px">客户信息</el-text>
<!-- 第一行姓名 + 历史金币 --> <!-- 第一行姓名 + 历史金币 -->
@ -734,10 +674,8 @@ onMounted(() => {
<p v-else></p> <p v-else></p>
</el-form-item> </el-form-item>
<el-form-item style="margin-top: -23px"> <el-form-item style="margin-top: -23px">
<span
style="display: inline; white-space: nowrap; color: #b1b1b1"
v-if="user.historyPermanentGold !== undefined"
>(永久金币:{{ user.historyPermanentGold /100 }};免费金币:{{
<span style="display: inline; white-space: nowrap; color: #b1b1b1"
v-if="user.historyPermanentGold !== undefined">(永久金币:{{ user.historyPermanentGold / 100 }};免费金币:{{
(user.historyFreeGold) / 100 (user.historyFreeGold) / 100
}};任务金币:{{ user.historyTaskGold / 100 }})</span> }};任务金币:{{ user.historyTaskGold / 100 }})</span>
</el-form-item> </el-form-item>
@ -753,17 +691,13 @@ onMounted(() => {
</el-col> </el-col>
<el-col :span="14"> <el-col :span="14">
<el-form-item label="当前金币总数" style="width: 500px"> <el-form-item label="当前金币总数" style="width: 500px">
<span
style="color: #2fa1ff; margin-right: 5px"
v-if="user.nowSumGold !== undefined"
>{{ user.nowSumGold /100}}</span>
<span style="color: #2fa1ff; margin-right: 5px" v-if="user.nowSumGold !== undefined">{{ user.nowSumGold
/100}}</span>
</el-form-item> </el-form-item>
<!-- 金币详情独立显示 --> <!-- 金币详情独立显示 -->
<el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 --> <el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
<span
style="color: #b1b1b1; margin-left: 0px"
v-if="user.nowPermanentGold !== undefined"
>(永久金币:{{ user.nowPermanentGold /100}};
<span style="color: #b1b1b1; margin-left: 0px" v-if="user.nowPermanentGold !== undefined">(永久金币:{{
user.nowPermanentGold /100}};
免费金币:{{ user.nowFreeGold / 100 }}; 免费金币:{{ user.nowFreeGold / 100 }};
任务金币:{{ user.nowTaskGold / 100 }})</span> 任务金币:{{ user.nowTaskGold / 100 }})</span>
</el-form-item> </el-form-item>

89
src/views/recharge/gold/coinRecharge.vue

@ -0,0 +1,89 @@
<template>
<div>
<!-- 这里放置标签切换的按钮 -->
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
<el-button
:type="activeTab === 'addCoinRecharge' ? 'primary' : 'default'"
@click="navigateTo('addCoinRecharge')"
:disabled="!hasAdd"
>
新增充值
</el-button>
<el-button
:type="activeTab === 'coinRechargeDetail' ? 'primary' : 'default'"
@click="navigateTo('coinRechargeDetail')"
:disabled="!hasDetail"
>
金币充值明细
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
<router-view></router-view>
</div>
</template>
<script setup>
import {onMounted, ref, watch} from 'vue';
import {useRoute, useRouter} from 'vue-router';
import {storeToRefs} from "pinia";
import {useAdminStore} from "@/store/index.js";
import {hasMenuPermission, permissionMapping} from "@/utils/menuTreePermission.js";
const router = useRouter();
const route = useRoute();
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const activeTab = ref('');
const hasAdd = ref(false);
const hasDetail = ref(false);
//
const navigateTo = (name) => {
activeTab.value = name;
router.push({name});
};
//
const initPermissions = () => {
if (!menuTree.value || !menuTree.value.length) return;
hasAdd.value = hasMenuPermission(menuTree.value, permissionMapping.Submit_Gold_Coin_Recharge);
hasDetail.value = hasMenuPermission(menuTree.value, permissionMapping.View_Gold_Coin_Recharge_Details);
};
//
const getDefaultAuditRoute = () => {
initPermissions();
if (hasAdd.value) return 'addCoinRecharge';
if (hasDetail.value) return 'coinRechargeDetail';
return 'addCoinRecharge';
};
//
watch(() => route.name, (newName) => {
initPermissions()
if (newName === 'addCoinRecharge' || newName === 'coinRechargeDetail') {
activeTab.value = newName;
} else if (newName === 'coinRecharge') {
// 访 /coinConsume
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
}
});
//
onMounted(() => {
initPermissions()
if (route.name === 'coinRecharge') {
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
} else {
//
if (route.name === 'addCoinRecharge' || route.name === 'coinRechargeDetail') {
activeTab.value = route.name;
}
}
});
</script>

263
src/views/recharge/coinRechargeDetail.vue → src/views/recharge/gold/coinRechargeDetail.vue

@ -1,11 +1,16 @@
<script setup> <script setup>
import {ref, onMounted, reactive, computed} from 'vue'
import ElementPlus from 'element-plus'
import {ElMessage, ElMessageBox} from 'element-plus'
import {AiFillRead} from 'vue-icons-plus/ai'
import axios from 'axios'
import { onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import moment from 'moment' import moment from 'moment'
import API from '@/util/http'
import API from '@/util/http.js'
import { reverseMarketMapping } from '@/utils/marketMap.js';
import dayjs from "dayjs";
const trimJwCode = () => {
if (rechargeUser.value.jwcode) {
rechargeUser.value.jwcode = rechargeUser.value.jwcode.replace(/\s/g, '');
}
}
// //
const adminData = ref({}) const adminData = ref({})
const getAdminData = async function () { const getAdminData = async function () {
@ -19,6 +24,10 @@ const getAdminData = async function () {
console.log('请求失败', error) console.log('请求失败', error)
} }
} }
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
// //
// //
const tableData = ref([]) const tableData = ref([])
@ -32,8 +41,105 @@ const handleDatePickerChange = () => {
// =========================================== // ===========================================
// recharge // recharge
const rechargeUser = ref({ const rechargeUser = ref({
adminId: adminData.value.id
adminId: adminData.value.id,
market: "",
}) })
/*//处理地区选择变化
const handleMarketChange = (value) => {
if (Array.isArray(value) && value.length > 0) {
const ids = new Set();
value.forEach(path => {
const lastName = path[path.length - 1];
const id = reverseMarketMapping[lastName];
if (id) ids.add(Number(id));
});
//
const getAllLeafNames = (nodes) => {
const leafNames = [];
const traverse = (node, parentName = null) => {
if (!node.children || node.children.length === 0) {
leafNames.push({ name: node.label, parent: parentName });
} else {
node.children.forEach(child => traverse(child, node.label));
}
};
nodes.forEach(node => traverse(node));
return leafNames;
};
const leafNameMap = getAllLeafNames(market.value); //
// { parentName: [childName1, childName2, ...] }
const parentToChildren = {};
leafNameMap.forEach(({ name, parent }) => {
if (!parentToChildren[parent]) parentToChildren[parent] = [];
parentToChildren[parent].push(name);
});
//
const selectedLeafNames = value.map(path => path[path.length - 1]);
// parent parent
Object.entries(parentToChildren).forEach(([parent, children]) => {
const allChildrenSelected = children.every(child => selectedLeafNames.includes(child));
if (allChildrenSelected && reverseMarketMapping[parent]) {
ids.add(Number(reverseMarketMapping[parent]));
}
});
// ID
const idArray = Array.from(ids);
rechargeUser.value.market= idArray.length > 0 ? idArray[0].toString() : "";
} else {
rechargeUser.value.market = "";
}
console.log('最终映射后的 market IDs:', rechargeUser.value.market);
};*/
//
const selectedMarketPath = ref("")
const handleMarketChange = (value) => {
if (value && value.length > 0) {
const lastValue = value[value.length - 1]
rechargeUser.value.market = reverseMarketMapping[lastValue]
} else {
rechargeUser.value.market = ''
}
}
/*// 处理地区选择变化单选场景
const handleMarketChange = (value) => {
//
rechargeUser.value.market = "";
//
if (Array.isArray(value) && value.length > 0) {
// value
const selectedPath = value[0];
if (Array.isArray(selectedPath) && selectedPath.length > 0) {
//
const lastName = selectedPath[selectedPath.length - 1];
// ID
const id = reverseMarketMapping[lastName];
// ID
if (id) {
rechargeUser.value.market = String(id);
}
}
}
console.log('最终选中的 market ID:', rechargeUser.value.market);
};*/
// //
const getObj = ref({ const getObj = ref({
pageNum: 1, pageNum: 1,
@ -48,7 +154,7 @@ const activity = ref([])
// //
const allData = ref([]) const allData = ref([])
// //
const market = ref([])
const market = ref("")
// //
const formatTime = (val) => val ? moment(val).format('YYYY-MM-DD HH:mm:ss') : '' const formatTime = (val) => val ? moment(val).format('YYYY-MM-DD HH:mm:ss') : ''
// money permanentGold freeGold // money permanentGold freeGold
@ -116,25 +222,40 @@ const getActivity = async function () {
// //
} }
} }
//
//
const marketsTree = ref("")
//
const getArea = async function () { const getArea = async function () {
console.log('获取地区adminid', adminData.value)
try { try {
// POST // POST
const result = await API({ const result = await API({
url: '/general/adminMarkets',
data: {account: adminData.value.account}
url: '/market/selectMarket',
}); });
// //
console.log('请求成功', result) console.log('请求成功', result)
//
const transformTree = (nodes) => {
//
const allChildren = nodes.flatMap(node => node.children || []);
return allChildren.map(child => {
const grandchildren = child.children && child.children.length
? transformTree([child]) //
: null;
return {
value: child.name,
label: child.name,
children: grandchildren
};
});
};
// //
market.value = result.data
console.log('地区', market.value)
market.value = transformTree(result.data)
console.log('转换后的地区树==============', market.value)
} catch (error) { } catch (error) {
console.log('请求失败', error) console.log('请求失败', error)
//
} }
} }
@ -189,6 +310,21 @@ const get = async function (val) {
rechargeUser.value.sortOrder = sortOrder.value rechargeUser.value.sortOrder = sortOrder.value
console.log('搜索参数', getObj.value) console.log('搜索参数', getObj.value)
// POST // POST
if (rechargeUser.value.market === '9' || rechargeUser.value.market === '9999') {
rechargeUser.value.market = '';
}
if (rechargeUser.value.jwcode) {
//
const numberRegex = /^\d{1,9}$/;
//
if (!numberRegex.test(rechargeUser.value.jwcode)) {
ElMessage.error('请检查精网号格式')
//
return
}
}
const result = await API({ const result = await API({
url: '/recharge/selectBy', url: '/recharge/selectBy',
data: { data: {
@ -231,11 +367,11 @@ const get = async function (val) {
tableData.value = tableData.value.map(item => ({ tableData.value = tableData.value.map(item => ({
...item, ...item,
// //
permanentGold: (Number(item.permanentGold) || 0) / 100,
permanentGold: (Number(item.permanentGold) || 0) ,
// //
freeGold: (Number(item.freeGold) || 0) / 100,
freeGold: (Number(item.freeGold) || 0) ,
// //
money: (Number(item.money) || 0) / 100
money: (Number(item.money) || 0)
})) }))
console.log('tableData', tableData.value) console.log('tableData', tableData.value)
// //
@ -248,6 +384,7 @@ const get = async function (val) {
} }
// //
const search = function () { const search = function () {
trimJwCode()
getObj.value.pageNum = 1 getObj.value.pageNum = 1
get() get()
} }
@ -256,7 +393,8 @@ const reset = function () {
delete rechargeUser.value.jwcode delete rechargeUser.value.jwcode
delete rechargeUser.value.activity delete rechargeUser.value.activity
delete rechargeUser.value.payPlatform delete rechargeUser.value.payPlatform
delete rechargeUser.value.market
rechargeUser.value.market = ""
selectedMarketPath.value = "" //
delete rechargeUser.value.startTime delete rechargeUser.value.startTime
delete rechargeUser.value.endTime delete rechargeUser.value.endTime
delete sortField.value delete sortField.value
@ -268,21 +406,12 @@ const reset = function () {
} }
// //
const getToday = function () { const getToday = function () {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate()
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + 1
)
const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
console.log('getTime', getTime.value) console.log('getTime', getTime.value)
activeTimeRange.value = 'today' // activeTimeRange.value = 'today' //
get() get()
} }
const handlePageSizeChange = function (val) { const handlePageSizeChange = function (val) {
@ -295,37 +424,21 @@ const handleCurrentChange = function (val) {
} }
// //
const getYesterday = function () { const getYesterday = function () {
const yesterday = new Date()
yesterday.setDate(yesterday.getDate() - 1)
const startTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate()
)
const endTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate() + 1
)
const today = dayjs()
const startTime = today.subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
console.log('getTime', getTime.value) console.log('getTime', getTime.value)
activeTimeRange.value = 'yesterday' // activeTimeRange.value = 'yesterday' //
get() get()
} }
// 7 // 7
const get7Days = function () { const get7Days = function () {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() - 6
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + 1
)
const today = dayjs()
const startTime = today.subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
console.log('getTime', getTime.value) console.log('getTime', getTime.value)
activeTimeRange.value = '7days' // activeTimeRange.value = '7days' //
@ -333,7 +446,10 @@ const get7Days = function () {
get() get()
} }
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
// //
onMounted(async function () { onMounted(async function () {
await get() await get()
@ -344,7 +460,7 @@ onMounted(async function () {
}) })
// //
const sortField = ref() const sortField = ref()
const sortOrder = ref(1)
const sortOrder = ref()
// //
const handleSortChange = (column) => { const handleSortChange = (column) => {
@ -354,8 +470,8 @@ const handleSortChange = (column) => {
sortField.value = 'money' sortField.value = 'money'
} else if (column.prop === 'freeGold') { } else if (column.prop === 'freeGold') {
sortField.value = 'freeGold' sortField.value = 'freeGold'
} else if (column.prop === 'payTime') {
sortField.value = 'payTime'
} else if (column.prop === 'auditTime') {
sortField.value = 'auditTime'
} else if (column.prop === 'createTime') { } else if (column.prop === 'createTime') {
sortField.value = 'createTime' sortField.value = 'createTime'
} else if (column.prop === 'permanentGold') { } else if (column.prop === 'permanentGold') {
@ -466,6 +582,8 @@ const getTagText = (state) => {
return '未知状态'; return '未知状态';
} }
} }
</script> </script>
<template> <template>
@ -490,9 +608,8 @@ const getTagText = (state) => {
<el-col :span="6"> <el-col :span="6">
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1" size="large">所属地区</el-text> <el-text class="mx-1" size="large">所属地区</el-text>
<el-select v-model="rechargeUser.market" placeholder="请选择所属地区" style="width: 180px" clearable>
<el-option v-for="item in market" :key="item" :label="item" :value="item"/>
</el-select>
<el-cascader v-model="selectedMarketPath" :options="market" placeholder="请选择所属地区" clearable
style="width:180px" @change="handleMarketChange" />
</div> </div>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
@ -510,7 +627,8 @@ const getTagText = (state) => {
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1" size="large">充值时间</el-text> <el-text class="mx-1" size="large">充值时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间" <el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange"/>
end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange"
:default-time="defaultTime" />
<el-button @click="getToday()" style="margin-left: 10px" <el-button @click="getToday()" style="margin-left: 10px"
:type="activeTimeRange === 'today' ? 'primary' : ''"> :type="activeTimeRange === 'today' ? 'primary' : ''">
</el-button> </el-button>
@ -534,9 +652,10 @@ const getTagText = (state) => {
<el-col> <el-col>
<el-card> <el-card>
<div> <div>
充值金额{{ (permanentGolds) / 100 }}新币永久金币{{
permanentGolds / 100
}}金币免费金币{{ freeGolds / 100 }}金币
充值新币{{ format3(permanentGolds) }}新币&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ format3(permanentGolds) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ format3(freeGolds ) }}金币
</div> </div>
<!-- 设置表格容器的高度和滚动样式 --> <!-- 设置表格容器的高度和滚动样式 -->
<div style="height: 520px; overflow-y: auto;margin-top: 10px;"> <div style="height: 520px; overflow-y: auto;margin-top: 10px;">
@ -560,9 +679,9 @@ const getTagText = (state) => {
<el-table-column prop="payModel" label="支付方式" width="100px" /> <el-table-column prop="payModel" label="支付方式" width="100px" />
<el-table-column prop="remark" label="备注" width="150px" show-overflow-tooltip /> <el-table-column prop="remark" label="备注" width="150px" show-overflow-tooltip />
<el-table-column prop="adminName" label="提交人" width="100px" /> <el-table-column prop="adminName" label="提交人" width="100px" />
<el-table-column prop="payTime" sortable label="充值时间" width="200px">
<el-table-column prop="auditTime" sortable label="充值时间" width="200px">
<template #default="scope"> <template #default="scope">
{{ moment(scope.row.payTime).format('YYYY-MM-DD HH:mm:ss') }}
{{ moment(scope.row.auditTime).format('YYYY-MM-DD HH:mm:ss') }}
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -571,8 +690,7 @@ const getTagText = (state) => {
<!-- 分页 --> <!-- 分页 -->
<div class="pagination" style="margin-top: 20px"> <div class="pagination" style="margin-top: 20px">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]" <el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="handlePageSizeChange"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination> @current-change="handleCurrentChange"></el-pagination>
</div> </div>
</el-card> </el-card>
@ -580,12 +698,11 @@ const getTagText = (state) => {
</el-row> </el-row>
<!-- 导出弹窗 --> <!-- 导出弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%"> <el-dialog v-model="exportListVisible" title="导出列表" width="80%">
<el-table :data="exportList" style="width: 100%" :loading="exportListLoading">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" label="文件名" /> <el-table-column prop="fileName" label="文件名" />
<el-table-column prop="state" label="状态"> <el-table-column prop="state" label="状态">
<template #default="scope"> <template #default="scope">
<el-tag :type="getTagType(scope.row.state)"
:effect="scope.row.state === 3 ? 'light' : 'plain'">
<el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
{{ getTagText(scope.row.state) }} {{ getTagText(scope.row.state) }}
</el-tag> </el-tag>
</template> </template>

101
src/views/refund/coinRefund.vue

@ -1,101 +0,0 @@
<template>
<div>
<!-- 这里放置标签切换的按钮 -->
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
<el-button
:type="activeTab === 'add' ? 'primary' : 'default'"
@click="goToAdd"
>
新增退款
</el-button>
<el-button
:type="activeTab === 'detail' ? 'primary' : 'default'"
@click="goToDetail"
>
金币退款明细
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
<router-view></router-view>
</div>
</template>
<script setup>
import {onMounted, ref, watch} from 'vue';
import { useRouter, useRoute } from 'vue-router';
import {useAdminStore} from "@/store/index.js";
import {storeToRefs} from "pinia";
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const router = useRouter();//
const route = useRoute();//
// activeTab
const activeTab = ref(route.name === 'coinRefundDetail' ? 'detail' : 'add');
const goToAdd = () => {
// activeTab add
activeTab.value = 'add';
router.push({ name: 'addCoinRefund' });
};
const goToDetail = () => {
// activeTab detail
activeTab.value = 'detail';
router.push({ name: 'coinRefundDetail' });
};
//
const navigateTo = (name) => {
activeTab.value = name;
if (name === 'add') {
router.push({name: 'addCoinRefund'});
} else if (name === 'detail') {
router.push({name: 'coinRefundDetail'});
}
};
// menuName
const hasMenuPermission = (tree, targetName) => {
for (const node of tree) {
if (node.menuName === targetName) return true;
if (node.children && hasMenuPermission(node.children, targetName)) return true;
}
return false;
};
//
const getDefaultRoute = () => {
if (!menuTree.value) return 'add';
const hasRecharge = hasMenuPermission(menuTree.value, '提交金币退款');
return hasRecharge ? 'add' : 'detail';
};
//
watch(() => route.name, (newName) => {
if (newName === 'add' || newName === 'detail') {
activeTab.value = newName;
} else if (newName === 'coinRefund') {
const defaultRoute = getDefaultRoute();
navigateTo(defaultRoute);
}
});
//
onMounted(() => {
if (route.name === 'coinRefund') {
const defaultRoute = getDefaultRoute();
navigateTo(defaultRoute);
} else {
//
if (route.name === 'add' || route.name === 'detail') {
activeTab.value = route.name;
}
}
});
</script>

170
src/views/refund/addCoinRefund.vue → src/views/refund/gold/addCoinRefund.vue

@ -1,4 +1,3 @@
<script setup> <script setup>
import { onMounted, reactive } from 'vue' import { onMounted, reactive } from 'vue'
import { ref, computed, watch } from 'vue' import { ref, computed, watch } from 'vue'
@ -6,7 +5,7 @@ import {ElMessage} from 'element-plus'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import axios from 'axios' import axios from 'axios'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import API from '@/util/http'
import API from '@/util/http.js'
import moment from 'moment' import moment from 'moment'
// import _ from 'lodash' // import _ from 'lodash'
@ -200,6 +199,17 @@ const user = ref({
const getUser = async function (jwcode) { const getUser = async function (jwcode) {
trimJwCode(); trimJwCode();
// cancelExceptJwcode(); // cancelExceptJwcode();
//
if (!jwcode) {
ElMessage.warning('精网号不能为空');
return;
}
//
if (!/^\d{1,9}$/.test(jwcode)) {
cancel()
return;
}
try { try {
// POST // POST
const result = await API({ const result = await API({
@ -267,6 +277,14 @@ const refundType = ref([{value: '商品退款', label: '商品退款'}]);
const goodsName = ref([]) const goodsName = ref([])
const getGoods = async function (jwcode) { const getGoods = async function (jwcode) {
trimJwCode(); trimJwCode();
//
//
if (!/^\d{1,9}$/.test(jwcode)) {
ElMessage.warning('精网号必须为数字且不超过九位');
return;
}
// //
if (!addRefund.value.jwcode) { if (!addRefund.value.jwcode) {
@ -422,56 +440,20 @@ onMounted(async function () {
<template> <template>
<div> <div>
<el-form
:model="addRefund"
ref="Ref"
:rules="rules"
label-width="auto"
style="max-width: 750px"
class="form-style"
>
<el-form :model="addRefund" ref="Ref" :rules="rules" label-width="auto" style="max-width: 750px" class="form-style">
<el-form-item prop="jwcode" label="精网号"> <el-form-item prop="jwcode" label="精网号">
<el-input
v-model="addRefund.jwcode"
style="width: 220px"
@change="getGoods(addRefund.jwcode)"
/>
<el-button
type="primary"
@click="getUser(addRefund.jwcode)"
style="margin-left: 20px"
>查询
</el-button
>
<el-input v-model="addRefund.jwcode" style="width: 220px" @change="getGoods(addRefund.jwcode)" />
<el-button type="primary" @click="getUser(addRefund.jwcode)" style="margin-left: 20px">查询
</el-button>
</el-form-item> </el-form-item>
<el-form-item prop="refundType" label="退款类型"> <el-form-item prop="refundType" label="退款类型">
<el-select
v-model="addRefund.refundType"
placeholder="请选择"
style="width: 300px"
>
<el-option
v-for="item in refundType"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="addRefund.refundType" placeholder="请选择" style="width: 220px">
<el-option v-for="item in refundType" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item prop="goodsName" label="商品名"> <el-form-item prop="goodsName" label="商品名">
<el-select
v-model="addRefund.goodsName"
placeholder="请选择"
style="width: 300px"
@change="handleSelectionChange"
>
<el-option
v-for="item in goodsName"
:key="item.key"
:label="item.label"
:value="item"
/>
<el-select v-model="addRefund.goodsName" placeholder="请选择" style="width: 220px" @change="handleSelectionChange">
<el-option v-for="item in goodsName" :key="item.key" :label="item.label" :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -482,77 +464,47 @@ onMounted(async function () {
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<div style="display: flex; align-items: center">
<el-form-item prop="permanentGold" label="永久金币" style="float: left">
<el-input
v-model="addRefund.permanentGold"
style="width: 100px"
:disabled="addRe.typeR === '0' ? true : false"
@input="handlePermanentGoldInput($event)"
type="number"
>
</el-input>
<p></p>
<el-form-item prop="permanentGold" label="永久金币">
<el-input v-model="addRefund.permanentGold" style="width: 220px"
:disabled="addRe.typeR === '0' ? true : false" @input="handlePermanentGoldInput($event)" type="number">
</el-input>&nbsp;&nbsp;
</el-form-item> </el-form-item>
<el-form-item
prop="freeGold"
label="免费金币"
style="margin-left: -20px; float: left"
>
<el-input
v-model="addRefund.freeGold"
style="float: left; width: 100px"
:disabled="addRe.typeR === '0' ? true : false"
@input="handleFreeGoldInput($event)"
type="number"
/>
<p></p>
<el-form-item prop="freeGold" label="免费金币">
<el-input v-model="addRefund.freeGold" style="float: left; width: 220px"
:disabled="addRe.typeR === '0' ? true : false" @input="handleFreeGoldInput($event)" type="number" />
&nbsp;&nbsp;
</el-form-item> </el-form-item>
<el-form-item prop="taskGold" label="任务金币" style="margin-left: -20px">
<el-input
v-model="addRefund.taskGold"
style="float: left; width: 100px"
:disabled="addRe.typeR === '0' ? true : false"
@input="handleTaskGoldInput($event)"
type="number"
/>
<p></p>
<div>
<el-form-item prop="taskGold" label="任务金币">
<el-input v-model="addRefund.taskGold" style="float: left; width: 220px"
:disabled="addRe.typeR === '0' ? true : false" @input="handleTaskGoldInput($event)" type="number" />
&nbsp;&nbsp;
</el-form-item> </el-form-item>
</div> </div>
<div>
<el-form-item prop="sumGold" label="退款金币总数"> <el-form-item prop="sumGold" label="退款金币总数">
<el-input disabled v-model="addRefund.sumGold" style="width: 100px">
<el-input disabled v-model="addRefund.sumGold" style="width: 220px">
</el-input> </el-input>
</el-form-item> </el-form-item>
</div>
<div>
<el-form-item prop="remark" label="备注"> <el-form-item prop="remark" label="备注">
<el-input
v-model="addRefund.remark"
style="width: 300px"
:rows="2"
maxlength="100"
show-word-limit
type="textarea"
/>
</el-form-item>
<el-form-item prop="adminName" label="提交人">
<el-input
style="width: 300px"
:value="adminData.adminName"
disabled
placeholder="提交人姓名"
/>
<el-input v-model="addRefund.remark" style="width: 220px" :rows="3" maxlength="100" show-word-limit
type="textarea" />
</el-form-item> </el-form-item>
<el-button type="success" @click="cancel()" style="margin-left: 280px">重置</el-button>
</div>
<el-button type="success" @click="cancel()" style="margin-left: 200px">重置</el-button>
<el-button type="primary" @click="addBefore"> 提交</el-button> <el-button type="primary" @click="addBefore"> 提交</el-button>
</el-form> </el-form>
<!-- 客户信息栏 --> <!-- 客户信息栏 -->
<el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info"> <el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info">
<el-form
:model="user"
label-width="auto"
style="max-width: 1000px"
label-position="left"
>
<el-form :model="user" label-width="auto" style="max-width: 1000px" label-position="left">
<el-text size="large" style="margin-left: 20px">客户信息</el-text> <el-text size="large" style="margin-left: 20px">客户信息</el-text>
<!-- 第一行姓名 + 历史金币 --> <!-- 第一行姓名 + 历史金币 -->
@ -580,17 +532,13 @@ onMounted(async function () {
</el-col> </el-col>
<el-col :span="14"> <el-col :span="14">
<el-form-item label="当前金币总数" style="width: 500px"> <el-form-item label="当前金币总数" style="width: 500px">
<span
style="color: #2fa1ff; margin-right: 5px"
v-if="user.nowSumGold !== undefined"
>{{ user.nowSumGold }}</span>
<span style="color: #2fa1ff; margin-right: 5px" v-if="user.nowSumGold !== undefined">{{ user.nowSumGold
}}</span>
</el-form-item> </el-form-item>
<!-- 金币详情独立显示 --> <!-- 金币详情独立显示 -->
<el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 --> <el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
<span
style="color: #b1b1b1; margin-left: 0px"
v-if="user.nowPermanentGold !== undefined"
>(永久金币:{{ user.nowPermanentGold }};
<span style="color: #b1b1b1; margin-left: 0px" v-if="user.nowPermanentGold !== undefined">(永久金币:{{
user.nowPermanentGold }};
免费金币:{{ user.nowFreeGold }}; 免费金币:{{ user.nowFreeGold }};
任务金币:{{ user.nowTaskGold }})</span> 任务金币:{{ user.nowTaskGold }})</span>
</el-form-item> </el-form-item>

89
src/views/refund/gold/coinRefund.vue

@ -0,0 +1,89 @@
<template>
<div>
<!-- 这里放置标签切换的按钮 -->
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
<el-button
:type="activeTab === 'addCoinRefund' ? 'primary' : 'default'"
@click="navigateTo('addCoinRefund')"
:disabled="!hasAdd"
>
新增退款
</el-button>
<el-button
:type="activeTab === 'coinRefundDetail' ? 'primary' : 'default'"
@click="navigateTo('coinRefundDetail')"
:disabled="!hasDetail"
>
金币退款明细
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
<router-view></router-view>
</div>
</template>
<script setup>
import {onMounted, ref, watch} from 'vue';
import {useRoute, useRouter} from 'vue-router';
import {storeToRefs} from "pinia";
import {useAdminStore} from "@/store/index.js";
import {hasMenuPermission, permissionMapping} from "@/utils/menuTreePermission.js";
const router = useRouter();
const route = useRoute();
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const activeTab = ref('');
const hasAdd = ref(false);
const hasDetail = ref(false);
//
const navigateTo = (name) => {
activeTab.value = name;
router.push({name});
};
//
const initPermissions = () => {
if (!menuTree.value || !menuTree.value.length) return;
hasAdd.value = hasMenuPermission(menuTree.value, permissionMapping.Submit_Gold_Coin_Refund);
hasDetail.value = hasMenuPermission(menuTree.value, permissionMapping.View_Gold_Coin_Refund_Details);
};
//
const getDefaultAuditRoute = () => {
initPermissions();
if (hasAdd.value) return 'addCoinRefund';
if (hasDetail.value) return 'coinRefundDetail';
return 'addCoinRefund';
};
//
watch(() => route.name, (newName) => {
initPermissions()
if (newName === 'addCoinRefund' || newName === 'coinRefundDetail') {
activeTab.value = newName;
} else if (newName === 'coinRefund') {
// 访 /coinConsume
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
}
});
//
onMounted(() => {
initPermissions()
if (route.name === 'coinRefund') {
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
} else {
//
if (route.name === 'addCoinRefund' || route.name === 'coinRefundDetail') {
activeTab.value = route.name;
}
}
});
</script>

342
src/views/refund/coinRefundDetail.vue → src/views/refund/gold/coinRefundDetail.vue

@ -1,14 +1,21 @@
<script setup> <script setup>
// 退 // 退
import {ref, onMounted, reactive, computed} from 'vue'
import ElementPlus from 'element-plus'
import {AiFillRead} from 'vue-icons-plus/ai'
import {ElMessage, ElMessageBox} from 'element-plus'
import axios from 'axios'
import { computed, onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import moment from 'moment' import moment from 'moment'
import API from '@/util/http'
import request from '@/util/http'
import API from '@/util/http.js'
import request from '@/util/http.js'
import { reverseMarketMapping } from "@/utils/marketMap.js";
import dayjs from "dayjs";
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
// //
const trimJwCode = () => { const trimJwCode = () => {
if (refundUser.value.jwcode) { if (refundUser.value.jwcode) {
@ -49,7 +56,9 @@ const getAdminData = async function () {
const tableData = ref([]) const tableData = ref([])
// ====================================== // ======================================
// detail // detail
const refundUser = ref({})
const refundUser = ref({
market: ""
})
// //
const getObj = ref({ const getObj = ref({
pageNum: 1, pageNum: 1,
@ -83,7 +92,8 @@ const getRefundTypes = async function () {
// 退 // 退
const result = await API({ const result = await API({
url: '/refund/refundType', //退 url: '/refund/refundType', //退
data: {} })
data: {}
})
console.log('退款类型请求成功', result) console.log('退款类型请求成功', result)
// //
if (Array.isArray(result.data)) { if (Array.isArray(result.data)) {
@ -108,7 +118,6 @@ const getSelectBy = async function (val) {
getObj.value.pageNum = val getObj.value.pageNum = val
} }
// todo
// //
if (getTime.value != null) { if (getTime.value != null) {
if (getTime.value.startTime != '' && getTime.value.endTime != '') { if (getTime.value.startTime != '' && getTime.value.endTime != '') {
@ -120,12 +129,25 @@ const getSelectBy = async function (val) {
refundUser.value.endTime = '' refundUser.value.endTime = ''
} }
// todo
// //
refundUser.value.sortField = sortField.value refundUser.value.sortField = sortField.value
refundUser.value.sortOrder = sortOrder.value refundUser.value.sortOrder = sortOrder.value
console.log('搜索参数', getObj.value) console.log('搜索参数', getObj.value)
// POST // POST
if (refundUser.value.market === '9' || refundUser.value.market === '9999') {
refundUser.value.market = '';
}
if (refundUser.value.jwcode) {
//
const numberRegex = /^\d{1,9}$/;
//
if (!numberRegex.test(refundUser.value.jwcode)) {
ElMessage.error('请检查精网号格式')
//
return
}
}
const result = await API({ const result = await API({
url: '/refund/selectBy', url: '/refund/selectBy',
data: { data: {
@ -158,13 +180,13 @@ const getSelectBy = async function (val) {
// //
tableData.value = result.data.list tableData.value = result.data.list
// 100
tableData.value = tableData.value.map(item => ({ tableData.value = tableData.value.map(item => ({
...item, ...item,
sumGold: (Number(item.sumGold) || 0) / 100,
permanentGold: (Number(item.permanentGold) || 0) / 100,
freeGold: (Number(item.freeGold) || 0) / 100,
taskGold: (Number(item.taskGold) || 0) / 100
sumGold: (Number(item.sumGold) || 0) ,
permanentGold: (Number(item.permanentGold) || 0) ,
freeGold: (Number(item.freeGold) || 0) ,
taskGold: (Number(item.taskGold) || 0)
})) }))
console.log('tableData', tableData.value) console.log('tableData', tableData.value)
// //
@ -179,32 +201,25 @@ const getSelectBy = async function (val) {
} }
// //
const search = function () { const search = function () {
trimJwCode()
getObj.value.pageNum = 1 getObj.value.pageNum = 1
getSelectBy() getSelectBy()
} }
// //
const reset = function () { const reset = function () {
refundUser.value = {}
refundUser.value = { market: "" }
sortField.value = '' sortField.value = ''
sortOrder.value = '' sortOrder.value = ''
getTime.value = {} getTime.value = {}
activeTimeRange.value = '' // activeTimeRange.value = '' //
selectedMarketPath.value = []
getSelectBy() getSelectBy()
} }
// //
const getToday = function () { const getToday = function () {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate()
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + 1
)
const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
console.log('getTime', getTime.value) console.log('getTime', getTime.value)
activeTimeRange.value = 'today' // activeTimeRange.value = 'today' //
@ -213,18 +228,9 @@ const getToday = function () {
} }
// //
const getYesterday = function () { const getYesterday = function () {
const yesterday = new Date()
yesterday.setDate(yesterday.getDate() - 1)
const startTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate()
)
const endTime = new Date(
yesterday.getFullYear(),
yesterday.getMonth(),
yesterday.getDate() + 1
)
const today = dayjs()
const startTime = today.subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
console.log('getTime', getTime.value) console.log('getTime', getTime.value)
activeTimeRange.value = 'yesterday' // activeTimeRange.value = 'yesterday' //
@ -233,17 +239,9 @@ const getYesterday = function () {
} }
// 7 // 7
const get7Days = function () { const get7Days = function () {
const today = new Date()
const startTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() - 6
)
const endTime = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + 1
)
const today = dayjs()
const startTime = today.subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
console.log('getTime', getTime.value) console.log('getTime', getTime.value)
activeTimeRange.value = '7days' // activeTimeRange.value = '7days' //
@ -266,24 +264,6 @@ const handleClick = function (tab, event) {
} }
} }
//
const getMarket = async function () {
try {
// POST
const result = await API({ url: '/general/adminMarkets', data: {
account:adminData.value.account,
} })
//
console.log('请求成功', result)
//
market.value = result.data
console.log('地区', market.value)
} catch (error) {
console.log('请求失败', error)
}
}
//
const delObj = ref({}) const delObj = ref({})
const del = function (row) { const del = function (row) {
delObj.value.detailId = row.detailId delObj.value.detailId = row.detailId
@ -379,7 +359,7 @@ const exportExcel = async function () {
refundUser: { refundUser: {
jwcode: refundUser.value.jwcode || '', jwcode: refundUser.value.jwcode || '',
refundModel: refundUser.value.refundModel || '', refundModel: refundUser.value.refundModel || '',
market: refundUser.value.market || '',
market: refundUser.value.market || "",
startTime: refundUser.value.startTime || '', startTime: refundUser.value.startTime || '',
endTime: refundUser.value.endTime || '', endTime: refundUser.value.endTime || '',
goodsName: refundUser.value.goodsName || '', goodsName: refundUser.value.goodsName || '',
@ -474,6 +454,53 @@ const getTagText = (state) => {
return '未知状态'; return '未知状态';
} }
} }
//
const selectedMarketPath = ref("")
const handleMarketChange = (value) => {
if (value && value.length > 0) {
const lastValue = value[value.length - 1]
refundUser.value.market = reverseMarketMapping[lastValue]
} else {
refundUser.value.market = ''
}
}
//
const getMarket = async function () {
try {
// POST
const result = await API({
url: '/market/selectMarket',
});
//
console.log('请求成功', result)
//
const transformTree = (nodes) => {
//
const allChildren = nodes.flatMap(node => node.children || []);
return allChildren.map(child => {
const grandchildren = child.children && child.children.length
? transformTree([child]) //
: null;
return {
value: child.name,
label: child.name,
children: grandchildren
};
});
};
//
market.value = transformTree(result.data)
console.log('转换后的地区树==============', market.value)
} catch (error) {
console.log('请求失败', error)
}
}
</script> </script>
<template> <template>
@ -484,71 +511,29 @@ const getTagText = (state) => {
<el-col :span="5"> <el-col :span="5">
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1">精网号</el-text> <el-text class="mx-1">精网号</el-text>
<el-input
v-model="refundUser.jwcode"
placeholder="请输入精网号"
style="width: 150px"
clearable
/>
<el-input v-model="refundUser.jwcode" placeholder="请输入精网号" style="width: 150px" clearable />
</div> </div>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1">商品名称</el-text> <el-text class="mx-1">商品名称</el-text>
<el-select
v-model="refundUser.goodsName"
placeholder="请选择商品名称"
style="width: 180px"
clearable
>
<el-option
v-for="item in goods"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="refundUser.goodsName" placeholder="请选择商品名称" style="width: 180px" clearable>
<el-option v-for="item in goods" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</div> </div>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<div class="head-card-element">
<el-text class="mx-1" >所属地区</el-text>
<el-select
v-model="refundUser.market"
placeholder="请选择所属地区"
style="width: 180px"
clearable
>
<el-option
v-for="item in market"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</div>
<el-text class="mx-1" size="large">所属地区</el-text>
<el-cascader v-model="selectedMarketPath" :options="market" placeholder="请选择所属地区" clearable
style="width:180px" @change="handleMarketChange" />
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1">退款类型</el-text> <el-text class="mx-1">退款类型</el-text>
<el-select
v-model="refundUser.refundType"
placeholder="请选择退款类型"
style="width: 180px"
clearable
>
<el-select v-model="refundUser.refundType" placeholder="请选择退款类型" style="width: 180px" clearable>
<!-- todo 这需要改--> <!-- todo 这需要改-->
<el-option
v-for="item in refundType"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-option v-for="item in refundType" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</div> </div>
</el-col> </el-col>
@ -557,10 +542,18 @@ const getTagText = (state) => {
<el-col :span="24"> <el-col :span="24">
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1">退款时间</el-text> <el-text class="mx-1">退款时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间" end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange"/>
<el-button @click="getToday()" style="margin-left: 10px" :type="activeTimeRange === 'today' ? 'primary' : ''"> </el-button>
<el-button @click="getYesterday()" style="margin-left: 10px" :type="activeTimeRange === 'yesterday' ? 'primary' : ''"> </el-button>
<el-button @click="get7Days()" style="margin-left: 10px" :type="activeTimeRange === '7days' ? 'primary' : ''"> 近7天</el-button>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange"
:default-time="defaultTime" />
<el-button @click="getToday()" style="margin-left: 10px"
:type="activeTimeRange === 'today' ? 'primary' : ''">
</el-button>
<el-button @click="getYesterday()" style="margin-left: 10px"
:type="activeTimeRange === 'yesterday' ? 'primary' : ''">
</el-button>
<el-button @click="get7Days()" style="margin-left: 10px"
:type="activeTimeRange === '7days' ? 'primary' : ''"> 近7天
</el-button>
<el-button type="success" @click="reset()">重置</el-button> <el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button> <el-button type="primary" @click="search()">查询</el-button>
@ -576,26 +569,15 @@ const getTagText = (state) => {
<el-col> <el-col>
<el-card> <el-card>
<div> <div>
退款金币总数{{ Math.abs(sumGolds) / 100 }}永久金币{{
Math.abs(permanentGolds) / 100
}}免费金币{{ Math.abs(freeGolds) / 100 }}任务金币{{
Math.abs(taskGolds) / 100
}}
退款金币总数{{ format3(Math.abs(sumGolds)) }}&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ format3(Math.abs(permanentGolds) ) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ format3(Math.abs(freeGolds) ) }}&nbsp;&nbsp;&nbsp;&nbsp;
任务金币{{ format3(Math.abs(taskGolds) ) }}
</div> </div>
<!-- 设置表格容器的高度和滚动样式 --> <!-- 设置表格容器的高度和滚动样式 -->
<div style="height: 520px; overflow-y: auto;margin-top:10px"> <div style="height: 520px; overflow-y: auto;margin-top:10px">
<el-table
:data="tableData"
style="width: 100%"
@sort-change="handleSortChange"
height="520px"
>
<el-table-column
type="index"
label="序号"
width="80px"
fixed="left"
>
<el-table :data="tableData" style="width: 100%" @sort-change="handleSortChange" height="520px">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope"> <template #default="scope">
<span>{{ <span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
@ -603,18 +585,8 @@ const getTagText = (state) => {
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
prop="name"
label="姓名"
fixed="left"
width="130px"
/>
<el-table-column
prop="jwcode"
label="精网号"
fixed="left"
width="110px"
/>
<el-table-column prop="name" label="姓名" fixed="left" width="130px" />
<el-table-column prop="jwcode" label="精网号" fixed="left" width="110px" />
<el-table-column prop="market" label="所属地区" width="110px" /> <el-table-column prop="market" label="所属地区" width="110px" />
<el-table-column prop="goodsName" label="商品名称" width="110px" show-overflow-tooltip /> <el-table-column prop="goodsName" label="商品名称" width="110px" show-overflow-tooltip />
<el-table-column prop="refundType" label="退款类型" width="100px" /> <el-table-column prop="refundType" label="退款类型" width="100px" />
@ -627,51 +599,21 @@ const getTagText = (state) => {
</template> </template>
</el-table-column> --> </el-table-column> -->
<el-table-column
prop="sumGold"
label="金额总数"
width="110px"
sortable="custom"
/>
<el-table-column prop="sumGold" label="金额总数" width="110px" sortable="custom" />
<el-table-column prop="refundModel" label="退款方式" width="110px"> <el-table-column prop="refundModel" label="退款方式" width="110px">
<template #default="scope"> <template #default="scope">
{{ scope.row.refundModel === 0 ? '全部退款' : scope.row.refundModel === 1 ? '部分退款' : '' }} {{ scope.row.refundModel === 0 ? '全部退款' : scope.row.refundModel === 1 ? '部分退款' : '' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
prop="permanentGold"
label="永久金币"
width="110px"
sortable="custom"
/>
<el-table-column
prop="freeGold"
sortable="custom"
label="免费金币"
width="110px"
/>
<el-table-column
prop="taskGold"
sortable="custom"
label="任务金币"
width="110px"
/>
<el-table-column prop="permanentGold" label="永久金币" width="110px" sortable="custom" />
<el-table-column prop="freeGold" sortable="custom" label="免费金币" width="110px" />
<el-table-column prop="taskGold" sortable="custom" label="任务金币" width="110px" />
<!-- 修改prop为taskGold --> <!-- 修改prop为taskGold -->
<el-table-column
prop="remark"
label="退款原因"
width="160px"
show-overflow-tooltip
/>
<el-table-column prop="remark" label="退款原因" width="160px" show-overflow-tooltip />
<el-table-column prop="adminName" label="提交人" width="100px" /> <el-table-column prop="adminName" label="提交人" width="100px" />
<el-table-column
prop="createTime"
sortable="custom"
label="提交时间"
width="180px"
>
<el-table-column prop="createTime" sortable="custom" label="提交时间" width="180px">
<template #default="scope"> <template #default="scope">
{{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }} {{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template> </template>
@ -681,16 +623,9 @@ const getTagText = (state) => {
<!-- 分页 --> <!-- 分页 -->
<div class="pagination" style="margin-top: 20px"> <div class="pagination" style="margin-top: 20px">
<el-pagination
background
:page-size="getObj.pageSize"
:page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"
@jump="checkPageNumber"
></el-pagination>
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange" @jump="checkPageNumber"></el-pagination>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
@ -698,12 +633,11 @@ const getTagText = (state) => {
<!-- 导出弹窗 --> <!-- 导出弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%"> <el-dialog v-model="exportListVisible" title="导出列表" width="80%">
<el-table :data="exportList" style="width: 100%" :loading="exportListLoading">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" label="文件名" /> <el-table-column prop="fileName" label="文件名" />
<el-table-column prop="state" label="状态"> <el-table-column prop="state" label="状态">
<template #default="scope"> <template #default="scope">
<el-tag :type="getTagType(scope.row.state)"
:effect="scope.row.state === 3 ? 'light' : 'plain'">
<el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
{{ getTagText(scope.row.state) }} {{ getTagText(scope.row.state) }}
</el-tag> </el-tag>
</template> </template>

101
src/views/usergold/clientCount.vue

@ -1,101 +0,0 @@
<template>
<div>
<!-- 这里放置标签切换的按钮 -->
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
<el-button
:type="activeTab === 'detail' ? 'primary' : 'default'"
@click="goToAdd"
>
金币明细
</el-button>
<el-button
:type="activeTab === 'balance' ? 'primary' : 'default'"
@click="goToDetail"
>
金币余额
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
<router-view></router-view>
</div>
</template>
<script setup>
import {onMounted, ref, watch} from 'vue';
import { useRouter, useRoute } from 'vue-router';
import {useAdminStore} from "@/store/index.js";
import {storeToRefs} from "pinia";
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const router = useRouter();//
const route = useRoute();//
// activeTab
const activeTab = ref(route.name === 'clientCountBalance' ? 'balance' : 'detail');
const goToAdd = () => {
// activeTab add
activeTab.value = 'detail';
router.push({ name: 'clientCountDetail' });
};
const goToDetail = () => {
// activeTab detail
activeTab.value = 'balance';
router.push({ name: 'clientCountBalance' });
};
//
const navigateTo = (name) => {
activeTab.value = name;
if (name === 'detail') {
router.push({name: 'clientCountDetail'});
} else if (name === 'balance') {
router.push({name: 'clientCountBalance'});
}
};
// menuName
const hasMenuPermission = (tree, targetName) => {
for (const node of tree) {
if (node.menuName === targetName) return true;
if (node.children && hasMenuPermission(node.children, targetName)) return true;
}
return false;
};
//
const getDefaultRoute = () => {
if (!menuTree.value) return 'detail';
const hasRecharge = hasMenuPermission(menuTree.value, '查看金币明细');
return hasRecharge ? 'detail' : 'balance';
};
//
watch(() => route.name, (newName) => {
if (newName === 'detail' || newName === 'balance') {
activeTab.value = newName;
} else if (newName === 'usergold') {
const defaultRoute = getDefaultRoute();
navigateTo(defaultRoute);
}
});
//
onMounted(() => {
if (route.name === 'usergold') {
const defaultRoute = getDefaultRoute();
navigateTo(defaultRoute);
} else {
//
if (route.name === 'detail' || route.name === 'balance') {
activeTab.value = route.name;
}
}
});
</script>

89
src/views/usergold/gold/clientCount.vue

@ -0,0 +1,89 @@
<template>
<div>
<!-- 这里放置标签切换的按钮 -->
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
<el-button
:type="activeTab === 'clientCountDetail' ? 'primary' : 'default'"
@click="navigateTo('clientCountDetail')"
:disabled="!hasDetail"
>
金币明细
</el-button>
<el-button
:type="activeTab === 'clientCountBalance' ? 'primary' : 'default'"
@click="navigateTo('clientCountBalance')"
:disabled="!hasBalance"
>
金币余额
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
<router-view></router-view>
</div>
</template>
<script setup>
import {onMounted, ref, watch} from 'vue';
import {useRoute, useRouter} from 'vue-router';
import {storeToRefs} from "pinia";
import {useAdminStore} from "@/store/index.js";
import {hasMenuPermission, permissionMapping} from "@/utils/menuTreePermission.js";
const router = useRouter();
const route = useRoute();
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const activeTab = ref('');
const hasDetail = ref(false);
const hasBalance = ref(false);
//
const navigateTo = (name) => {
activeTab.value = name;
router.push({name});
};
//
const initPermissions = () => {
if (!menuTree.value || !menuTree.value.length) return;
hasDetail.value = hasMenuPermission(menuTree.value, permissionMapping.View_Gold_Coin_Details);
hasBalance.value = hasMenuPermission(menuTree.value, permissionMapping.View_Gold_Coin_Balance);
};
//
const getDefaultAuditRoute = () => {
initPermissions();
if (hasDetail.value) return 'clientCountDetail';
if (hasBalance.value) return 'clientCountBalance';
return 'clientCountDetail';
};
//
watch(() => route.name, (newName) => {
initPermissions()
if (newName === 'clientCountDetail' || newName === 'clientCountBalance') {
activeTab.value = newName;
} else if (newName === 'usergold') {
// 访 /coinConsume
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
}
});
//
onMounted(() => {
initPermissions()
if (route.name === 'usergold') {
const defaultRoute = getDefaultAuditRoute();
navigateTo(defaultRoute);
} else {
//
if (route.name === 'clientCountDetail' || route.name === 'clientCountBalance') {
activeTab.value = route.name;
}
}
});
</script>

205
src/views/usergold/clientCountBalance.vue → src/views/usergold/gold/clientCountBalance.vue

@ -1,12 +1,10 @@
<script setup> <script setup>
// //
import {ref, onMounted, reactive, computed, watch} from 'vue'
import ElementPlus from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import axios from 'axios'
import {onMounted, ref} from 'vue'
import {ElMessage} from 'element-plus'
import moment from 'moment' import moment from 'moment'
import { ta } from 'element-plus/es/locales.mjs'
import API from '@/util/http'
import API from '@/util/http.js'
import {reverseMarketMapping} from "@/utils/marketMap.js";
// //
// //
@ -25,32 +23,7 @@ const getAdminData = async function () {
// //
const isLoadingmarket = ref(false); const isLoadingmarket = ref(false);
const market = ref([])
const getmarket = async () => {
isLoadingmarket.value = true;
try {
const result = await API({
url: '/general/adminMarkets',
data: {account: adminData.value.account}
});
console.log('获取地区数据成功', result)
// { value, label }
if (Array.isArray(result.data) && typeof result.data[0] === 'string') {
market.value = result.data
.filter(item => item !== null && item !== undefined && item.trim() !== '') //
.map(item => ({ value: item, label: item }));
} else {
market.value = result.data;
}
} catch (error) {
console.error('获取地区数据失败:', error);
ElMessage.error('获取地区数据失败,请稍后重试');
//
market.value = [];
} finally {
isLoadingmarket.value = false;
}
};
const markets = ref([])
// //
@ -78,7 +51,9 @@ const total = ref(100)
// //
const getTime = ref([]) const getTime = ref([])
// User // User
const user = ref({})
const user = ref({
markets: [],
})
// //
const getAllObj = ref({}) const getAllObj = ref({})
// //
@ -111,7 +86,21 @@ const get = async function (val) {
console.log('最终请求参数', JSON.stringify(requestData, null, 2)); // console.log('最终请求参数', JSON.stringify(requestData, null, 2)); //
//console.log('', requestData); //console.log('', requestData);
// markets''''
if (user.value.markets.includes('9') || user.value.markets.includes('9999')) {
user.value.markets = [];
}
if (user.value.jwcode) {
//
const numberRegex = /^\d{1,9}$/;
//
if (!numberRegex.test(user.value.jwcode)) {
ElMessage.error('请检查精网号格式')
//
return
}
}
const result = await API({ const result = await API({
url: '/goldDetail/getGold', url: '/goldDetail/getGold',
method: 'post', method: 'post',
@ -148,7 +137,20 @@ const get = async function (val) {
// 0 // 0
total.value = 0 total.value = 0
// ElMessage.warning('') // ElMessage.warning('')
} else {
}
// , result.data.list
else if(resultGoldTotal.data===0){
//
tableData.value = []
// 0
permanentGold.value = 0
freeJuneGold.value = 0
freeDecemberGold.value = 0
taskGold.value = 0
goldtotal.value = 0
freeGold.value = 0
}
else {
// //
console.log('总数据请求成功', result) console.log('总数据请求成功', result)
// //
@ -189,10 +191,14 @@ const search = function () {
} }
// //
const reset = function () { const reset = function () {
user.value = {}
user.value = {
jwcode: '',
markets: [],
}
sortField.value = '' sortField.value = ''
sortOrder.value = '' sortOrder.value = ''
get() get()
selectedMarketPath.value = []
} }
const cellClick = function (row, column) { const cellClick = function (row, column) {
console.log('cellClick', column.label) console.log('cellClick', column.label)
@ -223,7 +229,7 @@ const handleSortChange = (column) => {
onMounted(async function () { onMounted(async function () {
await getAdminData() await getAdminData()
await get() await get()
await getmarket()
await getMarket()
await getExportList() await getExportList()
}) })
const handlePageSizeChange = function (val) { const handlePageSizeChange = function (val) {
@ -320,24 +326,60 @@ const getTagText = (state) => {
return '未知状态'; return '未知状态';
} }
} }
const handleMarketChange = (val) => {
if (!Array.isArray(val)) return
const hasHeadquarters = val.includes('总部')
const hasOther = val.some(item => item !== '总部')
//
const selectedMarketPath = ref([])
if (hasHeadquarters && hasOther) {
if (val[val.length - 1] === '总部') {
//
user.value.markets = ['总部']
ElMessage.warning('“总部”与其他地区不可同时选择,系统已为您保留“总部”')
const handleMarketChange = (value) => {
if (value && value.length > 0) {
const lastValue = value[value.length - 1];
//
const marketValue = reverseMarketMapping[lastValue];
user.value.markets = Array.isArray(marketValue) ? marketValue : [marketValue];
} else { } else {
//
user.value.markets = val.filter(item => item !== '总部')
ElMessage.warning('“总部”与其他地区不可同时选择,系统已为您去除“总部”')
// []
user.value.markets = [];
}
};
//
const getMarket = async function () {
try {
// POST
const result = await API({
url: '/market/selectMarket',
});
//
console.log('请求成功', result)
//
const transformTree = (nodes) => {
//
const allChildren = nodes.flatMap(node => node.children || []);
return allChildren.map(child => {
const grandchildren = child.children && child.children.length
? transformTree([child]) //
: null;
return {
value: child.name,
label: child.name,
children: grandchildren
};
});
};
//
markets.value = transformTree(result.data)
console.log('转换后的地区树==============', markets.value)
} catch (error) {
console.log('请求失败', error)
} }
} }
const format3 = (num) => {
//
return num.toLocaleString('en-US')
} }
</script> </script>
<template> <template>
@ -351,9 +393,14 @@ const handleMarketChange = (val) => {
</div> </div>
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1" size="large">所属地区</el-text> <el-text class="mx-1" size="large">所属地区</el-text>
<el-select v-model="user.markets" placeholder="请选择所属地区" style="width: 180px" clearable multiple @change="handleMarketChange">
<el-option v-for="item in market" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
<el-cascader
v-model="selectedMarketPath"
:options="markets"
placeholder="请选择所属地区"
clearable
style="width:180px"
@change="handleMarketChange"
/>
</div> </div>
<el-button type="primary" @click="search()">查询</el-button> <el-button type="primary" @click="search()">查询</el-button>
<el-button @click="reset" type="success">重置</el-button> <el-button @click="reset" type="success">重置</el-button>
@ -369,10 +416,10 @@ const handleMarketChange = (val) => {
<el-col> <el-col>
<el-card> <el-card>
<div> <div>
金币总数{{ (goldtotal || 0) / 100 }}
永久金币{{(permanentGold || 0) / 100 }}
免费金币{{ (freeGold || 0) / 100 }}
任务金币{{ (taskGold || 0) / 100 }}
金币总数{{ format3(goldtotal || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ format3(permanentGold || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ format3(freeGold || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
任务金币{{ format3(taskGold || 0) }}
</div> </div>
<!-- 设置表格容器的高度和滚动样式 --> <!-- 设置表格容器的高度和滚动样式 -->
<div style="height: 626px; overflow-y: auto"> <div style="height: 626px; overflow-y: auto">
@ -394,28 +441,28 @@ const handleMarketChange = (val) => {
((scope.row.currentPermanentGold || 0) + ((scope.row.currentPermanentGold || 0) +
(scope.row.currentFreeJune || 0) + (scope.row.currentFreeJune || 0) +
(scope.row.currentFreeDecember || 0) + (scope.row.currentFreeDecember || 0) +
(scope.row.currentTaskGold || 0) ) /100
(scope.row.currentTaskGold || 0))
}}</span> }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="currentPermanentGold" label="永久金币" sortable="custom" width="110"> <el-table-column prop="currentPermanentGold" label="永久金币" sortable="custom" width="110">
<template #default="scope"> <template #default="scope">
<span>{{ (scope.row.currentPermanentGold || 0) / 100 }}</span>
<span>{{ (scope.row.currentPermanentGold || 0) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="currentFreeJune" label="6月份到期免费金币" sortable="custom" width="110"> <el-table-column prop="currentFreeJune" label="6月份到期免费金币" sortable="custom" width="110">
<template #default="scope"> <template #default="scope">
<span>{{ (scope.row.currentFreeJune || 0) / 100 }}</span>
<span>{{ (scope.row.currentFreeJune || 0) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="currentFreeDecember" label="12月份到期免费金币" sortable="custom" width="110"> <el-table-column prop="currentFreeDecember" label="12月份到期免费金币" sortable="custom" width="110">
<template #default="scope"> <template #default="scope">
<span>{{ (scope.row.currentFreeDecember || 0) / 100 }}</span>
<span>{{ (scope.row.currentFreeDecember || 0) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="currentTaskGold" label="任务金币" sortable="custom" width="130"> <el-table-column prop="currentTaskGold" label="任务金币" sortable="custom" width="130">
<template #default="scope"> <template #default="scope">
<span>{{(scope.row.currentTaskGold || 0) / 100 }}</span>
<span>{{ (scope.row.currentTaskGold || 0) }}</span>
</template> </template>
</el-table-column> </el-table-column>
@ -424,18 +471,18 @@ const handleMarketChange = (val) => {
<el-popover trigger="hover" placement="left" width="150"> <el-popover trigger="hover" placement="left" width="150">
<template #default> <template #default>
<div> <div>
<div>永久金币{{ (scope.row.sumPermanentGold || 0) / 100 }}</div>
<div>免费金币{{( (scope.row.sumFreeJune || 0) + (scope.row.sumFreeDecember || 0)) / 100 }}</div>
<div>任务金币{{ (scope.row.sumTaskGold || 0) / 100 }}</div>
<div>永久金币{{ (scope.row.sumPermanentGold || 0) }}</div>
<div>免费金币{{ ((scope.row.sumFreeJune || 0) + (scope.row.sumFreeDecember || 0)) }}</div>
<div>任务金币{{ (scope.row.sumTaskGold || 0) }}</div>
</div> </div>
</template> </template>
<template #reference> <template #reference>
<span> <span>
{{ {{
(scope.row.sumPermanentGold || 0) / 100 +
(scope.row.sumFreeJune || 0) / 100 +
(scope.row.sumFreeDecember || 0) / 100 +
(scope.row.sumTaskGold || 0) / 100
(scope.row.sumPermanentGold || 0) +
(scope.row.sumFreeJune || 0) +
(scope.row.sumFreeDecember || 0) +
(scope.row.sumTaskGold || 0)
}}</span> }}</span>
</template> </template>
</el-popover> </el-popover>
@ -446,18 +493,21 @@ const handleMarketChange = (val) => {
<el-popover trigger="hover" placement="left" width="150"> <el-popover trigger="hover" placement="left" width="150">
<template #default> <template #default>
<div> <div>
<div>永久金币{{ (scope.row.sumConsumeGold || 0) / 100 }}</div>
<div>免费金币{{ ((scope.row.sumConsumeJune || 0) + (scope.row.sumConsumeDecember || 0)) / 100 }}</div>
<div>任务金币{{ (scope.row.sumConsumeJune || 0) / 100 }}</div>
<div>永久金币{{ (scope.row.sumConsumeGold || 0) }}</div>
<div>免费金币{{
((scope.row.sumConsumeJune || 0) + (scope.row.sumConsumeDecember || 0))
}}
</div>
<div>任务金币{{ (scope.row.sumConsumeJune || 0) }}</div>
</div> </div>
</template> </template>
<template #reference> <template #reference>
<span> <span>
{{ {{
(scope.row.sumConsumeGold || 0) / 100 +
(scope.row.sumConsumeTaskGold || 0) / 100 +
(scope.row.sumConsumeJune || 0) / 100 +
(scope.row.sumConsumeDecember || 0) / 100
(scope.row.sumConsumeGold || 0) +
(scope.row.sumConsumeTaskGold || 0) +
(scope.row.sumConsumeJune || 0) +
(scope.row.sumConsumeDecember || 0)
}}</span> }}</span>
</template> </template>
</el-popover> </el-popover>
@ -470,7 +520,8 @@ const handleMarketChange = (val) => {
<!-- 分页 --> <!-- 分页 -->
<div class="pagination" style="margin-top: 20px"> <div class="pagination" style="margin-top: 20px">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]" <el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination> @current-change="handleCurrentChange"></el-pagination>
</div> </div>
</el-card> </el-card>
@ -478,7 +529,7 @@ const handleMarketChange = (val) => {
</el-row> </el-row>
<el-dialog v-model="exportListVisible" title="导出列表" width="80%"> <el-dialog v-model="exportListVisible" title="导出列表" width="80%">
<el-table :data="exportList" style="width: 100%" :loading="exportListLoading">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" label="文件名"/> <el-table-column prop="fileName" label="文件名"/>
<el-table-column prop="state" label="状态"> <el-table-column prop="state" label="状态">
<template #default="scope"> <template #default="scope">

210
src/views/usergold/clientCountDetail.vue → src/views/usergold/gold/clientCountDetail.vue

@ -1,15 +1,18 @@
<script setup> <script setup>
import {ref, onMounted, computed, nextTick, watch} from 'vue'
import {onMounted, ref} from 'vue'
import {ElMessage} from 'element-plus' import {ElMessage} from 'element-plus'
import axios from 'axios'
import moment from 'moment' import moment from 'moment'
import API from '@/util/http'
import {writeFile, utils} from 'xlsx'
import request from "@/util/request.js";
import API from '@/util/http.js'
import {reverseMarketMapping} from "../../../utils/marketMap.js";
import dayjs from "dayjs";
// //
const activeTimeRange = ref('') const activeTimeRange = ref('')
const defaultTime = [
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]
// //
const handleDatePickerChange = () => { const handleDatePickerChange = () => {
activeTimeRange.value = '' activeTimeRange.value = ''
@ -89,49 +92,9 @@ const type = [
// //
// //
const isLoadingmarket = ref(false); const isLoadingmarket = ref(false);
const market = ref([])
const markets = ref([])
const getmarket = async () => {
isLoadingmarket.value = true;
try {
const result = await API({
url: '/general/adminMarkets',
data: {account: adminData.value.account}
});
console.log('获取地区数据成功', result)
// { value, label }
if (Array.isArray(result.data) && typeof result.data[0] === 'string') {
market.value = result.data
.filter(item => item !== null && item !== undefined && item.trim() !== '') //
.map(item => ({ value: item, label: item }));
} else {
market.value = result.data;
}
} catch (error) {
console.error('获取地区数据失败:', error);
ElMessage.error('获取地区数据失败,请稍后重试');
//
market.value = [];
} finally {
isLoadingmarket.value = false;
}
};
/*// 地区下拉框
const getMarket = async function () {
try {
const result = await API({
url: '/general/market',
data: {}
})
console.log('@@@@@@@@@@@@@地区数据', result)
market.value = result.data
console.log('@@@@@@@@@@@@@@@@地区', market.value)
} catch (error) {
console.log('请求失败', error)
}
}*/
// //
const tableData = ref([]) const tableData = ref([])
// //
@ -145,7 +108,9 @@ const total = ref(100)
// //
const getTime = ref([]) const getTime = ref([])
// goldDetail // goldDetail
const goldDetail = ref({})
const goldDetail = ref({
markets: [],
})
// //
const getObj = ref({ const getObj = ref({
pageNum: 1, pageNum: 1,
@ -181,6 +146,23 @@ const get = async function (val) {
const requestData = {...getObj.value, goldDetail: {...goldDetail.value}}; const requestData = {...getObj.value, goldDetail: {...goldDetail.value}};
console.log('最终请求参数', JSON.stringify(requestData, null, 2)); // console.log('最终请求参数', JSON.stringify(requestData, null, 2)); //
// markets''''
console.log('goldDetail.value.markets:', goldDetail.value.markets)
if (goldDetail.value.markets.includes('9') || goldDetail.value.markets.includes('9999')) {
goldDetail.value.markets = [];
}
if (goldDetail.value.jwcode) {
//
const numberRegex = /^\d{1,9}$/;
//
if (!numberRegex.test(goldDetail.value.jwcode)) {
ElMessage.error('请检查精网号格式')
//
return
}
}
const result = await API({ const result = await API({
url: '/goldDetail/getGoldDetail', url: '/goldDetail/getGoldDetail',
method: 'post', method: 'post',
@ -221,6 +203,13 @@ const get = async function (val) {
totalFreeGold.value = data.freeGolds totalFreeGold.value = data.freeGolds
totalTaskGold.value = data.taskGolds totalTaskGold.value = data.taskGolds
totalGoldTotal.value = data.sumGolds totalGoldTotal.value = data.sumGolds
} else if (totalResult.code === 0) {
//
tableData.value = []
totalPermanentGold.value = 0
totalFreeGold.value = 0
totalTaskGold.value = 0
totalGoldTotal.value = 0
} else { } else {
ElMessage.error('获取合计数据失败') ElMessage.error('获取合计数据失败')
} }
@ -234,15 +223,12 @@ const get = async function (val) {
// //
const reset = function () { const reset = function () {
delete goldDetail.value.jwcode
delete goldDetail.value.type
delete goldDetail.value.startTime
delete goldDetail.value.endTime
delete goldDetail.value.markets
goldDetail.value = {}
goldDetail.value.markets = []
selectedMarketPath.value = []
delete sortField.value delete sortField.value
delete sortOrder.value delete sortOrder.value
getTime.value = [] getTime.value = []
delete goldDetail.value.payPlatform
activeTimeRange.value = '' // activeTimeRange.value = '' //
search() search()
@ -258,9 +244,9 @@ const search = function () {
// //
const getToday = function () { const getToday = function () {
const today = moment()
const startTime = today.startOf('day').toDate()
const endTime = today.add(1, 'days').startOf('day').toDate()
const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
activeTimeRange.value = 'today' // activeTimeRange.value = 'today' //
@ -269,10 +255,9 @@ const getToday = function () {
// //
const getYesterday = function () { const getYesterday = function () {
const today = moment()
const yesterday = moment().subtract(1, 'day')
const startTime = yesterday.startOf('day').toDate()
const endTime = today.startOf('day').toDate()
const today = dayjs()
const startTime = today.subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
activeTimeRange.value = 'yesterday' // activeTimeRange.value = 'yesterday' //
@ -281,8 +266,9 @@ const getYesterday = function () {
// 7 // 7
const get7Days = function () { const get7Days = function () {
const startTime = moment().subtract(6, 'day').startOf('day').toDate()
const endTime = moment().add(1, 'days').startOf('day').toDate()
const today = dayjs()
const startTime = today.subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
getTime.value = [startTime, endTime] getTime.value = [startTime, endTime]
activeTimeRange.value = '7days' // activeTimeRange.value = '7days' //
@ -367,7 +353,7 @@ const handleCurrentChange = function (val) {
onMounted(async function () { onMounted(async function () {
await getAdminData() await getAdminData()
await get() await get()
await getmarket()
await getMarket()
await getPlatform() // await getPlatform() //
}) })
@ -445,23 +431,60 @@ const getTagText = (state) => {
return '未知状态'; return '未知状态';
} }
} }
const handleMarketChange = (val) => {
if (!Array.isArray(val)) return
const hasHeadquarters = val.includes('总部')
const hasOther = val.some(item => item !== '总部')
if (hasHeadquarters && hasOther) {
if (val[val.length - 1] === '总部') {
//
goldDetail.value.markets = ['总部']
ElMessage.warning('“总部”与其他地区不可同时选择,系统已为您保留“总部”')
//
const selectedMarketPath = ref([])
const handleMarketChange = (value) => {
if (value && value.length > 0) {
const lastValue = value[value.length - 1];
//
const marketValue = reverseMarketMapping[lastValue];
goldDetail.value.markets = Array.isArray(marketValue) ? marketValue : [marketValue];
} else { } else {
//
goldDetail.value.markets = val.filter(item => item !== '总部')
ElMessage.warning('“总部”与其他地区不可同时选择,系统已为您去除“总部”')
// [""]
goldDetail.value.markets = [];
}
};
//
const getMarket = async function () {
try {
// POST
const result = await API({
url: '/market/selectMarket',
});
//
console.log('请求成功', result)
//
const transformTree = (nodes) => {
//
const allChildren = nodes.flatMap(node => node.children || []);
return allChildren.map(child => {
const grandchildren = child.children && child.children.length
? transformTree([child]) //
: null;
return {
value: child.name,
label: child.name,
children: grandchildren
};
});
};
//
markets.value = transformTree(result.data)
console.log('转换后的地区树==============', markets.value)
} catch (error) {
console.log('请求失败', error)
} }
} }
const format3 = (num) => {
//
return num.toLocaleString('en-US')
} }
</script> </script>
<template> <template>
@ -497,16 +520,14 @@ const handleMarketChange = (val) => {
<el-col :span="6"> <el-col :span="6">
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1" size="large">所属地区</el-text> <el-text class="mx-1" size="large">所属地区</el-text>
<el-select v-model="goldDetail.markets" placeholder="请选择所属地区" style="width: 180px" clearable
multiple
:loading="isLoadingArea" @change="handleMarketChange">
<el-option v-for="item in market"
:key="item.value || item"
:label="item.label || item"
:value="item.value || item"
<el-cascader
v-model="selectedMarketPath"
:options="markets"
placeholder="请选择所属地区"
clearable
style="width:180px"
@change="handleMarketChange"
/> />
</el-select>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
@ -514,7 +535,8 @@ const handleMarketChange = (val) => {
<div class="head-card-element"> <div class="head-card-element">
<el-text class="mx-1" size="large">更新时间</el-text> <el-text class="mx-1" size="large">更新时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间" <el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange"/>
end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange"
:default-time="defaultTime"/>
<el-button @click="getToday()" style="margin-left: 10px" <el-button @click="getToday()" style="margin-left: 10px"
:type="activeTimeRange === 'today' ? 'primary' : ''"> :type="activeTimeRange === 'today' ? 'primary' : ''">
</el-button> </el-button>
@ -537,10 +559,10 @@ const handleMarketChange = (val) => {
<el-col> <el-col>
<el-card> <el-card>
<div> <div>
金币总数{{ (totalGoldTotal || 0) / 100 }}
永久金币{{ (totalPermanentGold || 0) / 100 }}
免费金币{{ (totalFreeGold || 0) / 100 }}
任务金币{{ (totalTaskGold || 0) / 100 }}
金币总数{{ format3(totalGoldTotal || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ format3(totalPermanentGold || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ format3(totalFreeGold || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
任务金币{{ format3(totalTaskGold || 0) }}
</div> </div>
<div style="height: 584px; overflow-y: auto"> <div style="height: 584px; overflow-y: auto">
<el-table :data="tableData" style="width: 100%" @sort-change="handleSortChange" height="584px"> <el-table :data="tableData" style="width: 100%" @sort-change="handleSortChange" height="584px">
@ -577,23 +599,23 @@ const handleMarketChange = (val) => {
: scope.row.sumGold / 100 : scope.row.sumGold / 100
}} }}
</span> --> </span> -->
<span>{{ (scope.row.sumGold || 0) / 100 }}</span>
<span>{{ (scope.row.sumGold || 0) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="permanentGold" sortable="custom" label="永久金币" width="110"> <el-table-column prop="permanentGold" sortable="custom" label="永久金币" width="110">
<template #default="scope"> <template #default="scope">
<span>{{ (scope.row.permanentGold || 0) / 100 }}</span>
<span>{{ (scope.row.permanentGold || 0) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="freeGold" sortable="custom" label="免费金币" width="110"> <el-table-column prop="freeGold" sortable="custom" label="免费金币" width="110">
<template #default="scope"> <template #default="scope">
<span>{{ (calculateFreeGold(scope.row) || 0) / 100 }}</span>
<span>{{ (calculateFreeGold(scope.row) || 0) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="taskGold" sortable="custom" label="任务金币" width="110"> <el-table-column prop="taskGold" sortable="custom" label="任务金币" width="110">
<template #default="scope"> <template #default="scope">
<span>{{ (scope.row.taskGold || 0) / 100 }}</span>
<span>{{ (scope.row.taskGold || 0) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="adminName" label="提交人" width="110"/> <el-table-column prop="adminName" label="提交人" width="110"/>
@ -620,7 +642,7 @@ const handleMarketChange = (val) => {
<!-- 导出列表弹窗 --> <!-- 导出列表弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%"> <el-dialog v-model="exportListVisible" title="导出列表" width="80%">
<el-table :data="exportList" style="width: 100%" :loading="exportListLoading">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" label="文件名"/> <el-table-column prop="fileName" label="文件名"/>
<el-table-column prop="state" label="状态"> <el-table-column prop="state" label="状态">
<template #default="scope"> <template #default="scope">

199
src/views/usergold/userbean.vue

@ -0,0 +1,199 @@
<template>
<el-card style="margin-bottom: 20px;">
<el-text size="large">精网号</el-text>
<el-input v-model="searchObj.jwcode" placeholder="请输入精网号" style="width: 240px" clearable />
<el-text size="large" style="margin-left:20px">地区</el-text>
<el-select v-model="searchObj.dept" placeholder="请选择地区" style="width: 240px" clearable>
<el-option v-for="item in marketOptions" :key="item" :label="item" :value="item" />
</el-select>
<div style="float: right;">
<el-button type="success" @click="reset">重置</el-button>
<el-button type="primary" @click="get">查询</el-button>
</div>
</el-card>
<el-card>
<div class="stats-card">
现有金豆数{{ format3(stats.sumBean) }}金豆&nbsp;&nbsp;&nbsp;&nbsp;
付费金豆数{{ format3(stats.permanentBean) }}金豆&nbsp;&nbsp;&nbsp;&nbsp;
免费金豆数{{ format3(stats.freeBean) }}金豆&nbsp;&nbsp;&nbsp;&nbsp;
消费金豆总数{{ format3(stats.consumeSum) }}金豆&nbsp;&nbsp;&nbsp;&nbsp;
</div>
<el-table :data="tableData" height="650px" @sort-change="handleSortChange" :row-style="{ height: '60px' }">
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (pagination.pageNum - 1) * pagination.pageSize
}}</span>
</template>
</el-table-column>
<el-table-column label="姓名" style="width: 120px;" prop="name" show-overflow-tooltip />
<el-table-column label="精网号" style="width: 120px;" prop="jwcode" />
<el-table-column label="所属地区" style="width: 120px;" prop="dept" />
<el-table-column label="现有金豆" style="width: 120px;" prop="beanNum" sortable="custom" />
<el-table-column label="免费金豆" style="width: 120px;" prop="freeBean" sortable="custom" />
<el-table-column label="付费金豆" style="width: 120px;" prop="buyBean" sortable="custom" />
<el-table-column label="历史消费" style="width: 120px;" prop="totalCostBean" sortable="custom" />
</el-table>
<el-pagination class="pagination" v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize" layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</el-card>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import API from '@/util/http'
import { useAdminStore } from "@/store/index.js";
import { storeToRefs } from "pinia";
const adminStore = useAdminStore();
const { adminData, menuTree } = storeToRefs(adminStore);
import { permissionMapping, findMenuById } from "@/utils/menuTreePermission.js"
import { ElMessage } from 'element-plus';
const tableData = ref([])
const marketOptions = ref([])
const searchObj = ref({
jwcode: '',
dept: '',
sortField: '',
sortOrder: ''
})
const stats = ref({
sumBean: 0,
permanentBean: 0,
freeBean: 0,
consumeSum: 0
})
const pagination = ref({
pageNum: 1,
pageSize: 50,
total: 0
})
const get = async function () {
if(findMenuById(menuTree.value, permissionMapping.View_Golden_Bean_Balance)){
try {
trim()
if(searchObj.value.jwcode){
const numRef = /^\d{1,9}$/;
if(!numRef.test(searchObj.value.jwcode)){
ElMessage.error('请检查精网号格式')
return
}
}
const params = {
beanUser: { //
jwcode: searchObj.value.jwcode, //
dept: searchObj.value.dept, //
sortField: searchObj.value.sortField,//
sortOrder: searchObj.value.sortOrder//
},
pageNum: pagination.value.pageNum,
pageSize: pagination.value.pageSize
}
const res = await API({
url: '/beanUser/userBean',
data: params
})
if (res.code === 200) {
tableData.value = res.data.list
pagination.value.total = res.data.total
}
} catch (error) {
console.log(error)
}
}else{
ElMessage.error('无此权限')
}
}
const getStats = async () => {
if (findMenuById(menuTree.value, permissionMapping.View_Golden_Bean_Balance)) {
try {
const params = {
beanUser:{
jwcode: searchObj.value.jwcode,
dept: searchObj.value.dept
}
}
const res = await API({
url: '/beanUser/userBeanSum',
data: params
})
stats.value.sumBean = res.data.sumBean
stats.value.permanentBean = res.data.permanentBean
stats.value.freeBean = res.data.freeBean
stats.value.consumeSum = res.data.consumeSum
console.log('see see stats和搜索对象', stats.value, params)
} catch (error) {
console.log('请求失败', error)
}
}else{
ElMessage.error('无此权限')
}
}
const handleSortChange = (column) => {
if (column.prop === 'beanNum') {
searchObj.value.sortField = 'jinbi'
} else if (column.prop === 'freeBean') {
searchObj.value.sortField = 'jinbi_free'
} else if (column.prop === 'buyBean') {
searchObj.value.sortField = 'jinbi_buy'
} else if (column.prop === 'totalCostBean') {
searchObj.value.sortField = 'jinbi_cost_total'
} else {
searchObj.value.sortField = ''
}
searchObj.value.sortOrder = column.order === 'ascending' ? 'asc' : 'desc'
console.log('排序字段:', searchObj.value.sortField,'排序方式:', searchObj.value.sortOrder)
get()
}
const getmarkets = async () => {
try {
const result = await API({ url: '/beanUser/getDept', data: {} })
marketOptions.value = result.data || []
} catch (error) {
console.error('获取地区列表失败', error)
}
}
//
const trim = () => {
if (searchObj.value.jwcode) {
searchObj.value.jwcode = searchObj.value.jwcode.replace(/\s/g, '');
}
}
const reset = function () {
searchObj.value.jwcode = ''
searchObj.value.dept = ''
get()
}
const handlePageSizeChange = function (val) {
pagination.value.pageSize = val
get()
}
const handleCurrentChange = function (val) {
pagination.value.pageNum = val
get()
}
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
onMounted(() => {
get()
getmarkets()
getStats()
console.log('页面接收到的adminData:', adminData.value)
})
</script>
<style scoped>
/* .stats-card {
background-color: #EBEEF5;
padding: 5px;
} */
</style>

117
src/views/workspace/index.vue

@ -1,17 +1,15 @@
<template> <template>
<el-row> <el-row>
<!-- 数据总览卡片 --> <!-- 数据总览卡片 -->
<el-col :span="4" style="padding-right: 10px;"> <!-- 适当留白避免拥挤 -->
<el-col :span="4" style="padding-right: 10px;">
<el-card class="center-card margin-bottom">数据总览</el-card> <el-card class="center-card margin-bottom">数据总览</el-card>
</el-col> </el-col>
<!-- 最后更新时间 --> <!-- 最后更新时间 -->
<el-col :span="18" style="display: flex; align-items: center; font-size: 18px"> <el-col :span="18" style="display: flex; align-items: center; font-size: 18px">
最后更新时间{{ workDataUpdateTime && workDataUpdateTime !== '1970-01-01 08:00:00' ? workDataUpdateTime : '该地区暂无数据' }}
最后更新时间{{
workDataUpdateTime && workDataUpdateTime !== '1970-01-01 08:00:00' ? workDataUpdateTime : '该地区暂无数据'
}}
</el-col> </el-col>
<!-- 剩余栅格空间可选用于占满一行 -->
<el-col :span="18"></el-col>
</el-row> </el-row>
<el-row :gutter="10"> <el-row :gutter="10">
@ -21,8 +19,8 @@
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<div class="card-title">当前金币余量</div> <div class="card-title">当前金币余量</div>
<div>{{ currentGold / 100 }}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;较前一日 {{
dailyChange / 100 }}
<div>{{ currentGold / 100 }}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;较前一日
{{ dailyChange / 100 }}
<template v-if="dailyChange > 0"> <template v-if="dailyChange > 0">
<el-icon style="color:red"> <el-icon style="color:red">
<ArrowUpBold /> <ArrowUpBold />
@ -44,9 +42,9 @@
<div> <div>
<div class="margin-bottom">永久金币{{ currentPermanent / 100 }}</div> <div class="margin-bottom">永久金币{{ currentPermanent / 100 }}</div>
<div class="margin-bottom">免费金币{{ currentFree / 100 }}</div> <div class="margin-bottom">免费金币{{ currentFree / 100 }}</div>
<div class="margin-bottom">[六月到期|{{ currentFreeJune / 100 }}]&nbsp;&nbsp;&nbsp;&nbsp;[12月到期|{{
currentFreeDecember /
100 }}]</div>
<div class="margin-bottom">[六月到期|{{ currentFreeJune / 100 }}]&nbsp;&nbsp;&nbsp;&nbsp;
[十二月到期|{{ currentFreeDecember / 100 }}]
</div>
<div>任务金币{{ currentTask / 100 }}</div> <div>任务金币{{ currentTask / 100 }}</div>
</div> </div>
</el-card> </el-card>
@ -130,47 +128,42 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="10" style="margin-top: 20px">
<el-col :span="24">
<el-card style="width: 100%">
<el-row style="margin-top: 20px">
<el-card>
<el-row> <el-row>
<el-col :span="21">
<el-col>
<el-tabs v-model="activeTab" @tab-change="handleTabChange"> <el-tabs v-model="activeTab" @tab-change="handleTabChange">
<el-tab-pane label="金币充值" name="recharge"></el-tab-pane> <el-tab-pane label="金币充值" name="recharge"></el-tab-pane>
<el-tab-pane label="金币消费" name="consume"></el-tab-pane> <el-tab-pane label="金币消费" name="consume"></el-tab-pane>
</el-tabs> </el-tabs>
</el-col> </el-col>
<el-col :span="24">
<el-row>
<div style="margin-top:5px">合计&nbsp;&nbsp;&nbsp;&nbsp;
永久金币 {{ activeTab === 'recharge' ? sumRechargePermanent / 100 : sumConsumePermanent / 100
}}&nbsp;&nbsp;&nbsp;&nbsp;
免费金币 {{ activeTab === 'recharge' ? sumRechargeFree / 100 : sumConsumeFree / 100
}}&nbsp;&nbsp;&nbsp;&nbsp;
任务金币 {{ activeTab === 'recharge' ? sumRechargeTask / 100 : sumConsumeTask / 100
}}&nbsp;&nbsp;&nbsp;&nbsp;
<div style="margin-top:5px;width:40vw">合计&nbsp;
永久金币 {{ activeTab === 'recharge' ? sumRechargePermanent / 100 : sumConsumePermanent / 100 }}&nbsp;&nbsp;
免费金币 {{ activeTab === 'recharge' ? sumRechargeFree / 100 : sumConsumeFree / 100 }}&nbsp;&nbsp;
任务金币 {{ activeTab === 'recharge' ? sumRechargeTask / 100 : sumConsumeTask / 100 }}
</div> </div>
<div @change="handleDatePickerChange">
<el-button @click="getToday()" label="day" style="margin-left:250px" :type="activeTimeRange === 'today' ? 'primary' : ''">今日</el-button>
<el-button @click="getWeek()" label="week" :type="activeTimeRange === 'week' ? 'primary' : ''">本周</el-button>
<el-button @click="getMonth()" label="month" :type="activeTimeRange === 'month' ? 'primary' : ''">本月</el-button>
<el-button @click="getYear()" label="year" :type="activeTimeRange === 'year' ? 'primary' : ''">本年</el-button>
<div @change="handleDatePickerChange" style="width:15vw">
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''">今日
</el-button>
<el-button @click="getWeek()" :type="activeTimeRange === 'week' ? 'primary' : ''">本周
</el-button>
<el-button @click="getMonth()" :type="activeTimeRange === 'month' ? 'primary' : ''">本月
</el-button>
<el-button @click="getYear()" :type="activeTimeRange === 'year' ? 'primary' : ''">本年
</el-button>
</div> </div>
<div style="width:25vw">
<el-date-picker v-model="dateRange" type="datetimerange" range-separator="" start-placeholder="开始时间" <el-date-picker v-model="dateRange" type="datetimerange" range-separator="" start-placeholder="开始时间"
end-placeholder="结束时间" style="margin-left:10px" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" />
end-placeholder="结束时间" format="YYYY-MM-DD HH:mm:ss" style="width:20vw"
value-format="YYYY-MM-DD HH:mm:ss" />
<el-button type="primary" style="margin-left: 5px" @click="getChartData">查询</el-button> <el-button type="primary" style="margin-left: 5px" @click="getChartData">查询</el-button>
</el-row>
</el-col>
</div>
</el-row> </el-row>
<el-row :gutter="20" style="margin-top: 20px"> <el-row :gutter="20" style="margin-top: 20px">
<el-col :span="18"> <el-col :span="18">
<div class="bar"> <div class="bar">
<!-- <div v-if="chartLoading" class="loading-overlay">-->
<!-- <div class="loading-spinner"></div>-->
<!-- </div>-->
<div ref="chartRef" style="width: 100%; height: 400px"></div> <div ref="chartRef" style="width: 100%; height: 400px"></div>
</div> </div>
</el-col> </el-col>
@ -185,7 +178,11 @@
</el-select> </el-select>
<el-table :data="tableData" height="320px"> <el-table :data="tableData" height="320px">
<el-table-column prop="rank" label="排名" width="60" align="center"></el-table-column> <el-table-column prop="rank" label="排名" width="60" align="center"></el-table-column>
<el-table-column prop="market" label="地区" align="center"></el-table-column>
<el-table-column prop="market" label="地区" align="center">
<template #default="scope">
<span>{{ marketMapping[scope.row.market] || scope.row.market }}</span>
</template>
</el-table-column>
<el-table-column prop="coinAmount" label="金币数量" align="center"> <el-table-column prop="coinAmount" label="金币数量" align="center">
<template #default="{ row }"> <template #default="{ row }">
{{ row.coinAmount.toLocaleString() }} {{ row.coinAmount.toLocaleString() }}
@ -196,7 +193,6 @@
</el-col> </el-col>
</el-row> </el-row>
</el-card> </el-card>
</el-col>
</el-row> </el-row>
</template> </template>
@ -207,8 +203,12 @@ import API from '@/util/http'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import utc from 'dayjs-plugin-utc' import utc from 'dayjs-plugin-utc'
dayjs.extend(utc) dayjs.extend(utc)
import { ArrowUpBold, ArrowDownBold, SemiSelect } from '@element-plus/icons-vue' import { ArrowUpBold, ArrowDownBold, SemiSelect } from '@element-plus/icons-vue'
import { marketMapping } from "@/utils/marketMap.js";
// //
const markets = ref([]) const markets = ref([])
// //
@ -297,7 +297,8 @@ const formatDate = function(date) {
const getToday = function () { const getToday = function () {
const today = dayjs() const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss') const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.add(1,'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
// const endTime = today.add(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime] dateRange.value = [startTime, endTime]
console.log('看看dateRange', dateRange.value) console.log('看看dateRange', dateRange.value)
activeTimeRange.value = 'today' // activeTimeRange.value = 'today' //
@ -308,7 +309,8 @@ const getToday = function () {
const getWeek = function () { const getWeek = function () {
const today = dayjs() const today = dayjs()
const startTime = (today.startOf('week').add(1, 'day')).format('YYYY-MM-DD HH:mm:ss') const startTime = (today.startOf('week').add(1, 'day')).format('YYYY-MM-DD HH:mm:ss')
const endTime = today.add(1,'week').startOf('week').add(1,'day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('week').format('YYYY-MM-DD HH:mm:ss')
// const endTime = today.add(1, 'week').startOf('week').add(1, 'day').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime] dateRange.value = [startTime, endTime]
console.log('看看dateRange', dateRange.value) console.log('看看dateRange', dateRange.value)
activeTimeRange.value = 'week' // activeTimeRange.value = 'week' //
@ -319,7 +321,8 @@ const getWeek = function () {
const getMonth = function () { const getMonth = function () {
const today = dayjs() const today = dayjs()
const startTime = today.startOf('month').format('YYYY-MM-DD HH:mm:ss') const startTime = today.startOf('month').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.add(1,'month').startOf('month').format('YYYY-MM-DD HH:mm:ss')
// const endTime = today.add(1, 'month').startOf('month').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('month').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime] dateRange.value = [startTime, endTime]
console.log('看看dateRange', dateRange.value) console.log('看看dateRange', dateRange.value)
activeTimeRange.value = 'month' // activeTimeRange.value = 'month' //
@ -330,7 +333,8 @@ const getMonth = function () {
const getYear = function () { const getYear = function () {
const today = dayjs() const today = dayjs()
const startTime = today.startOf('year').format('YYYY-MM-DD HH:mm:ss') const startTime = today.startOf('year').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.add(1,'year').startOf('year').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('year').format('YYYY-MM-DD HH:mm:ss')
// const endTime = today.add(1, 'year').startOf('year').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime] dateRange.value = [startTime, endTime]
console.log('看看dateRange', dateRange.value) console.log('看看dateRange', dateRange.value)
activeTimeRange.value = 'year' // activeTimeRange.value = 'year' //
@ -433,6 +437,7 @@ const getMarkets = async () => {
} }
}) })
if (Array.isArray(response.data)) { if (Array.isArray(response.data)) {
// markets.value = response.data.filter(data => data !== "1")
markets.value = response.data markets.value = response.data
console.log('市场列表获取成功:', markets.value) console.log('市场列表获取成功:', markets.value)
} else { } else {
@ -452,12 +457,10 @@ const getChartData = async () => {
if (!markets.value || markets.value.length === 0) { if (!markets.value || markets.value.length === 0) {
await getMarkets() await getMarkets()
} }
// //
if (!dateRange.value || dateRange.value.length === 0) { if (!dateRange.value || dateRange.value.length === 0) {
getYear() getYear()
} }
const params = { const params = {
markets: markets.value, markets: markets.value,
startDate: dateRange.value[0], startDate: dateRange.value[0],
@ -471,6 +474,7 @@ const getChartData = async () => {
}) })
console.log('看看params', params) console.log('看看params', params)
if (Array.isArray(response.marketGraphs)) { if (Array.isArray(response.marketGraphs)) {
// const filteredGraphs = response.marketGraphs.filter(data => data.market !== "1");
// //
processChartData(response.marketGraphs) processChartData(response.marketGraphs)
// //
@ -821,29 +825,6 @@ onUnmounted(() => {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
} }
/* 添加加载动画 */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin { @keyframes spin {
0% { 0% {
transform: rotate(0deg); transform: rotate(0deg);

2
stats.html
File diff suppressed because it is too large
View File

3
vite.config.ts

@ -15,7 +15,8 @@ export default defineConfig(({ mode }) => {
supported: { supported: {
bigint: true bigint: true
}, },
treeShaking: true
treeShaking: true,
drop: ['console', 'debugger'],
}, },
plugins: [ plugins: [
vue(), vue(),

Loading…
Cancel
Save