鸿蒙原生应用 ArkUI 实战:虚线照片卡 + 五件套档案表单 —— 商品建档页的录入架构
鸿蒙原生应用 ArkUI 实战:虚线照片卡 + 五件套档案表单 —— 商品建档页的录入架构
App 53「保修期管理」商品页(Func1Tab),主题色
#00A862生态绿,4 个 Tab 分别为首页(🔧)、商品(📦)、保修(⏰)、我的(👤)。本篇基于53-warranty-mgr/entry/src/main/ets/pages/Func1Tab.ets(共 106 行)逐段拆解,附 4 张实机截图。

一、整体结构:三区"Header + 上传卡 + 表单"布局
商品页是"照片采集 + 档案表单"的组合布局——3 个 @Builder 块:
build() {
Column() {
this.Header()
Scroll() {
Column({ space: 16 }) {
this.PhotoZone()
this.FormCard()
Button('保存商品').fontSize(15).fontColor('#FFFFFF').backgroundColor(C.primary)
.width('100%').height(48).borderRadius(D.rMd)
.onClick(() => { promptAction.showToast({ message: this.name.length > 0 ? '商品已保存' : '请填写商品名称' }); })
}
.width('100%')
.padding({ left: D.pad, right: D.pad, top: 16, bottom: D.pad + this.safeBottom + 20 })
}
.layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)
}
.width('100%').height('100%').backgroundColor(C.bg)
}
3 块结构:PhotoZone(全宽虚线照片上传卡)→ FormCard(五件套档案表单)→ 保存按钮。商品页是"建档页"——先上传凭证(照片/发票),再填五件套(名称/型号/日期/分类/保修期限),最后保存。凭证在上、档案在下,模拟真实购买登记流程:买了新设备 → 拍下发票 → 记录型号 → 设保修期限 → 存档。
二、五状态声明 + 数据模型
@State name: string = '';
@State model: string = '';
@State buyDate: string = '';
@State activeCat: number = 0;
@State activeWarranty: number = 1;
private cats: string[] = ['数码', '家电', '家具', '汽车', '其他'];
private warranties: string[] = ['1年', '2年', '3年', '自定义'];
五个 @State:name(商品名称)、model(型号)、buyDate(购买日期)、activeCat(分类索引,默认 0 = 数码)、activeWarranty(期限索引,默认 1 = 2年)。cats 5 分类 + warranties 4 期限——两个独立选择维度。期限默认 2 年是中位数默认值(多数数码产品保修两年)。
三、Header:单行标题
Header() {
Row() { Text('添加商品').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.text) }
.width('100%').height(this.safeTop + 56).padding({ top: this.safeTop, left: D.pad, right: D.pad })
.backgroundColor(C.card).alignItems(VerticalAlign.Bottom)
}
20 号加粗、safeTop + 56 固定高、文字贴底——系列功能页 Header 统一范式。标题"添加商品"是动词短语(动作型标题),暗示"这是一个待完成的操作",比名词标题("商品")更有引导性。
四、PhotoZone:全宽虚线照片上传卡
PhotoZone() {
Column({ space: 8 }) {
Text('📷').fontSize(40)
Text('上传商品照片/发票').fontSize(13).fontColor(C.primary)
}
.width('100%').padding({ top: 24, bottom: 24 }).backgroundColor(C.card).borderRadius(D.rLg)
.border({ width: 1, color: C.primary, style: BorderStyle.Dashed })
.onClick(() => { promptAction.showToast({ message: '上传照片' }); })
}
逐点拆解:
- 全宽竖向卡:
width('100%')+padding({ top: 24, bottom: 24 })——整行一个上传区(与 App 52 的双卡横向排布不同,本页是单卡全宽),竖向居中布局(40 号图标 + 13 号文字上下排列)。 - 绿色虚线边框:
BorderStyle.Dashed+C.primary绿——虚线 = "可点击上传区"的视觉语言(与 App 52 的蓝色虚线同手法、异色系)。 - 40 号大图标:📷 是全卡视觉主体——比 App 52 的 36 号更大,单卡设计让图标有更大展示空间。
- 主色文字:13 号
C.primary绿字"上传商品照片/发票"——照片 + 发票双凭证(保修凭证通常是发票,照片是商品实拍)。 - 大圆角:
borderRadius(D.rLg = 20)——上传卡用最大圆角(比列表卡 14 更大),让"功能卡"从表单中突出。

