fix(app): 我的页 Tab 内嵌修复 + 身高体重改滚轮选择器——ProfileContent 组件化(占位+跳页方案把用户锁死在独立路由页,只能登出);身高 100-250/体重 25-300 滚轮选择(showTextPickerDialog),不再键盘输入

This commit is contained in:
xsl
2026-08-16 11:40:42 +08:00
parent 256f850a61
commit db9c4da0e3
4 changed files with 675 additions and 532 deletions
@@ -2,7 +2,8 @@
* ReportCreatePage —— 报告创建(F3):问卷六字段 + 模拟照片键 + 提交 + 任务轮询。
*
* 流程:
* 1) 问卷表单(性别/身高/体重/生日/核心诉求多选/心理风格九型),提交前前端校验
* 1) 问卷表单(性别/身高/体重/生日/核心诉求多选/心理风格九型),提交前前端校验;
* 身高/体重为滚轮选择器(showTextPickerDialog),不提供键盘输入
* 2) 照片键为模拟:V1 骨架 COS 直传未接入,留空自动生成 report_photo/{userId}/{随机}.jpg
* 3) POST /api/v1/reports(request_key 随机 hex;失败重试沿用同一 key,服务端幂等重放)
* 4) 40920(本期已生成)→ 页内对话框:可跳最近一次报告(AppStorage 记录),
@@ -24,6 +25,25 @@ const GOAL_OPTIONS: string[] = ['显高', '显瘦', '显白', '显气质', '遮
const STYLE_OPTIONS: string[] = ['戏剧', '睿智', '自然', '古典', '优雅', '浪漫', '前卫', '少女', '少年']
const LAST_REPORT_STORAGE_KEY: string = 'f3_last_report_id'
// 身高/体重改为选择器(不允许键盘输入):数值范围与原校验一致
const HEIGHT_MIN: number = 100
const HEIGHT_MAX: number = 250
const HEIGHT_DEFAULT: number = 165
const WEIGHT_MIN: number = 25
const WEIGHT_MAX: number = 300
const WEIGHT_DEFAULT: number = 52
function buildRangeOptions(min: number, max: number): string[] {
const arr: string[] = []
for (let v = min; v <= max; v++) {
arr.push(`${v}`)
}
return arr
}
const HEIGHT_OPTIONS: string[] = buildRangeOptions(HEIGHT_MIN, HEIGHT_MAX)
const WEIGHT_OPTIONS: string[] = buildRangeOptions(WEIGHT_MIN, WEIGHT_MAX)
interface FieldLabelParams {
label: string
err: string
@@ -66,23 +86,18 @@ struct ReportCreatePage {
}
}
private isNumeric(s: string): boolean {
return /^\d+(\.\d+)?$/.test(s.trim())
}
private validateForm(): boolean {
let ok = true
this.genderErr = this.gender === '' ? '请选择性别' : ''
if (this.genderErr !== '') {
ok = false
}
const h = Number(this.heightInput)
this.heightErr = !this.isNumeric(this.heightInput) || h < 100 || h > 250 ? '需为 100~250' : ''
// 选择器产出必然在合法区间,未选才是唯一错误态
this.heightErr = this.heightInput === '' ? '请选择身高' : ''
if (this.heightErr !== '') {
ok = false
}
const w = Number(this.weightInput)
this.weightErr = !this.isNumeric(this.weightInput) || w < 25 || w > 300 ? '需为 25~300' : ''
this.weightErr = this.weightInput === '' ? '请选择体重' : ''
if (this.weightErr !== '') {
ok = false
}
@@ -111,6 +126,46 @@ struct ReportCreatePage {
return this.coreGoals.indexOf(g) >= 0
}
/** TextPickerResult.index 是联合类型(多列选择器为数组),单列取数字下标。 */
private pickIndex(v: TextPickerResult): number {
return typeof v.index === 'number' ? v.index : -1
}
/** 已有值从当前值滚起,未选过从常见默认值(165cm)滚起。 */
private openHeightPicker(): void {
const cur = this.heightInput !== '' ? Number(this.heightInput) : HEIGHT_DEFAULT
const clamped = Math.min(Math.max(cur - HEIGHT_MIN, 0), HEIGHT_OPTIONS.length - 1)
this.getUIContext().showTextPickerDialog({
range: HEIGHT_OPTIONS,
selected: clamped,
canLoop: false,
onAccept: (value: TextPickerResult) => {
const idx = this.pickIndex(value)
if (idx >= 0 && idx < HEIGHT_OPTIONS.length) {
this.heightInput = HEIGHT_OPTIONS[idx]
this.heightErr = ''
}
}
})
}
private openWeightPicker(): void {
const cur = this.weightInput !== '' ? Number(this.weightInput) : WEIGHT_DEFAULT
const clamped = Math.min(Math.max(cur - WEIGHT_MIN, 0), WEIGHT_OPTIONS.length - 1)
this.getUIContext().showTextPickerDialog({
range: WEIGHT_OPTIONS,
selected: clamped,
canLoop: false,
onAccept: (value: TextPickerResult) => {
const idx = this.pickIndex(value)
if (idx >= 0 && idx < WEIGHT_OPTIONS.length) {
this.weightInput = WEIGHT_OPTIONS[idx]
this.weightErr = ''
}
}
})
}
private onSubmitTap(): void {
if (this.submitting) {
return
@@ -349,16 +404,23 @@ struct ReportCreatePage {
Row({ space: 12 }) {
Column({ space: 8 }) {
this.FieldLabel({ label: '身高 (cm)', err: this.heightErr, required: true })
TextInput({ placeholder: '如 165', text: this.heightInput })
.type(InputType.Number)
.height(44)
.fontSize(14)
.backgroundColor('#F5F6F8')
.borderRadius(10)
.onChange((v: string) => {
this.heightInput = v
this.heightErr = ''
})
Row() {
Text(this.heightInput === '' ? '请选择' : `${this.heightInput} cm`)
.fontSize(14)
.fontColor(this.heightInput === '' ? '#AAAAAA' : '#333333')
.layoutWeight(1)
Text('▼')
.fontSize(10)
.fontColor('#999999')
}
.height(44)
.padding({ left: 12, right: 12 })
.backgroundColor('#F5F6F8')
.borderRadius(10)
.alignItems(VerticalAlign.Center)
.onClick(() => {
this.openHeightPicker()
})
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
@@ -366,16 +428,23 @@ struct ReportCreatePage {
Column({ space: 8 }) {
this.FieldLabel({ label: '体重 (kg)', err: this.weightErr, required: true })
TextInput({ placeholder: '如 52', text: this.weightInput })
.type(InputType.Number)
.height(44)
.fontSize(14)
.backgroundColor('#F5F6F8')
.borderRadius(10)
.onChange((v: string) => {
this.weightInput = v
this.weightErr = ''
})
Row() {
Text(this.weightInput === '' ? '请选择' : `${this.weightInput} kg`)
.fontSize(14)
.fontColor(this.weightInput === '' ? '#AAAAAA' : '#333333')
.layoutWeight(1)
Text('▼')
.fontSize(10)
.fontColor('#999999')
}
.height(44)
.padding({ left: 12, right: 12 })
.backgroundColor('#F5F6F8')
.borderRadius(10)
.alignItems(VerticalAlign.Center)
.onClick(() => {
this.openWeightPicker()
})
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)