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.

525 lines
17 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
  1. <template>
  2. <el-row>
  3. <el-col>
  4. <div>
  5. <el-card style="margin-bottom: 5px">
  6. <el-row style="margin-bottom: 5px">
  7. <el-col :span="6">
  8. <el-text size="large">精网号</el-text>
  9. <el-input v-model="searchForm.jwcode" placeholder="请输入精网号" style="width: 240px" clearable />
  10. </el-col>
  11. <el-col :span="6">
  12. <el-text size="large">商品名</el-text>
  13. <el-select v-model="searchForm.goodsName" placeholder="请输入商品名" style="width: 240px" clearable>
  14. <el-option v-for="item in refundGoodsOptions" :key="item" :label="item" :value="item"> </el-option>
  15. </el-select>
  16. </el-col>
  17. <el-col :span="6">
  18. <el-text size="large">退款方式</el-text>
  19. <el-select v-model="searchForm.refundModel" placeholder="请选择" style="width: 240px" clearable>
  20. <el-option label="全部退款" value="0" />
  21. <el-option label="部分退款" value="1" />
  22. </el-select>
  23. </el-col>
  24. <el-col :span="6">
  25. <el-text size="large">所属地区</el-text>
  26. <el-select v-model="searchForm.market" placeholder="请选择" style="width: 240px" clearable>
  27. <el-option v-for="item in marketOptions" :key="item" :label="item" :value="item" />
  28. </el-select>
  29. </el-col>
  30. </el-row>
  31. <el-row>
  32. <el-col :span="15">
  33. <div class="time-controls">
  34. <div class="time-group">
  35. <el-text size="large">提交时间</el-text>
  36. <el-date-picker v-model="dateRange" type="datetimerange" range-separator="" start-placeholder="开始时间"
  37. end-placeholder="结束时间" style="width: 400px" />
  38. <el-button @click="getToday()" style="margin-left: 10px"></el-button>
  39. <el-button @click="getYesterday()" style="margin-left: 10px"></el-button>
  40. <el-button @click="get7Days()" style="margin-left: 10px">近7天</el-button>
  41. <el-button type="success" @click="resetSearch">重置</el-button>
  42. <el-button type="primary" @click="handleSearch">查询</el-button>
  43. </div>
  44. </div>
  45. </el-col>
  46. </el-row>
  47. </el-card>
  48. </div>
  49. </el-col>
  50. </el-row>
  51. <el-card>
  52. <el-tabs v-model="activeName" type="card" @tab-click="handleClick">
  53. <el-tab-pane label="待审核" name="wait"></el-tab-pane>
  54. <el-tab-pane label="已通过" name="pass"></el-tab-pane>
  55. <el-tab-pane label="已驳回" name="reject"></el-tab-pane>
  56. </el-tabs>
  57. <div>
  58. 总条数{{ stats.totalNum }}&nbsp;&nbsp;&nbsp;&nbsp;
  59. 退款总金币数{{ (stats.permanentGolds + stats.freeGolds + stats.taskGolds).toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
  60. 永久金币{{ stats.permanentGolds.toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
  61. 免费金币{{ stats.freeGolds.toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
  62. 任务金币{{ stats.taskGolds.toFixed(2) }}金币
  63. </div>
  64. <el-table :data="tableData" height="540px" @sort-change="handleSortChange">
  65. <el-table-column type="index" label="序号" width="60" />
  66. <el-table-column prop="name" label="姓名" width="120" />
  67. <el-table-column prop="jwcode" label="精网号" width="120" />
  68. <el-table-column prop="market" label="所属地区" width="120" />
  69. <el-table-column prop="refundType" label="退款类型" width="120" />
  70. <el-table-column prop="refundModel" label="退款方式" width="120">
  71. <template #default="{ row }">
  72. {{ row.refundModel === 0 ? '全部退款' : '部分退款' }}
  73. </template>
  74. </el-table-column>
  75. <el-table-column prop="goodsName" label="退款商品" width="120" show-overflow-tooltip />
  76. <el-table-column prop="sumGold" label="退款金额" width="120" sortable="custom">
  77. <template #default="{ row }">
  78. {{ row.sumGold / 100 }}
  79. </template>
  80. </el-table-column>
  81. <el-table-column prop="permanentGold" label="永久金币" width="120" sortable="custom">
  82. <template #default="{ row }">
  83. {{ row.permanentGold / 100 }}
  84. </template>
  85. </el-table-column>
  86. <el-table-column prop="freeGold" label="免费金币" width="120" sortable="custom">
  87. <template #default="{ row }">
  88. {{ (row.freeJune + row.freeDecember) / 100 }}
  89. </template>
  90. </el-table-column>
  91. <el-table-column prop="taskGold" label="任务金币" width="120" sortable="custom">
  92. <template #default="{ row }">
  93. {{ row.taskGold / 100 }}
  94. </template>
  95. </el-table-column>
  96. <el-table-column prop="remark" label="备注" width="150" show-overflow-tooltip /><!-- 当内容过长被隐藏时显示 tooltip -->
  97. <el-table-column prop="adminName" label="提交人" width="120" />
  98. <el-table-column v-if="checkTab === 'reject'" prop="rejectReason" label="驳回理由" width="150"
  99. show-overflow-tooltip />
  100. <el-table-column v-if="checkTab !== 'pending'" prop="auditName" label="审核人" width="120" />
  101. <el-table-column prop="createTime" label="提交时间" width="180" sortable="custom">
  102. <template #default="{ row }">
  103. {{ moment(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
  104. </template>
  105. </el-table-column>
  106. <el-table-column v-if="checkTab !== 'pending'" prop="auditTime" label="审核时间" width="180" sortable="custom">
  107. <template #default="{ row }">
  108. {{ row.auditTime ? moment(row.auditTime).format('YYYY-MM-DD HH:mm:ss') : '--' }}
  109. </template>
  110. </el-table-column>
  111. <el-table-column v-if="checkTab === 'pending'" fixed="right" prop="operation" label="操作" width="150px">
  112. <template #default="scope">
  113. <div class="operation">
  114. <el-popconfirm title="确定要通过此条记录吗?" @confirm="handleApprove(scope.row)">
  115. <template #reference>
  116. <el-button :disabled="scope.row.auditStatus === 1 || scope.row.auditStatus === 2" type="primary" text>
  117. 通过
  118. </el-button>
  119. </template>
  120. </el-popconfirm>
  121. <el-button :disabled="scope.row.auditStatus === 1 || scope.row.auditStatus === 2" type="primary" text
  122. @click="showRejectDialog(scope.row)">
  123. 驳回
  124. </el-button>
  125. </div>
  126. </template>
  127. </el-table-column>
  128. </el-table>
  129. <el-pagination class="pagination" v-model:current-page="pagination.pageNum" v-model:page-size="pagination.pageSize"
  130. layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" @size-change="handlePageSizeChange"
  131. @current-change="handleCurrentChange"></el-pagination>
  132. </el-card>
  133. <el-dialog v-model="rejectDialogVisible" title="驳回理由" width="500px">
  134. <el-form>
  135. <el-form-item label="驳回理由">
  136. <el-input v-model="rejectReason" type="textarea" :rows="4" placeholder="请输入驳回理由" maxlength="200"
  137. show-word-limit />
  138. </el-form-item>
  139. </el-form>
  140. <template #footer>
  141. <span class="dialog-footer">
  142. <el-button @click="rejectDialogVisible = false">取消</el-button>
  143. <el-button type="primary" @click="handleReject">确定</el-button>
  144. </span>
  145. </template>
  146. </el-dialog>
  147. </template>
  148. <script setup>
  149. import { onMounted, reactive, ref } from 'vue'
  150. import { ElMessage } from 'element-plus'
  151. import API from '@/util/http'
  152. import moment from 'moment'
  153. const scopeValue = ref(null) // 当前行
  154. const rejectDialogVisible = ref(false) // 驳回对话框显示状态
  155. const rejectReason = ref('') // 驳回理由
  156. // 状态常量
  157. const STATUS = {
  158. PENDING: 0, // 待审核
  159. APPROVED: 1, // 通过
  160. REJECTED: 2 // 驳回
  161. }
  162. // 搜索表单数据
  163. const searchForm = ref({
  164. jwcode: '',
  165. refundModel: '',
  166. goodsName: '',
  167. market: '',
  168. startTime: '',
  169. endTime: '',
  170. auditStatus: '0'
  171. })
  172. const checkTab = ref('pending') // 能否不用STATUS常量,0是待审批,1是已通过,2是驳回,参数status需要Integer
  173. const dateRange = ref([])
  174. const pagination = ref({
  175. pageNum: 1,
  176. pageSize: 50,
  177. total: 0
  178. })
  179. const tableData = ref([])
  180. const marketOptions = ref([])
  181. const refundGoodsOptions = ref([])
  182. const adminInfo = ref({})
  183. // 统计合计数
  184. const stats = ref({
  185. totalNum: 0,
  186. totalCoins: 0,
  187. permanentGolds: 0,
  188. freeGolds: 0,
  189. taskGolds: 0
  190. })
  191. // 驳回弹出框
  192. const rejectVisible = ref(false)
  193. // 驳回对象
  194. const rejectObj = ref({})
  195. // 通过对象
  196. const passObj = ref({})
  197. // 标签页默认是待审批
  198. const activeName = ref('wait')
  199. // 表单验证ref
  200. const Ref = ref(null)
  201. // 新增排序字段和排序方式
  202. const sortField = ref('')
  203. const sortOrder = ref('')
  204. // 表单验证
  205. const rules = reactive({
  206. reason: [{ required: true, message: '请输入驳回理由', trigger: 'blur' }]
  207. })
  208. const getAdminData = async function () {
  209. try {
  210. const result = await API({ url: '/admin/userinfo', data: {} })
  211. adminInfo.value = result
  212. console.log('请求成功', result)
  213. console.log('用户信息', adminInfo.value)
  214. } catch (error) {
  215. console.log('请求失败', error)
  216. }
  217. }
  218. const handlePageSizeChange = function (val) {
  219. pagination.value.pageSize = val
  220. get()
  221. console.log('aaaaaaaaaaaaaaaaaa这是pageSize改变调用')
  222. }
  223. const handleCurrentChange = function (val) {
  224. pagination.value.pageNum = val
  225. get()
  226. console.log('aaaaaaaaaaaaaaa这是pageNum改变调用')
  227. }
  228. // 处理排序事件
  229. const handleSortChange = (column) => {
  230. if (column.prop === 'sumGold') {
  231. sortField.value = 'sum_gold'
  232. } else if (column.prop === 'permanentGold') {
  233. sortField.value = 'permanent_gold'
  234. } else if (column.prop === 'freeGold') {
  235. sortField.value = 'free_gold'
  236. }else if (column.prop === 'taskGold') {
  237. sortField.value = 'task_gold'
  238. }else if (column.prop === 'createTime') {
  239. sortField.value = 'create_time'
  240. }else if (column.prop === 'auditTime') {
  241. sortField.value = 'audit_time'
  242. } else {
  243. sortField.value = ''
  244. }
  245. sortOrder.value = column.order === 'ascending' ? 'asc' : 'desc'
  246. console.log('排序字段:', sortField.value)
  247. console.log('排序方式:', sortOrder.value)
  248. get()
  249. }
  250. // 显示驳回对话框
  251. const showRejectDialog = (row) => {
  252. scopeValue.value = row
  253. rejectReason.value = ''
  254. rejectDialogVisible.value = true
  255. }
  256. // 查地区
  257. const getmarkets = async () => {
  258. try {
  259. const result = await API({ url: 'general/market' })
  260. marketOptions.value = result.data || []
  261. } catch (error) {
  262. console.error('获取地区列表失败', error)
  263. }
  264. }
  265. // 查商品名
  266. const getRefundGoods = async () => {
  267. try {
  268. const res = await API({ url: '/general/goods' })
  269. refundGoodsOptions.value = res.data || []
  270. } catch (error) {
  271. console.error('获取商品列表失败', error)
  272. }
  273. }
  274. // 搜索方法
  275. const get = async function (val) {
  276. try {
  277. if (typeof val === 'number') {
  278. pagination.value.pageNum = val
  279. }
  280. if (dateRange.value && dateRange.value.length === 2) {
  281. searchForm.value.startTime = moment(dateRange.value[0]).format('YYYY-MM-DD HH:mm:ss')
  282. searchForm.value.endTime = moment(dateRange.value[1]).format('YYYY-MM-DD HH:mm:ss')
  283. } else {
  284. searchForm.value.startTime = ''
  285. searchForm.value.endTime = ''
  286. }
  287. const params = {
  288. pageNum: pagination.value.pageNum,
  289. pageSize: pagination.value.pageSize,
  290. refundAudit: {
  291. ...searchForm.value,
  292. sortField:sortField.value,
  293. sortOrder:sortOrder.value
  294. }
  295. }
  296. console.log('看看传给后端的参数:', params)
  297. const res = await API({ url: '/audit/selectRefund', data: params })
  298. tableData.value = res.list || []
  299. pagination.value.total = res.total || 0
  300. console.log('查全部的total', pagination.value.total, res.total)
  301. } catch (error) {
  302. console.error('获取数据失败', error)
  303. }
  304. }
  305. // 通过
  306. const handleApprove = async (row) => {
  307. try {
  308. const params = {
  309. orderCode: row.orderCode,
  310. auditId: adminInfo.value.id,
  311. action: 1,// action的1是通过,2是驳回
  312. rejectReason: ''
  313. }
  314. await API({ url: '/audit/audit', data: params })// 试一下能不能发
  315. ElMessage.success('审核通过成功')
  316. get()
  317. getStats()
  318. console.log('aaaaaaaaaa这是通过调用')
  319. } catch (error) {
  320. console.error('审核通过失败', error)
  321. ElMessage.error('操作失败')
  322. }
  323. }
  324. // 处理驳回操作
  325. const handleReject = async () => {
  326. if (!rejectReason.value.trim()) {
  327. ElMessage.warning('请输入驳回理由')
  328. return
  329. }
  330. try {
  331. const params = {
  332. orderCode: scopeValue.value.orderCode,
  333. auditId: adminInfo.value.id,
  334. action: 2,
  335. rejectReason: rejectReason.value
  336. }
  337. await API({ url: '/audit/audit', data: params })
  338. ElMessage.success('驳回成功')
  339. rejectDialogVisible.value = false
  340. get()
  341. getStats()
  342. console.log('aaaaaaaaaa这是驳回调用',params)
  343. } catch (error) {
  344. console.error('驳回失败', error)
  345. ElMessage.error('操作失败')
  346. }
  347. }
  348. const getStats = async () => {
  349. try {
  350. const params = {
  351. pageNum: pagination.value.pageNum,
  352. pageSize: pagination.value.pageSize,
  353. refundAudit: {
  354. ...searchForm.value
  355. }
  356. }
  357. const res = await API({
  358. url: '/audit/sumRefundGold',
  359. data: params
  360. })
  361. stats.value.totalNum = res.totalNum
  362. stats.value.permanentGolds = res.permanentGolds / 100
  363. stats.value.freeGolds = res.freeGolds / 100
  364. stats.value.taskGolds = res.taskGolds / 100
  365. console.log('see see stats和搜索对象', stats.value, params)
  366. } catch (error) {
  367. console.log('请求失败', error)
  368. }
  369. }
  370. // 搜索
  371. const handleSearch = function () {
  372. // pagination.value.pageNum = 1
  373. get()
  374. getStats()
  375. console.log('aaaaaaaaaa这是搜索按钮调用')
  376. }
  377. // 重置
  378. const resetSearch = function () {
  379. searchForm.value = {
  380. jwcode: '',
  381. refundType: '',
  382. goodsName: '',
  383. market: '',
  384. startTime: '',
  385. endTime: '',
  386. sortField:'',
  387. sortOrder:''
  388. }
  389. dateRange.value = []
  390. }
  391. // 今天
  392. const getToday = function () {
  393. const today = new Date()
  394. const startTime = new Date(today.getFullYear(), today.getMonth(), today.getDate())
  395. const endTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1)
  396. dateRange.value = [startTime, endTime]
  397. console.log('dateRange', dateRange.value)
  398. get()
  399. getStats()
  400. console.log('aaaaaaaaaa这是今天调用')
  401. }
  402. // 昨天
  403. const getYesterday = function () {
  404. const yesterday = new Date()
  405. yesterday.setDate(yesterday.getDate() - 1)
  406. const startTime = new Date(yesterday.getFullYear(), yesterday.getMonth(), yesterday.getDate())
  407. const endTime = new Date(yesterday.getFullYear(), yesterday.getMonth(), yesterday.getDate() + 1)
  408. dateRange.value = [startTime, endTime]
  409. console.log('dateRange', dateRange.value)
  410. get()
  411. getStats()
  412. console.log('aaaaaaaaaa这是昨天调用')
  413. }
  414. // 近7天
  415. const get7Days = function () {
  416. const today = new Date()
  417. const startTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 6)
  418. const endTime = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1)
  419. dateRange.value = [startTime, endTime]
  420. console.log('dateRange', dateRange.value)
  421. get()
  422. getStats()
  423. console.log('aaaaaaaaaa这是近七天调用')
  424. }
  425. // 切换标签页
  426. const handleClick = function (tab, event) {
  427. activeName.value = tab.props.name
  428. if (tab.props.name === 'wait') {
  429. adminWait()
  430. } else if (tab.props.name === 'pass') {
  431. adminPass()
  432. } else if (tab.props.name === 'reject') {
  433. adminReject()
  434. }
  435. }
  436. // 当前状态
  437. const getCurrentStatus = () => {
  438. switch (activeName.value) {
  439. case 'wait':
  440. return STATUS.PENDING
  441. case 'pass':
  442. return STATUS.APPROVED
  443. case 'reject':
  444. return STATUS.REJECTED
  445. default:
  446. return ''
  447. }
  448. }
  449. // 待审核充值明细
  450. const adminWait = async function () {
  451. checkTab.value = 'pending'
  452. searchForm.value.auditStatus = STATUS.PENDING
  453. // pagination.value.pageNum = 1
  454. await get()
  455. await getStats()
  456. console.log('aaaaaaaaaaaaaaaaa看看checkTab,这是点击待审核调用', checkTab.value)
  457. }
  458. // 已通过充值明细
  459. const adminPass = async function () {
  460. checkTab.value = 'pass'
  461. searchForm.value.auditStatus = STATUS.APPROVED
  462. // pagination.value.pageNum = 1
  463. await get()
  464. await getStats()
  465. console.log('aaaaaaaaaaaaaaaaaaaaa看看checkTab,这是点击已通过调用', checkTab.value)
  466. }
  467. // 已驳回充值明细
  468. const adminReject = async function () {
  469. checkTab.value = 'reject'
  470. searchForm.value.auditStatus = STATUS.REJECTED
  471. // pagination.value.pageNum = 1
  472. await get()
  473. await getStats()
  474. console.log('aaaaaaaaaaaaaaaaaa看看checkTab,这是点击已驳回调用', checkTab.value)
  475. }
  476. onMounted(async () => {
  477. await getAdminData()
  478. getRefundGoods()
  479. getmarkets()
  480. await get()
  481. await getStats()
  482. console.log('aaaaaaaaaa这是挂载后调用')
  483. })
  484. </script>
  485. <style scoped>
  486. .pagination {
  487. display: flex;
  488. }
  489. .operation {
  490. display: flex;
  491. }
  492. .time-controls {
  493. display: flex;
  494. align-items: center;
  495. }
  496. .time-group {
  497. display: flex;
  498. align-items: center;
  499. gap: 10px;
  500. }
  501. </style>