From fa232086731fbabcb6df070246d1b09f68930096 Mon Sep 17 00:00:00 2001
From: shinnosuke <tantao@1hoper.com>
Date: 星期三, 12 八月 2026 15:19:49 +0800
Subject: [PATCH] fix(config): 更新开发环境基础URL并优化界面显示

---
 rencaika/config/api.uts                                                                           |    2 
 rencaika/pages/card/card.uvue                                                                     |    6 +-
 rencaika/static/card-assets/card-elite.jpg                                                        |    0 
 rencaika/static/card-assets/card-outstanding.jpg                                                  |    0 
 talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalController.java      |    4 
 rencaika/pages/benefit-search/benefit-search.uvue                                                 |    4 
 talent-card-server/src/main/java/com/talentcard/server/business/auth/WeChatMiniProgramClient.java |   28 +++++++--
 rencaika/static/card-assets/card-excellence.jpg                                                   |    0 
 rencaika/pages/card-application-conditions/card-application-conditions.uvue                       |   26 ++++++--
 rencaika/static/stay-assets/hotel-hampton.jpg                                                     |    0 
 rencaika/static/benefits-assets/benefits-hero.jpg                                                 |    0 
 rencaika/services/portal.uts                                                                      |    2 
 rencaika/static/policy-assets/policy-explanation.jpg                                              |    0 
 rencaika/pages/index/index.uvue                                                                   |   39 +++++-------
 rencaika/pages/premium-benefits/premium-benefits.uvue                                             |    4 
 rencaika/static/policy-assets/policy-hero.jpg                                                     |    0 
 rencaika/static/stay-assets/hotel-vienna.jpg                                                      |    0 
 rencaika_merchant/pages/bank-card-select/bank-card-select.uvue                                    |    4 
 /dev/null                                                                                         |    0 
 rencaika/static/home-assets/top.jpg                                                               |    0 
 talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalService.java         |    3 
 rencaika/pages/policy-detail/policy-detail.uvue                                                   |    6 +-
 rencaika/static/food-assets/food-2.jpg                                                            |    0 
 rencaika/static/card-assets/talent-card-background.jpg                                            |    0 
 rencaika/static/food-assets/food-1.jpg                                                            |    0 
 rencaika_merchant/pages/merchant/merchant.uvue                                                    |    8 +-
 26 files changed, 78 insertions(+), 58 deletions(-)

diff --git a/rencaika/config/api.uts b/rencaika/config/api.uts
index f7b5b50..63eaa97 100644
--- a/rencaika/config/api.uts
+++ b/rencaika/config/api.uts
@@ -6,7 +6,7 @@
 
 // 后台提供合法域名后仅在此处填写,业务页面不得硬编码域名。
 // 微信开发者工具运行在本机,使用回环地址可避免公共网络防火墙拦截局域网转发请求。
-const developmentBaseUrl = 'http://127.0.0.1:8080'
+const developmentBaseUrl = 'http://221.182.45.100:11451'
 const stagingBaseUrl = ''
 const productionBaseUrl = ''
 
diff --git a/rencaika/pages/benefit-search/benefit-search.uvue b/rencaika/pages/benefit-search/benefit-search.uvue
index 5258907..0da10c4 100644
--- a/rencaika/pages/benefit-search/benefit-search.uvue
+++ b/rencaika/pages/benefit-search/benefit-search.uvue
@@ -90,10 +90,10 @@
 		{ id: 101, name: '乡菜郎·攀枝花特色延边菜', category: '暖心食享', merchantType: 'food', discount: '6折', address: '西区格里坪镇清香坪东街78号', image: '/static/food-assets/food-1.jpg', sort: 10 },
 		{ id: 102, name: '攀枝花希尔顿欢朋酒店', category: '舒心安居', merchantType: 'hotel', discount: '6折', address: '东区机场路10号', image: '/static/stay-assets/hotel-hampton.jpg', sort: 20 },
 		{ id: 103, name: '攀枝花万达影城', category: '怡乐生活', merchantType: 'cinema', discount: '免费券', address: '东区攀枝花大道东段699号', image: '/static/entertainment-assets/wanda-cinema.webp', sort: 30 },
-		{ id: 104, name: '攀枝花中国三线建设博物馆', category: '悦览花城', merchantType: 'scenic', discount: '优惠票', address: '仁和区花城新区干坝塘村', image: '/static/home-assets/city-intro.jpg', sort: 40 },
+		{ id: 104, name: '攀枝花中国三线建设博物馆', category: '悦览花城', merchantType: 'scenic', discount: '优惠票', address: '仁和区花城新区干坝塘村', image: '/static/common/image-fallback.svg', sort: 40 },
 		{ id: 105, name: '金府酒楼(西区店)', category: '暖心食享', merchantType: 'food', discount: '6折', address: '西区清香坪北街51号', image: '/static/food-assets/food-2.jpg', sort: 50 },
 		{ id: 106, name: '中国石油东区新福路加油站', category: '畅行无忧', merchantType: 'fuel', discount: '9.4折', address: '东区新福路20号附3-21号', image: '/static/home-assets/top.jpg', sort: 60 },
-		{ id: 107, name: '渡口公司管辖停车场', category: '畅行无忧', merchantType: 'parking', discount: '免费', address: '攀枝花市东区指定停车场', image: '/static/home-assets/city-intro.jpg', sort: 70 },
+		{ id: 107, name: '渡口公司管辖停车场', category: '畅行无忧', merchantType: 'parking', discount: '免费', address: '攀枝花市东区指定停车场', image: '/static/common/image-fallback.svg', sort: 70 },
 		{ id: 108, name: '攀商未来城', category: '惠享好物', merchantType: 'shopping', discount: '购房优惠3%', address: '东区榕树街55号', image: '/static/home-assets/top.jpg', sort: 80 },
 		{ id: 109, name: '攀枝花市中心医院', category: '臻享礼遇', merchantType: 'medical', discount: '免费体检', address: '东区攀枝花大道中段益康街34号', image: '/static/stay-assets/hotel-vienna.jpg', sort: 90 },
 		{ id: 110, name: '人才金融服务中心', category: '臻享礼遇', merchantType: 'finance', discount: '专属金融方案', address: '东区人民街中心广场', image: '/static/stay-assets/hotel-hampton.jpg', sort: 100 },
diff --git a/rencaika/pages/card-application-conditions/card-application-conditions.uvue b/rencaika/pages/card-application-conditions/card-application-conditions.uvue
index 690addf..ea27d3f 100644
--- a/rencaika/pages/card-application-conditions/card-application-conditions.uvue
+++ b/rencaika/pages/card-application-conditions/card-application-conditions.uvue
@@ -97,16 +97,25 @@
 		// #endif
 		recipient.value = '经区人才服务专窗认定的高层次人才,根据人才层次发放卓越人才卡、杰出人才卡或菁英人才卡。'
 		conditions.value = '卓越人才卡\n发放对象:经区人才服务专窗认定的一至三类高层次人才。\n\n' + excellentConditions + '\n\n杰出人才卡\n发放对象:经区人才服务专窗认定的四至五类高层次人才。\n\n' + outstandingConditions + '\n\n菁英人才卡\n发放对象:经区人才服务专窗认定的六至七类高层次人才。\n\n' + eliteConditions
