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.

408 lines
13 KiB

3 weeks ago
3 weeks ago
2 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
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. <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.refundType" placeholder="请选择" style="width: 240px" clearable>
  14. <el-option label="商品退款" value="商品退款" />
  15. <el-option label="退点啥??" value="退点啥??" />
  16. </el-select>
  17. </el-col>
  18. <el-col :span="6">
  19. <el-text size="large">所属地区</el-text>
  20. <el-select v-model="searchForm.area" placeholder="请选择" style="width: 240px" clearable>
  21. <el-option v-for="item in areaOptions" :key="item" :label="item" :value="item" />
  22. </el-select>
  23. </el-col>
  24. </el-row>
  25. <el-row>
  26. <el-col :span="12">
  27. <el-text size="large">退款时间</el-text>
  28. <el-date-picker v-model="dateRange" type="datetimerange" range-separator="" start-placeholder="开始日期"
  29. end-placeholder="结束日期" style="width: 400px" />
  30. <el-button @click="getToday()" style="margin-left: 10px"></el-button>
  31. <el-button @click="getYesterday()" style="margin-left: 10px"></el-button>
  32. <el-button @click="get7Days()" style="margin-left: 10px">近7天</el-button>
  33. </el-col>
  34. <el-col :span="6">
  35. <el-button type="success" @click="resetSearch">重置</el-button>
  36. <el-button type="primary" @click="handleSearch">查询</el-button>
  37. </el-col>
  38. </el-row>
  39. </el-card>
  40. </div>
  41. </el-col>
  42. </el-row>
  43. <el-card>
  44. <el-tabs v-model="checkTab" @tab-change="switchTab">
  45. <el-tab-pane label="待审核" name="1" />
  46. <el-tab-pane label="已通过" name="2" />
  47. <el-tab-pane label="已驳回" name="3" />
  48. </el-tabs>
  49. <div class="stats-info">
  50. 退款记录条数: {{ stats.totalItems }}&nbsp;&nbsp;&nbsp;&nbsp;
  51. 退款金币总数: {{ stats.totalCoins }}&nbsp;&nbsp;&nbsp;&nbsp;
  52. 永久金币: {{ stats.permanentCoins }}&nbsp;&nbsp;&nbsp;&nbsp;
  53. 免费金币: {{ stats.freeCoins }}&nbsp;&nbsp;&nbsp;&nbsp;
  54. 任务金币: {{ stats.taskCoins }}
  55. </div>
  56. <el-table :data="tableData" height="540px" @sort-change="handleSortChange">
  57. <el-table-column type="index" label="序号" width="60" />
  58. <el-table-column prop="username" label="姓名" width="120" />
  59. <el-table-column prop="jwcode" label="精网号" width="120" />
  60. <el-table-column prop="area" label="所属地区" width="120" />
  61. <el-table-column prop="refundType" label="退款类型" width="120" />
  62. <el-table-column prop="refundMethod" label="退款方式" width="120" />
  63. <el-table-column prop="refundGoods" label="退款商品" width="120" />
  64. <el-table-column prop="refundAmount" label="退款金额" width="120" sortable="custom">
  65. <template #default="{ row }">
  66. {{ row.rechargeCoin + row.freeCoin + row.taskCoin }}
  67. </template>
  68. </el-table-column>
  69. <el-table-column prop="rechargeCoin" label="永久金币" width="120" sortable="custom" />
  70. <el-table-column prop="freeCoin" label="免费金币" width="120" sortable="custom" />
  71. <el-table-column prop="taskCoin" label="任务金币" width="120" sortable="custom" />
  72. <el-table-column prop="remark" label="备注" width="150" show-overflow-tooltip /><!-- 当内容过长被隐藏时显示 tooltip -->
  73. <el-table-column prop="adminName" label="提交人" width="120" />
  74. <el-table-column v-if="checkTab === 'rejected'" prop="reason" label="驳回理由" width="150" show-overflow-tooltip />
  75. <el-table-column v-if="checkTab !== 'pending'" prop="auditName" label="审核人" width="120" />
  76. <el-table-column prop="createTime" label="提交时间" width="180" sortable="custom">
  77. <template #default="{ row }">
  78. {{ moment(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
  79. </template>
  80. </el-table-column>
  81. <el-table-column v-if="checkTab !== 'pending'" prop="auditTime" label="审核时间" width="180" sortable="custom">
  82. <template #default="{ row }">
  83. {{ row.auditTime ? moment(row.auditTime).format('YYYY-MM-DD HH:mm:ss') : '--' }}
  84. </template>
  85. </el-table-column>
  86. <el-table-column label="操作" width="150" fixed="right" v-if="checkTab === 'pending'">
  87. <template #default="{ row }">
  88. <el-button type="success" size="small" @click="handleAction('approve', row)">通过</el-button>
  89. <el-button type="danger" size="small" @click="handleAction('reject', row)">驳回</el-button>
  90. </template>
  91. </el-table-column>
  92. </el-table>
  93. <el-pagination v-model:current-page="pagination.pageNum" v-model:page-size="pagination.pageSize"
  94. :total="pagination.total" :page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper"
  95. @size-change="handlePagination('size', $event)" @current-change="handlePagination('page', $event)"
  96. class="pagination" />
  97. </el-card>
  98. <!-- 驳回弹出框 -->
  99. <el-dialog v-model="rejectDialog" title="驳回理由" width="500px">
  100. <el-form :model="rejectForm">
  101. <el-form-item label="驳回理由">
  102. <el-input v-model="rejectForm.reason" type="textarea" :rows="4" placeholder="请输入驳回理由" maxlength="200"
  103. show-word-limit />
  104. </el-form-item>
  105. </el-form>
  106. <template #footer>
  107. <el-button @click="rejectDialog=false">取消</el-button>
  108. <el-button type="primary" @click="rejectRefund">确定</el-button>
  109. </template>
  110. </el-dialog>
  111. </template>
  112. <script setup>
  113. import { ref, onMounted } from 'vue'
  114. import request from '@/util/http'
  115. import { ElMessage } from 'element-plus'
  116. import moment from 'moment'
  117. // 状态常量
  118. const STATUS = {
  119. PENDING: 0,// 待审核
  120. APPROVED: 1,// 通过
  121. REJECTED: 2 // 驳回
  122. }
  123. // 搜索表单
  124. const searchForm = ref({
  125. jwcode: '',
  126. refundType: '',
  127. refundGoods: '',
  128. area: '',
  129. startTime: '',
  130. endTime: ''
  131. })
  132. const checkTab = ref(1) // 能否不用STATUS常量,1是待审批,2是已通过,3是驳回,参数status需要Integer
  133. const dateRange = ref([])
  134. const pagination = ref({
  135. pageNum: 1,
  136. pageSize: 50,
  137. total: 0
  138. })
  139. const tableData = ref([])
  140. const productOptions = ref([])
  141. const areaOptions = ref([])
  142. const adminInfo = ref({})
  143. const stats = ref({
  144. totalItems: 0,
  145. totalCoins: 0,
  146. permanentCoins: 0,
  147. freeCoins: 0,
  148. taskCoins: 0
  149. })
  150. // 驳回
  151. const rejectDialog = ref(false) // 驳回对话框
  152. const rejectForm = ref({ reason: '' })
  153. const currentRecord = ref(null)
  154. // 获取用户信息
  155. const fetchAdminInfo = async () => {
  156. try {
  157. const res = await request({ url: '/admin/userinfo' })
  158. adminInfo.value = res
  159. if (res.area !== '总部') {
  160. searchForm.value.area = res.area
  161. }
  162. } catch (error) {
  163. console.error('获取用户信息失败', error)
  164. }
  165. }
  166. // 查商品
  167. const fetchProducts = async () => {
  168. try {
  169. const res = await request({ url: '/product' })
  170. productOptions.value = res.data || []
  171. } catch (error) {
  172. console.error('获取商品列表失败', error)
  173. }
  174. }
  175. // 查地区
  176. const fetchAreas = async () => {
  177. try {
  178. const result = await request({ url: 'http://18.143.76.3:10704/general/market' })
  179. areaOptions.value = result.data || []
  180. } catch (error) {
  181. console.error('获取地区列表失败', error)
  182. }
  183. }
  184. const get = async () => {
  185. console.log('===========================================' + checkTab.value)
  186. try {
  187. const params = {
  188. pageNum: pagination.value.pageNum,
  189. pageSize: pagination.value.pageSize,
  190. detail: {
  191. ...searchForm.value,
  192. status: getCurrentStatus()
  193. }
  194. }
  195. // 时间格式化
  196. if (dateRange.value?.length === 2) {
  197. params.detail.startTime = moment(dateRange.value[0]).format('YYYY-MM-DD HH:mm:ss')
  198. params.detail.endTime = moment(dateRange.value[1]).format('YYYY-MM-DD HH:mm:ss')
  199. }
  200. const res = await request({
  201. url: '/audit/audit/refund',
  202. data: params
  203. })
  204. tableData.value = res.data?.list || []
  205. pagination.value.total = res.data?.total || 0
  206. fetchStats()
  207. } catch (error) {
  208. console.error('获取数据失败', error)
  209. }
  210. }
  211. const fetchStats = async () => {
  212. try {
  213. const params = { ...searchForm.value, status: getCurrentStatus() }
  214. const res = await request({
  215. url: '/refund/RefundA',
  216. data: params
  217. })
  218. if (res.data) {
  219. const data = res.data
  220. stats.value = {
  221. totalItems: data.raudit || 0,
  222. totalCoins: data.sumRaudit || 0,
  223. permanentCoins: data.permanentCoins || 0,
  224. freeCoins: data.freeCoins || 0,
  225. taskCoins: data.taskCoins || 0
  226. }
  227. }
  228. } catch (error) {
  229. console.error('获取统计信息失败', error)
  230. }
  231. }
  232. const handleSearch = () => {
  233. pagination.value.pageNum = 1
  234. get()
  235. }
  236. const resetSearch = () => {
  237. searchForm.value = {
  238. jwcode: '',
  239. refundType: '',
  240. refundGoods: '',
  241. area: adminInfo.value.area === '总部' ? '' : adminInfo.value.area,
  242. startTime: '',
  243. endTime: ''
  244. }
  245. dateRange.value = []
  246. handleSearch()
  247. }
  248. // 转换格式 moment(param).format('YYYY-MM-DD HH:mm:ss')
  249. // 今天
  250. const getToday = () => {
  251. const today = new Date()
  252. const start = moment(new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0)).format('YYYY-MM-DD HH:mm:ss')
  253. const end = moment(new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1, 0, 0, 0)).format('YYYY-MM-DD HH:mm:ss')
  254. dateRange.value = [start, end]
  255. get()
  256. }
  257. // 昨天
  258. const getYesterday = () => {
  259. const yesterday = new Date()
  260. yesterday.setDate(yesterday.getDate() - 1)
  261. const start = new Date(yesterday.getFullYear(), yesterday.getMonth(), yesterday.getDate(), 0, 0, 0)
  262. const end = new Date(yesterday.getFullYear(), yesterday.getMonth(), yesterday.getDate() + 1, 0, 0, 0)
  263. dateRange.value = [moment(start).format('YYYY-MM-DD HH:mm:ss'), moment(end).format('YYYY-MM-DD HH:mm:ss')]
  264. get()
  265. }
  266. // 近7天
  267. const get7Days = () => {
  268. const today = new Date()
  269. const start = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 6, 0, 0, 0)
  270. const end = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1, 0, 0, 0)
  271. dateRange.value = [start,end]// 好像不转换也可以??????
  272. get()
  273. }
  274. const switchTab = (tab) => {
  275. checkTab.value = tab
  276. pagination.value.pageNum = 1
  277. get()
  278. }
  279. // 当前状态
  280. const getCurrentStatus = () => {
  281. switch (checkTab.value) {
  282. case 'pending': return STATUS.PENDING
  283. case 'approved': return STATUS.APPROVED
  284. case 'rejected': return STATUS.REJECTED
  285. default: return ''
  286. }
  287. }
  288. const handlePagination = (type, val) => {
  289. if (type === 'size') {
  290. pagination.value.pageSize = val
  291. } else {
  292. pagination.value.pageNum = val
  293. }
  294. get()
  295. }
  296. const handleAction = (type, row) => {
  297. currentRecord.value = row
  298. if (type === 'reject') {
  299. rejectForm.value.reason = ''
  300. rejectDialog.value = true
  301. } else if (type === 'approve') {
  302. approveRefund()
  303. }
  304. }
  305. const approveRefund = async () => {
  306. try {
  307. const params = {
  308. adminId: adminInfo.value.adminId,
  309. auditId: currentRecord.value.auditId,
  310. status: STATUS.APPROVED,
  311. refundId: currentRecord.value.refundId
  312. }
  313. await request({
  314. url: '/audit/audit/edit',
  315. data: params
  316. })
  317. ElMessage.success('退款已批准')
  318. get()
  319. } catch (error) {
  320. console.error('批准失败', error)
  321. ElMessage.error('操作失败')
  322. }
  323. }
  324. const rejectRefund = async () => {
  325. if (!rejectForm.value.reason) {
  326. ElMessage.warning('请输入驳回理由')
  327. return
  328. }
  329. try {
  330. const params = {
  331. adminId: adminInfo.value.adminId,
  332. auditId: currentRecord.value.auditId,
  333. status: STATUS.REJECTED,
  334. refundId: currentRecord.value.refundId,
  335. reason: rejectForm.value.reason
  336. }
  337. await request({
  338. url: '/audit/audit/edit',
  339. data: params
  340. })
  341. ElMessage.success('退款已驳回')
  342. rejectDialog.value = false
  343. get()
  344. } catch (error) {
  345. console.error('驳回失败', error)
  346. ElMessage.error('操作失败')
  347. }
  348. }
  349. onMounted(async () => {
  350. await fetchAdminInfo()// 用户信息要挂吗
  351. await fetchProducts()
  352. await fetchAreas()
  353. get()
  354. })
  355. </script>
  356. <style scoped>
  357. .quickly-buttons {
  358. margin-left: 10px;
  359. }
  360. .stats-info {
  361. padding: 3px;
  362. background-color: #f5f7fa;
  363. }
  364. .pagination {
  365. margin-top: 20px;
  366. justify-content: flex-end;
  367. }
  368. </style>