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.

409 lines
13 KiB

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