鸿蒙electron跨端框架PC墨案写作实战:把 Markdown 正文区做成桌面写作的中心

前言

欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态:鸿蒙PC开发者社区 :https://harmonypc.csdn.net/

项目开源地址:https://AtomGit.com/lqjmac/ele-moanxiezuo

墨案写作这个小工具看起来轻,但真正落地时要先把主路径想清楚。

写作工具不能让按钮和侧栏抢走注意力,正文区要足够安静,同时还要支持状态、摘要和导出。

它面向的是长期写技术文章、教程、说明文档的人。

我写这一篇时更关心一个问题:桌面写作工具怎样既不打断输入,又能在交付时把摘要、状态和正文带出去。

下面不会按模板报目录,而是顺着"打开、写下去、整理、导出"这条线看实现。

一、先确认写作工具的主角

1.1 墨案写作真正要解决什么

写作工具不能让按钮和侧栏抢走注意力,正文区要足够安静,同时还要支持状态、摘要和导出。

如果这点没想明白,页面很容易变成工具按钮展览,正文反而没有位置。

这一版我把范围压在三个动作里:

  1. 先能安心写,而不是先配置一堆参数
  2. 每篇稿件都要带状态和摘要,方便回头判断进度
  3. 复制和导出要顺手,因为内容最终会离开这个工具

1.2 为什么不做成大而全

Markdown 写作工具如果一开始就塞进太多功能,会很快变成一个难维护的综合面板。

我没有做大纲树、评论流、协作编辑这类能力,原因不是它们没价值,而是它们会把第一版的重心从"写作"拉走。

取舍 这一版的处理 我的考虑
正文编辑 放在页面中心 写作工具首先要让输入持续
状态管理 保留草稿、整理、可发布等判断 长文不是一次写完的
导出动作 做成工具栏里的主动作 写完以后要能进入别的发布链路
复杂协作 暂时不碰 会引入另一套权限和冲突问题

边界收紧以后,页面就不用靠堆功能证明自己。

这类工具越安静,用户越容易真的把文章写完。

二、文件分工围绕写作闭环

2.1 主要文件职责

文件 职责 这篇关注点
Home.vue 搭起写作台 控制书架、编辑区、信息区之间的节奏
NoteSidebar.vue 管文稿列表 让用户快速回到正在写的稿件
NoteEditor.vue 承接正文输入 标题、摘要、正文都在这里沉淀
NoteToolbar.vue 放流转动作 新建、复制、导出这些动作不要藏太深
useNotes.ts 处理稿件状态 保存、切换、筛选和排序都先在这里收住
useNativeBridge.ts 对接桌面能力 剪贴板、通知这类能力给页面一个稳定入口

组件名依然比较通用,但讲法要落在写作场景里。

否则读者只会看到一套组件模板,看不到"墨案写作"自己的工作流。

三、整体结构服务长文写作

3.1 页面结构图

墨案写作结构图说明了文稿书架、正文区和信息侧栏的关系。

3.2 布局为什么这样分

墨案写作采用的是 文稿书架 + Markdown 正文区 + 文档信息侧栏

这不是为了显得信息量大,而是把写作中的三个动作拆开:找稿、写稿、判断稿件状态。

区域 承担的任务 设计注意点
左侧文稿书架 回到某篇文章 标题和状态足够,别把摘要也塞进去
中间正文区 长时间输入 留出足够行高和空白,让文本成为视觉中心
右侧信息栏 看摘要、状态、更新时间 只放辅助判断,不抢编辑焦点
顶部工具栏 新建、复制、导出 动作少一点,用户不用每次重新找按钮

桌面端窗口宽的时候可以把三块都铺开;窗口变窄时,也要保证正文区仍然是最舒服的那块。

四、字段设计要包含标题、摘要和标签

4.1 墨案写作的核心字段

字段不是数据库截图,而是这个写作工具的价值观。

我希望每条稿件一眼能看出"写到哪了、准备表达什么、正文在哪里"。

字段 含义 页面位置
id 稳定识别一篇稿件 状态层
title 文稿标题,也是列表里的第一识别点 列表/编辑区
status 草稿、整理中、可发布等写作阶段 列表/侧栏
summary 给未来的自己看的摘要 编辑区
content Markdown 正文 编辑区
updatedAt 最近修改时间 侧栏/导出

