【共创季稿事节】毕业季·鸿蒙同行:我用鸿蒙做毕设,也做了一个帮毕业生做毕设的鸿蒙应用

本文参与方向:方向3-毕业季-毕业生在毕业设计中使用鸿蒙技术的作品分享

我的毕业设计是一款鸿蒙应用,而它的主题,恰恰是"帮毕业生管理毕设全流程"。本文完整记录这款「鸿蒙毕设伴侣 GraduAssist」从构思、架构、AI 接入到手机与鸿蒙 PC 双端运行的全过程,也记录我从一个连 ArkTS 都不会的学生,成长为鸿蒙开发者的这段路。

一、写在前面:一个毕业生的自白

我是一名 2026 届计算机专业的应届毕业生。半年前,当我需要确定毕业设计选题时,摆在面前的有很多"稳妥"的选择——一个 Web 后台、一个安卓 App、一个小程序。但我最终选了一条当时看起来更"冒险"的路:用 HarmonyOS 从零做一款原生应用

原因有两个。其一,鸿蒙生态正在快速崛起,作为一个即将走向就业市场的学生,我想赌一个更有前景的方向;其二,说实话,我想做点不一样的东西,让我的毕设在答辩现场能被记住。
在这里插入图片描述

但真正动手时,我立刻慌了。我发现自己被两件事同时压着:一边要学一门全新的技术(ArkTS/ArkUI 我完全没接触过),一边要应付毕设本身那一整套让人头大的流程——开题报告、文献综述、实验、论文初稿、查重降重、答辩 PPT……每一项都有 deadline,散落在导师的微信、学院的通知、教务系统里,我经常记了这个忘了那个。

有天深夜赶开题报告时,我突然想到:我既然要用鸿蒙做一个毕设作品,为什么不干脆做一个"帮毕业生管理毕设"的应用? 这样一来,我的毕设作品本身,就成了我这段毕业季经历的最佳注脚——用我正在学的鸿蒙技术,解决我正在经历的毕业焦虑。

这个想法让我兴奋。于是有了这款应用——鸿蒙毕设伴侣 GraduAssist。它把毕设进度看板、AI 论文助手、答辩演练、以及我自己的鸿蒙学习成长记录,整合在一个应用里。

它全程在华为 MateBook Pro(鸿蒙 PC) 上开发,同时适配手机端运行。先看一眼它跑在鸿蒙 PC 上的样子——这是毕设看板页:

鸿蒙 PC 上的毕设看板

窗口化运行在鸿蒙 PC 桌面,我毕设的六个阶段一目了然:开题、文献已完成,"完成鸿蒙应用核心功能开发"还剩 3 天(正是我写这篇文章时的真实状态),后面的初稿、查重、答辩依次排开。总进度 33%——是的,写这篇文章时我的毕设也才刚过三分之一,我们一起加油。

下面,我完整拆解这款应用是怎么一步步做出来的,也顺带聊聊我作为一个鸿蒙新手,踩过的坑和学到的东西。

二、需求梳理:一个毕业生真正需要什么

在写代码前,我先以"用户"(也就是我自己)的身份,列出了毕业季最痛的几个需求:

  1. 毕设进度会失控——那么多阶段、那么多 deadline,需要一个地方统一管理、提醒。
  2. 论文写作太痛苦——摘要不够学术、大纲没思路、文献读不完、查重率过高,如果有个"AI 导师"帮忙就好了。
  3. 答辩心里没底——不知道评委会问什么,临场容易慌,需要提前演练。
  4. 学习没有正反馈——学鸿蒙的过程很孤独,如果能记录自己的成长和认证进度,会更有动力。

这四个需求,正好对应了应用的四个 Tab:看板、论文、答辩、成长。需求驱动设计,这是我从这个项目学到的第一课——先想清楚"给谁用、解决什么问题",再动手写代码,比一上来就堆功能靠谱得多。
在这里插入图片描述

三、技术选型与架构:新手也要讲究分层

3.1 为什么是 HarmonyOS + ArkTS

