鸿蒙项目实战 - 资料征集分发台:完整代码与运行效果
·


实例:资料征集分发台|技术:完整源码、路由接入、首页入口、运行核对
一、完整源码
import { ColumnLayoutAlgorithm, DynamicLayout, DynamicLayoutAttribute, GridLayoutAlgorithm, LayoutAlgorithm, LengthMetrics, curves } from '@kit.ArkUI';
@ComponentV2
struct CollectNoticeCard {
@Local title: string = '批次 A 通知';
@Local note: string = '先发给东区、北区和设备组。';
build() {
Column({ space: 10 }) {
Text('征集通知').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#111827')
TextInput({ text: this.title, placeholder: '通知标题' })
.backgroundColor('#F8FAFC')
.borderRadius(10)
.onChange((value: string) => this.title = value)
TextArea({ text: this.note, placeholder: '发送范围' })
.height(86)
.backgroundColor('#F8FAFC')
.borderRadius(10)
.onChange((value: string) => this.note = value)
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(18)
}
}
@ComponentV2
struct CollectReceiveCard {
@Local count: number = 18;
@Local note: string = '上午已签收 12 份,下午继续补收。';
build() {
Column({ space: 10 }) {
Text('签收记录').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#111827')
Row() {
Text('已到件数').fontSize(12).fontColor('#64748B')
Blank()
Text(`${Math.round(this.count)} 份`).fontSize(14).fontWeight(FontWeight.Medium).fontColor('#0F766E')
}.width('100%')
Slider({ value: this.count, min: 0, max: 40, step: 1 })
.selectedColor('#14B8A6')
.trackColor('#CCFBF1')
.blockColor('#0F766E')
.onChange((value: number) => this.count = value)
TextArea({ text: this.note, placeholder: '签收说明' })
.height(86)
.backgroundColor('#F8FAFC')
.borderRadius(10)
.onChange((value: string) => this.note = value)
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(18)
}
}
@ComponentV2
struct CollectMissingCard {
@Local urgent: boolean = true;
@Local note: string = '仍缺封面页、签章页和电子留档页。';
build() {
Column({ space: 10 }) {
Text('缺件提醒').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#111827')
Row() {
Text('是否加急').fontSize(12).fontColor('#64748B')
Blank()
Toggle({ type: ToggleType.Switch, isOn: this.urgent })
.selectedColor('#DC2626')
.onChange((value: boolean) => this.urgent = value)
}.width('100%')
TextArea({ text: this.note, placeholder: '缺件项目' })
.height(102)
.backgroundColor('#F8FAFC')
.borderRadius(10)
.onChange((value: string) => this.note = value)
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(18)
}
}
@ComponentV2
struct CollectPackageCard {
@Local owner: string = '夏打包';
@Local note: string = '按楼层顺序打包,便于下午统一分发。';
build() {
Column({ space: 10 }) {
Text('批次打包').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#111827')
TextInput({ text: this.owner, placeholder: '打包负责人' })
.backgroundColor('#F8FAFC')
.borderRadius(10)
.onChange((value: string) => this.owner = value)
TextArea({ text: this.note, placeholder: '打包说明' })
.height(86)
.backgroundColor('#F8FAFC')
.borderRadius(10)
.onChange((value: string) => this.note = value)
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(18)
}
}
@ComponentV2
struct CollectVerifyCard {
@Local done: boolean = false;
@Local note: string = '回传校验前,先检查页码、顺序和签章完整度。';
build() {
Column({ space: 10 }) {
Text('回传校验').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#111827')
Row() {
Text('是否完成').fontSize(12).fontColor('#64748B')
Blank()
Toggle({ type: ToggleType.Switch, isOn: this.done })
.selectedColor('#2563EB')
.onChange((value: boolean) => this.done = value)
}.width('100%')
TextArea({ text: this.note, placeholder: '校验说明' })
.height(102)
.backgroundColor('#F8FAFC')
.borderRadius(10)
.onChange((value: string) => this.note = value)
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(18)
}
}
@Entry
@ComponentV2
struct CollectionDeskPage {
@Local currentMode: string = '双列';
@Local layoutAlgorithm: LayoutAlgorithm = new GridLayoutAlgorithm({
columnsTemplate: '1fr 1fr',
rowsGap: LengthMetrics.vp(12),
columnsGap: LengthMetrics.vp(12)
});
private switchToSingle(): void {
this.getUIContext()?.animateTo({ curve: curves.springMotion(0.45, 0.82) }, () => {
this.layoutAlgorithm = new ColumnLayoutAlgorithm({
space: LengthMetrics.vp(12)
});
this.currentMode = '单列';
});
}
private switchToDouble(): void {
this.getUIContext()?.animateTo({ curve: curves.springMotion(0.45, 0.82) }, () => {
this.layoutAlgorithm = new GridLayoutAlgorithm({
columnsTemplate: '1fr 1fr',
rowsGap: LengthMetrics.vp(12),
columnsGap: LengthMetrics.vp(12)
});
this.currentMode = '双列';
});
}
private switchToTriple(): void {
this.getUIContext()?.animateTo({ curve: curves.springMotion(0.45, 0.82) }, () => {
this.layoutAlgorithm = new GridLayoutAlgorithm({
columnsTemplate: '1fr 1fr 1fr',
rowsGap: LengthMetrics.vp(10),
columnsGap: LengthMetrics.vp(10)
});
this.currentMode = '三列';
});
}
build() {
Column() {
Column({ space: 4 }) {
Text('🗃️ 62 资料征集分发台').fontSize(22).fontWeight(FontWeight.Bold)
Text('同一组资料卡片可在单列、双列、三列三种 DynamicLayout 模式之间切换,适合不同阶段的收件与分发视图。')
.fontSize(12).fontColor('#64748B').lineHeight(18)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.padding({ left: 16, right: 16, top: 14, bottom: 10 })
Row({ space: 8 }) {
Button('单列').height(36).backgroundColor(this.currentMode === '单列' ? '#111827' : '#E5E7EB')
.fontColor(this.currentMode === '单列' ? '#FFFFFF' : '#334155')
.onClick(() => this.switchToSingle())
Button('双列').height(36).backgroundColor(this.currentMode === '双列' ? '#111827' : '#E5E7EB')
.fontColor(this.currentMode === '双列' ? '#FFFFFF' : '#334155')
.onClick(() => this.switchToDouble())
Button('三列').height(36).backgroundColor(this.currentMode === '三列' ? '#111827' : '#E5E7EB')
.fontColor(this.currentMode === '三列' ? '#FFFFFF' : '#334155')
.onClick(() => this.switchToTriple())
}
.width('100%')
.padding({ left: 16, right: 16, bottom: 10 })
Scroll() {
DynamicLayout(this.layoutAlgorithm) {
CollectNoticeCard()
CollectReceiveCard()
CollectMissingCard()
CollectPackageCard()
CollectVerifyCard()
}
.width('100%')
.padding({ left: 16, right: 16, bottom: 24 })
}
.layoutWeight(1)
.scrollBar(BarState.Off)
}
.width('100%')
.height('100%')
.backgroundColor('#F4F6F8')
}
}
二、路由接入
"pages/api24_series/CollectionDeskPage",
三、首页入口
Button('🗃️ 62 资料征集分发台')
.fontSize(14)
.width('80%')
.onClick(() => {
this.getUIContext().getRouter().pushUrl({ url: 'pages/api24_series/CollectionDeskPage' });
})
四、运行核对表
| 操作 | 应看到的界面表现 | 要核对的代码点 |
|---|---|---|
| 切单列 | 卡片纵向展开,更适合逐项录入 | switchToSingle() 是否替换成列算法 |
| 切双列 | 卡片并排展示,输入状态保留 | switchToDouble() 是否恢复双列模板 |
| 切三列 | 卡片密度更高,仍可继续编辑 | switchToTriple() 是否使用三列模板 |
五、落地检查点
- 完整源码里最关键的是三个切换函数与
DynamicLayout的关系。 - 路由接入后,至少手动切一次单列、双列、三列,验证模式按钮和内容区同步。
- 增加资料卡数量后,继续检查滚动区和三列模式下的最小可编辑宽度。
六、本篇小结
第三篇保留完整源码,方便把这一页当成多视图资料页模板继续扩展到回收、分发、校验等相近场景。
更多推荐

所有评论(0)