4.2 TypeScript 类型

ts 复制代码
export interface AppItem {
  id: string;
  title: string;
  status: string;
  summary: number | string;
  content: string;
  updatedAt: string;
}

export type AppFilter = 'all' | 'active' | 'archived';

类型本身很朴素,重点是别让页面临时发明字段。

写作工具最怕同一篇稿在不同组件里有不同叫法。

五、默认稿件要像真实草稿

5.1 为什么要写种子数据

写作工具第一次打开如果是彻底空的,用户很难判断摘要、状态和正文该怎么配合。

所以默认稿件要像一篇真的待完善文章,而不是演示用占位符。

我写种子数据时看三件事:

  • 标题要像真实选题
  • 摘要要能解释正文方向
  • 状态要能影响用户下一步动作

5.2 示例数据

ts 复制代码
export const seedAppItems: AppItem[] = [
  {
    id: 'moan_xiezuo-001',
    title: '鸿蒙 PC 端写作工具体验记录',
    status: '整理中',
    summary: '沉浸写作',
    content: '先记录主线,再补代码片段,最后整理为可发布 Markdown。',
    updatedAt: '2026-05-23 10:30',
  },
];

有了接近真实的稿件,列表宽度、正文滚动、导出格式这些问题会更早暴露出来。

六、状态层处理保存和切换

6.1 composable 的职责

useNotes.ts 这层我更愿意把它理解成"当前工具的数据服务"。

页面不应该直接处理太多 localStorage、排序和导出拼接。

ts 复制代码
const STORAGE_KEY = 'moan-xiezuo';

const items = ref<AppItem[]>(loadItems());
const activeId = ref(items.value[0]?.id ?? '');

function persist() {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(items.value));
}

function loadItems() {
  const raw = localStorage.getItem(STORAGE_KEY);
  return raw ? JSON.parse(raw) : seedAppItems;
}

6.2 本地存储 key 一定要独立

这里的 key 我会明确写成 moan-xiezuo

这样做可以避免不同工具之间互相读到旧数据。

本地数据一旦串了,页面看起来像小问题,实际会让调试和截图都变得很难判断。

七、筛选排序服务稿件回找

7.1 computed 更适合承接派生视图

筛选、搜索、排序这些逻辑如果直接写在模板里,很快会让页面变得难读。

我更倾向于让状态层先准备好可展示列表。

ts 复制代码
const keyword = ref('');
const filter = ref<'all' | 'title'>('all');

const visibleItems = computed(() => {
  const text = keyword.value.trim().toLowerCase();
  return items.value
    .filter(item => JSON.stringify(item).toLowerCase().includes(text))
    .sort((a, b) => String(b.id).localeCompare(String(a.id)));
});

7.2 排序服务于场景

Markdown 写作工具里,排序不是"哪个字段容易写就按哪个排"。

它应该服务用户打开应用时最想看到的那批内容。

  1. 未处理内容优先出现
  2. 置顶或高优先级内容靠前
  3. 最近更新内容不要沉底

八、Vue 页面只组织写作空间

8.1 Home.vue 只做编排

我不希望 Home.vue 变成所有逻辑的大杂烩。

它更适合负责页面骨架和组件之间的数据传递。

vue 复制代码
<template>
  <main class="moan_xiezuo-page">
    <NoteToolbar
      @create="createItem"
      @copy="copyCurrent"
      @export="exportCurrent"
    />
    <section class="workspace">
      <NoteSidebar :items="visibleItems" @select="selectItem" />
      <NoteEditor :item="currentItem" @update="updateItem" />
    </section>
  </main>
</template>

8.2 组件之间的边界

组件 应该知道什么 不应该知道什么
NoteToolbar 当前能触发哪些动作 具体字段如何存储
NoteSidebar 列表、筛选、选中项 导出 Markdown 细节
NoteEditor 当前对象字段 全局搜索逻辑

边界清楚以后,后续改样式和改字段都会轻很多。

九、编辑器要尊重正文输入

9.1 不要只留下标题和正文

墨案写作如果只保留标题和正文,就会退回普通记事本。

所以编辑器必须把核心字段摆出来。

