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.

536 lines
17 KiB

3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
  1. <script setup>
  2. import { ref, onMounted, computed, nextTick } from 'vue'
  3. import { ElMessage } from 'element-plus'
  4. import axios from 'axios'
  5. import moment from 'moment'
  6. import API from '@/util/http'
  7. import { writeFile, utils } from 'xlsx'
  8. // 定义ref 变量来存储合计数据
  9. const totalPermanentGold = ref(0)
  10. const totalFreeGold = ref(0)
  11. const totalTaskGold = ref(0)
  12. const totalGoldTotal = ref(0)
  13. // 变量
  14. const adminData = ref({})
  15. const getAdminData = async function () {
  16. try {
  17. const result = await API({
  18. url: '/admin/userinfo',
  19. method: 'post',
  20. data: {}
  21. })
  22. adminData.value = result
  23. // console.log('请求成功', result)
  24. console.log('管理员用户信息', adminData.value)
  25. } catch (error) {
  26. console.log('管理员用户信息请求失败', error)
  27. }
  28. }
  29. const exportExcel = async function () {
  30. const params = {
  31. text:'',
  32. deptid:'',
  33. sort:1,
  34. field:'',
  35. page:getObj.pageNum,
  36. size:getObj.pageSize
  37. }
  38. const res = await API({ url: '/goldDetail/export', data: params })
  39. if (res.code === 200) {
  40. ElMessage.success('导出成功')
  41. }
  42. }
  43. // 精网号去空格,处理 goldDetail 中的 jwcode
  44. const trimJwCode = () => {
  45. if (goldDetail.value.jwcode) {
  46. goldDetail.value.jwcode = goldDetail.value.jwcode.replace(/\s/g, '');
  47. }
  48. }
  49. //定义平台信息的加载状态
  50. const isLoadingPlatform = ref(false)
  51. // 平台信息
  52. const platform = ref([])
  53. //获取平台信息的函数
  54. const getPlatform = async () => {
  55. isLoadingPlatform.value = true;
  56. try {
  57. const result = await API({
  58. url: '/general/platform',
  59. method: 'post',
  60. data: {}// 这里添加参数
  61. })
  62. // 假设后端返回的是字符串数组,转换为 { value, label } 格式
  63. if (Array.isArray(result.data)) {
  64. platform.value = result.data.map(item => ({ value: item, label: item }));
  65. } else {
  66. console.error('平台信息格式错误', result)
  67. ElMessage.error('平台信息格式错误,请联系管理员')
  68. }
  69. } catch (error) {
  70. console.error('获取平台信息失败:', error);
  71. ElMessage.error('获取平台信息失败,请稍后重试');
  72. } finally {
  73. isLoadingPlatform.value = false
  74. }
  75. }
  76. // 数量更新类型选项
  77. const type = [
  78. {
  79. value: '0',
  80. label: '充值'
  81. },
  82. {
  83. value: '1',
  84. label: '消耗'
  85. },
  86. {
  87. value: '2',
  88. label: '退款'
  89. }
  90. ]
  91. // 定义加载状态,获取地区数据
  92. const isLoadingArea = ref(false);
  93. const market = ref([])
  94. const getArea = async () => {
  95. isLoadingArea.value = true;
  96. try {
  97. const result = await API({
  98. url: '/general/market'
  99. });
  100. // 假设后端返回的是字符串数组,转换为 { value, label } 格式
  101. if (Array.isArray(result.data) && typeof result.data[0] === 'string') {
  102. market.value = result.data.map(item => ({ value: item, label: item }));
  103. } else {
  104. market.value = result.data;
  105. }
  106. } catch (error) {
  107. console.error('获取地区数据失败:', error);
  108. ElMessage.error('获取地区数据失败,请稍后重试');
  109. // 可以提供默认数据
  110. market.value = [];
  111. } finally {
  112. isLoadingArea.value = false;
  113. }
  114. };
  115. // 充值明细表格
  116. const tableData = ref([])
  117. // 各金币字段变量
  118. // const sumGoldTotal = ref(0)
  119. const permanentGold = ref(0)
  120. //const freeGold = ref(0)
  121. const taskGold = ref(0)
  122. // 搜索===========================================
  123. //分页总条目
  124. const total = ref(100)
  125. // 搜索对象时间
  126. const getTime = ref([])
  127. // 搜索goldDetail
  128. const goldDetail = ref({})
  129. // 搜索对象
  130. const getObj = ref({
  131. pageNum: 1,
  132. pageSize: 50
  133. })
  134. // 开启条件筛选导出excel
  135. // const getPutEX = ref(false)
  136. // 方法
  137. // 搜索===========================================================================
  138. // 搜索方法
  139. const get = async function (val) {
  140. try {
  141. // 搜索参数页码赋值
  142. if (typeof val === 'number') {
  143. getObj.value.pageNum = val
  144. }
  145. if (getTime.value.length === 2) {//检查是否同时选择了开始时间和结束时间,如果不是则置空
  146. goldDetail.value.startTime = moment(getTime.value[0]).format('YYYY-MM-DD HH:mm:ss');
  147. goldDetail.value.endTime = moment(getTime.value[1]).format('YYYY-MM-DD HH:mm:ss');
  148. } else {
  149. goldDetail.value.startTime = ''
  150. goldDetail.value.endTime = ''
  151. }
  152. // 添加排序字段和排序方式到请求参数
  153. goldDetail.value.sortField = sortField.value
  154. goldDetail.value.sortOrder = sortOrder.value
  155. console.log('搜索参数', getObj.value)
  156. console.log('jwcode 类型:', typeof goldDetail.value.jwcode);
  157. console.log('jwcode 值:', goldDetail.value.jwcode);
  158. const requestData = { ...getObj.value, goldDetail: { ...goldDetail.value } };
  159. console.log('最终请求参数', JSON.stringify(requestData, null, 2)); // 打印格式化后的请求参数
  160. const result = await API({
  161. url: '/goldDetail/getGoldDetail',
  162. method: 'post',
  163. data: { ...getObj.value, goldDetail: { ...goldDetail.value } }
  164. })
  165. console.log('响应数据', result)
  166. tableData.value = result.data.list
  167. total.value = result.data.total
  168. // 更新永久金币、任务金币
  169. // permanentGold.value = tableData.value.reduce((total, row) => {
  170. // return total + (Number(row.permanentGold) || 0);
  171. // }, 0);
  172. // taskGold.value = tableData.value.reduce((total, row) => {
  173. // return total + (Number(row.taskGold) || 0);
  174. // }, 0);
  175. //由于免费金币的计算方式是6月免费+12月免费,所以需要单独处理,以及计算总的免费金币和总的金币数放在一块
  176. const totalResult = await API({
  177. url: '/goldDetail/getTotal',
  178. method: 'post',
  179. data: {
  180. jwcode: goldDetail.value.jwcode || '',
  181. payPlatform: goldDetail.value.payPlatform || '',
  182. type: goldDetail.value.type || '',
  183. market: goldDetail.value.market || '',
  184. startTime: goldDetail.value.startTime || '',
  185. endTime: goldDetail.value.endTime || ''
  186. }
  187. })
  188. if (totalResult.code === 200) {
  189. const data = totalResult.data
  190. totalPermanentGold.value = data.permanentGolds
  191. totalFreeGold.value = data.freeGolds
  192. totalTaskGold.value = data.taskGolds
  193. totalGoldTotal.value = data.sumGolds
  194. } else {
  195. ElMessage.error('获取合计数据失败')
  196. }
  197. } catch (error) {
  198. console.log('请求失败', error)
  199. }
  200. }
  201. // 重置
  202. const reset = function () {
  203. delete goldDetail.value.jwcode
  204. delete goldDetail.value.type
  205. delete goldDetail.value.startTime
  206. delete goldDetail.value.endTime
  207. delete goldDetail.value.market
  208. delete sortField.value
  209. delete sortOrder.value
  210. getTime.value = []
  211. delete goldDetail.value.payPlatform
  212. search()
  213. }
  214. // 搜索,点击查询按钮后触发
  215. const search = function () {
  216. trimJwCode();
  217. getObj.value.pageNum = 1
  218. get()
  219. }
  220. // 今天
  221. const getToday = function () {
  222. const today = moment()
  223. const startTime = today.startOf('day').toDate()
  224. const endTime = today.add(1, 'days').startOf('day').toDate()
  225. getTime.value = [startTime, endTime]
  226. search()
  227. }
  228. // 昨天
  229. const getYesterday = function () {
  230. const today = moment()
  231. const yesterday = moment().subtract(1, 'day')
  232. const startTime = yesterday.startOf('day').toDate()
  233. const endTime = today.startOf('day').toDate()
  234. getTime.value = [startTime, endTime]
  235. search()
  236. }
  237. // 近7天
  238. const get7Days = function () {
  239. const startTime = moment().subtract(6, 'day').startOf('day').toDate()
  240. const endTime = moment().add(1, 'days').startOf('day').toDate()
  241. getTime.value = [startTime, endTime]
  242. search()
  243. }
  244. // // 计算所有记录的金币总数
  245. // const sumGoldTotal = computed(() => {
  246. // return tableData.value.reduce((total, row) => {
  247. // return total + (Number(row.sumGold) || 0);
  248. // }, 0);
  249. // });
  250. // 计算每行免费金币6月+12月的方法
  251. const calculateFreeGold = (row) => {
  252. const freeJune = row.freeJune || 0;
  253. const freeDecember = row.freeDecember || 0;
  254. return (freeJune + freeDecember);
  255. };
  256. // 计算总免费金币的计算属性
  257. // const totalFreeGold = computed(() => {
  258. // return tableData.value.reduce((total, row) => {
  259. // return total + calculateFreeGold(row);
  260. // }, 0);
  261. // });
  262. // 验证页码跳转输入框的数字是否合法
  263. const checkNumber = function () {
  264. if (typeof parseInt(getObj.value.pageNum) === 'number') {
  265. console.log('总共有多少页' + Math.ceil(total.value / getObj.value.pageSize))
  266. if (
  267. getObj.value.pageNum > 0 &&
  268. getObj.value.pageNum <= Math.ceil(total.value / getObj.value.pageSize)
  269. ) {
  270. getObj.value.pageNum = parseInt(getObj.value.pageNum)
  271. console.log('输入的数字合法')
  272. get()
  273. } else {
  274. //提示
  275. ElMessage({
  276. type: 'error',
  277. message: '请检查输入内容'
  278. })
  279. }
  280. } else {
  281. //提示
  282. ElMessage({
  283. type: 'error',
  284. message: '输入非法'
  285. })
  286. }
  287. }
  288. // 新增排序字段和排序方式
  289. const sortField = ref('')
  290. const sortOrder = ref('')
  291. // 处理排序事件
  292. const handleSortChange = (column) => {
  293. if (column.prop === 'sumGold') {//新增金币总数字段排序
  294. sortField.value = 'sum_gold'
  295. } else if (column.prop === 'permanentGold') {
  296. sortField.value = 'permanent_gold'
  297. } else if (column.prop === 'taskGold') {
  298. sortField.value = 'task_gold'
  299. } else if (column.prop === 'freeGold') {
  300. sortField.value = 'free_gold'
  301. } else if (column.prop === 'auditTime') {//删除了更新时间的creatTime
  302. sortField.value = 'audit_time'
  303. }
  304. sortOrder.value = column.order === 'ascending' ? 'ASC' : 'DESC'
  305. //get()要写在handleSortChange方法里面,不然会导致排序失效
  306. get()
  307. }
  308. const handlePageSizeChange = function (val) {
  309. getObj.value.pageSize = val
  310. get()
  311. }
  312. const handleCurrentChange = function (val) {
  313. getObj.value.pageNum = val
  314. get()
  315. }
  316. // 挂载
  317. onMounted(async function () {
  318. await get()
  319. await getArea()
  320. await getAdminData()
  321. await getPlatform() // 调用获取平台信息的函数
  322. })
  323. </script>
  324. <template>
  325. <div>
  326. <el-row>
  327. <el-col>
  328. <el-card style="margin-bottom: 20px;margin-top: 10px;">
  329. <el-row style="margin-bottom: 10px">
  330. <el-col :span="5">
  331. <div class="head-card-element">
  332. <el-text class="mx-1" size="large">精网号</el-text>
  333. <el-input v-model="goldDetail.jwcode" style="width: 150px" placeholder="请输入精网号" clearable />
  334. </div>
  335. </el-col>
  336. <el-col :span="6">
  337. <div class="head-card-element">
  338. <el-text class="mx-1" size="large">平台信息</el-text>
  339. <el-select v-model="goldDetail.payPlatform" placeholder="请选择平台信息" style="width: 160px" clearable
  340. :loading="isLoadingPlatform">
  341. <el-option v-for="item in platform" :key="item.value" :label="item.label" :value="item.value" />
  342. </el-select>
  343. </div>
  344. </el-col>
  345. <el-col :span="7">
  346. <div class="head-card-element">
  347. <el-text class="mx-1" size="large">数量更新类型</el-text>
  348. <el-select v-model="goldDetail.type" placeholder="请选择更新类型" style="width: 160px" clearable>
  349. <el-option v-for="item in type" :key="item.value" :label="item.label" :value="item.value" />
  350. </el-select>
  351. </div>
  352. </el-col>
  353. <el-col :span="6">
  354. <div class="head-card-element">
  355. <el-text class="mx-1" size="large">所属地区</el-text>
  356. <el-select v-model="goldDetail.market" placeholder="请选择所属地区" style="width: 180px" clearable
  357. :loading="isLoadingArea">
  358. <el-option v-for="item in market" :key="item.value || item" :label="item.label || item"
  359. :value="item.value || item" />
  360. </el-select>
  361. </div>
  362. </el-col>
  363. </el-row>
  364. <div class="head-card-element">
  365. <el-text class="mx-1" size="large">更新时间</el-text>
  366. <el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
  367. end-placeholder="结束时间" style="margin-right: 50px" />
  368. <el-button @click="getToday()"></el-button>
  369. <el-button @click="getYesterday()"></el-button>
  370. <el-button @click="get7Days()">近7天</el-button>
  371. <el-button type="success" @click="reset()">重置</el-button>
  372. <el-button type="primary" @click="search()">查询</el-button>
  373. <el-button type="primary" @click="exportExcel">导出Excel表格</el-button>
  374. </div>
  375. </el-card>
  376. </el-col>
  377. </el-row>
  378. <el-row>
  379. <el-col>
  380. <el-card>
  381. <div>
  382. 金币总数{{ Math.abs(totalGoldTotal) / 100 }}
  383. 永久金币{{ Math.abs(totalPermanentGold) / 100 }}
  384. 免费金币{{ Math.abs(totalFreeGold) / 100 }}
  385. 任务金币{{ Math.abs(totalTaskGold) / 100 }}
  386. </div>
  387. <div style="height: 584px; overflow-y: auto">
  388. <el-table :data="tableData" style="width: 100%" @sort-change="handleSortChange" height="584px">
  389. <el-table-column type="index" label="序号" width="100px" fixed="left">
  390. <template #default="scope">
  391. <span>{{
  392. scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
  393. }}</span>
  394. </template>
  395. </el-table-column>
  396. <el-table-column fixed="left" prop="name" label="姓名" width="150" />
  397. <el-table-column fixed="left" prop="jwcode" label="精网号" width="120" />
  398. <el-table-column prop="market" label="所属地区" width="120" />
  399. <el-table-column prop="payPlatform" label="平台信息" width="140">
  400. </el-table-column>
  401. <el-table-column prop="type" label="更新类型" width="110">
  402. <template #default="scope">
  403. <span v-if="scope.row.type === 0">充值</span>
  404. <span v-if="scope.row.type === 1">消耗</span>
  405. <span v-if="scope.row.type === 2">退款</span>
  406. </template>
  407. </el-table-column>
  408. <el-table-column prop="sumGold" sortable="custom" label="金币数量" width="110">
  409. <template #default="scope">
  410. <span>
  411. {{
  412. scope.row.type === 1 // 消费类型
  413. ? - scope.row.sumGold / 100
  414. : scope.row.sumGold / 100
  415. }}
  416. </span>
  417. </template>
  418. </el-table-column>
  419. <el-table-column prop="permanentGold" sortable="custom" label="永久金币" width="110">
  420. <template #default="scope">
  421. <span>{{ scope.row.permanentGold / 100 }}</span>
  422. </template>
  423. </el-table-column>
  424. <el-table-column prop="freeGold" sortable="custom" label="免费金币" width="110">
  425. <template #default="scope">
  426. <span>{{ calculateFreeGold(scope.row) / 100 }}</span>
  427. </template>
  428. </el-table-column>
  429. <el-table-column prop="taskGold" sortable="custom" label="任务金币" width="110">
  430. <template #default="scope">
  431. <span>{{ scope.row.taskGold / 100 }}</span>
  432. </template>
  433. </el-table-column>
  434. <el-table-column prop="adminName" label="提交人" width="110" />
  435. <el-table-column prop="auditTime" sortable="custom" label="更新时间" width="210" show-overflow-tooltip>
  436. <template #default="scope">
  437. <span>{{
  438. moment(scope.row.auditTime).format('YYYY-MM-DD HH:mm:ss')
  439. }}</span>
  440. </template>
  441. </el-table-column>
  442. </el-table>
  443. </div>
  444. <!-- 此处分页 -->
  445. <div class="pagination" style="margin-top: 20px">
  446. <el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
  447. layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
  448. @current-change="handleCurrentChange"></el-pagination>
  449. </div>
  450. </el-card>
  451. </el-col>
  452. </el-row>
  453. </div>
  454. </template>
  455. <style scoped>
  456. .pagination {
  457. display: flex;
  458. }
  459. .status {
  460. display: flex;
  461. }
  462. .head-card {
  463. display: flex;
  464. }
  465. .info-panel-header {
  466. font-weight: bold;
  467. margin-bottom: 10px;
  468. }
  469. .dialog-footer {
  470. display: flex;
  471. justify-content: flex-end;
  472. }
  473. .export-status {
  474. margin-top: 15px;
  475. text-align: center;
  476. color: #666;
  477. }
  478. .el-progress-bar__inner {
  479. transition: width 0.5s ease;
  480. }
  481. </style>