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.

560 lines
18 KiB

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