MatchmakerAudit.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  1. <template>
  2. <div class="matchmaker-audit-container">
  3. <h2 class="page-title">红娘审核</h2>
  4. <el-card shadow="never" class="toolbar-card">
  5. <el-row :gutter="20">
  6. <el-col :span="10">
  7. <el-space>
  8. <el-button
  9. type="primary"
  10. :disabled="selectedRows.length === 0"
  11. @click="handleBatchAudit"
  12. >
  13. 批量审核{{ selectedRows.length > 0 ? ` (${selectedRows.length})` : '' }}
  14. </el-button>
  15. <el-button icon="Refresh" @click="resetFilters">重置</el-button>
  16. </el-space>
  17. </el-col>
  18. <el-col :span="14">
  19. <el-space style="justify-content: flex-end; width: 100%;">
  20. <el-select
  21. v-model="filters.status"
  22. placeholder="审核状态"
  23. clearable
  24. style="width: 140px"
  25. @change="handleStatusChange"
  26. >
  27. <el-option label="待审核" :value="2" />
  28. <el-option label="审核通过" :value="0" />
  29. <el-option label="审核未通过" :value="1" />
  30. </el-select>
  31. <el-input
  32. v-model="filters.name"
  33. placeholder="搜索姓名"
  34. clearable
  35. style="width: 140px"
  36. @keyup.enter="loadList"
  37. />
  38. <el-input
  39. v-model="filters.phone"
  40. placeholder="搜索手机号"
  41. clearable
  42. style="width: 140px"
  43. @keyup.enter="loadList"
  44. >
  45. <template #append>
  46. <el-button icon="Search" @click="loadList" />
  47. </template>
  48. </el-input>
  49. </el-space>
  50. </el-col>
  51. </el-row>
  52. </el-card>
  53. <el-card shadow="never" class="table-card">
  54. <el-table
  55. v-loading="loading"
  56. :data="list"
  57. stripe
  58. @selection-change="handleSelectionChange"
  59. >
  60. <template #empty>
  61. <div class="custom-empty-state">
  62. <el-icon class="empty-icon"><User /></el-icon>
  63. <div class="empty-text">暂无红娘申请</div>
  64. <div class="empty-hint">等待用户提交申请后在此审核</div>
  65. </div>
  66. </template>
  67. <el-table-column type="selection" width="55" :selectable="row => !isAudited(row)" />
  68. <el-table-column type="index" label="序号" width="60" />
  69. <el-table-column prop="name" label="姓名" min-width="100">
  70. <template #default="{ row }">
  71. <span class="data-highlight">{{ row.name || '-' }}</span>
  72. </template>
  73. </el-table-column>
  74. <el-table-column prop="phone" label="手机号" min-width="140" />
  75. <el-table-column prop="age" label="年龄" width="80" align="center" />
  76. <el-table-column prop="gender" label="性别" width="90" align="center">
  77. <template #default="{ row }">
  78. <el-tag size="small" effect="light">
  79. {{ row.gender === 1 ? '男' : row.gender === 2 ? '女' : '未知' }}
  80. </el-tag>
  81. </template>
  82. </el-table-column>
  83. <el-table-column prop="area" label="地区" min-width="140" />
  84. <el-table-column prop="experience" label="经验" min-width="140" show-overflow-tooltip />
  85. <el-table-column prop="introduction" label="简介" min-width="160" show-overflow-tooltip />
  86. <el-table-column prop="createTime" label="申请时间" width="180">
  87. <template #default="{ row }">
  88. <span class="text-muted">{{ formatDateTime(row.createTime || row.create_time) }}</span>
  89. </template>
  90. </el-table-column>
  91. <el-table-column label="审核原因" min-width="150" show-overflow-tooltip>
  92. <template #default="{ row }">
  93. <span class="text-muted">{{ row.reason || '-' }}</span>
  94. </template>
  95. </el-table-column>
  96. <el-table-column label="操作" width="250" fixed="right">
  97. <template #default="{ row }">
  98. <el-button
  99. type="info"
  100. size="small"
  101. link
  102. @click="handleViewDetail(row)"
  103. >
  104. 详情
  105. </el-button>
  106. <el-button
  107. v-if="!isAudited(row)"
  108. type="primary"
  109. size="small"
  110. @click="handleAudit(row)"
  111. :loading="approvingId === row.applyId"
  112. >
  113. 审核
  114. </el-button>
  115. <el-tag
  116. v-else-if="row.status === 0"
  117. type="success"
  118. size="small"
  119. effect="light"
  120. >
  121. 审核通过
  122. </el-tag>
  123. <el-tag
  124. v-else-if="row.status === 1"
  125. class="audit-rejected-tag"
  126. size="small"
  127. effect="light"
  128. >
  129. 审核未通过
  130. </el-tag>
  131. <el-button
  132. type="danger"
  133. size="small"
  134. link
  135. @click="handleDelete(row)"
  136. :loading="deletingId === row.applyId"
  137. >
  138. 删除
  139. </el-button>
  140. </template>
  141. </el-table-column>
  142. </el-table>
  143. <div class="pagination-container">
  144. <el-pagination
  145. v-model:current-page="currentPage"
  146. v-model:page-size="pageSize"
  147. :total="total"
  148. :page-sizes="[10, 20, 50, 100]"
  149. layout="total, sizes, prev, pager, next, jumper"
  150. @size-change="loadList"
  151. @current-change="loadList"
  152. />
  153. </div>
  154. </el-card>
  155. <!-- 详情对话框 -->
  156. <el-dialog
  157. v-model="detailDialogVisible"
  158. title="红娘申请详情"
  159. width="800px"
  160. :close-on-click-modal="false"
  161. >
  162. <div v-if="detailData" class="detail-content">
  163. <!-- 基本信息 -->
  164. <el-card shadow="never" class="detail-section">
  165. <template #header>
  166. <div class="section-header">
  167. <el-icon><User /></el-icon>
  168. <span>基本信息</span>
  169. </div>
  170. </template>
  171. <el-descriptions :column="2" border>
  172. <el-descriptions-item label="姓名">
  173. {{ detailData.name || '-' }}
  174. </el-descriptions-item>
  175. <el-descriptions-item label="手机号">
  176. {{ detailData.phone || '-' }}
  177. </el-descriptions-item>
  178. <el-descriptions-item label="邮箱">
  179. {{ detailData.email || '-' }}
  180. </el-descriptions-item>
  181. <el-descriptions-item label="年龄">
  182. {{ detailData.age || '-' }}
  183. </el-descriptions-item>
  184. <el-descriptions-item label="性别">
  185. <el-tag v-if="detailData.gender === 1" type="primary" size="small">男</el-tag>
  186. <el-tag v-else-if="detailData.gender === 2" type="danger" size="small">女</el-tag>
  187. <span v-else>-</span>
  188. </el-descriptions-item>
  189. <el-descriptions-item label="所在地区">
  190. {{ detailData.area || '-' }}
  191. </el-descriptions-item>
  192. <el-descriptions-item label="用户ID">
  193. {{ detailData.userId || '-' }}
  194. </el-descriptions-item>
  195. <el-descriptions-item label="申请ID">
  196. {{ detailData.applyId || '-' }}
  197. </el-descriptions-item>
  198. </el-descriptions>
  199. </el-card>
  200. <!-- 红娘信息 -->
  201. <el-card shadow="never" class="detail-section">
  202. <template #header>
  203. <div class="section-header">
  204. <el-icon><Star /></el-icon>
  205. <span>红娘信息</span>
  206. </div>
  207. </template>
  208. <el-descriptions :column="2" border>
  209. <el-descriptions-item label="婚姻介绍经验" :span="2">
  210. {{ detailData.experience || '-' }}
  211. </el-descriptions-item>
  212. <el-descriptions-item label="可服务时间" :span="2">
  213. {{ detailData.serverTime || '-' }}
  214. </el-descriptions-item>
  215. <el-descriptions-item label="个人简介" :span="2">
  216. <div class="introduction-content">
  217. {{ detailData.introduction || '-' }}
  218. </div>
  219. </el-descriptions-item>
  220. </el-descriptions>
  221. </el-card>
  222. <!-- 审核信息 -->
  223. <el-card shadow="never" class="detail-section" v-if="detailData.status !== null && detailData.status !== undefined">
  224. <template #header>
  225. <div class="section-header">
  226. <el-icon><Document /></el-icon>
  227. <span>审核信息</span>
  228. </div>
  229. </template>
  230. <el-descriptions :column="2" border>
  231. <el-descriptions-item label="审核状态">
  232. <el-tag
  233. v-if="detailData.status === 0"
  234. type="success"
  235. size="small"
  236. >
  237. 审核通过
  238. </el-tag>
  239. <el-tag
  240. v-else-if="detailData.status === 1"
  241. class="audit-rejected-tag"
  242. size="small"
  243. >
  244. 审核未通过
  245. </el-tag>
  246. <el-tag
  247. v-else
  248. type="info"
  249. size="small"
  250. >
  251. 待审核
  252. </el-tag>
  253. </el-descriptions-item>
  254. <el-descriptions-item label="审核原因">
  255. {{ detailData.reason || '-' }}
  256. </el-descriptions-item>
  257. <el-descriptions-item label="申请时间">
  258. {{ detailData.createTime || detailData.create_time || '-' }}
  259. </el-descriptions-item>
  260. <el-descriptions-item label="更新时间">
  261. {{ detailData.updateTime || detailData.update_time || '-' }}
  262. </el-descriptions-item>
  263. </el-descriptions>
  264. </el-card>
  265. </div>
  266. <template #footer>
  267. <el-button @click="detailDialogVisible = false">关闭</el-button>
  268. </template>
  269. </el-dialog>
  270. <!-- 审核弹框 -->
  271. <el-dialog
  272. v-model="auditDialogVisible"
  273. :title="auditDialogTitle"
  274. width="600px"
  275. :close-on-click-modal="false"
  276. >
  277. <!-- 批量审核进度显示 -->
  278. <div v-if="isBatchAudit && batchAuditList.length > 0" class="batch-audit-progress">
  279. 进度:{{ currentBatchIndex }} / {{ batchAuditList.length }}
  280. </div>
  281. <!-- 当前审核数据信息 -->
  282. <div v-if="isBatchAudit && currentBatchRow" class="current-audit-info">
  283. <el-descriptions :column="2" border size="small">
  284. <el-descriptions-item label="姓名">{{ currentBatchRow.name || '-' }}</el-descriptions-item>
  285. <el-descriptions-item label="手机号">{{ currentBatchRow.phone || '-' }}</el-descriptions-item>
  286. <el-descriptions-item label="年龄">{{ currentBatchRow.age || '-' }}</el-descriptions-item>
  287. <el-descriptions-item label="性别">
  288. {{ currentBatchRow.gender === 1 ? '男' : currentBatchRow.gender === 2 ? '女' : '未知' }}
  289. </el-descriptions-item>
  290. <el-descriptions-item label="地区" :span="2">{{ currentBatchRow.area || '-' }}</el-descriptions-item>
  291. </el-descriptions>
  292. </div>
  293. <el-form :model="auditForm" :rules="auditRules" ref="auditFormRef" label-width="100px" style="margin-top: 20px">
  294. <el-form-item label="审核结果" prop="approved">
  295. <el-radio-group v-model="auditForm.approved">
  296. <el-radio :label="true">同意</el-radio>
  297. <el-radio :label="false">不同意</el-radio>
  298. </el-radio-group>
  299. </el-form-item>
  300. <el-form-item label="审核原因" prop="reason">
  301. <el-input
  302. v-model="auditForm.reason"
  303. type="textarea"
  304. :rows="4"
  305. placeholder="请输入审核原因(必填)"
  306. maxlength="500"
  307. show-word-limit
  308. />
  309. </el-form-item>
  310. </el-form>
  311. <template #footer>
  312. <el-button @click="handleCancelBatchAudit">取消</el-button>
  313. <el-button
  314. type="primary"
  315. @click="submitAudit"
  316. :loading="auditSubmitting"
  317. >
  318. {{ isBatchAudit && currentBatchIndex < batchAuditList.length ? '提交并继续' : '确定' }}
  319. </el-button>
  320. </template>
  321. </el-dialog>
  322. </div>
  323. </template>
  324. <script setup>
  325. import { ref, reactive, computed, onMounted } from 'vue'
  326. import { ElMessage, ElMessageBox } from 'element-plus'
  327. import { User, Star, Document } from '@element-plus/icons-vue'
  328. import request from '@/utils/request'
  329. import { API_ENDPOINTS } from '@/config/api'
  330. // 格式化日期时间
  331. const formatDateTime = (dateTime) => {
  332. if (!dateTime) return '-'
  333. try {
  334. const date = new Date(dateTime)
  335. if (isNaN(date.getTime())) return dateTime
  336. const year = date.getFullYear()
  337. const month = String(date.getMonth() + 1).padStart(2, '0')
  338. const day = String(date.getDate()).padStart(2, '0')
  339. const hours = String(date.getHours()).padStart(2, '0')
  340. const minutes = String(date.getMinutes()).padStart(2, '0')
  341. const seconds = String(date.getSeconds()).padStart(2, '0')
  342. return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`
  343. } catch {
  344. return dateTime
  345. }
  346. }
  347. const loading = ref(false)
  348. const list = ref([])
  349. const total = ref(0)
  350. const currentPage = ref(1)
  351. const pageSize = ref(10)
  352. const approvingId = ref(null)
  353. const deletingId = ref(null)
  354. const selectedRows = ref([])
  355. const auditDialogVisible = ref(false)
  356. const auditSubmitting = ref(false)
  357. const auditFormRef = ref(null)
  358. const currentAuditRow = ref(null)
  359. const isBatchAudit = ref(false)
  360. const batchAuditList = ref([]) // 批量审核的列表
  361. const currentBatchIndex = ref(1) // 当前审核的索引(从1开始)
  362. const currentBatchRow = ref(null) // 当前审核的行数据
  363. const auditDialogTitle = ref('审核') // 弹框标题
  364. // 详情对话框相关
  365. const detailDialogVisible = ref(false)
  366. const detailData = ref(null)
  367. const filters = reactive({
  368. name: '',
  369. phone: '',
  370. status: undefined
  371. })
  372. const handleStatusChange = () => {
  373. currentPage.value = 1
  374. loadList()
  375. }
  376. const auditForm = reactive({
  377. approved: true,
  378. reason: ''
  379. })
  380. const auditRules = {
  381. approved: [
  382. { required: true, message: '请选择审核结果', trigger: 'change' }
  383. ],
  384. reason: [
  385. { required: true, message: '请输入审核原因', trigger: 'blur' },
  386. { min: 1, max: 500, message: '审核原因长度在 1 到 500 个字符', trigger: 'blur' }
  387. ]
  388. }
  389. const loadList = async () => {
  390. loading.value = true
  391. try {
  392. const params = {
  393. page: currentPage.value,
  394. pageSize: pageSize.value
  395. }
  396. if (filters.status !== undefined && filters.status !== null) {
  397. params.status = filters.status
  398. }
  399. if (filters.name && filters.name.trim()) {
  400. params.name = filters.name.trim()
  401. }
  402. if (filters.phone && filters.phone.trim()) {
  403. params.phone = filters.phone.trim()
  404. }
  405. const res = await request.get(API_ENDPOINTS.MATCHMAKER_AUDIT_LIST, { params })
  406. if (res.code === 200) {
  407. const data = res.data || {}
  408. list.value = data.list || data.records || []
  409. total.value = data.total || list.value.length
  410. }
  411. } catch (error) {
  412. console.error('加载审核列表失败:', error)
  413. ElMessage.error('加载审核列表失败')
  414. } finally {
  415. loading.value = false
  416. }
  417. }
  418. const resetFilters = () => {
  419. filters.name = ''
  420. filters.phone = ''
  421. filters.status = undefined
  422. currentPage.value = 1
  423. loadList()
  424. }
  425. // 判断是否已审核通过(根据status字段判断,status=0表示审核通过)
  426. const isApproved = (row) => {
  427. return row.status === 0
  428. }
  429. // 判断是否已审核(status=0或1都表示已审核)
  430. const isAudited = (row) => {
  431. return row.status === 0 || row.status === 1
  432. }
  433. // 表格选择变化
  434. const handleSelectionChange = (selection) => {
  435. selectedRows.value = selection
  436. }
  437. // 打开审核弹框(单个)
  438. const handleAudit = (row) => {
  439. if (!row.userId) {
  440. ElMessage.error('用户ID不存在,无法审核')
  441. return
  442. }
  443. if (isAudited(row)) {
  444. ElMessage.warning('该申请已审核,无需重复操作')
  445. return
  446. }
  447. currentAuditRow.value = row
  448. isBatchAudit.value = false
  449. auditForm.approved = true
  450. auditForm.reason = ''
  451. auditDialogTitle.value = `审核 - ${row.name || '该用户'}`
  452. auditDialogVisible.value = true
  453. }
  454. // 批量审核
  455. const handleBatchAudit = () => {
  456. if (selectedRows.value.length === 0) {
  457. ElMessage.warning('请至少选择一条记录')
  458. return
  459. }
  460. // 过滤掉已审核的记录
  461. const unapprovedRows = selectedRows.value.filter(row => !isAudited(row))
  462. if (unapprovedRows.length === 0) {
  463. ElMessage.warning('所选记录均已审核,无需重复操作')
  464. return
  465. }
  466. // 初始化批量审核列表
  467. batchAuditList.value = unapprovedRows
  468. currentBatchIndex.value = 1
  469. currentAuditRow.value = null
  470. isBatchAudit.value = true
  471. auditForm.approved = true
  472. auditForm.reason = ''
  473. // 设置标题
  474. const count = unapprovedRows.length
  475. auditDialogTitle.value = count > 0 ? `批量审核 (${count}条)` : '批量审核'
  476. // 设置当前审核的行数据
  477. updateCurrentBatchRow()
  478. auditDialogVisible.value = true
  479. }
  480. // 更新当前批量审核的行数据
  481. const updateCurrentBatchRow = () => {
  482. if (isBatchAudit.value && batchAuditList.value.length > 0) {
  483. const index = currentBatchIndex.value - 1
  484. if (index >= 0 && index < batchAuditList.value.length) {
  485. currentBatchRow.value = batchAuditList.value[index]
  486. } else {
  487. currentBatchRow.value = null
  488. }
  489. }
  490. }
  491. // 批量审核分页变化
  492. const handleBatchPageChange = (page) => {
  493. currentBatchIndex.value = page
  494. updateCurrentBatchRow()
  495. // 重置表单
  496. auditForm.approved = true
  497. auditForm.reason = ''
  498. // 清除表单验证状态
  499. if (auditFormRef.value) {
  500. auditFormRef.value.clearValidate()
  501. }
  502. }
  503. // 取消批量审核
  504. const handleCancelBatchAudit = () => {
  505. if (isBatchAudit.value && batchAuditList.value.length > 0) {
  506. ElMessageBox.confirm(
  507. `还有 ${batchAuditList.value.length - currentBatchIndex.value + 1} 条记录未审核,确定要取消吗?`,
  508. '确认取消',
  509. {
  510. confirmButtonText: '确定取消',
  511. cancelButtonText: '继续审核',
  512. type: 'warning'
  513. }
  514. ).then(() => {
  515. auditDialogVisible.value = false
  516. resetBatchAudit()
  517. }).catch(() => {
  518. // 用户选择继续审核,不做任何操作
  519. })
  520. } else {
  521. auditDialogVisible.value = false
  522. resetBatchAudit()
  523. }
  524. }
  525. // 重置批量审核状态
  526. const resetBatchAudit = () => {
  527. batchAuditList.value = []
  528. currentBatchIndex.value = 1
  529. currentBatchRow.value = null
  530. isBatchAudit.value = false
  531. auditDialogTitle.value = '审核'
  532. auditForm.approved = true
  533. auditForm.reason = ''
  534. }
  535. // 提交审核
  536. const submitAudit = async () => {
  537. if (!auditFormRef.value) return
  538. try {
  539. await auditFormRef.value.validate()
  540. auditSubmitting.value = true
  541. if (isBatchAudit.value) {
  542. // 批量审核 - 逐条审核
  543. const row = currentBatchRow.value
  544. if (!row) {
  545. ElMessage.error('当前审核数据不存在')
  546. return
  547. }
  548. approvingId.value = row.applyId
  549. // 使用单条审核接口
  550. const res = await request.post(
  551. `${API_ENDPOINTS.MATCHMAKER_AUDIT_APPROVE}/${row.applyId}`,
  552. null,
  553. {
  554. params: {
  555. userId: row.userId,
  556. approved: auditForm.approved,
  557. reason: auditForm.reason
  558. }
  559. }
  560. )
  561. if (res.code === 200) {
  562. ElMessage.success(
  563. `${row.name || '该用户'} ${auditForm.approved ? '审核通过' : '审核不通过'}成功`
  564. )
  565. // 从批量审核列表中移除已审核的项
  566. const index = currentBatchIndex.value - 1
  567. batchAuditList.value.splice(index, 1)
  568. // 如果还有未审核的记录,自动跳转到下一条
  569. if (batchAuditList.value.length > 0) {
  570. // 删除后,如果当前索引超出范围(说明删除的是最后一条),则跳转到新的最后一条
  571. // 否则保持当前索引不变,因为后面的记录会前移,当前索引对应的就是下一条
  572. if (currentBatchIndex.value > batchAuditList.value.length) {
  573. currentBatchIndex.value = batchAuditList.value.length
  574. }
  575. // 更新标题
  576. const count = batchAuditList.value.length
  577. auditDialogTitle.value = count > 0 ? `批量审核 (${count}条)` : '批量审核'
  578. updateCurrentBatchRow()
  579. // 重置表单
  580. auditForm.approved = true
  581. auditForm.reason = ''
  582. // 清除表单验证状态
  583. if (auditFormRef.value) {
  584. auditFormRef.value.clearValidate()
  585. }
  586. } else {
  587. // 所有记录都已审核完成
  588. ElMessage.success('批量审核完成')
  589. auditDialogVisible.value = false
  590. selectedRows.value = []
  591. resetBatchAudit()
  592. loadList()
  593. }
  594. } else {
  595. ElMessage.error(res.msg || '审核失败')
  596. }
  597. } else {
  598. // 单个审核
  599. const row = currentAuditRow.value
  600. approvingId.value = row.applyId
  601. const res = await request.post(
  602. `${API_ENDPOINTS.MATCHMAKER_AUDIT_APPROVE}/${row.applyId}`,
  603. null,
  604. {
  605. params: {
  606. userId: row.userId,
  607. approved: auditForm.approved,
  608. reason: auditForm.reason
  609. }
  610. }
  611. )
  612. if (res.code === 200) {
  613. ElMessage.success(res.msg || (auditForm.approved ? '审核通过成功' : '审核不通过成功'))
  614. auditDialogVisible.value = false
  615. loadList()
  616. } else {
  617. ElMessage.error(res.msg || '审核失败')
  618. }
  619. }
  620. } catch (error) {
  621. if (error !== false) { // validate 失败会返回 false
  622. console.error('审核失败:', error)
  623. ElMessage.error(error.response?.data?.msg || error.message || '审核失败')
  624. }
  625. } finally {
  626. auditSubmitting.value = false
  627. approvingId.value = null
  628. }
  629. }
  630. // 查看详情
  631. const handleViewDetail = (row) => {
  632. detailData.value = { ...row }
  633. detailDialogVisible.value = true
  634. }
  635. // 删除申请
  636. const handleDelete = async (row) => {
  637. try {
  638. await ElMessageBox.confirm(
  639. `确定要删除 ${row.name || '该用户'} 的红娘申请吗?\n删除后该申请将不再显示在列表中,此操作为逻辑删除。`,
  640. '确认删除',
  641. {
  642. confirmButtonText: '确定删除',
  643. cancelButtonText: '取消',
  644. type: 'warning',
  645. dangerouslyUseHTMLString: false
  646. }
  647. )
  648. deletingId.value = row.applyId
  649. const res = await request.delete(
  650. `${API_ENDPOINTS.MATCHMAKER_AUDIT_DELETE}/${row.applyId}`
  651. )
  652. if (res.code === 200) {
  653. ElMessage.success('删除成功')
  654. loadList()
  655. } else {
  656. ElMessage.error(res.msg || '删除失败')
  657. }
  658. } catch (error) {
  659. if (error !== 'cancel') {
  660. console.error('删除失败:', error)
  661. ElMessage.error(error.response?.data?.msg || error.message || '删除失败')
  662. }
  663. } finally {
  664. deletingId.value = null
  665. }
  666. }
  667. onMounted(loadList)
  668. </script>
  669. <style scoped>
  670. .matchmaker-audit-container {
  671. padding: 20px;
  672. }
  673. .page-title {
  674. margin: 0 0 20px 0;
  675. font-size: 24px;
  676. font-weight: 600;
  677. color: #303133;
  678. }
  679. .toolbar-card {
  680. margin-bottom: 20px;
  681. }
  682. .table-card {
  683. margin-bottom: 20px;
  684. }
  685. .batch-audit-progress {
  686. font-size: 14px;
  687. color: #606266;
  688. font-weight: 500;
  689. margin-bottom: 20px;
  690. padding: 10px 15px;
  691. background-color: #f5f7fa;
  692. border-radius: 4px;
  693. text-align: center;
  694. }
  695. .current-audit-info {
  696. margin-bottom: 20px;
  697. }
  698. .current-audit-info :deep(.el-descriptions__label) {
  699. font-weight: 500;
  700. }
  701. .audit-rejected-tag {
  702. background-color: #f4f4f5;
  703. border-color: #d3d4d6;
  704. color: #909399;
  705. }
  706. .detail-content {
  707. min-height: 200px;
  708. }
  709. .detail-section {
  710. margin-bottom: 20px;
  711. }
  712. .detail-section:last-child {
  713. margin-bottom: 0;
  714. }
  715. .section-header {
  716. display: flex;
  717. align-items: center;
  718. gap: 8px;
  719. font-weight: 500;
  720. }
  721. .introduction-content {
  722. white-space: pre-wrap;
  723. word-break: break-word;
  724. line-height: 1.6;
  725. }
  726. </style>