【华夏二十四节气|07】HarmonyOS 6.0.2(22) ArkTS 节气搜索实战:多字段匹配与四态闭环
本文唯一核验标记:
AGC18-HMOS-15-07-SEARCH-POEM-GAP
证据边界: 本文的"当前实现"以本轮复核的SearchPage.ets、SolarTermModel.ets、SolarTermService.ets与solar_terms.json为准;文中构建通过记录属于历史证据,本轮未重新执行构建,不将其表述为新的构建结果。架构扩展和性能优化段落均为建议实现,不代表当前工程已经具备对应能力。
二十四节气只有 24 条主记录,搜索不需要服务器、倒排索引或复杂数据库,但"小数据"并不等于可以忽略搜索合同。用户输入"饺子"时希望命中冬至美食,输入诗句时希望找到对应节气,输入空格时不应看到莫名其妙的空页;加载失败、无结果和返回详情也都需要明确状态。
本文基于真实 HarmonyOS 工程 D:\huawei\one5(华夏二十四节气),复核 SearchPage.ets、SolarTermModel.ets 与 SolarTermService.ets。审计开始时,源码只检索名称、摘要、描述、习俗、食物和养生字段;本轮已经把诗词、物候、加载态、错误态、空状态、清除入口和结果数量合入 SearchPage.ets,并以 assembleHap 通过 ArkTS 编译。下文保留修复前证据,同时给出修复后的复核输出。

