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.

802 lines
24 KiB

2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
  1. <!-- 支付凭证 -->
  2. <template>
  3. <el-row>
  4. <el-col>
  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="rechargeAudit.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="rechargeAudit.activity" placeholder="请选择活动名称" style="width: 240px" clearable>
  14. <el-option v-for="item in activity" :key="item" :label="item" :value="item"/>
  15. </el-select>
  16. </el-col>
  17. <el-col :span="6">
  18. <el-text size="large">支付方式</el-text>
  19. <el-select v-model="rechargeAudit.payModel" placeholder="请选择支付方式" style="width: 240px" clearable>
  20. <el-option v-for="item in payModel" :key="item.value" :label="item.label" :value="item.value"/>
  21. </el-select>
  22. </el-col>
  23. <el-col :span="6">
  24. <el-text class="mx-1" size="large">所属地区</el-text>
  25. <el-cascader
  26. v-model="selectedMarketPath"
  27. :options="markets"
  28. placeholder="请选择所属地区"
  29. clearable
  30. collapse-tags
  31. collapse-tags-tooltip
  32. style="width:180px"
  33. @change="handleMarketChange"
  34. :props="props"
  35. />
  36. </el-col>
  37. </el-row>
  38. <el-row>
  39. <el-col :span="12">
  40. <div class="time-controls">
  41. <div class="time-group">
  42. <el-text size="large" style="width: 80px">充值时间</el-text>
  43. <el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
  44. end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange"/>
  45. <el-button @click="getToday()" style="margin-left: 10px"
  46. :type="activeTimeRange === 'today' ? 'primary' : ''">
  47. </el-button>
  48. <el-button @click="getYesterday()" style="margin-left: 10px"
  49. :type="activeTimeRange === 'yesterday' ? 'primary' : ''">
  50. </el-button>
  51. <el-button @click="get7Days()" style="margin-left: 10px"
  52. :type="activeTimeRange === '7days' ? 'primary' : ''"> 近7天
  53. </el-button>
  54. <el-button @click="resetSearch" type="success">重置</el-button>
  55. <el-button type="primary" @click="handleSearch">查询</el-button>
  56. </div>
  57. </div>
  58. </el-col>
  59. </el-row>
  60. </el-card>
  61. </el-col>
  62. </el-row>
  63. <el-row>
  64. <el-col>
  65. <el-card>
  66. <el-tabs v-model="activeName" type="card" @tab-click="handleClick">
  67. <el-tab-pane label="待审核" name="wait"></el-tab-pane>
  68. <el-tab-pane label="已通过" name="pass"></el-tab-pane>
  69. <el-tab-pane label="已驳回" name="reject"></el-tab-pane>
  70. <div>
  71. 总条数{{ stats.totalNum }}&nbsp;&nbsp;&nbsp;&nbsp;
  72. 总金币数{{ (stats.permanentGolds + stats.freeGolds + stats.taskGolds).toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
  73. 永久金币{{ stats.permanentGolds.toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
  74. 免费金币{{ stats.freeGolds.toFixed(2) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
  75. <!-- 任务金币{{ stats.taskGolds.toFixed(2) }}金币-->
  76. </div>
  77. </el-tabs>
  78. <!--表格-->
  79. <div style="height: 540px; overflow-y: auto">
  80. <el-table :data="tableData" style="width: 100%" height="540px" @sort-change="handleSortChange"
  81. :row-style="{ height: '50px' }">
  82. <el-table-column type="index" label="序号" width="100px" fixed="left">
  83. <template #default="scope">
  84. <span>{{ scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize }}</span>
  85. </template>
  86. </el-table-column>
  87. <el-table-column fixed="left" prop="name" label="姓名" width="150px"/>
  88. <el-table-column fixed="left" prop="jwcode" label="精网号" width="110px"/>
  89. <el-table-column prop="market" label="所属地区" width="100px">
  90. <template #default="scope">
  91. {{ marketMapping[scope.row.market] || scope.row.market }}
  92. </template>
  93. </el-table-column>
  94. <el-table-column prop="activity" label="活动名称" width="100px" show-overflow-tooltip/>
  95. <el-table-column prop="money" label="充值金额" sortable="custom" width="110px">
  96. <template #default="scope">{{ scope.row.permanentGold / 100 }}</template>
  97. </el-table-column>
  98. <el-table-column prop="permanentGold" label="永久金币" width="110px" sortable="custom">
  99. <template #default="scope">{{ scope.row.permanentGold / 100 }}</template>
  100. </el-table-column>
  101. <el-table-column prop="freeGold" label="免费金币" sortable="custom" width="110px">
  102. <template #default="scope">{{ (scope.row.freeGold) / 100 }}</template>
  103. </el-table-column>
  104. <el-table-column prop="remark" label="备注" width="200px" show-overflow-tooltip/>
  105. <el-table-column prop="payModel" label="支付方式" width="110px"/>
  106. <el-table-column prop="voucher" label="支付凭证" width="110px">
  107. <template #default="scope">
  108. <div v-if="scope.row.voucher"
  109. style="display: flex; justify-content: center; align-items: center; cursor: pointer;"
  110. @click="previewImage(scope.row.voucher)">
  111. <img :src="scope.row.voucher" alt="支付凭证" style="width: auto; height: 40px;">
  112. </div>
  113. <div v-else style="display: flex; justify-content: center; align-items: center; height: 40px;">--</div>
  114. </template>
  115. </el-table-column>
  116. <el-table-column prop="adminName" label="提交人" width="100px"/>
  117. <el-table-column prop="rejectReason" v-if="activeName === 'reject'" label="驳回理由" width="200px"
  118. show-overflow-tooltip/>
  119. <el-table-column v-if="activeName !== 'wait'" prop="auditName" label="审核人" width="100px"/>
  120. <el-table-column prop="payTime" sortable="custom" label="付款时间" width="200px">
  121. <template #default="scope">
  122. {{ moment(scope.row.payTime).format('YYYY-MM-DD HH:mm:ss') }}
  123. </template>
  124. </el-table-column>
  125. <el-table-column prop="createTime" sortable="custom" label="提交时间" width="200px">
  126. <template #default="scope">
  127. {{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
  128. </template>
  129. </el-table-column>
  130. <el-table-column v-if="activeName !== 'wait'" prop="auditTime" label="审核时间" width="200px">
  131. <template #default="scope">
  132. {{ moment(scope.row.auditTime).format('YYYY-MM-DD HH:mm:ss') }}
  133. </template>
  134. </el-table-column>
  135. <el-table-column v-if="activeName === 'wait'" fixed="right" prop="operation" label="操作" width="150px">
  136. <template #default="scope">
  137. <div class="operation">
  138. <el-popconfirm title="确定要通过此条记录吗?" @confirm="handleApprove(scope.row)">
  139. <template #reference>
  140. <el-button :disabled="scope.row.auditStatus === 1 || scope.row.auditStatus === 2" type="primary"
  141. text>
  142. 通过
  143. </el-button>
  144. </template>
  145. </el-popconfirm>
  146. <el-button :disabled="scope.row.auditStatus === 1 || scope.row.auditStatus === 2" type="primary" text
  147. @click="showRejectDialog(scope.row)">
  148. 驳回
  149. </el-button>
  150. </div>
  151. </template>
  152. </el-table-column>
  153. </el-table>
  154. </div>
  155. <div class="pagination">
  156. <el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
  157. layout="total, sizes, prev, pager, next, jumper" :total="total"
  158. @size-change="handlePagination('size', $event)"
  159. @current-change="handlePagination('page', $event)"></el-pagination>
  160. </div>
  161. </el-card>
  162. </el-col>
  163. </el-row>
  164. <el-dialog v-model="rejectDialogVisible" title="驳回理由" width="500px">
  165. <el-form>
  166. <el-form-item label="驳回理由" required>
  167. <el-input v-model="rejectReason" type="textarea" :rows="4" placeholder="请输入驳回理由" maxlength="200"
  168. show-word-limit/>
  169. </el-form-item>
  170. </el-form>
  171. <template #footer>
  172. <span class="dialog-footer">
  173. <el-button @click="rejectDialogVisible = false">取消</el-button>
  174. <el-button type="primary" @click="handleReject">确定</el-button>
  175. </span>
  176. </template>
  177. </el-dialog>
  178. </template>
  179. <script setup>
  180. import {onMounted, reactive, ref} from 'vue'
  181. import {marketMapping, reverseMarketMapping} from '@/utils/marketMap.js'
  182. import {ElMessage} from 'element-plus'
  183. import request from '@/util/http'
  184. import API from '@/util/http'
  185. import moment from 'moment'
  186. import {useAdminStore} from "@/store/index.js";
  187. import {storeToRefs} from "pinia";
  188. import {findMenuById, permissionMapping} from "@/utils/menuTreePermission.js"
  189. // 精网号去空格
  190. const trimJwCode = () => {
  191. if (rechargeAudit.value.jwcode) {
  192. rechargeAudit.value.jwcode = rechargeAudit.value.jwcode.replace(/\s/g, '');
  193. }
  194. }
  195. const formatTime = (val) => val ? moment(val).format('YYYY-MM-DD HH:mm:ss') : ''
  196. // 这是获取用户信息的接口
  197. const adminData = ref({})
  198. // 充值明细表格
  199. const tableData = ref([])
  200. // 标记当前激活的时间范围按钮
  201. const activeTimeRange = ref('')
  202. // 日期选择器变化时清除按钮激活状态
  203. const handleDatePickerChange = () => {
  204. activeTimeRange.value = ''
  205. }
  206. // 搜索表单数据
  207. const rechargeAudit = ref({
  208. jwcode: "", // 精网号
  209. activity: "", // 活动名称
  210. payModel: "", // 支付方式
  211. startTime: "", // 充值时间开始
  212. endTime: "", // 充值时间结束
  213. markets: [], // 地区
  214. auditStatus: "0",
  215. })
  216. // 搜索对象
  217. const getObj = ref({
  218. pageNum: 1,
  219. pageSize: 50
  220. })
  221. // 分页总条目
  222. const total = ref(50)
  223. // 搜索对象时间
  224. const getTime = ref([])
  225. const activity = ref([])
  226. // 搜索地区列表
  227. const markets = ref([])
  228. // 驳回弹出框
  229. const rejectDialogVisible = ref(false)
  230. // 驳回理由
  231. const rejectReason = ref('')
  232. // 当前行信息
  233. const currentRecord = ref(null)
  234. // 标签页默认是待审批
  235. const activeName = ref('wait')
  236. // 支付方式选项
  237. const payModel = [
  238. // 现金、支票、刷卡、其他(各地区电子支付)
  239. {
  240. value: '现金',
  241. label: '现金'
  242. },
  243. {
  244. value: '支票',
  245. label: '支票'
  246. },
  247. {
  248. value: '刷卡',
  249. label: '刷卡'
  250. },
  251. {
  252. value: '其他(各地区电子支付)',
  253. label: '其他(各地区电子支付)'
  254. },
  255. ]
  256. // 表单验证ref
  257. const Ref = ref(null)
  258. // 统计合计数
  259. const stats = ref({
  260. totalNum: 0,
  261. totalCoins: 0,
  262. permanentGolds: 0,
  263. freeGolds: 0,
  264. taskGolds: 0
  265. })
  266. // 新增排序字段和排序方式
  267. const sortField = ref('')
  268. const sortOrder = ref('')
  269. // 表单验证
  270. const rules = reactive({
  271. rejectReason: [{required: true, message: '请输入驳回理由', trigger: 'blur'}]
  272. })
  273. const getAdminData = async function () {
  274. try {
  275. const result = await request({
  276. url: '/admin/userinfo',
  277. data: {}
  278. })
  279. adminData.value = result
  280. console.log('用户信息', adminData.value)
  281. } catch (error) {
  282. console.log('请求失败', error)
  283. }
  284. }
  285. // 搜索方法
  286. const getRecharge = async function (val) {
  287. try {
  288. // 搜索参数页码赋值
  289. if (typeof val === 'number') {
  290. getObj.value.pageNum = val
  291. }
  292. // 搜索参数时间赋值
  293. if (getTime.value && getTime.value.length === 2) {
  294. rechargeAudit.value.startTime = formatTime(getTime.value[0])
  295. rechargeAudit.value.endTime = formatTime(getTime.value[1])
  296. } else {
  297. rechargeAudit.value.startTime = ''
  298. rechargeAudit.value.endTime = ''
  299. }
  300. console.log('搜索参数', getObj.value)
  301. const result = await request({
  302. url: '/audit/selectRecharge',
  303. data: {
  304. pageNum: getObj.value.pageNum,
  305. pageSize: getObj.value.pageSize,
  306. rechargeAudit: {
  307. ...rechargeAudit.value,
  308. sortField: sortField.value,
  309. sortOrder: sortOrder.value
  310. }
  311. }
  312. })
  313. // 存储表格数据
  314. tableData.value = result.list
  315. // 存储总条数
  316. total.value = result.total
  317. } catch (error) {
  318. console.log('请求失败', error)
  319. }
  320. }
  321. const getStats = async () => {
  322. try {
  323. const params = {
  324. pageNum: getObj.value.pageNum,
  325. pageSize: getObj.value.pageSize,
  326. rechargeAudit: rechargeAudit.value
  327. }
  328. const res = await API({
  329. url: '/audit/sumRechargeGold',
  330. data: params
  331. })
  332. stats.value.totalNum = res.totalNum
  333. stats.value.permanentGolds = res.permanentGolds / 100
  334. stats.value.freeGolds = res.freeGolds / 100
  335. stats.value.taskGolds = res.taskGolds / 100
  336. console.log('see see stats和搜索对象', stats.value, params)
  337. } catch (error) {
  338. console.log('请求失败', error)
  339. }
  340. }
  341. // 搜索
  342. const handleSearch = function () {
  343. trimJwCode();
  344. getObj.value.pageNum = 1
  345. getRecharge()
  346. getStats()
  347. }
  348. // 重置
  349. const resetSearch = function () {
  350. rechargeAudit.value = {
  351. jwcode: "",
  352. activity: "",
  353. payModel: "",
  354. startTime: "",
  355. endTime: "",
  356. markets: [],
  357. auditStatus: rechargeAudit.value.auditStatus,
  358. }
  359. selectedMarketPath.value = []
  360. getTime.value = []
  361. activeTimeRange.value = '' // 清除激活状态
  362. getRecharge()
  363. getStats()
  364. }
  365. // 今天
  366. const getToday = function () {
  367. const today = new Date()
  368. const startTime = new Date(
  369. today.getFullYear(),
  370. today.getMonth(),
  371. today.getDate()
  372. )
  373. const endTime = new Date(
  374. today.getFullYear(),
  375. today.getMonth(),
  376. today.getDate() + 1
  377. )
  378. getTime.value = [startTime, endTime]
  379. console.log('getTime', getTime.value)
  380. activeTimeRange.value = 'today' // 标记当前激活状态
  381. getRecharge()
  382. getStats()
  383. }
  384. // 昨天
  385. const getYesterday = function () {
  386. const yesterday = new Date()
  387. yesterday.setDate(yesterday.getDate() - 1)
  388. const startTime = new Date(
  389. yesterday.getFullYear(),
  390. yesterday.getMonth(),
  391. yesterday.getDate()
  392. )
  393. const endTime = new Date(
  394. yesterday.getFullYear(),
  395. yesterday.getMonth(),
  396. yesterday.getDate() + 1
  397. )
  398. getTime.value = [startTime, endTime]
  399. console.log('getTime', getTime.value)
  400. activeTimeRange.value = 'yesterday' // 标记当前激活状态
  401. getRecharge()
  402. getStats()
  403. }
  404. // 近7天
  405. const get7Days = function () {
  406. const today = new Date()
  407. const startTime = new Date(
  408. today.getFullYear(),
  409. today.getMonth(),
  410. today.getDate() - 6
  411. )
  412. const endTime = new Date(
  413. today.getFullYear(),
  414. today.getMonth(),
  415. today.getDate() + 1
  416. )
  417. getTime.value = [startTime, endTime]
  418. console.log('getTime', getTime.value)
  419. activeTimeRange.value = '7days' // 标记当前激活状态
  420. getRecharge()
  421. getStats()
  422. }
  423. // 待审核充值明细
  424. const adminWait = async function () {
  425. rechargeAudit.value.auditStatus = "0"
  426. getObj.value.pageNum = 1
  427. await getRecharge()
  428. await getStats()
  429. console.log('adminWait,这是点击待审核调用')
  430. }
  431. // 已通过充值明细
  432. const adminPass = async function () {
  433. rechargeAudit.value.auditStatus = "1"
  434. getObj.value.pageNum = 1
  435. await getRecharge()
  436. await getStats()
  437. console.log('adminWait,这是点击已通过调用')
  438. }
  439. // 已驳回充值明细
  440. const adminReject = async function () {
  441. rechargeAudit.value.auditStatus = "2"
  442. getObj.value.pageNum = 1
  443. await getRecharge()
  444. await getStats()
  445. console.log('adminWait,这是点击已驳回调用')
  446. }
  447. const handleClick = function (tab, event) {
  448. activeName.value = tab.props.name
  449. if (tab.props.name === 'wait') {
  450. adminWait()
  451. } else if (tab.props.name === 'pass') {
  452. adminPass()
  453. } else if (tab.props.name === 'reject') {
  454. adminReject()
  455. }
  456. }
  457. const getActivity = async function () {
  458. try {
  459. const result = await request({
  460. url: '/general/activity',
  461. data: {}
  462. })
  463. activity.value = result.data
  464. console.log('activity', activity.value)
  465. } catch (error) {
  466. console.log('请求失败', error)
  467. }
  468. }
  469. const handlePagination = (type, val) => {
  470. if (type === 'size') {
  471. getObj.value.pageSize = val
  472. } else {
  473. getObj.value.pageNum = val
  474. }
  475. getRecharge()
  476. getStats()
  477. }
  478. const adminStore = useAdminStore();
  479. const {menuTree} = storeToRefs(adminStore);
  480. // 处理通过操作
  481. const handleApprove = async (row) => {
  482. if (findMenuById(menuTree.value, permissionMapping.Recharge_Approval)) {
  483. try {
  484. const params = {
  485. orderCode: row.orderCode,
  486. auditId: adminData.value.id,
  487. action: 1,
  488. rejectReason: ''
  489. }
  490. await request({url: '/audit/audit', data: params})
  491. ElMessage.success('审核通过成功')
  492. await getRecharge()
  493. await getStats()
  494. } catch (error) {
  495. console.error('审核通过失败', error)
  496. ElMessage.error('操作失败')
  497. }
  498. } else {
  499. ElMessage.error('无权限')
  500. }
  501. }
  502. // 显示驳回对话框
  503. const showRejectDialog = (row) => {
  504. currentRecord.value = row
  505. rejectReason.value = '' // 清空之前的
  506. rejectDialogVisible.value = true
  507. }
  508. // 处理驳回操作
  509. const handleReject = async () => {
  510. if (!rejectReason.value.trim()) {
  511. ElMessage.warning('请输入驳回理由')
  512. return
  513. }
  514. try {
  515. const params = {
  516. orderCode: currentRecord.value.orderCode,
  517. auditId: adminData.value.id,
  518. action: 2,
  519. rejectReason: rejectReason.value
  520. }
  521. await request({url: '/audit/audit', data: params})
  522. ElMessage.success('驳回操作成功')
  523. rejectDialogVisible.value = false
  524. await getRecharge()
  525. await getStats()
  526. } catch (error) {
  527. console.error('驳回操作失败', error)
  528. ElMessage.error('操作失败')
  529. }
  530. }
  531. // 处理排序事件
  532. const handleSortChange = (column) => {
  533. console.log('排序字段:', column.prop)
  534. console.log('排序方式:', column.order)
  535. if (column.prop === 'money') {
  536. sortField.value = 'permanent_gold'
  537. } else if (column.prop === 'permanentGold') {
  538. sortField.value = 'permanent_gold'
  539. } else if (column.prop === 'freeGold') {
  540. sortField.value = 'freeGold'
  541. } else if (column.prop === 'createTime') {
  542. sortField.value = 'create_time'
  543. } else if (column.prop === 'payTime') {
  544. sortField.value = 'pay_time'
  545. } else if (column.prop === 'auditTime') {
  546. sortField.value = 'audit_time'
  547. }
  548. sortOrder.value = column.order === 'ascending' ? 'asc' : 'desc'
  549. getRecharge()
  550. getStats()
  551. }
  552. // 预览图片函数
  553. const previewImage = (imageUrl) => {
  554. // 使用 element-plus 的 el-image 组件实现图片预览功能
  555. const imageElement = document.createElement('img');
  556. imageElement.src = imageUrl;
  557. imageElement.style.maxWidth = '80vw';
  558. imageElement.style.maxHeight = '80vh';
  559. const viewer = document.createElement('div');
  560. viewer.style.position = 'fixed';
  561. viewer.style.top = '0';
  562. viewer.style.left = '0';
  563. viewer.style.width = '100vw';
  564. viewer.style.height = '100vh';
  565. viewer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
  566. viewer.style.display = 'flex';
  567. viewer.style.justifyContent = 'center';
  568. viewer.style.alignItems = 'center';
  569. viewer.style.zIndex = '9999';
  570. viewer.style.overflow = 'auto';
  571. viewer.appendChild(imageElement);
  572. document.body.appendChild(viewer);
  573. viewer.addEventListener('click', () => {
  574. document.body.removeChild(viewer);
  575. });
  576. };
  577. // 存储地区选择变化
  578. const selectedMarketPath = ref([])
  579. //处理地区选择变化
  580. const handleMarketChange = (value) => {
  581. if (Array.isArray(value) && value.length > 0) {
  582. const ids = new Set();
  583. value.forEach(path => {
  584. const lastName = path[path.length - 1];
  585. const id = reverseMarketMapping[lastName];
  586. if (id) ids.add(Number(id));
  587. });
  588. // 添加额外处理:如果一个父节点下所有子节点都被选中,则把父节点也加入
  589. const getAllLeafNames = (nodes) => {
  590. const leafNames = [];
  591. const traverse = (node, parentName = null) => {
  592. if (!node.children || node.children.length === 0) {
  593. leafNames.push({name: node.label, parent: parentName});
  594. } else {
  595. node.children.forEach(child => traverse(child, node.label));
  596. }
  597. };
  598. nodes.forEach(node => traverse(node));
  599. return leafNames;
  600. };
  601. const leafNameMap = getAllLeafNames(markets.value); // 所有叶子节点和对应父级名称
  602. // 列表构建
  603. const parentToChildren = {};
  604. leafNameMap.forEach(({name, parent}) => {
  605. if (!parentToChildren[parent]) parentToChildren[parent] = [];
  606. parentToChildren[parent].push(name);
  607. });
  608. // 构建当前被选中的叶子节点
  609. const selectedLeafNames = value.map(path => path[path.length - 1]);
  610. // 如果 parent 下所有子节点都选中了,就把 parent 加进来
  611. Object.entries(parentToChildren).forEach(([parent, children]) => {
  612. const allChildrenSelected = children.every(child => selectedLeafNames.includes(child));
  613. if (allChildrenSelected && reverseMarketMapping[parent]) {
  614. ids.add(Number(reverseMarketMapping[parent]));
  615. }
  616. });
  617. rechargeAudit.value.markets = Array.from(ids);
  618. } else {
  619. rechargeAudit.value.markets = [];
  620. }
  621. console.log('最终映射后的 market IDs:', rechargeAudit.value.markets);
  622. };
  623. const props = {multiple: true}
  624. // 获取地区,修改为级联下拉框
  625. const getMarket = async function () {
  626. try {
  627. // 发送POST请求
  628. const result = await API({
  629. url: '/market/selectMarket',
  630. });
  631. // 将响应结果存储到响应式数据中
  632. console.log('请求成功', result)
  633. // 递归转换树形结构为级联选择器需要的格式(跳过第一级节点)
  634. const transformTree = (nodes) => {
  635. // 直接处理第一级节点的子节点
  636. const allChildren = nodes.flatMap(node => node.children || []);
  637. return allChildren.map(child => {
  638. const grandchildren = child.children && child.children.length
  639. ? transformTree([child]) // 递归处理子节点
  640. : null;
  641. return {
  642. value: child.name,
  643. label: child.name,
  644. children: grandchildren
  645. };
  646. });
  647. };
  648. // 存储地区信息
  649. markets.value = transformTree(result.data)
  650. console.log('转换后的地区树==============', markets.value)
  651. } catch (error) {
  652. console.log('请求失败', error)
  653. }
  654. }
  655. // 挂载
  656. onMounted(async function () {
  657. await getAdminData()
  658. await getActivity()
  659. await getMarket()
  660. await getRecharge()
  661. console.log('111')
  662. await getStats()
  663. })
  664. </script>
  665. <style scoped>
  666. .pagination {
  667. display: flex;
  668. }
  669. .operation {
  670. display: flex;
  671. }
  672. .green-dot {
  673. background-color: #67C23A;
  674. }
  675. .grey-dot {
  676. background-color: #909399;
  677. }
  678. .red-dot {
  679. background-color: #F56C6C;
  680. }
  681. .time-controls {
  682. display: flex;
  683. align-items: center;
  684. }
  685. .time-group {
  686. display: flex;
  687. align-items: center;
  688. gap: 10px;
  689. }
  690. .quick-buttons {
  691. display: flex;
  692. align-items: center;
  693. }
  694. .status {
  695. display: flex;
  696. align-items: center;
  697. /* 确保子元素垂直居中对齐 */
  698. gap: 6px;
  699. /* 设置圆点和文字之间的间距 */
  700. }
  701. .green-dot,
  702. .grey-dot,
  703. .red-dot {
  704. display: inline-block;
  705. width: 8px;
  706. height: 8px;
  707. border-radius: 50%;
  708. flex-shrink: 0;
  709. /* 防止圆点在空间不足时缩小 */
  710. margin: 0;
  711. /* 移除原有的 margin-right */
  712. }
  713. /* 备注列样式 */
  714. .remark-cell {
  715. display: block;
  716. width: 100%;
  717. overflow: hidden;
  718. text-overflow: ellipsis;
  719. white-space: nowrap;
  720. }
  721. /* 设置单元格内容溢出隐藏 */
  722. .el-table .el-table__cell {
  723. overflow: hidden;
  724. text-overflow: ellipsis;
  725. white-space: nowrap;
  726. }
  727. </style>