From a375ecc695efe1739871e9d9d0bf981454aa8061 Mon Sep 17 00:00:00 2001
From: shinnosuke <tantao@1hoper.com>
Date: 星期二, 11 八月 2026 10:20:35 +0800
Subject: [PATCH] fix: restore features overwritten by merge

---
 talent-card-admin/src/views/BusinessManagementView.vue                                                |   76 ++++++++++++++++---
 talent-card-admin/src/views/FamilyCardApplicationDetailView.vue                                       |   15 ---
 talent-card-admin/src/types/admin.ts                                                                  |    8 ++
 talent-card-server/src/main/java/com/talentcard/server/response/DependentCardApplyDetailResponse.java |   46 +++++++++++
 rencaika/pages/external-webview/external-webview.uvue                                                 |   31 +++++++
 talent-card-admin/src/router/index.ts                                                                 |    9 +-
 talent-card-admin/src/layouts/AdminLayout.vue                                                         |   23 +++++
 7 files changed, 172 insertions(+), 36 deletions(-)

diff --git a/rencaika/pages/external-webview/external-webview.uvue b/rencaika/pages/external-webview/external-webview.uvue
index 3a553b2..f1ef6a6 100644
--- a/rencaika/pages/external-webview/external-webview.uvue
+++ b/rencaika/pages/external-webview/external-webview.uvue
@@ -12,7 +12,8 @@
 </template>
 
 <script setup lang="uts">
-	import { getCarWashMerchantDetail, getTalentCardProfile } from '../../services/portal.uts'
+	import { getAppHome, getCarWashMerchantDetail, getTalentCardProfile } from '../../services/portal.uts'
+	import { getApiBaseUrl } from '../../config/api.uts'
 	import { safeBack } from '../../utils/navigation.uts'
 
 	type PageState = 'loading' | 'ready' | 'error' | 'invalid'
@@ -30,6 +31,7 @@
 		merchantId.value = options != null && options['merchantId'] != null ? options['merchantId'] as string : ''
 		talentId.value = options != null && options['talentId'] != null ? options['talentId'] as string : ''
 		talentCardId.value = options != null && options['talentCardId'] != null ? options['talentCardId'] as string : ''
+		if (source.value == 'homeMiniProgram') uni.setNavigationBarTitle({ title: '首页服务' })
 		if (!isValidSource()) {
 			showInvalid('服务地址无效', '请从已配置的服务入口重新进入')
 			return
@@ -38,7 +40,14 @@
 	})
 
 	const isSafeWebUrl = (value: string): boolean => /^https:\/\/[A-Za-z0-9.-]+(?::\d+)?(?:\/|$)/.test(value)
