From 9e6b0d57c26f98aa70aa1dc1903375b1f556648f Mon Sep 17 00:00:00 2001
From: shinnosuke <tantao@1hoper.com>
Date: 星期一, 24 八月 2026 17:44:53 +0800
Subject: [PATCH] 又又更新

---
 rencaika/pages/card/card.uvue                                        |   21 +++--
 talent-card-admin/src/views/MerchantCreateView.vue                   |  105 ++++++++++++++++---------
 rencaika_merchant/pages/scan/scan.uvue                               |    5 +
 rencaika_merchant/pages/merchant-onboarding/merchant-onboarding.uvue |    7 +
 rencaika_merchant/pages/withdrawal-detail/withdrawal-detail.uvue     |   42 ++++++++-
 talent-card-admin/src/views/RoleFormView.vue                         |    3 
 talent-card-admin/src/types/admin.ts                                 |    1 
 talent-card-admin/src/views/StaffManagementView.vue                  |   12 +-
 rencaika/pages/talent-code/talent-code.uvue                          |   10 +
 talent-card-admin/src/layouts/AdminLayout.vue                        |    1 
 rencaika_merchant/pages/merchant/merchant.uvue                       |    6 -
 11 files changed, 137 insertions(+), 76 deletions(-)

diff --git a/rencaika/pages/card/card.uvue b/rencaika/pages/card/card.uvue
index f8917a4..e8a83d9 100644
--- a/rencaika/pages/card/card.uvue
+++ b/rencaika/pages/card/card.uvue
@@ -48,14 +48,14 @@
 				<text class="refresh-tip">{{canShowCode ? refreshTip : '请联系服务专员了解卡片状态'}}</text>
 			</view>
 
-			<view v-if="advisorName != ''" class="service-panel">
+			<view v-if="canShowCode" class="service-panel">
 				<view class="service-title-row"><view class="title-line"></view><text>{{cardLevel}}专属服务专员</text><view class="title-line"></view></view>
-				<text class="service-advisor">{{advisorDepartment}} {{advisorName}}</text>
+				<text class="service-advisor">{{advisorName != '' ? advisorDepartment + ' ' + advisorName : '暂未配置服务专员'}}</text>
 				<view class="service-actions">
-					<view v-if="advisorConsultUrl != ''" class="service-action" hover-class="service-action-active" @tap="onlineConsult">
+					<view class="service-action" hover-class="service-action-active" @tap="onlineConsult">
 						<image src="/static/card-assets/consult.png"></image><text>在线咨询</text>
 					</view>
-					<view v-if="advisorMobile != ''" class="service-action" hover-class="service-action-active" @tap="callAdvisor">
+					<view class="service-action" hover-class="service-action-active" @tap="callAdvisor">
 						<image src="/static/card-assets/phone.png"></image><text>拨打电话</text>
 					</view>
 				</view>
@@ -75,8 +75,11 @@
 		<view v-if="showApplyNotice" class="apply-mask" @tap="closeApplyNotice">
 			<view class="apply-dialog" @tap.stop="preventClose">
 				<text class="dialog-title">温馨提示</text>
-				<text class="dialog-copy">请前往东区政务服务中心(东区大渡口街86号)区人才综合服务专窗线下进行人才认定。</text>
-				<text class="dialog-phone">0812-3509008,东区人才综合服务专窗</text>
+				<text class="dialog-copy">  请前往东区政务服务中心(东区大渡口街86号)线下进行人才认定。</text>
+				<view class="dialog-contact">
+					<text class="dialog-phone">联系电话:0812-3509008</text>
+					<text class="dialog-office">东区人才综合服务专窗</text>
+				</view>
 				<view class="dialog-confirm" hover-class="dialog-confirm-active" @tap="closeApplyNotice"><text>确定</text></view>
 			</view>
 		</view>