选 HarmonyOS 是毕设的前提,但深入之后我真切感受到它的几个好处:

  • 声明式 UI(ArkUI) 开发效率很高,Column/Row/@State 这套写起来直观,对新手友好;
  • 一次开发、多端部署——同一套代码能同时跑在手机和鸿蒙 PC 上,这对我这个"在 PC 上开发、想在手机上演示"的场景太合适了;
  • ArkTS 的强类型——虽然一开始被它的严格搞得很痛苦,但后来发现它逼着我把代码写规范,反而少了很多运行时 bug。

3.2 架构分层

作为新手,我最初的代码是"面条式"的——所有逻辑塞在一个页面文件里。写到第二个功能就乱得改不动了。痛定思痛,我重构成清晰的四层:

entry/src/main/ets/
├── entryability/     应用入口(生命周期、窗口、安全区)
├── common/           通用层(主题、AI 配置、时间工具)
├── model/            数据与服务层(数据模型、持久化、AI 服务)
└── pages/            UI 层(四个功能页 + 主壳)

具体文件:

├── common/
│   ├── Theme.ets          深蓝学术科技风主题(颜色/圆角常量)
│   ├── AIConfig.ets       模型服务配置 + 论文导师系统提示词
│   └── TimeUtil.ets       deadline 天数计算、阶段配色
├── model/
│   ├── GraduStore.ets     数据模型 + 持久化(毕设/成长/认证)
│   └── AIService.ets      AI 服务:多轮对话 + 单轮任务
└── pages/
    ├── Index.ets          四 Tab 主壳
    ├── BoardTab.ets       毕设看板
    ├── PaperTab.ets       AI 论文助手
    ├── DefenseTab.ets     答辩演练
    └── GrowthTab.ets      我的成长

分层的核心是"单一职责":UI 层只管展示,不碰网络;所有 AI 交互收敛在 AIService;所有数据读写收敛在 GraduStore;配置和工具函数集中在 common。这样即使是新手,改起来也不容易出错——想改提示词就去 AIConfig,想调 UI 就去对应的 Tab,互不干扰。这是我学到的第二课:好的架构,是新手对抗复杂度的最好武器。
在这里插入图片描述

四、功能一:毕设看板——把失控的 deadline 管起来

看板是这个应用的地基,也是我用得最勤的功能。它要解决"进度失控"的痛点,核心是三件事:总进度可视化、任务清单管理、deadline 倒计时
在这里插入图片描述

4.1 数据模型

先定义毕设阶段任务的结构。作为学过的第一课,我给每个模型都写了显式 interface(ArkTS 强制要求,否则报 arkts-no-untyped-obj-literals):

export interface Milestone {
  id: string;
  stage: string;       // 阶段:开题/文献/实验/初稿/查重/答辩
  title: string;
  dueTime: number;     // 截止时间戳 ms
  done: boolean;
}

export const STAGES: string[] = ['开题', '文献', '实验', '初稿', '查重', '答辩'];

这里有个体验上的小心思:应用首次运行时,我会注入一套示例任务(我自己毕设的真实阶段),让用户打开就能看到一个填充好的看板,而不是空荡荡的界面:

static async listMilestones(): Promise<Milestone[]> {
  const raw = await GraduStore.store.get(KEY_MILESTONES, '') as string;
  if (raw === '') {                       // 首次运行
    const seed = demoMilestones();        // 注入示例数据
    await GraduStore.saveMilestones(seed);
    return seed;
  }
  const arr = JSON.parse(raw) as Milestone[];
  return arr.sort((a, b) => a.dueTime - b.dueTime);   // 按 deadline 排序
}

4.2 deadline 倒计时引擎

"还剩几天"这个信息对毕业生太重要了。我在 TimeUtil 里写了一套计算逻辑,把时间戳转成人性化的文案和颜色:

