Files
FamilyBook_harmonyos/entry/src/main/ets/components/FbFormField.ets
T
laoyang 0c2cf9b5e7 表单规范全量铺开 + 编辑时视频附件不显示 (#56)
① 编辑行程的视频附件不显示

根因:照片墙对所有附件都用 Image 渲染,视频是 .mp4,
Image 加载不出来 ⇒ 空白块 ⇒ 用户以为"视频没带进来"。
按 mime 分流:video/ → 深色底 + ▶(不取首帧,端上解视频首帧很卡)。

② 新增共享组件 FbFormField.ets(GLOBAL-FORM-SPEC)

5 个表单原来各写一份 field()/labelRow(),改一处要改五处,
而且外层结构不一致(有的 Row 有的 Column),批量脚本改坏过两次。
收敛到一个组件,表单只调用不自己实现。

③ 全量铺开

- FbPersonEditSheet  姓名标必填(红色无星号)
- FbEventEditSheet   日程 field 两排
- FbMovieNewSheet    片名/影院/同看的人/海报/观影照片/简介/备注 全部两排
- FbDateTimeField    FbDateTimeRow 改两排(电影+日程同时生效)

规范:标签主题色 + Bold + fontSmall,必填红 #C0533F 无星号,
标签独占第一排、控件第二排整宽。
2026-11-05 12:52:51 +08:00

149 lines
5.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 全局表单字段组件(GLOBAL-FORM-SPEC)。
*
* 🔴 用户 2026-11-05 定的**全局规范**(原话):
*
* 「你记住,以后我们所有的功能全部都这样:
* 1. 点击名册里面的"添加",姓名之类的内容全部都按这种形式显示。
* 它会按照主题色,加粗大一点显示。**所有地方都需要这样改**。
* 2. 为了解决字数原因导致输入框左右来回对不齐的问题,我们全部按两排来显示:
* 第一排显示片名,因为是必选必填,所以用红色;下一行开始是片名的输入框,
* 以及开始时间、结束时间的输入框,全部放在下一行,这样一列下来就行了。」
*
* ## 规范
*
* | 项 | 规则 |
* |---|---|
* | 标签颜色 | **主题色**(传入的 `accent`) |
* | 标签字重 | **Bold** |
* | 标签字号 | `fontSmall`(比正文大一号) |
* | 必填标记 | **红色** `#C0533F`,**不加星号** |
* | 版式 | **标签独占第一排,控件第二排整宽** |
*
* ## 为什么做成组件而不是各表单各写一份 `@Builder`
*
* 之前 5 个表单各有一份 `field()` / `labelRow()`,改一处就要改五处,
* 而且各文件的**外层结构不一致**(有的 `Row` 有的 `Column`),
* 批量脚本改坏过两次(`@Builder` 重复、括号错乱)。
*
* ⇒ 收敛到一个组件,**表单只调用,不自己实现**:
* · 规范改一次,全站生效
* · 结构统一,不会再出现"这个表单两排、那个单排"
*
* ## 为什么两排能解决左右对齐
*
* 原来是 `标签 | 输入框` 同行,标签宽度由**字数**决定:
*
* 姓名 [__________] ← 2 字
* 我对他的称呼 [__________] ← 6 字,输入框起点更靠右
*
* 有人用 `标签.width(48)` 硬凑,但字数一变就得改数字,
* 而且旁边多个按钮(如「自动获取」)又会挤歪。
*
* 标签独占一行后,所有控件都从**同一左边缘**开始,与字数、有无侧边按钮**无关**。
*/
import { FbColors, FbMetric } from '../common/Theme';
/**
* 一行表单字段(标签 + 输入框,两排)。
*
* @example
* FbFormField({
* label: '片名',
* value: this.title,
* placeholder: '看了哪一部?',
* accent: this.accentColor(),
* required: true,
* onChange: (v: string): void => { this.title = v; }
* })
*/
@ComponentV2
export struct FbFormField {
/** 标签文字 */
@Param label: string = '';
/** 当前值 */
@Param value: string = '';
/** 占位提示(输入框为空时显示) */
@Param placeholder: string = '';
/** 主题色 —— 标签用这个颜色(`required` 时被红色覆盖) */
@Param accent: string = '#3F7D5A';
/** 是否必填 —— 必填时标签变红,**不加星号**(用户明确要求) */
@Param required: boolean = false;
/** 是否多行(简介、备注这类长文本) */
@Param multiline: boolean = false;
/** 输入框高度(多行时用) */
@Param boxHeight: number = 42;
/** 值变化回调 */
@Event onChange: (v: string) => void = (_v: string): void => {
};
/** 标签颜色:必填红,否则主题色 */
private labelColor(): string {
return this.required ? '#C0533F' : this.accent;
}
build() {
Column({ space: 6 }) {
/* ① 第一排:标签(主题色/必填红,Bold,大一号,无星号) */
Text(this.label)
.fontSize(FbMetric.fontSmall)
.fontWeight(FontWeight.Bold)
.fontColor(this.labelColor())
.width('100%')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
/* ② 第二排:输入框(整宽,与标签左边缘对齐) */
if (this.multiline) {
TextArea({ text: this.value, placeholder: this.placeholder })
.placeholderColor(FbColors.faint)
.fontSize(FbMetric.fontSmall)
.fontColor(FbColors.ink)
.backgroundColor(FbColors.inputBg)
.borderRadius(FbMetric.radiusS)
.width('100%')
.height(this.boxHeight)
.padding({ left: 12, right: 12, top: 10, bottom: 10 })
.onChange((v: string): void => {
this.onChange(v);
})
} else {
TextInput({ text: this.value, placeholder: this.placeholder })
.placeholderColor(FbColors.faint)
.fontSize(FbMetric.fontSmall)
.fontColor(FbColors.ink)
.backgroundColor(FbColors.inputBg)
.borderRadius(FbMetric.radiusS)
.width('100%')
.height(this.boxHeight)
.padding({ left: 12, right: 12 })
.onChange((v: string): void => {
this.onChange(v);
})
}
}
.width('100%')
.alignItems(HorizontalAlign.Start)
}
}
/**
* 只读的「标签 + 说明」两排块(用于"参与的人""照片"这类
* 内容由别处渲染的字段,只需要一个统一的标签样式)。
*/
@ComponentV2
export struct FbFormLabel {
@Param label: string = '';
@Param accent: string = '#3F7D5A';
@Param required: boolean = false;
build() {
Text(this.label)
.fontSize(FbMetric.fontSmall)
.fontWeight(FontWeight.Bold)
.fontColor(this.required ? '#C0533F' : this.accent)
.width('100%')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
}