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

17 hours ago
17 hours ago
17 hours ago
17 hours ago
17 hours ago
17 hours ago
17 hours ago
17 hours ago
17 hours ago
17 hours ago
  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" size="large">精网号</el-text>
  362. <el-input
  363. v-model="refundUser.jwcode"
  364. placeholder="请输入精网号"
  365. size="large"
  366. style="width: 150px"
  367. clearable
  368. />
  369. </div>
  370. </el-col>
  371. <el-col :span="6">
  372. <div class="head-card-element">
  373. <el-text class="mx-1" size="large">退款类型</el-text>
  374. <el-select
  375. v-model="refundUser.refundType"
  376. placeholder="请选择退款类型"
  377. size="large"
  378. style="width: 180px"
  379. clearable
  380. >
  381. <!-- todo 这需要改-->
  382. <el-option
  383. v-for="item in refundType"
  384. :key="item.value"
  385. :label="item.label"
  386. :value="item.value"
  387. />
  388. </el-select>
  389. </div>
  390. </el-col>
  391. <el-col :span="6">
  392. <div class="head-card-element">
  393. <el-text class="mx-1" size="large">退款商品</el-text>
  394. <el-select
  395. v-model="refundUser.goodsName"
  396. placeholder="请选择退款商品"
  397. size="large"
  398. style="width: 180px"
  399. clearable
  400. >
  401. <el-option
  402. v-for="item in goods"
  403. :key="item.value"
  404. :label="item.label"
  405. :value="item.value"
  406. />
  407. </el-select>
  408. </div>
  409. </el-col>
  410. <el-col :span="6">
  411. <div class="head-card-element">
  412. <el-text class="mx-1" size="large">所属地区</el-text>
  413. <el-select
  414. v-model="refundUser.market"
  415. placeholder="请选择所属地区"
  416. size="large"
  417. style="width: 180px"
  418. clearable
  419. >
  420. <el-option
  421. v-for="item in market"
  422. :key="item"
  423. :label="item"
  424. :value="item"
  425. />
  426. </el-select>
  427. </div>
  428. </el-col>
  429. </el-row>
  430. <el-row>
  431. <el-col :span="21">
  432. <div class="head-card-element">
  433. <el-text class="mx-1" size="large">退款时间</el-text>
  434. <el-date-picker
  435. v-model="getTime"
  436. type="datetimerange"
  437. range-separator="至"
  438. start-placeholder="起始时间"
  439. end-placeholder="结束时间"
  440. />
  441. <el-button style="margin-left: 10px" @click="getToday()"
  442. >
  443. </el-button
  444. >
  445. <el-button @click="getYesterday()"></el-button>
  446. <el-button @click="get7Days()">近7天</el-button>
  447. <el-button type="success" @click="reset()">重置</el-button>
  448. <el-button type="primary" @click="search()">查询</el-button>
  449. <el-button type="primary" @click="exportExcel">导出Excel</el-button>
  450. </div>
  451. </el-col>
  452. </el-row>
  453. </el-card>
  454. </el-col>
  455. </el-row>
  456. <el-row>
  457. <el-col>
  458. <el-card>
  459. <div>
  460. 退款金币总数{{ Math.abs(sumGolds) / 100 }}永久金币{{
  461. Math.abs(permanentGolds) / 100
  462. }}免费金币{{ Math.abs(freeGolds) / 100 }}任务金币{{
  463. Math.abs(taskGolds) / 100
  464. }}
  465. </div>
  466. <!-- 设置表格容器的高度和滚动样式 -->
  467. <div style="height: 520px; overflow-y: auto;margin-top:10px">
  468. <el-table
  469. :data="tableData"
  470. style="width: 100%"
  471. @sort-change="handleSortChange"
  472. height="520px"
  473. >
  474. <el-table-column
  475. type="index"
  476. label="序号"
  477. width="100px"
  478. fixed="left"
  479. >
  480. <template #default="scope">
  481. <span>{{
  482. scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
  483. }}</span>
  484. </template>
  485. </el-table-column>
  486. <el-table-column
  487. prop="name"
  488. label="姓名"
  489. fixed="left"
  490. width="100px"
  491. />
  492. <el-table-column
  493. prop="jwcode"
  494. label="精网号"
  495. fixed="left"
  496. width="110px"
  497. />
  498. <el-table-column prop="market" label="所属地区" width="110px" />
  499. <el-table-column prop="goodsName" label="商品名称" width="100px" />
  500. <el-table-column prop="refundType" label="退款类型" width="100px" />
  501. <!-- <el-table-column label="金额总数" width="110px">
  502. <template #default="scope">
  503. {{
  504. scope.row.sumGold
  505. }}
  506. </template>
  507. </el-table-column> -->
  508. <el-table-column
  509. prop="sumGold"
  510. label="金额总数"
  511. width="110px"
  512. sortable="custom"
  513. />
  514. <el-table-column prop="refundModel" label="退款方式" width="110px">
  515. <template #default="scope">
  516. {{ scope.row.refundModel === 0 ? '全部退款' : scope.row.refundModel === 1 ? '部分退款' : '' }}
  517. </template>
  518. </el-table-column>
  519. <el-table-column
  520. prop="permanentGold"
  521. label="永久金币"
  522. width="110px"
  523. sortable="custom"
  524. />
  525. <el-table-column
  526. prop="freeGold"
  527. sortable="custom"
  528. label="免费金币"
  529. width="110px"
  530. />
  531. <el-table-column
  532. prop="taskGold"
  533. sortable="custom"
  534. label="任务金币"
  535. width="110px"
  536. />
  537. <!-- 修改prop为taskGold -->
  538. <el-table-column
  539. prop="remark"
  540. label="退款原因"
  541. width="160px"
  542. show-overflow-tooltip
  543. />
  544. <el-table-column prop="adminName" label="提交人" width="100px" />
  545. <el-table-column
  546. prop="createTime"
  547. sortable="custom"
  548. label="提交时间"
  549. width="180px"
  550. >
  551. <template #default="scope">
  552. {{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
  553. </template>
  554. </el-table-column>
  555. </el-table>
  556. </div>
  557. <!-- 分页 -->
  558. <div class="pagination" style="margin-top: 20px">
  559. <el-pagination
  560. background
  561. :page-size="getObj.pageSize"
  562. :page-sizes="[5, 10, 20, 50, 100]"
  563. layout="total, sizes, prev, pager, next, jumper"
  564. :total="total"
  565. @size-change="handlePageSizeChange"
  566. @current-change="handleCurrentChange"
  567. @jump="checkPageNumber"
  568. ></el-pagination>
  569. </div>
  570. </el-card>
  571. </el-col>
  572. </el-row>
  573. </template>
  574. <style scoped>
  575. .status {
  576. display: flex;
  577. }
  578. .head-card {
  579. display: flex;
  580. }
  581. .head-card-element {
  582. margin-right: 20px;
  583. }
  584. .head-card-btn {
  585. margin-left: auto;
  586. }
  587. .pagination {
  588. display: flex;
  589. margin-top: 20px;
  590. }
  591. </style>