家庭物品清单全量代码与效果:ArkTS 在 HarmonyOS 的数字收纳盒


实例:家庭物品清单(Household)|收官文章
一、文件清单
| 文件 | 职责 | 行数 |
|---|---|---|
database/HouseholdDao.ets |
数据层:双表、事务借还、LEFT JOIN、16 件物品 + 5 条借用种子 | 约 385 行 |
pages/samples/HouseholdPage.ets |
UI 层:收纳格卡片 + 分类/状态筛选 + 借用记录 + 借还登记 | 约 229 行 |
resources/base/profile/main_pages.json |
路由注册:pages/samples/HouseholdPage |
追加一行 |
pages/Index.ets |
首页入口按钮 | 追加一个按钮 |
本篇文章完整展示可编译运行的 HouseholdPage 代码,最后描述运行效果。
二、HouseholdPage 完整代码
import { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
import { HouseholdDao, HouseholdItem, BorrowRecord, HouseholdOverview } from '../../database/HouseholdDao';
@Entry
@Component
struct HouseholdPage {
@State items: HouseholdItem[] = [];
@State borrows: BorrowRecord[] = [];
@State overview: HouseholdOverview = { total: 0, borrowed: 0, atHome: 0 };
@State categoryFilter: string = '全部';
@State statusFilter: number = -1;
@State borrowVisible: boolean = false;
@State borrowItem: HouseholdItem | null = null;
@State borrower: string = '';
@State note: string = '';
private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
private readonly categories: string[] = ['厨房', '家用', '工具', '数码', '书籍', '运动'];
aboutToAppear(): void {
this.refresh();
}
async refresh(): Promise<void> {
try {
await HouseholdDao.initSeedData(this.context);
this.overview = await HouseholdDao.overview(this.context);
this.borrows = await HouseholdDao.queryBorrows(this.context);
await this.applyFilter();
} catch (e) {
promptAction.showToast({ message: `加载失败: ${e}` });
}
}
async applyFilter(): Promise<void> {
this.items = await HouseholdDao.queryFilter(this.context, this.categoryFilter, '全部', this.statusFilter);
}
async switchCategory(cat: string): Promise<void> {
this.categoryFilter = cat;
await this.applyFilter();
}
async switchStatus(s: number): Promise<void> {
this.statusFilter = this.statusFilter === s ? -1 : s;
await this.applyFilter();
}
openBorrow(item: HouseholdItem): void {
this.borrowItem = item;
this.borrower = '';
this.note = '';
this.borrowVisible = true;
}
async doBorrow(): Promise<void> {
if (!this.borrower.trim() || !this.borrowItem) {
promptAction.showToast({ message: '请输入借用人' });
return;
}
const ok = await HouseholdDao.borrowItem(this.context, this.borrowItem.id, this.borrower.trim(), this.note.trim());
if (ok) {
this.borrowVisible = false;
await this.refresh();
promptAction.showToast({ message: '📤 借用已登记' });
} else {
promptAction.showToast({ message: '登记失败' });
}
}
/** 归还最近一条未还记录 */
returnBorrow(b: BorrowRecord): void {
if (b.returnTime !== 0) {
return;
}
HouseholdDao.returnItem(this.context, b.id, b.itemId).then(async () => {
await this.refresh();
promptAction.showToast({ message: '📥 已归还' });
});
}
deleteItem(item: HouseholdItem): void {
promptAction.showDialog({
title: '删除物品',
message: `删除「${item.name}」及借用记录?`,
buttons: [
{ text: '取消', color: '#808080' },
{ text: '删除', color: '#EF4444' },
],
}).then((res: promptAction.ShowDialogSuccessResponse) => {
if (res.index === 1) {
HouseholdDao.deleteItem(this.context, item.id).then(async () => {
await this.refresh();
promptAction.showToast({ message: '🗑 已删除' });
});
}
});
}
private fmtDate(ts: number): string {
const d = new Date(ts);
return `${d.getMonth() + 1}月${d.getDate()}日`;
}
build() {
Column() {
// ===== 标题栏 =====
Row() {
Column() {
Text('🏠 家庭物品清单').fontSize(22).fontWeight(FontWeight.Bold)
Text(`共 ${this.overview.total} 件 · 借出 ${this.overview.borrowed}`)
.fontSize(12).fontColor('#999999').margin({ top: 2 })
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text('↻').fontSize(22).onClick(() => this.refresh())
}.width('100%').padding({ left: 16, right: 16, top: 12 })
// ===== 分类筛选 =====
Scroll() {
Row({ space: 8 }) {
ForEach(['全部'].concat(this.categories), (cat: string) => {
Text(cat)
.fontSize(13).padding({ left: 12, right: 12, top: 6, bottom: 6 })
.borderRadius(16)
.backgroundColor(this.categoryFilter === cat ? '#F59E0B' : '#FFFFFF')
.fontColor(this.categoryFilter === cat ? Color.White : '#4B5563')
.onClick(() => this.switchCategory(cat))
}, (cat: string) => cat)
}.padding({ left: 16, right: 16, top: 10 })
}.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).width('100%')
Row({ space: 8 }) {
Text(this.statusFilter === -1 ? '📍 全部状态' : '📍 在家')
.fontSize(12).padding({ left: 10, right: 10, top: 4, bottom: 4 })
.borderRadius(12).backgroundColor('#FFFFFF').fontColor('#6B7280')
.onClick(() => this.switchStatus(0))
Text('📤 借出中')
.fontSize(12).padding({ left: 10, right: 10, top: 4, bottom: 4 })
.borderRadius(12).backgroundColor(this.statusFilter === 1 ? '#FEE2E2' : '#FFFFFF')
.fontColor(this.statusFilter === 1 ? '#DC2626' : '#6B7280')
.onClick(() => this.switchStatus(1))
}.width('94%').margin({ top: 8 })
// ===== 物品收纳格 =====
List({ space: 10 }) {
ForEach(this.items, (item: HouseholdItem) => {
ListItem() {
Row({ space: 12 }) {
Text(item.emoji).fontSize(30)
.width(52).height(52).borderRadius(12)
.backgroundColor('#FFF7E6').textAlign(TextAlign.Center)
Column({ space: 3 }) {
Row({ space: 6 }) {
Text(item.name).fontSize(15).fontWeight(FontWeight.Bold)
Text(item.status === 1 ? '借出中' : '在家')
.fontSize(10).padding({ left: 6, right: 6, top: 2, bottom: 2 })
.borderRadius(8)
.backgroundColor(item.status === 1 ? '#FEE2E2' : '#E5F6EC')
.fontColor(item.status === 1 ? '#DC2626' : '#059669')
}
Text(`${item.category} · ${item.location}`).fontSize(11).fontColor('#9CA3AF')
if (item.note) {
Text(`📌 ${item.note}`).fontSize(11).fontColor('#B45309')
}
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
if (item.status === 0) {
Button('借出').height(30).fontSize(12).backgroundColor('#F59E0B')
.onClick(() => this.openBorrow(item))
}
Text('✕').fontSize(14).fontColor('#CCCCCC').onClick(() => this.deleteItem(item))
}
.width('100%').padding(12)
.backgroundColor(Color.White).borderRadius(10)
.border({ width: 1, color: '#FDE68A' })
}
}, (item: HouseholdItem) => `${item.id}-${item.name}`)
}
.width('94%').layoutWeight(1).margin({ top: 10 })
.scrollBar(BarState.Off)
// ===== 借用记录 =====
Column() {
Text('借用记录').fontSize(15).fontWeight(FontWeight.Bold)
ForEach(this.borrows.slice(0, 5), (b: BorrowRecord) => {
Row({ space: 8 }) {
Text(b.itemName).fontSize(13).fontWeight(FontWeight.Medium).layoutWeight(1)
Text(`${b.borrower} ${this.fmtDate(b.borrowTime)}`)
.fontSize(11).fontColor('#9CA3AF')
if (b.returnTime === 0) {
Text('归还').fontSize(11).padding({ left: 8, right: 8, top: 3, bottom: 3 })
.borderRadius(10).backgroundColor('#E5F6EC').fontColor('#059669')
.onClick(() => this.returnBorrow(b))
} else {
Text('已还').fontSize(11).fontColor('#9CA3AF')
}
}
.width('100%').padding({ top: 6, bottom: 6 })
}, (b: BorrowRecord) => `${b.id}-${b.itemId}`)
}
.width('94%').padding(12).backgroundColor(Color.White).borderRadius(12)
.margin({ top: 4, bottom: 20 })
.alignItems(HorizontalAlign.Start)
// ===== 借出登记弹窗 =====
if (this.borrowVisible) {
Column() {
Text(`📤 借出「${this.borrowItem?.name ?? ''}」`).fontSize(18).fontWeight(FontWeight.Bold)
TextInput({ placeholder: '借用人 *', text: this.borrower })
.margin({ top: 12 }).onChange((v: string) => this.borrower = v)
TextInput({ placeholder: '备注(可选)', text: this.note })
.margin({ top: 8 }).onChange((v: string) => this.note = v)
Row({ space: 8 }) {
Button('取消').layoutWeight(1).backgroundColor('#EEF2F7').fontColor('#555555')
.onClick(() => this.borrowVisible = false)
Button('确认借出').layoutWeight(1).backgroundColor('#F59E0B')
.onClick(() => this.doBorrow())
}.margin({ top: 16 })
}
.padding(20).borderRadius(16).backgroundColor(Color.White).width('88%')
.position({ x: '6%', y: '30%' })
}
}
.width('100%').height('100%').backgroundColor('#FFFBEB')
}
}
三、注册与运行
main_pages.json追加"pages/samples/HouseholdPage";Index.ets追加:Button('🏠 20 家庭物品清单').fontSize(15).width('70%') .onClick(() => this.getUIContext().getRouter().pushUrl({ url: 'pages/samples/HouseholdPage' }))- 构建验证 →
BUILD SUCCESSFUL。
四、运行效果描述
进入「🏠 20 家庭物品清单」:
第一屏:标题栏「🏠 家庭物品清单 · 共 16 件 · 借出 3」;分类筛选(全部/厨房/家用/工具/数码/书籍/运动,选中橙色);状态切换(📍 全部状态 / 📤 借出中);物品列表——借出在前:破壁机 🥤「借出中」红标 + 📌 借给王阿姨 → 冲击钻 🛠️ → 《三体》📚,之后 13 件在家(绿标 + 「借出」橙钮);下方「借用记录」5 条:李哥冲击钻(最近)→ 王阿姨破壁机 → 小刘《三体》→ 表弟笔记本「已还」→ 赵叔电饭煲「已还」。
交互一(借出闭环):点羽毛球拍「借出」→ 弹窗「📤 借出「羽毛球拍」」→ 填「小明」→ 确认借出 → Toast「📤 借用已登记」→ 羽毛球拍变「借出中」红标、「借出」按钮消失、借用记录顶部多一条「小明 6月25日 归还」。
交互二(归还闭环):点刚登记的「归还」→ Toast「📥 已归还」→ 羽毛球拍回「在家」绿标、「借出」按钮回来、记录变「已还」。
交互三(筛选):点「借出中」→ 4 件红标(无借出按钮)→ 再点取消 → 16 件 → 点「工具」→ 3 件(电钻🔧/冲击钻🛠️/扳手🔩)。
交互四(删除级联):删除「冲击钻」→ 确认框「删除「冲击钻」及借用记录?」→ 确认 → 物品消失、借用记录里李哥那条也消失(级联删除)、借出 3→2。
五、代码质量要点回顾
| 关注点 | 本实例做法 |
|---|---|
| 事务 | begin/commit/rollBack 借还原子 |
| LEFT JOIN | 联表取 item_name |
| 多条件筛选 | 分类 + 位置 + 状态哨兵 |
| 借出在前 | orderByDesc(status) |
| 级联删除 | 先删借用再删物品 |
| 状态驱动 UI | 在家显示借出按钮 |
| 归还守卫 | returnTime 判断 |
六、文章小结(系列收官)
实例 「家庭物品清单」收官。五篇文章覆盖:双表借还建模(1)→ 收纳格卡片 UI(2)→ 事务与联表查询(3)→ 16 件物品种子(4)→ 全量代码(5)。核心技术是事务三件套(多表写原子性)、LEFT JOIN 联表取名、多条件哨兵筛选、级联删除。
更多推荐



所有评论(0)