You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

598 lines
19 KiB

1 month ago
1 month ago
1 month ago
4 weeks ago
1 month ago
4 weeks ago
1 month ago
2 weeks ago
4 weeks ago
1 month ago
1 month ago
4 weeks ago
4 weeks ago
1 month ago
  1. <script setup>
  2. // 这是客户金币余额页面
  3. import {onMounted, ref} from 'vue'
  4. import {ElMessage} from 'element-plus'
  5. import moment from 'moment'
  6. import API from '@/util/http.js'
  7. import {reverseMarketMapping} from "@/utils/marketMap.js";
  8. // 变量
  9. //这是获取用户信息的接口
  10. const adminData = ref({})
  11. const dialogVisible = ref(false)
  12. const getAdminData = async function () {
  13. try {
  14. const result = await API({url: '/admin/userinfo', data: {}})
  15. adminData.value = result
  16. // console.log('请求成功', result)
  17. console.log('管理员用户信息', adminData.value)
  18. } catch (error) {
  19. console.log('请求失败', error)
  20. }
  21. }
  22. // 定义加载状态,获取地区数据
  23. const isLoadingmarket = ref(false);
  24. const markets = ref([])
  25. // 充值明细表格
  26. const tableData = ref([])
  27. // 新增金币总数变量
  28. const goldtotal = ref(0)
  29. // 计算用户各金币总数的不分页对象
  30. const tableAllData = ref([])
  31. // 各金币字段
  32. const permanentGold = ref(0) // 修改为 currentPermanentGold 对应字段
  33. const freeJuneGold = ref(0) // 修改为 currentFreeJune 对应字段
  34. const freeDecemberGold = ref(0) // 修改为 currentFreeDecember 对应字段
  35. const taskGold = ref(0) // 修改为 currentTaskGold 对应字段
  36. const freeGold = ref(0) // 计算免费金币总数
  37. //客户消费记录
  38. const tableCountData = ref([])
  39. const userInfo = ref({})
  40. // 搜索===========================================
  41. //分页总条目
  42. const total = ref(100)
  43. // 搜索对象时间
  44. const getTime = ref([])
  45. // 搜索User
  46. const user = ref({
  47. markets: [],
  48. })
  49. // 不分页的搜索对象
  50. const getAllObj = ref({})
  51. // 搜索对象
  52. const getObj = ref({
  53. pageNum: 1,
  54. pageSize: 50
  55. })
  56. // 新增排序字段和排序方式
  57. const sortField = ref('')
  58. const sortOrder = ref('')
  59. // 方法
  60. // 搜索===========================================================================
  61. // 搜索方法
  62. const get = async function (val) {
  63. try {
  64. // 搜索参数页码赋值
  65. if (typeof val === 'number') {
  66. getObj.value.pageNum = val
  67. }
  68. // 添加排序字段和排序方式到请求参数
  69. user.value.sortField = sortField.value
  70. user.value.sortOrder = sortOrder.value
  71. console.log('搜索参数', getObj.value)
  72. // 发送POST请求
  73. const requestData = {...getObj.value, user: {...user.value}};//控制台打印请求的参数
  74. console.log('最终请求参数', JSON.stringify(requestData, null, 2)); // 打印格式化后的请求参数
  75. //console.log('请求参数', requestData);
  76. // 检查markets数组中是否包含'总部'或'研发部'
  77. if (user.value.markets.includes('9') || user.value.markets.includes('9999')) {
  78. user.value.markets = [];
  79. }
  80. if (user.value.jwcode) {
  81. // 纯数字
  82. const numberRegex = /^\d{1,9}$/;
  83. // 检查是否不是数字
  84. if (!numberRegex.test(user.value.jwcode)) {
  85. ElMessage.error('请检查精网号格式')
  86. // 上面提示过了
  87. return
  88. }
  89. }
  90. const result = await API({
  91. url: '/goldDetail/getGold',
  92. method: 'post',
  93. data: {...getObj.value, user: {...user.value}}
  94. })
  95. console.log('响应数据', result)
  96. tableData.value = result.data.list
  97. total.value = result.data.total
  98. console.log('兄弟你是什么 user', user.value)
  99. // 获取合计数
  100. const resultGoldTotal = await API({
  101. url: '/goldDetail/goldTotal',
  102. data: {
  103. jwcode: user.value.jwcode,
  104. markets: user.value.markets
  105. }
  106. })
  107. // 判断精网号是否存在,假设精网号不存在时 result.data.list 为空数组
  108. if (result.data.list.length === 0) {
  109. // 将表格数据设置为空数组
  110. tableData.value = []
  111. // 将合计数设置为 0
  112. permanentGold.value = 0
  113. freeJuneGold.value = 0
  114. freeDecemberGold.value = 0
  115. taskGold.value = 0
  116. goldtotal.value = 0
  117. freeGold.value = 0
  118. // // 新增金币总数变量
  119. // const goldtotal = ref(0)
  120. // 分页总数设置为 0
  121. total.value = 0
  122. // ElMessage.warning('精网号不存在,请检查输入')
  123. }
  124. // 判断合计数是否存在,不存在时 result.data.list 为空数组
  125. else if(resultGoldTotal.data===0){
  126. // 将表格数据设置为空数组
  127. tableData.value = []
  128. // 将合计数设置为 0
  129. permanentGold.value = 0
  130. freeJuneGold.value = 0
  131. freeDecemberGold.value = 0
  132. taskGold.value = 0
  133. goldtotal.value = 0
  134. freeGold.value = 0
  135. }
  136. else {
  137. // 将响应结果存储到响应式数据中
  138. console.log('总数据请求成功', result)
  139. // 存储表格数据
  140. tableData.value = result.data.list
  141. console.log('tableData', tableData.value)
  142. // 从接口返回数据中获取各金币数值
  143. if (resultGoldTotal.data) {
  144. permanentGold.value = parseFloat(resultGoldTotal.data.permanentGold.toFixed(2))
  145. freeGold.value = parseFloat(resultGoldTotal.data.freeGold.toFixed(2))
  146. taskGold.value = parseFloat(resultGoldTotal.data.taskGold.toFixed(2))
  147. goldtotal.value = parseFloat(resultGoldTotal.data.goldtotal.toFixed(2))
  148. } else {
  149. console.error('合计数数据格式错误', resultGoldTotal)
  150. ElMessage.error('获取合计数失败,请稍后重试')
  151. }
  152. // 存储分页总数
  153. total.value = result.data.total
  154. console.log('total', total.value)
  155. }
  156. } catch (error) {
  157. console.log('请求失败', error)
  158. // 在这里可以处理错误逻辑,比如显示错误提示等
  159. }
  160. }
  161. // 精网号去空格,同时处理 user 和 putExcel 中的 jwcode
  162. const trimJwCode = () => {
  163. if (user.value.jwcode) {
  164. user.value.jwcode = user.value.jwcode.replace(/\s/g, '');
  165. }
  166. }
  167. // 搜索
  168. const search = function () {
  169. trimJwCode();
  170. getObj.value.pageNum = 1
  171. get()
  172. }
  173. // 重置
  174. const reset = function () {
  175. user.value = {
  176. jwcode: '',
  177. markets: [],
  178. }
  179. sortField.value = ''
  180. sortOrder.value = ''
  181. get()
  182. selectedMarketPath.value = []
  183. }
  184. const cellClick = function (row, column) {
  185. console.log('cellClick', column.label)
  186. if (column.label === '姓名') {
  187. dialogVisible.value = true
  188. userInfo.value = row
  189. }
  190. }
  191. // 处理排序事件
  192. const handleSortChange = (column) => {
  193. console.log('排序字段:', column.prop)
  194. console.log('排序方式:', column.order)
  195. if (column.prop === 'currentPermanentGold') {
  196. sortField.value = 'current_permanent_gold'
  197. } else if (column.prop === 'currentTaskGold') {
  198. sortField.value = 'current_task_gold'
  199. } else if (column.prop === 'currentFreeJune') {
  200. sortField.value = 'current_free_june'
  201. } else if (column.prop === 'currentFreeDecember') {
  202. sortField.value = 'current_free_december'
  203. }
  204. sortOrder.value = column.order === 'ascending' ? 'ASC' : 'DESC'
  205. get()
  206. }
  207. // 挂载
  208. onMounted(async function () {
  209. await getAdminData()
  210. await get()
  211. await getMarket()
  212. await getExportList()
  213. })
  214. const handlePageSizeChange = function (val) {
  215. getObj.value.pageSize = val
  216. get()
  217. }
  218. const handleCurrentChange = function (val) {
  219. getObj.value.pageNum = val
  220. get()
  221. }
  222. const exportExcel = async function () {
  223. const params = {
  224. user: {
  225. jwcode: user.value.jwcode || '',
  226. markets: user.value.markets || []
  227. }
  228. }
  229. const res = await API({url: '/goldDetail/exportGold', data: params})
  230. if (res.code === 200) {
  231. ElMessage.success('导出成功')
  232. }
  233. }
  234. const exportListVisible = ref(false)
  235. // 打开导出列表弹窗
  236. const openExportList = () => {
  237. getExportList()
  238. exportListVisible.value = true
  239. }
  240. // 导出列表数据
  241. const exportList = ref([])
  242. // 导出列表加载状态
  243. const exportListLoading = ref(false)
  244. // 获取导出列表
  245. const getExportList = async () => {
  246. exportListLoading.value = true
  247. try {
  248. const result = await API({url: '/export/export'})
  249. if (result.code === 200) {
  250. const filteredData = result.data.filter(item => {
  251. return item.type === 1; //返回type為0即客户金币余额的数据
  252. });
  253. exportList.value = filteredData
  254. } else {
  255. ElMessage.error(result.msg || '获取导出列表失败')
  256. }
  257. } catch (error) {
  258. console.error('获取导出列表出错:', error)
  259. ElMessage.error('获取导出列表失败,请稍后重试')
  260. } finally {
  261. exportListLoading.value = false
  262. }
  263. }
  264. // 下载导出文件
  265. const downloadExportFile = (item) => {
  266. if (item.state === 2) {
  267. const link = document.createElement('a')
  268. link.href = item.url
  269. link.download = item.fileName
  270. link.click()
  271. } else {
  272. ElMessage.warning('文件还在导出中,请稍后再试')
  273. }
  274. }
  275. //根据状态返回对应的标签类型
  276. const getTagType = (state) => {
  277. switch (state) {
  278. case 0:
  279. return 'info';
  280. case 1:
  281. return 'primary';
  282. case 2:
  283. return 'success';
  284. case 3:
  285. return 'danger';
  286. default:
  287. return 'info';
  288. }
  289. }
  290. //根据状态返回对应的标签文案
  291. const getTagText = (state) => {
  292. switch (state) {
  293. case 0:
  294. return '待执行';
  295. case 1:
  296. return '执行中';
  297. case 2:
  298. return '执行完成';
  299. case 3:
  300. return '执行出错';
  301. default:
  302. return '未知状态';
  303. }
  304. }
  305. // 存储地区选择变化
  306. const selectedMarketPath = ref([])
  307. const handleMarketChange = (value) => {
  308. if (value && value.length > 0) {
  309. const lastValue = value[value.length - 1];
  310. // 确保返回值是数组,如果不是则包装成数组
  311. const marketValue = reverseMarketMapping[lastValue];
  312. user.value.markets = Array.isArray(marketValue) ? marketValue : [marketValue];
  313. } else {
  314. // 保持[]格式
  315. user.value.markets = [];
  316. }
  317. };
  318. // 获取地区,修改为级联下拉框
  319. const getMarket = async function () {
  320. try {
  321. // 发送POST请求
  322. const result = await API({
  323. url: '/market/selectMarket',
  324. });
  325. // 将响应结果存储到响应式数据中
  326. console.log('请求成功', result)
  327. // 递归转换树形结构为级联选择器需要的格式(跳过第一级节点)
  328. const transformTree = (nodes) => {
  329. // 直接处理第一级节点的子节点
  330. const allChildren = nodes.flatMap(node => node.children || []);
  331. return allChildren.map(child => {
  332. const grandchildren = child.children && child.children.length
  333. ? transformTree([child]) // 递归处理子节点
  334. : null;
  335. return {
  336. value: child.name,
  337. label: child.name,
  338. children: grandchildren
  339. };
  340. });
  341. };
  342. // 存储地区信息
  343. markets.value = transformTree(result.data)
  344. console.log('转换后的地区树==============', markets.value)
  345. } catch (error) {
  346. console.log('请求失败', error)
  347. }
  348. }
  349. const format3 = (num) => {
  350. // 每三位添加逗号
  351. return num.toLocaleString('en-US')
  352. }
  353. </script>
  354. <template>
  355. <el-row>
  356. <el-col>
  357. <el-card style="margin-bottom: 20px;margin-top: 10px">
  358. <div class="head-card">
  359. <div class="head-card-element">
  360. <el-text class="mx-1" size="large">精网号</el-text>
  361. <el-input v-model="user.jwcode" style="width: 160px" placeholder="请输入精网号" clearable/>
  362. </div>
  363. <div class="head-card-element">
  364. <el-text class="mx-1" size="large">所属地区</el-text>
  365. <el-cascader
  366. v-model="selectedMarketPath"
  367. :options="markets"
  368. placeholder="请选择所属地区"
  369. clearable
  370. style="width:180px"
  371. @change="handleMarketChange"
  372. />
  373. </div>
  374. <el-button type="primary" @click="search()">查询</el-button>
  375. <el-button @click="reset" type="success">重置</el-button>
  376. <el-button type="primary" @click="exportExcel()">导出Excel</el-button>
  377. <el-button type="primary" @click="openExportList">查看导出列表</el-button>
  378. </div>
  379. <!-- </div> -->
  380. </el-card>
  381. </el-col>
  382. </el-row>
  383. <el-row>
  384. <el-col>
  385. <el-card>
  386. <div>
  387. 金币总数{{ format3(goldtotal || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
  388. 永久金币{{ format3(permanentGold || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
  389. 免费金币{{ format3(freeGold || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
  390. 任务金币{{ format3(taskGold || 0) }}
  391. </div>
  392. <!-- 设置表格容器的高度和滚动样式 -->
  393. <div style="height: 626px; overflow-y: auto">
  394. <el-table :data="tableData" @cellClick="cellClick" style="width: 100%" height="626px"
  395. @sort-change="handleSortChange">
  396. <el-table-column type="index" label="序号" width="100px" fixed="left">
  397. <template #default="scope">
  398. <span>{{
  399. scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
  400. }}</span>
  401. </template>
  402. </el-table-column>
  403. <el-table-column prop="name" label="姓名" width="120"/>
  404. <el-table-column prop="jwcode" label="精网号" width="120"/>
  405. <el-table-column prop="market" label="所属地区" width="120"/>
  406. <el-table-column prop="allJb" label="金币总数" width="120" aligh="center">
  407. <template #default="scope">
  408. <span>{{
  409. ((scope.row.currentPermanentGold || 0) +
  410. (scope.row.currentFreeJune || 0) +
  411. (scope.row.currentFreeDecember || 0) +
  412. (scope.row.currentTaskGold || 0))
  413. }}</span>
  414. </template>
  415. </el-table-column>
  416. <el-table-column prop="currentPermanentGold" label="永久金币" sortable="custom" width="110">
  417. <template #default="scope">
  418. <span>{{ (scope.row.currentPermanentGold || 0) }}</span>
  419. </template>
  420. </el-table-column>
  421. <el-table-column prop="currentFreeJune" label="6月份到期免费金币" sortable="custom" width="110">
  422. <template #default="scope">
  423. <span>{{ (scope.row.currentFreeJune || 0) }}</span>
  424. </template>
  425. </el-table-column>
  426. <el-table-column prop="currentFreeDecember" label="12月份到期免费金币" sortable="custom" width="110">
  427. <template #default="scope">
  428. <span>{{ (scope.row.currentFreeDecember || 0) }}</span>
  429. </template>
  430. </el-table-column>
  431. <el-table-column prop="currentTaskGold" label="任务金币" sortable="custom" width="130">
  432. <template #default="scope">
  433. <span>{{ (scope.row.currentTaskGold || 0) }}</span>
  434. </template>
  435. </el-table-column>
  436. <el-table-column prop="rcoin" label="历史金币总额" width="150">
  437. <template #default="scope">
  438. <el-popover trigger="hover" placement="left" width="150">
  439. <template #default>
  440. <div>
  441. <div>永久金币{{ (scope.row.sumPermanentGold || 0) }}</div>
  442. <div>免费金币{{ ((scope.row.sumFreeJune || 0) + (scope.row.sumFreeDecember || 0)) }}</div>
  443. <div>任务金币{{ (scope.row.sumTaskGold || 0) }}</div>
  444. </div>
  445. </template>
  446. <template #reference>
  447. <span>
  448. {{
  449. (scope.row.sumPermanentGold || 0) +
  450. (scope.row.sumFreeJune || 0) +
  451. (scope.row.sumFreeDecember || 0) +
  452. (scope.row.sumTaskGold || 0)
  453. }}</span>
  454. </template>
  455. </el-popover>
  456. </template>
  457. </el-table-column>
  458. <el-table-column prop="sumConsume" label="历史消费" width="150">
  459. <template #default="scope">
  460. <el-popover trigger="hover" placement="left" width="150">
  461. <template #default>
  462. <div>
  463. <div>永久金币{{ (scope.row.sumConsumeGold || 0) }}</div>
  464. <div>免费金币{{
  465. ((scope.row.sumConsumeJune || 0) + (scope.row.sumConsumeDecember || 0))
  466. }}
  467. </div>
  468. <div>任务金币{{ (scope.row.sumConsumeJune || 0) }}</div>
  469. </div>
  470. </template>
  471. <template #reference>
  472. <span>
  473. {{
  474. (scope.row.sumConsumeGold || 0) +
  475. (scope.row.sumConsumeTaskGold || 0) +
  476. (scope.row.sumConsumeJune || 0) +
  477. (scope.row.sumConsumeDecember || 0)
  478. }}</span>
  479. </template>
  480. </el-popover>
  481. </template>
  482. </el-table-column>
  483. </el-table>
  484. </div>
  485. <!-- 分页 -->
  486. <div class="pagination" style="margin-top: 20px">
  487. <el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
  488. layout="total, sizes, prev, pager, next, jumper" :total="total"
  489. @size-change="handlePageSizeChange"
  490. @current-change="handleCurrentChange"></el-pagination>
  491. </div>
  492. </el-card>
  493. </el-col>
  494. </el-row>
  495. <el-dialog v-model="exportListVisible" title="导出列表" width="80%">
  496. <el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
  497. <el-table-column prop="fileName" label="文件名"/>
  498. <el-table-column prop="state" label="状态">
  499. <template #default="scope">
  500. <el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
  501. {{ getTagText(scope.row.state) }}
  502. </el-tag>
  503. </template>
  504. </el-table-column>
  505. <el-table-column prop="createTime" label="创建时间">
  506. <template #default="scope">
  507. {{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
  508. </template>
  509. </el-table-column>
  510. <el-table-column label="操作">
  511. <template #default="scope">
  512. <el-button type="primary" size="small" @click="downloadExportFile(scope.row)"
  513. :disabled="scope.row.state !== 2">
  514. 下载
  515. </el-button>
  516. </template>
  517. </el-table-column>
  518. </el-table>
  519. <template #footer>
  520. <div class="dialog-footer">
  521. <el-button text @click="exportListVisible = false">关闭</el-button>
  522. </div>
  523. </template>
  524. </el-dialog>
  525. </template>
  526. <style scoped lang="scss">
  527. .pagination {
  528. display: flex;
  529. }
  530. .status {
  531. display: flex;
  532. }
  533. .head-card {
  534. display: flex;
  535. }
  536. .head-card-element {
  537. margin-right: 20px;
  538. }
  539. .head-card-btn {
  540. margin-left: auto;
  541. }
  542. .custom-box {
  543. display: flex;
  544. flex-wrap: wrap;
  545. row-gap: 5px;
  546. div:nth-child(1) {
  547. flex: 1 0 100%;
  548. }
  549. div {
  550. margin-right: 20px;
  551. }
  552. }
  553. </style>