feat: redesign activity and profile tabs

This commit is contained in:
Codex
2026-09-14 07:05:20 +08:00
parent b7dc52b517
commit a6037ded59
14 changed files with 143 additions and 358 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ Component({
methods: {
onTap() {
if (!this.data.activity) return;
this.triggerEvent('tap', { activityId: this.data.activity.activityId });
this.triggerEvent('tap', { activityId: this.data.activity.activityId || this.data.activity.id });
},
},
});
+8 -51
View File
@@ -1,62 +1,19 @@
<view class="ui-act {{compact ? 'ui-act--compact' : ''}}" bindtap="onTap">
<view class="ui-act__cover">
<image
wx:if="{{activity.coverUrl}}"
class="ui-act__cover-img"
src="{{activity.coverUrl}}"
mode="aspectFill"
/>
<view wx:else class="ui-act__cover-placeholder">
<view class="ui-act__cover-icon"></view>
</view>
<view class="ui-act__fee-badge {{activity.feeType === 'paid' ? 'ui-act__fee-badge--paid' : 'ui-act__fee-badge--free'}}">
{{activity.feeType === 'paid' ? '付费' : '公益免费'}}
</view>
<image class="ui-act__cover-img" src="{{activity.coverUrl || activity.coverImageUrl || '/assets/covers/activity-default.jpg'}}" mode="aspectFill" />
<view class="ui-act__fee-badge">公益活动</view>
</view>
<view class="ui-act__body">
<view class="ui-act__title">{{activity.title}}</view>
<view class="ui-act__meta">
<text class="ui-act__time">{{activity.startAtText || activity.startAt}}</text>
<text wx:if="{{activity.location}}"> · {{activity.location}}</text>
</view>
<view wx:if="{{activity.organizer}}" class="ui-act__meta ui-act__organizer">
主办:{{activity.organizer}}
</view>
<view wx:if="{{activity.suitableFor}}" class="ui-act__meta">
适合:{{activity.suitableFor}}
</view>
<view wx:if="{{!compact}}" class="ui-act__intro">{{activity.intro}}</view>
<view class="ui-act__meta"><text>{{activity.startAtText || activity.activityStartAt || activity.startAt}}</text></view>
<view class="ui-act__meta"><text>{{activity.locationName || activity.location || activity.address}}</text></view>
<view wx:if="{{activity.suitableFor}}" class="ui-act__meta">适合:{{activity.suitableFor}}</view>
<view wx:if="{{!compact && (activity.intro || activity.description)}}" class="ui-act__intro">{{activity.intro || activity.description}}</view>
<view class="ui-act__footer">
<view class="ui-act__capacity">
<text wx:if="{{activity.capacity}}">容量 {{activity.capacity}}</text>
</view>
<view wx:if="{{showStatusBadge}}">
<ui-status-badge
wx:if="{{activity.status === 'registration_open'}}"
kind="success"
label="报名中"
/>
<ui-status-badge
wx:elif="{{activity.status === 'registration_closed'}}"
kind="warning"
label="报名截止"
/>
<ui-status-badge
wx:elif="{{activity.status === 'ongoing'}}"
kind="info"
label="进行中"
/>
<ui-status-badge
wx:elif="{{activity.status === 'ended'}}"
kind="disabled"
label="已结束"
/>
<ui-status-badge
wx:elif="{{activity.status === 'cancelled'}}"
kind="danger"
label="已取消"
/>
<text wx:if="{{activity.capacity || activity.totalLimit}}">报名人数 {{activity.registeredCount !== undefined ? activity.registeredCount : ((activity.currentMaleCount || 0) + (activity.currentFemaleCount || 0))}}/{{activity.capacity || activity.totalLimit}}</text>
</view>
<view class="ui-act__status-action {{activity.status !== 'registration_open' ? 'disabled' : ''}}">{{activity.status === 'registration_open' ? '立即报名' : (activity.status === 'ongoing' ? '进行中' : '查看详情')}}</view>
</view>
</view>
</view>
+1 -108
View File
@@ -1,108 +1 @@
.ui-act {
background: var(--color-bg-card);
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-card);
margin-bottom: var(--spacing-3);
}
.ui-act__cover {
position: relative;
width: 100%;
height: 240rpx;
background: var(--color-bg-disabled);
}
.ui-act__cover-img {
width: 100%;
height: 100%;
display: block;
}
.ui-act__cover-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: var(--color-primary-light);
}
.ui-act__cover-icon {
width: 80rpx;
height: 80rpx;
background: var(--color-primary);
opacity: 0.4;
border-radius: var(--radius-md);
}
.ui-act__fee-badge {
position: absolute;
top: var(--spacing-2);
right: var(--spacing-2);
padding: 6rpx 14rpx;
border-radius: var(--radius-full);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
color: #FFFFFF;
}
.ui-act__fee-badge--free {
background: var(--color-success);
}
.ui-act__fee-badge--paid {
background: var(--color-text-tertiary);
}
.ui-act__body {
padding: var(--spacing-3) var(--spacing-4);
}
.ui-act__title {
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
color: var(--color-text-primary);
line-height: var(--line-height-tight);
margin-bottom: var(--spacing-1);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.ui-act__meta {
font-size: var(--font-size-sm);
color: var(--color-text-tertiary);
line-height: var(--line-height-normal);
margin-top: 4rpx;
}
.ui-act__organizer {
color: var(--color-text-secondary);
}
.ui-act__intro {
margin-top: var(--spacing-2);
font-size: var(--font-size-md);
color: var(--color-text-secondary);
line-height: var(--line-height-relaxed);
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.ui-act__footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: var(--spacing-3);
}
.ui-act__capacity {
font-size: var(--font-size-xs);
color: var(--color-text-tertiary);
}
.ui-act--compact {
display: flex;
height: 200rpx;
}
.ui-act--compact .ui-act__cover {
width: 200rpx;
height: 200rpx;
flex-shrink: 0;
}
.ui-act--compact .ui-act__body {
flex: 1;
min-width: 0;
padding: var(--spacing-2) var(--spacing-3);
}
.ui-act--compact .ui-act__intro {
display: none;
}
.ui-act{overflow:hidden;border-radius:28rpx;background:#fff;box-shadow:0 10rpx 32rpx rgba(34,35,91,.08)}.ui-act__cover{position:relative;width:100%;height:240rpx;background:#F0F1F6}.ui-act__cover-img{display:block;width:100%;height:100%}.ui-act__fee-badge{position:absolute;top:18rpx;right:18rpx;padding:7rpx 15rpx;border-radius:22rpx;background:linear-gradient(135deg,var(--mini-accent-coral),var(--mini-accent-pink));color:#fff;font-size:18rpx;font-weight:600}.ui-act__body{padding:22rpx 24rpx}.ui-act__title{overflow:hidden;color:#222746;font-size:28rpx;font-weight:700;line-height:1.35;white-space:nowrap;text-overflow:ellipsis}.ui-act__meta{margin-top:7rpx;color:#767C94;font-size:20rpx;line-height:1.45}.ui-act__intro{display:-webkit-box;overflow:hidden;margin-top:12rpx;color:#626981;font-size:21rpx;line-height:1.55;-webkit-box-orient:vertical;-webkit-line-clamp:2}.ui-act__footer{display:flex;align-items:center;justify-content:space-between;margin-top:18rpx}.ui-act__capacity{color:#858A9E;font-size:19rpx}.ui-act__status-action{min-width:126rpx;height:56rpx;display:flex;align-items:center;justify-content:center;border-radius:28rpx;background:linear-gradient(135deg,var(--mini-accent-coral),var(--mini-accent-pink));color:#fff;font-size:21rpx;font-weight:650}.ui-act__status-action.disabled{background:#EFF0F5;color:#8E93A6}.ui-act--compact{display:flex;height:200rpx}.ui-act--compact .ui-act__cover{width:200rpx;height:200rpx;flex-shrink:0}.ui-act--compact .ui-act__body{flex:1;min-width:0;padding:16rpx 18rpx}.ui-act--compact .ui-act__intro{display:none}
+1 -1
View File
@@ -11,7 +11,7 @@
/>
<view wx:else class="ui-avatar__fallback">
<text wx:if="{{defaultText}}" class="ui-avatar__fallback-text">{{defaultText}}</text>
<view wx:else class="ui-avatar__fallback-icon"></view>
<image wx:else class="ui-avatar__fallback-icon" src="/assets/brand/default-avatar.png" mode="aspectFill" />
</view>
<view wx:if="{{showBadge && (verified || reviewed)}}" class="ui-avatar__badges">
<view wx:if="{{verified}}" class="ui-avatar__badge ui-avatar__badge--verified">实名</view>
+2 -5
View File
@@ -32,11 +32,8 @@
font-weight: var(--font-weight-semibold);
}
.ui-avatar__fallback-icon {
width: 50%;
height: 50%;
border-radius: 50%;
background: var(--color-primary);
opacity: 0.6;
width: 100%;
height: 100%;
}
.ui-avatar__badges {
position: absolute;
+24
View File
@@ -18,6 +18,13 @@ Page({
loadingMore: false,
hasMore: true,
firstLoaded: false,
activeCategory: 'all',
categories: [
{ code: 'all', label: '全部' },
{ code: 'matchmaking', label: '公益联谊' },
{ code: 'classroom', label: '婚恋课堂' },
{ code: 'anti_fraud', label: '反诈宣传' },
],
/** @type {{title:string, description:string, illustration:string, actions:Array}} */
emptyConfig: {
title: '暂无进行中的活动',
@@ -56,6 +63,7 @@ Page({
this.setData({ loading: true });
try {
await activityStore.fetchActivities();
this.applyActivityFilter();
this.setData({
loading: false,
firstLoaded: true,
@@ -73,6 +81,7 @@ Page({
this.setData({ loadingMore: true });
try {
await activityStore.loadMoreActivities();
this.applyActivityFilter();
this.setData({
loadingMore: false,
hasMore: activityStore.state.activitiesHasMore,
@@ -93,6 +102,21 @@ Page({
this.loadMore();
},
onCategoryTap(e) {
this.setData({ activeCategory: e.currentTarget.dataset.code });
this.applyActivityFilter();
},
applyActivityFilter() {
const source = activityStore.state.activities || [];
const { activeCategory } = this.data;
this.setData({
activities: activeCategory === 'all'
? source
: source.filter((item) => (item.activityType || item.category) === activeCategory),
});
},
/** 点击活动卡片 → 跳转详情 */
onActivityTap(e) {
const activityId = e.detail.activityId;
+1
View File
@@ -1,5 +1,6 @@
{
"navigationBarTitleText": "公益活动",
"navigationStyle": "custom",
"enablePullDownRefresh": true,
"usingComponents": {
"ui-section-header": "/components/section-header/index",
+23 -31
View File
@@ -1,43 +1,35 @@
<!-- 公益活动列表页 U31 -->
<view class="act-page reference-page">
<!-- 顶部区:标题 + 我的活动入口 -->
<view class="act-page__topbar">
<ui-section-header
title="公益活动"
subtitle="丹阳本地公益组织主办"
action="我的活动"
bind:action="onMyActivities"
/>
<!-- 公益活动列表页:仅展示与报名,不在列表创建 Match 或聊天 -->
<view class="act-page app-page reference-page">
<view class="activity-hero">
<view class="activity-nav">
<text class="activity-title">公益活动</text>
<view class="activity-calendar" bindtap="onMyActivities">
<image src="/assets/icons/home/activity.png" mode="aspectFit" />
</view>
</view>
<scroll-view scroll-x class="activity-categories">
<view class="activity-category-row">
<view wx:for="{{categories}}" wx:key="code" class="activity-category {{activeCategory === item.code ? 'active' : ''}}" data-code="{{item.code}}" bindtap="onCategoryTap">{{item.label}}</view>
</view>
</scroll-view>
</view>
<view class="activity-heading">
<view><text class="heading-title">丹阳公益活动</text><text class="heading-subtitle">丰富活动,连接更多可能</text></view>
<text class="my-activity-link" bindtap="onMyActivities">我的活动</text>
</view>
<!-- 首次加载 -->
<view wx:if="{{loading && !firstLoaded}}" class="act-loading">
<ui-loading mode="skeleton" rows="3" />
</view>
<!-- 列表 -->
<view wx:elif="{{activities.length}}" class="act-list">
<ui-activity-card
wx:for="{{activities}}"
wx:key="activityId"
activity="{{item}}"
bind:tap="onActivityTap"
/>
<!-- 加载更多 -->
<view wx:if="{{loadingMore}}" class="act-list__more">
<ui-loading mode="spinner" inline text="加载更多活动…" />
</view>
<view wx:elif="{{!hasMore}}" class="act-list__end">已到底部</view>
<ui-activity-card wx:for="{{activities}}" wx:key="activityId" activity="{{item}}" bind:tap="onActivityTap" />
<view wx:if="{{loadingMore}}" class="act-list__more"><ui-loading mode="spinner" inline text="加载更多活动" /></view>
<view wx:elif="{{!hasMore}}" class="act-list__end">更多公益活动正在筹备</view>
</view>
<!-- 空状态 -->
<view wx:else class="act-empty">
<ui-empty
title="{{emptyConfig.title}}"
description="{{emptyConfig.description}}"
illustration="{{emptyConfig.illustration}}"
actions="{{emptyConfig.actions}}"
bind:action="onEmptyAction"
/>
<ui-empty title="{{emptyConfig.title}}" description="{{emptyConfig.description}}" illustration="{{emptyConfig.illustration}}" actions="{{emptyConfig.actions}}" bind:action="onEmptyAction" />
</view>
</view>
+1 -44
View File
@@ -1,44 +1 @@
/**
* 公益活动列表页 U31
*/
.act-page {
min-height: 100vh;
background: var(--color-bg-page);
padding-bottom: var(--spacing-section);
}
.act-page__topbar {
padding: var(--spacing-3) var(--spacing-page-x) var(--spacing-1);
}
/* ============ 加载骨架 ============ */
.act-loading {
padding: var(--spacing-3) var(--spacing-page-x);
}
/* ============ 列表 ============ */
.act-list {
padding: var(--spacing-2) var(--spacing-page-x) 0;
display: flex;
flex-direction: column;
gap: var(--spacing-3);
}
.act-list__more {
padding: var(--spacing-3) 0;
display: flex;
justify-content: center;
}
.act-list__end {
padding: var(--spacing-3) 0;
text-align: center;
font-size: var(--font-size-xs);
color: var(--color-text-placeholder);
}
/* ============ 空状态 ============ */
.act-empty {
padding-top: 160rpx;
}
.act-page{min-height:100vh;padding-bottom:40rpx;background:#F4F5FA}.activity-hero{padding:env(safe-area-inset-top) 24rpx 26rpx;border-radius:0 0 36rpx 36rpx;background:linear-gradient(160deg,var(--mini-night-start),var(--mini-night-end));box-shadow:var(--mini-shadow)}.activity-nav{position:relative;display:flex;align-items:center;justify-content:center;height:88rpx}.activity-title{color:#fff;font-size:31rpx;font-weight:700}.activity-calendar{position:absolute;right:0;display:flex;align-items:center;justify-content:center;width:62rpx;height:62rpx;border:1rpx solid rgba(255,255,255,.18);border-radius:31rpx;background:rgba(255,255,255,.1)}.activity-calendar image{width:34rpx;height:34rpx;filter:brightness(0) invert(1)}.activity-categories{white-space:nowrap}.activity-category-row{display:inline-flex;gap:12rpx}.activity-category{padding:11rpx 22rpx;border-radius:25rpx;background:rgba(255,255,255,.1);color:rgba(255,255,255,.72);font-size:20rpx}.activity-category.active{background:#fff;color:var(--mini-accent-pink);font-weight:650}.activity-heading{display:flex;align-items:flex-end;justify-content:space-between;padding:32rpx 28rpx 16rpx}.activity-heading>view{display:flex;flex-direction:column}.heading-title{color:#252A49;font-size:30rpx;font-weight:700}.heading-subtitle{margin-top:5rpx;color:#8A8FA4;font-size:20rpx}.my-activity-link{color:var(--mini-accent-pink);font-size:21rpx}.act-loading{padding:12rpx 24rpx}.act-list{display:flex;flex-direction:column;gap:20rpx;padding:8rpx 24rpx 0}.act-list__more{display:flex;justify-content:center;padding:24rpx 0}.act-list__end{text-align:center;padding:24rpx 0;color:#9A9EB0;font-size:20rpx}.act-empty{padding-top:70rpx}
+10 -2
View File
@@ -32,11 +32,12 @@ Page({
canPause: false,
canResume: false,
likersCount: 0,
matchCount: 0,
showLogoutDialog: false,
logoutAction: DANGER_ACTION.LOGOUT,
loginActions: [{ text: '微信登录', event: 'login', variant: 'primary' }],
hasOperatorToken: false,
profileCompleteness: 78,
profileCompleteness: 0,
},
onLoad() {
@@ -44,7 +45,12 @@ Page({
this._unsubUser = userStore.subscribe((state) => this._syncFromUserStore(state));
this._syncFromUserStore(userStore.state);
// 绑定 matchStore.likersCount(驱动"喜欢我"徽标)
this._unsubMatch = matchStore.bindToPage(this, { likersCount: 'likersCount' });
this._unsubMatch = matchStore.subscribe((state) => {
this.setData({
likersCount: state.likersCount || 0,
matchCount: Array.isArray(state.mutualList) ? state.mutualList.length : 0,
});
});
},
onShow() {
@@ -54,6 +60,7 @@ Page({
userStore.fetchProfile().catch(() => {});
userStore.fetchVerificationStatus().catch(() => {});
matchStore.fetchLikersCount().catch(() => {});
matchStore.fetchMutualList().catch(() => {});
}
},
@@ -94,6 +101,7 @@ Page({
relationshipText,
canPause,
canResume,
profileCompleteness: state.profile?.profileCompletion || 0,
});
},
+1
View File
@@ -1,5 +1,6 @@
{
"navigationBarTitleText": "我的",
"navigationStyle": "custom",
"usingComponents": {
"ui-section-header": "/components/section-header/index",
"ui-avatar": "/components/avatar/index",
+52 -14
View File
@@ -1,22 +1,60 @@
<view class="page reference-page">
<ui-empty wx:if="{{!isLoggedIn}}" illustration="default" title="登录后查看我的服务" description="管理资料、缘分与公益服务记录" actions="{{loginActions}}" bind:action="onLoginAction"/>
<ui-loading wx:elif="{{loadStatus === 'loading' && !profile}}" mode="skeleton" rows="6"/>
<view class="page app-page reference-page">
<view wx:if="{{!isLoggedIn}}" class="mine-guest">
<view class="guest-brand"><image src="/assets/brand/logo-mark.png" mode="aspectFit" /><text>善缘丹阳</text><text class="guest-subtitle">公益婚恋服务平台</text></view>
<ui-empty illustration="default" title="登录后查看我的服务" description="管理资料、缘分与公益服务记录" actions="{{loginActions}}" bind:action="onLoginAction" />
</view>
<ui-loading wx:elif="{{loadStatus === 'loading' && !profile}}" mode="skeleton" rows="6" />
<block wx:else>
<view class="profile-card"><ui-avatar src="{{profile.avatarUrl || ''}}" size="xl" default-text="{{profile.displayName || ''}}" verified="{{isRealNameVerified}}" reviewed="{{isProfileApproved}}"/><view class="profile-info"><view class="profile-name">{{profile.displayName || '未填写昵称'}}</view><view class="profile-complete">资料完整度 {{profileCompleteness}}%</view><view class="profile-progress"><view style="width:{{profileCompleteness}}%"></view></view><view class="profile-badges"><ui-status-badge kind="{{isRealNameVerified ? 'success' : 'warning'}}" label="{{isRealNameVerified ? '已实名' : '未实名'}}"/><ui-status-badge kind="success" label="本地资格"/><ui-status-badge kind="{{isProfileApproved ? 'success' : 'warning'}}" label="{{isProfileApproved ? '资料已审核' : '资料待审核'}}"/></view></view></view>
<view class="profile-hero">
<view class="mine-nav"><text>我的</text><image src="/assets/icons/home/service-station.png" mode="aspectFit" bindtap="onGoSettings" /></view>
<view class="profile-main">
<ui-avatar src="{{profile.avatarUrl || ''}}" size="xl" default-text="{{profile.displayName || '善缘用户'}}" verified="{{isRealNameVerified}}" reviewed="{{isProfileApproved}}" />
<view class="profile-copy">
<view class="profile-name-row"><text class="profile-name">{{profile.displayName || '未填写昵称'}}</text><text class="profile-status">{{relationshipText}}</text></view>
<text class="profile-meta">资料完整度 {{profileCompleteness}}%</text>
<view class="profile-progress"><view style="width:{{profileCompleteness}}%"></view></view>
<view class="profile-badges"><ui-status-badge kind="{{isRealNameVerified ? 'success' : 'warning'}}" label="{{isRealNameVerified ? '已实名' : '未实名'}}" /><ui-status-badge kind="{{isProfileApproved ? 'success' : 'warning'}}" label="{{isProfileApproved ? '资料已审核' : '资料待审核'}}" /></view>
</view>
<text class="edit-profile" bindtap="onEditProfile">编辑资料</text>
</view>
</view>
<ui-section-header title="我的" subtitle="资料与缘分"/>
<view class="list-group"><view class="list-row" bindtap="onEditProfile"><text class="list-label">我的资料</text><text class="list-value">{{reviewStatusText}}</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onEditPreference"><text class="list-label">择偶条件</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onGoFate"><text class="list-label">我的缘分</text><text class="list-arrow">›</text></view></view>
<view class="profile-stats">
<view class="profile-stat"><text class="stat-value">{{profileCompleteness}}%</text><text class="stat-label">资料完整</text></view>
<view class="profile-stat" bindtap="onGoLikers"><text class="stat-value">{{likersCount}}</text><text class="stat-label">喜欢我的</text></view>
<view class="profile-stat" bindtap="onGoFate"><text class="stat-value">{{matchCount}}</text><text class="stat-label">当前缘分</text></view>
<view class="profile-stat"><text class="stat-value stat-value--text">{{reviewStatusText}}</text><text class="stat-label">资料状态</text></view>
</view>
<ui-section-header title="公益服务"/>
<view class="list-group"><view class="list-row" bindtap="onGoMatchmaker"><text class="list-label">我的红娘</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onGoCounseling"><text class="list-label">我的咨询</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onGoActivities"><text class="list-label">我的活动</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onGoAppointments"><text class="list-label">我的预约</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onGoServiceHistory"><text class="list-label">公益服务记录</text><text class="list-arrow">›</text></view></view>
<view class="public-identity-card" bindtap="onEditProfile">
<image src="/assets/brand/logo-mark.png" mode="aspectFit" />
<view><text class="identity-title">我在善缘丹阳</text><text class="identity-desc">用真诚遇见美好,让爱在丹阳绽放</text></view>
<image class="identity-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" />
</view>
<ui-section-header title="安全与隐私"/>
<view class="list-group"><view class="list-row" bindtap="onGoPrivacy"><text class="list-label">隐私设置</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onGoBlacklist"><text class="list-label">黑名单</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onGoSafety"><text class="list-label">举报与安全</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onGoSettings"><text class="list-label">账号设置</text><text class="list-arrow">›</text></view></view>
<ui-section-header title="我的公益服务" subtitle="服务进度与预约记录" />
<view class="service-grid">
<view class="service-entry" bindtap="onGoMatchmaker"><view class="service-icon rose"><image src="/assets/icons/home/matchmaker.png" mode="aspectFit" /></view><text>我的红娘</text></view>
<view class="service-entry" bindtap="onGoCounseling"><view class="service-icon blue"><image src="/assets/icons/home/counseling.png" mode="aspectFit" /></view><text>我的咨询</text></view>
<view class="service-entry" bindtap="onGoActivities"><view class="service-icon amber"><image src="/assets/icons/home/activity.png" mode="aspectFit" /></view><text>我的活动</text></view>
<view class="service-entry" bindtap="onGoAppointments"><view class="service-icon green"><image src="/assets/icons/home/service-station.png" mode="aspectFit" /></view><text>我的预约</text></view>
</view>
<ui-section-header title="公益信息"/>
<view class="list-group"><view class="list-row" bindtap="onGoClassroom"><text class="list-label">婚恋课堂</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onGoAntiFraud"><text class="list-label">婚恋反诈</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onGoStation"><text class="list-label">善缘服务站</text><text class="list-arrow">›</text></view><view class="list-row" bindtap="onGoAbout"><text class="list-label">关于善缘丹阳</text><text class="list-arrow">›</text></view></view>
<view class="list-group">
<view class="list-row" bindtap="onEditProfile"><text class="list-label">我的资料</text><text class="list-value">{{reviewStatusText}}</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onEditPreference"><text class="list-label">择偶条件</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoFate"><text class="list-label">我的缘分</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoServiceHistory"><text class="list-label">公益服务记录</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoSettings"><text class="list-label">账号设置</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
</view>
<view class="list-group"><view class="list-row danger" bindtap="onLogout"><text class="list-label">退出登录</text></view></view>
<ui-danger-action-dialog visible="{{showLogoutDialog}}" action="{{logoutAction}}" bind:confirm="onConfirmLogout" bind:cancel="onCancelLogout"/>
<view class="list-group secondary-list">
<view class="list-row" bindtap="onGoPrivacy"><text class="list-label">隐私设置</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoSafety"><text class="list-label">举报与安全</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoAntiFraud"><text class="list-label">婚恋反诈</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
</view>
<view class="logout-row" bindtap="onLogout">退出登录</view>
<ui-danger-action-dialog visible="{{showLogoutDialog}}" action="{{logoutAction}}" bind:confirm="onConfirmLogout" bind:cancel="onCancelLogout" />
</block>
</view>
+1 -101
View File
@@ -1,101 +1 @@
/**
* 我的页样式(Design Token,中低密度留白)
*/
.page {
min-height: 100vh;
background: var(--color-bg-page);
padding-bottom: var(--spacing-8);
}
/* ============ 个人资料卡 ============ */
.profile-card {
display: flex;
align-items: center;
gap: var(--spacing-4);
background: var(--color-bg-card);
padding: var(--spacing-5) var(--spacing-page-x);
border-bottom: var(--border-width) solid var(--color-divider);
}
.profile-info {
flex: 1;
min-width: 0;
}
.profile-name {
font-size: var(--font-size-xl);
font-weight: var(--font-weight-semibold);
color: var(--color-text-primary);
margin-bottom: var(--spacing-1);
}
.profile-meta {
font-size: var(--font-size-sm);
color: var(--color-text-tertiary);
margin-bottom: var(--spacing-2);
}
.profile-badges {
display: flex;
gap: var(--spacing-2);
flex-wrap: wrap;
}
/* ============ 操作行 ============ */
.action-row {
background: var(--color-bg-card);
padding: var(--spacing-3) var(--spacing-page-x);
border-bottom: var(--border-width) solid var(--color-divider);
}
.action-hint {
text-align: center;
color: var(--color-text-tertiary);
font-size: var(--font-size-sm);
padding: var(--spacing-2) 0;
}
/* ============ 列表组 ============ */
.list-group {
background: var(--color-bg-card);
margin-bottom: var(--spacing-section);
}
.list-row {
display: flex;
align-items: center;
padding: var(--spacing-4) var(--spacing-page-x);
border-bottom: var(--border-width) solid var(--color-divider);
background: var(--color-bg-card);
}
.list-row:last-child {
border-bottom: none;
}
.list-label {
flex: 1;
font-size: var(--font-size-base);
color: var(--color-text-primary);
}
.list-value {
font-size: var(--font-size-sm);
color: var(--color-text-tertiary);
margin-right: var(--spacing-2);
}
.list-arrow {
font-size: var(--font-size-lg);
color: var(--color-text-disabled);
line-height: 1;
}
.list-row.danger .list-label {
color: var(--color-danger);
text-align: center;
flex: none;
width: 100%;
}
.profile-complete{font-size:22rpx;color:var(--color-text-tertiary);margin-top:4rpx}.profile-progress{height:8rpx;margin:10rpx 0 14rpx;border-radius:999rpx;background:var(--color-divider);overflow:hidden}.profile-progress view{height:100%;background:var(--color-primary);border-radius:999rpx}.list-row{min-height:88rpx;box-sizing:border-box}.page ui-section-header{display:block;padding:0 var(--spacing-page-x)}
.page{min-height:100vh;padding-bottom:48rpx;background:#F4F5FA}.mine-guest{min-height:100vh;background:linear-gradient(160deg,var(--mini-night-start) 0,var(--mini-night-end) 34%,#F4F5FA 34%)}.guest-brand{display:flex;flex-direction:column;align-items:center;padding:calc(env(safe-area-inset-top) + 70rpx) 0 80rpx;color:#fff}.guest-brand image{width:92rpx;height:92rpx}.guest-brand>text{margin-top:12rpx;font-size:34rpx;font-weight:700}.guest-brand .guest-subtitle{margin-top:5rpx;color:rgba(255,255,255,.72);font-size:20rpx;font-weight:400;letter-spacing:4rpx}.profile-hero{padding:env(safe-area-inset-top) 24rpx 54rpx;border-radius:0 0 42rpx 42rpx;background:linear-gradient(160deg,var(--mini-night-start),var(--mini-night-end));box-shadow:var(--mini-shadow)}.mine-nav{position:relative;display:flex;align-items:center;justify-content:center;height:88rpx;color:#fff;font-size:31rpx;font-weight:700}.mine-nav image{position:absolute;right:12rpx;width:36rpx;height:36rpx;filter:brightness(0) invert(1)}.profile-main{position:relative;display:flex;align-items:center}.profile-copy{flex:1;min-width:0;margin-left:20rpx;color:#fff}.profile-name-row{display:flex;align-items:center;gap:12rpx}.profile-name{font-size:31rpx;font-weight:700}.profile-status{padding:5rpx 10rpx;border-radius:15rpx;background:rgba(255,255,255,.12);color:rgba(255,255,255,.78);font-size:17rpx}.profile-meta{display:block;margin-top:8rpx;color:rgba(255,255,255,.72);font-size:20rpx}.profile-progress{width:78%;height:7rpx;margin:10rpx 0 13rpx;overflow:hidden;border-radius:8rpx;background:rgba(255,255,255,.14)}.profile-progress view{height:100%;border-radius:8rpx;background:linear-gradient(90deg,#FF8C9C,#FF5C82)}.profile-badges{display:flex;gap:8rpx}.edit-profile{position:absolute;right:0;top:4rpx;color:rgba(255,255,255,.82);font-size:19rpx}.profile-stats{position:relative;z-index:2;display:grid;grid-template-columns:repeat(4,1fr);margin:-24rpx 24rpx 0;padding:24rpx 8rpx;border-radius:28rpx;background:#fff;box-shadow:var(--mini-shadow)}.profile-stat{display:flex;flex-direction:column;align-items:center;border-right:1rpx solid #EFF0F5}.profile-stat:last-child{border-right:0}.stat-value{color:#283056;font-size:29rpx;font-weight:700}.stat-value--text{font-size:22rpx}.stat-label{margin-top:5rpx;color:#8A8FA4;font-size:18rpx}.public-identity-card{display:flex;align-items:center;margin:24rpx;padding:22rpx;border-radius:26rpx;background:linear-gradient(135deg,#FFF0F5,#F7EEFF);box-shadow:0 8rpx 26rpx rgba(87,62,145,.08)}.public-identity-card>image:first-child{width:72rpx;height:72rpx}.public-identity-card>view{flex:1;display:flex;flex-direction:column;margin-left:16rpx}.identity-title{color:#F14D79;font-size:26rpx;font-weight:700}.identity-desc{margin-top:5rpx;color:#8A6F91;font-size:19rpx}.public-identity-card .identity-arrow{width:26rpx;height:26rpx}.service-grid{display:grid;grid-template-columns:repeat(4,1fr);margin:0 24rpx 28rpx;padding:24rpx 8rpx;border-radius:26rpx;background:#fff;box-shadow:0 8rpx 28rpx rgba(35,35,95,.07)}.service-entry{display:flex;flex-direction:column;align-items:center;color:#343957;font-size:20rpx}.service-icon{display:flex;align-items:center;justify-content:center;width:64rpx;height:64rpx;margin-bottom:10rpx;border-radius:20rpx}.service-icon image{width:42rpx;height:42rpx}.service-icon.rose{background:#FFF0F5}.service-icon.blue{background:#EAF3FF}.service-icon.amber{background:#FFF3E5}.service-icon.green{background:#EAF8F2}.list-group{overflow:hidden;margin:0 24rpx 22rpx;border-radius:26rpx;background:#fff;box-shadow:0 8rpx 28rpx rgba(35,35,95,.06)}.list-row{display:flex;align-items:center;min-height:88rpx;padding:0 24rpx;border-bottom:1rpx solid #EFF0F5}.list-row:last-child{border-bottom:0}.list-label{flex:1;color:#2B304E;font-size:23rpx}.list-value{margin-right:12rpx;color:#8A8FA4;font-size:19rpx}.list-arrow{width:24rpx;height:24rpx}.secondary-list{margin-top:0}.logout-row{margin:26rpx 24rpx 0;padding:24rpx;text-align:center;color:#D95561;font-size:23rpx;background:#fff;border-radius:26rpx}
+17
View File
@@ -67,6 +67,23 @@ assert.match(messagesTemplate, /ui-conv__badge|unreadTotal/, '消息页缺少未
assert.match(messagesTemplate, /互相喜欢后/, '消息页缺少无 Mutual 状态说明')
assert.match(messagesLogic, /canSendMessage/, '消息页进入聊天前必须校验服务端会话能力')
const activityTemplate = read('miniapp/pages/activity/activity.wxml')
const activityCard = read('miniapp/components/activity-card/index.wxml')
const activityLogic = read('miniapp/pages/activity/activity.js')
assert.match(activityTemplate, /activity-categories/, '活动页缺少活动分类')
assert.match(activityTemplate, /activity-calendar/, '活动页缺少日历入口')
assert.match(activityCard, /ui-act__capacity/, '活动卡缺少人数和容量')
assert.match(activityCard, /ui-act__status-action/, '活动卡缺少状态按钮')
assert.doesNotMatch(activityLogic, /matchStore|to\.chat/, '活动列表不得直接创建 Match 或进入聊天')
const mineTemplate = read('miniapp/pages/mine/mine.wxml')
assert.match(mineTemplate, /profile-hero/, '我的页缺少个人头部')
assert.match(mineTemplate, /profile-stats/, '我的页缺少四项统计')
assert.match(mineTemplate, /我在善缘丹阳/, '我的页缺少公益身份卡')
for (const label of ['我的红娘', '我的咨询', '我的活动', '我的预约', '账号设置']) {
assert.ok(mineTemplate.includes(label), `我的页缺少入口 ${label}`)
}
const theme = read('miniapp/app.wxss')
for (const token of [
'--mini-night-start',