一、搜索对象来自本地 rawfile
SolarTermService 首次加载:
ts
const buf: Uint8Array =
await rm.getRawFileContent('solar_terms.json');
const decoder = util.TextDecoder.create('utf-8');
const text = decoder.decodeToString(buf);
this.bundle = JSON.parse(text) as SolarTermBundle;
搜索页不访问网络,而是从应用包内的 solar_terms.json 取得数据。加载完成后使用内存数组过滤,因此断网不会阻止搜索。
这也限定了结果范围:搜索只能命中当前安装包携带的节气内容,不能声称检索互联网诗词库或实时资讯。
二、数据模型决定可搜索字段
真实 SolarTerm 包含:
ts
export interface SolarTerm {
id: string;
order: number;
name: string;
season: Season;
summary: string;
description: string;
phenology: string[];
customs: string[];
food: string[];
health: string[];
poems: SolarTermPoem[];
}
搜索字段应该根据用户意图选择,而不是把对象序列化后做一次粗糙匹配。名称、简介、习俗、美食、养生、物候与诗词都有不同展示价值。
id、顺序和季节是结构字段,是否参与搜索需要明确产品需求。当前页面没有检索这些字段。
三、页面加载完成后保存全部节气
SearchPage 维护:
ts
@State keyword: string = '';
@State loading: boolean = true;
@State allTerms: SolarTerm[] = [];
在 aboutToAppear() 调用 loadData(),等待服务加载后把 all() 结果赋给 allTerms。关键词变化触发重新构建,matchedTerms() 从这份内存数组计算结果。
当前记录只有 24 条,直接保存全部内容并同步过滤是合适的最小方案,不需要分页接口或后台 Worker。
四、真实搜索入口是 TextInput
搜索栏使用:
ts
TextInput({
placeholder: '搜索节气、习俗、美食...',
text: this.keyword
})
.onChange((value: string) => {
this.keyword = value;
})
用户每次输入都会更新 @State keyword。页面没有"搜索"按钮,也没有提交动作;体验属于即时过滤。
对于 24 条本地记录,即时过滤延迟很小。若以后数据增长到数万条,再考虑防抖、预索引或后台计算,而不是提前增加复杂度。
五、空关键词返回全部 24 条
真实逻辑先执行:
ts
const k = this.keyword.trim();
if (!k) return this.allTerms;
空字符串或只包含空格的输入都会展示全部节气。这个行为使搜索页也可以作为节气索引页,而不是空白初始页。
如果产品希望初始状态显示"输入关键词开始搜索",就需要单独定义 idle 状态。不能一边返回全部数据,一边在文案中声称默认不展示内容。
六、当前匹配采用精确子串
每个字段通过:
ts
text.indexOf(k) >= 0
判断关键词是否是原字符串的连续子串。中文名称、短语和食物名适合这种策略,行为直观、可预测。
它不支持拼音、错别字纠正、同义词、分词组合或相关度排序。输入"清 明"也不会自动命中"清明",这些都不是当前源码能力。
七、名称、摘要和描述已经覆盖
真实条件首先检查:
ts
t.name.indexOf(k) >= 0
|| t.summary.indexOf(k) >= 0
|| t.description.indexOf(k) >= 0
名称适合精确定位节气,摘要和描述则允许用户用内容概念搜索。例如数据里出现的气候、农事或季节性词汇,可以通过简介命中。
但结果行只展示 name 与 summary,若命中发生在 description,用户未必知道为什么出现。高亮或命中来源提示可以改善可解释性。
八、习俗、食物和养生使用 some
数组字段通过:
ts
t.customs.some(s => s.indexOf(k) >= 0)
|| t.food.some(s => s.indexOf(k) >= 0)
|| t.health.some(s => s.indexOf(k) >= 0)
任意元素命中,就保留整个节气。搜索"踏青""春饼"或某种养生建议时,不需要先把数组拼成大字符串。
some() 在找到第一个匹配项后即可停止,语义也清楚。对 24 条数据,这种写法比建立复杂索引更容易维护和测试。
九、初版诗词搜索缺口与修复结果
SolarTerm 明确定义:
ts
export interface SolarTermPoem {
title: string;
author: string;
content: string;
}
初版 matchedTerms() 没有读取 t.poems。因此输入诗名、作者或诗句不会因为诗词字段而命中,除非同一关键词偶然出现在摘要或描述中。
本轮已经完成代码改造,并通过补丁后的源码静态复核与 assembleHap 构建。这里保留初版缺口,是为了让读者理解修复动机,而不是把它描述成当前版本的未完成项。
十、补齐诗名、作者和正文
可复用的纯函数写法如下:
ts
private poemMatches(
poems: SolarTermPoem[],
keyword: string
): boolean {
return poems.some((poem: SolarTermPoem) =>
poem.title.indexOf(keyword) >= 0
|| poem.author.indexOf(keyword) >= 0
|| poem.content.indexOf(keyword) >= 0
);
}
当前源码采用同等语义的内联 some() 条件,已经把诗名、作者与正文加入过滤。诗词是结构化对象数组,必须分别检查标题、作者和正文,不能把对象直接转字符串。
回归用例应选择只出现在诗句而不出现在其他字段的关键词,避免测试出现假阳性。
十一、物候字段已经纳入匹配合同
模型里有 phenology: string[]。初版过滤条件遗漏了它,本轮已经将物候数组纳入匹配合同,用户输入真实物候词时可以返回对应节气。
当前实现与习俗字段一致:
ts
|| t.phenology.some(
(item: string) => item.indexOf(k) >= 0
)
代码已经支持物候匹配。后续若在占位文案中直接写出"物候",还应结合窄屏宽度验证文字是否截断;这属于界面文案优化,不影响本轮搜索合同。
十二、把搜索合同集中到一个方法
继续把条件写在页面里会越来越长。可以将单条匹配提取为:
ts
private matches(t: SolarTerm, k: string): boolean {
return this.includes(t.name, k)
|| this.includes(t.summary, k)
|| this.includes(t.description, k)
|| this.arrayIncludes(t.customs, k)
|| this.arrayIncludes(t.food, k)
|| this.arrayIncludes(t.health, k)
|| this.arrayIncludes(t.phenology, k)
|| this.poemMatches(t.poems, k);
}
这样字段清单成为可审查合同,单元测试也能逐字段覆盖。页面的 matchedTerms() 只负责 trim 与 filter。