@@ -209,7 +212,7 @@
 		if (!advisorConsultUrl.value.match(/^https:\/\//)) { uni.showToast({ title: '暂未配置在线咨询', icon: 'none' }); return }
 		uni.navigateTo({ url: '/pages/external-webview/external-webview?source=advisor&talentId=' + encodeURIComponent(talentId.value) })
 	}
-	const callAdvisor = () => { if (advisorMobile.value != '') uni.makePhoneCall({ phoneNumber: advisorMobile.value }) }
+	const callAdvisor = () => { if (advisorMobile.value == '') { uni.showToast({ title: '暂未配置服务专员电话', icon: 'none' }); return }; uni.makePhoneCall({ phoneNumber: advisorMobile.value }) }
 	const goHome = () => uni.reLaunch({ url: '/pages/index/index' })
 	const goBenefits = () => uni.reLaunch({ url: '/pages/benefits/benefits' })
 	const goPolicy = () => uni.reLaunch({ url: '/pages/policy/policy' })
@@ -293,7 +296,9 @@
 	.apply-dialog { width: 100%; box-sizing: border-box; padding: 59rpx 47rpx 39rpx; border-radius: 32rpx; background-color: #FFFFFF; }
 	.dialog-title { display: block; color: #386BB0; font-size: 43rpx; line-height: 60rpx; font-weight: 700; text-align: center; }
 	.dialog-copy { display: block; margin-top: 44rpx; color: #737F91; font-size: 27rpx; line-height: 42rpx; font-weight: 600; }
-	.dialog-phone { display: block; margin-top: 16rpx; color: #FF8957; font-size: 27rpx; line-height: 39rpx; font-weight: 700; }
+	.dialog-contact { display: flex; margin-top: 16rpx; flex-direction: column; align-items: flex-end; }
+	.dialog-phone, .dialog-office { display: block; color: #FF8957; font-size: 27rpx; line-height: 39rpx; font-weight: 700; text-align: right; }
+	.dialog-office { margin-top: 4rpx; }
 	.dialog-confirm { display: flex; width: 200rpx; height: 60rpx; margin: 39rpx auto 0; border-radius: 31rpx; background-image: linear-gradient(90deg, #FF4A32 0%, #FF8A1B 100%); justify-content: center; align-items: center; }
 	.dialog-confirm text { color: #FFFFFF; font-size: 27rpx; font-weight: 700; }
 	.dialog-confirm-active { opacity: .78; }
diff --git a/rencaika/pages/talent-code/talent-code.uvue b/rencaika/pages/talent-code/talent-code.uvue
index 5982b8d..7306ed8 100644
--- a/rencaika/pages/talent-code/talent-code.uvue
+++ b/rencaika/pages/talent-code/talent-code.uvue
@@ -132,7 +132,6 @@
 		stopTimers()
 		codeLoading.value = true
 		codeLoadFailed.value = false
-		codeImageUrl.value = ''
 		codeMessage.value = '正在生成动态人才码'
 		issueTalentDynamicCodeForScene(talentId.value, scene.value, merchantId.value, (data: any) => {
 			codeLoading.value = false
@@ -141,8 +140,13 @@
 			codeImageUrl.value = getApiBaseUrl().replace(/\/$/, '') + (data.imagePath as string)
 			expiresAt.value = Number(data.expiresAt)
 			setCountdown()
-			const seconds = data.refreshAfterSeconds != null ? Number(data.refreshAfterSeconds) : 60
-			refreshTimer = setTimeout(() => requestDynamicCode(), Math.max(1, seconds - 2) * 1000)
+			// Refresh only after the displayed code expires so the countdown never jumps back early.
+			const refreshDelay = Math.max(1, expiresAt.value - Date.now())
+			refreshTimer = setTimeout(() => {
+				remainingSeconds.value = 0
+				refreshTip.value = '正在刷新动态人才码'
+				requestDynamicCode()
+			}, refreshDelay)
 		}, (error) => { if (pageVisible) handleCodeFailure(error.message); else codeLoading.value = false })
 	}
 	const handleCodeFailure = (message: string) => { stopTimers(); codeLoading.value = false; codeImageUrl.value = ''; codeLoadFailed.value = true; codeMessage.value = message == '' ? '动态人才码加载失败' : message; refreshTip.value = '动态人才码已失效,请重新获取' }
diff --git a/rencaika_merchant/pages/merchant-onboarding/merchant-onboarding.uvue b/rencaika_merchant/pages/merchant-onboarding/merchant-onboarding.uvue
index 70d1139..3c57d35 100644
--- a/rencaika_merchant/pages/merchant-onboarding/merchant-onboarding.uvue
+++ b/rencaika_merchant/pages/merchant-onboarding/merchant-onboarding.uvue
@@ -18,7 +18,7 @@
 
 				<view class="field-block">
 					<text class="field-label">统一社会信用代码</text>
-					<view class="input-shell" :class="invalidField == 'creditCode' ? 'field-invalid' : ''"><input class="text-input" v-model="creditCode" maxlength="18" placeholder="请输入18位统一社会信用代码" placeholder-class="input-placeholder" @input="normalizeCreditCode" /></view>
+					<view class="input-shell" :class="invalidField == 'creditCode' ? 'field-invalid' : ''"><input class="text-input" :value="creditCode" maxlength="18" placeholder="请输入18位统一社会信用代码" placeholder-class="input-placeholder" @input="normalizeCreditCode" /></view>
 					<text v-if="invalidField == 'creditCode'" class="field-error">{{errorMessage}}</text>
 				</view>
 
@@ -160,8 +160,9 @@
 		}
 	}
 
-	const normalizeCreditCode = () => {
-		creditCode.value = creditCode.value.toUpperCase().replace(/[^0-9A-Z]/g, '')
+	const normalizeCreditCode = (event: any) => {
+		const input = event != null && event.detail != null && event.detail.value != null ? '' + event.detail.value : ''
+		creditCode.value = input.toUpperCase().replace(/[^0-9A-Z]/g, '')
 		clearFieldError('creditCode')
 	}
 
diff --git a/rencaika_merchant/pages/merchant/merchant.uvue b/rencaika_merchant/pages/merchant/merchant.uvue
index 0223318..4d198c3 100644
--- a/rencaika_merchant/pages/merchant/merchant.uvue
+++ b/rencaika_merchant/pages/merchant/merchant.uvue
@@ -19,8 +19,6 @@
 			</view>
 
 			<view class="setting-card">
-				<view class="setting-row" @tap="handleMenuAction('结算管理')"><view class="setting-leading"><image class="setting-icon" src="/static/icons/lucide/hand-coins.svg" mode="aspectFit"></image><text class="setting-label">结算管理</text></view><image class="setting-chevron" src="/static/icons/lucide/chevron-right.svg" mode="aspectFit"></image></view>
-				<view class="setting-row" @tap="handleMenuAction('商户提现')"><view class="setting-leading"><image class="setting-icon" src="/static/icons/lucide/wallet-cards-gray.svg" mode="aspectFit"></image><text class="setting-label">商户提现</text></view><image class="setting-chevron" src="/static/icons/lucide/chevron-right.svg" mode="aspectFit"></image></view>
 				<view class="setting-row">
 					<view class="setting-leading"><image class="setting-icon" src="/static/icons/lucide/store.svg" mode="aspectFit"></image><text class="setting-label">营业状态</text></view>
 					<view class="business-state" :class="businessUpdating ? 'business-state-disabled' : ''" @tap="toggleBusiness"><text class="business-state-text">{{isBusinessOpen ? '营业中' : '休息中'}}</text><view class="state-switch" :class="isBusinessOpen ? 'open' : ''"><view class="state-thumb"></view></view></view>
@@ -93,10 +91,6 @@
 		}
 		if (name === '商户提现') {
 			uni.navigateTo({ url: '/pages/withdrawal/withdrawal' })
-			return
-		}
-		if (name === '结算管理') {
-			uni.navigateTo({ url: '/pages/settlement-management/settlement-management' })
 			return
 		}
 		if (name === '营业时间') {
diff --git a/rencaika_merchant/pages/scan/scan.uvue b/rencaika_merchant/pages/scan/scan.uvue
index c645ed9..26c2009 100644
--- a/rencaika_merchant/pages/scan/scan.uvue
+++ b/rencaika_merchant/pages/scan/scan.uvue
@@ -101,7 +101,10 @@
 				canRetryVerification.value = false
 				return
 			}
-			if (result.credentialType == 'TALENT_CODE') {
+			// An order QR must always enter the confirmation page. Keep the order
+			// number as a fallback discriminator for older/incorrect type payloads.
+			const isTalentCode = result.credentialType == 'TALENT_CODE' && result.orderNo.length == 0
+			if (isTalentCode) {
 				openedManualConsumption = true
 				uni.navigateTo({ url: '/pages/consume-input/consume-input?verificationId=' + encodeURIComponent(result.verificationId) })
 				return
diff --git a/rencaika_merchant/pages/withdrawal-detail/withdrawal-detail.uvue b/rencaika_merchant/pages/withdrawal-detail/withdrawal-detail.uvue
index 55dc1a3..530fb0a 100644
--- a/rencaika_merchant/pages/withdrawal-detail/withdrawal-detail.uvue
+++ b/rencaika_merchant/pages/withdrawal-detail/withdrawal-detail.uvue
@@ -1,15 +1,14 @@
 <template>
 	<view class="page">
-		<view class="navigation-bar"><view class="back-button" @tap="goBack"><image class="back-icon" src="/static/icons/lucide/back-left.svg" mode="aspectFit"></image></view><text class="page-title">商户提现</text></view>
+		<view class="navigation-bar"><view class="back-button" @tap="goBack"><image class="back-icon" src="/static/icons/lucide/back-left-white.svg" mode="aspectFit"></image></view><text class="page-title">商户提现</text></view>
 		<scroll-view class="form-scroll" scroll-y="true" :show-scrollbar="false" :enhanced="false">
-			<view class="available-row"><text class="available-label">当前可提</text><text class="available-value">¥{{availableText}}</text></view>
 			<view class="form-card">
+				<view class="available-row"><text class="available-label">当前可提</text><text class="available-value">¥{{availableText}}</text></view>
 				<view class="field-block">
 					<view class="field-row amount-field-row">
 						<text class="field-label">提现金额</text>
 						<view class="amount-input-shell" :class="error.length > 0 ? 'field-invalid' : ''">
-							<text class="amount-prefix">¥</text>
-							<input class="amount-input" v-model="amount" type="digit" maxlength="10" placeholder="请输入提现金额" placeholder-class="input-placeholder" @input="clearError" />
+							<input class="amount-input" v-model="amount" type="digit" maxlength="10" placeholder="请输入" placeholder-class="input-placeholder" @input="clearError" />
 						</view>
 					</view>
 					<text v-if="error.length > 0" class="field-error">{{error}}</text>
@@ -17,18 +16,17 @@
 				<view class="field-block bank-field-block">
 					<view class="field-row bank-field-row">
 						<text class="field-label">提现银行卡</text>
-						<view class="bank-value">
+						<view v-if="selected != null" class="bank-value">
 							<view v-if="selected != null" class="selected-bank-copy">
 								<text class="selected-bank-name">{{selected.bankName}}</text>
 								<text class="selected-card-number">{{selected.cardMasked}}</text>
 							</view>
-							<text v-else class="bank-placeholder">请选择银行卡</text>
 						</view>
 						<view class="select-bank-button" @tap="chooseBank"><text>{{selected == null ? '选择' : '更换'}}</text></view>
 					</view>
 				</view>
-				<view class="submit-button" :class="submitting ? 'submit-disabled' : ''" @tap="submit"><text>{{submitting ? '提交中' : '发起提现'}}</text></view>
 			</view>
+			<view class="submit-button" :class="submitting ? 'submit-disabled' : ''" @tap="submit"><text>{{submitting ? '提交中' : '发起提现'}}</text></view>
 			<view class="safe-space"></view>
 		</scroll-view>
 	</view>
@@ -101,4 +99,34 @@
 	.select-bank-button { width: 240rpx; height: 76rpx; margin: 18rpx 0 0; border-color: #F0D1B9; border-radius: 38rpx; color: #D97643; background-color: #FFF9F4; font-size: 28rpx; }
 	.submit-button { width: 100%; height: 88rpx; margin: 62rpx 0 0; border-radius: 44rpx; color: #FFFFFF; background-image: linear-gradient(90deg, #FF7625 0%, #FF9630 100%); font-size: 29rpx; line-height: 41rpx; font-weight: 700; box-shadow: 0 12rpx 24rpx rgba(255, 107, 36, 0.20); }
 	.safe-space { height: 70rpx; }
+
+	/* Keep the withdrawal form aligned with the compact prototype layout. */
+	page { background-color: #FFFFFF; }
+	.page { background-color: #FFFFFF; }
+	.navigation-bar { height: 144rpx; padding: 64rpx 32rpx 0; background-color: #B7B7B7; align-items: center; }
+	.back-button { left: 32rpx; top: 64rpx; width: 64rpx; height: 64rpx; }
+	.back-icon { width: 40rpx; height: 40rpx; }
+	.page-title { color: #FFFFFF; font-size: 30rpx; line-height: 42rpx; }
+	.form-scroll { padding: 34rpx 26rpx 0; background-color: #FFFFFF; }
+	.available-row { display: flex; height: 88rpx; padding: 0; flex-direction: row; justify-content: flex-start; align-items: center; }
+	.available-label, .available-value { color: #777777; font-size: 27rpx; line-height: 38rpx; font-weight: 400; }
+	.available-value { margin-left: 12rpx; }
+	.form-card { padding: 0 20rpx; border-radius: 0; background-color: #FFFFFF; box-shadow: none; }
+	.field-row { min-height: 158rpx; flex-direction: row; align-items: center; }
+	.field-label, .amount-field-row .field-label, .bank-field-row .field-label { width: 142rpx; margin: 0; padding: 0; flex: none; color: #222222; font-size: 28rpx; line-height: 40rpx; font-weight: 400; text-align: left; }
+	.amount-field-row { min-height: 158rpx; flex-direction: row; align-items: center; }
+	.amount-input-shell { width: calc(100% - 142rpx); height: 104rpx; padding: 0 18rpx; flex: none; border: 1rpx solid #D7D7D7; border-radius: 5rpx; background-color: #FFFFFF; }
+	.amount-prefix { color: #999999; font-size: 28rpx; }
+	.amount-input { height: 102rpx; margin-left: 8rpx; color: #333333; font-size: 28rpx; }
+	.input-placeholder { color: #999999; font-size: 28rpx; }
+	.bank-field-block { margin-top: 0; }
+	.bank-field-row { min-height: 158rpx; flex-direction: row; align-items: center; }
+	.bank-value { min-width: 0; margin-right: 12rpx; flex: 1; }
+	.selected-bank-copy { flex-direction: column; }
+	.selected-bank-name { color: #333333; font-size: 26rpx; }
+	.selected-card-number { margin-top: 2rpx; color: #888888; font-size: 23rpx; }
+	.select-bank-button { width: 134rpx; height: 58rpx; padding: 0; margin: 0; border: 0; border-radius: 5rpx; color: #FFFFFF; background-color: #3478FF; font-size: 27rpx; font-weight: 400; }
+	.submit-button { width: calc(100% - 40rpx); height: 80rpx; margin: 70rpx 0 0 20rpx; border-radius: 6rpx; color: #FFFFFF; background-color: #3478FF; background-image: none; font-size: 28rpx; line-height: 40rpx; font-weight: 400; box-shadow: none; }
+	.field-error { margin-left: 142rpx; }
+	.safe-space { height: 70rpx; }
 </style>
diff --git a/talent-card-admin/src/layouts/AdminLayout.vue b/talent-card-admin/src/layouts/AdminLayout.vue
index e5e8411..4746c43 100644
--- a/talent-card-admin/src/layouts/AdminLayout.vue
+++ b/talent-card-admin/src/layouts/AdminLayout.vue
@@ -83,7 +83,6 @@
 
 const reservationMenus: MenuItem[] = [
   { path: '/medical-reservations', label: '体检预约管理' },
-  { path: '/medical-applications', label: '就医申请管理' },
   { path: '/parking-plates', label: '停车车牌管理' },
   { path: '/fuel-services', label: '加油服务管理' },
   { path: '/spouse-employment', label: '配偶就业管理' },
diff --git a/talent-card-admin/src/types/admin.ts b/talent-card-admin/src/types/admin.ts
index 11d04f8..89dd3bb 100644
--- a/talent-card-admin/src/types/admin.ts
+++ b/talent-card-admin/src/types/admin.ts
@@ -181,6 +181,7 @@
   id: number | string
   merchantName: string
   merchantType?: string
+  merchantTypeCode?: string
   contactName?: string
   contactMobile?: string
   logoUrl?: string
diff --git a/talent-card-admin/src/views/MerchantCreateView.vue b/talent-card-admin/src/views/MerchantCreateView.vue
index 0376b43..4431ecf 100644
--- a/talent-card-admin/src/views/MerchantCreateView.vue
+++ b/talent-card-admin/src/views/MerchantCreateView.vue
@@ -80,35 +80,61 @@
 const isEditMode = computed(() => route.query.mode === 'edit' && hasValidMerchantId.value)
 const hasExistingMerchant = computed(() => isEditMode.value)
 const normalizedMerchantType = computed(() => String(form.merchantType || '').trim())
-const isFoodMerchant = computed(() => normalizedMerchantType.value === '餐饮服务')
-const isApartmentMerchant = computed(() => normalizedMerchantType.value === '公寓租赁')
-const isHotelMerchant = computed(() => normalizedMerchantType.value === '酒店服务')
-const isCarWashMerchant = computed(() => normalizedMerchantType.value === '自助洗车')
-const isHealthCheckMerchant = computed(() => normalizedMerchantType.value === '免费体检')
-const isParkingMerchant = computed(() => normalizedMerchantType.value === '停车服务')
-const isFuelMerchant = computed(() => normalizedMerchantType.value === '加油服务')
-const isChildcareMerchant = computed(() => normalizedMerchantType.value === '子女托管')
-const isFinancialMerchant = computed(() => normalizedMerchantType.value === '金融服务')
+const merchantTypeAliases: Record<string, string> = {
+  '餐饮服务': 'food', food: 'food',
+  '景区服务': 'scenic', scenic: 'scenic',
+  '酒店服务': 'hotel', hotel: 'hotel',
+  '公寓租赁': 'apartment', apartment: 'apartment',
+  '自助洗车': 'carWash', '自助洗车服务': 'carWash', carWash: 'carWash',
+  '免费体检': 'medical', medical: 'medical',
+  '停车服务': 'parking', parking: 'parking',
+  '加油服务': 'fuel', fuel: 'fuel',
+  '子女托管': 'children', children: 'children',
+  '金融服务': 'finance', finance: 'finance',
+  '配偶推荐就业': 'employment', employment: 'employment',
+  '行政审批': 'approval', approval: 'approval',
+  '观影服务': 'cinema', cinema: 'cinema', theatre: 'cinema', '电影院': 'cinema', '影院': 'cinema', '影城': 'cinema', '剧院': 'cinema',
+  '购房优惠': 'housing', housing: 'housing',
+  '购车服务': 'car', car: 'car',
+  '家电服务': 'appliance', appliance: 'appliance',
+  '数码服务': 'digital', digital: 'digital',
+  '文化服务': 'culture', culture: 'culture', book: 'culture',
+  '电信服务': 'telecom', telecom: 'telecom',
+  '口腔治疗保健': 'dental', dental: 'dental',
+}
+const merchantTypeCode = computed(() => {
+  const raw = String(form.merchantTypeCode || normalizedMerchantType.value).trim()
+  return merchantTypeAliases[raw] || raw
+})
+const isFoodMerchant = computed(() => merchantTypeCode.value === 'food')
+const isApartmentMerchant = computed(() => merchantTypeCode.value === 'apartment')
+const isHotelMerchant = computed(() => merchantTypeCode.value === 'hotel')
+const isCarWashMerchant = computed(() => merchantTypeCode.value === 'carWash')
+const isHealthCheckMerchant = computed(() => merchantTypeCode.value === 'medical')
+const isParkingMerchant = computed(() => merchantTypeCode.value === 'parking')
+const isFuelMerchant = computed(() => merchantTypeCode.value === 'fuel')
+const isChildcareMerchant = computed(() => merchantTypeCode.value === 'children')
+const isFinancialMerchant = computed(() => merchantTypeCode.value === 'finance')
 const isMovieMerchant = computed(() => [
-  '观影服务', 'cinema', 'theatre', '电影院', '影院', '影城', '剧院',
-].includes(normalizedMerchantType.value))
-const isSpouseEmploymentMerchant = computed(() => normalizedMerchantType.value === '配偶推荐就业')
-const isApprovalMerchant = computed(() => normalizedMerchantType.value === '行政审批')
-const isServiceMerchant = computed(() => isFoodMerchant.value || normalizedMerchantType.value === '景区服务')
+  'cinema',
+].includes(merchantTypeCode.value))
+const isSpouseEmploymentMerchant = computed(() => merchantTypeCode.value === 'employment')
+const isApprovalMerchant = computed(() => merchantTypeCode.value === 'approval')
+const isServiceMerchant = computed(() => isFoodMerchant.value || merchantTypeCode.value === 'scenic')
 const supportsStoreConsumption = computed(() => [
-  '酒店服务', '景区服务', '餐饮服务', '口腔治疗保健', '免费体检',
-].includes(normalizedMerchantType.value) || (isCarWashMerchant.value && form.miniProgramJumpType === 'normal'))
+  'hotel', 'scenic', 'food', 'dental', 'medical',
+].includes(merchantTypeCode.value) || (isCarWashMerchant.value && form.miniProgramJumpType === 'normal'))
 const merchantAppLoginOptionalTypes = [
-  '购房优惠', '购车服务', '家电服务', '数码服务', '文化服务', '电信服务', '口腔治疗保健', '加油服务',
+  'housing', 'car', 'appliance', 'digital', 'culture', 'telecom', 'dental', 'fuel',
 ]
 const simplifiedMerchantSettingsTypes = [
-  '购房优惠', '购车服务', '家电服务', '数码服务', '文化服务', '电信服务', '口腔治疗保健', '加油服务',
+  'housing', 'car', 'appliance', 'digital', 'culture', 'telecom', 'dental', 'fuel',
 ]
-const usesSimplifiedMerchantSettings = computed(() => simplifiedMerchantSettingsTypes.includes(normalizedMerchantType.value))
+const usesSimplifiedMerchantSettings = computed(() => simplifiedMerchantSettingsTypes.includes(merchantTypeCode.value))
 const settingsAndRightsOnlyMerchantTypes = [
-  '加油服务', '购房优惠', '购车服务', '家电服务', '数码服务', '文化服务', '电信服务',
+  'fuel', 'housing', 'car', 'appliance', 'digital', 'culture', 'telecom',
 ]
-const usesSettingsAndRightsOnlyTabs = computed(() => settingsAndRightsOnlyMerchantTypes.includes(normalizedMerchantType.value))
+const usesSettingsAndRightsOnlyTabs = computed(() => settingsAndRightsOnlyMerchantTypes.includes(merchantTypeCode.value))
 const supportsMerchantAppLogin = computed(() => (
   isServiceMerchant.value
   || isApartmentMerchant.value
@@ -121,10 +147,10 @@
   || isMovieMerchant.value
   || isSpouseEmploymentMerchant.value
   || isApprovalMerchant.value
-  || merchantAppLoginOptionalTypes.includes(normalizedMerchantType.value)
+  || merchantAppLoginOptionalTypes.includes(merchantTypeCode.value)
 ))
 const requiresMerchantOperator = computed(() => (!isEditMode.value || !merchantHasLoginAccount.value) && (
-  normalizedMerchantType.value === '景区服务'
+  merchantTypeCode.value === 'scenic'
   || (supportsMerchantAppLogin.value && Number(form.canLoginMerchantApp) === 1)
 ))
 function carWashJumpMeta() {
@@ -167,7 +193,8 @@
 
 function defaultMerchantRecommend() {
   const merchantType = queryString('merchantType')
-  return merchantType === '餐饮服务' || merchantType === '景区服务' ? 1 : 0
+  const merchantTypeCode = merchantTypeAliases[merchantType] || merchantType
+  return merchantTypeCode === 'food' || merchantTypeCode === 'scenic' ? 1 : 0
 }
 
 function applyNewMerchantDefaults() {
@@ -175,7 +202,9 @@
 
   const merchantType = queryString('merchantType')
   form.merchantType = merchantType
-  form.recommend = merchantType === '餐饮服务' || merchantType === '景区服务' ? 1 : 0
+  form.merchantTypeCode = ''
+  const merchantTypeCode = merchantTypeAliases[merchantType] || merchantType
+  form.recommend = merchantTypeCode === 'food' || merchantTypeCode === 'scenic' ? 1 : 0
   form.sortNo = undefined
 }
 
@@ -227,7 +256,7 @@
   ]
 })
 
-const form = reactive<MerchantPayload & { createdAt: string }>({
+const form = reactive<MerchantPayload & { createdAt: string; merchantTypeCode?: string }>({
   merchantName: '',
   merchantType: queryString('merchantType', ''),
   contactMobile: '',
@@ -548,18 +577,18 @@
 const businessHours = reactive<BusinessHour[]>([{ id: 1, value: [] }])
 
 function defaultMerchantRights(): MerchantRightConfig[] {
-  const currentType = queryString('merchantType', '') || normalizedMerchantType.value
-  const supportsDefaultStoreConsumption = ['餐饮服务', '景区服务', '口腔治疗保健', '免费体检'].includes(currentType)
-    || (currentType === '自助洗车' && form.miniProgramJumpType === 'normal')
-  const isHotel = currentType === '酒店服务'
-  const isCarWash = currentType === '自助洗车'
-  const isHealthCheck = currentType === '免费体检'
-  const isParking = currentType === '停车服务'
-  const isChildcare = currentType === '子女托管'
-  const isFinancial = currentType === '金融服务'
-  const isMovie = currentType === '观影服务'
-  const isSpouseEmployment = currentType === '配偶推荐就业'
-  const isApproval = currentType === '行政审批'
+  const currentType = merchantTypeCode.value
+  const supportsDefaultStoreConsumption = ['food', 'scenic', 'dental', 'medical'].includes(currentType)
+    || (currentType === 'carWash' && form.miniProgramJumpType === 'normal')
+  const isHotel = currentType === 'hotel'
+  const isCarWash = currentType === 'carWash'
+  const isHealthCheck = currentType === 'medical'
+  const isParking = currentType === 'parking'
+  const isChildcare = currentType === 'children'
+  const isFinancial = currentType === 'finance'
+  const isMovie = currentType === 'cinema'
+  const isSpouseEmployment = currentType === 'employment'
+  const isApproval = currentType === 'approval'
 
   if (isHealthCheck) {
     return [
diff --git a/talent-card-admin/src/views/RoleFormView.vue b/talent-card-admin/src/views/RoleFormView.vue
index 98981f7..3471b3e 100644
--- a/talent-card-admin/src/views/RoleFormView.vue
+++ b/talent-card-admin/src/views/RoleFormView.vue
@@ -38,11 +38,8 @@
   {
     name: '预约管理',
     pages: [
-      { code: 'appointment-management', name: '预约服务管理', actions: ['查看', '审核', '下载'] },
       { code: 'medical-reservations', name: '体检预约管理', actions: ['查看', '审核', '下载'] },
-      { code: 'medical-applications', name: '就医申请管理', actions: ['查看', '审核', '下载'] },
       { code: 'parking-plates', name: '停车车牌管理', actions: ['查看', '审核', '下载'] },
-      { code: 'car-wash-benefits', name: '洗车权益管理', actions: ['查看', '审核'] },
       { code: 'fuel-services', name: '加油服务管理', actions: ['查看', '审核', '下载'] },
       { code: 'spouse-employment', name: '配偶就业管理', actions: ['查看', '审核', '下载'] },
       { code: 'hotel-reservations', name: '酒店预约管理', actions: ['查看', '审核', '下载'] },
diff --git a/talent-card-admin/src/views/StaffManagementView.vue b/talent-card-admin/src/views/StaffManagementView.vue
index fec4397..e815f9f 100644
--- a/talent-card-admin/src/views/StaffManagementView.vue
+++ b/talent-card-admin/src/views/StaffManagementView.vue
@@ -48,7 +48,7 @@
     { required: true, message: "请输入登录账号", trigger: "blur" },
     {
       validator: (_rule, value, callback) => {
-        if (!editId.value && value && !accountPattern.test(String(value).trim())) {
+        if (value && !accountPattern.test(String(value).trim())) {
           callback(new Error("登录账号仅支持英文、数字或英文+数字"));
           return;
         }
@@ -116,8 +116,8 @@
 const keepMobileDigits = () => {
   form.mobile = form.mobile.replace(/\D/g, "").slice(0, 11);
 };
-const normalizeAccount = () => {
-  form.account = form.account.replace(/[^A-Za-z0-9]/g, "").slice(0, 32);
+const normalizeAccount = (value: string) => {
+  form.account = String(value || "").replace(/[^A-Za-z0-9]/g, "").slice(0, 32);
   accountTaken.value = false;
 };
 async function checkAccountAvailable() {
@@ -344,11 +344,11 @@
             @input="keepMobileDigits" /></el-form-item
         ><el-form-item label="登录账号" prop="account" required :error="accountTaken ? '登录账号已存在,请更换' : ''"
           ><el-input
-            v-model="form.account"
+            :model-value="form.account"
             :disabled="Boolean(editId)"
             maxlength="32"
             placeholder="仅支持英文和数字"
-            @input="normalizeAccount"
+            @update:model-value="normalizeAccount"
             @blur="checkAccountAvailable" /></el-form-item
         ><el-form-item label="登录密码" prop="password" :required="!editId"
           ><el-input
@@ -357,7 +357,7 @@
             :placeholder="
               editId ? '留空则不修改' : '至少 6 位'
             " /></el-form-item
-        ><el-form-item v-if="!editId || form.password" label="确认密码" prop="confirmPassword" :required="!editId || Boolean(form.password)"
+        ><el-form-item label="确认密码" prop="confirmPassword" :required="!editId || Boolean(form.password)"
           ><el-input
             v-model="form.confirmPassword"
             type="password"

--
Gitblit v1.7.1