fix(app): 真机首验三连修——信封解析/DatePicker 折叠/跳转兜底
真机+模拟器注入实测发现并修复: 1. ApiClient 把一切非 200 HTTP 吞成 50000「服务开小差了」——后端业务 错误(40920/40301/42910…)走非 200 状态码但 body 是统一信封,先验 信封形状再判成败。这是「提交不起作用」的直接原因(用户二次提交 =409,红字被布局挤在视野外) 2. 生日 DatePicker 默认展开高约半屏:九型必填项与提交按钮被推出视野、 滚动手势被选择器消费。改点击展开,表单一屏可见 3. 校验失败加顶部汇总红条(字段红字可能在视野外的可见性兜底) 4. 轮询完成不自动跳结果页:@Builder 传函数给子组件在真机调用无效 (手动按钮同一方法正常)——加「查看报告」兜底按钮 + replaceUrl 显式 catch + 完成态文案引导,回调保留 try/catch 与日志 验证:40920 对话框正常弹出;新用户提交→轮询→查看报告→32 维结果页 完整渲染(含降级黄条、报告编号)
This commit is contained in:
@@ -81,7 +81,15 @@ export struct TaskPollingPanel {
|
||||
}
|
||||
if (t.status === 'SUCCEEDED') {
|
||||
this.stopTimer()
|
||||
this.onSucceeded()
|
||||
this.stageText = '已完成——点击下方「查看报告」查看结果'
|
||||
// 回调经 @Builder 传函数在真机上存在调用无效的坑(手动按钮同一
|
||||
// 方法完全正常,真机实测轮询完成不自动跳转)。保留回调尝试,
|
||||
// 失败静默并由宿主的「查看报告」兜底按钮承担导航。
|
||||
try {
|
||||
this.onSucceeded()
|
||||
} catch (e) {
|
||||
console.error(`poll onSucceeded failed: ${JSON.stringify(e)}`)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (t.status === 'FAILED') {
|
||||
|
||||
@@ -76,15 +76,20 @@ export class ApiClient {
|
||||
connectTimeout: 10000,
|
||||
readTimeout: 30000
|
||||
})
|
||||
if (resp.responseCode !== 200 || resp.result === null || resp.result === undefined) {
|
||||
// 网关/代理层错误:没有信封可用,给统一话术(NFR-UX-02)
|
||||
throw new ApiError(50000, '服务开小差了', '请稍后重试', resp.responseCode)
|
||||
}
|
||||
// 后端业务错误(40920/40301/42910…)走非 200 的 HTTP 状态码,
|
||||
// 但 body 仍是统一信封——先验信封形状再判成败;只有 body 不是
|
||||
// 信封(网关 502 的 HTML、字符串等)才按网关错误兜底。
|
||||
// 真机实测教训:把非 200 一律吞成 50000,会吃掉全部业务错误分支
|
||||
// (40920 的"已生成过"对话框、40301 的额度提示全部失效)。
|
||||
const env = resp.result as ApiEnvelope<T>
|
||||
if (env.code !== 0) {
|
||||
throw new ApiError(env.code, env.message, env.hint, resp.responseCode)
|
||||
if (typeof env === 'object' && env !== null && typeof env.code === 'number') {
|
||||
if (env.code !== 0) {
|
||||
throw new ApiError(env.code, env.message, env.hint, resp.responseCode)
|
||||
}
|
||||
return env.data
|
||||
}
|
||||
return env.data
|
||||
// 网关/代理层错误:没有信封可用,给统一话术(NFR-UX-02)
|
||||
throw new ApiError(50000, '服务开小差了', '请稍后重试', resp.responseCode)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
|
||||
@@ -41,6 +41,9 @@ struct ReportCreatePage {
|
||||
@State goalsErr: string = ''
|
||||
@State styleErr: string = ''
|
||||
@State submitting: boolean = false
|
||||
@State showDatePicker: boolean = false
|
||||
@State formSummary: string = ''
|
||||
@State navError: string = ''
|
||||
@State submitError: string = ''
|
||||
@State failMsg: string = ''
|
||||
@State showDupDialog: boolean = false
|
||||
@@ -105,7 +108,13 @@ struct ReportCreatePage {
|
||||
if (this.submitting) {
|
||||
return
|
||||
}
|
||||
this.formSummary = ''
|
||||
if (!this.validateForm()) {
|
||||
// 字段级红字可能滚在视野外(真机实测的"点了没反应"就是这种情况):
|
||||
// 顶部再放一条汇总,让失败永远可见。
|
||||
const errs: string[] = [this.genderErr, this.heightErr, this.weightErr, this.goalsErr, this.styleErr]
|
||||
const n = errs.filter((e: string): boolean => e !== '').length
|
||||
this.formSummary = `还有 ${n} 项未完成,请检查标红的字段(性别/身高/体重/核心诉求/心理风格)`
|
||||
return
|
||||
}
|
||||
this.doSubmit()
|
||||
@@ -176,7 +185,12 @@ struct ReportCreatePage {
|
||||
|
||||
private onTaskSucceeded(): void {
|
||||
const params: Record<string, Object> = { 'reportId': this.reportId }
|
||||
// 显式 catch:replaceUrl 的 Promise 拒绝在无 catch 时完全静默(真机实测
|
||||
// 卡在轮询页无任何提示),至少把失败暴露出来并由兜底按钮接管。
|
||||
router.replaceUrl({ url: 'pages/ReportResultPage', params: params })
|
||||
.catch((e: Object): void => {
|
||||
this.navError = `报告已生成,但打开结果页失败:${String(e).substring(0, 80)}`
|
||||
})
|
||||
}
|
||||
|
||||
private onTaskFailed(reason: string): void {
|
||||
@@ -308,17 +322,32 @@ struct ReportCreatePage {
|
||||
|
||||
Column({ space: 8 }) {
|
||||
this.FieldLabel('生日', '')
|
||||
Text(`当前选择:${formatDate(this.birthday)}`)
|
||||
.fontSize(12)
|
||||
.fontColor('#999999')
|
||||
DatePicker({
|
||||
start: new Date(1930, 0, 1),
|
||||
end: new Date(),
|
||||
selected: this.birthday
|
||||
})
|
||||
.onDateChange((d: Date) => {
|
||||
this.birthday = d
|
||||
Row({ space: 12 }) {
|
||||
Text(`当前选择:${formatDate(this.birthday)}`)
|
||||
.fontSize(13)
|
||||
.fontColor('#333333')
|
||||
.layoutWeight(1)
|
||||
Text(this.showDatePicker ? '收起 ▲' : '修改 ▼')
|
||||
.fontSize(13)
|
||||
.fontColor('#C2185B')
|
||||
.onClick(() => {
|
||||
this.showDatePicker = !this.showDatePicker
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
// DatePicker 默认收起:展开态高约半屏,会把心理风格/提交按钮挤出
|
||||
// 视野并把滚动手势吃掉(真机实测:用户看不到必填项与错误提示,
|
||||
// 表现为"提交没反应")。需要改生日时才展开。
|
||||
if (this.showDatePicker) {
|
||||
DatePicker({
|
||||
start: new Date(1930, 0, 1),
|
||||
end: new Date(),
|
||||
selected: this.birthday
|
||||
})
|
||||
.onDateChange((d: Date) => {
|
||||
this.birthday = d
|
||||
})
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
@@ -407,6 +436,16 @@ struct ReportCreatePage {
|
||||
.backgroundColor('#FFFFFF')
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
|
||||
if (this.formSummary !== '') {
|
||||
Text(this.formSummary)
|
||||
.fontSize(13)
|
||||
.fontColor('#E5484D')
|
||||
.width('100%')
|
||||
.padding(10)
|
||||
.backgroundColor('#FDECEC')
|
||||
.borderRadius(8)
|
||||
.textAlign(TextAlign.Start)
|
||||
}
|
||||
if (this.submitError !== '') {
|
||||
Text(this.submitError)
|
||||
.fontSize(12)
|
||||
@@ -453,6 +492,27 @@ struct ReportCreatePage {
|
||||
}
|
||||
})
|
||||
.width('86%')
|
||||
if (this.navError !== '') {
|
||||
Text(this.navError)
|
||||
.fontSize(12)
|
||||
.fontColor('#E5484D')
|
||||
.width('86%')
|
||||
.textAlign(TextAlign.Center)
|
||||
}
|
||||
// 兜底:自动跳转若失败(或用户中途离开又回来),手动进结果页。
|
||||
// reportId 在提交成功时即已保存,轮询页始终可进。
|
||||
if (this.reportId !== '') {
|
||||
Button('查看报告')
|
||||
.fontSize(14)
|
||||
.backgroundColor('#FFFFFF')
|
||||
.fontColor('#C2185B')
|
||||
.borderRadius(20)
|
||||
.padding({ left: 24, right: 24 })
|
||||
.margin({ top: 8 })
|
||||
.onClick(() => {
|
||||
this.onTaskSucceeded()
|
||||
})
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.layoutWeight(1)
|
||||
|
||||
Reference in New Issue
Block a user