前言

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

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

写 剪贴台 时,我没有把它当成简单换皮,而是先回到用户动作本身。
客户回复、版本通知、Bug 模板、签名档这些短文本使用频率高,但散在聊天和文档里很难找。
它面向的是每天需要反复复制固定话术、模板和回复的人。

剪贴台这一篇要写得更“短平快”一点。
它解决的不是复杂内容管理,而是每天反复复制固定话术时,少翻一次聊天记录、少漏一个变量。

一、先确认高频文本的真实痛点

1.1 剪贴台真正要解决什么

客户回复、版本通知、Bug 模板、签名档这些短文本使用频率高,但散在聊天和文档里很难找。
这类工具的价值不在收藏,而在一键取用。
如果复制路径不够短,它就会被用户忘掉。

第一版只盯住三个动作:

  1. 快速找到某类短文本
  2. 看清是否有变量需要替换
  3. 一键复制到剪贴板

1.2 为什么不做成大而全

剪贴台如果加太多流程,会违背它本来的效率定位。
我没有做富文本编辑、团队模板库和审批流,先把个人高频复制做好。

能力 当前处理 原因
分类 保留 高频文本需要按场景找
快捷提示 保留 shortcut 字段 方便记住使用入口
来源和用法 简要记录 避免复制错场景
富文本 暂不做 普通文本更稳定、复制更干净

这个边界能让剪贴台保持轻,不会变成另一个文档系统。

二、文件分工对应复制链路

2.1 主要文件职责

文件 职责 这篇关注点
Home.vue 组织复制工作台 把分类、速拷区和编辑区串起来
NoteSidebar.vue 管文本分类 常用回复、通知、Bug 模板分开找
NoteEditor.vue 维护短文本 内容、来源、使用说明都在这里补齐
NoteToolbar.vue 放复制动作 新建、复制、导出、删除
useNotes.ts 管片段数据 本地保存、筛选和排序
useNativeBridge.ts 对接剪贴板 这是剪贴台最核心的桥接点

剪贴台的组件可以简单,但复制动作必须稳定。
这一点比把组件名改得很业务化更重要。

三、整体结构围绕查找和粘贴

3.1 页面结构图

在这里插入图片描述

剪贴台结构图展示分类收件栏、置顶速拷和编辑工作台的关系。

3.2 布局为什么这样分

剪贴台采用的是 分类收件栏 + 置顶速拷区 + 片段编辑台
用户打开它通常不是来阅读的,而是要马上拿走一段文本。

区域 承担的任务 设计注意点
分类收件栏 快速缩小范围 分类名要比描述更醒目
置顶速拷区 放最常用文本 复制按钮要够近
片段编辑台 维护内容和变量 不要把短文本做成长表单
顶部动作 新建、复制、导出 保持路径短

这里的体验关键词是“快读快点”。
如果用户还要思考按钮在哪,就已经慢了。

四、字段设计要包含场景和变量

4.1 剪贴台的核心字段

短文本工具的字段不用多,但必须能避免误用。
特别是模板类话术,来源和使用说明比看起来更重要。

字段 含义 页面位置
id 文本片段标识 状态层
category 使用场景分类 列表/筛选
shortcut 快捷提示或缩写 列表/编辑区
content 要复制的正文 编辑区/剪贴板
source 来源或维护人 编辑区
usage 使用注意点 侧栏/导出

4.2 TypeScript 类型

export interface AppItem {
  id: string;
  category: string;
  shortcut: string;
  content: number | string;
  source: string;
  usage: string;
}

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

这段类型有意保持轻。
剪贴台要服务复制,不该让数据模型比文本本身还复杂。

五、默认片段要像真实回复

5.1 为什么要写种子数据

默认片段要像真实回复。
这样才能看出变量、换行和复制提示是否合理。

我写种子文本时会看:

  • 分类是否真实
  • 正文能不能直接复制
  • usage 是否提醒变量替换

5.2 示例数据

export const seedAppItems: AppItem[] = [
  {
    id: 'jiantie_tai-001',
    category: '客户回复',
    shortcut: 'delay-reply',
    content: '收到,我们会在今天 18:00 前同步处理进展。',
    source: '售后支持话术',
    usage: '发送前确认时间点是否需要替换。',
  },
];

这种短文本能测试一键复制、列表截断和使用说明展示。

六、状态层处理保存和复制次数

6.1 composable 的职责

useNotes.ts 这层我更愿意把它理解成“当前工具的数据服务”。
页面不应该直接处理太多 localStorage、排序和导出拼接。

const STORAGE_KEY = 'jiantie-tai';

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 我会明确写成 jiantie-tai
这样做可以避免不同工具之间互相读到旧数据。

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

七、筛选排序服务最近常用

7.1 computed 更适合承接派生视图

筛选、搜索、排序这些逻辑如果直接写在模板里,很快会让页面变得难读。
我更倾向于让状态层先准备好可展示列表。

