乐于分享
好东西不私藏

Day 3|开发软件设置和互动数据界面UI

Day 3|开发软件设置和互动数据界面UI

项目:3D 桌面数字人(Electron + Vue3 + Three.js + @pixiv/three-vrm) 这版是 v3.0 阶段的第一篇总结,按「项目介绍 → 本次实现 → 卡点难点 → 后续规划 → 召唤文案」五段写,通俗版。


一、项目介绍:这个桌宠是啥

dg-man 是一个常驻在电脑桌面的 3D 数字人(二次元风格):

  • 透明无边框小窗,贴右下角,像养了个桌面小人;
  • 会开场打招呼、待机发呆、关软件说拜拜(VRMA 动画驱动);
  • 鼠标点它有反应,旁边透明区域还能点穿到桌面;
  • 技术栈:Electron 39 + Vue3 + Three.js 0.180 + @pixiv/three-vrm 3.5。

当前进度:v2.0.0 骨架已稳(双窗口、穿透、动画、打包都能跑),v3.0 正在填「设置 + 数据」这块。


二、本次实现内容:设置窗口 + 互动数据窗口 + 真持久化

1. 软件设置窗口(需求 3.0)——4 个页签

页签
能干的事
模型设置
切换模型、新建自定义模型(性别/昵称/图片/VRM 文件)
人物设置
人物音色、导演机上传动画文件(.vrma)列表
提醒设置
特殊日提醒(日期/农历/名称/类型),带倒数天数
服务设置
快捷键(支持 Ctrl/Alt/Shift 组合键)、LLM/ASR/TTS 按厂商分组配 API

细节:LLM、ASR、TTS 各有独立的「编辑/完成」按钮,互不共用;API 凭据字段按厂商动态显示(腾讯云 SecretId/SecretKey、火山 AppId/Token、Ollama 无 Key 等)。

2. 设置真正存下来了(不再只是假保存)

  • 接 electron-store:点「保存」写进 userData/settings.json,重开软件自动回填;
  • 密钥加密
    :apiKey / secretId / secretKey / token 等凭据用 Electron safeStorage 加密落盘(enc: 密文),明文不落地。

3. 互动数据窗口(需求 3.1)

右键菜单新增「互动数据」入口,700×600 独立窗口,风格同设置窗:

  • 按天
    (默认):左侧是「今天 / 昨天 / 前天…」列表,点某天右侧看当天完整对话流(用户气泡 / AI 气泡 / 系统事件,统一对话形式,无标签);
  • 全部
    :按日期分组的直接列表;
  • 顶部搜索框实时过滤;底部「导出」可弹框选时间段(今天 / 近 7 天 / 近 30 天 / 自定义)后导出;
  • 与设置窗口互斥:两个不能同时开,冲突时数字人用小气泡提示(不是丑丑的原生弹窗)。

三、卡点难点:这一路踩过的坑

卡点 1:electron-store 装完用不了 —— "Store is not a constructor"

现象:点保存报错 TypeError: Store is not a constructor

根因:electron-store v11 是纯 ESM 包,而主进程被打包成 CJS,import 被转成 require(),ESM 包用 require 拿不到构造函数。

解法:把静态 import 改成动态 import()(CJS 里照样支持),打包器不再做 require 转换,一次解决。

卡点 2:又报 "(intermediate value) is not a function"

现象:修复完上一条又报 {...} is not a function

根因:经典 ASI(自动分号插入) 坑——对象字面量 } 后面直接跟 (await ...),JS 把 {...}(...) 解析成"把对象当函数调用"。

解法:先把 await getSettingsStore() 的结果赋值给一个变量,再调用 .set(),不依赖分号猜测。

卡点 3:服务设置页突然整页空白

现象:设置窗打开,服务设置页白屏。

根因settings:load 在存储为空时返回 { llm: decryptSvc(undefined) },解密函数返回 undefinedObject.assign(form, data) 把 form.llm/asr/tts 整体覆盖成 undefined,模板一访问 .vendor 直接抛错。

解法:load 时只回写存在且非 undefined 的字段 + 渲染侧做安全深合并(嵌套对象逐字段合并),undefined 一律跳过。

卡点 4:Windows 打包 EPERM,构建反复失败

现象pnpm build 报 EPERM: operation not permitted, open 'out/renderer/assets/xxx.vrma',而且每次锁的文件还不一样。

根因:Windows 下残留的 electron/node 进程 + Defender 实时扫描,把旧构建产物(.vrm/.vrma 资源)文件句柄占住,写不进去。属环境问题,不是代码问题。

解法:杀残留进程(taskkill/PowerShell Stop-Process)→ 删被锁的 out/main 或 0 字节 stale 产物 → 重新 pnpm build。每次都有效。

卡点 5:关闭软件,设置窗/互动窗没跟着关

现象:点「关闭软件」,小人退了,设置窗口或互动数据窗口还挂在屏幕上,进程退不干净。

解法:退出路径统一先销毁子窗口(before-quit 里 destroy settingsWin / interactWin),并把互斥提示从 dialog.showMessageBox 换成主窗 app:toast → 数字人 showBubble 气泡,体验更贴产品。


四、后续规划:从"会动"走向"有灵魂"

接下来重点是把数据层和 AI 能力接上:

  1. 接 better-sqlite3(memory.db)
    :表结构已设计好,一张图讲清边界——
    • sessions
       会话、messages 消息 → 对话历史;
    • memories
       长期记忆 → 从聊天里抽事实(偏好/人物/日程),按重要性+最近访问+关键词召回,暂不装向量库
    • interactions
       互动事件日志 → 驱动互动数据窗口(按天分组、按时间段导出);
    • schema_migrations
       版本迁移。
  2. 配置与业务分库
    :设置类(模型/音色/特殊日/API 密钥)继续走 settings.json;会话/记忆/互动走 sqlite,各管各的。
  3. v3.0 能力线
    :LLM 流式对话 → TTS 语音合成 → ASR 语音识别 → 记忆检索让小人"记得住你"→ 主动搭话。
  4. 同步输出
    :每一阶段发小红书/掘金通俗笔记,归档进 docs。

五、📣 召唤文案:一起来做你自己的桌面数字人

设置、持久化、互动记录、数据库这些最不"炫"但最磨人的坑都趟平了,现在是拉人最好的时候——

  • 👉 评论区扣"1",我把关键代码和思路整理好贴给你,照抄就能把小人摆上桌面;
  • 💡 想加功能的一起头脑风暴:会记住你偏好的 AI 聊天、到点提醒喝水、换装换发型……你的脑洞就是下一个版本;
  • 🐣 零基础也别怕:VRM 模型网上能下,前端会一点就行,先把小人跑起来再说;
  • 🤝 组个「桌面数字人共创」小队:你做动画、我做交互、他接 AI,凑一起就是个会撒娇、还记得你的桌面搭子。

别让你的桌面空着了——一起把桌面养出"活物感"吧~

#Electron #Vue3 #ThreeJS #VRM #数字人 #桌宠 #独立开发 #electron-store #SQLite #一起做项目