fix(miniapp): 小程序界面全量中文化,消除英文/非中文枚举码上屏
- 新增 miniapp/constants/profileLabels.js 作为「枚举码→中文简体」唯一字典 (婚姻/子女/学历/职业/吸烟饮酒/择偶子女/红娘状态),覆盖表单值与种子 实际值两套并集,保证详情页无论后端返回哪种码都显示中文 - 修复「择偶条件-子女」下拉英文选项(any/accept/reject→不限/可接受/不接受), 改用 value/label 数组 + range-key="label" - 修复 recommend-card / user-card / profile-detail / profile-confirm 直接渲染 后端枚举码(educationCode/maritalStatus/occupationCode…)的问题,改由 observers 派生中文 label 上屏 - 清理残留英文文案:Required 条件→必须符合的条件、喜欢TA/向Ta→喜欢对方/向对方、 U51 服务预约→服务预约、U64 公益服务记录→公益服务记录 - 修复 7 个英文窗口标题与 3 个英文占位页正文 - matchmaker 历史服务状态消除原始码兜底,统一走字典 - 加固 scripts/audit-miniapp-language.mjs(A 整行先剥离 mustache、B 排除 bind:action 处理器、F 窗口标题、JPG/PNG 入白名单) - 新增 ui-tests/miniapp-language-contracts.mjs 语种守卫(复用审计脚本,断言 0 命中), UI 契约门禁 MIN_GUARDS 21→22;已做负向注入验证判别力
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
* 不变量(AGENTS.md 第 2 条):用户已勾选"必须符合"的字段不可被前端自动放宽为
|
||||
* "最好符合",UI 必须显式提示用户此操作将影响匹配精度。
|
||||
*/
|
||||
const { ACCEPT_CHILDREN_OPTIONS } = require('../../constants')
|
||||
|
||||
Component({
|
||||
options: { addGlobalClass: true },
|
||||
properties: {
|
||||
@@ -19,6 +21,24 @@ Component({
|
||||
required: '必须符合',
|
||||
preferred: '最好符合',
|
||||
},
|
||||
// 子女(是否接受有孩)下拉:选项均为中文,value 为后端枚举码
|
||||
childrenOptions: ACCEPT_CHILDREN_OPTIONS,
|
||||
childrenLabelMap: {
|
||||
any: '不限',
|
||||
accept: '可接受',
|
||||
reject: '不接受',
|
||||
},
|
||||
childrenIndex: -1,
|
||||
},
|
||||
observers: {
|
||||
preference(pref) {
|
||||
if (!pref) {
|
||||
this.setData({ childrenIndex: -1 })
|
||||
return
|
||||
}
|
||||
const idx = this.data.childrenOptions.findIndex((o) => o.value === pref.acceptChildren)
|
||||
this.setData({ childrenIndex: idx })
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 切换某字段的 importance:required ↔ preferred
|
||||
@@ -54,9 +74,13 @@ Component({
|
||||
},
|
||||
onAcceptChildrenChange(e) {
|
||||
if (this.data.readonly) return;
|
||||
const idx = Number(e.detail.value)
|
||||
const opt = this.data.childrenOptions[idx]
|
||||
if (!opt) return
|
||||
this.setData({ childrenIndex: idx })
|
||||
this.triggerEvent('change', {
|
||||
field: 'acceptChildren',
|
||||
value: e.detail.value,
|
||||
value: opt.value,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
@@ -111,13 +111,13 @@
|
||||
<view class="ui-pref__label">子女</view>
|
||||
<picker
|
||||
class="ui-pref__picker"
|
||||
value="{{preference.acceptChildren}}"
|
||||
range="{{['any', 'accept', 'reject']}}"
|
||||
range-key=""
|
||||
value="{{childrenIndex < 0 ? 0 : childrenIndex}}"
|
||||
range="{{childrenOptions}}"
|
||||
range-key="label"
|
||||
bindchange="onAcceptChildrenChange"
|
||||
>
|
||||
<view class="ui-pref__picker-value">
|
||||
{{preference.acceptChildren === 'any' ? '不限' : preference.acceptChildren === 'accept' ? '可接受' : '不接受'}}
|
||||
{{childrenLabelMap[preference.acceptChildren] || '不限'}}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* 不暴露"放宽了 X 条件才得到这个推荐"的字样,前端只显示后端返回的 reasons。
|
||||
* - 第 5 条:红娘不可替用户操作——本卡片仅由当前用户操作,不接受 matchmakerId 参数。
|
||||
*/
|
||||
const { STATUS_BADGE } = require('../../constants');
|
||||
const { STATUS_BADGE, profileLabelOf } = require('../../constants');
|
||||
|
||||
Component({
|
||||
options: { addGlobalClass: true },
|
||||
@@ -29,6 +29,21 @@ Component({
|
||||
medium: 'info',
|
||||
normal: 'warning',
|
||||
},
|
||||
// 由后端枚举码派生的中文展示值(界面不得出现英文码)
|
||||
educationLabel: '',
|
||||
occupationLabel: '',
|
||||
},
|
||||
observers: {
|
||||
recommendation(rec) {
|
||||
if (!rec) {
|
||||
this.setData({ educationLabel: '', occupationLabel: '' })
|
||||
return
|
||||
}
|
||||
this.setData({
|
||||
educationLabel: profileLabelOf('educationCode', rec.educationCode),
|
||||
occupationLabel: profileLabelOf('occupationCode', rec.occupationCode),
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onViewDetail() {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<ui-avatar src="{{recommendation.avatarUrl || '/assets/brand/default-avatar.png'}}" size="lg" verified="{{recommendation.verified}}" reviewed="{{recommendation.reviewed}}" default-text="{{recommendation.displayName}}" />
|
||||
<view class="ui-rec__info">
|
||||
<view class="ui-rec__name-row"><text class="ui-rec__name">{{recommendation.displayName || '善缘用户'}}</text><text wx:if="{{recommendation.age}}" class="ui-rec__age">{{recommendation.age}}岁</text><text wx:if="{{recommendation.online}}" class="ui-rec__online">在线</text></view>
|
||||
<view class="ui-rec__meta"><text wx:if="{{recommendation.regionName}}">{{recommendation.regionName}}</text><text wx:if="{{recommendation.heightCm}}"> · {{recommendation.heightCm}}cm</text><text wx:if="{{recommendation.educationCode}}"> · {{recommendation.educationCode}}</text></view>
|
||||
<view wx:if="{{recommendation.occupationCode}}" class="ui-rec__meta">{{recommendation.occupationCode}}</view>
|
||||
<view class="ui-rec__meta"><text wx:if="{{recommendation.regionName}}">{{recommendation.regionName}}</text><text wx:if="{{recommendation.heightCm}}"> · {{recommendation.heightCm}}cm</text><text wx:if="{{educationLabel}}"> · {{educationLabel}}</text></view>
|
||||
<view wx:if="{{occupationLabel}}" class="ui-rec__meta">{{occupationLabel}}</view>
|
||||
<view wx:if="{{recommendation.recommendationReasons && recommendation.recommendationReasons.length}}" class="ui-rec__reasons"><text wx:for="{{recommendation.recommendationReasons}}" wx:key="*this" class="ui-rec__reason">{{item}}</text></view>
|
||||
</view>
|
||||
<view wx:if="{{showActions}}" class="ui-rec__actions">
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
* <view slot="actions">…自定义按钮…</view>
|
||||
* </ui-user-card>
|
||||
*/
|
||||
const { profileLabelOf } = require('../../constants')
|
||||
|
||||
Component({
|
||||
options: { addGlobalClass: true, multipleSlots: true },
|
||||
properties: {
|
||||
@@ -15,6 +17,25 @@ Component({
|
||||
selectable: { type: Boolean, value: false }, // 是否显示选择框
|
||||
selected: { type: Boolean, value: false },
|
||||
},
|
||||
data: {
|
||||
// 由后端枚举码派生的中文展示值(界面不得出现英文码)
|
||||
educationLabel: '',
|
||||
occupationLabel: '',
|
||||
maritalLabel: '',
|
||||
},
|
||||
observers: {
|
||||
user(u) {
|
||||
if (!u) {
|
||||
this.setData({ educationLabel: '', occupationLabel: '', maritalLabel: '' })
|
||||
return
|
||||
}
|
||||
this.setData({
|
||||
educationLabel: profileLabelOf('educationCode', u.educationCode),
|
||||
occupationLabel: profileLabelOf('occupationCode', u.occupationCode),
|
||||
maritalLabel: profileLabelOf('maritalStatus', u.maritalStatus),
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onTap() {
|
||||
if (!this.data.user) return;
|
||||
|
||||
@@ -19,16 +19,16 @@
|
||||
<view class="ui-user-card__meta">
|
||||
<text wx:if="{{user.heightCm}}">{{user.heightCm}}cm · </text>
|
||||
<text>{{user.regionName}}</text>
|
||||
<text wx:if="{{user.educationCode}}"> · {{user.educationCode}}</text>
|
||||
<text wx:if="{{educationLabel}}"> · {{educationLabel}}</text>
|
||||
</view>
|
||||
<view wx:if="{{user.occupationCode}}" class="ui-user-card__meta">
|
||||
{{user.occupationCode}}
|
||||
<view wx:if="{{occupationLabel}}" class="ui-user-card__meta">
|
||||
{{occupationLabel}}
|
||||
</view>
|
||||
<view wx:if="{{user.recommendationReasons && user.recommendationReasons.length}}" class="ui-user-card__tags">
|
||||
<text wx:for="{{user.recommendationReasons}}" wx:key="*this" class="ui-user-card__tag">{{item}}</text>
|
||||
</view>
|
||||
<view wx:if="{{user.maritalStatus || user.hasChildren !== undefined}}" class="ui-user-card__meta">
|
||||
<text wx:if="{{user.maritalStatus}}">{{user.maritalStatus}}</text>
|
||||
<view wx:if="{{maritalLabel || user.hasChildren !== undefined}}" class="ui-user-card__meta">
|
||||
<text wx:if="{{maritalLabel}}">{{maritalLabel}}</text>
|
||||
<text wx:if="{{user.hasChildren}}"> · 有孩</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -10,6 +10,7 @@ const statusEnums = require('./statusEnums');
|
||||
const permissionCodes = require('./permissionCodes');
|
||||
const storageKeys = require('./storageKeys');
|
||||
const businessLimits = require('./businessLimits');
|
||||
const profileLabels = require('./profileLabels');
|
||||
|
||||
module.exports = {
|
||||
// 路由
|
||||
@@ -60,4 +61,15 @@ module.exports = {
|
||||
// 业务限制
|
||||
BUSINESS_LIMITS: businessLimits.BUSINESS_LIMITS,
|
||||
mergeAppConfig: businessLimits.mergeAppConfig,
|
||||
|
||||
// 资料/择偶枚举码 → 中文简体字典(界面不得出现英文枚举码)
|
||||
PROFILE_LABELS: profileLabels,
|
||||
profileLabelOf: profileLabels.labelOf,
|
||||
yesNoLabel: profileLabels.yesNoLabel,
|
||||
futureDanyangLabel: profileLabels.futureDanyangLabel,
|
||||
MATCHMAKER_SERVICE_STATUS_LABEL: profileLabels.MATCHMAKER_SERVICE_STATUS_LABEL,
|
||||
ACCEPT_CHILDREN_OPTIONS: profileLabels.ACCEPT_CHILDREN_OPTIONS,
|
||||
MARITAL_OPTIONS: profileLabels.MARITAL_OPTIONS,
|
||||
EDUCATION_OPTIONS: profileLabels.EDUCATION_OPTIONS,
|
||||
OCCUPATION_OPTIONS: profileLabels.OCCUPATION_OPTIONS,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* 资料 / 择偶条件枚举码 → 中文简体 唯一字典
|
||||
* ============================================================
|
||||
* 铁律:界面上不允许出现任何英文枚举码(如 never_married / bachelor /
|
||||
* government / any / accept / reject)。所有会展示给用户的状态码、选项码
|
||||
* 都必须在本文件里有中文 label。
|
||||
*
|
||||
* 为什么要集中:
|
||||
* - 小程序里最典型的缺陷是「后端枚举码直接当展示值」上屏(例如某卡片
|
||||
* 直接渲染 educationCode=bachelor)。静态类型检查 / WXSS 编译 / 结构
|
||||
* 守卫都发现不了,只有人眼在真机才看得见。
|
||||
* - 后端 demo 种子与实际表单存的码存在历史不一致(表单用 occasional,
|
||||
* 种子用 sometimes;表单用 yes/no,种子用 是/否)。labelOf 统一覆盖
|
||||
* 两套并集,保证详情页无论后端返回哪种都显示中文。
|
||||
*
|
||||
* labelOf(group, code):
|
||||
* - 空值返回 ''
|
||||
* - 已知码返回中文
|
||||
* - 未知码返回原值(便于排查;生产路径不应触发,否则说明字典需补)
|
||||
*/
|
||||
|
||||
// ============ 婚姻状况 ============
|
||||
const MARITAL = {
|
||||
never_married: '未婚',
|
||||
divorced: '离异',
|
||||
widowed: '丧偶',
|
||||
single: '未婚', // profile-confirm 旧别名
|
||||
}
|
||||
|
||||
// ============ 子女情况 ============
|
||||
const CHILDREN_STATUS = {
|
||||
none: '无子女',
|
||||
has: '有子女',
|
||||
}
|
||||
|
||||
// ============ 学历 ============
|
||||
// 覆盖表单值(high_school) 与种子实际值(senior_high/junior_high) 及别名
|
||||
const EDUCATION = {
|
||||
high_school: '高中/中专',
|
||||
senior_high: '高中/中专',
|
||||
junior_high: '初中',
|
||||
college: '大专',
|
||||
bachelor: '本科',
|
||||
master: '硕士',
|
||||
phd: '博士',
|
||||
doctor: '博士', // profile-confirm 旧别名
|
||||
}
|
||||
|
||||
// ============ 职业 ============
|
||||
// 表单类目(生产路径)+ demo 种子实际值(自由英文词)
|
||||
const OCCUPATION = {
|
||||
government: '机关事业单位',
|
||||
education: '教育',
|
||||
medical: '医疗',
|
||||
enterprise: '企业职员',
|
||||
technical: '技术人员',
|
||||
self_employed: '个体经营',
|
||||
freelance: '自由职业',
|
||||
service: '服务行业',
|
||||
agriculture: '农业',
|
||||
other: '其他',
|
||||
// demo 种子实际值
|
||||
clerical: '行政文职',
|
||||
designer: '设计',
|
||||
factory: '工厂生产',
|
||||
nurse: '医护',
|
||||
engineer: '工程师',
|
||||
programmer: '程序员',
|
||||
sales: '销售',
|
||||
doctor: '医生',
|
||||
researcher: '科研',
|
||||
teacher: '教师',
|
||||
}
|
||||
|
||||
// ============ 择偶条件:是否接受有孩(preference-editor 子女下拉) ============
|
||||
const ACCEPT_CHILDREN = {
|
||||
any: '不限',
|
||||
accept: '可接受',
|
||||
reject: '不接受',
|
||||
}
|
||||
|
||||
// ============ 吸烟 / 饮酒 ============
|
||||
// 覆盖表单值(occasional) 与种子实际值(sometimes)
|
||||
const SMOKING = {
|
||||
never: '不吸烟',
|
||||
occasional: '偶尔吸烟',
|
||||
sometimes: '偶尔吸烟',
|
||||
often: '经常吸烟',
|
||||
}
|
||||
const DRINKING = {
|
||||
never: '不喝酒',
|
||||
occasional: '偶尔喝酒',
|
||||
sometimes: '偶尔喝酒',
|
||||
often: '经常喝酒',
|
||||
}
|
||||
|
||||
// ============ 红娘服务状态(matchmaker 历史服务) ============
|
||||
const MATCHMAKER_SERVICE_STATUS_LABEL = {
|
||||
submitted: '已提交',
|
||||
assigned: '已分配',
|
||||
in_service: '服务中',
|
||||
waiting_user: '待您确认',
|
||||
completed: '已完成',
|
||||
cancelled: '已取消',
|
||||
}
|
||||
|
||||
const GROUPS = {
|
||||
maritalStatus: MARITAL,
|
||||
childrenStatus: CHILDREN_STATUS,
|
||||
educationCode: EDUCATION,
|
||||
occupationCode: OCCUPATION,
|
||||
acceptChildren: ACCEPT_CHILDREN,
|
||||
smokingStatus: SMOKING,
|
||||
drinkingStatus: DRINKING,
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用映射:已知码→中文,未知→原值
|
||||
* @param {string} group GROUPS 的键
|
||||
* @param {*} code 后端返回的原始码
|
||||
* @returns {string}
|
||||
*/
|
||||
function labelOf(group, code) {
|
||||
if (code === undefined || code === null || code === '') return ''
|
||||
const map = GROUPS[group]
|
||||
if (!map) return String(code)
|
||||
return map[code] !== undefined ? map[code] : String(code)
|
||||
}
|
||||
|
||||
/** 是/否类(长期居住丹阳等):兼容布尔、yes/no、是/否 三种来源 */
|
||||
function yesNoLabel(v) {
|
||||
if (v === true) return '是'
|
||||
if (v === false) return '否'
|
||||
if (v === 'yes') return '是'
|
||||
if (v === 'no') return '否'
|
||||
if (v === '是' || v === '否') return v
|
||||
return v === undefined || v === null || v === '' ? '' : String(v)
|
||||
}
|
||||
|
||||
/** 未来规划(未来是否愿意继续在丹阳):willing/uncertain/unwilling 或 是/否 */
|
||||
function futureDanyangLabel(v) {
|
||||
if (v === 'willing' || v === '是') return '愿意'
|
||||
if (v === 'uncertain') return '不确定'
|
||||
if (v === 'unwilling' || v === '否') return '不愿意'
|
||||
return v === undefined || v === null || v === '' ? '' : String(v)
|
||||
}
|
||||
|
||||
// ============ picker 选项数组(value/label 形式,供 wxml range + range-key 使用) ============
|
||||
const ACCEPT_CHILDREN_OPTIONS = [
|
||||
{ value: 'any', label: '不限' },
|
||||
{ value: 'accept', label: '可接受' },
|
||||
{ value: 'reject', label: '不接受' },
|
||||
]
|
||||
|
||||
const MARITAL_OPTIONS = [
|
||||
{ value: 'never_married', label: '未婚' },
|
||||
{ value: 'divorced', label: '离异' },
|
||||
{ value: 'widowed', label: '丧偶' },
|
||||
]
|
||||
|
||||
const EDUCATION_OPTIONS = [
|
||||
{ value: 'senior_high', label: '高中/中专' },
|
||||
{ value: 'junior_high', label: '初中' },
|
||||
{ value: 'college', label: '大专' },
|
||||
{ value: 'bachelor', label: '本科' },
|
||||
{ value: 'master', label: '硕士' },
|
||||
{ value: 'phd', label: '博士' },
|
||||
]
|
||||
|
||||
const OCCUPATION_OPTIONS = Object.keys(OCCUPATION).map((v) => ({ value: v, label: OCCUPATION[v] }))
|
||||
|
||||
module.exports = {
|
||||
MARITAL,
|
||||
CHILDREN_STATUS,
|
||||
EDUCATION,
|
||||
OCCUPATION,
|
||||
ACCEPT_CHILDREN,
|
||||
SMOKING,
|
||||
DRINKING,
|
||||
MATCHMAKER_SERVICE_STATUS_LABEL,
|
||||
GROUPS,
|
||||
labelOf,
|
||||
yesNoLabel,
|
||||
futureDanyangLabel,
|
||||
ACCEPT_CHILDREN_OPTIONS,
|
||||
MARITAL_OPTIONS,
|
||||
EDUCATION_OPTIONS,
|
||||
OCCUPATION_OPTIONS,
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{"navigationBarTitleText":"about","usingComponents":{}}
|
||||
{"navigationBarTitleText":"关于我们","usingComponents":{}}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<view class="status-section" wx:else>
|
||||
<view class="pending-icon">💬</view>
|
||||
<text class="pending-title">向Ta表达兴趣</text>
|
||||
<text class="pending-title">向对方表达兴趣</text>
|
||||
<text class="pending-hint">双方确认后才能开启线上沟通</text>
|
||||
<button class="confirm-btn" bindtap="onConfirm">确认开启沟通</button>
|
||||
</view>
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"navigationBarTitleText":"activity-register","usingComponents":{}}
|
||||
{"navigationBarTitleText":"活动报名","usingComponents":{}}
|
||||
|
||||
@@ -1 +1 @@
|
||||
<view class="page"><text>页面: activity-register</text></view>
|
||||
<view class="page"><text>活动报名(建设中)</text></view>
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"navigationBarTitleText":"bind-phone","usingComponents":{}}
|
||||
{"navigationBarTitleText":"绑定手机","usingComponents":{}}
|
||||
|
||||
@@ -1 +1 @@
|
||||
<view class="page"><text>bind-phone</text></view>
|
||||
<view class="page"><text>手机号绑定(建设中)</text></view>
|
||||
|
||||
@@ -242,7 +242,7 @@ Page({
|
||||
const { otherUserId } = this.data;
|
||||
if (!otherUserId) { appStore.toast('暂无更多信息'); return; }
|
||||
wx.showActionSheet({
|
||||
itemList: ['查看 Ta 的资料', '举报 Ta', '拉黑 Ta'],
|
||||
itemList: ['查看对方的资料', '举报对方', '拉黑对方'],
|
||||
success: (res) => {
|
||||
if (res.tapIndex === 0) wx.navigateTo({ url: to.detail(otherUserId) });
|
||||
else if (res.tapIndex === 1) wx.navigateTo({ url: to.report(otherUserId) });
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
<view wx:elif="{{!loading}}" class="discover-empty">
|
||||
<image src="/assets/brand/logo-mark.png" mode="aspectFit" />
|
||||
<text class="empty-title">暂无符合条件的人选</text>
|
||||
<text class="empty-desc">Required 条件不会自动放宽,可以调整筛选后再看看</text>
|
||||
<text class="empty-desc">必须符合的条件不会自动放宽,可以调整筛选后再看看</text>
|
||||
<view class="empty-action" bindtap="onResetFilter">清空筛选</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
catchtap="onLikeBack"
|
||||
data-user-id="{{item.userId}}"
|
||||
disabled="{{actionLoadingUserId === item.userId}}"
|
||||
>喜欢TA</button>
|
||||
>喜欢对方</button>
|
||||
</view>
|
||||
</ui-user-card>
|
||||
</view>
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"navigationBarTitleText":"help-center","usingComponents":{}}
|
||||
{"navigationBarTitleText":"帮助中心","usingComponents":{}}
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
</view>
|
||||
<view wx:else class="recommend-empty">
|
||||
<image class="empty-logo" src="/assets/brand/logo-mark.png" mode="aspectFit" />
|
||||
<view class="empty-copy"><text class="empty-title">暂时没有完全符合双方条件的人</text><text class="empty-desc">我们坚持 Required 条件,不会为了数量降低筛选标准</text></view>
|
||||
<view class="empty-copy"><text class="empty-title">暂时没有完全符合双方条件的人</text><text class="empty-desc">我们坚持必须符合的条件,不会为了数量降低筛选标准</text></view>
|
||||
<view class="empty-actions"><view class="empty-btn secondary" bindtap="onEmptyAction" data-event="preference">调整条件</view><view class="empty-btn primary" bindtap="onEmptyAction" data-event="matchmaker">公益红娘</view></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"navigationBarTitleText":"login","usingComponents":{}}
|
||||
{"navigationBarTitleText":"登录","usingComponents":{}}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* - 推荐:红娘推荐列表跳 fate-center 页(AGENTS 第 5 条:红娘不可替用户操作)
|
||||
*/
|
||||
import { matchmakerStore, appStore } from '../../stores';
|
||||
import { ROUTE, to, buildPath, MATCHMAKER_SERVICE_STATUS } from '../../constants';
|
||||
import { ROUTE, to, buildPath, MATCHMAKER_SERVICE_STATUS, MATCHMAKER_SERVICE_STATUS_LABEL } from '../../constants';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -59,10 +59,17 @@ Page({
|
||||
activeService.status === MATCHMAKER_SERVICE_STATUS.IN_SERVICE ||
|
||||
activeService.status === MATCHMAKER_SERVICE_STATUS.WAITING_USER
|
||||
);
|
||||
const historyServices = activeService
|
||||
const withStatusText = (s: any) => ({
|
||||
...s,
|
||||
statusText: MATCHMAKER_SERVICE_STATUS_LABEL[s.status] || s.statusText || s.status || '',
|
||||
})
|
||||
const historyServices = (activeService
|
||||
? myServices.filter((s: any) => s.serviceId !== activeService.serviceId)
|
||||
: myServices;
|
||||
this.setData({ loading: false, firstLoaded: true, activeService, historyServices, showRecommendations });
|
||||
: myServices).map(withStatusText)
|
||||
const activeServiceWithText = activeService
|
||||
? { ...activeService, statusText: MATCHMAKER_SERVICE_STATUS_LABEL[activeService.status] || activeService.statusText || activeService.status || '' }
|
||||
: null
|
||||
this.setData({ loading: false, firstLoaded: true, activeService: activeServiceWithText, historyServices, showRecommendations });
|
||||
} catch (err: any) {
|
||||
this.setData({ loading: false, firstLoaded: true });
|
||||
appStore.toastError(err, '加载红娘服务失败');
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"navigationBarTitleText":"my-activities","usingComponents":{}}
|
||||
{"navigationBarTitleText":"我的活动","usingComponents":{}}
|
||||
|
||||
@@ -1 +1 @@
|
||||
<view class="page"><text>页面: my-activities</text></view>
|
||||
<view class="page"><text>我的活动(建设中)</text></view>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
const { userStore, appStore } = require('../../stores');
|
||||
const { profileService } = require('../../services');
|
||||
const { ROUTE, STORAGE_KEYS, PROFILE_REVIEW } = require('../../constants');
|
||||
const { ROUTE, STORAGE_KEYS, PROFILE_REVIEW, profileLabelOf } = require('../../constants');
|
||||
|
||||
// 审核状态 → StatusBadge kind 映射
|
||||
const REVIEW_BADGE = {
|
||||
@@ -23,28 +23,6 @@ const REVIEW_BADGE = {
|
||||
[PROFILE_REVIEW.REJECTED]: { label: '未通过', kind: 'danger' },
|
||||
};
|
||||
|
||||
// 婚姻状况字典(前端展示用,权威值仍由服务端 Profile.maritalStatus 给出)
|
||||
const MARITAL_LABEL = {
|
||||
single: '未婚',
|
||||
divorced: '离异',
|
||||
widowed: '丧偶',
|
||||
};
|
||||
|
||||
// 学历字典
|
||||
const EDUCATION_LABEL = {
|
||||
high_school: '高中/中专',
|
||||
college: '大专',
|
||||
bachelor: '本科',
|
||||
master: '硕士',
|
||||
doctor: '博士',
|
||||
};
|
||||
|
||||
// 子女情况字典
|
||||
const CHILDREN_LABEL = {
|
||||
none: '无子女',
|
||||
has: '有子女',
|
||||
};
|
||||
|
||||
/**
|
||||
* 计算资料完整度(客户端派生展示)
|
||||
*
|
||||
@@ -165,9 +143,9 @@ Page({
|
||||
resubmitAllowed,
|
||||
completion,
|
||||
reviewBadge: REVIEW_BADGE[reviewStatus] || REVIEW_BADGE[PROFILE_REVIEW.NOT_SUBMITTED],
|
||||
maritalLabel: MARITAL_LABEL[profile && profile.maritalStatus] || (profile && profile.maritalStatus) || '-',
|
||||
educationLabel: EDUCATION_LABEL[profile && profile.educationCode] || (profile && profile.educationCode) || '-',
|
||||
childrenLabel: CHILDREN_LABEL[profile && (profile.hasChildren ? 'has' : 'none')] || '-',
|
||||
maritalLabel: profileLabelOf('maritalStatus', profile && profile.maritalStatus) || '-',
|
||||
educationLabel: profileLabelOf('educationCode', profile && profile.educationCode) || '-',
|
||||
childrenLabel: profile && (profile.hasChildren ? '有子女' : '无子女'),
|
||||
});
|
||||
} catch (err) {
|
||||
appStore.toastError(err, '加载失败');
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
const { matchStore, appStore, blockUser } = require('../../stores');
|
||||
const { recommendationService } = require('../../services');
|
||||
const { to, DANGER_ACTION, RELATION_STATUS, BUSINESS_ERROR_CODE } = require('../../constants');
|
||||
const { to, DANGER_ACTION, RELATION_STATUS, BUSINESS_ERROR_CODE, profileLabelOf, yesNoLabel, futureDanyangLabel } = require('../../constants');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -96,22 +96,30 @@ Page({
|
||||
const metaParts = [];
|
||||
if (user.heightCm) metaParts.push(`${user.heightCm}cm`);
|
||||
if (user.regionName) metaParts.push(user.regionName);
|
||||
if (user.educationCode) metaParts.push(user.educationCode);
|
||||
if (user.occupationCode) metaParts.push(user.occupationCode);
|
||||
const eduLabel = profileLabelOf('educationCode', user.educationCode);
|
||||
if (eduLabel) metaParts.push(eduLabel);
|
||||
const occLabel = profileLabelOf('occupationCode', user.occupationCode);
|
||||
if (occLabel) metaParts.push(occLabel);
|
||||
|
||||
const basicInfo = [];
|
||||
if (user.maritalStatus) basicInfo.push({ label: '婚姻状况', value: user.maritalStatus });
|
||||
const maritalLabel = profileLabelOf('maritalStatus', user.maritalStatus);
|
||||
if (maritalLabel) basicInfo.push({ label: '婚姻状况', value: maritalLabel });
|
||||
if (user.childrenStatus || user.hasChildren !== undefined) {
|
||||
basicInfo.push({ label: '子女情况', value: user.childrenStatus || (user.hasChildren ? '有孩' : '无孩') });
|
||||
basicInfo.push({ label: '子女情况', value: user.childrenStatus ? profileLabelOf('childrenStatus', user.childrenStatus) : (user.hasChildren ? '有孩' : '无孩') });
|
||||
}
|
||||
if (user.educationCode) basicInfo.push({ label: '学历', value: user.educationCode });
|
||||
if (user.occupationCode) basicInfo.push({ label: '职业', value: user.occupationCode });
|
||||
if (user.smokingStatus) basicInfo.push({ label: '吸烟', value: user.smokingStatus });
|
||||
if (user.drinkingStatus) basicInfo.push({ label: '饮酒', value: user.drinkingStatus });
|
||||
if (user.longTermDanyang) basicInfo.push({ label: '定居意愿', value: user.longTermDanyang, full: true });
|
||||
if (user.futureDanyang) basicInfo.push({ label: '未来规划', value: user.futureDanyang, full: true });
|
||||
if (eduLabel) basicInfo.push({ label: '学历', value: eduLabel });
|
||||
if (occLabel) basicInfo.push({ label: '职业', value: occLabel });
|
||||
const smokingLabel = profileLabelOf('smokingStatus', user.smokingStatus);
|
||||
if (smokingLabel) basicInfo.push({ label: '吸烟', value: smokingLabel });
|
||||
const drinkingLabel = profileLabelOf('drinkingStatus', user.drinkingStatus);
|
||||
if (drinkingLabel) basicInfo.push({ label: '饮酒', value: drinkingLabel });
|
||||
const longTermLabel = yesNoLabel(user.longTermDanyang);
|
||||
if (longTermLabel) basicInfo.push({ label: '定居意愿', value: longTermLabel, full: true });
|
||||
const futureLabel = futureDanyangLabel(user.futureDanyang);
|
||||
if (futureLabel) basicInfo.push({ label: '未来规划', value: futureLabel, full: true });
|
||||
|
||||
const attitudeInfo = [];
|
||||
// relationshipGoal / marriagePlan 由后端返回中文文案(结婚 / 恋爱 等),未知码原样透传
|
||||
if (user.relationshipGoal) attitudeInfo.push({ label: '寻找', value: user.relationshipGoal, full: true });
|
||||
if (user.marriagePlan) attitudeInfo.push({ label: '结婚计划', value: user.marriagePlan, full: true });
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
<view class="service-page"><view class="hero"><text class="eyebrow">U51 服务预约</text><text class="hero-title">预约到站服务</text><text class="hero-desc">选择合适的时间和服务项目,提交后等待工作人员确认。</text></view><view class="content"><view class="card"><view class="field" wx:if="{{serviceLocationId}}"><text class="label">服务站点</text><view class="select-box is-readonly">{{serviceLocationName || '已选服务站点'}}</view></view><view class="field"><text class="label">服务项目</text><picker range="{{services}}" bindchange="chooseService"><view class="select-box">{{service}}<text class="arrow" style="margin-left:auto">›</text></view></picker></view><view class="field"><text class="label">预约日期</text><picker mode="date" bindchange="chooseDate"><view class="select-box">{{date || '请选择日期'}}</view></picker></view><view class="field"><text class="label">希望沟通的事项(选填)</text><textarea class="textarea" value="{{note}}" bindinput="onNoteInput" placeholder="请简要说明,避免填写身份证号等敏感信息" maxlength="200"/></view><view class="notice">提交预约不代表已确认。工作人员确认后,预约状态会更新为“已确认”。</view></view></view><view class="fixed-action"><view class="primary-btn {{submitting ? 'is-disabled' : ''}}" bindtap="submit">{{submitting ? '提交中…' : '提交预约'}}</view></view></view>
|
||||
<view class="service-page"><view class="hero"><text class="eyebrow">服务预约</text><text class="hero-title">预约到站服务</text><text class="hero-desc">选择合适的时间和服务项目,提交后等待工作人员确认。</text></view><view class="content"><view class="card"><view class="field" wx:if="{{serviceLocationId}}"><text class="label">服务站点</text><view class="select-box is-readonly">{{serviceLocationName || '已选服务站点'}}</view></view><view class="field"><text class="label">服务项目</text><picker range="{{services}}" bindchange="chooseService"><view class="select-box">{{service}}<text class="arrow" style="margin-left:auto">›</text></view></picker></view><view class="field"><text class="label">预约日期</text><picker mode="date" bindchange="chooseDate"><view class="select-box">{{date || '请选择日期'}}</view></picker></view><view class="field"><text class="label">希望沟通的事项(选填)</text><textarea class="textarea" value="{{note}}" bindinput="onNoteInput" placeholder="请简要说明,避免填写身份证号等敏感信息" maxlength="200"/></view><view class="notice">提交预约不代表已确认。工作人员确认后,预约状态会更新为“已确认”。</view></view></view><view class="fixed-action"><view class="primary-btn {{submitting ? 'is-disabled' : ''}}" bindtap="submit">{{submitting ? '提交中…' : '提交预约'}}</view></view></view>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<view class="service-page"><view class="hero"><text class="eyebrow">U64 公益服务记录</text><text class="hero-title">每一次服务,都清楚可查</text><text class="hero-desc">这里只展示与你有关的公益服务事实,不展示咨询正文。</text></view><view class="content"><view wx:if="{{loading}}" class="card empty-panel"><view class="empty-symbol">…</view><text class="empty-title">正在加载</text></view><view wx:elif="{{error}}" class="card empty-panel"><view class="empty-symbol">!</view><text class="empty-title">{{error}}</text></view><view wx:elif="{{records.length}}" class="block"><view class="card"><view wx:for="{{records}}" wx:key="key" class="record-row"><view class="feature-icon">{{item.icon}}</view><view class="feature-copy"><text class="feature-title">{{item.title}}</text><text class="feature-desc">{{item.date}}</text></view><text class="tag">{{item.status}}</text></view></view></view><view wx:else class="card empty-panel"><view class="empty-symbol">记</view><text class="empty-title">还没有公益服务记录</text><text class="meta">预约到站服务或申请公益咨询后,会在这里留下时间线。</text></view></view><view class="notice">咨询与公益红娘记录彼此分离;工作人员只能查看职责范围内的信息。</view></view>
|
||||
<view class="service-page"><view class="hero"><text class="eyebrow">公益服务记录</text><text class="hero-title">每一次服务,都清楚可查</text><text class="hero-desc">这里只展示与你有关的公益服务事实,不展示咨询正文。</text></view><view class="content"><view wx:if="{{loading}}" class="card empty-panel"><view class="empty-symbol">…</view><text class="empty-title">正在加载</text></view><view wx:elif="{{error}}" class="card empty-panel"><view class="empty-symbol">!</view><text class="empty-title">{{error}}</text></view><view wx:elif="{{records.length}}" class="block"><view class="card"><view wx:for="{{records}}" wx:key="key" class="record-row"><view class="feature-icon">{{item.icon}}</view><view class="feature-copy"><text class="feature-title">{{item.title}}</text><text class="feature-desc">{{item.date}}</text></view><text class="tag">{{item.status}}</text></view></view></view><view wx:else class="card empty-panel"><view class="empty-symbol">记</view><text class="empty-title">还没有公益服务记录</text><text class="meta">预约到站服务或申请公益咨询后,会在这里留下时间线。</text></view></view><view class="notice">咨询与公益红娘记录彼此分离;工作人员只能查看职责范围内的信息。</view></view>
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"navigationBarTitleText":"settings","usingComponents":{}}
|
||||
{"navigationBarTitleText":"设置","usingComponents":{}}
|
||||
|
||||
Reference in New Issue
Block a user