很多鸿蒙开发者都听过开源项目「HarmonyKit」------纯 ArkTS 原生开发、全本地离线运行、无广告无登录,集成了编解码、格式化、系统工具等几十种实用能力,是很多开发者手机里的常备调试工具。

其实从零实现一款这样的工具 App 门槛并不高。依托鸿蒙 7 的 @kit 套件体系,核心能力全部由系统原生提供,不需要任何第三方依赖,新手也能快速复刻。本文就带你基于全套 @kit 标准 API,从零搭建一个精简版本地工具箱,覆盖 8 个高频实用工具,代码可直接复制运行,全程纯本地、不联网、无需额外敏感权限。
一、项目整体规划与技术选型
1.1 功能清单
我们复刻核心的三类工具,覆盖日常开发调试 90% 的高频需求:
- 编解码类:Base64 编解码、URL 编解码、MD5 哈希、SHA-256 哈希
- 转换类:时间戳日期互转、进制转换、JSON 格式化/压缩
- 系统类:一键复制结果、本地文件导出、沙箱文件管理
1.2 技术栈与选型原则
- 开发模型:Stage 模型(HarmonyOS NEXT 官方标准)
- SDK 版本:API 26(鸿蒙 7.0)
- UI 框架:ArkUI 声明式开发
- 核心依赖:全部基于官方 @kit 套件,零第三方 npm 包
- 网络权限:无,所有计算纯本地完成,不上传任何数据
1.3 项目核心优势
- 极致隐私:所有运算在本地完成,无网络请求,符合鸿蒙沙箱安全规范
- 零权限申请:仅使用应用沙箱与系统剪贴板标准接口,无需用户授权
- 原生高性能:加密、文件等能力由系统底层优化,速度远快于纯 JS 第三方库
- 模块化易扩展:工具逻辑与 UI 完全分离,新增工具只需加一个方法和对应区块
二、工程基础配置
2.1 新建工程
打开 DevEco Studio 5.0 及以上版本,新建 Stage 模型的 Empty Ability 工程:
- 语言选择 ArkTS
- 设备类型勾选 Phone
- Compile SDK 选择 HarmonyOS NEXT API 26
2.2 编译配置校准
打开工程根目录 build-profile.json5,确认 SDK 版本与运行环境配置正确:
json5
{
"app": {
"signingConfigs": [],
"compileSdkVersion": "26.0.0",
"compatibleSdkVersion": "26.0.0",
"targets": [
{
"name": "default",
"runtimeOS": "HarmonyOS"
}
]
}
}
2.3 权限说明
本项目所有能力都在应用沙箱与系统标准接口范围内,无需申请任何敏感权限:
- 剪贴板使用系统标准 API,无需额外授权
- 文件操作仅在应用私有沙箱内执行,符合鸿蒙隐私安全规范
- 无网络、无定位、无通讯录读取,纯离线工具
三、核心工具模块封装
我们把所有工具逻辑统一封装到工具类中,与 UI 彻底分离,便于复用与维护。新建 entry/src/main/ets/utils/ToolUtils.ets 文件,以下代码全部放入该文件。
3.1 哈希加密:基于 @kit.SecurityKit
使用系统原生加密框架实现 MD5 与 SHA-256 哈希计算,性能远高于第三方 JS 实现。

typescript
import { cryptoFramework } from '@kit.SecurityKit';
/**
* 哈希加密工具
*/
export class HashUtils {
/**
* 计算字符串哈希值
* @param text 原文
* @param algorithm 算法:MD5 / SHA256
*/
static computeHash(text: string, algorithm: 'MD5' | 'SHA256'): string {
if (!text) return '';
const mdInstance = cryptoFramework.createMd(algorithm);
const textBuffer = new TextEncoder().encode(text);
mdInstance.update(textBuffer.buffer);
const result = mdInstance.digest();
return Array.from(new Uint8Array(result))
.map(byte => byte.toString(16).padStart(2, '0'))
.join('');
}
static md5(text: string): string {
return this.computeHash(text, 'MD5');
}
static sha256(text: string): string {
return this.computeHash(text, 'SHA256');
}
}
💡 提示:MD5 仅用于文件校验与普通摘要,不可用于密码存储;高安全场景请使用 SHA-256 加盐。
3.2 编解码工具:标准 API 封装
Base64 与 URL 编解码使用 ES 标准 API,开箱即用,兼容所有鸿蒙版本。