-	const isValidSource = (): boolean => (source.value == 'carWash' && /^\d+$/.test(merchantId.value)) || (source.value == 'advisor' && /^\d+$/.test(talentId.value))
+	const isSafeHomeLinkUrl = (value: string): boolean => {
+		if (isSafeWebUrl(value)) return true
+		const apiHost = getApiBaseUrl().replace(/^https?:\/\//, '').split('/')[0].split(':')[0]
+		if (apiHost == '') return false
+		const escapedHost = apiHost.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+		return new RegExp('^http://' + escapedHost + '(?::\\d+)?(?:/|$)').test(value)
+	}
+	const isValidSource = (): boolean => (source.value == 'carWash' && /^\d+$/.test(merchantId.value)) || (source.value == 'advisor' && /^\d+$/.test(talentId.value)) || source.value == 'homeMiniProgram'
 	const loadExternalService = () => {
 		if (!isValidSource()) {
 			showInvalid('服务地址无效', '请从已配置的服务入口重新进入')
@@ -48,6 +57,7 @@
 		stateTitle.value = '正在打开服务'
 		stateDescription.value = '请稍候'
 		if (source.value == 'advisor') { loadAdvisorService(); return }
+		if (source.value == 'homeMiniProgram') { loadHomeMiniProgram(); return }
 		getCarWashMerchantDetail(merchantId.value, talentCardId.value, (data: any) => {
 			if (data == null || data.available !== true) {
 				showInvalid('服务暂不可用', data != null && data.unavailableReason != null ? data.unavailableReason as string : '请稍后重试')
@@ -56,6 +66,21 @@
 			const url = data.externalLinkType == 'web' && data.externalWebUrl != null ? data.externalWebUrl as string : ''
 			if (!isSafeWebUrl(url)) {
 				showInvalid('服务暂不可用', '服务地址暂未配置或未通过安全校验')
+				return
+			}
+			targetUrl.value = url
+			pageState.value = 'ready'
+		}, () => {
+			pageState.value = 'error'
+			stateTitle.value = '服务加载失败'
+			stateDescription.value = '请检查网络后重新加载'
+		})
+	}
+	const loadHomeMiniProgram = () => {
+		getAppHome((data: any) => {
+			const url = data != null && data.miniProgramUrl != null ? data.miniProgramUrl as string : ''
+			if (!isSafeHomeLinkUrl(url)) {
+				showInvalid('首页跳转链接不可用', '请在后台配置有效链接;生产环境请使用 HTTPS')
 				return
 			}
 			targetUrl.value = url
@@ -93,7 +118,7 @@
 		stateTitle.value = '网页加载失败'
 		stateDescription.value = '请稍后重新加载或返回服务详情'
 	}
-	const goBack = () => safeBack(source.value == 'advisor' ? '/pages/card/card?preview=member&talentId=' + encodeURIComponent(talentId.value) : '/pages/car-wash-merchant/car-wash-merchant?merchantId=' + encodeURIComponent(merchantId.value))
+	const goBack = () => safeBack(source.value == 'advisor' ? '/pages/card/card?preview=member&talentId=' + encodeURIComponent(talentId.value) : (source.value == 'homeMiniProgram' ? '/pages/index/index' : '/pages/car-wash-merchant/car-wash-merchant?merchantId=' + encodeURIComponent(merchantId.value)))
 </script>
 
 <style>
diff --git a/talent-card-admin/src/layouts/AdminLayout.vue b/talent-card-admin/src/layouts/AdminLayout.vue
index 6220948..87b55bd 100644
--- a/talent-card-admin/src/layouts/AdminLayout.vue
+++ b/talent-card-admin/src/layouts/AdminLayout.vue
@@ -34,6 +34,7 @@
 const route = useRoute()
 const router = useRouter()
 const appStore = useAppStore()
+const avatarText = computed(() => (appStore.user.name || 'A').trim().slice(0, 1).toUpperCase())
 
 interface HeaderNotice {
   id: number
@@ -128,6 +129,24 @@
   { index: 'operation-management', label: '运营管理', icon: Operation, items: operationMenus },
   { index: 'system-management', label: '系统管理', icon: Setting, items: systemMenus },
 ]
+
+// Keep the navigation in sync with the permissions returned at login.  Newer
+// routes without a dedicated permission code intentionally remain visible.
+const routePermissions: Record<string, string> = {
+  '/talents': 'admin.talents.view', '/family-card-applications': 'admin.family-cards.view', '/upgrade-applications': 'admin.talents.view', '/family-cards': 'admin.family-cards.view', '/child-cards': 'admin.family-cards.view',
+  '/merchants': 'admin.merchants.view', '/products': 'admin.products.view', '/orders': 'admin.orders.view', '/refund-records': 'admin.refunds.view', '/hotel-free-verifications': 'admin.orders.view',
+  '/medical-reservations': 'admin.medical-reservations.view', '/medical-applications': 'admin.medical-reservations.view', '/parking-plates': 'admin.parking-plates.view', '/fuel-services': 'admin.parking-plates.view', '/spouse-employment': 'admin.parking-plates.view', '/hotel-reservations': 'admin.medical-reservations.view',
+  '/merchant-settlement': 'admin.merchants.view', '/equity-images': 'admin.equity-images.view', '/talent-news-management': 'admin.news.view', '/talent-policy-management': 'admin.news.view', '/movie-tickets': 'admin.news.view',
+  '/system-settings': 'admin.settings.view', '/protocol-settings': 'admin.settings.view', '/roles': 'admin.roles.view', '/staff': 'admin.staff.view',
+}
+const canAccess = (path?: string) => {
+  if (!path || appStore.user.role === '平台管理') return true
+  const required = routePermissions[path]
+  return !required || Boolean(appStore.user.permissionCodes?.includes('*') || appStore.user.permissionCodes?.includes(required))
+}
+const visibleMenuGroups = computed(() => menuGroups
+  .map((group) => ({ ...group, items: group.items.filter((item) => canAccess(item.path)) }))
+  .filter((group) => group.items.length > 0))
 
 const defaultOpeneds = computed(() => {
   const currentGroup = menuGroups.find((item) => findMenuLabel(item.items, activeMenu.value))
@@ -288,7 +307,7 @@
         <el-tooltip content="退出登录" placement="bottom">
           <el-button :icon="SwitchButton" circle @click="logout" />
         </el-tooltip>
-        <div class="admin-avatar" title="平台管理员" aria-label="平台管理员头像">A</div>
+        <div class="admin-avatar" :title="appStore.user.name" :aria-label="`${appStore.user.name}头像`">{{ avatarText }}</div>
       </div>
     </el-header>
 
@@ -306,7 +325,7 @@
             <span>首页</span>
           </el-menu-item>
 
-          <el-sub-menu v-for="group in menuGroups" :key="group.index" :index="group.index">
+          <el-sub-menu v-for="group in visibleMenuGroups" :key="group.index" :index="group.index">
             <template #title>
               <el-icon>
                 <component :is="group.icon" />
diff --git a/talent-card-admin/src/router/index.ts b/talent-card-admin/src/router/index.ts
index 5e60950..84705d7 100644
--- a/talent-card-admin/src/router/index.ts
+++ b/talent-card-admin/src/router/index.ts
@@ -4,6 +4,7 @@
 
 const familyCardView = () => import('../views/FamilyCardView.vue')
 const businessManagementView = () => import('../views/BusinessManagementView.vue')
+const cardPlaceholderView = () => import('../views/CardPlaceholderView.vue')
 
 const router = createRouter({
   history: createWebHistory(),
@@ -154,7 +155,7 @@
         {
           path: 'appointment-management',
           name: 'appointment-management',
-          component: () => import('../views/AppointmentManagementView.vue'),
+          component: cardPlaceholderView,
           meta: { title: '预约服务管理', businessKind: 'appointmentManagement' },
         },
         {
@@ -196,7 +197,7 @@
         {
           path: 'car-wash-benefits',
           name: 'car-wash-benefits',
-          component: () => import('../views/CarWashBenefitView.vue'),
+          component: cardPlaceholderView,
           meta: { title: '洗车权益管理', businessKind: 'carWashBenefit' },
         },
         {
@@ -238,13 +239,13 @@
         {
           path: 'benefit-coupons',
           name: 'benefit-coupons',
-          component: () => import('../views/BenefitCouponManagementView.vue'),
+          component: cardPlaceholderView,
           meta: { title: '优惠券管理', businessKind: 'benefitCoupons' },
         },
         {
           path: 'display-services',
           name: 'display-services',
-          component: () => import('../views/DisplayServiceManagementView.vue'),
+          component: cardPlaceholderView,
           meta: { title: '展示类服务管理', businessKind: 'displayServices' },
         },
         {
diff --git a/talent-card-admin/src/types/admin.ts b/talent-card-admin/src/types/admin.ts
index ccc237e..7ab8dbd 100644
--- a/talent-card-admin/src/types/admin.ts
+++ b/talent-card-admin/src/types/admin.ts
@@ -560,21 +560,29 @@
 export interface FamilyCardApplication {
   id: number | string
   applicationNo?: string
+  applyNo?: string
   talentId?: number | string
   talentName?: string
   talentMobile?: string
   talentCardNo?: string
   cardLevel?: string
   familyName?: string
+  dependentName?: string
   familyMobile?: string
+  dependentMobile?: string
   relationship?: string
+  relation?: string
   idCardNo?: string
+  dependentIdCardNo?: string
   applyTime?: string
+  createdAt?: string
   status?: string | number
   statusText?: string
   auditTime?: string
   auditorName?: string
+  auditBy?: string
   auditRemark?: string
+  proofFiles?: string | string[]
   familyTalentId?: number | string
   familyCardId?: number | string
   cardId?: number | string
diff --git a/talent-card-admin/src/views/BusinessManagementView.vue b/talent-card-admin/src/views/BusinessManagementView.vue
index 324e906..a3e7f77 100644
--- a/talent-card-admin/src/views/BusinessManagementView.vue
+++ b/talent-card-admin/src/views/BusinessManagementView.vue
@@ -2,14 +2,17 @@
 import { computed, onMounted, reactive, ref, watch } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import { Download, Edit, Plus, Refresh, Search, View } from '@element-plus/icons-vue'
-import { ElMessage } from 'element-plus'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import type { FormInstance, FormRules } from 'element-plus'
 import {
   createAdminRole,
   createAdminStaff,
+  deleteAdminRole,
   getAdminRoles,
   getAdminStaff,
   getUpgradeApplications,
   updateAdminRole,
+  updateAdminRoleStatus,
   updateAdminStaff,
 } from '../api/admin'
 import type { AdminRole, AdminStaff } from '../types/admin'
@@ -25,25 +28,54 @@
 const rows = ref<Record<string, unknown>[]>([])
 const dialogVisible = ref(false)
 const editVisible = ref(false)
+const formRef = ref<FormInstance>()
 const activeRow = ref<Record<string, unknown>>()
 const editingId = ref<number | string>()
 const filters = reactive({ keyword: '', cardLevel: '', status: '', timeRange: [] as string[] })
 const pagination = reactive({ page: 1, size: 10, total: 0 })
 const form = reactive({ name: '', code: '', password: '', remark: '', status: 1 })
+const accountPattern = /^[A-Za-z0-9]+$/
+const rules = computed<FormRules>(() => ({
+  name: [{ required: true, message: isRole.value ? '请输入角色名称' : '请输入员工姓名', trigger: 'blur' }],
+  code: [
+    { required: true, message: isRole.value ? '请输入角色编码' : '请输入登录账号', trigger: 'blur' },
+    {
+      validator: (_rule, value, callback) => !isRole.value && value && !accountPattern.test(String(value).trim())
+        ? callback(new Error('登录账号仅支持英文、数字或英文+数字'))
+        : callback(),
+      trigger: 'blur',
+    },
+  ],
+  password: [{
+    validator: (_rule, value, callback) => !isRole.value && !editingId.value && !value
+      ? callback(new Error('请设置登录密码'))
+      : callback(),
+    trigger: 'blur',
+  }],
+}))
+
+const filteredRows = computed(() => isRole.value
+  ? rows.value.filter((row) => (!filters.keyword || String(row.roleName || '').includes(filters.keyword)) && (!filters.status || String(row.status) === filters.status))
+  : rows.value)
+const pagedRows = computed(() => isUpgrade.value
+  ? rows.value
+  : filteredRows.value.slice((pagination.page - 1) * pagination.size, pagination.page * pagination.size))
+const pageTotal = computed(() => isRole.value ? filteredRows.value.length : pagination.total)
 
 const columns = computed(() => isUpgrade.value
   ? [['applicationNo', '申请编号'], ['talentName', '持卡人名称'], ['talentMobile', '持卡人手机号'], ['targetLevel', '卡片等级'], ['applyTime', '申请时间'], ['status', '状态']]
   : isRole.value
-    ? [['roleCode', '角色编码'], ['roleName', '角色名称'], ['remark', '备注'], ['statusText', '状态'], ['createdAt', '创建时间']]
+    ? [['serialNo', '序号'], ['roleName', '角色名称'], ['remark', '备注'], ['statusText', '状态'], ['createdAt', '创建时间']]
     : [['account', '登录账号'], ['displayName', '员工姓名'], ['roleCode', '所属角色'], ['statusText', '状态'], ['createdAt', '创建时间']])
 
 function statusText(value: unknown) { return Number(value) === 1 ? '启用' : '停用' }
 function formatDateTime(value: unknown) {
   return typeof value === 'string' && value ? value.replace('T', ' ').slice(0, 19) : ''
 }
-function normalizeRole(row: AdminRole) { return { ...row, createdAt: formatDateTime(row.createdAt), statusText: statusText(row.status) } }
+function normalizeRole(row: AdminRole, index: number) { return { ...row, serialNo: index + 1, createdAt: formatDateTime(row.createdAt), statusText: statusText(row.status) } }
 function normalizeStaff(row: AdminStaff) { return { ...row, createdAt: formatDateTime(row.createdAt), statusText: statusText(row.status) } }
 function normalizeUpgrade(row: Record<string, unknown>) { return { ...row, applyTime: formatDateTime(row.applyTime) } }
+function isPlatformAdminRole(row: Record<string, unknown>) { return String(row.roleCode || '') === 'platform_admin' }
 
 async function loadData() {
   loading.value = true
@@ -64,6 +96,7 @@
 
 function search() { pagination.page = 1; loadData() }
 function reset() { filters.keyword = ''; filters.cardLevel = ''; filters.status = ''; filters.timeRange = []; search() }
+function changePage() { if (isUpgrade.value) loadData() }
 function view(row: Record<string, unknown>) {
   if (isUpgrade.value) {
     router.push(`/upgrade-applications/${row.id}`)
@@ -88,7 +121,8 @@
 }
 
 async function save() {
-  if (!form.name.trim() || !form.code.trim()) { ElMessage.warning('请填写名称和编码/账号'); return }
+  const valid = await formRef.value?.validate().catch(() => false)
+  if (!valid) return
   if (isRole.value) {
     const payload = { roleName: form.name.trim(), roleCode: form.code.trim(), remark: form.remark.trim(), status: form.status }
     if (editingId.value) await updateAdminRole(editingId.value, payload); else await createAdminRole(payload)
@@ -100,6 +134,20 @@
   ElMessage.success('保存成功')
   editVisible.value = false
   loadData()
+}
+
+async function changeRoleStatus(row: Record<string, unknown>) {
+  const status = Number(row.status) === 1 ? 0 : 1
+  await updateAdminRoleStatus(row.id as number | string, status)
+  ElMessage.success(status === 1 ? '已启用' : '已停用')
+  await loadData()
+}
+
+async function removeRole(row: Record<string, unknown>) {
+  await ElMessageBox.confirm(`确认删除角色“${row.roleName}”?`, '删除角色', { type: 'warning' })
+  await deleteAdminRole(row.id as number | string)
+  ElMessage.success('已删除')
+  await loadData()
 }
 
 async function download() {
@@ -157,29 +205,33 @@
         <el-button v-if="!isUpgrade" type="primary" :icon="Plus" @click="openCreate">新建</el-button>
         <el-button :icon="Download" @click="download">下载</el-button>
       </div>
-      <el-table v-loading="loading" :data="rows" border class="business-fill-table" :empty-text="`暂无${title}数据`">
+      <el-table v-loading="loading" :data="pagedRows" border class="business-fill-table" :empty-text="`暂无${title}数据`">
         <el-table-column v-for="[key, label] in columns" :key="key" :prop="key" :label="label" min-width="130" show-overflow-tooltip />
         <el-table-column label="操作" width="150" fixed="right" align="center">
           <template #default="{ row }">
             <el-button link type="primary" :icon="View" @click="view(row)">查看</el-button>
             <el-button v-if="!isUpgrade" link type="primary" :icon="Edit" @click="openEdit(row)">编辑</el-button>
+            <template v-if="isRole && !isPlatformAdminRole(row)">
+              <el-button link type="primary" @click="changeRoleStatus(row)">{{ Number(row.status) === 1 ? '停用' : '启用' }}</el-button>
+              <el-button link type="danger" @click="removeRole(row)">删除</el-button>
+            </template>
           </template>
         </el-table-column>
       </el-table>
       <div class="pagination-row">
-        <el-pagination v-if="isUpgrade" v-model:current-page="pagination.page" v-model:page-size="pagination.size" background layout="total, prev, pager, next" :total="pagination.total" @change="loadData" />
+        <el-pagination v-model:current-page="pagination.page" v-model:page-size="pagination.size" background layout="total, sizes, prev, pager, next" :page-sizes="[10, 20, 50]" :total="pageTotal" @change="changePage" />
       </div>
     </section>
-    <el-dialog v-model="dialogVisible" :title="title" width="520px">
+    <el-dialog v-model="dialogVisible" :title="title" width="520px" :close-on-click-modal="false">
       <el-descriptions v-if="activeRow" :column="1" border>
         <el-descriptions-item v-for="[key, label] in columns" :key="key" :label="label">{{ activeRow[key] || '-' }}</el-descriptions-item>
       </el-descriptions>
     </el-dialog>
-    <el-dialog v-model="editVisible" :title="`编辑${title}`" width="480px" destroy-on-close>
-      <el-form label-width="92px">
-        <el-form-item :label="isRole ? '角色名称' : '员工姓名'"><el-input v-model="form.name" /></el-form-item>
-        <el-form-item :label="isRole ? '角色编码' : '登录账号'"><el-input v-model="form.code" /></el-form-item>
-        <el-form-item v-if="!isRole" label="登录密码"><el-input v-model="form.password" type="password" :placeholder="editingId ? '留空则不修改' : '请输入登录密码'" /></el-form-item>
+    <el-dialog v-model="editVisible" :title="`编辑${title}`" width="480px" destroy-on-close :close-on-click-modal="false">
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="92px">
+        <el-form-item :label="isRole ? '角色名称' : '员工姓名'" prop="name"><el-input v-model="form.name" /></el-form-item>
+        <el-form-item :label="isRole ? '角色编码' : '登录账号'" prop="code"><el-input v-model="form.code" :placeholder="isRole ? '' : '仅支持英文和数字'" maxlength="32" /></el-form-item>
+        <el-form-item v-if="!isRole" label="登录密码" prop="password"><el-input v-model="form.password" type="password" :placeholder="editingId ? '留空则不修改' : '请输入登录密码'" /></el-form-item>
         <el-form-item v-if="isRole" label="备注"><el-input v-model="form.remark" /></el-form-item>
         <el-form-item label="状态"><el-switch v-model="form.status" :active-value="1" :inactive-value="0" /></el-form-item>
       </el-form>
diff --git a/talent-card-admin/src/views/FamilyCardApplicationDetailView.vue b/talent-card-admin/src/views/FamilyCardApplicationDetailView.vue
index 01525ce..53e9997 100644
--- a/talent-card-admin/src/views/FamilyCardApplicationDetailView.vue
+++ b/talent-card-admin/src/views/FamilyCardApplicationDetailView.vue
@@ -29,20 +29,6 @@
   return text(status)
 }
 
-function formatFiles(value?: string | string[] | null) {
-  if (!value) return [] as string[]
-  if (Array.isArray(value)) return value.filter(Boolean)
-  const raw = String(value).trim()
-  if (!raw) return [] as string[]
-  try {
-    const parsed = JSON.parse(raw)
-    if (Array.isArray(parsed)) return parsed.filter(Boolean).map(String)
-  } catch {
-    // ignore
-  }
-  return raw.split(/[;,\n]/).map(item => item.trim()).filter(Boolean)
-}
-
 function familyName() {
   return detail.value?.familyName || detail.value?.dependentName || '-'
 }
@@ -86,7 +72,6 @@
   { label: '审核备注:', value: text(detail.value?.auditRemark) },
 ])
 
-const proofFiles = computed(() => formatFiles(detail.value?.proofFiles as unknown as string | string[] | null))
 const isPending = computed(() => detail.value?.status === 0 || detail.value?.status === '0' || detail.value?.statusText === '待审核')
 
 async function loadDetail() {
diff --git a/talent-card-server/src/main/java/com/talentcard/server/response/DependentCardApplyDetailResponse.java b/talent-card-server/src/main/java/com/talentcard/server/response/DependentCardApplyDetailResponse.java
new file mode 100644
index 0000000..d04a138
--- /dev/null
+++ b/talent-card-server/src/main/java/com/talentcard/server/response/DependentCardApplyDetailResponse.java
@@ -0,0 +1,46 @@
+package com.talentcard.server.response;
+
+import java.time.LocalDateTime;
+
+import com.talentcard.server.entity.DependentCardApply;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "DependentCardApplyDetailResponse", description = "家属附属卡申请详情")
+public record DependentCardApplyDetailResponse(
+        Long id,
+        String applyNo,
+        String dependentName,
+        String dependentIdCardNo,
+        String dependentMobile,
+        String relation,
+        Integer status,
+        String statusText,
+        String auditRemark,
+        LocalDateTime applyTime
+) {
+    public static DependentCardApplyDetailResponse from(DependentCardApply apply) {
+        return new DependentCardApplyDetailResponse(
+                apply.getId(),
+                apply.getApplyNo(),
+                apply.getDependentName(),
+                apply.getDependentIdCardNo(),
+                apply.getDependentMobile(),
+                apply.getRelation(),
+                apply.getStatus(),
+                statusText(apply.getStatus()),
+                apply.getAuditRemark(),
+                apply.getApplyTime()
+        );
+    }
+
+    private static String statusText(Integer status) {
+        if (status == null) return "未知";
+        return switch (status) {
+            case DependentCardApply.STATUS_PENDING_REVIEW -> "待审核";
+            case DependentCardApply.STATUS_APPROVED -> "已通过";
+            case DependentCardApply.STATUS_REJECTED -> "已驳回";
+            case DependentCardApply.STATUS_CANCELLED -> "已取消";
+            default -> "未知";
+        };
+    }
+}

--
Gitblit v1.7.1