茂井镇芒果 ERP 的原始页面已经包含销售渠道、基地、农户、品种、物候、农事、水肥、病虫害、采摘、库存、订单、物流、客户、销售、收支、结算、成本、利润和设置。页面设计也不是标准后台,而是暖金芒果色、Bento 卡片和数据看板混排。
如果直接套用通用模板,功能可能更快统一,但客户喜欢的东西会消失。因此这次把“页面视觉”和“底层实现”拆开处理:布局、颜色、字号、圆角、卡片和动效全部保留;数据、认证、权限、校验、持久化和构建方式重新做。
| 问题 | 原实现 | 修复方向 |
|---|---|---|
| 数据持久化 | 业务数据写 localStorage | Node.js 服务端 + data/db.json 原子写入 |
| 登录安全 | 账号密码写在前端脚本 | scrypt 哈希 + HttpOnly Cookie + 登录限流 |
| 角色权限 | 所有登录用户都能删除 | 管理员 / 操作员 / 只读访客三层控制 |
| XSS | 用户输入直接拼 innerHTML | 渲染前统一 HTML 转义 |
| 金额 | 最多两位小数显示,内部浮点计算 | 统一两位小数展示并收口金额显示 |
| 外部依赖 | Chart.js 与登录背景走外网 | 本地 Chart.js + 本地同图背景 |
| 交付 | 单文件、双入口、无手册 | 服务端版本 + 使用手册 + PDF + 部署包 |
我们没有改动原来的主页 CSS 结构,而是把数据和事件层接上服务端。页面仍然使用原来的渲染函数、卡片样式和图表布局;登录框只补了标准 form 与无障碍标签,按钮和输入框位置不变。
主页 CSS 与修复前逐行对比,最终只有两处非视觉调整:登录背景 URL 从 Unsplash 换成本地下载的同一张图片;新增一个视觉隐藏的 .sr-only 工具类。260px 侧栏、暖金配色、Bento 看板、渐变、光晕和动效都没有重做。
数据模型不是把页面拼在一起,而是让记录之间能互相解释:基地影响产量,采摘形成库存,订单形成销售,销售进入收支,结算再扣除农资成本。最终利润报表不应该再依赖手工抄数。
这次把管理员、操作员和只读访客分开。前端会隐藏越权操作,但真正决定能不能保存的是服务端:访客不能写,操作员可以新增和编辑但不能删除,也不能修改系统参数;管理员拥有完整权限。保存失败时页面会提示并重新加载服务端数据,而不是悄悄丢掉记录。
数据写入采用临时文件加原子替换,刷新页面后数据还在;导出仍然保留 JSON 备份;修改密码后旧会话立即失效。
最终交付不是只有一个 HTML。目录里包含服务端、种子数据、模块配置、前端、使用手册 HTML/PDF、标注截图、API 测试、权限/XSS E2E 和完整 CRUD 测试。所有 JavaScript 文件通过语法检查,20 个页面完成无障碍结构检查,外部资源请求为 0。
这次项目最重要的经验是:视觉保真不是“看一眼差不多”,而是可以被逐行比较的约束;代码安全也不是能不能运行,而是换一个用户、断一次网、刷新一次页面之后还成不成立。









芒果 ERP 的页面没有换脸,但底层已经变成一套可以继续维护、继续扩展、可以交给客户的服务端系统。