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.

590 lines
19 KiB

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