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.

626 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 sumGolds = 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. getSelectBy()
  170. }
  171. // 今天
  172. const getToday = function () {
  173. const today = new Date()
  174. const startTime = new Date(
  175. today.getFullYear(),
  176. today.getMonth(),
  177. today.getDate()
  178. )
  179. const endTime = new Date(
  180. today.getFullYear(),
  181. today.getMonth(),
  182. today.getDate() + 1
  183. )
  184. getTime.value = [startTime, endTime]
  185. console.log('getTime', getTime.value)
  186. getSelectBy()
  187. }
  188. // 昨天
  189. const getYesterday = function () {
  190. const yesterday = new Date()
  191. yesterday.setDate(yesterday.getDate() - 1)
  192. const startTime = new Date(
  193. yesterday.getFullYear(),
  194. yesterday.getMonth(),
  195. yesterday.getDate()
  196. )
  197. const endTime = new Date(
  198. yesterday.getFullYear(),
  199. yesterday.getMonth(),
  200. yesterday.getDate() + 1
  201. )
  202. getTime.value = [startTime, endTime]
  203. console.log('getTime', getTime.value)
  204. getSelectBy()
  205. }
  206. // 近7天
  207. const get7Days = function () {
  208. const today = new Date()
  209. const startTime = new Date(
  210. today.getFullYear(),
  211. today.getMonth(),
  212. today.getDate() - 6
  213. )
  214. const endTime = new Date(
  215. today.getFullYear(),
  216. today.getMonth(),
  217. today.getDate() + 1
  218. )
  219. getTime.value = [startTime, endTime]
  220. console.log('getTime', getTime.value)
  221. getSelectBy()
  222. }
  223. //点击标签页
  224. // 设置tab.props.name默认为all
  225. const tabName = ref('all')
  226. const handleClick = function (tab, event) {
  227. if (tab.props.name === 'all') {
  228. adminAll()
  229. } else if (tab.props.name === 'wait') {
  230. adminWait()
  231. } else if (tab.props.name === 'pass') {
  232. adminPass()
  233. } else if (tab.props.name === 'reject') {
  234. adminReject()
  235. }
  236. }
  237. // 获取地区列表的方法
  238. const getMarket = async function () {
  239. try {
  240. // 发送POST请求
  241. const result = await API({ url: '/general/market', data: {} })
  242. // 将响应结果存储到响应式数据中
  243. console.log('请求成功', result)
  244. // 存储地区信息
  245. market.value = result.data
  246. console.log('地区', market.value)
  247. } catch (error) {
  248. console.log('请求失败', error)
  249. }
  250. }
  251. //删除气泡
  252. const delObj = ref({})
  253. const del = function (row) {
  254. delObj.value.detailId = row.detailId
  255. console.log('delObj', delObj.value)
  256. }
  257. // 删除按钮的气泡弹出框确认按钮
  258. const delConfirm = async function () {
  259. try {
  260. console.log('delObj', delObj.value)
  261. // 发送POST请求
  262. const result = await API({
  263. url: '/refund/softDelete?detailId=' + delObj.value.detailId,
  264. data: {}
  265. })
  266. // 将响应结果存储到响应式数据中
  267. console.log('请求成功', result)
  268. // 刷新表格数据
  269. getSelectBy()
  270. } catch (error) {
  271. console.log('请求失败', error)
  272. // 在这里可以处理错误逻辑,比如显示错误提示等
  273. }
  274. }
  275. // 查询商品的接口
  276. const goods = ref([])
  277. const getGoods = async function () {
  278. try {
  279. // 发送POST请求
  280. const result = await request({
  281. url: '/general/goods',
  282. data: {}
  283. })
  284. // 将响应结果存储到响应式数据中
  285. console.log('请求成功product', result)
  286. if (Array.isArray(result.data)) {
  287. // 过滤掉空字符串和 null 值
  288. const validGoods = result.data.filter(item => item && item.trim() !== '');
  289. // 直接使用后端返回的字符串作为 value 和 label
  290. goods.value = validGoods.map(item => ({
  291. value: item,
  292. label: item
  293. }));
  294. }
  295. } catch (error) {
  296. console.log('请求失败', error)
  297. // 在这里可以处理错误逻辑,比如显示错误提示等
  298. }
  299. }
  300. const exportExcel = async function () {
  301. const params = {
  302. text:'',
  303. deptid:'',
  304. sort:1,
  305. field:'',
  306. page:getObj.pageNum,
  307. size:getObj.pageSize
  308. }
  309. const res = await API({ url: '/export/exportRefund', data: params })
  310. if (res.code === 200) {
  311. ElMessage.success('导出成功')
  312. }
  313. }
  314. // 挂载
  315. onMounted(async function () {
  316. await getAdminData()
  317. await getSelectBy()
  318. await getMarket()
  319. await getRefundTypes()
  320. await getGoods()
  321. })
  322. // 新增排序字段和排序方式
  323. const sortField = ref('')
  324. const sortOrder = ref('')
  325. // 处理排序事件
  326. const handleSortChange = (column) => {
  327. console.log('排序字段:', column.prop)
  328. console.log('排序方式:', column.order)
  329. if (column.prop === 'permanentGold') {
  330. sortField.value = 'permanentGold'
  331. } else if (column.prop === 'taskGold') {
  332. sortField.value = 'taskGold'
  333. } else if (column.prop === 'freeGold') {
  334. sortField.value = 'freeGold'
  335. } else if (column.prop === 'createTime') {
  336. sortField.value = 'createTime'
  337. } else if (column.prop === 'auditTime') {
  338. sortField.value = 'auditTime'
  339. } else if (column.prop === 'sumGold') {
  340. sortField.value = 'sumGold'
  341. }
  342. sortOrder.value = column.order === 'ascending' ? 'ASC' : 'DESC'
  343. getSelectBy()
  344. }
  345. const handlePageSizeChange = function (val) {
  346. getObj.value.pageSize = val
  347. getSelectBy()
  348. }
  349. const handleCurrentChange = function (val) {
  350. getObj.value.pageNum = val
  351. getSelectBy()
  352. }
  353. </script>
  354. <template>
  355. <el-row>
  356. <el-col>
  357. <el-card style="margin-bottom: 20px;margin-top:10px">
  358. <el-row style="margin-bottom: 10px">
  359. <el-col :span="5">
  360. <div class="head-card-element">
  361. <el-text class="mx-1" >精网号</el-text>
  362. <el-input
  363. v-model="refundUser.jwcode"
  364. placeholder="请输入精网号"
  365. style="width: 150px"
  366. clearable
  367. />
  368. </div>
  369. </el-col>
  370. <el-col :span="6">
  371. <div class="head-card-element">
  372. <el-text class="mx-1" >退款类型</el-text>
  373. <el-select
  374. v-model="refundUser.refundType"
  375. placeholder="请选择退款类型"
  376. style="width: 180px"
  377. clearable
  378. >
  379. <!-- todo 这需要改-->
  380. <el-option
  381. v-for="item in refundType"
  382. :key="item.value"
  383. :label="item.label"
  384. :value="item.value"
  385. />
  386. </el-select>
  387. </div>
  388. </el-col>
  389. <el-col :span="6">
  390. <div class="head-card-element">
  391. <el-text class="mx-1" >退款商品</el-text>
  392. <el-select
  393. v-model="refundUser.goodsName"
  394. placeholder="请选择退款商品"
  395. style="width: 180px"
  396. clearable
  397. >
  398. <el-option
  399. v-for="item in goods"
  400. :key="item.value"
  401. :label="item.label"
  402. :value="item.value"
  403. />
  404. </el-select>
  405. </div>
  406. </el-col>
  407. <el-col :span="6">
  408. <div class="head-card-element">
  409. <el-text class="mx-1" >所属地区</el-text>
  410. <el-select
  411. v-model="refundUser.market"
  412. placeholder="请选择所属地区"
  413. style="width: 180px"
  414. clearable
  415. >
  416. <el-option
  417. v-for="item in market"
  418. :key="item"
  419. :label="item"
  420. :value="item"
  421. />
  422. </el-select>
  423. </div>
  424. </el-col>
  425. </el-row>
  426. <el-row>
  427. <el-col :span="21">
  428. <div class="head-card-element">
  429. <el-text class="mx-1" >退款时间</el-text>
  430. <el-date-picker
  431. v-model="getTime"
  432. type="datetimerange"
  433. range-separator="至"
  434. start-placeholder="起始时间"
  435. end-placeholder="结束时间"
  436. />
  437. <el-button style="margin-left: 10px" @click="getToday()"
  438. >
  439. </el-button
  440. >
  441. <el-button @click="getYesterday()"></el-button>
  442. <el-button @click="get7Days()">近7天</el-button>
  443. <el-button type="success" @click="reset()">重置</el-button>
  444. <el-button type="primary" @click="search()">查询</el-button>
  445. <el-button type="primary" @click="exportExcel">导出Excel</el-button>
  446. </div>
  447. </el-col>
  448. </el-row>
  449. </el-card>
  450. </el-col>
  451. </el-row>
  452. <el-row>
  453. <el-col>
  454. <el-card>
  455. <div>
  456. 退款金币总数{{ Math.abs(sumGolds) / 100 }}永久金币{{
  457. Math.abs(permanentGolds) / 100
  458. }}免费金币{{ Math.abs(freeGolds) / 100 }}任务金币{{
  459. Math.abs(taskGolds) / 100
  460. }}
  461. </div>
  462. <!-- 设置表格容器的高度和滚动样式 -->
  463. <div style="height: 520px; overflow-y: auto;margin-top:10px">
  464. <el-table
  465. :data="tableData"
  466. style="width: 100%"
  467. @sort-change="handleSortChange"
  468. height="520px"
  469. >
  470. <el-table-column
  471. type="index"
  472. label="序号"
  473. width="100px"
  474. fixed="left"
  475. >
  476. <template #default="scope">
  477. <span>{{
  478. scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
  479. }}</span>
  480. </template>
  481. </el-table-column>
  482. <el-table-column
  483. prop="name"
  484. label="姓名"
  485. fixed="left"
  486. width="100px"
  487. />
  488. <el-table-column
  489. prop="jwcode"
  490. label="精网号"
  491. fixed="left"
  492. width="110px"
  493. />
  494. <el-table-column prop="market" label="所属地区" width="110px" />
  495. <el-table-column prop="goodsName" label="商品名称" width="100px" />
  496. <el-table-column prop="refundType" label="退款类型" width="100px" />
  497. <!-- <el-table-column label="金额总数" width="110px">
  498. <template #default="scope">
  499. {{
  500. scope.row.sumGold
  501. }}
  502. </template>
  503. </el-table-column> -->
  504. <el-table-column
  505. prop="sumGold"
  506. label="金额总数"
  507. width="110px"
  508. sortable="custom"
  509. />
  510. <el-table-column prop="refundModel" label="退款方式" width="110px">
  511. <template #default="scope">
  512. {{ scope.row.refundModel === 0 ? '全部退款' : scope.row.refundModel === 1 ? '部分退款' : '' }}
  513. </template>
  514. </el-table-column>
  515. <el-table-column
  516. prop="permanentGold"
  517. label="永久金币"
  518. width="110px"
  519. sortable="custom"
  520. />
  521. <el-table-column
  522. prop="freeGold"
  523. sortable="custom"
  524. label="免费金币"
  525. width="110px"
  526. />
  527. <el-table-column
  528. prop="taskGold"
  529. sortable="custom"
  530. label="任务金币"
  531. width="110px"
  532. />
  533. <!-- 修改prop为taskGold -->
  534. <el-table-column
  535. prop="remark"
  536. label="退款原因"
  537. width="160px"
  538. show-overflow-tooltip
  539. />
  540. <el-table-column prop="adminName" label="提交人" width="100px" />
  541. <el-table-column
  542. prop="createTime"
  543. sortable="custom"
  544. label="提交时间"
  545. width="180px"
  546. >
  547. <template #default="scope">
  548. {{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
  549. </template>
  550. </el-table-column>
  551. </el-table>
  552. </div>
  553. <!-- 分页 -->
  554. <div class="pagination" style="margin-top: 20px">
  555. <el-pagination
  556. background
  557. :page-size="getObj.pageSize"
  558. :page-sizes="[5, 10, 20, 50, 100]"
  559. layout="total, sizes, prev, pager, next, jumper"
  560. :total="total"
  561. @size-change="handlePageSizeChange"
  562. @current-change="handleCurrentChange"
  563. @jump="checkPageNumber"
  564. ></el-pagination>
  565. </div>
  566. </el-card>
  567. </el-col>
  568. </el-row>
  569. </template>
  570. <style scoped>
  571. .status {
  572. display: flex;
  573. }
  574. .head-card {
  575. display: flex;
  576. }
  577. .head-card-element {
  578. margin-right: 20px;
  579. }
  580. .head-card-btn {
  581. margin-left: auto;
  582. }
  583. .pagination {
  584. display: flex;
  585. margin-top: 20px;
  586. }
  587. </style>