chore: establish V1.1 recovery baseline
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* 婚恋状态页 U25(重构:用 userStore + matchStore + DangerActionDialog)
|
||||
*
|
||||
* 状态:searching / paused / getting_to_know / matched_successfully / service_exited
|
||||
* 操作:暂停寻找 / 恢复寻找 / 设置正在了解 / 解除匹配
|
||||
*
|
||||
* 不变量(AGENTS.md):
|
||||
* - 第 6 条:成功牵手必须双确认——matched_successfully 不能通过本页直接设置,
|
||||
* 仅由 /matches/{id}/success/request + confirm 双确认流程触发;本页只做状态展示与暂停/恢复/解除。
|
||||
* - 第 4 条:解除匹配后双方会话立即停用——unmatch 走 matchStore.unmatchConversation 同步 MessageStore。
|
||||
* - 第 9 条:PostgreSQL 是 Source of Truth——状态以 userStore.fetchRelationshipStatus 服务端返回为准。
|
||||
*/
|
||||
const { userStore, appStore, unmatchConversation } = require('../../stores');
|
||||
const { RELATIONSHIP_STATUS, DANGER_ACTION } = require('../../constants');
|
||||
|
||||
const STATUS_META = {
|
||||
[RELATIONSHIP_STATUS.SEARCHING]: {
|
||||
label: '正在寻找',
|
||||
desc: '你正在被推荐给合适的对象,也会收到新的喜欢提示。',
|
||||
kind: 'success',
|
||||
},
|
||||
[RELATIONSHIP_STATUS.PAUSED]: {
|
||||
label: '已暂停寻找',
|
||||
desc: '你暂时不会被推荐给他人,也无法收到新的喜欢提示。已有聊天仍可继续使用。',
|
||||
kind: 'warning',
|
||||
},
|
||||
[RELATIONSHIP_STATUS.GETTING_TO_KNOW]: {
|
||||
label: '正在了解',
|
||||
desc: '你正在与某位对象互相了解中,平台会适当减少新推荐,让你专注这段关系。',
|
||||
kind: 'info',
|
||||
},
|
||||
[RELATIONSHIP_STATUS.MATCHED_SUCCESSFULLY]: {
|
||||
label: '已成功牵手',
|
||||
desc: '恭喜!你与对方已成功牵手,平台将不再为你推荐其他人。如有需要可联系公益红娘。',
|
||||
kind: 'success',
|
||||
},
|
||||
[RELATIONSHIP_STATUS.SERVICE_EXITED]: {
|
||||
label: '已退出婚恋服务',
|
||||
desc: '你已退出婚恋服务,不再出现在推荐和搜索结果中。历史匹配与聊天记录已保留。',
|
||||
kind: 'disabled',
|
||||
},
|
||||
};
|
||||
|
||||
Page({
|
||||
data: {
|
||||
status: RELATIONSHIP_STATUS.SEARCHING,
|
||||
statusLabel: '',
|
||||
statusDesc: '',
|
||||
statusKind: 'success',
|
||||
matchingWith: null,
|
||||
recommendationEnabled: true,
|
||||
loading: false,
|
||||
actionLoading: false,
|
||||
|
||||
// DangerActionDialog
|
||||
showDangerDialog: false,
|
||||
dangerAction: DANGER_ACTION.PAUSE,
|
||||
dangerTargetName: '',
|
||||
|
||||
statusBarHeight: 20,
|
||||
navBarHeight: 44,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
try {
|
||||
const info = (wx.getWindowInfo && wx.getWindowInfo()) || wx.getSystemInfoSync();
|
||||
this.setData({
|
||||
statusBarHeight: info.statusBarHeight || 20,
|
||||
navBarHeight: info.platform === 'android' ? 48 : 44,
|
||||
});
|
||||
} catch (e) { /* 使用默认值 */ }
|
||||
|
||||
this._unsub = userStore.bindToPage(this, {
|
||||
userState: 'userState',
|
||||
});
|
||||
this.loadStatus();
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
if (this._unsub) this._unsub();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.loadStatus();
|
||||
},
|
||||
|
||||
async loadStatus() {
|
||||
if (this.data.loading) return;
|
||||
this.setData({ loading: true });
|
||||
try {
|
||||
const result = await userStore.fetchRelationshipStatus();
|
||||
this._applyStatus(result.status, result.matchingWith, result.recommendationEnabled);
|
||||
} catch (err) {
|
||||
// 兜底:用 userState 缓存渲染
|
||||
const us = userStore.state.userState || {};
|
||||
this._applyStatus(us.relationshipStatus || RELATIONSHIP_STATUS.SEARCHING, us.matchingWith || null, us.recommendationEnabled ?? true);
|
||||
appStore.toastError(err);
|
||||
} finally {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
_applyStatus(status, matchingWith, recommendationEnabled) {
|
||||
const meta = STATUS_META[status] || STATUS_META[RELATIONSHIP_STATUS.SEARCHING];
|
||||
this.setData({
|
||||
status,
|
||||
statusLabel: meta.label,
|
||||
statusDesc: meta.desc,
|
||||
statusKind: meta.kind,
|
||||
matchingWith: matchingWith || null,
|
||||
recommendationEnabled: recommendationEnabled ?? true,
|
||||
});
|
||||
},
|
||||
|
||||
// ============ 操作(先弹 DangerActionDialog 二次确认) ============
|
||||
|
||||
onPause() {
|
||||
this.setData({
|
||||
showDangerDialog: true,
|
||||
dangerAction: DANGER_ACTION.PAUSE,
|
||||
dangerTargetName: '',
|
||||
});
|
||||
},
|
||||
|
||||
onResume() {
|
||||
// 恢复寻找为正向操作,直接执行
|
||||
this._doAction(async () => {
|
||||
await userStore.resumeSearching();
|
||||
appStore.toastSuccess('已恢复寻找');
|
||||
this.loadStatus();
|
||||
});
|
||||
},
|
||||
|
||||
onSetGettingToKnow() {
|
||||
this.setData({
|
||||
showDangerDialog: true,
|
||||
dangerAction: DANGER_ACTION.PAUSE, // 复用"暂停类"确认样式(非 danger)
|
||||
dangerTargetName: '',
|
||||
});
|
||||
this._pendingGtk = true;
|
||||
},
|
||||
|
||||
onUnmatch() {
|
||||
const m = this.data.matchingWith;
|
||||
if (!m || !m.matchId) return;
|
||||
this.setData({
|
||||
showDangerDialog: true,
|
||||
dangerAction: DANGER_ACTION.UNMATCH,
|
||||
dangerTargetName: m.displayName || '对方',
|
||||
});
|
||||
},
|
||||
|
||||
/** DangerActionDialog 确认回调 */
|
||||
async onDangerConfirm() {
|
||||
this.setData({ showDangerDialog: false });
|
||||
const action = this.data.dangerAction;
|
||||
if (this._pendingGtk) {
|
||||
this._pendingGtk = false;
|
||||
this._doAction(async () => {
|
||||
await userStore.markGettingToKnow();
|
||||
appStore.toastSuccess('已设置为正在了解');
|
||||
this.loadStatus();
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (action === DANGER_ACTION.PAUSE) {
|
||||
this._doAction(async () => {
|
||||
await userStore.pauseSearching();
|
||||
appStore.toastSuccess('已暂停寻找');
|
||||
this.loadStatus();
|
||||
});
|
||||
} else if (action === DANGER_ACTION.UNMATCH) {
|
||||
const m = this.data.matchingWith;
|
||||
if (!m || !m.matchId) return;
|
||||
this._doAction(async () => {
|
||||
await unmatchConversation(m.matchId);
|
||||
this.setData({ matchingWith: null });
|
||||
appStore.toastSuccess('已解除匹配');
|
||||
this.loadStatus();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/** DangerActionDialog 取消回调 */
|
||||
onDangerCancel() {
|
||||
this.setData({ showDangerDialog: false });
|
||||
this._pendingGtk = false;
|
||||
},
|
||||
|
||||
/** 统一执行带 loading 的动作(防重) */
|
||||
async _doAction(fn) {
|
||||
if (this.data.actionLoading) return;
|
||||
this.setData({ actionLoading: true });
|
||||
try {
|
||||
await fn();
|
||||
} catch (err) {
|
||||
appStore.toastError(err);
|
||||
} finally {
|
||||
this.setData({ actionLoading: false });
|
||||
}
|
||||
},
|
||||
|
||||
onBack() {
|
||||
wx.navigateBack();
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "婚恋状态",
|
||||
"backgroundColor": "#FFF8F7",
|
||||
"backgroundTextStyle": "dark",
|
||||
"usingComponents": {
|
||||
"ui-nav-bar": "/components/nav-bar/index",
|
||||
"ui-section-header": "/components/section-header/index",
|
||||
"ui-status-badge": "/components/status-badge/index",
|
||||
"ui-loading": "/components/loading/index",
|
||||
"ui-danger-action-dialog": "/components/danger-action-dialog/index"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<!-- 婚恋状态页 U25(重构:userStore + DangerActionDialog) -->
|
||||
<view class="page">
|
||||
<ui-nav-bar
|
||||
title="婚恋状态"
|
||||
show-back="{{true}}"
|
||||
status-bar-height="{{statusBarHeight}}"
|
||||
nav-bar-height="{{navBarHeight}}"
|
||||
bind:back="onBack"
|
||||
/>
|
||||
|
||||
<view class="body" style="padding-top: {{statusBarHeight + navBarHeight}}px;">
|
||||
<ui-loading wx:if="{{loading}}" mode="spinner" text="加载中..." />
|
||||
|
||||
<block wx:else>
|
||||
<!-- 当前状态卡片 -->
|
||||
<view class="status-card status-card--{{statusKind}}">
|
||||
<view class="status-card__head">
|
||||
<text class="status-card__label">当前状态</text>
|
||||
<ui-status-badge kind="{{statusKind}}" label="{{statusLabel}}" icon="{{true}}" />
|
||||
</view>
|
||||
<text class="status-card__desc">{{statusDesc}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 正在了解中的对象 -->
|
||||
<view wx:if="{{matchingWith}}" class="partner-card">
|
||||
<ui-section-header title="正在了解的对象" />
|
||||
<view class="partner-row">
|
||||
<view class="partner-info">
|
||||
<text class="partner-name">{{matchingWith.displayName || '对方'}}</text>
|
||||
<text class="partner-hint">你们已互相喜欢,正在了解中</text>
|
||||
</view>
|
||||
<button class="btn-danger-outline" bindtap="onUnmatch" disabled="{{actionLoading}}">解除匹配</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作区 -->
|
||||
<view class="actions">
|
||||
<ui-section-header title="状态管理" />
|
||||
|
||||
<!-- 正在寻找:可暂停 / 设为正在了解 -->
|
||||
<block wx:if="{{status === 'searching'}}">
|
||||
<button class="btn-primary" bindtap="onSetGettingToKnow" disabled="{{actionLoading}}">设置为"正在了解"</button>
|
||||
<button class="btn-outline" bindtap="onPause" disabled="{{actionLoading}}">暂停寻找</button>
|
||||
</block>
|
||||
|
||||
<!-- 已暂停:可恢复 -->
|
||||
<block wx:elif="{{status === 'paused'}}">
|
||||
<button class="btn-primary" bindtap="onResume" disabled="{{actionLoading}}">恢复寻找</button>
|
||||
</block>
|
||||
|
||||
<!-- 正在了解:可恢复寻找或解除匹配 -->
|
||||
<block wx:elif="{{status === 'getting_to_know'}}">
|
||||
<button class="btn-outline" bindtap="onResume" disabled="{{actionLoading}}">恢复寻找</button>
|
||||
</block>
|
||||
|
||||
<!-- 已成功牵手 / 已退出:无操作 -->
|
||||
<view wx:elif="{{status === 'matched_successfully' || status === 'service_exited'}}" class="actions-tip">
|
||||
<text>当前状态无需调整</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 公益免费标识 -->
|
||||
<view class="free-badge">
|
||||
<text>本平台为公益服务,所有功能永久免费</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 危险操作二次确认弹窗 -->
|
||||
<ui-danger-action-dialog
|
||||
visible="{{showDangerDialog}}"
|
||||
action="{{dangerAction}}"
|
||||
target-name="{{dangerTargetName}}"
|
||||
bind:confirm="onDangerConfirm"
|
||||
bind:cancel="onDangerCancel"
|
||||
/>
|
||||
</view>
|
||||
@@ -0,0 +1,152 @@
|
||||
/* 婚恋状态页 U25 — Design Tokens */
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #FFF8F7;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding: 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 状态卡片 */
|
||||
.status-card {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 32rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
border-left: 8rpx solid #E96B6B;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.status-card--warning { border-left-color: #F5A623; }
|
||||
.status-card--info { border-left-color: #4A90D9; }
|
||||
.status-card--disabled { border-left-color: #B0B0B0; }
|
||||
|
||||
.status-card__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.status-card__label {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.status-card__desc {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 正在了解对象卡片 */
|
||||
.partner-card {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.partner-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 16rpx;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.partner-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.partner-name {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #1A1A1A;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.partner-hint {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 操作区 */
|
||||
.actions {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx 32rpx 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.actions button {
|
||||
margin-top: 20rpx;
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
border-radius: 44rpx;
|
||||
font-size: 30rpx;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.actions button::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #E96B6B;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: #FFF3F2;
|
||||
color: #E96B6B;
|
||||
border: 2rpx solid #E96B6B;
|
||||
}
|
||||
|
||||
.btn-danger-outline {
|
||||
flex-shrink: 0;
|
||||
height: 64rpx;
|
||||
line-height: 60rpx;
|
||||
padding: 0 28rpx;
|
||||
border-radius: 32rpx;
|
||||
font-size: 24rpx;
|
||||
background: #fff;
|
||||
color: #E04C4C;
|
||||
border: 2rpx solid #E04C4C;
|
||||
}
|
||||
|
||||
.btn-danger-outline::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.actions-tip {
|
||||
text-align: center;
|
||||
padding: 40rpx 0;
|
||||
color: #B0B0B0;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
/* 公益免费标识 */
|
||||
.free-badge {
|
||||
text-align: center;
|
||||
padding: 24rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.free-badge text {
|
||||
display: inline-block;
|
||||
font-size: 22rpx;
|
||||
color: #1A6B3E;
|
||||
background: #E8F5E9;
|
||||
padding: 8rpx 24rpx;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
Reference in New Issue
Block a user