typescript
/**
* 编解码工具
*/
export class EncodeUtils {
// Base64 编码
static base64Encode(text: string): string {
try {
return btoa(unescape(encodeURIComponent(text)));
} catch {
return '编码失败';
}
}
// Base64 解码
static base64Decode(text: string): string {
try {
return decodeURIComponent(escape(atob(text.trim())));
} catch {
return '解码失败,请检查输入格式';
}
}
// URL 编码
static urlEncode(text: string): string {
return encodeURIComponent(text);
}
// URL 解码
static urlDecode(text: string): string {
try {
return decodeURIComponent(text);
} catch {
return '解码失败';
}
}
}
3.3 转换类工具
包含时间戳互转、进制转换、JSON 格式化三个高频工具。

typescript
/**
* 转换工具
*/
export class ConvertUtils {
// 时间戳转日期字符串
static timestampToDate(timestamp: number): string {
if (!timestamp) return '';
// 自动识别秒级/毫秒级
const time = timestamp < 10000000000 ? timestamp * 1000 : timestamp;
const date = new Date(time);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
const second = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
// 日期转时间戳(毫秒级)
static dateToTimestamp(dateStr: string): number {
if (!dateStr) return 0;
return new Date(dateStr).getTime();
}
// 进制转换:支持 2/8/10/16 进制互转
static baseConvert(value: string, fromBase: number, toBase: number): string {
try {
const decimal = parseInt(value, fromBase);
if (isNaN(decimal)) return '输入格式错误';
return decimal.toString(toBase);
} catch {
return '转换失败';
}
}
// JSON 格式化
static jsonFormat(jsonStr: string): string {
try {
const obj = JSON.parse(jsonStr);
return JSON.stringify(obj, null, 2);
} catch (e) {
return 'JSON 格式错误,请检查语法';
}
}
// JSON 压缩
static jsonMinify(jsonStr: string): string {
try {
const obj = JSON.parse(jsonStr);
return JSON.stringify(obj);
} catch {
return 'JSON 格式错误,请检查语法';
}
}
}
3.4 系统能力:剪贴板与文件操作
剪贴板基于 @kit.BasicServicesKit,文件操作基于 @kit.CoreFileKit,全部为系统原生能力。

typescript
import { pasteboard } from '@kit.BasicServicesKit';
import { fs } from '@kit.CoreFileKit';
import { promptAction } from '@kit.ArkUI';
/**
* 系统工具
*/
export class SystemUtils {
// 复制文本到剪贴板
static copyToClipboard(text: string): void {
if (!text) {
promptAction.showToast({ message: '无内容可复制' });
return;
}
const data = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text);
pasteboard.getSystemPasteboard().setData(data);
promptAction.showToast({ message: '已复制到剪贴板' });
}
// 保存文本到沙箱文件
static saveToFile(context: Context, fileName: string, content: string): void {
try {
const filePath = `${context.filesDir}/${fileName}`;
fs.writeText(filePath, content);
promptAction.showToast({ message: `已保存到沙箱:${fileName}` });
} catch {
promptAction.showToast({ message: '保存失败' });
}
}
}
四、完整主页面实现
我们使用 Tabs 组件分为「编解码」「转换工具」「系统工具」三个标签页,每个工具独立区块,布局清晰,操作直观。
打开 entry/src/main/ets/pages/Index.ets,替换为以下完整代码,复制即可直接运行:
typescript
import { HashUtils, EncodeUtils, ConvertUtils, SystemUtils } from '../utils/ToolUtils';
import { promptAction } from '@kit.ArkUI';
@Entry
@Component
struct ToolBoxPage {
@State currentTab: number = 0;
// 编解码模块状态
@State encodeInput: string = '';
@State encodeResult: string = '';
// 转换模块状态
@State convertInput: string = '';
@State convertResult: string = '';
private tabTitles: string[] = ['编解码', '转换工具', '系统工具'];
build() {
Column() {
// 顶部标题
Text('开发者工具箱')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.width('90%')
.margin({ top: 20, bottom: 16 });
// Tab 标签栏
Tabs({ barPosition: BarPosition.Start }) {
// 第一页:编解码工具
TabContent() {
this.buildEncodeSection();
}
.tabBar(this.tabTitles[0])
// 第二页:转换工具
TabContent() {
this.buildConvertSection();
}
.tabBar(this.tabTitles[1])
// 第三页:系统工具
TabContent() {
this.buildSystemSection();
}
.tabBar(this.tabTitles[2])
}
.barMode(BarMode.Fixed)
.barHeight(48)
.layoutWeight(1)
.onChange((index) => {
this.currentTab = index;
});
}
.width('100%')
.height('100%')
.backgroundColor('#F8F9FA');
}
// ========== 编解码工具区块 ==========
@Builder
buildEncodeSection() {
Scroll() {
Column({ space: 16 }) {
this.buildToolCard('Base64 编解码', this.encodeInput, (val) => this.encodeInput = val, [
{ text: '编码', color: '#0A59F7', action: () => this.encodeResult = EncodeUtils.base64Encode(this.encodeInput) },
{ text: '解码', color: '#00B578', action: () => this.encodeResult = EncodeUtils.base64Decode(this.encodeInput) }
], this.encodeResult);
this.buildToolCard('URL 编解码', this.encodeInput, (val) => this.encodeInput = val, [
{ text: '编码', color: '#0A59F7', action: () => this.encodeResult = EncodeUtils.urlEncode(this.encodeInput) },
{ text: '解码', color: '#00B578', action: () => this.encodeResult = EncodeUtils.urlDecode(this.encodeInput) }
], this.encodeResult);
this.buildToolCard('MD5 哈希', this.encodeInput, (val) => this.encodeInput = val, [
{ text: '计算', color: '#9B59B6', action: () => this.encodeResult = HashUtils.md5(this.encodeInput) }
], this.encodeResult);
this.buildToolCard('SHA-256 哈希', this.encodeInput, (val) => this.encodeInput = val, [
{ text: '计算', color: '#E67E22', action: () => this.encodeResult = HashUtils.sha256(this.encodeInput) }
], this.encodeResult);
}
.width('90%')
.margin({ top: 16, bottom: 20 });
}
.width('100%');
}
// ========== 转换工具区块 ==========
@Builder
buildConvertSection() {
Scroll() {
Column({ space: 16 }) {
this.buildToolCard('时间戳转日期', this.convertInput, (val) => this.convertInput = val, [
{ text: '转换', color: '#0A59F7', action: () => {
const ts = parseInt(this.convertInput);
this.convertResult = isNaN(ts) ? '请输入数字时间戳' : ConvertUtils.timestampToDate(ts);
}}
], this.convertResult);
this.buildToolCard('日期转时间戳', this.convertInput, (val) => this.convertInput = val, [
{ text: '转换', color: '#00B578', action: () => {
const result = ConvertUtils.dateToTimestamp(this.convertInput);
this.convertResult = result ? String(result) : '请输入正确日期格式';
}}
], this.convertResult);
this.buildToolCard('JSON 格式化', this.convertInput, (val) => this.convertInput = val, [
{ text: '格式化', color: '#0A59F7', action: () => this.convertResult = ConvertUtils.jsonFormat(this.convertInput) },
{ text: '压缩', color: '#E67E22', action: () => this.convertResult = ConvertUtils.jsonMinify(this.convertInput) }
], this.convertResult);
this.buildToolCard('十进制转十六进制', this.convertInput, (val) => this.convertInput = val, [
{ text: '转换', color: '#9B59B6', action: () => {
this.convertResult = ConvertUtils.baseConvert(this.convertInput, 10, 16);
}}
], this.convertResult);
}
.width('90%')
.margin({ top: 16, bottom: 20 });
}
.width('100%');
}
// ========== 系统工具区块 ==========
@Builder
buildSystemSection() {
Column({ space: 20 }) {
// 剪贴板演示
this.buildToolCard('剪贴板测试', '输入内容会复制到剪贴板', (val) => {}, [
{ text: '复制结果', color: '#0A59F7', action: () => SystemUtils.copyToClipboard(this.encodeResult || this.convertResult) }
], '点击按钮可复制上方工具的计算结果');
// 文件保存演示
this.buildToolCard('保存到本地文件', '输入要保存的内容', (val) => this.convertInput = val, [
{ text: '保存到沙箱', color: '#00B578', action: () => {
SystemUtils.saveToFile(getContext(this), 'tool_result.txt', this.convertInput);
}}
], '文件会保存到应用私有沙箱目录');
Blank();
Text('💡 所有工具均为本地计算,无网络请求')
.fontSize(12)
.fontColor('#AAAAAA')
.margin({ bottom: 20 });
}
.width('90%')
.margin({ top: 16 });
}
// ========== 通用工具卡片组件 ==========
@Builder
buildToolCard(
title: string,
inputValue: string,
onInputChange: (val: string) => void,
buttons: Array<{ text: string, color: string, action: () => void }>,
result: string
) {
Column({ space: 12 }) {
Text(title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.width('100%');
TextArea({ text: inputValue, placeholder: '请输入内容' })
.width('100%')
.height(80)
.borderRadius(8)
.padding(10)
.backgroundColor('#F5F5F5')
.fontSize(14)
.onChange(onInputChange);
// 操作按钮组
Flex({ wrap: FlexWrap.Wrap, gap: 10 }) {
ForEach(buttons, (btn: { text: string, color: string, action: () => void }) => {
Button(btn.text)
.height(36)
.padding({ left: 16, right: 16 })
.borderRadius(18)
.backgroundColor(btn.color)
.fontSize(14)
.onClick(btn.action);
});
}
.width('100%');
// 结果展示
if (result) {
Column({ space: 8 }) {
Text('结果')
.fontSize(14)
.fontColor('#666666')
.width('100%');
Text(result)
.width('100%')
.padding(10)
.backgroundColor('#F0F7FF')
.borderRadius(8)
.fontSize(14)
.fontFamily('monospace')
.wordBreak(WordBreak.BreakAll)
.maxLines(6)
.textOverflow({ overflow: TextOverflow.Ellipsis });
Text('复制结果')
.fontSize(13)
.fontColor('#0A59F7')
.width('100%')
.textAlign(TextAlign.End)
.onClick(() => SystemUtils.copyToClipboard(result));
}
.width('100%');
}
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({ radius: 4, color: '#1A000000' });
}
}
代码核心设计亮点
- 组件复用 :抽取通用
buildToolCard卡片组件,新增工具只需传参数,不用重复写布局 - 状态统一管理:同类型工具复用输入输出状态,减少冗余变量
- 异常兜底:所有转换、编解码操作都加了 try-catch 异常捕获,非法输入不会导致应用崩溃
- 一键复制:所有结果都自带复制按钮,操作效率更高
五、运行验证与扩展指南
5.1 运行验证步骤
- 将工具类和页面代码复制到对应文件
- 同步工程,编译运行到模拟器或真机
- 切换三个 Tab,测试各个工具的计算效果
- 点击「复制结果」验证剪贴板能力,点击「保存到沙箱」验证文件写入
5.2 可扩展功能方向
原版 HarmonyKit 有几十种工具,都可以基于这套架构快速扩展:
- 编解码类:Unicode 转码、摩斯密码、二维码生成
- 工具类:正则表达式测试、颜色拾取、单位换算、随机数生成
- 文件类:文件哈希校验、文本批量替换、沙箱文件管理器
- 系统类:设备信息查看、应用详情导出、权限检测
5.3 开发最佳实践
- 逻辑 UI 分离:工具方法统一放到 utils 目录,便于单元测试与跨页面复用
- 优先原生 API:加密、文件、剪贴板等能力优先用 @kit 原生实现,性能与稳定性更优
- 输入异常兜底:所有用户输入都做异常捕获,避免非法格式导致应用崩溃
- 结果一键复制:工具类产品默认提供复制能力,大幅提升使用效率
总结
这款精简版工具箱,核心业务代码不到 500 行,全部基于鸿蒙官方 @kit 套件实现,没有任何第三方依赖。对于新手来说,通过这个项目可以一次性吃透 SecurityKit、CoreFileKit、BasicServicesKit 等多个核心 Kit 的用法,同时完成一个可日常使用的实战项目。
原版 HarmonyKit 的强大之处,本质上就是对系统原生能力的封装与整合------把零散的系统 API 包装成一个个开箱即用的工具。掌握了这套模块化封装思路,你也可以根据自己的需求,扩展出专属的开发者工具箱。