Drsakura
1ffcc1ea07
feat: add admin web updates and verified username changes
...
Add isolated update jobs with database backups, progress logs and application rollback. Remove default account hints and prevent default admin recreation after renaming.
Enforce customer and order search scopes, secure session cookies, fix quotation rounding and amount wording, and update backend dependencies. Add API and deployment regression coverage.
2026-10-22 17:24:19 +08:00
Drsakura and Claude Opus 5
dc607e4ea3
perf(static): 带 hash 的前端资源缓存一年,index.html 不缓存
...
线上实测 /assets/index-u1PUen4p.js 返回的是 express.static 的默认
Cache-Control: public, max-age=0 —— 每开一次页面就要为每个 JS/CSS 跑一趟
回源校验。而这些文件名里本来就带内容 hash,内容一变文件名就变。
分两档:
assets/* max-age=31536000, immutable(连 304 都不用问)
index.html no-cache
index.html 必须不缓存:它文件名固定、里面引用的才是带 hash 的资源名。
缓存住的话发了新版用户还拿着旧 index.html、指向旧资源,等于白更新。
SPA fallback 那条单独加了一遍 no-cache —— 它走 sendFile,不经过
express.static 的 setHeaders,/orders 这类深链拿到的同样是 index.html。
Co-Authored-By: Claude Opus 5 <[email protected] >
2026-10-22 16:17:44 +08:00
Drsakura and Claude Opus 5
3380dbc9d7
chore(release): v1.1.0
...
本次内容:
fix 客户联系人新增 / 跟进编辑补归属校验(两处越权,已实测利用成功再封堵)
feat 订单支出台账 —— 快递费、吊卡费等杂费挂到订单上,带跨币种汇率快照
feat 产品速查浮层 Ctrl/⌘+J —— 报价时就地查采购价、起订量、箱规
三处 package.json 一起改:lib/updater.js 的 currentVersion() 按
「仓库根 → backend」的顺序找版本号,只改一处的话,部署形态变了版本号就不对。
Co-Authored-By: Claude Opus 5 <[email protected] >
2026-10-22 15:23:21 +08:00
Drsakura and Claude Opus 5
226949fa53
feat(sku): 产品速查浮层(Ctrl/⌘+J)
...
报价报到一半想确认某个货的采购价、起订量、箱规时用。跟已有的两个东西都
不是一件事:
⌘K 全局搜索 是「找到后跳过去」—— 一跳走,正在写的报价就断了
SkuPicker 是「挑一个塞进表单」—— 要点产品组再点货号,两层
速查 是「就地看数」—— 不跳转、不改任何东西,Esc 回到刚才的地方
· 新接口 GET /api/sku/quick:一次把采购价、起订量、供应商、规格、箱规、
阶梯价全带回来,不返回 path(它不是用来跳转的)。
· 货号精确/前缀命中排最前。报价时多半直接敲货号,让它跟在一堆品名模糊
匹配后面就等于没做速查。
· 箱规拼成「40×30×25cm / 12.5kg / 装 24」一句话 —— 报价要的是这一句,
不是四个分开的数字。
· 点货号或价格可复制;clipboard API 在非 HTTPS 内网下不可用,退回
execCommand。
· 没占 ⌘K,因为那已经是全局搜索了,抢掉会打断老习惯。
· 页脚写死「价格是采购价,不是报价」防手滑。
Co-Authored-By: Claude Opus 5 <[email protected] >
2026-10-22 15:23:10 +08:00
Drsakura and Claude Opus 5
f32a2daaef
feat(order): 订单支出台账(快递费/吊卡费等杂费)
...
订单上原本只有「卖给客户多少钱」,而一张单真实花出去的还有寄样品的快递费、
印吊卡的费用、商检报关拖车。不记下来就算不出这单赚没赚 —— 小单子几百块
杂费能把毛利吃掉一截。
· 新表 order_expenses,一张订单挂多笔:类型/金额/币种/日期/收款方/单号/
已付未付/备注。单独成表而不是往 orders 加列,因为杂费是多笔不定项的,
寄三次样品就是三条快递费,各有单号;塞进 orders 只能存一个合计,事后
对不出「这 800 块是哪来的」。
· 跨币种强制填汇率并存快照。杂费基本是人民币、订单常是美元;不存快照的话
汇率一变历史订单毛利跟着变,账永远对不上。同币种强制按 1,防止前端传个
7.2 把美元支出又乘一遍。
· 新权限点 order.expense,不复用 order.edit。掏钱寄样品的是销售业务员,
而销售业务组只有 order.view —— 挂在 order.edit 上要么他记不了账,要么
为了让他记账得把「改订单金额」一起给他。启动时 syncNewPermissions()
会自动把这个新点补给四个默认组。
· 订单列表加「杂费」列(合计 + 未付)。SUM 外层再 ROUND 一次:不加会漏出
348.15999999999997 这种浮点尾巴直接显示在页面上。
· 金额逐行取两位再相加,保证明细几行加起来等于合计。
Co-Authored-By: Claude Opus 5 <[email protected] >
2026-10-22 15:22:56 +08:00
Drsakura and Claude Opus 5
3e9fd2dc97
fix(customer): 联系人新增与跟进编辑补上归属校验
...
两个端点都只有 requirePermission('customer.edit'),而那只说明「这个人有权
编辑客户」,没说明「有权编辑**这一条**客户」。实测一个 sales_rep 账号:
GET /api/customers/4 → 404(读是隔离的)
POST /api/customers/4/contacts → 201(写进去了)
PATCH /api/customers/4/follow-ups/12 → 200
把「客户确认 3000 套订单」改成了「客户取消了订单」
也就是说一个销售能往看不见的客户里插联系人(还能把自己设成主联系人),
还能改写另一个销售客户的跟进历史。
两处都有对照组:紧挨着的 PATCH /contacts 和 POST /follow-ups 一直有这道
检查,所以是漏了而不是设计如此。补上后重放两个利用都是 403,归属人自己
仍然 201/200。
Co-Authored-By: Claude Opus 5 <[email protected] >
2026-10-22 15:22:15 +08:00
Drsakura
691f315341
docs(readme): 功能模块与开发计划对齐代码现状
...
功能模块:按应用实际的导航结构重写,补上此前完全没提的模块——
系统管理(用户/权限组/系统日志三级授权与审计)、合同清洗中心、
客户公海、采购单管理、报价单与询盘管理、工作台的汇率计算器、
时区时钟与 AI 助手。
开发计划:订正大规模过期状态。Phase 2(SKU 迁移、阶梯价格、合同解析、
产品图片)与 Phase 3 主体均已实现,原文误标为全部未完成;Phase 4 补标
已完成的订单状态管理,保留未实现的订单备注与 PDF 导出;Phase 5 标出
已完成的 AI 智能分析与客户公海线索转化,保留未做的 GoodJob 集成与
数据源适配。商机管道视图注明 deals 表已建但前端界面待实现。
2026-09-18 11:05:05 +08:00
Drsakura
13d67c804f
feat: 多用户权限体系、AI 助手、SKU 目录与合同清洗(开源准备)
...
本地长期开发成果的整体入库快照,包含后端鉴权/权限、AI 接入、合同解析、
SKU 目录等模块,以及前端相应页面与样式改版。
后端
- 新增鉴权与会话:scrypt 密码哈希、HttpOnly cookie(happy_sid)、登录失败锁定
- 新增「角色 → 权限组 → 权限点」三级权限模型与审计日志
- 新增 AI 提供商接入与预设(兼容 OpenAI 协议,内置 DeepSeek / 通义千问等)
- 新增合同解析、合同文件保留策略、客户归属与访问控制
- 新增 SKU 目录、组织品牌、新客开发、系统日志等路由
前端
- 新增登录页与用户 / 权限组 / 系统日志管理页
- 新增客户详情、客户公海、询盘详情、报价编辑器、合同清洗中心
- 新增 AI 配置、组织品牌、包装参数、SKU 选择器等组件
- 新增英文大写金额、未保存改动拦截、抽屉与骨架屏
- 新增 stage1 样式层;产品、供应商、订单等页面改版
- EmbeddedSku 页面并入现有产品体系后删除
开源准备
- 新增 AGPL-3.0 LICENSE
- README:移除未启用的 SQLCipher 加密声明(改为如实的「已知限制」)、
补充环境变量说明、许可证由「私有项目」改为 AGPL-3.0
- 移除历史遗留的空文件 _index_backup.txt
- 文档中的项目路径更新为 happy-export
2026-09-17 14:53:02 +08:00
Drsakura
436519e0a6
feat(embed): 合同导入与工作台融成一体(主题/字体/配色/滚动全对齐)
...
深色外壳里嵌一张米白纸、外加两层滚动条,是「没浑然一体」的根源:
- 宿主把当前主题下的设计令牌 postMessage 给嵌入页(跨源读不到对方的
CSS 变量),两边共用同一套配色/字体/圆角;切主题即时跟随,不用刷新
(src 里的 theme 只作首帧初值,避免换主题触发 iframe 重载白闪)
- 收起嵌入页里重复的部件:自带侧栏、顶栏指标条(货号总数/供应商/待核对)、
右下角「问一句」悬浮球(宿主已有小皮)
- 只留一层滚动:路由区固定为视口高度,滚动交给 iframe 内部
- 输入框沿用宿主的「无方框」规则:去掉描边与焦点光环,改底色区分
- 页面头去掉「新窗口打开」(打开的是裸 SKU 页),刷新改成图标按钮
- 令牌名对不上时会告警,免得嵌入页悄悄回落到旧配色
2026-09-11 12:53:22 +08:00
Drsakura
453f3b1583
fix(header): 顶栏搜索框补 type="text"(最后一个裸 input,避免再掉进浏览器默认样式)
2026-09-11 11:48:07 +08:00
Drsakura
fa4dd6482b
feat(ui): 助手正名「小皮」+ 全站输入框去掉描边方框
...
助手改名
- 「小屁」→「小皮」(AI 浮窗标题/按钮/占位文案/工作台引导语 + 注释)
输入框一律无方框(靠底色区分,聚焦换成 primary 底色)
- 根因一:全局选择器写成 input[type="text"] 列表,AI 浮窗输入框没写 type
完全没吃到样式,渲染成浏览器默认的 2px 灰直角框(就是「丑出天际」那个),
顺手补上 input:not([type]) 兜底 + 给该输入框补 type="text"
- 根因二:聚焦时同时叠了 input[type] 的 1px 描边 + 3px 光环,以及
:focus-visible 的 2px 外描边(鼠标点文本框也会命中),一共三层方框
- 覆盖范围:全局 form 控件、顶栏搜索、汇率金额、备忘录添加行、
日历添加日程、跳转浮层年月输入框、设置里 API Key / 下拉
2026-09-11 11:45:20 +08:00
Drsakura
850abe9327
feat(calendar): 跳转浮层年月改为直接输入,回车快速跳转
...
- 点开 🗓 后的浮层标题由「弹出拨盘」改为输入框:前 4 位是年、后 1~2 位是月,
只填 4 位则沿用当前月;回车跳转并关闭编辑态,Esc 取消且不关浮层
- 非数字自动过滤、上限 6 位;月越界或年份越界标红提示且保留焦点
- 进入编辑态时把「2026年9月」换成「202609」并全选,两种触发(鼠标按下/聚焦)都稳
- 工具条上的年月文字仍保留 iOS 拨盘入口
2026-09-11 11:22:04 +08:00
Drsakura
0453a49e4e
feat(calendar): 年月拨盘(iOS 闹钟式滚轮)替换工具条的月份文字
...
- 新增 components/MonthYearWheel.jsx:年、月两列滚轮
· scroll-snap 吸附 + 指针按住拖动 + 鼠标滚轮/触摸板,停稳 140ms 后落值
· 滚动过程中实时算居中行,中间行放大加粗高亮、相邻行缩小淡出(像苹果拨盘)
· 点某一行直接跳到该行;「回到今天」一键回当月
· 年份范围默认前后 12/20 年,两侧夹在视口内,下方放不下自动翻到上方
- 两个入口:
· 工具条上的「2026年9月」由 <strong> 换成按钮,点击弹出拨盘,再点一次收起
· 日期跳转浮层里的标题同样可点:点击后浮层收起、同位弹出拨盘
- 拨盘选完年月,主日历与跳转浮层同步过去
修掉一个竞态:两列在同一帧内先后停稳时,后一次会读到渲染期闭包里的旧值,
把前一次的结果覆盖(滚到 2028年12月 会退回 2026年12月)。
改为组件内用 ref 保存最新年月、每次落值先更新 ref 再回调。
顺带修正跳转浮层标题多出来的一个右花括号。
2026-09-11 11:06:36 +08:00
Drsakura
4c3ec995b6
feat(workspace): 日历再瘦身 + 右侧面板等高填满 + 日期跳转按钮 + 小屁浮窗化
...
日历
- 单元格 72→60px,日号 19→17px、字号 11→10px,事件条与周标同步收紧,
标题区 padding 与提示文案一起压小
- 工具条右边的「月」按钮删掉,换成日历小图标按钮:悬浮/展开变蓝(primary 实底),
点击弹出迷你月历(自带年月切换 + 回到今天),点某天即跳月并选中该日
右侧面板
- 「今日 to do」与「待办事项」改成按列高 58/42 平分,列表自身 flex 撑满并可滚动;
之前待办被挤到底部只剩一条空文案,现在两块都填满整列
- 跳转浮层 z-index 82,压过小屁浮窗(78);设置遮罩提到 90,保证仍是最高层
AI 助手 → 小屁
- 全屏遮罩 + 右侧整条抽屉删掉,改成右上角 348px 宽浮动小窗:
可拖动(位置记 localStorage,越界自动夹回视口)、可最小化成小条、双击标题栏切换
- App.jsx 不再因为小屁打开而 inert 主界面(只有设置才是模态),
所以可以一边让小屁干活一边自己点系统:实测开着窗导航、选日期、填备忘录都正常
- 文案全部改名小屁,顶栏按钮、面板标题、工作台摘要里的引导语一并更新
顺手
- 顶栏搜索的 ⌘K 提示块加 nowrap,1024 宽度下不再折成两行
2026-09-10 23:56:52 +08:00
Drsakura
b9d6296591
feat(workspace): 日历瘦身 + 右侧「今日 to do / 选中日待办」双面板
...
日历
- 行高 92→72px,行程条只留 2 条(超出折成 +N 并压缩成小 chip),标题区与周标同步收窄
- 选中态重做:去掉了原来生硬的 2px 内描边,改成单元格内嵌圆角环 + 淡入缩放动画;
「今天」改为日号描边圆环,与「选中」的实心日号区分开
- 侧栏宽度不足时(≤1200px)自动改为上下排布
右侧新增两块
- 「今日 to do」:备忘录性质。底部常驻加号输入框,回车即建;建完前面出现勾选框,
勾选后划线变淡并沉到已完成组末尾(输入框始终在最后一行);可取消勾选回到未完成组;
悬浮出现删除按钮;列表过长可滚动,勾选后自动滚进视野
- 「待办事项」:显示日历上选中那天的日程与节日(未选中时默认今天),
带优先级点与说明,右上角「新建」直接为该日添加日程
- 原左栏重复的 [TASK QUEUE] 面板移到这里后删除,避免同名面板出现两次
后端
- todos 增加 list 列(schedule 日历日程 / memo 备忘录待办),老库启动时自动补列
- GET /api/todos 支持 list=memo;POST 支持 list,memo 不写日期
- 新增 PATCH /api/todos/:id(勾选完成/取消、改标题等)与 DELETE /api/todos/:id
2026-09-10 23:35:20 +08:00
Drsakura
33dc71f8c3
feat(calendar): 工作日历选中态 + 再次单击添加日程 + 日程/节日悬浮详情
...
- 单击日期选中(主题色底 + 内描边环),再次单击同一日期弹出「添加日程」浮层
- 浮层支持标题 / 优先级 / 备注,保存后写库并即时刷新日历与工作台待办
- 悬浮日程或节日显示详情小窗:日程含日期/优先级/状态/客户/备注,
节日含外贸视角的具体说明(对出货、订舱、客户响应的影响)
- 节日补充春节(2025–2035 逐年查表)、感恩节与黑色星期五
- 超过 3 项时折叠为 +N,悬浮查看剩余条目
- 后端新增 GET /api/todos(按日期范围)与 POST /api/todos
- 浮层用 fixed 定位 + 视口翻转/夹取,渲染后按真实高度校正一次;
滚动跟随锚点;Esc 与点击外部关闭;日期格支持键盘 Enter/Space
2026-09-10 23:16:29 +08:00
Drsakura
02e985c011
fix(suppliers): 主营类目去重,消除 React 重复 key 警告
...
splitCats 未去重,源数据里同一类目重复登记时(如「游标卡尺;游标卡尺」)
会渲染出两个 key 相同的 chip。React 会因此报
'Encountered two children with the same key',且该行为官方标注为 unsupported。
在 splitCats 出口处按原顺序去重,既消警告也避免卡片上出现重复标签。
由无头浏览器全站巡检发现(供应商页原本有 22 条此类警告)。
2026-09-10 17:52:56 +08:00
Drsakura
46a963e23d
feat(header): 顶栏时钟可切时区 + 汇率计算器;工作台移除运行日期
...
1) 工作台
- 删掉欢迎区右侧的「运行日期」卡片,含 luxury.css 里对应样式与窄屏残留
2) 顶栏时钟(仿小满 CRM)
- 新增 TimeZoneClock:时钟图标 +「东八区:北京,上海 ⌄」+ 大号时间(20px 等宽,
tabular-nums 保证秒位不跳动),下方小字带日期
- 点整块展开时区列表,23 个时区由西向东排列,每行右侧实时显示该地时间与 UTC 偏移
- 时区用 IANA 名(Europe/London 等)而非写死偏移,夏令时自动正确;
打开列表会自动滚到当前选中项;选择记 localStorage('happy.clockZone')
- 新增 src/timezones.js 集中时区表与格式化工具
3) 顶栏汇率计算器
- 新增 FxCalculator:计算器图标点开浮层,默认 USD→CNY
- 左右两侧各带可搜索的币种下拉(币种来自接口返回的全部币种,常用币种排前),
中间一键互换;任一框输入另一框实时反算;显示主汇率与倒数汇率
- 换币种时锁住所改那一侧的数字(与 Google 换算一致)
- 金额按数量级自动取精度(USD→VND 与 CNY→USD 都不出现一堆无意义的零)
- 新增 src/currencies.js(币种中文名 + 排序 + 交叉汇率与格式化)
4) 后端汇率接口
- 新增 /api/fx/rates:以 USD 为基准返回全量汇率,前端据此算任意币种交叉汇率
- 主源 open.er-api.com(166 币种),备源 frankfurter.app(欧洲央行);
内存 + 落盘缓存 1 小时,并发请求只发一次网络
- 网络不可用时依次回退「上次成功的数据」→「内置离线汇率表」,
并在响应里用 source/warning 标注,前端明确提示数据来源
5) 顶栏排布
- 时钟变宽后中等屏会溢出,新增 ≤1440px 断点收紧留白/品牌区/搜索框;
搜索框允许收缩;1280/1100/980/700 各档实测无横向溢出
- 移动端(≤680px)汇率浮层改用固定定位铺满两侧,避免左侧被裁
6) .gitignore
- backend/data/ 整体忽略(trade.db、fx-cache.json 等全是运行时产物)
2026-09-10 17:44:36 +08:00
Drsakura
1edf5ac995
feat(nav): 侧栏高对比度 + 顶栏已打开页面标签 + 折叠态只留日夜切换 + 换明月图标
...
1) 侧边栏对比度
- 新增 --sb-* 专属色板(固定深色底,不随日夜主题翻转),文字 #c3d7e3、
hover 纯白、选中纯白 + 品牌色强调条 + 内描边,参考 GoodJob 的 Command Deck
- 原过渡曲线(--transition-fast/base)保持不变
- luxury.css 里把侧栏刷回浅底的覆盖一并改为同一套色板
2) 顶栏「已打开页面」标签
- 新增 src/navigation.js:NAV_GROUPS 与 PAGE_LABELS 单点维护,App 与顶栏共用
- GlobalHeader 用 useLocation 自动登记访问过的页面,工作台恒定置顶且不可关闭,
其余按最近使用排序,可单独关闭,最多 10 个,落 localStorage(happy.openTabs)
- 关掉当前标签会退到剩下的最近一个,没有则回工作台
3) 折叠态页脚
- .sidebar.collapsed .settings-btn 隐藏,76px 宽度下只留一个日夜切换按钮
4) 明月图标
- MoonIcon → MoonStarsIcon,造型取自 igoutu.cn/icon/101343/moon-and-stars,
重绘为矢量(月牙 = 两段圆弧相减,两枚凹边圆角四角星),走 currentColor 随主题变色
- 源图存 design-refs/(已忽略,不入库)
5) 顶栏排布修正
- 标签条加入后头部变挤,收窄品牌区/搜索框,按钮加 nowrap 防止文字折行
- ≤1280px 隐藏标签条
2026-09-10 17:25:38 +08:00
Drsakura
d650ff9c4d
refactor(layout): 顶栏横跨整宽并压在侧边栏之上,侧栏去标题、按钮移到工作台右侧
...
- App.jsx: 新增 .app-shell / .app-body 两层结构,GlobalHeader 提到侧边栏之前;
inert 上移到 .app-shell,行为不变
- 侧边栏删除带 happy出口通 的 .sidebar-header(logo 与名字统一交给顶栏,且更大)
- 折叠/展开按钮从侧栏标题栏移到「工作台」右侧(.sidebar-top),收起时自动落到工作台下方
- 新增 --header-height 变量(80px,≤680px 时 62px),顶栏 min-height 与侧栏 sticky top/
height、route-content min-height 全部据此计算
- 移除已失效的 .sidebar-header 规则(含 ≤900px 媒体查询)
2026-09-10 17:04:05 +08:00
Drsakura
47f6214056
feat: 品牌标识用云+数据库矢量图标(favicon + 页眉品牌标)
...
- Icons.jsx 新增 CloudDbIcon:照 icons8-云 重绘为矢量,左云随主题色、右圆柱用品牌蓝
- GlobalHeader 品牌区前置该图标,原「系统在线」绿点改为挂在其右下角
- index.html 增加 rel=icon / mask-icon 指向 public/favicon.svg
- luxury.css 补品牌样式,并让 :not() 选择器排除 .brand-mark 避免样式误伤
2026-09-10 16:58:52 +08:00
Drsakura
5cff29702b
chore: 忽略设计参考素材(icons8 厂/云 等),仅本地保留不入库
2026-09-10 16:47:28 +08:00
Drsakura
250d2505d5
fix: tag 回退时更新链接使用原始标签名(v1.0.0,而非清洗后的 1.0.0)
2026-09-10 16:26:08 +08:00
Drsakura
e4537d5bf0
feat: 设置页新增「系统与更新」,按 GitHub 版本号检查更新
...
- 顶层 package.json 作为应用版本唯一来源(happy-export v1.0.0)
- backend/lib/updater.js:查 GitHub Release(优先)或 tag,与本机版本做 semver 比对
- backend/routes/system.js:GET /api/system/info、GET /api/system/check-update(30s 缓存)
- /api/health 版本号改为读真实版本
- 设置页新增第 4 个分类:当前版本 / 更新源仓库 / 检查更新(含更新说明折叠)
- 新增 RefreshIcon
2026-09-10 16:23:42 +08:00
Drsakura
d8df2c3960
chore: happy 出口通初始提交(前端 React + 后端 Express,采购管理已原生化)
2026-09-10 16:16:59 +08:00