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.

629 lines
18 KiB

  1. <script setup>
  2. // 这是退款明细页面
  3. import {ref, onMounted, reactive, computed} from 'vue'
  4. import ElementPlus from 'element-plus'
  5. import {AiFillRead} from 'vue-icons-plus/ai'
  6. import {ElMessage, ElMessageBox} from 'element-plus'
  7. import axios from 'axios'
  8. import moment from 'moment'
  9. import API from '@/util/http'
  10. import request from '@/util/http'
  11. // 精网号去空格
  12. const trimJwCode = () => {
  13. if (refundUser.value.jwcode) {
  14. // 去除所有空格,并尝试转换为整数
  15. const trimmed = refundUser.value.jwcode.toString().replace(/\s/g, '');
  16. const numeric = Number(trimmed);
  17. // 如果转换为数字成功,保存为数字,否则提示错误
  18. if (!isNaN(numeric)) {
  19. refundUser.value.jwcode = numeric;
  20. } else {
  21. ElMessage.error("精网号格式不正确,请输入数字");
  22. }
  23. }
  24. }
  25. // 变量
  26. //这是获取用户信息的接口
  27. const adminData = ref({})
  28. const getAdminData = async function () {
  29. try {
  30. const result = await API({url: '/admin/userinfo', data: {}})
  31. adminData.value = result
  32. console.log('请求成功', result)
  33. console.log('用户信息', adminData.value)
  34. } catch (error) {
  35. console.log('请求失败', error)
  36. }
  37. }
  38. // 充值明细表格
  39. const tableData = ref([])
  40. // 搜索======================================
  41. // 搜索detail
  42. const refundUser = ref({})
  43. // 搜索对象
  44. const getObj = ref({
  45. pageNum: 1,
  46. pageSize: 50
  47. })
  48. //分页总条目
  49. const total = ref(100)
  50. // 搜索对象时间
  51. const getTime = ref([])
  52. // 搜索地区列表
  53. const market = ref([])
  54. // 定义响应式变量存储金币合计数
  55. const permanentGolds = ref(0)
  56. const freeGolds = ref(0)
  57. const taskGolds = ref(0)
  58. // 计算总金币数
  59. const sumGold = computed(() => permanentGolds.value + freeGolds.value + taskGolds.value)
  60. // 退款类型
  61. const refundType = ref([])
  62. //时间格式化
  63. const formatTime = (val) => val ? moment(val).format('YYYY-MM-DD HH:mm:ss') : ''
  64. // 获取退款类型
  65. const getRefundTypes = async function () {
  66. try {
  67. // 发送请求获取退款类型
  68. const result = await API({
  69. url: '/refund/refundType', //这里应该写上一个退款类型的接口
  70. data: {} })
  71. console.log('退款类型请求成功', result)
  72. // 检查返回的数据是否为数组
  73. if (Array.isArray(result.data)) {
  74. // 将字符串数组转换为 { value, label } 格式
  75. refundType.value = result.data.map(item => ({ value: item, label: item }));
  76. } else {
  77. console.error('退款类型数据格式错误', result)
  78. ElMessage.error('退款类型数据格式错误,请联系管理员')
  79. }
  80. console.log('退款类型', refundType.value)
  81. } catch (error) {
  82. console.log('退款类型请求失败', error)
  83. }
  84. }
  85. // 搜索==============================================================
  86. // 搜索方法
  87. const getSelectBy = async function (val) {
  88. try {
  89. // 搜索参数页码赋值
  90. if (typeof val === 'number') {
  91. getObj.value.pageNum = val
  92. }
  93. // todo 时间格式化
  94. // 搜索参数时间赋值
  95. if (getTime.value != null) {
  96. if (getTime.value.startTime != '' && getTime.value.endTime != '') {
  97. refundUser.value.startTime = formatTime(getTime.value[0])
  98. refundUser.value.endTime = formatTime(getTime.value[1])
  99. }
  100. } else {
  101. refundUser.value.startTime = ''
  102. refundUser.value.endTime = ''
  103. }
  104. // todo 排序后端还没有弄
  105. // 添加排序字段和排序方式到请求参数
  106. refundUser.value.sortField = sortField.value
  107. refundUser.value.sortOrder = sortOrder.value
  108. console.log('搜索参数', getObj.value)
  109. // 发送POST请求
  110. const result = await API({
  111. url: '/refund/selectBy',
  112. data: {
  113. ...getObj.value,
  114. refundUser: { ...refundUser.value }
  115. }
  116. })
  117. // 复制一份 refundUser.value 并移除排序字段和排序方式
  118. const detailWithoutSort = { ...refundUser.value }
  119. delete detailWithoutSort.sortField
  120. delete detailWithoutSort.sortOrder
  121. const resultTotalGold = await API({
  122. url: '/refund/statsGold',
  123. data: {
  124. ...detailWithoutSort
  125. }
  126. })
  127. // 将响应结果存储到响应式数据中
  128. console.log('resultTotalGold请求成功', resultTotalGold)
  129. // 检查响应的 code 是否为 200 且 data 存在
  130. if (resultTotalGold.code === 200 && resultTotalGold.data) {
  131. const data = resultTotalGold.data
  132. console.log('获取到的金币数据:', data)
  133. permanentGolds.value = (Number(data.permanentGolds) || 0)
  134. freeGolds.value = (Number(data.freeGolds) || 0)
  135. taskGolds.value = (Number(data.taskGolds) || 0)
  136. }
  137. // 存储表格数据
  138. tableData.value = result.data.list
  139. // 对表格中的金币数据除以 100
  140. tableData.value = tableData.value.map(item => ({
  141. ...item,
  142. sumGold: (Number(item.sumGold) || 0) / 100,
  143. permanentGold: (Number(item.permanentGold) || 0) / 100,
  144. freeGold: (Number(item.freeGold) || 0) / 100,
  145. taskGold: (Number(item.taskGold) || 0) / 100
  146. }))
  147. console.log('tableData', tableData.value)
  148. // 存储分页总数
  149. total.value = result.data.total
  150. console.log('total', total.value)
  151. // 调用分类的方法
  152. handleClick()
  153. } catch (error) {
  154. console.log('请求失败', error)
  155. // 在这里可以处理错误逻辑,比如显示错误提示等
  156. }
  157. }
  158. // 搜索
  159. const search = function () {
  160. getObj.value.pageNum = 1
  161. getSelectBy()
  162. }
  163. // 重置
  164. const reset = function () {
  165. refundUser.value = {}
  166. sortField.value = ''
  167. sortOrder.value = ''
  168. getTime.value = {}
  169. }
  170. // 今天
  171. const getToday = function () {
  172. const today = new Date()
  173. const startTime = new Date(
  174. today.getFullYear(),
  175. today.getMonth(),
  176. today.getDate()
  177. )
  178. const endTime = new Date(
  179. today.getFullYear(),
  180. today.getMonth(),
  181. today.getDate() + 1
  182. )
  183. getTime.value = [startTime, endTime]
  184. console.log('getTime', getTime.value)
  185. getSelectBy()
  186. }
  187. // 昨天
  188. const getYesterday = function () {
  189. const yesterday = new Date()
  190. yesterday.setDate(yesterday.getDate() - 1)
  191. const startTime = new Date(
  192. yesterday.getFullYear(),
  193. yesterday.getMonth(),
  194. yesterday.getDate()
  195. )
  196. const endTime = new Date(
  197. yesterday.getFullYear(),
  198. yesterday.getMonth(),
  199. yesterday.getDate() + 1
  200. )
  201. getTime.value = [startTime, endTime]
  202. console.log('getTime', getTime.value)
  203. getSelectBy()
  204. }
  205. // 近7天
  206. const get7Days = function () {
  207. const today = new Date()
  208. const startTime = new Date(
  209. today.getFullYear(),
  210. today.getMonth(),
  211. today.getDate() - 6
  212. )
  213. const endTime = new Date(
  214. today.getFullYear(),
  215. today.getMonth(),
  216. today.getDate() + 1
  217. )
  218. getTime.value = [startTime, endTime]
  219. console.log('getTime', getTime.value)
  220. getSelectBy()
  221. }
  222. //点击标签页
  223. // 设置tab.props.name默认为all
  224. const tabName = ref('all')
  225. const handleClick = function (tab, event) {
  226. if (tab.props.name === 'all') {
  227. adminAll()
  228. } else if (tab.props.name === 'wait') {
  229. adminWait()
  230. } else if (tab.props.name === 'pass') {
  231. adminPass()
  232. } else if (tab.props.name === 'reject') {
  233. adminReject()
  234. }
  235. }
  236. // 获取地区列表的方法
  237. const getMarket = async function () {
  238. try {
  239. // 发送POST请求
  240. const result = await API({ url: '/general/market', data: {} })
  241. // 将响应结果存储到响应式数据中
  242. console.log('请求成功', result)
  243. // 存储地区信息
  244. market.value = result.data
  245. console.log('地区', market.value)
  246. } catch (error) {
  247. console.log('请求失败', error)
  248. }
  249. }
  250. //删除气泡
  251. const delObj = ref({})
  252. const del = function (row) {
  253. delObj.value.detailId = row.detailId
  254. console.log('delObj', delObj.value)
  255. }
  256. // 删除按钮的气泡弹出框确认按钮
  257. const delConfirm = async function () {
  258. try {
  259. console.log('delObj', delObj.value)
  260. // 发送POST请求
  261. const result = await API({
  262. url: '/refund/softDelete?detailId=' + delObj.value.detailId,
  263. data: {}
  264. })
  265. // 将响应结果存储到响应式数据中
  266. console.log('请求成功', result)
  267. // 刷新表格数据
  268. getSelectBy()
  269. } catch (error) {
  270. console.log('请求失败', error)
  271. // 在这里可以处理错误逻辑,比如显示错误提示等
  272. }
  273. }
  274. // 查询商品的接口
  275. const goods = ref([])
  276. const getGoods = async function () {
  277. try {
  278. // 发送POST请求
  279. const result = await request({
  280. url: '/general/goods',
  281. data: {}
  282. })
  283. // 将响应结果存储到响应式数据中
  284. console.log('请求成功product', result)
  285. if (Array.isArray(result.data)) {
  286. // 过滤掉空字符串和 null 值
  287. const validGoods = result.data.filter(item => item && item.trim() !== '');
  288. // 直接使用后端返回的字符串作为 value 和 label
  289. goods.value = validGoods.map(item => ({
  290. value: item,
  291. label: item
  292. }));
  293. }
  294. } catch (error) {
  295. console.log('请求失败', error)
  296. // 在这里可以处理错误逻辑,比如显示错误提示等
  297. }
  298. }
  299. // 验证跳转输入框的数字是否合法
  300. const checkNumber = function () {
  301. if (typeof parseInt(getObj.value.pageNum) === 'number') {
  302. console.log('总共有多少页' + Math.ceil(total.value / getObj.value.pageSize))
  303. if (
  304. getObj.value.pageNum > 0 &&
  305. getObj.value.pageNum <= Math.ceil(total.value / getObj.value.pageSize)
  306. ) {
  307. getObj.value.pageNum = parseInt(getObj.value.pageNum)
  308. console.log('输入的数字合法')
  309. getSelectBy()
  310. } else {
  311. //提示
  312. ElMessage({
  313. type: 'error',
  314. message: '请检查输入内容'
  315. })
  316. }
  317. } else {
  318. //提示
  319. ElMessage({
  320. type: 'error',
  321. message: '请检查输入内容'
  322. })
  323. }
  324. }
  325. // 挂载
  326. onMounted(async function () {
  327. await getAdminData()
  328. await getSelectBy()
  329. await getMarket()
  330. await getRefundTypes()
  331. await getGoods()
  332. })
  333. // 新增排序字段和排序方式
  334. const sortField = ref('')
  335. const sortOrder = ref('')
  336. // 处理排序事件
  337. const handleSortChange = (column) => {
  338. console.log('排序字段:', column.prop)
  339. console.log('排序方式:', column.order)
  340. if (column.prop === 'permanentGold') {
  341. sortField.value = 'permanent_gold'
  342. } else if (column.prop === 'taskGold') {
  343. sortField.value = 'task_gold'
  344. } else if (column.prop === 'freeGold') {
  345. sortField.value = 'free_gold'
  346. } else if (column.prop === 'createTime') {
  347. sortField.value = 'create_time'
  348. } else if (column.prop === 'auditTime') {
  349. sortField.value = 'audit_time'
  350. }
  351. sortOrder.value = column.order === 'ascending' ? 'ASC' : 'DESC'
  352. getSelectBy()
  353. }
  354. const handlePageSizeChange = function (val) {
  355. getObj.value.pageSize = val
  356. getSelectBy()
  357. }
  358. const handleCurrentChange = function (val) {
  359. getObj.value.pageNum = val
  360. getSelectBy()
  361. }
  362. </script>
  363. <template>
  364. <el-row>
  365. <el-col>
  366. <el-card style="margin-bottom: 20px;margin-top:10px">
  367. <el-row style="margin-bottom: 10px">
  368. <el-col :span="5">
  369. <div class="head-card-element">
  370. <el-text class="mx-1" size="large">精网号</el-text>
  371. <el-input
  372. v-model="refundUser.jwcode"
  373. placeholder="请输入精网号"
  374. size="large"
  375. style="width: 150px"
  376. clearable
  377. />
  378. </div>
  379. </el-col>
  380. <el-col :span="6">
  381. <div class="head-card-element">
  382. <el-text class="mx-1" size="large">退款类型</el-text>
  383. <el-select
  384. v-model="refundUser.refundType"
  385. placeholder="请选择退款类型"
  386. size="large"
  387. style="width: 180px"
  388. clearable
  389. >
  390. <!-- todo 这需要改-->
  391. <el-option
  392. v-for="item in refundType"
  393. :key="item.value"
  394. :label="item.label"
  395. :value="item.value"
  396. />
  397. </el-select>
  398. </div>
  399. </el-col>
  400. <el-col :span="6">
  401. <div class="head-card-element">
  402. <el-text class="mx-1" size="large">退款商品</el-text>
  403. <el-select
  404. v-model="refundUser.goodsName"
  405. placeholder="请选择退款商品"
  406. size="large"
  407. style="width: 180px"
  408. clearable
  409. >
  410. <el-option
  411. v-for="item in goods"
  412. :key="item.value"
  413. :label="item.label"
  414. :value="item.value"
  415. />
  416. </el-select>
  417. </div>
  418. </el-col>
  419. <el-col :span="6">
  420. <div class="head-card-element">
  421. <el-text class="mx-1" size="large">所属地区</el-text>
  422. <el-select
  423. v-model="refundUser.market"
  424. placeholder="请选择所属地区"
  425. size="large"
  426. style="width: 180px"
  427. clearable
  428. >
  429. <el-option
  430. v-for="item in market"
  431. :key="item"
  432. :label="item"
  433. :value="item"
  434. />
  435. </el-select>
  436. </div>
  437. </el-col>
  438. </el-row>
  439. <el-row>
  440. <el-col :span="21">
  441. <div class="head-card-element">
  442. <el-text class="mx-1" size="large">退款时间</el-text>
  443. <el-date-picker
  444. v-model="getTime"
  445. type="datetimerange"
  446. range-separator="至"
  447. start-placeholder="起始时间"
  448. end-placeholder="结束时间"
  449. />
  450. <el-button style="margin-left: 10px" @click="getToday()"
  451. >
  452. </el-button
  453. >
  454. <el-button @click="getYesterday()"></el-button>
  455. <el-button @click="get7Days()">近7天</el-button>
  456. <el-button type="success" @click="reset()">重置</el-button>
  457. <el-button type="primary" @click="search()">查询</el-button>
  458. <el-button type="primary" @click="exportExcel">导出Excel</el-button>
  459. </div>
  460. </el-col>
  461. </el-row>
  462. </el-card>
  463. </el-col>
  464. </el-row>
  465. <el-row>
  466. <el-col>
  467. <el-card>
  468. <div>
  469. 退款金币总数{{ Math.abs(sumGold) / 100 }}永久金币{{
  470. Math.abs(permanentGolds) / 100
  471. }}免费金币{{ Math.abs(freeGolds) / 100 }}任务金币{{
  472. Math.abs(taskGolds) / 100
  473. }}
  474. </div>
  475. <!-- 设置表格容器的高度和滚动样式 -->
  476. <div style="height: 520px; overflow-y: auto;margin-top:10px">
  477. <el-table
  478. :data="tableData"
  479. style="width: 100%"
  480. @sort-change="handleSortChange"
  481. height="520px"
  482. >
  483. <el-table-column
  484. type="index"
  485. label="序号"
  486. width="100px"
  487. fixed="left"
  488. >
  489. <template #default="scope">
  490. <span>{{
  491. scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
  492. }}</span>
  493. </template>
  494. </el-table-column>
  495. <el-table-column
  496. prop="name"
  497. label="姓名"
  498. fixed="left"
  499. width="100px"
  500. />
  501. <el-table-column
  502. prop="jwcode"
  503. label="精网号"
  504. fixed="left"
  505. width="110px"
  506. />
  507. <el-table-column prop="market" label="所属地区" width="110px" />
  508. <el-table-column prop="goodsName" label="商品名称" width="100px" />
  509. <el-table-column prop="refundType" label="退款类型" width="100px" />
  510. <el-table-column label="退款金币总数" width="110px">
  511. <template #default="scope">
  512. {{
  513. scope.row.sumGold
  514. }}
  515. </template>
  516. </el-table-column>
  517. <el-table-column prop="refundModel" label="退款方式" width="110px">
  518. <template #default="scope">
  519. {{ scope.row.refundModel === 0 ? '全部退款' : scope.row.refundModel === 1 ? '部分退款' : '' }}
  520. </template>
  521. </el-table-column>
  522. <el-table-column
  523. prop="permanentGold"
  524. label="永久金币"
  525. width="110px"
  526. sortable="custom"
  527. />
  528. <el-table-column
  529. prop="freeGold"
  530. sortable="custom"
  531. label="免费金币"
  532. width="110px"
  533. />
  534. <el-table-column
  535. prop="taskGold"
  536. sortable="custom"
  537. label="任务金币"
  538. width="110px"
  539. />
  540. <!-- 修改prop为taskGold -->
  541. <el-table-column
  542. prop="remark"
  543. label="退款原因"
  544. width="160px"
  545. show-overflow-tooltip
  546. />
  547. <el-table-column prop="adminName" label="提交人" width="100px" />
  548. <el-table-column
  549. prop="createTime"
  550. sortable="custom"
  551. label="提交时间"
  552. width="180px"
  553. >
  554. <template #default="scope">
  555. {{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
  556. </template>
  557. </el-table-column>
  558. </el-table>
  559. </div>
  560. <!-- 分页 -->
  561. <div class="pagination" style="margin-top: 20px">
  562. <el-pagination
  563. background
  564. :page-size="getObj.pageSize"
  565. :page-sizes="[5, 10, 20, 50, 100]"
  566. layout="total, sizes, prev, pager, next, jumper"
  567. :total="total"
  568. @size-change="handlePageSizeChange"
  569. @current-change="handleCurrentChange"
  570. @jump="checkPageNumber"
  571. ></el-pagination>
  572. </div>
  573. </el-card>
  574. </el-col>
  575. </el-row>
  576. </template>
  577. <style scoped>
  578. .status {
  579. display: flex;
  580. }
  581. .head-card {
  582. display: flex;
  583. }
  584. .head-card-element {
  585. margin-right: 20px;
  586. }
  587. .head-card-btn {
  588. margin-left: auto;
  589. }
  590. .pagination {
  591. display: flex;
  592. margin-top: 20px;
  593. }
  594. </style>