五、FormCard:五件套档案表单
FormCard() {
Column({ space: 14 }) {
Column({ space: 6 }) {
Text('商品名称').fontSize(13).fontColor(C.textSub).width('100%')
TextInput({ placeholder: '例如:iPhone 15 Pro' })
.backgroundColor(C.cardSoft).borderRadius(D.rSm).height(44)
.onChange((v: string) => { this.name = v; })
}
Column({ space: 6 }) {
Text('型号规格').fontSize(13).fontColor(C.textSub).width('100%')
TextInput({ placeholder: '输入型号' })
.backgroundColor(C.cardSoft).borderRadius(D.rSm).height(44)
.onChange((v: string) => { this.model = v; })
}
Column({ space: 6 }) {
Text('购买日期').fontSize(13).fontColor(C.textSub).width('100%')
TextInput({ placeholder: '2026-08-14' })
.backgroundColor(C.cardSoft).borderRadius(D.rSm).height(44)
.onChange((v: string) => { this.buyDate = v; })
}
前三件套:
- 商品名称:
TextInput44 高淡绿灰底圆角 10,onChange绑定this.name——保存按钮校验的锚点字段。 - 型号规格:记录商品具体型号(如 A2896),保修时需要凭型号查询售后信息。
- 购买日期:日期格式占位符(年-月-日),保修期限从购买日算起,是"剩余天数"的计算起点。
Column({ space: 6 }) {
Text('商品分类').fontSize(13).fontColor(C.textSub).width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.cats, (c: string, idx: number) => {
Text(c).fontSize(12)
.fontColor(this.activeCat === idx ? '#FFFFFF' : C.textSub)
.padding({ left: 14, right: 14, top: 7, bottom: 7 })
.backgroundColor(this.activeCat === idx ? C.primary : C.cardSoft).borderRadius(14)
.margin({ right: 10, bottom: 10 })
.onClick(() => { this.activeCat = idx; })
}, (c: string) => c)
}.width('100%')
}
第四件套——商品分类 5 胶囊:Flex({ wrap: FlexWrap.Wrap }) 换行胶囊——数码/家电/家具/汽车/其他,选中态绿底白字、未选灰底灰字,圆角 14、margin({ right: 10, bottom: 10 }) 间距;默认选中第一项"数码"。
Column({ space: 6 }) {
Text('保修期限').fontSize(13).fontColor(C.textSub).width('100%')
Row({ space: 10 }) {
ForEach(this.warranties, (w: string, idx: number) => {
Text(w).fontSize(12)
.fontColor(this.activeWarranty === idx ? '#FFFFFF' : C.textSub)
.textAlign(TextAlign.Center).layoutWeight(1).padding({ top: 9, bottom: 9 })
.backgroundColor(this.activeWarranty === idx ? C.primary : C.cardSoft).borderRadius(D.rSm)
.onClick(() => { this.activeWarranty = idx; })
}, (w: string) => w)
}.width('100%')
}
第五件套——保修期限 4 等分胶囊:Row + layoutWeight(1) 四等分 + textAlign(TextAlign.Center) 居中——1年/2年/3年/自定义一行排开。与分类胶囊的换行布局不同,期限选项少且短,一行等分排开更高效。同一页两种胶囊布局,按选项数量与长度分别适配。

六、保存按钮:三元校验
Button('保存商品').fontSize(15).fontColor('#FFFFFF').backgroundColor(C.primary)
.width('100%').height(48).borderRadius(D.rMd)
.onClick(() => { promptAction.showToast({ message: this.name.length > 0 ? '商品已保存' : '请填写商品名称' }); })
逐点拆解:
- 全宽绿色 CTA:
width('100%').height(48)+C.primary——表单页标准 CTA 形态。 - 三元校验:
this.name.length > 0 ? '商品已保存' : '请填写商品名称'——以名称为校验锚点(商品名称是建档的核心字段,没有名字的档案没有意义),非空 → 保存成功、为空 → 提示填写。 - 与 App 52 的差异:52 校验金额(票据核心字段是钱),53 校验名称(商品核心字段是名字)——两个应用的校验锚点都指向各自最关键的字段。

七、系列横向对比:录入页的两种表单形态
| 录入页 | 代表 App | 录入方式 | 核心特色 | 主题色 |
|---|---|---|---|---|
| 全宽照片卡 + 五件套 | App 53 商品 | 凭证 + 档案 | 双胶囊布局 | #00A862 生态绿 |
| 双虚线卡 + 四件套 | App 52 上传 | 双来源 + 财务 | 金额校验 | #0066CC 科技蓝 |
| 动态选项表单 | App 49 投票 | 纯表单 | 动态增删选项 | #6C5CE7 紫 |
差异点:App 53 的"双胶囊布局"是系列中表单设计的精细案例——分类 5 项较长用 Flex 换行、期限 4 项较短用 Row 等分,按选项数量与长度分别适配布局;而 App 52 的两组胶囊都用 Flex 换行(类型 4 项 + 分类 6 项都较多)。
八、开发者视角:调试与验证技巧
- 日期输入校验:购买日期用
TextInput自由输入——产品化必须加格式校验(正则匹配年-月-日),否则用户输错格式会导致保修期限计算错误;更优方案是换 DatePicker 日期选择器。 - 期限与到期日联动:当前期限胶囊只存选中索引,没有联动计算到期日——产品化时应
到期日 = 购买日期 + 期限,并在表单中实时预览剩余天数。 - 默认值语义:分类默认"数码"、期限默认"2年"——默认值要符合多数场景,但"自定义"期限选中时需弹出输入框(1 到 99 年),当前版本未实现自定义输入逻辑。
- 胶囊选中态公式:两组胶囊选中 = 白字绿底、未选 = 灰字灰底——与 App 52 完全一致,系列表单的统一交互范式。
- Scroll 与按钮可达性:表单较长时保存按钮可能在屏幕外——当前 Scroll 包裹保证按钮可滚动到达,
safeBottom内边距已处理底部安全区。
九、演进方向
| 方向 | 当前状态 | 产品化演进 |
|---|---|---|
| 照片上传 | Toast"上传照片" | PhotoViewPicker 选图 + 多图上传 |
| 日期输入 | TextInput 自由输入 | DatePicker 日期选择器 |
| 期限联动 | 仅存选中索引 | 到期日 = 购买日 + 期限 实时预览 |
| 自定义期限 | 未实现 | 弹出输入框(1-99 年) |
| 保存反馈 | Toast"商品已保存" | 返回首页刷新 + 新商品置顶 |
十、一句话总结
商品页以"全宽虚线照片卡 + 五件套档案表单 + 校验保存"完成了商品的建档闭环。BorderStyle.Dashed 绿色虚线边框暗示可点击上传区、Flex 换行胶囊(5 分类)+ Row 等分胶囊(4 期限)双布局按选项适配、三元校验 name.length > 0 ? '商品已保存' : '请填写商品名称' 以名称为锚点——106 行代码,一个"拍凭证 → 填档案 → 设期限 → 校验保存"的商品建档页,也是"凭证 + 档案"双录型表单的标准范式。

更多推荐





所有评论(0)