const keyword = ref('');
const filter = ref<'all' | 'category'>('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 排序服务于场景

文本复制助手里,排序不是“哪个字段容易写就按哪个排”。
它应该服务用户打开应用时最想看到的那批内容。

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

八、Vue 页面突出一键可取

8.1 Home.vue 只做编排

我不希望 Home.vue 变成所有逻辑的大杂烩。
它更适合负责页面骨架和组件之间的数据传递。

<template>
  <main class="jiantie_tai-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 不要只留下标题和正文

剪贴台如果只保留标题和正文,就会退回普通记事本。
所以编辑器必须把核心字段摆出来。

<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.category" />
    <textarea v-model="item.source" />
  </form>
</template>

9.2 表单不是越多越好

我会优先放能影响用户判断的字段。
辅助字段可以放到右侧信息区,或者只在导出时使用。

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

10.1 工具栏放哪些按钮

工具栏最容易变成按钮仓库。
剪贴台里我只保留和主流程强相关的动作。

  • 新增片段
  • 置顶常用文本
  • 一键复制
  • 按分类筛选
  • 维护使用说明
  • 导出片段库

10.2 复制摘要

function buildAppSummary(item: AppItem) {
  return [
    '# 剪贴台摘要',
    '- category: ' + item.category,
    '- shortcut: ' + item.shortcut,
    '- content: ' + item.content,
    '- source: ' + item.source,
  ].join('\n');
}

复制摘要的好处是很实际的。
用户不一定每次都要导出文件,有时只是想把当前内容发到聊天窗口或文档里。

十一、桥接层专心处理剪贴板

11.1 桥接层只暴露稳定动作

页面不应该知道底层是 Electron clipboard,还是 OpenHarmony 侧的能力。
它只需要知道“复制”“导出”“通知”这些动作。

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 不能只是把字段拼起来。
它最好离开应用以后也能被看懂。

function exportAppMarkdown(item: AppItem) {
  return [
    '# 剪贴台',
    '',
    '> 由 剪贴台 导出。',
    '## category', String(item.category ?? ''),
    '## shortcut', String(item.shortcut ?? ''),
    '## content', String(item.content ?? ''),
    '## source', String(item.source ?? ''),
    '## usage', String(item.usage ?? ''),
  ].join('\n');
}

12.2 导出动作和通知联动

async function exportCurrent() {
  if (!currentItem.value) return;
  const markdown = exportAppMarkdown(currentItem.value);
  await bridge.copyText(markdown);
  await bridge.notify('剪贴台内容已复制为 Markdown');
}

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

十三、主进程加载保证常驻可用

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

桌面应用最常见的白屏问题之一,是生产环境还在访问开发服务器。
所以主进程里一定要把加载逻辑分清楚。

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 只注入必要接口

const { contextBridge, ipcRenderer } = require('electron');

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

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

十四、贴板样式要快读快点

14.1 视觉气质服务使用场景

剪贴台的视觉方向是:暖色、轻快、高频操作感
这个判断会影响间距、字号、卡片密度和按钮重量。

.jiantie_tai-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 先确认前端产物能生成

写文章之前,我会先跑一次构建。
这一步很朴素,但能挡住不少低级问题。

cd ../../electron_for_harmony/electron-openharmony-vue3-18/ohos_hap/web_engine/src/main/resources/resfile/resources/app/vue-app
npm install
npm run build

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

rg "jiantie-tai|/copy-assistant|剪贴台" src package.json
rg "TODO|旧标题|测试数据" src

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

十六、这版贴板工具的经验

16.1 先换问题,再换界面

剪贴台最重要的不是页面长什么样,而是它先回答了一个明确问题:客户回复、版本通知、Bug 模板、签名档这些短文本使用频率高,但散在聊天和文档里很难找。
问题清楚以后,字段、布局和按钮才知道往哪里收。

16.2 哪些东西可以复用

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

16.3 哪些东西不要硬套

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

十七、后续可以补的效率能力

剪贴台现在已经能满足高频短文本的保存、查找和一键复制。
真要继续加功能,我会优先从这些方向补:

  1. 增加变量占位提醒,比如时间、姓名、版本号
  2. 记录最近复制的片段
  3. 支持置顶最常用话术
  4. 增加重复内容检测
  5. 给不同分类设置默认前缀或签名

剪贴台后续要继续缩短复制路径,任何复杂功能都不能拖慢取用。

十八、发布前做一次片段检查

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

检查项 结果 说明
标题和主题一致 通过 剪贴台实战:把高频短文本整理成一键可取的桌面片段库
图片存在 通过 保留项目结构图或运行效果图
代码块数量 通过 覆盖类型、状态、组件、桥接、导出、构建
资源链接 通过 保留社区和官方文档入口

总结

剪贴台这一版解决的是高频短文本散落的问题。它把常用回复、模板和签名档集中起来,让用户需要粘贴时少翻一次聊天记录。
剪贴台的体验好坏,基本取决于用户能不能在几秒钟内拿走文本。
分类、快捷提示、来源和 usage 都是为了减少误复制,而不是为了把短文本管理做复杂。

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


相关资源:

Logo

一站式 AI 云服务平台

更多推荐