export function dueText(due: number, done: boolean): string {
  if (done) { return '已完成'; }
  const n = daysLeft(due);
  if (n < 0) { return '已逾期 ' + (-n) + ' 天'; }
  if (n === 0) { return '今天截止'; }
  if (n === 1) { return '明天截止'; }
  return '还剩 ' + n + ' 天';
}

export function dueColor(due: number, done: boolean): string {
  if (done) { return C.ok; }              // 绿色
  const n = daysLeft(due);
  if (n < 0) { return C.danger; }         // 逾期红
  if (n <= 3) { return C.gold; }          // 临近黄
  return C.textSub;                        // 常规灰
}

于是逾期的任务标红、三天内的标黄——一眼就知道哪个火烧眉毛了。这种"用颜色传递紧迫感"的设计,比单纯列个日期有用得多。

4.3 进度可视化与交互

看板顶部是一张渐变进度卡,用 Progress 组件展示完成比例,并把最近的 deadline 单独拎出来提醒。任务列表支持点击勾选完成(带删除线动画)、右侧删除、底部弹窗新增。这些都是基础的 @State 驱动 UI:

async toggle(id: string): Promise<void> {
  this.list = await GraduStore.toggleMilestone(id);   // 更新并刷新
}

一个新手容易踩的坑:更新数组后,要让 ArkUI 重渲染,ForEach 的键值要能反映变化。我把键设成 m.id + m.done,这样勾选状态一变,对应项就会重绘。

五、功能二:AI 论文助手——给自己请一位 24 小时导师

这是整个应用技术含量最高、也是我最得意的部分。论文写作的痛苦,AI 能极大缓解——但前提是把提示词设计好,让 AI 真正像个导师

5.1 把大模型定位成"毕设导师"

我在系统提示词里给 AI 设定了明确的人设:

static readonly systemPrompt: string =
  '你是一位资深的毕业设计指导老师,熟悉论文写作、答辩流程与学术规范。' +
  '请用专业、条理清晰、鼓励性的中文,帮助毕业生完成论文润色、大纲生成、' +
  '答辩问题预测等任务。回答要具体、可操作,避免空泛。';

“资深指导老师”“专业、鼓励性”“具体、可操作”——这些词决定了 AI 回复的语气和质量。给模型一个清晰的角色,是提示工程里性价比最高的技巧。

5.2 任务模板:降低使用门槛

论文助手提供四个预设任务:润色摘要、生成大纲、文献要点、降重建议。每个任务背后是一个提示词模板,用户只要选任务、贴内容,剩下的交给模板:

private tasks: QuickTask[] = [
  { label: '润色摘要', tpl: '请帮我润色下面这段论文摘要,使其更学术、流畅、精炼,并指出改进点:\n{X}' },
  { label: '生成大纲', tpl: '我的毕业设计题目/主题是:{X}。请帮我生成一份详细的论文章节大纲(含各章要点)。' },
  { label: '文献要点', tpl: '请提炼下面这段文献内容的核心观点、方法与结论,用要点列出:\n{X}' },
  { label: '降重建议', tpl: '下面这段文字疑似重复率偏高,请在保持原意的前提下改写降重,并给出修改说明:\n{X}' }
];

发送时用用户输入替换模板里的 {X}

const prompt = this.messages.length === 0
  ? this.tasks[this.activeTask].tpl.replace('{X}', raw)   // 首轮:套模板
  : raw;                                                  // 后续:自由追问

这个设计的妙处在于:用户不用自己写提示词,选个任务就行;同时首轮之后还能自由追问,形成多轮对话。下图是我用"生成大纲"功能,输入毕设主题后 AI 给出的完整章节大纲:

AI 论文助手·生成大纲

再看"润色摘要"——我把一段口语化的摘要丢给它,它不仅润色成了学术表达,还用表格列出了每一处改进点和优化策略:

AI 论文助手·润色摘要

顺带一提:AI 润色时可能会"帮"你加上一些量化数据(如复用率、时延),这些数据一定要自己核实,不能直接写进论文——这是学术规范,AI 只是助手,不能代替真实实验。这一点我在应用的说明里也特意提醒了。

5.3 统一的 AI 服务封装