vue 复制代码
<script setup lang="ts">
defineProps<{ item: AppItem | null }>();
const emit = defineEmits<{ update: [item: AppItem] }>();
</script>

<template>
  <form v-if="item" class="editor-form">
    <input v-model="item.title" />
    <textarea v-model="item.content" />
  </form>
</template>

9.2 表单不是越多越好

我会优先放能影响用户判断的字段。

辅助字段可以放到右侧信息区,或者只在导出时使用。

十、工具栏动作围绕复制和导出

10.1 工具栏放哪些按钮

工具栏最容易变成按钮仓库。

墨案写作里我只保留和主流程强相关的动作。

  • 新建文稿
  • 编辑 Markdown
  • 切换状态
  • 复制正文
  • 导出文稿
  • 保存通知

10.2 复制摘要

ts 复制代码
function buildAppSummary(item: AppItem) {
  return [
    '# 墨案写作摘要',
    '- title: ' + item.title,
    '- status: ' + item.status,
    '- summary: ' + item.summary,
    '- content: ' + item.content,
  ].join('\n');
}

复制摘要的好处是很实际的。

用户不一定每次都要导出文件,有时只是想把当前内容发到聊天窗口或文档里。

十一、桥接层收住文件和剪贴板

11.1 桥接层只暴露稳定动作

页面不应该知道底层是 Electron clipboard,还是 OpenHarmony 侧的能力。

它只需要知道"复制""导出""通知"这些动作。

ts 复制代码
export function useNativeBridge() {
  const api = window.ohosBridge ?? window.electronAPI;

  async function copyText(text: string) {
    if (api?.copyText) return api.copyText(text);
    return navigator.clipboard.writeText(text);
  }

  async function notify(message: string) {
    if (api?.notify) return api.notify(message);
  }

  return { copyText, notify };
}

11.2 为什么要有浏览器兜底

开发阶段经常会直接跑 Vite。

如果没有浏览器兜底,页面调试会被原生环境绑得太死。

十二、导出 Markdown 保持可发布

12.1 导出内容要能独立阅读

导出的 Markdown 不能只是把字段拼起来。

它最好离开应用以后也能被看懂。

ts 复制代码
function exportAppMarkdown(item: AppItem) {
  return [
    '# 墨案写作',
    '',
    '> 由 墨案写作 导出。',
    '## title', String(item.title ?? ''),
    '## status', String(item.status ?? ''),
    '## summary', String(item.summary ?? ''),
    '## content', String(item.content ?? ''),
    '## updatedAt', String(item.updatedAt ?? ''),
  ].join('\n');
}

12.2 导出动作和通知联动

ts 复制代码
async function exportCurrent() {
  if (!currentItem.value) return;
  const markdown = exportAppMarkdown(currentItem.value);
  await bridge.copyText(markdown);
  await bridge.notify('墨案写作内容已复制为 Markdown');
}

这样用户完成导出以后能马上得到反馈。

十三、主进程加载保证编辑稳定

13.1 开发环境和生产环境分开

桌面应用最常见的白屏问题之一,是生产环境还在访问开发服务器。

所以主进程里一定要把加载逻辑分清楚。

js 复制代码
const path = require('path');

function resolveRendererUrl() {
  if (process.env.VITE_DEV_SERVER_URL) {
    return process.env.VITE_DEV_SERVER_URL;
  }
  return `file://${path.join(__dirname, '../dist/index.html')}`;
}

mainWindow.loadURL(resolveRendererUrl());

13.2 preload 只注入必要接口

js 复制代码
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  copyText: text => ipcRenderer.invoke('copy-text', text),
  notify: message => ipcRenderer.invoke('notify', message),
});

接口少一点,维护起来更安心。

十四、写作样式要让正文安静

14.1 视觉气质服务使用场景

墨案写作的视觉方向是:安静、正文优先、轻编辑器感

这个判断会影响间距、字号、卡片密度和按钮重量。

css 复制代码
.moan_xiezuo-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: #f7f8fb;
  color: #1f2937;
}

.workspace {
  display: grid;
  grid-template-columns: 280px minmax(0, 1fr);
  gap: 16px;
  min-height: 0;
}

14.2 滚动区要提前处理

桌面应用窗口经常被用户缩小。