-		conditionLines.value = conditions.value
-			.replace(/(([0-9]+))/g, '\n($1)')
-			.split('\n')
-			.map((line: string): string => line.trim())
-			.filter((line: string): boolean => line != '')
+		conditionLines.value = normalizeConditionLines(conditions.value)
 	})
-	const conditionLineClass = (line: string): string => {
+		const normalizeConditionLines = (value: string): string[] => {
+			const result = [] as string[]
+			for (const sourceLine of value.split('\n')) {
+				const line = sourceLine.trim()
+				if (line == '') continue
+				for (const piece of line.split(/(?=[((][0-90-9]+[))])/)) {
+					const normalized = piece.trim()
+					if (normalized != '') result.push(normalized)
+				}
+			}
+			return result
+		}
+		const conditionLineClass = (line: string): string => {
 		if (line == '卓越人才卡' || line == '杰出人才卡' || line == '菁英人才卡') return 'condition-line card-heading'
 		if (line.indexOf('发放对象:') == 0) return 'condition-line recipient-heading'
 		if (/^[一二三四五六七]类人才/.test(line)) return 'condition-line category-heading'
+		if (/^\d+\.\s*[^((]+[。.]$/.test(line)) return 'condition-line numbered-heading'
 		return 'condition-line'
 	}
 	const goBack = () => safeBack('/pages/card/card')
@@ -124,7 +133,8 @@
 	.condition-line { display: block; color: #535B66; font-size: 27rpx; line-height: 48rpx; }
 	.card-heading { margin-top: 34rpx; color: #E84B35; font-size: 32rpx; font-weight: 700; }
 	.conditions-content .card-heading:first-child { margin-top: 0; }
-	.recipient-heading { margin: 15rpx 0 22rpx; color: #34383E; font-weight: 700; text-align: center; }
-	.category-heading { margin-top: 19rpx; color: #34383E; font-weight: 700; }
+	.recipient-heading { margin: 15rpx 0 22rpx; color: #E84B35; font-weight: 700; text-align: center; }
+	.category-heading { margin-top: 19rpx; color: #34383E; font-weight: 700; text-align: center; }
+	.numbered-heading { margin-top: 19rpx; color: #34383E; font-weight: 700; }
 	.content-bottom { height: 52rpx; height: calc(52rpx + env(safe-area-inset-bottom)); }
 </style>
diff --git a/rencaika/pages/card/card.uvue b/rencaika/pages/card/card.uvue
index d0888c7..e83a5bb 100644
--- a/rencaika/pages/card/card.uvue
+++ b/rencaika/pages/card/card.uvue
@@ -10,7 +10,7 @@
 
 		<scroll-view class="content" scroll-y="true" show-scrollbar="false">
 			<view v-if="isGuest" class="guest-content">
-				<view class="guest-intro"><text class="guest-intro-title">东区人才卡</text><text class="guest-intro-copy">面向区内不同层次人才推出的“一卡三类人才卡”,提供消费优惠、便捷政务、安家补助、子女教育、创业支持等优质服务,增强人才归属感,提升人才引育用留质效,加快建设区域人才集聚高地。</text><view class="apply-button" hover-class="apply-button-active" @tap="openApplyNotice"><text>立即申请</text></view></view>
+				<view class="guest-intro"><text class="guest-intro-title">东区人才卡</text><text class="guest-intro-copy">面向区内不同层次人才推出的一二三类人才卡,提供消费优惠、便捷政务、安家补助、子女教育、创业支持等优质服务,增强人才归属感,提升人才引育用留质效,加快建设区域人才集聚高地。</text><view class="apply-button" hover-class="apply-button-active" @tap="openApplyNotice"><text>立即申请</text></view></view>
 				<view class="guest-card-item" v-for="item in guestCardTypes" :key="item.title">
 					<view class="guest-card-title"><view></view><text>{{item.title}}</text><view></view></view>
 					<view class="guest-card-preview"><image class="guest-card-image" :src="item.image" mode="aspectFit"></image></view>
@@ -294,8 +294,8 @@
 	.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-confirm { display: flex; width: 240rpx; height: 68rpx; margin: 39rpx auto 0; border-radius: 35rpx; background-image: linear-gradient(90deg, #FF4A32 0%, #FF8A1B 100%); justify-content: center; align-items: center; }
-	.dialog-confirm text { color: #FFFFFF; font-size: 28rpx; font-weight: 700; }
+	.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; }
 	@keyframes card-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
 </style>
diff --git a/rencaika/pages/index/index.uvue b/rencaika/pages/index/index.uvue
index 5bccbda..c753250 100644
--- a/rencaika/pages/index/index.uvue
+++ b/rencaika/pages/index/index.uvue
@@ -2,27 +2,24 @@
 	<view class="page">
 		<scroll-view class="scroll-page" scroll-y @scroll="handleScroll">
 			<view class="hero">
-				<image class="top-image" src="/static/home-assets/home-banner-v2.jpg" mode="aspectFill"></image>
 				<view class="hero-gradient"></view>
-				<view class="play-button" @tap="playVideo"><image src="/static/home-assets/play.svg"></image><text>向东看</text></view>
+				<view v-if="homeVideoUrl != ''" class="play-button" @tap="playVideo"><text>向东看</text></view>
 			</view>
 
 			<view class="city-panel">
-				<image class="city-mascot" src="/static/home-assets/talent-east.png" mode="aspectFit"></image>
-				<image class="city-flower" src="/static/home-assets/talent-east-flower.png" mode="aspectFit"></image>
 				<view class="city-heading"><text>才聚东区,共富未来</text><view class="city-label" @tap="openIntro"><text>幸福东区</text><image src="/static/home-assets/arrow-right.svg"></image></view></view>
 				<view class="city-grid">
-					<view class="city-card city-intro" @tap="openIntro"><image class="city-photo" src="/static/home-assets/city-intro.jpg" mode="aspectFill"></image><view class="city-shade"></view><text class="intro-name">东区简介</text><view class="intro-button"><text>查看更多</text><image src="/static/home-assets/arrow-right.svg"></image></view></view>
-					<view class="city-side"><view class="city-card city-potential"><image class="city-photo" src="/static/home-assets/city-potential.jpg" mode="aspectFill"></image><view class="city-shade"></view><text>潜力城</text></view><view class="city-card city-golden"><image class="city-photo" src="/static/home-assets/city-golden.webp" mode="aspectFill"></image><view class="city-shade"></view><text>黄金城</text></view></view>
-					<view class="city-side"><view class="city-card city-industry"><image class="city-photo" src="/static/home-assets/city-industry.webp" mode="aspectFill"></image><view class="city-shade"></view><text>兴业城</text></view><view class="city-card city-livable"><image class="city-photo" src="/static/home-assets/city-livable.jpg" mode="aspectFill"></image><view class="city-shade"></view><text>宜居城</text></view></view>
+					<view class="city-card city-intro" @tap="openIntro"><text class="intro-name">东区简介</text><view class="intro-button"><text>查看更多</text><image src="/static/home-assets/arrow-right.svg"></image></view></view>
+					<view class="city-side"><view class="city-card city-potential"><text>潜力城</text></view><view class="city-card city-golden"><text>黄金城</text></view></view>
+					<view class="city-side"><view class="city-card city-industry"><text>兴业城</text></view><view class="city-card city-livable"><text>宜居城</text></view></view>
 				</view>
 			</view>
 
-			<view class="section-head"><view class="section-title"><image src="/static/home-assets/news-mascot-x3.png"></image><text>人才资讯</text></view><text class="more" @tap="goNews">查看更多</text></view>
+			<view class="section-head"><view class="section-title"><text>人才资讯</text></view><text class="more" @tap="goNews">查看更多</text></view>
 			<view v-if="homeLoading" class="news-state"><text>资讯加载中...</text></view>
 			<view v-else-if="homeError" class="news-state" @tap="loadHome"><text>{{homeError}}</text><text class="news-retry">点击重试</text></view>
 			<view v-else>
-				<view class="news-featured" @tap="openNews(featuredNews.id)"><image src="/static/home-assets/home-banner-v2.jpg" mode="aspectFill"></image><view class="news-shade"></view><view class="top-badge"><text>置顶</text></view><view class="featured-copy"><text>{{featuredNews.title}}</text><text>{{featuredNews.date}}</text></view></view>
+				<view class="news-featured" @tap="openNews(featuredNews.id)"><image v-if="featuredNews.coverUrl != ''" :src="featuredNews.coverUrl" mode="aspectFill"></image><view class="news-shade"></view><view class="top-badge"><text>置顶</text></view><view class="featured-copy"><text>{{featuredNews.title}}</text><text>{{featuredNews.date}}</text></view></view>
 				<view class="news-list"><view class="news-row" v-for="item in news" :key="item.id" @tap="openNews(item.id)"><text class="news-name">{{item.title}}</text><view class="news-meta"><text v-if="item.isNew" class="new-badge">最新</text><text>{{item.date}}</text></view></view></view>
 			</view>
 			<view class="news-more" @tap="goNews"><text>查看更多</text></view>
@@ -32,7 +29,7 @@
 		<view class="sticky-nav" :class="isStickyNav ? 'is-solid' : ''">
 			<view class="sticky-nav-bg"></view>
 			<view class="safe-space" :style="'height:' + navTop + 'px'"></view>
-			<view class="nav-row" :style="'height:' + navHeight + 'px;padding-right:' + navRight + 'px'"><view class="nav-title"><image src="/static/home-assets/home-title-left-v3.png" mode="aspectFit"></image></view><view class="capsule-reserve" :style="'width:' + navWidth + 'px'"></view></view>
+			<view class="nav-row" :style="'height:' + navHeight + 'px;padding-right:' + navRight + 'px'"><view class="nav-title"><text>攀枝花市东区人才服务平台</text></view><view class="capsule-reserve" :style="'width:' + navWidth + 'px'"></view></view>
 		</view>
 
 		<view class="tab-bar">
@@ -72,7 +69,7 @@
 		loadHome()
 	})
 
-	const featuredNews = ref({ id: 'news-20260603-senior-talent', title: '攀枝花市东区事业单位2026年直接考核招聘副高职称及以上人才公告', date: '2026-06-03 09:19' })
+	const featuredNews = ref({ id: 'news-20260603-senior-talent', title: '攀枝花市东区事业单位2026年直接考核招聘副高职称及以上人才公告', date: '2026-06-03 09:19', coverUrl: '' })
 	const news = ref([
 		{ id: 'news-20260601-score-review', title: '关于攀枝花市东区事业单位2026年上半年公开考试招聘工作人员东区岗位加分情况、笔试总成绩及岗位排名、面试资格复审有关事宜的公告', date: '2026-06-01 16:36', isNew: true },
 		{ id: 'news-20260529-teacher-review', title: '攀枝花市2026年上半年公开考试招聘中小学教师东区岗位面试资格复审及领取面试通知书公告', date: '2026-05-29 10:42', isNew: false },
@@ -95,7 +92,7 @@
 			const articles: any[] = data != null && data.news instanceof Array ? data.news as any[] : []
 			if (articles.length == 0) return
 			const first: any = articles[0]
-			featuredNews.value = { id: '' + first.id, title: first.title != null ? first.title as string : '人才资讯', date: formatNoticeDate(first.publishTime) }
+			featuredNews.value = { id: '' + first.id, title: first.title != null ? first.title as string : '人才资讯', date: formatNoticeDate(first.publishTime), coverUrl: first.coverUrl != null && first.coverUrl != '' ? resolveApiUrl(first.coverUrl as string) : '' }
 			const nextItems: any[] = []
 			for (let index = 1; index < articles.length; index++) {
 				const item: any = articles[index]
@@ -144,31 +141,29 @@
 	.page { height: 100%; background-color: #FFFFFF; }
 	.scroll-page { height: calc(100% - 126rpx); height: calc(100% - 126rpx - env(safe-area-inset-bottom)); background-color: #FFFFFF; }
 	.hero { height: 468rpx; background-color: #FFFFFF; position: relative; overflow: hidden; }
-	.top-image { position: absolute; z-index: 0; top: 0; left: 0; width: 100%; height: 468rpx; border-bottom-left-radius: 48rpx; border-bottom-right-radius: 48rpx; }
-	.hero-gradient { position: absolute; z-index: 1; top: 0; left: 0; right: 0; height: 300rpx; background-image: linear-gradient(180deg, rgba(239,81,39,.92) 0%, rgba(239,81,39,.68) 42%, rgba(239,81,39,0) 100%); }
+	.hero-gradient { position: absolute; z-index: 1; top: 0; left: 0; right: 0; bottom: 0; border-bottom-left-radius: 48rpx; border-bottom-right-radius: 48rpx; background-image: linear-gradient(145deg, #F45A2A 0%, #FF9E4B 55%, #FFE0AC 100%); }
 	.safe-space { height: 28px; position: relative; z-index: 2; }
 	.nav-row { height: 32px; padding: 0 32rpx; position: relative; z-index: 2; flex-direction: row; align-items: center; justify-content: space-between; }
 	.sticky-nav { position: absolute; z-index: 30; top: 0; left: 0; right: 0; padding-bottom: 20rpx; }
 	.sticky-nav-bg { position: absolute; z-index: 0; top: 0; left: 0; right: 0; bottom: 0; opacity: 0; background-image: linear-gradient(180deg, #F26A38 0%, #EE542B 100%); transition: opacity 280ms ease-out; }
 	.sticky-nav.is-solid .sticky-nav-bg { opacity: 1; }
-	.nav-title { position: absolute; z-index: 1; left: 0; top: 10rpx; width: 750rpx; height: 100%; padding: 0; flex-direction: row; align-items: center; justify-content: flex-start; }
-	.nav-title image { width: 750rpx; height: 66rpx; }
+	.nav-title { position: absolute; z-index: 1; left: 30rpx; top: 10rpx; height: 100%; flex-direction: row; align-items: center; justify-content: flex-start; }
+	.nav-title text { color: #FFFFFF; font-size: 28rpx; font-weight: 700; opacity: .95; }
 	.capsule-reserve { width: 88px; height: 100%; flex-shrink: 0; }
 	.play-button { position: absolute; z-index: 2; bottom: 20rpx; left: 50%; margin-left: -78rpx; width: 156rpx; height: 56rpx; border-radius: 28rpx; background-image: linear-gradient(180deg, #FFB75A 0%, #FF6A24 100%); border-width: 2rpx; border-color: rgba(255,255,255,.82); box-shadow: 0 7rpx 14rpx rgba(234,92,26,.26); flex-direction: row; justify-content: center; align-items: center; }
-	.play-button image { width: 24rpx; height: 24rpx; margin-right: 9rpx; } .play-button text { color: #FFFFFF; font-size: 23rpx; }
+	.play-button text { color: #FFFFFF; font-size: 23rpx; }
 
 	.city-panel { margin: -10rpx 16rpx 38rpx; padding: 22rpx 0 0; overflow: visible; background-color: #FFFFFF; border-radius: 26rpx; position: relative; box-shadow: 0 -8rpx 14rpx rgba(255, 103, 39, .27); }
-	.city-mascot { position: absolute; z-index: 2; top: 45rpx; right: 174rpx; width: 112rpx; height: 86rpx; } .city-flower { position: absolute; z-index: 2; top: 6rpx; right: 6rpx; width: 46rpx; height: 72rpx; }
 	.city-heading { height: 58rpx; margin: 0 22rpx; flex-direction: row; justify-content: space-between; align-items: center; }
 	.city-heading > text { color: #F16624; font-size: 35rpx; font-weight: 700; letter-spacing: 1rpx; background-image: linear-gradient(180deg, #F45A2A 0%, #FFB257 100%); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
 	.city-label { margin-right: 48rpx; padding: 10rpx 13rpx; border-radius: 18rpx; background-image: linear-gradient(180deg, #FFB85E 0%, #FF6726 100%); box-shadow: 0 5rpx 10rpx rgba(239,101,34,.22); flex-direction: row; align-items: center; } .city-label text { color: #FFFFFF; font-size: 20rpx; } .city-label image { width: 20rpx; height: 20rpx; margin-left: 7rpx; }
-	.city-grid { box-sizing: border-box; height: 292rpx; margin-top: 18rpx; padding: 22rpx; overflow: visible; background-image: linear-gradient(180deg, #FF5F2D 0%, #FFB36B 100%); border-bottom-left-radius: 26rpx; border-bottom-right-radius: 26rpx; flex-direction: row; } .city-card { overflow: hidden; border-radius: 17rpx; position: relative; justify-content: flex-end; background-color: #455A71; } .city-photo { position: absolute; left: 0; top: 0; width: 100%; height: 100%; } .city-shade { position: absolute; left: 0; right: 0; bottom: 0; height: 92rpx; background-image: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.62) 100%); }
+	.city-grid { box-sizing: border-box; height: 292rpx; margin-top: 18rpx; padding: 22rpx; overflow: visible; background-image: linear-gradient(180deg, #FF5F2D 0%, #FFB36B 100%); border-bottom-left-radius: 26rpx; border-bottom-right-radius: 26rpx; flex-direction: row; } .city-card { overflow: hidden; border-radius: 17rpx; position: relative; justify-content: flex-end; background-image: linear-gradient(135deg, #E96C3D 0%, #F8AA56 100%); }
 	.city-intro { width: 242rpx; height: 308rpx; box-sizing: border-box; margin-right: 14rpx; padding: 22rpx; } .intro-name { position: absolute; z-index: 1; top: 50%; left: 0; right: 0; transform: translateY(-50%); color: #FFFFFF; font-size: 37rpx; font-weight: 700; text-align: center; } .intro-button { position: relative; z-index: 1; align-self: center; box-sizing: border-box; width: 146rpx; height: 42rpx; margin-top: 13rpx; padding: 0 12rpx; border-radius: 21rpx; background-image: linear-gradient(90deg, rgba(255,207,138,.92) 0%, rgba(255,101,36,.68) 100%); border-width: 1rpx; border-color: rgba(255,255,255,.42); flex-direction: row; align-items: center; justify-content: space-between; } .intro-button text { color: #FFFFFF; font-size: 19rpx; } .intro-button image { width: 19rpx; height: 19rpx; margin-left: 8rpx; }
-	.city-side { height: 308rpx; flex: 1; margin-right: 14rpx; justify-content: space-between; } .city-side:last-child { margin-right: 0; } .city-side .city-card { height: 147rpx; padding: 0; } .city-side .city-card > text { position: absolute; z-index: 1; left: 0; right: 0; bottom: 0; padding: 10rpx 12rpx; background-image: linear-gradient(90deg, rgba(236,76,118,.96) 0%, rgba(247,94,135,.82) 45%, rgba(255,169,187,0) 72%, rgba(255,169,187,0) 100%); color: #FFFFFF; font-size: 22rpx; font-weight: 700; text-shadow: 0 2rpx 8rpx rgba(0,0,0,.35); }
+	.city-side { height: 308rpx; flex: 1; margin-right: 14rpx; justify-content: space-between; } .city-side:last-child { margin-right: 0; } .city-side .city-card { height: 147rpx; padding: 0; } .city-side .city-card > text { position: absolute; z-index: 1; left: 0; right: 0; bottom: 0; padding: 10rpx 12rpx; color: #FFFFFF; font-size: 22rpx; font-weight: 700; }
 
-	.section-head { height: 93rpx; padding: 0 31rpx; flex-direction: row; align-items: center; justify-content: space-between; } .section-title { flex-direction: row; align-items: center; } .section-title image { width: 40rpx; height: 40rpx; margin-right: 12rpx; } .section-title text { color: #353535; font-size: 31rpx; font-weight: 700; } .more { color: #5C5C5C; font-size: 22rpx; }
+	.section-head { height: 93rpx; padding: 0 31rpx; flex-direction: row; align-items: center; justify-content: space-between; } .section-title { flex-direction: row; align-items: center; } .section-title text { color: #353535; font-size: 31rpx; font-weight: 700; } .more { color: #5C5C5C; font-size: 22rpx; }
 	.news-state { min-height: 150rpx; margin: 0 31rpx; justify-content: center; align-items: center; color: #8B8B8B; font-size: 24rpx; } .news-retry { margin-top: 12rpx; color: #F16624; }
-	.news-featured { height: 205rpx; margin: 0 19rpx; border-radius: 18rpx; overflow: hidden; position: relative; } .news-featured > image { width: 100%; height: 100%; } .news-shade { position: absolute; left: 0; right: 0; bottom: 0; height: 118rpx; background-color: rgba(0,0,0,.44); } .top-badge { position: absolute; left: 20rpx; top: 18rpx; padding: 7rpx 14rpx; border-radius: 12rpx; background-color: #FFF3D4; } .top-badge text { color: #F2722E; font-size: 20rpx; font-weight: 700; } .featured-copy { position: absolute; left: 20rpx; right: 20rpx; bottom: 17rpx; } .featured-copy text:first-child { display: block; color: #FFFFFF; font-size: 24rpx; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .featured-copy text:last-child { display: block; margin-top: 8rpx; color: #FFFFFF; opacity: .85; font-size: 19rpx; }
+	.news-featured { height: 205rpx; margin: 0 19rpx; border-radius: 18rpx; overflow: hidden; position: relative; background-image: linear-gradient(135deg, #F26A38 0%, #F9A551 100%); } .news-featured > image { width: 100%; height: 100%; } .news-shade { position: absolute; left: 0; right: 0; bottom: 0; height: 118rpx; background-color: rgba(0,0,0,.44); } .top-badge { position: absolute; left: 20rpx; top: 18rpx; padding: 7rpx 14rpx; border-radius: 12rpx; background-color: #FFF3D4; } .top-badge text { color: #F2722E; font-size: 20rpx; font-weight: 700; } .featured-copy { position: absolute; left: 20rpx; right: 20rpx; bottom: 17rpx; } .featured-copy text:first-child { display: block; color: #FFFFFF; font-size: 24rpx; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .featured-copy text:last-child { display: block; margin-top: 8rpx; color: #FFFFFF; opacity: .85; font-size: 19rpx; }
 	.news-list { margin: 0 31rpx; } .news-row { min-height: 116rpx; padding: 22rpx 0 16rpx; border-bottom-width: 1rpx; border-bottom-color: #EEEEEE; } .news-name { color: #363636; font-size: 25rpx; line-height: 36rpx; } .news-meta { margin-top: 12rpx; flex-direction: row; align-items: center; color: #98A2B3; font-size: 19rpx; } .new-badge { margin-right: 14rpx; padding: 4rpx 10rpx; border-radius: 12rpx; background-color: #FFF0D9; color: #FE7930; font-size: 18rpx; }
 	.news-more { height: 87rpx; justify-content: center; align-items: center; color: #545454; font-size: 22rpx; } .content-bottom { height: 22rpx; }
 	.tab-bar { height: 126rpx; height: calc(126rpx + constant(safe-area-inset-bottom)); height: calc(126rpx + env(safe-area-inset-bottom)); padding: 8rpx 14rpx; padding-bottom: calc(8rpx + constant(safe-area-inset-bottom)); padding-bottom: calc(8rpx + env(safe-area-inset-bottom)); box-sizing: border-box; overflow: visible; background-color: #FFFFFF; border-top-width: 1rpx; border-top-color: #F0F0F0; flex-direction: row; justify-content: space-between; } .tab { width: 20%; padding-top: 4rpx; align-items: center; justify-content: flex-start; color: #708099; font-size: 24rpx; } .tab-icon-wrap { width: 66rpx; height: 66rpx; margin-bottom: 13rpx; border-radius: 33rpx; justify-content: center; align-items: center; } .tab-icon-wrap image { width: 42rpx; height: 42rpx; } .active-icon { background-color: #FFF1F1; } .tab.active { color: #FF6030; font-weight: 700; } .card-tab { margin-top: -16rpx; padding-top: 0; justify-content: flex-start; } .card-icon { width: 160rpx; height: 160rpx; position: relative; justify-content: center; align-items: center; } .card-icon image { width: 160rpx; height: 160rpx; } .card-label { position: absolute; left: 0; right: 0; bottom: 42rpx; z-index: 1; color: #FFFFFF; font-size: 18rpx; font-weight: 700; text-align: center; }
diff --git a/rencaika/pages/policy-detail/policy-detail.uvue b/rencaika/pages/policy-detail/policy-detail.uvue
index a420b98..778b483 100644
--- a/rencaika/pages/policy-detail/policy-detail.uvue
+++ b/rencaika/pages/policy-detail/policy-detail.uvue
@@ -29,7 +29,7 @@
 	const policyCategory = ref('东区政策')
 	const policyContent = ref('')
 	const policyContentImages = ref<string[]>([])
-	const policyCover = ref('/static/home-assets/home-banner-v2.jpg')
+	const policyCover = ref('')
 	const policyId = ref('')
 	const initialLoading = ref(true)
 	const initialError = ref(false)
@@ -64,7 +64,7 @@
 			policyCategory.value = data != null && data.subtitle != null && (data.subtitle as string).trim() != '' ? data.subtitle as string : '人才政策'
 			// 正文直接取后台政策内容;contentText 为空时兼容后台仅返回富文本 content 的情况。
 			policyContent.value = policyContentText(data)
-			policyCover.value = '/static/home-assets/home-banner-v2.jpg'
+			policyCover.value = ''
 			const heroImage = data != null && data.heroImage != null ? data.heroImage : (data != null ? data.coverUrl : null)
 			if (heroImage != null && (heroImage as string).length > 0) policyCover.value = resolveApiUrl(heroImage as string)
 			const images: string[] = []
@@ -82,7 +82,7 @@
 			initialError.value = !policyUnavailable.value
 		})
 	}
-	const useHeroFallback = () => { policyCover.value = '/static/home-assets/home-banner-v2.jpg' }
+	const useHeroFallback = () => { policyCover.value = '' }
 	const useContentImageFallback = (index: number) => {
 		const next = policyContentImages.value.slice()
 		next[index] = '/static/policy-assets/policy-explanation.jpg'
diff --git a/rencaika/pages/premium-benefits/premium-benefits.uvue b/rencaika/pages/premium-benefits/premium-benefits.uvue
index f4a7ebd..02f5091 100644
--- a/rencaika/pages/premium-benefits/premium-benefits.uvue
+++ b/rencaika/pages/premium-benefits/premium-benefits.uvue
@@ -40,8 +40,8 @@
 		getPremiumTopic((data: any) => {
 			topicError.value = false; if (data != null && data.title != null && data.title != '') topicTitle.value = data.title as string; if (data != null && data.description != null) topicDescription.value = data.description as string
 			const imageRecords: any[] = data != null && data.images instanceof Array ? data.images as any[] : []; const nextScenes: any[] = []; for (let index = 0; index < imageRecords.length; index++) { const item = imageRecords[index]; if (item.imageUrl != null && item.imageUrl != '') nextScenes.push({ id: '' + item.id, name: item.name != null ? item.name as string : '', image: resolveApiUrl(item.imageUrl as string) }) }; if (nextScenes.length > 0) { scenes.value = nextScenes; sceneTrackWidth.value = 644 + nextScenes.length * 137; selectScene(Math.min(activeIndex.value, nextScenes.length - 1)) }
-			const categoryRecords: any[] = data != null && data.categories instanceof Array ? data.categories as any[] : []; const nextTabs: any[] = []; for (let index = 0; index < categoryRecords.length; index++) { const item = categoryRecords[index]; if (item.id != null && item.name != null && item.name != '') nextTabs.push({ id: '' + item.id, name: item.name as string }) }; serviceTabs.value = nextTabs
-			if (nextTabs.length == 0) { initialLoading.value = false; hasLoaded.value = true; visibleServices.value = []; return }; selectTab(nextTabs[0].id)
+			const categoryRecords: any[] = data != null && data.categories instanceof Array ? data.categories as any[] : []; const nextTabs: any[] = []; for (let index = 0; index < categoryRecords.length; index++) { const item = categoryRecords[index]; if (item.name != null && item.name != '') nextTabs.push({ id: item.name as string, name: item.name as string }) }; serviceTabs.value = nextTabs
+			if (nextTabs.length == 0) { initialLoading.value = false; hasLoaded.value = true; visibleServices.value = []; return }; selectTab(nextTabs[0].name)
 		}, () => { topicError.value = true; initialLoading.value = false; initialError.value = true })
 	}
 	const selectTab = (categoryId: string) => { if (categoryId == '') return; activeTab.value = categoryId; loadInitialServices() }
diff --git a/rencaika/services/portal.uts b/rencaika/services/portal.uts
index 8f7435f..13c1155 100644
--- a/rencaika/services/portal.uts
+++ b/rencaika/services/portal.uts
@@ -47,7 +47,7 @@
 export const getShoppingTopic = (onSuccess: (data: any) => void, onError: (error: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('appShoppingTopic'), method: 'GET', skipAuth: true }, onSuccess, onError)
 export const getShoppingTopicMerchants = (categoryId: string, page: number, size: number, onSuccess: (data: any) => void, onError: (error: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('appShoppingTopicMerchants'), method: 'GET', skipAuth: true, data: { categoryId: categoryId, page: page, size: size } }, onSuccess, onError)
 export const getPremiumTopic = (onSuccess: (data: any) => void, onError: (error: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('appPremiumTopic'), method: 'GET', skipAuth: true }, onSuccess, onError)
-export const getPremiumTopicServices = (categoryId: string, page: number, size: number, onSuccess: (data: any) => void, onError: (error: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('appPremiumTopicServices'), method: 'GET', skipAuth: true, data: { categoryId: categoryId, page: page, size: size } }, onSuccess, onError)
+export const getPremiumTopicServices = (type: string, page: number, size: number, onSuccess: (data: any) => void, onError: (error: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('appPremiumTopicServices'), method: 'GET', skipAuth: true, data: { type: type, page: page, size: size } }, onSuccess, onError)
 export const getSpouseEmploymentBenefit = (serviceId: string, onSuccess: (data: any) => void, onError: (error: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('appSpouseEmploymentBenefit') + encodeURIComponent(serviceId), method: 'GET', skipAuth: true }, onSuccess, onError)
 export const submitSpouseEmploymentApplication = (serviceId: string, payload: any, idempotencyKey: string, onSuccess: (data: any) => void, onError: (error: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('appSpouseEmploymentBenefit') + encodeURIComponent(serviceId) + '/applications', method: 'POST', skipAuth: true, data: payload, idempotencyKey: idempotencyKey }, onSuccess, onError)
 export const getAdminApprovalBenefit = (serviceId: string, onSuccess: (data: any) => void, onError: (error: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('appAdminApprovalBenefit') + encodeURIComponent(serviceId), method: 'GET', skipAuth: true }, onSuccess, onError)
diff --git a/rencaika/static/benefits-assets/benefits-hero.jpg b/rencaika/static/benefits-assets/benefits-hero.jpg
index 2337a10..3c8a191 100644
--- a/rencaika/static/benefits-assets/benefits-hero.jpg
+++ b/rencaika/static/benefits-assets/benefits-hero.jpg
Binary files differ
diff --git a/rencaika/static/card-assets/card-elite.jpg b/rencaika/static/card-assets/card-elite.jpg
index dd3b395..f37ba03 100644
--- a/rencaika/static/card-assets/card-elite.jpg
+++ b/rencaika/static/card-assets/card-elite.jpg
Binary files differ
diff --git a/rencaika/static/card-assets/card-excellence.jpg b/rencaika/static/card-assets/card-excellence.jpg
index 43e05f2..fc85b31 100644
--- a/rencaika/static/card-assets/card-excellence.jpg
+++ b/rencaika/static/card-assets/card-excellence.jpg
Binary files differ
diff --git a/rencaika/static/card-assets/card-outstanding.jpg b/rencaika/static/card-assets/card-outstanding.jpg
index 393fc70..b0a49fd 100644
--- a/rencaika/static/card-assets/card-outstanding.jpg
+++ b/rencaika/static/card-assets/card-outstanding.jpg
Binary files differ
diff --git a/rencaika/static/card-assets/talent-card-background.jpg b/rencaika/static/card-assets/talent-card-background.jpg
index bdeafa8..87b32a1 100644
--- a/rencaika/static/card-assets/talent-card-background.jpg
+++ b/rencaika/static/card-assets/talent-card-background.jpg
Binary files differ
diff --git a/rencaika/static/food-assets/food-1.jpg b/rencaika/static/food-assets/food-1.jpg
index 2b287aa..8b58c80 100644
--- a/rencaika/static/food-assets/food-1.jpg
+++ b/rencaika/static/food-assets/food-1.jpg
Binary files differ
diff --git a/rencaika/static/food-assets/food-2.jpg b/rencaika/static/food-assets/food-2.jpg
index 7d7084c..827e0ac 100644
--- a/rencaika/static/food-assets/food-2.jpg
+++ b/rencaika/static/food-assets/food-2.jpg
Binary files differ
diff --git a/rencaika/static/home-assets/city-golden.webp b/rencaika/static/home-assets/city-golden.webp
deleted file mode 100644
index a84fe63..0000000
--- a/rencaika/static/home-assets/city-golden.webp
+++ /dev/null
Binary files differ
diff --git a/rencaika/static/home-assets/city-industry.webp b/rencaika/static/home-assets/city-industry.webp
deleted file mode 100644
index d855aef..0000000
--- a/rencaika/static/home-assets/city-industry.webp
+++ /dev/null
Binary files differ
diff --git a/rencaika/static/home-assets/city-intro.jpg b/rencaika/static/home-assets/city-intro.jpg
deleted file mode 100644
index 74b36bf..0000000
--- a/rencaika/static/home-assets/city-intro.jpg
+++ /dev/null
Binary files differ
diff --git a/rencaika/static/home-assets/city-livable.jpg b/rencaika/static/home-assets/city-livable.jpg
deleted file mode 100644
index 2ce650b..0000000
--- a/rencaika/static/home-assets/city-livable.jpg
+++ /dev/null
Binary files differ
diff --git a/rencaika/static/home-assets/city-potential.jpg b/rencaika/static/home-assets/city-potential.jpg
deleted file mode 100644
index bfe528e..0000000
--- a/rencaika/static/home-assets/city-potential.jpg
+++ /dev/null
Binary files differ
diff --git a/rencaika/static/home-assets/home-banner-v2.jpg b/rencaika/static/home-assets/home-banner-v2.jpg
deleted file mode 100644
index f0a908f..0000000
--- a/rencaika/static/home-assets/home-banner-v2.jpg
+++ /dev/null
Binary files differ
diff --git a/rencaika/static/home-assets/home-title-left-v3.png b/rencaika/static/home-assets/home-title-left-v3.png
deleted file mode 100644
index 4bfb4e2..0000000
--- a/rencaika/static/home-assets/home-title-left-v3.png
+++ /dev/null
Binary files differ
diff --git a/rencaika/static/home-assets/news-mascot-x3.png b/rencaika/static/home-assets/news-mascot-x3.png
deleted file mode 100644
index c3a6eba..0000000
--- a/rencaika/static/home-assets/news-mascot-x3.png
+++ /dev/null
Binary files differ
diff --git a/rencaika/static/home-assets/talent-east-flower.png b/rencaika/static/home-assets/talent-east-flower.png
deleted file mode 100644
index 48873ea..0000000
--- a/rencaika/static/home-assets/talent-east-flower.png
+++ /dev/null
Binary files differ
diff --git a/rencaika/static/home-assets/talent-east.png b/rencaika/static/home-assets/talent-east.png
deleted file mode 100644
index d7de792..0000000
--- a/rencaika/static/home-assets/talent-east.png
+++ /dev/null
Binary files differ
diff --git a/rencaika/static/home-assets/top.jpg b/rencaika/static/home-assets/top.jpg
index 3918faf..bf8bbeb 100644
--- a/rencaika/static/home-assets/top.jpg
+++ b/rencaika/static/home-assets/top.jpg
Binary files differ
diff --git a/rencaika/static/policy-assets/policy-explanation.jpg b/rencaika/static/policy-assets/policy-explanation.jpg
index 0d6c6dc..c4522a1 100644
--- a/rencaika/static/policy-assets/policy-explanation.jpg
+++ b/rencaika/static/policy-assets/policy-explanation.jpg
Binary files differ
diff --git a/rencaika/static/policy-assets/policy-hero.jpg b/rencaika/static/policy-assets/policy-hero.jpg
index 24c9d48..df27645 100644
--- a/rencaika/static/policy-assets/policy-hero.jpg
+++ b/rencaika/static/policy-assets/policy-hero.jpg
Binary files differ
diff --git a/rencaika/static/stay-assets/hotel-hampton.jpg b/rencaika/static/stay-assets/hotel-hampton.jpg
index 00cdae0..1cffd30 100644
--- a/rencaika/static/stay-assets/hotel-hampton.jpg
+++ b/rencaika/static/stay-assets/hotel-hampton.jpg
Binary files differ
diff --git a/rencaika/static/stay-assets/hotel-vienna.jpg b/rencaika/static/stay-assets/hotel-vienna.jpg
index cc122d0..2d0f8de 100644
--- a/rencaika/static/stay-assets/hotel-vienna.jpg
+++ b/rencaika/static/stay-assets/hotel-vienna.jpg
Binary files differ
diff --git a/rencaika_merchant/pages/bank-card-select/bank-card-select.uvue b/rencaika_merchant/pages/bank-card-select/bank-card-select.uvue
index c66fb7b..b9ac4bc 100644
--- a/rencaika_merchant/pages/bank-card-select/bank-card-select.uvue
+++ b/rencaika_merchant/pages/bank-card-select/bank-card-select.uvue
@@ -1,5 +1,5 @@
 <template>
-  <view class="page"><view class="nav"><view class="nav-back" @tap="goBack"><image class="back-icon" src="/static/icons/lucide/back-left.svg" mode="aspectFit"></image></view><text>选择银行卡</text><view></view></view><view class="add" @tap="addCard">添加银行卡</view><scroll-view class="scroll" scroll-y="true"><view v-if="loading" class="state">正在加载银行卡</view><view v-else-if="cards.length == 0" class="state">暂无银行卡</view><view v-for="card in cards" :key="card.id" class="card" @tap="selectCard(card)"><view class="row"><text class="name">{{card.bankName}}</text><text :class="'status ' + card.status">{{statusText(card.status)}}</text></view><text class="number">{{card.cardMasked}}</text><text class="branch">{{card.branchName}}</text><text v-if="card.reason.length > 0" class="reason">{{card.reason}}</text></view></scroll-view></view>
+  <view class="page"><view class="nav"><view class="nav-back" @tap="goBack"><image class="back-icon" src="/static/icons/lucide/back-left.svg" mode="aspectFit"></image></view><text class="nav-title">选择银行卡</text><view class="nav-right-placeholder"></view></view><view class="add" @tap="addCard">添加银行卡</view><scroll-view class="scroll" scroll-y="true"><view v-if="loading" class="state">正在加载银行卡</view><view v-else-if="cards.length == 0" class="state">暂无银行卡</view><view v-for="card in cards" :key="card.id" class="card" @tap="selectCard(card)"><view class="row"><text class="name">{{card.bankName}}</text><text :class="'status ' + card.status">{{statusText(card.status)}}</text></view><text class="number">{{card.cardMasked}}</text><text class="branch">{{card.branchName}}</text><text v-if="card.reason.length > 0" class="reason">{{card.reason}}</text></view></scroll-view></view>
 </template>
 <script setup lang="uts">
 import { loadBankCards, SettlementBankCard } from '../../services/settlement.uts'
@@ -12,4 +12,4 @@
 const load = () => { loading.value = true; loadBankCards((data) => { cards.value = data; loading.value = false }, (error: any) => { loading.value = false; uni.showToast({ title: error.message || '银行卡加载失败', icon: 'none' }) }) }
 onShow(() => load())
 </script>
-<style>.page{height:100vh;background:#f4f6f9}.nav{height:174rpx;padding:68rpx 38rpx 0;display:flex;justify-content:space-between;align-items:center;box-sizing:border-box;color:#202b3e;font-size:32rpx;font-weight:700}.nav view{width:80rpx;font-size:25rpx}.nav-back{display:flex;align-items:center;justify-content:center}.back-icon{width:44rpx;height:44rpx}.add{height:88rpx;margin:0 34rpx 28rpx;border-radius:44rpx;background:#ff7625;color:#fff;text-align:center;line-height:88rpx;font-weight:700}.scroll{height:calc(100vh - 290rpx);padding:0 34rpx;box-sizing:border-box}.card{padding:28rpx;margin-bottom:22rpx;background:#fff;border-radius:28rpx}.row{display:flex;justify-content:space-between}.name{font-size:30rpx;font-weight:700;color:#2c3748}.number{display:block;margin-top:20rpx;font-size:28rpx;color:#3a4556}.branch,.reason{display:block;margin-top:10rpx;color:#8993a1;font-size:23rpx}.reason{color:#e95b5b}.status{font-size:23rpx}.approved{color:#249765}.pending{color:#e58b28}.rejected{color:#e95b5b}.state{padding:150rpx 0;text-align:center;color:#8993a1}</style>
+<style>.page{height:100vh;background:#f4f6f9}.nav{height:174rpx;padding:68rpx 38rpx 0;display:flex;position:relative;justify-content:center;align-items:center;box-sizing:border-box;color:#202b3e}.nav-back{display:flex;width:64rpx;height:64rpx;position:absolute;left:38rpx;top:89rpx;align-items:center;justify-content:center;z-index:2}.back-icon{width:38rpx;height:38rpx}.nav-title{width:100%;font-size:34rpx;line-height:48rpx;text-align:center;font-weight:700}.nav-right-placeholder{width:64rpx;height:64rpx;position:absolute;right:38rpx;top:89rpx}.add{height:88rpx;margin:0 34rpx 28rpx;border-radius:44rpx;background:#ff7625;color:#fff;text-align:center;line-height:88rpx;font-weight:700}.scroll{height:calc(100vh - 290rpx);padding:0 34rpx;box-sizing:border-box}.card{padding:28rpx;margin-bottom:22rpx;background:#fff;border-radius:28rpx}.row{display:flex;justify-content:space-between}.name{font-size:30rpx;font-weight:700;color:#2c3748}.number{display:block;margin-top:20rpx;font-size:28rpx;color:#3a4556}.branch,.reason{display:block;margin-top:10rpx;color:#8993a1;font-size:23rpx}.reason{color:#e95b5b}.status{font-size:23rpx}.approved{color:#249765}.pending{color:#e58b28}.rejected{color:#e95b5b}.state{padding:150rpx 0;text-align:center;color:#8993a1}</style>
diff --git a/rencaika_merchant/pages/merchant/merchant.uvue b/rencaika_merchant/pages/merchant/merchant.uvue
index 5fdb03f..4d198c3 100644
--- a/rencaika_merchant/pages/merchant/merchant.uvue
+++ b/rencaika_merchant/pages/merchant/merchant.uvue
@@ -19,7 +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/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>
@@ -27,6 +26,7 @@
 				<view class="setting-row" @tap="handleMenuAction('营业时间')"><view class="setting-leading"><image class="setting-icon" src="/static/icons/lucide/clock-3.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/badge-check.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/file-text.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" @tap="handleMenuAction('设置')"><view class="setting-leading"><image class="setting-icon" src="/static/icons/lucide/settings.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>
 			</view>
@@ -146,9 +146,9 @@
 	.setting-chevron { width: 28rpx; height: 28rpx; }
 	.business-state { display: flex; flex-direction: row; align-items: center; }
 	.business-state-disabled { opacity: 0.58; }
-	.business-state-text { color: #FF5A42; font-size: 27rpx; line-height: 38rpx; font-weight: 600; }
-	.state-switch { display: flex; width: 78rpx; height: 48rpx; padding: 4rpx; margin-left: 18rpx; border-radius: 28rpx; align-items: center; background-color: #CDD5E0; box-sizing: border-box; }
+	.business-state-text { color: #FF5A42; font-size: 25rpx; line-height: 35rpx; font-weight: 600; }
+	.state-switch { display: flex; width: 68rpx; height: 40rpx; padding: 3rpx; margin-left: 12rpx; border-radius: 22rpx; align-items: center; background-color: #CDD5E0; box-sizing: border-box; }
 	.state-switch.open { justify-content: flex-end; background-color: #FF5A42; }
-	.state-thumb { width: 40rpx; height: 40rpx; border-radius: 20rpx; background-color: #FFFFFF; box-shadow: 0 2rpx 7rpx rgba(36, 41, 49, 0.18); }
+	.state-thumb { width: 34rpx; height: 34rpx; border-radius: 17rpx; background-color: #FFFFFF; box-shadow: 0 2rpx 7rpx rgba(36, 41, 49, 0.18); }
 
 </style>
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/auth/WeChatMiniProgramClient.java b/talent-card-server/src/main/java/com/talentcard/server/business/auth/WeChatMiniProgramClient.java
index 926a263..de71ca9 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/auth/WeChatMiniProgramClient.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/auth/WeChatMiniProgramClient.java
@@ -45,8 +45,14 @@
     }
 
     public String exchangePhoneNumber(String code) {
-        String token = clientAccessToken();
+        String token = clientAccessToken(false);
         Map<String, Object> response = post("/wxa/business/getuserphonenumber?access_token=" + encode(token), Map.of("code", code.trim()));
+        // 微信返回 40001 时,缓存的 access_token 可能已被其它实例刷新。使用稳定
+        // 令牌接口强制刷新一次并重试,避免把令牌问题误报成手机号授权失效。
+        if (number(response.get("errcode"), 0) == 40001) {
+            token = clientAccessToken(true);
+            response = post("/wxa/business/getuserphonenumber?access_token=" + encode(token), Map.of("code", code.trim()));
+        }
         ensureSuccess(response, "手机号授权已失效,请重新授权");
         Object phoneInfo = response.get("phone_info");
         if (!(phoneInfo instanceof Map<?, ?> raw)) throw new BusinessException(ErrorCode.CONFLICT, "未获取到微信手机号,请重新授权");
@@ -56,16 +62,24 @@
         return phone;
     }
 
-    private String clientAccessToken() {
+    private String clientAccessToken(boolean forceRefresh) {
         long now = System.currentTimeMillis();
-        if (!accessToken.isBlank() && now < accessTokenExpiresAt) return accessToken;
+        if (!forceRefresh && !accessToken.isBlank() && now < accessTokenExpiresAt) return accessToken;
         synchronized (this) {
             now = System.currentTimeMillis();
-            if (!accessToken.isBlank() && now < accessTokenExpiresAt) return accessToken;
-            Map<String, Object> response = get("/cgi-bin/token", Map.of(
-                    "grant_type", "client_credential",
+            if (!forceRefresh && !accessToken.isBlank() && now < accessTokenExpiresAt) return accessToken;
+            Map<String, Object> response = post("/cgi-bin/stable_token", Map.of(
                     "appid", required(properties.getAppId()),
-                    "secret", required(properties.getAppSecret())));
+                    "secret", required(properties.getAppSecret()),
+                    "grant_type", "client_credential",
+                    "force_refresh", forceRefresh));
+            // 兼容旧微信接口/网关:稳定令牌接口不可用时再回退到旧接口。
+            if (number(response.get("errcode"), 0) != 0 && !forceRefresh) {
+                response = get("/cgi-bin/token", Map.of(
+                "grant_type", "client_credential",
+                "appid", required(properties.getAppId()),
+                "secret", required(properties.getAppSecret())));
+            }
             ensureSuccess(response, "微信服务暂不可用,请稍后重试");
             String token = text(response.get("access_token"));
             long expiresIn = number(response.get("expires_in"), 7200);
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalController.java b/talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalController.java
index de5e57b..7d03cfe 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalController.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalController.java
@@ -169,11 +169,11 @@
 
 	@GetMapping("/topics/premium/services")
 	public ApiResponse<PageResult<Map<String, Object>>> premiumServices(
-			@RequestParam String categoryId,
+			@RequestParam String type,
 			@RequestParam(defaultValue = "1") int page,
 			@RequestParam(defaultValue = "4") int size
 	) {
-		return ApiResponse.success(portalService.premiumServices(categoryId, page, size));
+		return ApiResponse.success(portalService.premiumServices(type, page, size));
 	}
 
 	@GetMapping("/merchants/{id}")
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalService.java b/talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalService.java
index e4897cf..e65eebb 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalService.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalService.java
@@ -512,7 +512,8 @@
 	}
 
 	@Transactional(readOnly = true)
-	public PageResult<Map<String, Object>> premiumServices(String categoryId, int page, int size) {
+	public PageResult<Map<String, Object>> premiumServices(String type, int page, int size) {
+		String categoryId = jdbcTemplate.query("select category_key from premium_topic_category where category_name = ? and enabled = 1 and deleted = 0 limit 1", rs -> rs.next() ? rs.getString("category_key") : null, type == null ? "" : type.trim());
 		if (categoryId == null || categoryId.isBlank()) throw new BusinessException(ErrorCode.BAD_REQUEST, "请选择臻享服务分类");
 		Integer categoryCount = jdbcTemplate.queryForObject("""
 				select count(*) from premium_topic_category

--
Gitblit v1.7.1