论文助手、答辩预测、答辩点评三处都要调大模型,我把它抽成统一的 AIService,提供多轮 chat 和单轮 task 两个方法:

export class AIService {
  /** 多轮对话 */
  static async chat(history: ChatMessage[]): Promise<string> {
    const messages: ChatMessage[] = [{ role: 'system', content: AIConfig.systemPrompt }];
    for (const m of history) { messages.push(m); }
    // ... 发请求、解析、异常兜底
  }

  /** 单轮任务:便捷封装 */
  static async task(prompt: string): Promise<string> {
    return AIService.chat([{ role: 'user', content: prompt }]);
  }
}

网络请求用鸿蒙 @kit.NetworkKit 的 http 模块,OpenAI 兼容协议,readTimeout 给到 40 秒(论文类回复较长)。任何失败都返回友好中文提示,绝不让界面卡死:

try {
  const resp = await client.request(AIConfig.endpoint, { /* ... */ });
  if (resp.responseCode !== 200) { return `(请求失败:${resp.responseCode}`; }
  const text = AIService.parse(`${resp.result}`);
  return text !== '' ? text : '(助手暂无返回,请重试)';
} catch (e) {
  return '(网络异常)请检查连接后重试';
} finally {
  client.destroy();   // 新手易忘:一定要释放
}

六、功能三:答辩演练——让 AI 当我的模拟评委

答辩是毕业季最让人紧张的一关。我最怕的是"被问到没准备的问题当场卡壳"。所以我设计了这个功能:输入毕设题目 → AI 预测评委可能问的问题 → 我逐题作答 → AI 点评我的回答

6.1 预测答辩问题

关键在于让 AI 站在"评委"视角出题,并且只输出问题列表,方便我解析成一条条卡片:

const prompt = `我的毕业设计题目是「${this.topic.trim()}」。` +
  `请站在答辩评委角度,预测 5 个最可能被问到的答辩问题。` +
  `只输出问题列表,每行一个问题,用数字编号,不要额外说明。`;

拿到回复后,解析层要处理各种编号格式(1.1、Q1:……),我用正则统一剥离行首编号:

private parseQuestions(text: string): string[] {
  const lines = text.split('\n');
  const out: string[] = [];
  for (const raw of lines) {
    const line = raw.trim();
    if (line === '') { continue; }
    const cleaned = line.replace(/^[Qq]?\s*\d+\s*[\.\、\:\))\.]?\s*/, '').trim();
    if (cleaned !== '') { out.push(cleaned); }
  }
  return out.slice(0, 8);
}

这又是一次"驯服大模型输出"的实践——即使我要求它只给列表,它偶尔还是会加编号或空行,解析层必须容错。下图是我输入"基于 HarmonyOS 的智慧生活助手应用设计与实现"后,AI 预测的 5 个答辩问题:

答辩演练·AI 预测问题

不得不说,AI 出的问题相当专业——“为什么选 HarmonyOS 而非 Android/iOS”“分布式能力做了哪些落地”“如何保障隐私安全”“是否完成真机验证”——这些几乎就是答辩现场评委的高频提问。看到这些问题,我心里对答辩的底气一下就足了。

6.2 作答与 AI 点评

每个问题下有"练习作答",我写下回答后提交,AI 会以评委身份点评(优点、不足、改进建议,控制在 150 字内):

const prompt = `答辩问题:${this.questions[i]}\n` +
  `学生的回答:${this.answer.trim()}\n` +
  `请作为答辩评委,简要点评这个回答(优点、不足、改进建议),控制在 150 字内。`;

这就形成了一个"出题 → 作答 → 点评"的完整闭环,相当于把答辩现场提前搬到了自己的电脑上反复演练。

七、功能四:我的成长——记录从学生到开发者的路

如果说前三个 Tab 解决的是"事",那第四个 Tab「成长」承载的是"人"——我这段从零学鸿蒙的心路历程。它有三块内容:认证备考清单、学习里程碑时间线、模型配置。