十三、当前搜索没有大小写归一化
节气内容主要是中文,但模型中可能出现英文、拉丁符号或 ID。indexOf() 区分大小写,Spring 与 spring 不等价。
如果决定支持拉丁文本,可以在比较两端执行:
ts
const normalized =
value.toLocaleLowerCase().normalize('NFKC');
归一化策略要保持一致,并验证目标 HarmonyOS ArkTS 运行环境支持的字符串方法。中文不需要无意义地转小写,但全角半角与兼容字符仍可能受益。
十四、不要擅自删除正文中的空格
trim() 只移除关键词两端空白,不会删除中间空格。这个行为保护了诗句和短语的原始结构。
如果为了"更宽松匹配"把所有空格都删除,可能产生意外命中,也会改变英文作者名和标点语义。更稳妥的方式是明确提供"连续子串"合同。
需要支持多关键词时,应先定义 AND、OR 和排序规则,再实现分词,不能简单 split(' ') 后随意组合。
验收时应准备含前后空格、连续空格、中文标点和英文作者名的关键词,分别记录标准化前后结果,确保规则没有破坏诗词原文或产生无法解释的宽泛命中。
十五、当前结果顺序保持原始节气顺序
filter() 不改变未删除元素的相对顺序,因此结果沿用 solar_terms.json 的数组顺序,通常对应节气序。
当前没有相关度评分:名称命中与描述命中的位置一样,不会把更精确结果置顶。对于 24 条数据,这可能足够;若要排序,可定义名称精确匹配、名称包含、数组字段、长描述的分值。
排序必须稳定,分值相同的结果仍应按节气顺序展示。
若加入相关度排序,测试应同时断言分值与次级排序,不要只看首条结果;否则数据内容微调后,同分结果可能在不同构建中出现顺序漂移。
十六、匹配函数在每次构建时执行
ResultList 直接调用:
ts
ForEach(this.matchedTerms(), ...)
关键词状态变化会触发构建并重新扫描所有字段。24 条数据与短文本下,计算量很小,无需缓存。
若内容扩大,应该先用性能分析确认重建成本,再考虑把结果维护为 @State、添加防抖或预先生成规范化搜索文档。过早缓存会增加状态同步风险。
十七、loading 状态已经进入四态渲染
虽然页面有:
ts
@State loading: boolean = true;
初版 loadData() 会切换它,但 ResultList() 没有对应分支。修复后,ResultList() 首先判断 this.loading,显示 LoadingProgress 和"正在加载节气数据",不会把首次加载误呈现为空结果。
当前实现让状态变量与可见 UI 保持一致。即使 rawfile 很小,也保留了可测试的加载态,为未来数据量增长和解析耗时变化留下明确行为。
十八、try/catch/finally 建立错误与重试闭环
初版加载只有 try/finally。本轮实现调整为:
ts
try {
await SolarTermService.instance.ensureLoaded(getContext(this));
this.allTerms = SolarTermService.instance.all().slice();
} catch (_) {
this.allTerms = [];
this.errorMessage = '节气数据加载失败,请重试';
} finally {
this.loading = false;
}
JSON 解析或资源读取失败时,页面进入 error 分支并提供"重新加载"入口。这样 loading / error / empty / content 四态拥有独立判定,加载失败不会伪装成"没有搜索结果"。
十九、无结果空状态与恢复入口
修复后,关键词无法命中任何节气时会进入专用空状态:
ts
if (this.matchedTerms().length === 0) {
EmptyView({
text: '未找到相关节气'
})
}
当前页面同时显示查询词和"清除关键词"入口,用户可以一步恢复全部 24 条结果。若后续扩大数据规模,可把结果在一个方法中计算后传给 Builder,减少同一轮构建中的重复过滤。
二十、搜索框清除与结果数量闭环
当前 TextInput 右侧会在关键词非空时显示"清除",点击后重置 keyword 与导航提示。用户无需逐字删除,即可回到全部节气。
结果区域显示"共 N 条结果",用户可以直接判断关键词是否过宽;提示位于列表顶部,没有增加额外卡片层级。
清除操作应同时恢复输入、结果和键盘焦点,不能只把视觉文本清空却保留旧状态。
清除按钮还应具备可访问性说明与稳定触控尺寸,并只在关键词非空时显示或启用;自动化测试可断言清除后 keyword 为空且结果数量恢复为全部节气。
二十一、命中原因需要可解释
结果行只显示:
ts
Text(t.name)
Text(t.summary)
若关键词命中 food 或诗词正文,summary 中可能没有该词。用户会疑惑结果为何出现。
可以返回:
ts
interface SearchHit {
term: SolarTerm;
field: 'name' | 'custom' | 'food' | 'poem' | 'other';
snippet: string;
}
列表第二行显示命中片段,并高亮关键词。这样补齐诗词后,诗句命中才真正可见,而不只是过滤数组里的隐藏逻辑。
二十二、高亮必须处理重复和边界
简单高亮可以用 indexOf() 找到第一个位置,拆成前、中、后三段。关键词为空时不能进入拆分,否则会产生无意义片段。
多次出现时,要决定只高亮首处还是全部;长文本应先生成围绕命中的短 snippet,再设置 maxLines 和省略。
ArkUI 可以用 Span 组合不同样式,但构造富文本应放在 helper 中,保持 Builder 声明式。
高亮测试应包含关键词位于开头、结尾、重复出现以及包含正则特殊字符的情况;实现若只使用 indexOf(),就不应引入不必要的正则转义风险。
二十三、结果摘要缺少溢出约束
当前 Text(t.summary) 没有显式:
ts
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
真实数据摘要若变长,列表行高度会扩大,影响扫描效率。在手机横屏、小窗口或系统字体放大时更明显。
名称列使用 layoutWeight(1) 的父容器是正确基础,再补充行数与溢出策略即可提升适配稳定性。
二十四、路由参数使用稳定 ID
点击结果行时构造:
ts
const params: TermDetailParams = { termId: t.id };
router.pushUrl({
url: Routes.TermDetail,
params
})
页面不传整份对象,而是让详情页用稳定 ID 回查内容。这与收藏、历史模块使用同一身份策略。
结果排序或高亮变化不会影响路由合同,rawfile 内容更新也不需要修改页面间大对象传递。
二十五、路由异常已经提供可见反馈
初版源码末尾使用空 catch,点击失败时没有反馈。本轮改为设置页面状态:
ts
router.pushUrl({ url: Routes.TermDetail, params }).catch(() => {
this.navigationMessage = '详情页打开失败,请重试';
});
结果数量栏会同步显示提示,用户可以继续搜索或重试。后续若接入日志,应只记录公共错误码、路由名与 termId,不写入完整节气内容。
二十六、ForEach Key 使用 t.id
ts
(t: SolarTerm) => t.id
稳定 ID 作为 Key 能让 ArkUI 在过滤变化时识别同一节气行。它优于数组索引,因为删除前部结果不会改变其他行身份。
这要求数据源保证 ID 唯一。加载 solar_terms.json 时应验证重复 ID,否则列表复用和详情路由都会出现不确定行为。
二十七、all() 暴露了服务内部数组
SolarTermService.all() 当前返回:
ts
return this.bundle ? this.bundle.terms : [];
SearchPage 只读取并过滤,没有修改它,因此真实路径没有破坏缓存。但公共调用者若执行 sort() 或 splice(),会直接改变服务内部顺序。
更安全的服务可以返回 slice() 或只读数组。小数据复制成本低,能加强数据所有权。
二十八、数据加载也需要结构校验
JSON.parse(text) as SolarTermBundle 只是类型断言,运行时不会验证 terms 是否数组,也不会验证 customs、food 和 poems 是否存在。
搜索大量调用字符串方法和 some(),坏数据可能直接导致构建异常。加载边界应校验每个可搜索字段,缺失的可选数组可规范化为空数组。
错误应停留在 Service 层并映射为页面 error 状态,而不是让某一个异常节气拖垮整个页面。

