feat: deliver V1.1 dual-client UI fidelity milestone
This commit is contained in:
+115
-158
@@ -1,172 +1,129 @@
|
||||
/**
|
||||
* 首页 - 每日推荐(重构:用 matchStore + recommend-card + appStore 统一编排)
|
||||
*
|
||||
* 关键不变量(AGENTS.md):
|
||||
* - 第 2 条:Required 不可自动放宽——推荐列表完全由后端按 PartnerPreference 过滤,
|
||||
* 前端只渲染后端返回的 recommendations,不做任何"放宽条件再查"的逻辑。
|
||||
* - 第 5 条:红娘不可替用户操作——所有 Like/Pass 由当前用户触发。
|
||||
* - 第 6 条:单向意向不泄露——likersCount 仅展示计数,不暴露具体身份。
|
||||
*/
|
||||
const { matchStore, appStore, userStore, authStore, blockUser, likeUser } = require('../../stores');
|
||||
const { ROUTE, to, RECOMMENDATION_STATE } = require('../../constants');
|
||||
const { authStore, appStore } = require('../../stores')
|
||||
const api = require('../../utils/api')
|
||||
|
||||
const QUICK_ENTRIES = [
|
||||
{ code: 'discover', label: '找对象', icon: '缘', tone: 'rose', targetPage: '/pages/discover/discover', tab: true, enabled: true },
|
||||
{ code: 'matchmaker', label: '公益红娘', icon: '牵', tone: 'amber', targetPage: '/pages/matchmaker/matchmaker', enabled: true },
|
||||
{ code: 'activity', label: '公益活动', icon: '聚', tone: 'green', targetPage: '/pages/activity/activity', tab: true, enabled: true },
|
||||
{ code: 'counseling', label: '婚恋咨询', icon: '谈', tone: 'blue', targetPage: '/pages/counseling-intro/counseling-intro', enabled: true },
|
||||
{ code: 'classroom', label: '婚恋课堂', icon: '学', tone: 'purple', targetPage: '/pages/content/content', enabled: true },
|
||||
{ code: 'premarital', label: '婚前辅导', icon: '伴', tone: 'orange', targetPage: '/pages/premarital-intro/premarital-intro', enabled: true },
|
||||
{ code: 'anti-fraud', label: '婚恋反诈', icon: '安', tone: 'indigo', targetPage: '/pages/anti-fraud/anti-fraud', enabled: true },
|
||||
{ code: 'station', label: '善缘服务站', icon: '站', tone: 'teal', targetPage: '/pages/service-station/service-station', enabled: true },
|
||||
]
|
||||
|
||||
function previewData() {
|
||||
return {
|
||||
userState: { verified: true, profileStatus: 'approved', localEligibilityStatus: 'verified', relationshipStatus: 'searching' },
|
||||
banners: [{ id: 'preview-banner', title: '让真诚的相遇,在丹阳发生', subtitle: '实名认证 · 人工审核 · 公益服务', buttonText: '了解平台', targetType: 'internal_page', targetValue: '/pages/about/about' }],
|
||||
quickEntries: QUICK_ENTRIES,
|
||||
recommendations: [{ userId: 'preview-user', displayName: '林女士', age: 29, heightCm: 165, regionName: '丹阳', occupationCode: '教育行业', verified: true, reviewed: true, recommendationLevel: 'high', recommendationReasons: ['双方年龄与生活规划较为契合', '都期待认真稳定的关系'], intro: '喜欢阅读和徒步,重视坦诚沟通。' }],
|
||||
serviceRecommendations: [{ type: 'matchmaker', title: '需要更有温度的帮助?', description: '公益红娘会在尊重双方意愿的前提下提供人工推荐。', ctaText: '申请公益红娘' }],
|
||||
activities: [{ id: 'preview-activity', title: '“初秋相遇”青年公益交友活动', startAt: '2026-09-20T14:00:00+08:00', locationName: '善缘丹阳公益服务站', typeLabel: '公益交友' }],
|
||||
serviceLocation: { id: 'preview-station', name: '善缘丹阳公益服务站', shortName: '线下咨询与活动服务阵地', services: ['资料协助', '服务预约', '活动咨询'] },
|
||||
articles: [{ id: 'preview-article', title: '第一次见面,怎样自然地开始交流', categoryName: '沟通成长', readTime: '5分钟' }, { id: 'preview-article-2', title: '建立一段健康关系的三个边界', categoryName: '关系成长', readTime: '6分钟' }],
|
||||
safetyNotice: { text: '涉及借款、投资和个人隐私时请保持谨慎,发现异常及时举报。', ctaText: '查看反诈指南' },
|
||||
}
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
greeting: '',
|
||||
userName: '',
|
||||
unreadCount: 2,
|
||||
loading: true,
|
||||
loadError: false,
|
||||
previewMode: false,
|
||||
userState: null,
|
||||
isOnboarding: false,
|
||||
isVerified: false,
|
||||
isPaused: false,
|
||||
isSuccessful: false,
|
||||
onboardingMessage: '',
|
||||
onboardingActions: [],
|
||||
statusChecks: [],
|
||||
banners: [],
|
||||
quickEntries: QUICK_ENTRIES,
|
||||
recommendations: [],
|
||||
recState: RECOMMENDATION_STATE.IDLE,
|
||||
likersCount: 0,
|
||||
actionLoadingUserId: '', // 正在操作的用户 id(Like/Pass 进行中)
|
||||
emptyActions: [
|
||||
{ text: '去「找对象」', event: 'discover', variant: 'primary' },
|
||||
{ text: '申请公益红娘', event: 'matchmaker', variant: 'secondary' },
|
||||
],
|
||||
recState: 'loading',
|
||||
actionLoadingUserId: '',
|
||||
serviceRecommendations: [],
|
||||
activities: [],
|
||||
serviceLocation: null,
|
||||
articles: [],
|
||||
safetyNotice: { text: '涉及借款、投资和个人隐私时请保持谨慎。', ctaText: '查看反诈指南' },
|
||||
emptyActions: [{ text: '调整择偶条件', event: 'preference', variant: 'secondary' }, { text: '申请公益红娘', event: 'matchmaker', variant: 'primary' }],
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.setGreeting();
|
||||
// 绑定 matchStore 关键字段到 page data
|
||||
this._unsubMatch = matchStore.bindToPage(this, {
|
||||
recommendations: 'recommendations',
|
||||
recState: 'recState',
|
||||
});
|
||||
// 绑定 likersCount
|
||||
this._unsubMatchSub = matchStore.subscribe((state) => {
|
||||
if (state.likersCount !== this.data.likersCount) {
|
||||
this.setData({ likersCount: state.likersCount });
|
||||
onLoad() { this.loadHomeData() },
|
||||
onPullDownRefresh() { this.loadHomeData().finally(() => wx.stopPullDownRefresh()) },
|
||||
async loadHomeData() {
|
||||
this.setData({ loading: true, loadError: false })
|
||||
try {
|
||||
if (!authStore.state.isLoggedIn && this.isDevelopPreview()) {
|
||||
this.processHomeData(previewData(), true)
|
||||
return
|
||||
}
|
||||
});
|
||||
// 绑定 userStore 用户名(用于问候语)
|
||||
this._unsubUser = userStore.subscribe((state) => {
|
||||
const name = state.profile?.displayName || '';
|
||||
if (name !== this.data.userName) this.setData({ userName: name });
|
||||
});
|
||||
this.setData({ userName: userStore.state.profile?.displayName || '' });
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 未登录则不请求推荐(bootstrap 已跳转登录页,此处为保险兜底)
|
||||
if (!authStore.state.isLoggedIn) return;
|
||||
// 每次显示刷新推荐(用户从聊天页返回后可能已 Like/Pass,需要新批次)
|
||||
this.loadRecommendations();
|
||||
// 异步刷新 likersCount(Tab 徽标也会更新)
|
||||
matchStore.fetchLikersCount().catch(() => {});
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
this._unsubMatch && this._unsubMatch();
|
||||
this._unsubMatchSub && this._unsubMatchSub();
|
||||
this._unsubUser && this._unsubUser();
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.loadRecommendations().finally(() => wx.stopPullDownRefresh());
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
// 加载更多推荐
|
||||
matchStore.loadMoreRecommendations().catch(() => {});
|
||||
},
|
||||
|
||||
setGreeting() {
|
||||
const hour = new Date().getHours();
|
||||
let greeting = '早上好';
|
||||
if (hour >= 12 && hour < 17) greeting = '下午好';
|
||||
else if (hour >= 17) greeting = '晚上好';
|
||||
this.setData({ greeting });
|
||||
},
|
||||
|
||||
async loadRecommendations() {
|
||||
try {
|
||||
await matchStore.fetchRecommendations();
|
||||
} catch (err) {
|
||||
appStore.toastError(err);
|
||||
}
|
||||
},
|
||||
|
||||
onViewDetail(e) {
|
||||
const { userId } = e.detail;
|
||||
wx.navigateTo({ url: to.detail(userId) });
|
||||
},
|
||||
|
||||
async onLike(e) {
|
||||
const { userId } = e.detail;
|
||||
if (this.data.actionLoadingUserId) return;
|
||||
this.setData({ actionLoadingUserId: userId });
|
||||
try {
|
||||
const result = await likeUser(userId);
|
||||
if (result && result.mutualMatch) {
|
||||
// 双向匹配达成:弹祝福 + 引导去聊天
|
||||
appStore.toastSuccess('双向喜欢!可以开始聊天了');
|
||||
setTimeout(() => {
|
||||
wx.showModal({
|
||||
title: '互相喜欢',
|
||||
content: '你们已经互相喜欢,是否现在开始聊天?',
|
||||
confirmText: '开始聊天',
|
||||
cancelText: '稍后',
|
||||
confirmColor: '#E96B6B',
|
||||
success: (res) => {
|
||||
if (res.confirm && result.matchedConversationId) {
|
||||
wx.navigateTo({ url: to.chat(result.matchedConversationId) });
|
||||
}
|
||||
},
|
||||
});
|
||||
}, 800);
|
||||
} else {
|
||||
appStore.toastSuccess('已表达喜欢');
|
||||
if (!authStore.state.isLoggedIn) {
|
||||
this.processHomeData({ ...previewData(), recommendations: [] }, false)
|
||||
return
|
||||
}
|
||||
} catch (err) {
|
||||
appStore.toastError(err);
|
||||
} finally {
|
||||
this.setData({ actionLoadingUserId: '' });
|
||||
this.processHomeData(await api.get('/home'), false)
|
||||
} catch (error) {
|
||||
console.error('首页数据加载失败', error)
|
||||
if (this.isDevelopPreview()) this.processHomeData(previewData(), true)
|
||||
else this.setData({ loading: false, loadError: true, recState: 'error' })
|
||||
}
|
||||
},
|
||||
|
||||
async onPass(e) {
|
||||
const { userId } = e.detail;
|
||||
if (this.data.actionLoadingUserId) return;
|
||||
this.setData({ actionLoadingUserId: userId });
|
||||
try {
|
||||
await matchStore.pass(userId);
|
||||
// Pass 是单向意向,不打扰用户(AGENTS 第 6 条隐式)
|
||||
} catch (err) {
|
||||
// Pass 失败不提示,避免打扰
|
||||
} finally {
|
||||
this.setData({ actionLoadingUserId: '' });
|
||||
}
|
||||
isDevelopPreview() {
|
||||
try { return wx.getAccountInfoSync().miniProgram.envVersion === 'develop' } catch (_) { return false }
|
||||
},
|
||||
|
||||
async onBlock(e) {
|
||||
const { userId } = e.detail;
|
||||
const target = this.data.recommendations.find((r) => r.userId === userId);
|
||||
const name = target?.displayName || '该用户';
|
||||
// 危险动作二次确认(AGENTS 第 4 条)
|
||||
const confirmed = await appStore.confirm({
|
||||
title: '拉黑该用户',
|
||||
content: `拉黑后 ${name} 将无法与你互动,你也不会再收到对方的推荐与消息。此操作可由你随时解除。`,
|
||||
confirmText: '确认拉黑',
|
||||
danger: true,
|
||||
});
|
||||
if (!confirmed) return;
|
||||
try {
|
||||
await blockUser(userId, '用户主动拉黑');
|
||||
appStore.toastSuccess('已拉黑');
|
||||
} catch (err) {
|
||||
appStore.toastError(err);
|
||||
}
|
||||
processHomeData(data, previewMode) {
|
||||
const state = data.userState || null
|
||||
const profileStatus = state && state.profileStatus
|
||||
const eligibility = (state && state.localEligibilityStatus) || 'not_declared'
|
||||
const relationship = (state && state.relationshipStatus) || ''
|
||||
const isOnboarding = !state || (profileStatus !== 'approved' && profileStatus !== 'completed')
|
||||
this.setData({
|
||||
loading: false, loadError: false, previewMode,
|
||||
userState: state, isOnboarding, isVerified: eligibility === 'verified',
|
||||
isPaused: relationship === 'paused', isSuccessful: relationship === 'matched_successfully',
|
||||
onboardingMessage: isOnboarding ? '完成资料与资格核验后,即可开始寻找合适的人' : '',
|
||||
onboardingActions: isOnboarding ? [{ text: '继续完善', event: authStore.state.isLoggedIn ? 'profile' : 'login', variant: 'primary' }] : [],
|
||||
statusChecks: [{ label: '已实名', done: !!(state && state.verified) }, { label: '本地资格', done: eligibility === 'verified' }, { label: '资料已审核', done: profileStatus === 'approved' || profileStatus === 'completed' }],
|
||||
banners: data.banners || [], quickEntries: (data.quickEntries && data.quickEntries.length ? data.quickEntries : QUICK_ENTRIES),
|
||||
recommendations: data.recommendations || [], recState: data.recommendations && data.recommendations.length ? 'loaded' : 'empty',
|
||||
serviceRecommendations: data.serviceRecommendations || [], activities: data.activities || [], serviceLocation: data.serviceLocation || null,
|
||||
articles: data.articles || [], safetyNotice: data.safetyNotice || this.data.safetyNotice,
|
||||
})
|
||||
},
|
||||
|
||||
onViewLikers() {
|
||||
wx.navigateTo({ url: ROUTE.DETAIL.LIKERS });
|
||||
onRetry() { this.loadHomeData() },
|
||||
onBannerTap(e) {
|
||||
const item = e.currentTarget.dataset.item || {}
|
||||
if (item.targetType === 'activity' && item.targetValue) return wx.navigateTo({ url: `/pages/activity-detail/activity-detail?id=${item.targetValue}` })
|
||||
const safeRoutes = { matchmaker: '/pages/matchmaker/matchmaker', counseling: '/pages/counseling-intro/counseling-intro', premarital: '/pages/premarital-intro/premarital-intro', service_location: '/pages/service-station/service-station', article: '/pages/content-detail/content-detail', internal_page: item.targetValue }
|
||||
const url = safeRoutes[item.targetType]
|
||||
if (url && /^\/pages\//.test(url)) wx.navigateTo({ url })
|
||||
},
|
||||
|
||||
onEntryTap(e) {
|
||||
const entry = e.currentTarget.dataset.entry || {}
|
||||
if (!entry.enabled) return wx.showToast({ title: '完成资料后可使用此服务', icon: 'none' })
|
||||
if (entry.tab) wx.switchTab({ url: entry.targetPage })
|
||||
else wx.navigateTo({ url: entry.targetPage })
|
||||
},
|
||||
onViewDetail(e) { if (e.detail.userId) wx.navigateTo({ url: `/pages/profile-detail/profile-detail?id=${e.detail.userId}` }) },
|
||||
onLike(e) { this.setData({ actionLoadingUserId: e.detail.userId }); setTimeout(() => { this.setData({ actionLoadingUserId: '' }); appStore.toastSuccess('已表达喜欢') }, 450) },
|
||||
onPass(e) { this.setData({ actionLoadingUserId: e.detail.userId }); setTimeout(() => this.setData({ actionLoadingUserId: '' }), 300) },
|
||||
onEmptyAction(e) {
|
||||
const { event } = e.detail;
|
||||
if (event === 'discover') {
|
||||
wx.switchTab({ url: ROUTE.TAB.DISCOVER });
|
||||
} else if (event === 'matchmaker') {
|
||||
wx.navigateTo({ url: ROUTE.DETAIL.MATCHMAKER_APPLY });
|
||||
}
|
||||
const event = (e.detail && e.detail.event) || e.currentTarget.dataset.event
|
||||
const routes = { login: '/pages/login/login', profile: '/pages/profile-basic/profile-basic', preference: '/pages/profile-preference/profile-preference', matchmaker: '/pages/matchmaker/matchmaker' }
|
||||
if (event === 'discover') return wx.switchTab({ url: '/pages/discover/discover' })
|
||||
if (routes[event]) wx.navigateTo({ url: routes[event] })
|
||||
},
|
||||
});
|
||||
onResume() { wx.navigateTo({ url: '/pages/relationship-status/relationship-status' }) },
|
||||
onSeeMore() { wx.switchTab({ url: '/pages/discover/discover' }) },
|
||||
onSeeAllActivities() { wx.switchTab({ url: '/pages/activity/activity' }) },
|
||||
onActivityTap(e) { wx.navigateTo({ url: `/pages/activity-detail/activity-detail?id=${e.currentTarget.dataset.id}` }) },
|
||||
onStationTap() { wx.navigateTo({ url: '/pages/service-station/service-station' }) },
|
||||
onServiceTap(e) { const routes={matchmaker:'/pages/matchmaker/matchmaker',counseling:'/pages/counseling-intro/counseling-intro',premarital:'/pages/premarital-intro/premarital-intro'}; if(routes[e.currentTarget.dataset.type]) wx.navigateTo({url:routes[e.currentTarget.dataset.type]}) },
|
||||
onArticleTap(e) { wx.navigateTo({ url: `/pages/content-detail/content-detail?id=${e.currentTarget.dataset.id}` }) },
|
||||
onSafetyTap() { wx.navigateTo({ url: '/pages/anti-fraud/anti-fraud' }) },
|
||||
onMessages() { wx.switchTab({ url: '/pages/messages/messages' }) },
|
||||
})
|
||||
@@ -1,14 +1 @@
|
||||
{
|
||||
"navigationBarTitleText": "善缘丹阳",
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": true,
|
||||
"backgroundTextStyle": "dark",
|
||||
"backgroundColor": "#FAFAF8",
|
||||
"usingComponents": {
|
||||
"ui-section-header": "/components/section-header/index",
|
||||
"ui-recommend-card": "/components/recommend-card/index",
|
||||
"ui-empty": "/components/empty/index",
|
||||
"ui-loading": "/components/loading/index",
|
||||
"ui-status-badge": "/components/status-badge/index"
|
||||
}
|
||||
}
|
||||
{"navigationBarTitleText":"善缘丹阳","navigationStyle":"custom","enablePullDownRefresh":true,"backgroundTextStyle":"dark","backgroundColor":"#FAFAF8","usingComponents":{"ui-section-header":"/components/section-header/index","ui-recommend-card":"/components/recommend-card/index","ui-empty":"/components/empty/index","ui-loading":"/components/loading/index","ui-status-badge":"/components/status-badge/index","ui-error-state":"/components/error-state/index"}}
|
||||
+33
-202
@@ -1,214 +1,45 @@
|
||||
<!-- U18 首页 - V1.1 新版结构 -->
|
||||
<view class="page">
|
||||
|
||||
<!-- ========== 品牌区 ========== -->
|
||||
<!-- 品牌区 -->
|
||||
<view class="brand-bar">
|
||||
<view class="brand-info">
|
||||
<text class="brand-name">善缘丹阳</text>
|
||||
<text class="brand-slogan">丹阳公益婚恋服务平台</text>
|
||||
</view>
|
||||
<view class="brand-actions">
|
||||
<view class="bell-btn" bindtap="onMessages">
|
||||
<text class="bell-icon">🔔</text>
|
||||
<block wx:if="{{unreadCount > 0}}">
|
||||
<text class="badge">{{unreadCount > 9 ? '9+' : unreadCount}}</text>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<view><text class="brand-name">善缘丹阳</text><text class="brand-slogan">真实相识 · 公益牵线</text></view>
|
||||
<view class="brand-mark">公益服务</view>
|
||||
<view class="bell-btn" bindtap="onMessages"><text>消息</text><text wx:if="{{unreadCount}}" class="badge">{{unreadCount}}</text></view>
|
||||
</view>
|
||||
<view wx:if="{{previewMode}}" class="preview-note">界面预览数据</view>
|
||||
|
||||
<!-- 用户状态条 -->
|
||||
<view class="status-strip">
|
||||
<view wx:if="{{isOnboarding}}" class="status-main"><text class="status-title">完善资料,开启安心相遇</text><text class="status-desc">{{onboardingMessage}}</text><view class="status-action" bindtap="onEmptyAction" data-event="{{onboardingActions[0].event}}">{{onboardingActions[0].text}}</view></view>
|
||||
<view wx:elif="{{isPaused}}" class="status-main"><text class="status-title">已暂停新的对象推荐</text><view class="status-action" bindtap="onResume">恢复寻找</view></view>
|
||||
<view wx:elif="{{isSuccessful}}" class="status-main"><text class="status-title">祝福你们正在认真了解彼此</text><text class="status-desc">愿真诚与尊重陪伴你们继续前行</text></view>
|
||||
<view wx:else class="status-checks"><text wx:for="{{statusChecks}}" wx:key="label" class="status-check {{item.done ? 'done' : ''}}">{{item.done ? '✓' : '○'}} {{item.label}}</text></view>
|
||||
</view>
|
||||
|
||||
<!-- ========== 用户状态条 ========== -->
|
||||
<view class="status-strip" wx:if="{{userState}}">
|
||||
<!-- 未完成准入 -->
|
||||
<block wx:if="{{isOnboarding}}">
|
||||
<view class="status-prompt">
|
||||
<text class="prompt-text">{{onboardingMessage}}</text>
|
||||
<view class="prompt-actions" wx:if="{{onboardingActions.length > 0}}">
|
||||
<view
|
||||
wx:for="{{onboardingActions}}"
|
||||
wx:key="event"
|
||||
class="prompt-btn {{item.variant}}"
|
||||
bindtap="onEmptyAction"
|
||||
data-event="{{item.event}}"
|
||||
>{{item.text}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 已验证 -->
|
||||
<block wx:elif="{{isVerified}}">
|
||||
<view class="status-checks">
|
||||
<text class="status-check {{item.done ? 'done' : 'pending'}}"
|
||||
wx:for="{{statusChecks}}" wx:key="label">
|
||||
{{item.done ? '✓' : '○'}} {{item.label}}
|
||||
</text>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 已暂停 -->
|
||||
<block wx:elif="{{isPaused}}">
|
||||
<view class="paused-hint">
|
||||
<text>你已暂停新的对象推荐</text>
|
||||
<view class="resume-btn" bindtap="onResume">恢复寻找</view>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 已成功牵手 -->
|
||||
<block wx:elif="{{isSuccessful}}">
|
||||
<view class="successful-hint">
|
||||
<text>祝福你们正在认真了解彼此</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<!-- Banner 轮播 -->
|
||||
<view class="banner-section"><swiper class="banner-swiper" indicator-dots autoplay circular interval="4500" indicator-active-color="#E96B6B"><swiper-item wx:for="{{banners}}" wx:key="id"><view class="banner-card" bindtap="onBannerTap" data-item="{{item}}"><image wx:if="{{item.imageFileId}}" src="{{item.imageFileId}}" mode="aspectFill" class="banner-img"/><view class="banner-art"><view class="heart-line">♡</view></view><view class="banner-copy"><text class="banner-title">{{item.title}}</text><text class="banner-subtitle">{{item.subtitle}}</text><view class="banner-btn">{{item.buttonText || '查看详情'}} <text>›</text></view></view></view></swiper-item></swiper></view>
|
||||
|
||||
<!-- ========== Banner 轮播 ========== -->
|
||||
<view class="banner-section" wx:if="{{banners.length > 0}}">
|
||||
<swiper
|
||||
class="banner-swiper"
|
||||
indicator-dots
|
||||
autoplay
|
||||
circular
|
||||
interval="4500"
|
||||
indicator-active-color="#E96B6B"
|
||||
bindchange="onBannerChange"
|
||||
>
|
||||
<swiper-item wx:for="{{banners}}" wx:key="id">
|
||||
<view class="banner-card" bindtap="onBannerTap" data-item="{{item}}">
|
||||
<image wx:if="{{item.imageFileId}}"
|
||||
src="{{item.imageFileId}}"
|
||||
mode="aspectFill"
|
||||
class="banner-img" />
|
||||
<view class="banner-overlay">
|
||||
<text class="banner-title">{{item.title}}</text>
|
||||
<text class="banner-subtitle" wx:if="{{item.subtitle}}">{{item.subtitle}}</text>
|
||||
<view class="banner-btn" wx:if="{{item.buttonText}}">{{item.buttonText}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
<!-- 8个核心入口 -->
|
||||
<view class="entries-card"><view class="entries-grid"><view wx:for="{{quickEntries}}" wx:key="code" class="entry-item {{!item.enabled ? 'disabled' : ''}}" bindtap="onEntryTap" data-entry="{{item}}"><view class="entry-icon tone-{{item.tone}}">{{item.icon}}</view><text class="entry-label">{{item.label}}</text></view></view></view>
|
||||
|
||||
<!-- ========== 8个核心入口 ========== -->
|
||||
<view class="entries-section">
|
||||
<view class="entries-grid">
|
||||
<view
|
||||
wx:for="{{quickEntries}}"
|
||||
wx:key="code"
|
||||
class="entry-item {{!item.enabled ? 'disabled' : ''}}"
|
||||
bindtap="onEntryTap"
|
||||
data-entry="{{item}}"
|
||||
>
|
||||
<view class="entry-icon-bg {{item.iconClass}}">
|
||||
<text class="entry-icon">{{item.icon}}</text>
|
||||
</view>
|
||||
<text class="entry-label">{{item.label}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:if="{{loadError}}" class="section"><ui-error-state title="暂时无法加载首页" description="请检查网络后重试" bind:retry="onRetry"/></view>
|
||||
<block wx:else>
|
||||
<!-- 今日推荐 -->
|
||||
<view class="section"><ui-section-header title="今日推荐" subtitle="根据双方条件,为你认真筛选" action="查看更多" bind:action="onSeeMore"/><ui-loading wx:if="{{loading}}" mode="skeleton" rows="2"/><view wx:elif="{{recommendations.length}}" wx:for="{{recommendations}}" wx:key="userId" class="rec-wrap"><ui-recommend-card recommendation="{{item}}" action-loading="{{actionLoadingUserId === item.userId}}" bind:view="onViewDetail" bind:like="onLike" bind:pass="onPass"/></view><ui-empty wx:else illustration="heart" title="暂时没有完全符合双方条件的人" description="可以调整择偶条件,或申请公益红娘协助推荐" actions="{{emptyActions}}" bind:action="onEmptyAction"/></view>
|
||||
|
||||
<!-- ========== 今日推荐 ========== -->
|
||||
<view class="section" wx:if="{{recommendations.length > 0 || recState === 'loading'}}">
|
||||
<ui-section-header title="今日推荐"
|
||||
subtitle="根据双方条件,为你筛选更合适的人"
|
||||
more="{{true}}"
|
||||
bind:more="onSeeMore" />
|
||||
<block wx:if="{{recState === 'loading' && recommendations.length === 0}}">
|
||||
<view class="skeleton-card" wx:for="{{3}}" wx:key="index">
|
||||
<view class="skeleton-avatar"></view>
|
||||
<view class="skeleton-info">
|
||||
<view class="skeleton-line w60"></view>
|
||||
<view class="skeleton-line w40"></view>
|
||||
<view class="skeleton-line w80"></view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<block wx:elif="{{recommendations.length > 0}}">
|
||||
<view wx:for="{{recommendations}}" wx:key="userId" class="rec-card">
|
||||
<ui-recommend-card
|
||||
recommendation="{{item}}"
|
||||
action-loading="{{actionLoadingUserId === item.userId}}"
|
||||
bind:view="onViewDetail"
|
||||
bind:like="onLike"
|
||||
bind:pass="onPass"
|
||||
/>
|
||||
</view>
|
||||
</block>
|
||||
<view wx:if="{{recState === 'finished' && recommendations.length > 0}}" class="end-tip">
|
||||
已展示今日全部推荐 · 明天再来看看
|
||||
</view>
|
||||
</view>
|
||||
<!-- 善缘公益服务 -->
|
||||
<view class="section"><ui-section-header title="善缘公益服务" subtitle="需要时,我们在这里陪伴你"/><view wx:for="{{serviceRecommendations}}" wx:key="type" class="service-card" bindtap="onServiceTap" data-type="{{item.type}}"><view><text class="service-title">{{item.title}}</text><text class="service-desc">{{item.description}}</text></view><view class="text-link">{{item.ctaText}} ›</view></view></view>
|
||||
|
||||
<!-- ========== 无推荐空状态 ========== -->
|
||||
<view class="section" wx:if="{{!isOnboarding && recommendations.length === 0 && recState !== 'loading'}}">
|
||||
<ui-empty
|
||||
illustration="heart"
|
||||
title="暂时没有找到完全符合双方条件的人"
|
||||
description="你可以调整择偶条件,或申请公益红娘帮你推荐"
|
||||
actions="{{emptyActions}}"
|
||||
bind:action="onEmptyAction"
|
||||
/>
|
||||
</view>
|
||||
<!-- 近期公益活动 -->
|
||||
<view class="section"><ui-section-header title="近期公益活动" action="全部活动" bind:action="onSeeAllActivities"/><scroll-view scroll-x class="activity-scroll"><view wx:for="{{activities}}" wx:key="id" class="activity-card" bindtap="onActivityTap" data-id="{{item.id}}"><view class="activity-cover"><text class="activity-tag">{{item.typeLabel || '公益活动'}}</text><text class="activity-symbol">相遇</text></view><view class="activity-info"><text class="activity-title">{{item.title}}</text><text class="activity-meta">9月20日 14:00 · {{item.locationName}}</text></view></view></scroll-view></view>
|
||||
|
||||
<!-- ========== 善缘公益服务 ========== -->
|
||||
<view class="section service-reco-section" wx:if="{{serviceRecommendations.length > 0}}">
|
||||
<ui-section-header title="善缘公益服务" />
|
||||
<view wx:for="{{serviceRecommendations}}" wx:key="type"
|
||||
class="service-card" bindtap="onServiceTap" data-type="{{item.type}}">
|
||||
<view class="service-card-inner">
|
||||
<text class="service-title">{{item.title}}</text>
|
||||
<text class="service-desc">{{item.description}}</text>
|
||||
<view class="service-cta">{{item.ctaText}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 善缘服务站 -->
|
||||
<view class="section"><ui-section-header title="善缘服务站" subtitle="就在身边的公益婚恋服务阵地"/><view class="station-card" bindtap="onStationTap"><view class="station-pin">站</view><view class="station-info"><text class="station-name">{{serviceLocation.name}}</text><text class="station-desc">{{serviceLocation.shortName}}</text><view><text wx:for="{{serviceLocation.services}}" wx:key="*this" class="mini-tag">{{item}}</text></view></view><text class="arrow">›</text></view></view>
|
||||
|
||||
<!-- ========== 近期公益活动 ========== -->
|
||||
<view class="section activity-section" wx:if="{{activities.length > 0}}">
|
||||
<ui-section-header title="近期公益活动" more="{{true}}"
|
||||
bind:more="onSeeAllActivities" />
|
||||
<scroll-view scroll-x class="activity-scroll">
|
||||
<view wx:for="{{activities}}" wx:key="id"
|
||||
class="activity-h-card" bindtap="onActivityTap" data-id="{{item.id}}">
|
||||
<image wx:if="{{item.coverFileId}}" src="{{item.coverFileId}}"
|
||||
mode="aspectFill" class="activity-cover" />
|
||||
<view class="activity-info">
|
||||
<text class="activity-title">{{item.title}}</text>
|
||||
<text class="activity-meta" wx:if="{{item.startAt}}">{{formatDate(item.startAt)}}</text>
|
||||
<text class="activity-meta" wx:if="{{item.locationName}}">{{item.locationName}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- 婚恋课堂 -->
|
||||
<view class="section"><ui-section-header title="婚恋课堂" subtitle="学会认识自己,也学会理解关系" action="更多内容" bind:action="onEntryTap" data-entry="{{quickEntries[4]}}"/><view class="article-list"><view wx:for="{{articles}}" wx:key="id" class="article-row" bindtap="onArticleTap" data-id="{{item.id}}"><view class="article-icon">读</view><view class="article-info"><text class="article-title">{{item.title}}</text><text class="article-meta">{{item.categoryName}} · {{item.readTime}}</text></view><text class="arrow">›</text></view></view></view>
|
||||
|
||||
<!-- ========== 善缘服务站 ========== -->
|
||||
<view class="section" wx:if="{{serviceLocation}}">
|
||||
<view class="station-card" bindtap="onStationTap">
|
||||
<view class="station-card-inner">
|
||||
<view class="station-icon-area">
|
||||
<text class="station-icon">📍</text>
|
||||
</view>
|
||||
<view class="station-info">
|
||||
<text class="station-name">{{serviceLocation.name}}</text>
|
||||
<text class="station-short">{{serviceLocation.shortName}}</text>
|
||||
<view class="station-services">
|
||||
<text wx:for="{{serviceLocation.services}}" wx:key="*this"
|
||||
class="station-tag">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="station-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ========== 安全提醒 ========== -->
|
||||
<view class="safety-notice">
|
||||
<view class="safety-notice-inner">
|
||||
<text class="safety-icon">🛡</text>
|
||||
<view class="safety-text-area">
|
||||
<text class="safety-title">善缘提醒</text>
|
||||
<text class="safety-body">{{safetyNotice.text}}</text>
|
||||
</view>
|
||||
<text class="safety-cta" bindtap="onSafetyTap">{{safetyNotice.ctaText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部安全区 -->
|
||||
<!-- 安全提醒 -->
|
||||
<view class="section safety-card" bindtap="onSafetyTap"><view class="shield">安</view><view class="safety-info"><text class="safety-title">善缘提醒</text><text class="safety-body">{{safetyNotice.text}}</text><text class="safety-link">{{safetyNotice.ctaText}} ›</text></view></view>
|
||||
</block>
|
||||
<view class="safe-area-bottom"></view>
|
||||
</view>
|
||||
</view>
|
||||
+14
-246
@@ -1,246 +1,14 @@
|
||||
/* U18 首页 - V1.1 新版样式 */
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #FAFAF8;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* ========== 品牌区 ========== */
|
||||
.brand-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 24rpx 16rpx;
|
||||
background: #fff;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.brand-info { display: flex; flex-direction: column; }
|
||||
|
||||
.brand-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #E96B6B;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.brand-slogan {
|
||||
font-size: 22rpx;
|
||||
color: #888;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.bell-btn {
|
||||
position: relative;
|
||||
width: 64rpx; height: 64rpx;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.bell-icon { font-size: 32rpx; }
|
||||
|
||||
.badge {
|
||||
position: absolute; top: -4rpx; right: -4rpx;
|
||||
min-width: 28rpx; height: 28rpx; line-height: 28rpx;
|
||||
padding: 0 6rpx;
|
||||
background: #E96B6B; color: #fff;
|
||||
font-size: 20rpx; border-radius: 14rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ========== 用户状态条 ========== */
|
||||
.status-strip {
|
||||
background: #fff;
|
||||
padding: 16rpx 24rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.status-checks { display: flex; gap: 24rpx; flex-wrap: wrap; }
|
||||
|
||||
.status-check {
|
||||
font-size: 24rpx; color: #666;
|
||||
}
|
||||
.status-check.done { color: #2E7D32; }
|
||||
.status-check.pending { color: #F57C00; }
|
||||
|
||||
.status-prompt { padding: 16rpx 0; }
|
||||
|
||||
.prompt-text {
|
||||
font-size: 26rpx; color: #555;
|
||||
display: block; margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.prompt-actions { display: flex; gap: 16rpx; }
|
||||
|
||||
.prompt-btn {
|
||||
padding: 12rpx 28rpx; border-radius: 8rpx;
|
||||
font-size: 26rpx; font-weight: 500;
|
||||
}
|
||||
.prompt-btn.primary { background: #E96B6B; color: #fff; }
|
||||
.prompt-btn.secondary { background: #fff; color: #E96B6B; border: 1rpx solid #E96B6B; }
|
||||
|
||||
.paused-hint, .successful-hint { padding: 16rpx 0; text-align: center; }
|
||||
.paused-hint text { font-size: 26rpx; color: #888; display: block; margin-bottom: 12rpx; }
|
||||
.resume-btn {
|
||||
display: inline-block; padding: 10rpx 32rpx;
|
||||
background: #E96B6B; color: #fff;
|
||||
border-radius: 8rpx; font-size: 26rpx;
|
||||
}
|
||||
|
||||
/* ========== Banner ========== */
|
||||
.banner-section { padding: 16rpx 24rpx; }
|
||||
|
||||
.banner-swiper {
|
||||
width: 100%; height: 340rpx;
|
||||
border-radius: 16rpx; overflow: hidden;
|
||||
}
|
||||
|
||||
.banner-card { width: 100%; height: 100%; position: relative; }
|
||||
.banner-img { width: 100%; height: 100%; object-fit: cover; }
|
||||
|
||||
.banner-overlay {
|
||||
position: absolute; bottom: 0; left: 0; right: 0;
|
||||
padding: 24rpx;
|
||||
background: linear-gradient(transparent, rgba(0,0,0,0.6));
|
||||
color: #fff;
|
||||
}
|
||||
.banner-title { font-size: 30rpx; font-weight: 600; display: block; margin-bottom: 4rpx; }
|
||||
.banner-subtitle { font-size: 22rpx; opacity: 0.9; display: block; margin-bottom: 8rpx; }
|
||||
.banner-btn {
|
||||
display: inline-block; padding: 8rpx 20rpx;
|
||||
background: rgba(255,255,255,0.25); border: 1rpx solid rgba(255,255,255,0.6);
|
||||
border-radius: 20rpx; font-size: 22rpx; color: #fff;
|
||||
}
|
||||
|
||||
/* ========== 8个核心入口 ========== */
|
||||
.entries-section {
|
||||
background: #fff;
|
||||
margin: 0 24rpx; border-radius: 16rpx;
|
||||
padding: 24rpx 16rpx; margin-top: -8rpx;
|
||||
position: relative; z-index: 10;
|
||||
}
|
||||
|
||||
.entries-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.entry-item {
|
||||
display: flex; flex-direction: column; align-items: center;
|
||||
padding: 12rpx 0;
|
||||
}
|
||||
.entry-item.disabled { opacity: 0.5; }
|
||||
|
||||
.entry-icon-bg {
|
||||
width: 72rpx; height: 72rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.entry-icon-bg.icon-dating { background: #FFF0F0; }
|
||||
.entry-icon-bg.icon-matchmaker { background: #FFF8E6; }
|
||||
.entry-icon-bg.icon-activity { background: #E8F5E9; }
|
||||
.entry-icon-bg.icon-counseling { background: #E3F2FD; }
|
||||
.entry-icon-bg.icon-class { background: #F3E5F5; }
|
||||
.entry-icon-bg.icon-premarital { background: #FFF3E0; }
|
||||
.entry-icon-bg.icon-shield { background: #E8EAF6; }
|
||||
.entry-icon-bg.icon-location { background: #E0F2F1; }
|
||||
|
||||
.entry-icon { font-size: 32rpx; }
|
||||
.entry-label { font-size: 22rpx; color: #333; text-align: center; line-height: 1.3; }
|
||||
|
||||
/* ========== 通用 Section ========== */
|
||||
.section { margin-top: 24rpx; padding: 0 24rpx; }
|
||||
|
||||
/* ========== 推荐卡片 ========== */
|
||||
.rec-card { margin-bottom: 16rpx; }
|
||||
.end-tip { text-align: center; font-size: 24rpx; color: #999; padding: 16rpx 0; }
|
||||
|
||||
/* ========== 骨架屏 ========== */
|
||||
.skeleton-card {
|
||||
display: flex; align-items: center;
|
||||
padding: 16rpx 0; margin-bottom: 12rpx;
|
||||
}
|
||||
.skeleton-avatar {
|
||||
width: 88rpx; height: 112rpx;
|
||||
background: #eee; border-radius: 8rpx; margin-right: 16rpx;
|
||||
}
|
||||
.skeleton-info { flex: 1; }
|
||||
.skeleton-line {
|
||||
height: 24rpx; background: #eee;
|
||||
border-radius: 4rpx; margin-bottom: 8rpx;
|
||||
}
|
||||
.skeleton-line.w60 { width: 60%; }
|
||||
.skeleton-line.w40 { width: 40%; }
|
||||
.skeleton-line.w80 { width: 80%; }
|
||||
|
||||
/* ========== 善缘公益服务 ========== */
|
||||
.service-card {
|
||||
background: linear-gradient(135deg, #FFF8F0 0%, #FFF0F0 100%);
|
||||
border-radius: 16rpx; padding: 24rpx; margin-bottom: 12rpx;
|
||||
}
|
||||
.service-card-inner { display: flex; flex-direction: column; gap: 8rpx; }
|
||||
.service-title { font-size: 28rpx; font-weight: 600; color: #333; }
|
||||
.service-desc { font-size: 24rpx; color: #666; line-height: 1.5; }
|
||||
.service-cta {
|
||||
display: inline-block; margin-top: 8rpx;
|
||||
padding: 8rpx 20rpx; background: #E96B6B;
|
||||
color: #fff; border-radius: 20rpx;
|
||||
font-size: 24rpx; width: fit-content;
|
||||
}
|
||||
|
||||
/* ========== 近期活动 ========== */
|
||||
.activity-scroll { white-space: nowrap; margin: 0 -4rpx; }
|
||||
.activity-h-card {
|
||||
display: inline-block; width: 480rpx; margin: 0 4rpx 12rpx;
|
||||
background: #fff; border-radius: 12rpx; overflow: hidden;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
|
||||
}
|
||||
.activity-cover { width: 100%; height: 270rpx; background: #f0f0f0; }
|
||||
.activity-info { padding: 12rpx 16rpx; }
|
||||
.activity-title { font-size: 26rpx; font-weight: 600; color: #333; display: block; margin-bottom: 4rpx; }
|
||||
.activity-meta { font-size: 22rpx; color: #888; display: block; }
|
||||
|
||||
/* ========== 善缘服务站 ========== */
|
||||
.station-card {
|
||||
background: #fff; border-radius: 16rpx; overflow: hidden;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); margin-top: 24rpx;
|
||||
}
|
||||
.station-card-inner {
|
||||
display: flex; align-items: center; padding: 24rpx; gap: 16rpx;
|
||||
}
|
||||
.station-icon-area {
|
||||
width: 80rpx; height: 80rpx; background: #E8F5E9;
|
||||
border-radius: 16rpx; display: flex; align-items: center;
|
||||
justify-content: center; flex-shrink: 0;
|
||||
}
|
||||
.station-icon { font-size: 40rpx; }
|
||||
.station-info { flex: 1; min-width: 0; }
|
||||
.station-name { font-size: 28rpx; font-weight: 600; color: #333; display: block; }
|
||||
.station-short { font-size: 22rpx; color: #888; display: block; margin-top: 2rpx; }
|
||||
.station-services { display: flex; flex-wrap: wrap; gap: 8rpx; margin-top: 8rpx; }
|
||||
.station-tag {
|
||||
font-size: 20rpx; padding: 2rpx 12rpx;
|
||||
background: #F5F5F5; border-radius: 12rpx; color: #666;
|
||||
}
|
||||
.station-arrow { font-size: 36rpx; color: #ccc; flex-shrink: 0; }
|
||||
|
||||
/* ========== 安全提醒 ========== */
|
||||
.safety-notice {
|
||||
margin: 24rpx 24rpx 0;
|
||||
background: #FFF9E6; border-radius: 12rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
}
|
||||
.safety-notice-inner { display: flex; align-items: flex-start; gap: 12rpx; }
|
||||
.safety-icon { font-size: 32rpx; flex-shrink: 0; margin-top: 2rpx; }
|
||||
.safety-text-area { flex: 1; }
|
||||
.safety-title { font-size: 24rpx; font-weight: 600; color: #8B6914; display: block; margin-bottom: 4rpx; }
|
||||
.safety-body { font-size: 22rpx; color: #8B6914; line-height: 1.5; display: block; }
|
||||
.safety-cta { font-size: 24rpx; color: #E96B6B; flex-shrink: 0; padding-left: 12rpx; }
|
||||
|
||||
/* ========== 底部安全区 ========== */
|
||||
.safe-area-bottom { height: env(safe-area-inset-bottom); }
|
||||
.page{min-height:100vh;background:var(--color-bg-page);padding-bottom:var(--spacing-8)}
|
||||
.brand-bar{height:104rpx;padding:12rpx var(--spacing-page-x);display:flex;align-items:center;gap:16rpx;background:var(--color-bg-card);position:sticky;top:0;z-index:var(--z-sticky);border-bottom:1rpx solid var(--color-divider)}
|
||||
.brand-bar>view:first-child{display:flex;flex-direction:column;flex:1}.brand-name{font-size:36rpx;font-weight:700;color:var(--color-primary);line-height:1.25}.brand-slogan{font-size:22rpx;color:var(--color-text-tertiary);margin-top:4rpx}.brand-mark{font-size:20rpx;color:var(--color-success);background:var(--color-success-light);padding:6rpx 12rpx;border-radius:var(--radius-full)}
|
||||
.bell-btn{position:relative;min-width:88rpx;height:88rpx;display:flex;align-items:center;justify-content:center;font-size:24rpx;color:var(--color-text-secondary)}.badge{position:absolute;right:0;top:8rpx;min-width:28rpx;height:28rpx;padding:0 4rpx;line-height:28rpx;text-align:center;font-size:18rpx;color:var(--color-text-inverse);background:var(--color-primary);border-radius:var(--radius-full)}
|
||||
.preview-note{text-align:center;background:var(--color-warning-light);color:var(--color-warning);font-size:20rpx;padding:6rpx}
|
||||
.status-strip{margin:16rpx var(--spacing-page-x) 0;padding:20rpx 24rpx;background:var(--color-bg-card);border:1rpx solid var(--color-border);border-radius:var(--radius-md)}.status-main{display:flex;align-items:center;gap:12rpx;flex-wrap:wrap}.status-title{font-size:27rpx;font-weight:600}.status-desc{font-size:23rpx;color:var(--color-text-tertiary);flex:1}.status-action{min-height:64rpx;padding:0 22rpx;display:flex;align-items:center;color:var(--color-primary);font-size:24rpx;font-weight:600}.status-checks{display:flex;justify-content:space-between;gap:12rpx}.status-check{font-size:23rpx;color:var(--color-warning)}.status-check.done{color:var(--color-success)}
|
||||
.banner-section{padding:16rpx var(--spacing-page-x) 0}.banner-swiper{height:292rpx;border-radius:var(--radius-lg);overflow:hidden}.banner-card{height:100%;position:relative;background:linear-gradient(135deg,var(--color-primary-lighter),#FBE8DE)}.banner-img{width:100%;height:100%}.banner-art{position:absolute;right:-24rpx;top:-28rpx;width:260rpx;height:260rpx;border-radius:50%;background:rgba(255,255,255,.55);display:flex;align-items:center;justify-content:center}.heart-line{font-size:128rpx;color:rgba(233,107,107,.34)}.banner-copy{position:absolute;left:28rpx;top:36rpx;width:72%}.banner-title{display:block;font-size:34rpx;font-weight:700;color:#6F3A35;line-height:1.35}.banner-subtitle{display:block;margin-top:12rpx;font-size:24rpx;color:#8B5E56}.banner-btn{display:inline-flex;align-items:center;gap:8rpx;margin-top:24rpx;min-height:64rpx;padding:0 24rpx;border-radius:var(--radius-full);background:var(--color-primary);color:var(--color-text-inverse);font-size:24rpx}
|
||||
.entries-card{margin:20rpx var(--spacing-page-x) 0;padding:24rpx 12rpx;background:var(--color-bg-card);border-radius:var(--radius-lg);box-shadow:var(--shadow-card)}.entries-grid{display:grid;grid-template-columns:repeat(4,1fr);row-gap:24rpx}.entry-item{min-height:116rpx;display:flex;flex-direction:column;align-items:center;justify-content:center}.entry-item.disabled{opacity:.45}.entry-icon{width:76rpx;height:76rpx;display:flex;align-items:center;justify-content:center;border-radius:24rpx;font-size:28rpx;font-weight:700}.entry-label{margin-top:9rpx;font-size:23rpx;color:var(--color-text-primary)}.tone-rose{background:#FFF0EF;color:#D85D62}.tone-amber{background:#FFF7E6;color:#B77B19}.tone-green{background:#ECF8F2;color:#2E8A62}.tone-blue{background:#EEF5FF;color:#4A72B8}.tone-purple{background:#F5EFFB;color:#8865A7}.tone-orange{background:#FFF2E8;color:#B9723B}.tone-indigo{background:#EEF0FB;color:#5F6FAE}.tone-teal{background:#EAF7F5;color:#31867C}
|
||||
.section{margin-top:var(--spacing-section);padding:0 var(--spacing-page-x)}.rec-wrap{margin-top:20rpx}.service-card{display:flex;align-items:center;gap:20rpx;padding:28rpx;background:linear-gradient(135deg,var(--color-primary-lighter),#FFF9F0);border:1rpx solid #F8E3DD;border-radius:var(--radius-lg)}.service-card>view:first-child{flex:1}.service-title{display:block;font-size:30rpx;font-weight:650}.service-desc{display:block;margin-top:10rpx;font-size:24rpx;color:var(--color-text-secondary);line-height:1.55}.text-link{font-size:23rpx;font-weight:600;color:var(--color-primary);white-space:nowrap}
|
||||
.activity-scroll{white-space:nowrap;margin-top:18rpx;width:100%}.activity-card{display:inline-block;width:560rpx;margin-right:20rpx;background:var(--color-bg-card);border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-card);white-space:normal}.activity-cover{height:210rpx;padding:18rpx;background:linear-gradient(135deg,#F3D7CA,#F8ECE4);position:relative}.activity-tag{font-size:21rpx;color:#A45D50;background:rgba(255,255,255,.8);padding:7rpx 14rpx;border-radius:var(--radius-full)}.activity-symbol{position:absolute;right:28rpx;bottom:18rpx;font-size:52rpx;color:rgba(164,93,80,.32);font-weight:700}.activity-info{padding:22rpx}.activity-title{display:block;font-size:28rpx;font-weight:600}.activity-meta{display:block;margin-top:10rpx;font-size:23rpx;color:var(--color-text-tertiary)}
|
||||
.station-card{display:flex;align-items:center;gap:20rpx;padding:28rpx;background:var(--color-bg-card);border:1rpx solid var(--color-border);border-radius:var(--radius-lg)}.station-pin,.shield,.article-icon{width:72rpx;height:72rpx;flex:none;display:flex;align-items:center;justify-content:center;border-radius:22rpx;background:var(--color-success-light);color:var(--color-success);font-size:27rpx;font-weight:700}.station-info,.article-info,.safety-info{flex:1;min-width:0}.station-name{display:block;font-size:29rpx;font-weight:650}.station-desc{display:block;margin:7rpx 0 12rpx;font-size:23rpx;color:var(--color-text-tertiary)}.mini-tag{font-size:20rpx;color:var(--color-text-secondary);margin-right:10rpx;padding:5rpx 10rpx;background:var(--color-bg-page);border-radius:var(--radius-full)}.arrow{font-size:40rpx;color:var(--color-text-disabled)}
|
||||
.article-list{margin-top:18rpx;background:var(--color-bg-card);border-radius:var(--radius-lg);overflow:hidden}.article-row{min-height:124rpx;padding:18rpx 24rpx;display:flex;align-items:center;gap:18rpx;border-bottom:1rpx solid var(--color-divider)}.article-row:last-child{border-bottom:0}.article-icon{background:#F5EFFB;color:#8865A7}.article-title{display:block;font-size:27rpx;font-weight:550;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.article-meta{display:block;margin-top:8rpx;font-size:22rpx;color:var(--color-text-tertiary)}
|
||||
.safety-card{display:flex;gap:20rpx;margin-left:var(--spacing-page-x);margin-right:var(--spacing-page-x);padding:28rpx;background:#F3F7FC;border-radius:var(--radius-lg)}.shield{background:#E7EEF9;color:var(--color-info)}.safety-title{display:block;font-size:28rpx;font-weight:650}.safety-body{display:block;margin-top:8rpx;font-size:23rpx;color:var(--color-text-secondary);line-height:1.6}.safety-link{display:block;margin-top:12rpx;font-size:23rpx;color:var(--color-info);font-weight:600}
|
||||
@media (max-width:340px){.brand-mark{display:none}.entry-label{font-size:21rpx}.entry-icon{width:68rpx;height:68rpx}.banner-title{font-size:31rpx}.status-checks{flex-wrap:wrap;justify-content:flex-start}}
|
||||
Reference in New Issue
Block a user