7.1 认证备考清单

学鸿蒙绕不开认证。我把 HarmonyOS 应用开发者认证的关键考点做成一个可勾选的清单,掌握一项勾一项:

function demoCert(): CertItem[] {
  return [
    { id: 'c1', name: 'HarmonyOS 应用开发者基础认证', done: true },
    { id: 'c2', name: 'HarmonyOS 应用开发者高级认证', done: false },
    { id: 'c3', name: 'ArkTS 语言与 ArkUI 框架', done: true },
    { id: 'c4', name: 'Stage 模型与 Ability 生命周期', done: false },
    { id: 'c5', name: '分布式与一多能力', done: false }
  ];
}

看着"已掌握 2/5"慢慢往上涨,是很实在的正反馈。

7.2 学习里程碑时间线

这一块对我意义特殊。我用一条竖向时间线,记录了自己学鸿蒙的关键节点:

  • 2026.06.01 —— 开始学习 ArkTS 与 ArkUI,跑通第一个 Hello World
  • 2026.06.16 —— 掌握了 @State/@Prop 状态管理,能写多页面应用
  • 2026.07.01 —— 成功接入大模型 API,做出了 AI 对话功能

时间线用 Circle 节点 + Divider 连线实现:

Column() {
  Circle({ width: 10, height: 10 }).fill(C.primary)
  if (i < this.growth.length - 1) {
    Divider().vertical(true).color(C.stroke).strokeWidth(2).height(30)
  }
}

一个多月,从连 ArkTS 语法都看不懂,到能独立做出一个接入 AI 的四页应用——把这些节点记录下来,我才真切感受到自己的成长。这也是应用名"鸿蒙同行者"的含义:这段路,应用陪我一起走过。下图是成长页在鸿蒙 PC 上的样子:

我的成长页

顶部紫色渐变卡"从学生到鸿蒙开发者的成长之路",下面是认证清单和里程碑时间线——这一页,我把它当成自己毕业季的一份纪念。

八、一次开发,两端运行:手机 + 鸿蒙 PC

这是我这个作品最想强调的技术点,也是最契合鸿蒙特色的地方——同一套 ArkTS 代码,同时跑在鸿蒙 PC 和手机上,无需修改。

8.1 声明支持的设备

module.json5 里声明设备类型,把 PC(2in1)和手机都纳入:

"deviceTypes": ["phone", "tablet", "2in1"]

8.2 弹性布局是多端适配的根基

能做到一套代码跑两端,靠的是全程弹性布局——宽度用 layoutWeight、百分比、Flex 自动换行,绝不写死像素。安全区(手机刘海 / PC 窗口边框)统一在入口计算、全局共享:

const top = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
const bottom = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
AppStorage.setOrCreate('safeTop', px2vp(top.topRect.height));
AppStorage.setOrCreate('safeBottom', px2vp(bottom.bottomRect.height));

前面所有页面都用 @StorageProp('safeTop') 读取安全区来避让,这套逻辑在手机和 PC 上都成立。

8.3 两端各有所长

同样的应用,在两端有不同的使用场景,体验各有千秋。

鸿蒙 PC 端:适合"坐下来认真处理"的场景——写论文时,我把应用开在电脑一侧,用物理键盘往论文助手里贴大段文字、改摘要,效率远高于手机。大屏也让看板的六个阶段、答辩的多个问题一览无余。

手机端:适合"随时查看、随手记录"的场景——躺床上刷一眼看板还剩几天 deadline,或者突然想到一个学习心得,掏出手机在成长页记一笔。

下面是应用跑在手机模拟器(Mate 80 Pro,HarmonyOS 6.1.0)上的样子。先看毕设看板——同样的代码,在手机竖屏上自动适配成紧凑的单列布局:

手机端·毕设看板

再看成长页在手机上的表现,认证清单和时间线依然清晰:

手机端·成长页

从鸿蒙 PC 大屏到手机竖屏,我没有为任何一端单独写界面——这就是"一次开发、多端部署"的威力。对一个人力有限的毕业生来说,这个特性意味着我能用一份精力,交付一个覆盖多设备的完整作品,这在答辩时也是一个很有说服力的亮点。

