chore: establish V1.1 recovery baseline

This commit is contained in:
2026-09-09 22:05:00 +08:00
commit b46b48eb33
947 changed files with 155471 additions and 0 deletions
@@ -0,0 +1,247 @@
/**
* 基础资料页 U06(Phase 04 P04-T21)
*
* 重构要点(遵循 AGENTS.md 不变量):
* - 数据:profileService.getProfile + userStore.updateProfile,不再直连 api(第 9 条)
* - 性别/出生日期:由身份证自动生成,只读展示(第 8 条:本地不长期保存身份证/完整生日)
* - 草稿恢复:onLoad 读 ONBOARDING_DRAFT.basic,保存前写回(验收条件"Draft退出后可恢复")
* - 数量限制:本页无数值型限制字段,选项列表遵循 §4.1 配置
* - 下一步:跳 profile-lifestyle
*/
const { userStore, appStore } = require('../../stores');
const { profileService } = require('../../services');
const { ROUTE, STORAGE_KEYS } = require('../../constants');
const MARITAL_OPTIONS = [
{ value: 'never_married', label: '未婚' },
{ value: 'divorced', label: '离异' },
{ value: 'widowed', label: '丧偶' },
];
const CHILDREN_OPTIONS = [
{ value: 'none', label: '无子女' },
{ value: 'has', label: '有子女' },
];
const EDUCATION_OPTIONS = [
{ value: 'high_school', label: '高中/中专' },
{ value: 'college', label: '大专' },
{ value: 'bachelor', label: '本科' },
{ value: 'master', label: '硕士' },
{ value: 'phd', label: '博士' },
];
const OCCUPATION_OPTIONS = [
{ value: 'government', label: '机关事业单位' },
{ value: 'education', label: '教育' },
{ value: 'medical', label: '医疗' },
{ value: 'enterprise', label: '企业职员' },
{ value: 'technical', label: '技术人员' },
{ value: 'self_employed', label: '个体经营' },
{ value: 'freelance', label: '自由职业' },
{ value: 'service', label: '服务行业' },
{ value: 'agriculture', label: '农业' },
{ value: 'other', label: '其他' },
];
const REGION_OPTIONS = ['丹阳', '镇江市区', '镇江其他辖区', '南京'];
Page({
data: {
statusBarHeight: 20,
navBarHeight: 44,
loading: false,
saving: false,
// 身份证自动生成(只读)
gender: '',
genderLabel: '',
ageText: '',
// 用户填写
regionName: '',
heightCm: '',
maritalStatus: '',
hasChildren: '',
educationCode: '',
occupationCode: '',
// 选项
maritalOptions: MARITAL_OPTIONS,
childrenOptions: CHILDREN_OPTIONS,
educationOptions: EDUCATION_OPTIONS,
occupationOptions: OCCUPATION_OPTIONS,
regionOptions: REGION_OPTIONS,
regionIndex: -1,
maritalIndex: -1,
childrenIndex: -1,
educationIndex: -1,
occupationIndex: -1,
},
onLoad() {
this._initSystemInfo();
this._applyDraft();
this.loadProfile();
},
_initSystemInfo() {
try {
const sysInfo = wx.getSystemInfoSync();
this.setData({
statusBarHeight: sysInfo.statusBarHeight || 20,
navBarHeight: 44,
});
} catch (e) { /* 使用默认值 */ }
},
/** 从服务端拉取已有资料(合并草稿) */
async loadProfile() {
this.setData({ loading: true });
try {
const profile = await profileService.getProfile();
this._mergeProfile(profile);
} catch (err) {
// 首次填写时无资料,使用草稿默认值
} finally {
this.setData({ loading: false });
}
},
/** 合并服务端资料到表单(草稿优先,草稿未填则用服务端值) */
_mergeProfile(profile) {
if (!profile) return;
const draft = this._readDraft();
const regionName = draft.regionName || profile.regionName || '';
const maritalStatus = draft.maritalStatus || profile.maritalStatus || '';
const hasChildren = draft.hasChildren || (profile.hasChildren !== undefined ? (profile.hasChildren ? 'has' : 'none') : '');
const educationCode = draft.educationCode || profile.educationCode || '';
const occupationCode = draft.occupationCode || profile.occupationCode || '';
const heightCm = draft.heightCm || profile.heightCm || '';
this.setData({
gender: profile.gender || draft.gender || '',
genderLabel: profile.gender === 'male' ? '男' : (profile.gender === 'female' ? '女' : ''),
ageText: profile.age ? `${profile.age}岁` : '',
regionName,
heightCm,
maritalStatus,
hasChildren,
educationCode,
occupationCode,
regionIndex: REGION_OPTIONS.indexOf(regionName),
maritalIndex: MARITAL_OPTIONS.findIndex((o) => o.value === maritalStatus),
childrenIndex: CHILDREN_OPTIONS.findIndex((o) => o.value === hasChildren),
educationIndex: EDUCATION_OPTIONS.findIndex((o) => o.value === educationCode),
occupationIndex: OCCUPATION_OPTIONS.findIndex((o) => o.value === occupationCode),
});
},
// ============ 草稿 ============
_readDraft() {
try {
const draft = wx.getStorageSync(STORAGE_KEYS.ONBOARDING_DRAFT) || {};
return draft.basic || {};
} catch (e) {
return {};
}
},
/** 把草稿值填入表单(loadProfile 失败时也能恢复) */
_applyDraft() {
const d = this._readDraft();
if (!d || Object.keys(d).length === 0) return;
this.setData({
regionName: d.regionName || '',
heightCm: d.heightCm || '',
maritalStatus: d.maritalStatus || '',
hasChildren: d.hasChildren || '',
educationCode: d.educationCode || '',
occupationCode: d.occupationCode || '',
regionIndex: REGION_OPTIONS.indexOf(d.regionName || ''),
maritalIndex: MARITAL_OPTIONS.findIndex((o) => o.value === (d.maritalStatus || '')),
childrenIndex: CHILDREN_OPTIONS.findIndex((o) => o.value === (d.hasChildren || '')),
educationIndex: EDUCATION_OPTIONS.findIndex((o) => o.value === (d.educationCode || '')),
occupationIndex: OCCUPATION_OPTIONS.findIndex((o) => o.value === (d.occupationCode || '')),
});
},
_writeDraft() {
try {
const draft = wx.getStorageSync(STORAGE_KEYS.ONBOARDING_DRAFT) || {};
draft.basic = {
regionName: this.data.regionName,
heightCm: this.data.heightCm,
maritalStatus: this.data.maritalStatus,
hasChildren: this.data.hasChildren,
educationCode: this.data.educationCode,
occupationCode: this.data.occupationCode,
};
draft.currentStep = 'basic';
wx.setStorageSync(STORAGE_KEYS.ONBOARDING_DRAFT, draft);
} catch (e) { /* ignore */ }
},
// ============ 表单交互 ============
onRegionChange(e) {
const idx = Number(e.detail.value);
this.setData({ regionIndex: idx, regionName: REGION_OPTIONS[idx] });
},
onHeightInput(e) {
this.setData({ heightCm: e.detail.value });
},
onMaritalChange(e) {
const idx = Number(e.detail.value);
this.setData({ maritalIndex: idx, maritalStatus: MARITAL_OPTIONS[idx].value });
},
onChildrenChange(e) {
const idx = Number(e.detail.value);
this.setData({ childrenIndex: idx, hasChildren: CHILDREN_OPTIONS[idx].value });
},
onEducationChange(e) {
const idx = Number(e.detail.value);
this.setData({ educationIndex: idx, educationCode: EDUCATION_OPTIONS[idx].value });
},
onOccupationChange(e) {
const idx = Number(e.detail.value);
this.setData({ occupationIndex: idx, occupationCode: OCCUPATION_OPTIONS[idx].value });
},
// ============ 导航 ============
onBack() {
this._writeDraft();
wx.navigateBack();
},
/** 下一步:校验必填 → 保存草稿 → 提交服务端 → 跳 profile-lifestyle */
async onNext() {
if (this.data.saving) return;
const { regionName, heightCm, maritalStatus, hasChildren, educationCode, occupationCode } = this.data;
if (!regionName) return appStore.toast('请选择居住区域');
if (!heightCm) return appStore.toast('请填写身高');
if (!maritalStatus) return appStore.toast('请选择婚姻状况');
if (!hasChildren) return appStore.toast('请选择子女情况');
if (!educationCode) return appStore.toast('请选择学历');
if (!occupationCode) return appStore.toast('请选择职业类型');
this.setData({ saving: true });
this._writeDraft();
try {
await userStore.updateProfile({
regionName,
heightCm: Number(heightCm),
maritalStatus,
hasChildren: hasChildren === 'has',
educationCode,
occupationCode,
});
this.setData({ saving: false });
wx.navigateTo({ url: ROUTE.ONBOARDING.PROFILE_LIFESTYLE });
} catch (err) {
this.setData({ saving: false });
appStore.toastError(err, '保存失败');
}
},
});