二十九、字段权重比模糊算法更重要
若要改善排序,先建立简单可解释的权重:
text
名称完全相等 100
名称包含 80
习俗/食物/诗词 50
摘要/描述/养生 30
分值相同按 order 排序。用户能理解"清明"排在名称只包含"清"的结果前,也方便测试。
在只有 24 条数据时,没有必要先引入编辑距离或向量检索。可解释、可复现的排序更适合离线文化内容。
三十、最小单元测试按字段组织
至少准备以下互斥用例:
- 只在名称出现的关键词;
- 只在习俗数组出现的关键词;
- 只在食物数组出现的关键词;
- 只在养生数组出现的关键词;
- 只在诗名出现的关键词;
- 只在作者出现的关键词;
- 只在诗句正文出现的关键词;
- 空字符串与纯空格;
- 完全无结果的关键词。
其中 5 至 7 在修复前源码应失败,用来证明诗词缺口;本轮补齐诗词与物候条件后,这些用例已经具备进入通过集的代码基础。测试记录能力演进,而不是只挑现有代码能过的案例。
三十一、页面手工验收关注状态转换
打开搜索页时应看到全部节气或明确 loading;输入名称后结果收窄;输入食物与习俗词时出现可解释结果;输入不存在词时显示无结果状态。
补齐诗词后,输入作者和诗句验证对应节气。点击结果进入详情,再返回时关键词是否保留,需要按页面路由生命周期实际确认。
还要测试快速输入、清除、系统字体放大、横屏、小窗口、键盘弹出与返回键,确保搜索栏和结果都可达。
每一步都应记录关键词、结果数量、首条结果、命中来源和页面状态;返回详情后若关键词重置,也要明确这是当前生命周期行为还是回归缺陷。
三十二、性能优化先守住 24 条事实
每次输入扫描 24 条记录,即使检查多个短数组,成本仍很低。当前最重要的问题是能力缺口和状态表达,而不是 CPU 优化。
如果未来节气扩展成大规模传统文化库,可以在加载时生成规范化文档:
ts
interface SearchDocument {
term: SolarTerm;
normalizedText: string;
}
但文档拼接会丢失命中字段信息,因此还需保存字段索引。数据规模没有增长前,不必支付这份复杂度。
三十三、隐私与网络边界
关键词只保存在 @State keyword,源码没有把搜索词写入 Preferences、日志或网络。页面离开后,短期 UI 状态随组件生命周期结束。
隐私材料不应声称收集搜索历史,也不能把本地搜索包装成在线推荐。若以后保存最近搜索,要提供清除入口、容量上限和本地处理说明。
搜索词可能反映用户兴趣,即使是文化应用也应遵守最小化原则。
验证时可在工程内搜索 Preferences、日志、HTTP 与分析 SDK 调用,并在断网环境操作搜索页;只有代码证据与运行观察都一致,才能写出"本地搜索且不保存关键词"的隐私结论。
三十四、上架审核需要真实描述
当前可准确描述为"支持按节气名称、简介、习俗、美食和养生内容搜索"。在诗词条件补齐并通过真实数据测试前,不应在应用商店声称已支持诗名、作者和诗句搜索。
如果版本说明写"多字段搜索",审核人员应能从页面快速进入,看到无结果提示,并通过可复查关键词验证字段命中。
页面加载失败、点击无响应和文本溢出都可能影响运行稳定与布局审核,应在发布前覆盖。
发布素材若展示诗词关键词,应使用已经补齐诗词匹配的同一构建包,并保留测试关键词与命中截图;否则应从描述、截图和标签中移除该能力,避免审核素材超前于代码。
三十五、推荐的搜索职责分层
| 层 | 职责 |
|---|---|
| SolarTermService | 加载并校验本地数据 |
| SearchMatcher | 规范化关键词、字段匹配与评分 |
| SearchPage | 输入、状态和结果展示 |
| Router | 用稳定 termId 打开详情 |
匹配逻辑从页面抽出后,可以用纯数据测试诗词、习俗和食物,不需要启动 ArkUI。页面只负责 loading、error、empty、content 四态。
这不是为 24 条数据制造框架,而是让"搜索哪些字段"成为一个清晰、可回归的业务合同。
三十六、发布前检查清单
- rawfile 加载失败时显示错误与重试;
- 空关键词行为与产品说明一致;
- 名称、摘要、描述、习俗、食物和养生可命中;
- 诗名、作者、诗句在补齐代码后可命中;
- 物候是否参与搜索有明确决定;
- 无结果时显示关键词和清除入口;
- 命中片段能解释结果来源;
- 长摘要在小窗口与大字体下不溢出;
- 结果 Key 唯一稳定,路由失败有反馈;
- 搜索词不被无声明地持久化或上传;
- 24 条数据下无不必要防抖和后台任务;
- 应用介绍不超出当前安装包真实能力。
这份清单把功能、性能、隐私和审核口径放在同一条验收链上。
三十七、可直接落地的状态闭环补丁
针对源码审计发现的诗词字段和页面状态边界,可以在不引入网络、数据库或第三方依赖的前提下完成一次小范围改造。补丁仍以 SolarTermService 提供的 24 条本地数据为唯一输入,只把匹配合同和页面状态显式化:
ts
type SearchPageState = 'loading' | 'content' | 'empty' | 'error';
interface SearchHit {
term: SolarTerm;
fields: string[];
}
private normalize(value: string): string {
return value.trim().toLocaleLowerCase('zh-CN');
}
private poemMatches(term: SolarTerm, keyword: string): boolean {
const poems = term.poems ?? [];
return poems.some((poem: SolarTermPoem) => {
return this.normalize(poem.title).includes(keyword)
|| this.normalize(poem.author).includes(keyword)
|| this.normalize(poem.content).includes(keyword);
});
}
页面加载时使用 try/catch/finally 收敛状态,而不是让异常和零结果共享空白区域:
ts
private async loadTerms(): Promise<void> {
this.pageState = 'loading';
try {
await SolarTermService.instance.ensureLoaded(getContext(this));
this.allTerms = SolarTermService.instance.all().slice();
this.pageState = this.allTerms.length > 0 ? 'content' : 'empty';
} catch (error) {
this.pageState = 'error';
}
}
搜索结果使用 SearchHit 返回命中字段。名称、简介、习俗、食物、养生和诗词分别记录来源,页面既能显示"共 3 条",也能展示"命中:诗句",避免用户面对结果却不知道为何命中。空关键词继续返回 24 条完整数据;规范化后的非空关键词没有结果时进入 empty;rawfile 解析失败进入 error 并提供重试;只有正常匹配才进入 content。
这次改造的验收矩阵可以固定为四组:名称输入"冬至"命中 1 条,食物输入真实 JSON 中的已有词命中对应节气,诗名或诗句输入命中包含该诗词的节气,不存在的随机词进入无结果状态。再补充纯空格、快速连续输入、加载失败和详情返回四个边界,就形成从数据加载、字段匹配、状态渲染到路由的完整闭环。本轮补丁已经合入当前源码并通过构建,但没有执行真机输入法、滚动和路由交互测试,因此不把"编译通过"扩大成"真机全链路通过"。
三十八、总结
华夏二十四节气的搜索页已经具备一个轻量离线搜索的核心:从 rawfile 加载 24 条节气,TextInput 实时更新关键词,名称、摘要、描述、习俗、食物和养生通过 indexOf() 与 some() 进行同步过滤,稳定 ID 用于列表复用与详情路由。
本轮已经补齐诗名、作者、诗句与物候匹配,并建立 loading/error/empty/content 四态、清除入口、结果数量和路由失败提示。仍未实现的是命中来源标签、文本高亮、拼音搜索、错别字纠正和相关度排序;这些能力不在本文通过项中。标题中的多字段搜索由真实源码与构建结果支撑,不再是一句超出源码的宣传。
本文部分内容由 AI 辅助整理,所有源码结论均以文中所列本地工程文件为复核依据。
三十九、2026-07-27 修复后复核:源码与构建双证据
本轮复核固定在应用版本 1.0.1、targetSdkVersion 6.0.2(22)、compatibleSdkVersion 6.0.2(22)。数据源仍是 entry/src/main/resources/rawfile/solar_terms.json,实际包含 24 条节气;搜索实现仍以 SearchPage.ets 为准。下面的检查在补丁合入后再次执行,用来确认当前源码覆盖范围。
powershell
$search = Get-Content -Raw -Encoding UTF8 `
'entry/src/main/ets/pages/SearchPage.ets'
$terms = Get-Content -Raw -Encoding UTF8 `
'entry/src/main/resources/rawfile/solar_terms.json' | ConvertFrom-Json
[pscustomobject]@{
termCount = $terms.terms.Count
hasTextInput = $search.Contains('TextInput')
matchesPoems = $search.Contains('t.poems')
matchesPhenology = $search.Contains('t.phenology')
declaresLoading = $search.Contains('@State loading')
rendersLoading = $search.Contains('if (this.loading)')
hasCatch = $search.Contains('catch (')
swallowsRouteError = $search.Contains('.catch(() => {})')
} | ConvertTo-Json
在上述版本源码上执行,实际输出如下:
json
{
"termCount": 24,
"hasTextInput": true,
"matchesPoems": true,
"matchesPhenology": true,
"declaresLoading": true,
"rendersLoading": true,
"hasCatch": true,
"swallowsRouteError": false
}
这组结果给出了一条可复查的边界:当前版本已经有 TextInput 和 24 条本地数据,poems 与 phenology 已进入匹配,loading 已渲染为页面分支,数据加载具有 catch 错误态,详情路由也不再使用空回调吞错。清除关键词、结果数量和无结果提示同样位于当前 SearchPage.ets。
构建命令使用 DevEco Studio 26.0.0.461 自带 hvigor:
powershell
hvigorw.bat --no-daemon assembleHap
原工程目录包含命令行校验不接受的全角括号,因此将同一份源码复制到临时 ASCII 路径后执行;实际结果为 BUILD SUCCESSFUL in 17 s 400 ms,CompileArkTS、PackageHap 与 SignHap 均完成。构建仍有项目既存的废弃 API 和兼容性警告,但没有新增 ArkTS 编译错误。
复核时还应把"纯逻辑可验证"和"真机才能验证"分开。字段匹配、空关键词返回 24 条、稳定 ID 和命中来源可以通过纯数据测试;输入法联想、返回键行为、滚动手感、错误态视觉以及详情路由失败提示,必须在 HarmonyOS 真机或模拟器中检查。本轮没有执行真机交互测试,所以不写"真机已通过"。这一限制同样是复核结果的一部分。
唯一复核标记:AGC18-HMOS-15-07-STATIC-AUDIT-V2。
**AI 辅助声明:**本文在人工核对真实工程源码、数据文件和页面状态后,使用 AI 辅助整理结构、润色表达并生成配图;代码能力、工程状态与验证边界均以文中列出的本地证据为准。
CSDN-SERIES:ALL-163252527