尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

文件资料库全量代码与效果:ArkTS 在 HarmonyOS 的个人网盘

文件资料库全量代码与效果:ArkTS 在 HarmonyOS 的个人网盘 实例文件资料库FileLib收官文章一、文件清单文件职责行数database/FileLibDao.ets数据层LIKE 搜索、GROUP BY 统计、批量去重、18 个种子约 270 行pages/samples/FileLibPage.etsUI 层搜索框 动态筛选条 图标行列表约 160 行resources/base/profile/main_pages.json路由注册pages/samples/FileLibPage追加一行pages/Index.ets首页入口按钮追加一个按钮本篇文章完整展示可编译运行的 FileLibPage 代码最后描述运行效果。二、FileLibPage 完整代码import{common}fromkit.AbilityKit;import{promptAction}fromkit.ArkUI;import{FileLibDao,FileRecord,TypeStat}from../../database/FileLibDao;EntryComponentstruct FileLibPage{Statefiles:FileRecord[][];StatetypeFilter:string全部;Statekeyword:string;StatetotalCount:number0;StatetotalSize:number0;StatetypeStats:TypeStat[][];privatecontext:common.UIAbilityContextgetContext(this)ascommon.UIAbilityContext;privatereadonlyicons:Recordstring,string{文档:,图片:️,视频:,音频:,压缩包:️,其他:,};aboutToAppear():void{this.refresh();}asyncrefresh():Promisevoid{try{awaitFileLibDao.initSeedData(this.context);this.typeStatsawaitFileLibDao.typeStats(this.context);consttawaitFileLibDao.totalStats(this.context);this.totalCountt.count;this.totalSizet.totalSize;awaitthis.applyFilter();}catch(e){promptAction.showToast({message:加载失败:${e}});}}asyncapplyFilter():Promisevoid{if(this.keyword.trim()){this.filesawaitFileLibDao.search(this.context,this.keyword.trim());}else{this.filesawaitFileLibDao.queryByType(this.context,this.typeFilter);}}asyncswitchType(t:string):Promisevoid{this.typeFiltert;this.keyword;awaitthis.applyFilter();}onSearch(v:string):void{this.keywordv;this.applyFilter();}deleteFile(f:FileRecord):void{promptAction.showDialog({title:删除文件,message:删除「${f.name}」,buttons:[{text:取消,color:#808080},{text:删除,color:#EF4444},],}).then((res:promptAction.ShowDialogSuccessResponse){if(res.index1){FileLibDao.delete(this.context,f.id).then(async(){awaitthis.refresh();promptAction.showToast({message: 已删除});});}});}privatefmtSize(size:number):string{if(size1073741824){return(size/1073741824).toFixed(2) GB;}if(size1048576){return(size/1048576).toFixed(1) MB;}if(size1024){return(size/1024).toFixed(0) KB;}returnsize B;}privatefmtTime(ts:number):string{constdnewDate(ts);return${d.getMonth()1}月${d.getDate()}日;}build(){Column(){// 标题栏 Row(){Column(){Text(️ 文件资料库).fontSize(22).fontWeight(FontWeight.Bold)Text(${this.totalCount}个文件 · 共${this.fmtSize(this.totalSize)}).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})// 搜索框 TextInput({placeholder: 按名称 / 标签搜索…,text:this.keyword}).width(94%).height(40).margin({top:10}).backgroundColor(Color.White).borderRadius(20).onChange((v:string)this.onSearch(v))// 类型筛选 统计 Scroll(){Row({space:8}){ForEach([全部].concat(this.typeStats.map((s:TypeStat)s.fileType)),(t:string){Text(${t}).fontSize(13).padding({left:12,right:12,top:6,bottom:6}).borderRadius(16).backgroundColor(this.typeFiltert?#111827:#FFFFFF).fontColor(this.typeFiltert?Color.White:#4B5563).onClick(()this.switchType(t))},(t:string)t)}.padding({left:16,right:16,top:10})}.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).width(100%)// 文件列表 List({space:8}){ForEach(this.files,(f:FileRecord){ListItem(){Row({space:12}){Text(this.icons[f.fileType]??).fontSize(28).width(48).height(48).borderRadius(10).backgroundColor(#F3F4F6).textAlign(TextAlign.Center)Column({space:3}){Text(f.name).fontSize(15).fontWeight(FontWeight.Medium).maxLines(1)Row({space:8}){Text(f.fileType).fontSize(11).padding({left:6,right:6,top:2,bottom:2}).borderRadius(8).backgroundColor(#DBEAFE).fontColor(#3B82F6)Text(this.fmtSize(f.size)).fontSize(11).fontColor(#9CA3AF)Text(this.fmtTime(f.createdTime)).fontSize(11).fontColor(#9CA3AF)}if(f.tags){Text(${f.tags}).fontSize(11).fontColor(#9CA3AF)}}.alignItems(HorizontalAlign.Start).layoutWeight(1)Text(✕).fontSize(14).fontColor(#CCCCCC).onClick(()this.deleteFile(f))}.width(100%).padding(12).backgroundColor(Color.White).borderRadius(10)}},(f:FileRecord)${f.id}-${f.name})}.width(94%).layoutWeight(1).margin({top:10}).scrollBar(BarState.Off)}.width(100%).height(100%).backgroundColor(#F8FAFC)}}三、注册与运行main_pages.json追加pages/samples/FileLibPageIndex.ets追加Button(️ 16 文件资料库).fontSize(15).width(70%).onClick(()this.getUIContext().getRouter().pushUrl({url:pages/samples/FileLibPage}))构建验证 →BUILD SUCCESSFUL。四、运行效果描述进入「️ 16 文件资料库」第一屏标题栏「️ 文件资料库 · 18 个文件 · 共 1.7GB」白色胶囊搜索框「 按名称 / 标签搜索…」类型筛选条全部/文档/图片/视频/音频/压缩包/其他黑底选中「全部」下方 18 行文件——每行 图标浅灰底 文件名 蓝色类型胶囊 大小/日期灰字 标签行 灰色 ✕。交互一搜索搜索框输「发布」→ 列表只剩 2 行用户手册终稿.pdf、发布包 v2.3.0.zip——名称/标签 LIKE 命中 → 清空 → 恢复 18 行。交互二类型筛选点「视频」→ 3 行 功能演示录屏 120.0 MB / 发布会宣传片 350.0 MB / 培训课程录像 800.0 MB→ 点「文档」→ 4 行 。交互三搜索与筛选互斥点「压缩包」→ 4 行 ️关键词已清空→ 搜索框输「备份」→ 命中「旧版备份.zip」→ 列表只剩 1 行。交互四删除点某行 ✕ → 确认框 → 删除 → 标题栏「17 个文件 · 共 …」同步更新。五、代码质量要点回顾关注点本实例做法模糊搜索LIKE ‘%kw%’ OR tags动态筛选条typeStats 驱动选项分组统计GROUP BY COUNT SUM路径去重path UNIQUE 先查后插大小换算fmtSize 分级单位图标映射Record 类型 ?? 兜底六、文章小结实例 16「文件资料库」收官。五篇文章覆盖元数据建模与 UNIQUE 路径16-1→ 图标行文件浏览器 UI16-2→ LIKE 搜索与 GROUP BY 统计16-3→ 18 个文件种子16-4→ 全量代码16-5。核心技术是 LIKE 通配符搜索%包含匹配 OR 跨字段、GROUP BY 分组统计COUNT SUM 每类一行、path UNIQUE 约束去重、字节整数存储可聚合可换算。这是「文件元数据管理」的完整范式。下一个实例17 预算管理回归财务数据月度预算 vs 实际支出的对比——支出汇总、预算进度、超支预警UI 是进度条预算卡。七、核心代码片段补充讲解1. applyFilter搜索与筛选的互斥开关asyncapplyFilter():Promisevoid{if(this.keyword.trim()){this.filesawaitFileLibDao.search(this.context,this.keyword.trim());}else{this.filesawaitFileLibDao.queryByType(this.context,this.typeFilter);}}这段代码是整页的「数据路由」keyword非空走搜索否则走类型筛选。两个条件在 UI 上也是互斥的——switchType里this.keyword 先清空搜索词onSearch输入时筛选条虽仍高亮但结果被搜索覆盖。为什么互斥搜索是「全局命中」名称/标签跨类型筛选是「类型内浏览」同时生效会让用户困惑「当前到底在看哪个范围」。互斥后状态机只剩两种keyword ≠ → 搜索keyword → 类型——逻辑简单、无歧义。2. 动态筛选条渲染ForEach([全部].concat(this.typeStats.map((s:TypeStat)s.fileType)),(t:string){Text(${t}).backgroundColor(this.typeFiltert?#111827:#FFFFFF).fontColor(this.typeFiltert?Color.White:#4B5563).onClick(()this.switchType(t))},(t:string)t)筛选条选项不是写死的 6 个类型而是typeStatsGROUP BY 统计结果驱动——数据库里有什么类型条上就有什么选项。以后种子数据新增类型比如「代码」页面零改动自动多一个筛选胶囊。高亮是纯条件三元typeFilter t时黑底白字、否则白底灰字(t: string) t是 ForEach 的 key 生成器保证选项增删时列表 diff 稳定、不整条重绘。八、运行效果补充搜索命中高亮思路LIKE 查询只返回整行记录、不返回命中位置所以高亮必须在页面层做把文件名按关键词切段用Span分段渲染ArkUI 的Text支持子Span// 思路name.split(kw) 切段命中段用 Span 标红加粗// Text() { Span(段1); Span(kw).fontColor(#EF4444).fontWeight(FontWeight.Bold); ... }运行效果搜索「发布」后两行命中文件名的「发布」二字显示为红色加粗其余字保持黑色——用户一眼看到「为什么命中」。实现注意kw为空时直接渲染整段、不做切割避免空串 split 产生多余空段。九、代码质量要点补充关注点本实例做法收益互斥状态机applyFilter 二选一 switchType 清词逻辑简单、无歧义动态配置筛选条由 typeStats 驱动新增类型零改 UI统一刷新refresh 重置全部状态删除/清空后数据一致图标兜底?? 未知类型不崩、不空白列表 key${f.id}-${f.name}删除/更新时 diff 稳定十、FAQ问题回答为什么关键词优先于类型筛选搜索是跨类型全局命中与类型筛选语义不同互斥后状态只有两种避免「搜到的结果又被类型过滤」的歧义LIKE 搜索大小写敏感吗SQLite 的 LIKE 对 ASCII 字母不区分大小写中文无大小写、不受影响如需严格区分可用 GLOB 或 binary 比较动态筛选条为什么不用写死数组写死数组在新增类型时需同步改 UI用 typeStats 驱动后数据层加种子、UI 自动出现新胶囊搜索框 onSearch 为什么没 awaitonChange 高频触发applyFilter 内部已 try/catch 且每次覆盖式赋值不 await 避免输入卡顿最后一次结果自然胜出
返回列表