九、开发环境与真机调试

整个项目用 DevEco Studio 开发。我的目标设备里既有鸿蒙 PC 真机 HUAWEI MateBook Pro,也有手机模拟器 Mate 80 Pro,随时切换验证多端表现:

DevEco Studio 设备选择

从设备下拉列表能看到鸿蒙生态覆盖之广——从穿戴、手机、平板到 MateBook Pro,SDK 已经到了 HarmonyOS 6.1.0。作为学生,能接触到这么完整的一套开发工具链和真机环境,我觉得很幸运。选中设备直接 Run,几百毫秒就构建完成,应用就跑起来了。

十、我踩过的坑(新手血泪)

作为一个鸿蒙新手,这个项目让我踩了不少坑,记录下来给同样起步的同学参考:

坑一:ArkTS 的强类型。 这是最先给我"下马威"的。所有对象字面量必须有显式 interface,不然编译报 arkts-no-untyped-obj-literals。我一开始随手写 { role: 'user', content: x } 就报错,后来老老实实给每个结构都定义了 interfaceChatMessageMilestoneCertItem……)。习惯之后,反而觉得代码更清晰、bug 更少。

坑二:@State 数组不刷新。 增删任务后界面不更新,卡了我半天。后来才知道要 this.list = this.list.slice() 做浅拷贝赋值,或者让 ForEach 的键能反映数据变化(我用 m.id + m.done)。

坑三:大模型输出不听话。 让它"只输出编号问题列表",它偶尔还是加空行、加说明。解决靠解析层用正则容错,绝不能假设模型 100% 听话。

坑四:网络超时。 论文润色、大纲生成的回复很长,默认超时不够,要把 readTimeout 调到 40 秒。

坑五:忘了 client.destroy() http 客户端用完不释放会泄漏,我在 finally 里统一销毁。

坑六:多端安全区。 一开始只考虑了手机刘海,在鸿蒙 PC 上标题被窗口边框挡住。后来统一用 getWindowAvoidArea 计算,两端都正常了。

这些坑,每一个当时都让我抓狂,但填完之后,我对鸿蒙开发的理解就深了一层。踩坑,其实就是成长本身。

十一、总结:作品即成长

回头看这个项目,它对我的意义远超一个毕业设计的分数。

从技术上,我完整走通了一款现代 AI 应用的全流程:声明式 UI、状态管理、本地持久化、大模型接入、提示工程、多端适配。我从一个连 ArkTS 都不认识的学生,变成了一个能独立做出接入 AI、覆盖手机与鸿蒙 PC 双端应用的开发者。

从意义上,这个作品是我毕业季的一面镜子——它管理着我的毕设进度,辅导着我的论文,陪练着我的答辩,也记录着我学鸿蒙的每一步。我用鸿蒙做了一个帮自己度过毕业季的工具,而做这个工具的过程,本身就是我毕业季最重要的收获。
在这里插入图片描述

如果你也是一名即将或正在做毕设的同学,我想把这段经历里最真实的三点建议留给你:

  1. 别怕选新技术。 鸿蒙对新手很友好,官方文档、认证课程、社区都很完善。选一个有前景的方向,本身就是给自己的未来加分。
  2. 让你的作品和你自己产生联系。 我做"毕设伴侣",是因为我正需要它。当作品解决的是你真实的痛点,你会有源源不断的动力,答辩时也讲得出真情实感。
  3. 记录你的成长。 哪怕就像我在应用里做的那样,简单记几行"今天跑通了 Hello World"。回头看时,这些记录会告诉你:你真的走了很远。

毕业季,鸿蒙同行。愿每一位正在为毕设焦头烂额的同学,都能顺利上岸,也都能在鸿蒙这条路上,找到属于自己的成长。

如果你对这个项目、对鸿蒙开发有任何问题,欢迎交流——我们都是这条路上的同行者。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