fix(hair): 图片区布局修复——Stack 显式定高 320(额度胶囊 Row height(100%) 在自适应 Stack 里会被 Scroll 视口撑飞,导致图片区下坠出大片空白);裁切回 Cover 居中(人脸通常居中,顶部锚定会只显示天花板);选图/拍照用户取消静默化(13900042 不再误报「读取照片失败」)

真机走查验证:布局紧贴页签、选中描边+「试戴『×××』」按钮、试戴成功自动切效果图页签、额度 3→2 递减
This commit is contained in:
xsl
2026-09-12 13:23:54 +08:00
parent f244f14ab8
commit 1a6fde919b
3 changed files with 29 additions and 22 deletions
@@ -18,6 +18,7 @@
import { image } from '@kit.ImageKit'
import { util } from '@kit.ArkTS'
import { BusinessError } from '@kit.BasicServicesKit'
import { photoAccessHelper } from '@kit.MediaLibraryKit'
import { cameraPicker } from '@kit.CameraKit'
import { camera } from '@kit.CameraKit'
@@ -70,6 +71,12 @@ export class ImageCodec {
)
}
/** 用户取消判定:多数路径取消返回空数组,但预览态/部分 ROM 会 reject 13900042。 */
private static isUserCancel(e: Object): boolean {
const code: number | undefined = (e as BusinessError).code
return code === 13900042
}
/** 相册选 1 张。用户取消返回 undefined。 */
static async pickFromGallery(): Promise<PickedImage | undefined> {
const picker = new photoAccessHelper.PhotoViewPicker()
@@ -90,6 +97,9 @@ export class ImageCodec {
return { uri: res.photoUris[0], width: 0, height: 0 }
} catch (e) {
hilog.error(IMG_DOMAIN, IMG_TAG, 'gallery: pick failed: %{public}s', String(e))
if (ImageCodec.isUserCancel(e)) {
return undefined // 取消不报错(页面保持原状,不弹「读取照片失败」)
}
throw e as Error
}
}
@@ -109,6 +119,9 @@ export class ImageCodec {
return { uri: res.resultUri, width: 0, height: 0 }
} catch (e) {
hilog.error(IMG_DOMAIN, IMG_TAG, 'camera: pick failed: %{public}s', String(e))
if (ImageCodec.isUserCancel(e)) {
return undefined // 取消不报错
}
throw e as Error
}
}
+8 -11
View File
@@ -303,7 +303,8 @@ struct HairColorPage {
.backgroundColor(COLOR_IMAGE_BG)
.alignSelf(ItemAlign.Start)
// 图片区(单区域):无照片=上传入口;生成中=loading;按页签显示原图/效果图
// 图片区(单区域,定高 320):无照片=上传入口;生成中=loading;按页签显示原图/效果图。
// 注意 Stack 必须显式定高:内部额度胶囊 Row 用 height(100%),不定高会被 Scroll 视口撑飞
Stack({ alignContent: Alignment.BottomStart }) {
if (this.photoUri.length === 0) {
Column({ space: 6 }) {
@@ -321,11 +322,9 @@ struct HairColorPage {
.width('100%').height(320).justifyContent(FlexAlign.Center)
.borderRadius(12).backgroundColor(COLOR_IMAGE_BG)
} else if (this.imageTab === 1 && this.resultPm !== undefined) {
// 顶部对齐裁切:容器定高 clip,图片只定宽按原比缩放,头顶/发型不被居中裁掉
Column() {
Image(this.resultPm).width('100%').objectFit(ImageFit.Cover)
}
.width('100%').height(320).clip(true).borderRadius(12).backgroundColor(COLOR_IMAGE_BG)
// Cover 居中裁切:人脸通常在画面中央,头顶/下巴各让一点比只切下巴安全
Image(this.resultPm)
.width('100%').height(320).objectFit(ImageFit.Cover).borderRadius(12)
Text('效果图')
.fontSize(11).fontColor(Color.White)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
@@ -333,11 +332,8 @@ struct HairColorPage {
.backgroundColor(COLOR_BADGE_BG)
.margin(8)
} else {
// 顶部对齐裁切:同上
Column() {
Image(this.photoUri).width('100%').objectFit(ImageFit.Cover)
}
.width('100%').height(320).clip(true).borderRadius(12).backgroundColor(COLOR_IMAGE_BG)
Image(this.photoUri)
.width('100%').height(320).objectFit(ImageFit.Cover).borderRadius(12)
Text('原图')
.fontSize(11).fontColor(Color.White)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
@@ -363,6 +359,7 @@ struct HairColorPage {
}
}
.width('100%')
.height(320)
// 入口文字链:相册/拍照 + 保存(有结果才可点)
Row({ space: 16 }) {
@@ -340,7 +340,8 @@ struct HairDesignPage {
.backgroundColor(COLOR_IMAGE_BG)
.alignSelf(ItemAlign.Start)
// 图片区(单区域):无照片=上传入口;生成中=loading;按页签显示原图/效果图
// 图片区(单区域,定高 320):无照片=上传入口;生成中=loading;按页签显示原图/效果图。
// 注意 Stack 必须显式定高:内部额度胶囊 Row 用 height(100%),不定高会被 Scroll 视口撑飞
Stack({ alignContent: Alignment.BottomStart }) {
if (this.photoUri.length === 0) {
Column({ space: 6 }) {
@@ -358,11 +359,9 @@ struct HairDesignPage {
.width('100%').height(320).justifyContent(FlexAlign.Center)
.borderRadius(12).backgroundColor(COLOR_IMAGE_BG)
} else if (this.imageTab === 1 && this.resultPm !== undefined) {
// 顶部对齐裁切:容器定高 clip,图片只定宽按原比缩放,头顶/发型不被居中裁掉
Column() {
Image(this.resultPm).width('100%').objectFit(ImageFit.Cover)
}
.width('100%').height(320).clip(true).borderRadius(12).backgroundColor(COLOR_IMAGE_BG)
// Cover 居中裁切:人脸通常在画面中央,头顶/下巴各让一点比只切下巴安全
Image(this.resultPm)
.width('100%').height(320).objectFit(ImageFit.Cover).borderRadius(12)
Text('效果图')
.fontSize(11).fontColor(Color.White)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
@@ -370,11 +369,8 @@ struct HairDesignPage {
.backgroundColor(COLOR_BADGE_BG)
.margin(8)
} else {
// 顶部对齐裁切:同上
Column() {
Image(this.photoUri).width('100%').objectFit(ImageFit.Cover)
}
.width('100%').height(320).clip(true).borderRadius(12).backgroundColor(COLOR_IMAGE_BG)
Image(this.photoUri)
.width('100%').height(320).objectFit(ImageFit.Cover).borderRadius(12)
Text('原图')
.fontSize(11).fontColor(Color.White)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
@@ -400,6 +396,7 @@ struct HairDesignPage {
}
}
.width('100%')
.height(320)
// 入口文字链:相册/拍照 + 保存(有结果才可点)
Row({ space: 16 }) {