# 鸿蒙 ArkTS 实战:生肖查询 ZodiacQuery(示例 45)

引言
中华文化博大精深,生肖、天干、五行是其中最具代表性的符号体系。示例 45「生肖查询」让用户输入出生年份,就能查询到对应的生肖(12 生肖之一)、天干(10 天干之一)、五行(金木水火土)和周岁年龄。这个应用的实现核心是查表映射——通过年份与生肖/天干/五行的固定映射关系,快速查询对应结果。
在技术层面,这个示例涉及 ArkTS 的几个关键知识点:const 常量数组作为映射表、非负取模运算(mod 函数)、TextInput 数字输入与校验、Row + Column 组合布局的卡片设计、以及多状态变量的独立更新。
1. 应用概述与功能
「生肖查询」页面自上而下分为:顶部返回栏、年份输入框、查询按钮、生肖/天干/五行三栏卡片、年龄卡片。
1.1 核心功能清单
- 年份输入:通过
TextInput输入出生年份(数字格式)。 - 生肖查询:根据年份计算对应生肖(鼠、牛、虎…猪)。
- 天干查询:根据年份计算对应天干(甲、乙、丙…癸)。
- 五行查询:根据天干映射对应五行(木、火、土、金、水)。
- 年龄计算:根据当前年份与输入年份的差值计算周岁。
- 输入校验:检查年份是否为有效数字、范围是否在 1900 至当前年份之间。
1.2 技术要点一览
- 常量映射表:
ZODIAC、STEM、ELEMENT三个字符串数组作为查表数据源。 - 非负取模:
mod()函数处理负数取模的边界情况。 - 年份到生肖/天干的映射公式:
(year - 4) % 12和(year - 4) % 10。 - 动态三栏卡片:
Row+ 三个Column用Divider分隔,展示三个查询结果。 - 独立状态更新:生肖、天干、五行、年龄各用独立的
@State变量。
2. 核心知识点
2.1 三个常量映射表
const ZODIAC: string[] = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪'];
const STEM: string[] = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
const ELEMENT: string[] = ['木', '木', '火', '火', '土', '土', '金', '金', '水', '水'];
ZODIAC:12 生肖,索引 0=鼠、1=牛…11=猪。STEM:10 天干,索引 0=甲、1=乙…9=癸。ELEMENT:10 天干对应的五行(每个天干对应一个五行)。甲=木、乙=木、丙=火、丁=火、戊=土、己=土、庚=金、辛=金、壬=水、癸=水。
三个数组都是模块级 const,在组件外部定义,不随组件实例变化。
2.2 生肖映射公式
生肖的映射关系基于年份与索引的对应:
- 公元 4 年是鼠年(索引 0)。
- 公式:
(year - 4) % 12得到生肖索引。
例如:
- 2024 年:
(2024 - 4) % 12 = 2020 % 12 = 4→ ZODIAC[4] = ‘龙’ - 2000 年:
(2000 - 4) % 12 = 1996 % 12 = 4→ ZODIAC[4] = ‘龙’ - 1996 年:
(1996 - 4) % 12 = 1992 % 12 = 0→ ZODIAC[0] = ‘鼠’
2.3 天干映射公式
天干的映射关系同理:
- 公元 4 年是甲年(索引 0)。
- 公式:
(year - 4) % 10得到天干索引。
例如:
- 2024 年:
(2024 - 4) % 10 = 2020 % 10 = 0→ STEM[0] = ‘甲’ - 1990 年:
(1990 - 4) % 10 = 1986 % 10 = 6→ STEM[6] = ‘庚’
2.4 五行映射
五行通过天干的索引直接映射(使用 ELEMENT[stemIdx])。天干与五行的对应关系:
- 甲、乙 → 木
- 丙、丁 → 火
- 戊、己 → 土
- 庚、辛 → 金
- 壬、癸 → 水
2.5 mod 函数:非负取模
mod(a: number, b: number): number {
const r: number = a % b;
return r < 0 ? r + b : r;
}
JavaScript 的 % 运算符对负数返回负数结果:(-1) % 12 = -1。但作为数组索引需要非负值。mod 函数在余数为负时加上除数,确保结果在 [0, b) 范围内。
例如:mod(-1, 12) = -1 + 12 = 11,对应「猪」。
在正常年份范围内(1900-当前年份),year - 4 不会为负数,所以 mod 函数实际上不会触发负数分支。但作为防御性编程,处理了边界情况。
2.6 query:查询逻辑
query(): void {
const year: number = parseInt(this.yearText);
if (Number.isNaN(year)) {
promptAction.showToast({ message: '请输入有效年份' });
return;
}
const curYear: number = new Date().getFullYear();
if (year < 1900 || year > curYear) {
promptAction.showToast({ message: '请输入 1900~' + curYear + ' 之间的年份' });
return;
}
const zodiacIdx: number = this.mod(year - 4, 12);
const stemIdx: number = this.mod(year - 4, 10);
this.zodiac = ZODIAC[zodiacIdx];
this.stem = STEM[stemIdx];
this.element = ELEMENT[stemIdx];
this.ageText = (curYear - year) + ' 岁';
}
查询流程:
- 解析年份:
parseInt(this.yearText)解析用户输入。 - 有效性检查:
Number.isNaN(year)拦截非数字输入。 - 范围检查:检查年份是否在 1900 到当前年份之间。
- 计算索引:
mod(year - 4, 12)和mod(year - 4, 10)。 - 查表赋值:从映射表中获取结果,分别赋值给四个
@State变量。
2.7 @State 状态变量
@State yearText: string = '';
@State zodiac: string = '--';
@State stem: string = '--';
@State element: string = '--';
@State ageText: string = '--';
yearText:输入框文本,初始为空。zodiac/stem/element:查询结果,初始为--占位符。ageText:年龄文本,初始为--。
四个结果变量独立更新,互不影响。
3. 源码逐段解析
3.1 常量映射表定义
三个 const 数组定义在组件外部。这是因为它们是模块级的常量,不需要组件实例化。如果定义在组件内部(private),每次组件创建都会重新初始化,效率较低。
3.2 输入与查询
TextInput 使用 type(InputType.Number) 弹出数字键盘,onChange 实时更新 yearText。查询按钮点击触发 query() 方法。
3.3 三栏卡片布局
Row() {
Column() {
Text('生肖')
Text(this.zodiac).fontSize(28).fontColor('#1a6cff')
}.layoutWeight(1)
Divider().vertical(true).height(52)
Column() {
Text('天干')
Text(this.stem).fontSize(28).fontColor('#e03131')
}.layoutWeight(1)
Divider().vertical(true).height(52)
Column() {
Text('五行')
Text(this.element).fontSize(28).fontColor('#0aa869')
}.layoutWeight(1)
}
三个 Column 用 layoutWeight(1) 等宽分布,Divider 垂直分隔。每个栏目标题(灰色小字)+ 结果(彩色大字)。三个结果使用不同颜色区分:生肖蓝、天干红、五行绿。
3.4 年龄卡片
独立的卡片展示年龄,使用 Row 布局:左侧标签「年龄」,右侧年龄文本。
3.5 整体风格
白色圆角卡片 + 浅灰背景 + 主题蓝/红/绿三色点缀,与系列其他示例一致。
4. 关键实现细节分析
4.1 查表映射 vs 计算映射
生肖和天干使用「查表映射」而非「计算映射」。虽然存在数学公式可以直接计算(如 (year - 4) % 12),但查表法更直观、易于扩展。如果未来需要调整映射规则(如加入闰月修正),只需修改数组内容。
4.2 五行映射的二次查表
五行不是直接从年份计算的,而是通过天干索引二次查表:ELEMENT[stemIdx]。这种「年份 → 天干 → 五行」的两步映射符合中华文化中年份与五行的关系——五行是天干的属性。
4.3 输入校验的防御层次
三层校验保护:
- 非空检查:
Number.isNaN检查输入是否为有效数字。 - 范围检查:
year < 1900 || year > curYear检查年份是否在合理范围。 - 计算防御:
mod()函数处理负数取模(虽然实际不会触发)。
4.4 占位符设计
未查询时,所有结果显示 --,给用户明确的「无数据」提示。查询后替换为实际值。这种「占位符 → 实际值」的模式比隐藏/显示更友好。
4.5 年龄计算的简化
ageText = (curYear - year) + ' 岁' 是一个简化的年龄计算,没有考虑生日。如果当前日期在生日之前,实际年龄应该比计算值小 1 岁。但作为生肖查询的附属功能,这种简化是可接受的。
5. 运行效果与操作指南
5.1 运行步骤
- 从入口页导航到「生肖查询」页面。
- 在输入框中输入出生年份(如 2000)。
- 点击「查询」按钮。
- 查看生肖、天干、五行和年龄结果。
5.2 使用场景
- 生肖查询:知道自己的生肖属性。
- 文化学习:了解天干地支五行等传统文化。
- 八字排盘:作为更复杂命理计算的基础。
6. 可扩展方向
6.1 添加农历生日转换
支持公历生日转农历生日,再计算生肖和八字。
6.2 添加八字排盘
根据年、月、日、时计算八字(年月日时的天干地支组合)。
6.3 添加生肖详情
点击生肖查看生肖详情(如性格特征、运势等)。
6.4 添加历史年份对照
显示 1900 至今年的生肖/天干对照表。
7. 常见问题与调试技巧
7.1 查询结果显示 --
检查 query() 是否执行。确认输入的年份通过了所有校验。
7.2 生肖/天干计算错误
检查 mod(year - 4, 12) 的结果。确认年份索引的基准(公元 4 年为鼠年/甲年)。
7.3 五行结果不对
检查 ELEMENT[stemIdx] 是否正确。确认天干到五行的映射表 ELEMENT 与 STEM 的索引对应。
7.4 年龄计算偏差
如果当前日期在生日之前,实际年龄比显示的小 1 岁。如需精确计算,需要加入生日比较逻辑。
8. 总结
示例 45「生肖查询」用约 129 行代码,展示了 ArkTS 在「查表映射 + 卡片布局」场景下的实现。重点回顾:
- 常量映射表:模块级
const数组作为查表数据源。 - 非负取模:
mod()函数处理负数边界。 - 输入校验:三层防御(非空、范围、防御性)。
- 三栏卡片:
Row + Column + Divider的经典布局模式。 - 独立状态:每个查询结果用独立
@State变量。
这些查表映射的技术在后续的单位换算、汇率计算等示例中会被复用。
更多推荐


所有评论(0)