如果滚动区没有处理好,内容一多就会挤成一团。

  • 左侧列表要能独立滚动
  • 编辑区不能把工具栏挤出屏幕
  • 右侧信息区要允许内容截断和换行

十五、构建后检查写作主题

15.1 先确认前端产物能生成

写文章之前,我会先跑一次构建。

这一步很朴素,但能挡住不少低级问题。

bash 复制代码
cd ../../electron_for_harmony/electron-openharmony-vue3-11/ohos_hap/web_engine/src/main/resources/resfile/resources/app/vue-app
npm install
npm run build

15.2 再确认关键文件没有串主题

bash 复制代码
rg "moan-xiezuo|/markdown|墨案写作" src package.json
rg "TODO|旧标题|测试数据" src

构建通过不代表体验完美,但至少说明当前页面和依赖关系是站得住的。

十六、这版写作工具的经验

16.1 先换问题,再换界面

墨案写作最重要的不是页面长什么样,而是它先回答了一个明确问题:写作工具不能让按钮和侧栏抢走注意力,正文区要足够安静,同时还要支持状态、摘要和导出。

问题清楚以后,字段、布局和按钮才知道往哪里收。

16.2 哪些东西可以复用

  • 清晰的页面、状态层、桥接层分工
  • 状态层和本地存储节奏
  • 复制、导出、通知这组桌面动作
  • 开发环境与生产环境分开的加载逻辑

16.3 哪些东西不要硬套

  • 旧的数据字段
  • 旧的默认文案
  • 旧的视觉重心
  • 旧的排序规则

十七、后续可以补的写作能力

墨案写作现在已经能覆盖从起稿到导出的基本路径。

真要继续加功能,我会优先从这些方向补:

  1. 增加字数统计和阅读时长估算
  2. 补充专注模式,只保留正文区
  3. 支持按发布平台导出不同 Markdown 模板
  4. 给草稿增加历史版本对比
  5. 增加常用标题和摘要片段

这些能力都围绕写作本身展开,不会把工具带向复杂协作平台。

十八、发布前做一次稿件检查

发布前我会按下面这张表再扫一遍,尤其确认 主题一致性 和可发布性。

检查项 结果 说明
标题和主题一致 通过 墨案写作实战:把 Markdown 正文区做成桌面写作的中心
图片存在 通过 保留项目结构图或运行效果图
代码块数量 通过 覆盖类型、状态、组件、桥接、导出、构建
资源链接 通过 保留社区和官方文档入口

总结

墨案写作这版的核心价值,是把 Markdown 写作工具 从一个想法落成了一个能操作、能保存、能复制、能导出的桌面工具。

这类工具最难的不是把按钮摆满,而是让正文区一直保持主角位置。

只要这个判断不变,后面加统计、模板或历史版本,都不会把写作体验带偏。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

相关推荐
Swift社区8 小时前
HarmonyOS 鸿蒙PC平台三方库移植:使用 vcpkg 移植 libzen(ZenLib)
华为·harmonyos
ai安歌8 小时前
鸿蒙PC:鸿蒙electron跨端框架PC影像巡检台实战:把图片管理做成可复核的本地工作流
华为·electron·harmonyos
G_dou_8 小时前
鸿蒙结合Flutter 实战:XMB Tracker
harmonyos
前端不太难8 小时前
鸿蒙 PC:从“用户点击”到“AI 调度”
人工智能·华为·harmonyos
空中海9 小时前
HarmonyOS知识图谱和回顾
华为·知识图谱·鸿蒙
大雷神9 小时前
HarmonyOS APP<<古今职鉴定>>开源教程第26篇:【完整案例】职业性格测试开发
harmonyos·arkts·鸿蒙·古今职鉴
ai安歌9 小时前
鸿蒙PC:鸿蒙electron跨端框架PC链接雷达实战:把本地收藏夹升级成可巡检的链接管理面板
华为·electron·harmonyos
ai安歌10 小时前
鸿蒙PC:Qt适配OpenHarmony实战【昼刻】:用 Qt Quick 做一个可运行的鸿蒙时钟应用
qt·华为·harmonyos
想你依然心痛10 小时前
HarmonyOS 6 悬浮导航 + 沉浸光感:打造鸿蒙智能体驱动的沉浸式音乐创作协作工坊
华为·ar·harmonyos·智能体