ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

21天从Vite到AI驱动:Vue.js电商后台管理系统完整实战

21天从Vite到AI驱动:Vue.js电商后台管理系统完整实战 做电商后台管理系统这件事最早我拿到的是一个极度含糊的需求“做一个能用的商城管理端”。页面要多少、权限怎么分、要不要图表、要不要AI功能没人说得清。我用了21天从Vite工程化入手一步步把Vue.js的商城后台做成型最后把AI驱动架构接了进来。这篇就记录完整的过程、踩过的坑、以及每个阶段我为什么这么选型。如果你也正在用Vue.js做中后台项目或者想把手头一个会跑但很乱的工程收拾利索这份21天进阶计划可以直接拿过去改。我不讲废话都是实测下来的选型和代码路径。1. 21天能把一个电商后台做成什么样1.1 电商后台管理系统到底在管什么我在动手之前先花了半天把业务方口中的“电商后台”拆成了一张模块清单。无论什么规模电商后台基本逃不过这五块商品管理、订单管理、会员与权限、营销配置、数据分析。商品管理里藏着SKU、库存、上下架订单管理里有状态机、售后、物流单号权限体系要管菜单、按钮、接口三级数据分析往往还要带可视化报表。这些模块加起来就是一套典型的中后台管理系统。选Vue.js作为主框架的原因很直接生态成熟团队上手成本低Element Plus这类组件库几乎把后台80%的界面零件都做好了。真正决定项目成败的反而不是某个组件而是工程化水平——路由怎么组织、状态怎么管理、接口怎么封装、权限怎么控制。这些地基没打好业务越做越乱。1.2 21天的时间切片底座、业务、智能化21天不是瞎拍的。我把整个过程切成三段每段7天对应三种完全不同的任务类型。第1-7天Vite工程化底座。搭项目、定规范、通路由、接状态、封装请求层目标是一口气跑通一个带登录、布局、权限占位的最小后台骨架。第8-14天核心业务模块。把RBAC权限、商品管理、订单管理做扎实同时沉淀公共组件。目标是系统从“骨架”变成“肉”。第15-21天AI驱动架构。接入统一AI服务层做商品AI助手和智能数据看板两个真实场景再把AI嵌入到业务流程里做重构。为什么选21天而不是“慢慢来”因为后台管理系统的知识边界太宽没有deadline就一直在填坑。21天刚好覆盖一个知识从陌生到熟练的周期每天投入4-6小时完成度能到达“可演示、可交付、可扩展”。如果只给自己7天业务模块和AI肯定做不透如果拖到60天前期的技术选型会反复推翻。21天是在“工程化深度”和“业务覆盖度”之间的平衡点。2. 第一个7天把Vite工程化底座砸实2.1 为什么选Vite冷启动和热更新不是唯一理由很多人选Vite是因为“启动快”但真正让它成为中后台项目首选的理由我自己总结有三点。第一是开发服务器启动速度。Vite基于原生ESM开发环境按需编译不打包。我实测一个30多个页面的后台项目Webpack冷启动动辄20秒起步Vite基本在2秒内能起来。这个差距在中后台项目里会每天放大一天启动几十次省下的时间都是实打实的。第二是依赖预构建。Vite用esbuild预打包node_modules里的依赖把CommonJS模块转成ESM。这解决了“依赖混乱导致启动慢”的痛点我后面在第五部分踩坑里会讲一个和这个机制相关的坑。第三是HMR的颗粒度。Vite只更新模块本身不刷新整页表单填写到一半改代码也不会丢状态。这个体验在做后台复杂表单时尤其关键商品SKU编辑器那种多层级嵌套的表单热更新如果刷新页面填了一半的规格矩阵直接没了心态会崩。选型上还要求“能支撑AI驱动架构”这体现在两个方面一是Vite的代理配置dev proxy方便对接本地AI网关二是它对TypeScript、SSE流式接口的前端开发体验很顺。项目后期接入AI时代码改动量会比预期小很多。2.2 从 create-vue 出来的项目还要调整哪些工程细节初始化直接用官方脚手架命令是npm create vuelatest slow-admin交互提示里我勾选了Router、Pinia、ESLint、PrettierTypeScript也选了。注意这里要确认Node版本Vite 5要求Node 18以上低版本Node会导致依赖安装异常后面有单独排查。脚手架生成后第一步要改目录结构。我习惯这样规划src/ api/ # 接口层按模块拆文件 assets/ # 静态资源 components/ # 公共组件如 ProTable、FormDialog directives/ # 自定义指令如 v-permission layouts/ # 布局组件侧边栏/顶栏/主内容区 router/ # 路由配置 stores/ # Pinia 状态按模块拆 store utils/ # 工具函数 views/ # 页面按业务模块组织 types/ # TypeScript 类型定义这一步不能省。后期业务膨胀后如果api和views都是平铺的找文件像挖矿。按模块组织前后端对接时也能直接对口商品模块的接口、页面、类型定义都在各自的目录里改动范围可控。工程化里最容易被忽视的一环是代码规范。create-vue默认装了ESLint和Prettier但要做两件事加husky做提交前检查加commitlint约束提交信息格式。命令如下npm install -D husky commitlint npx husky inithusky配置好之后每次git commit前自动跑lint-staged格式错误和低级语法错误根本不会进入代码库。我自己经历过没做这一步的项目两周后代码里出现几十种风格混杂的写法重构成本极高。2.3 路由、状态与请求层后台系统的三个地基后台系统不是简单把页面拼起来路由、状态、请求层三个地基必须一次打牢。路由设计。我采用的是“静态路由 动态路由”结合的方式。登录页、404页、错误页是静态路由业务页面路由由后端返回的菜单权限生成。动态路由的基础是用import.meta.glob批量加载页面组件// src/router/dynamic.ts const modules import.meta.glob(/src/views/**/*.vue) export function buildRoutes(menuList: MenuItem[]) { return menuList.map(menu ({ path: menu.path, component: modules[/src/views${menu.component}.vue], meta: { title: menu.title, icon: menu.icon } })) }这段代码的意思是后端返回菜单数据路径、组件路径、标题前端动态生成路由。好处是权限控制权在后端前端只渲染有权限的页面改菜单不用发版。坑是刷新路由会丢这个在踩坑部分细说。状态管理。我用Pinia按域名拆storeuserStore存用户信息和tokenpermissionStore存菜单和权限标识appStore存界面状态。一个关键点是store里的数据不要全部塞到localStorage除了token和基础用户信息必须持久化其他状态应通过接口获取避免脏数据。请求层。基于axios二次封装核心代码// src/api/request.ts import axios from axios const service axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 }) service.interceptors.request.use(config { const token useAuthStore().token if (token) config.headers.Authorization Bearer ${token} return config }) service.interceptors.response.use( response response.data, error { if (error.response?.status 401) { useAuthStore().logout() window.location.href /login } return Promise.reject(error) } )拦截器解决两件最关键的事统一注入token、统一处理401跳登录。后续接AI服务时我额外封装了一个不依赖axios的流式请求模块因为SSE场景里axios不如原生fetch好用。2.4 环境变量与多环境部署开发一天部署时不慌环境变量这块提前规划好后面部署能省一堆事。我习惯按环境拆分文件用途典型变量.env.development本地开发环境VITE_API_BASE_URL/api.env.production线上生产环境VITE_API_BASE_URLhttps://api.example.com.env.local本地私有覆盖不进GitVITE_AI_BASE_URLhttp://localhost:8000Vite规定只有以VITE_开头的变量才会暴露到源码里。有两处使用场景axios请求的baseURL、AI服务网关的地址。前端代码里不要出现任何硬编码的后端地址这是第一原则。3. 第二个7天把电商后台的业务模块做扎实3.1 权限系统RBAC模型在前端的落地权限系统是后台系统的地基我做的是经典RBAC模型用户关联角色角色关联权限权限分为菜单权限、按钮权限、接口权限。前端侧需要完成三层控制菜单不显示、路由进不去、按钮看不见。菜单控制的核心是动态路由上一节提到了。按钮级权限需要用自定义指令实现// src/directives/permission.ts export const vPermission { mounted(el, binding) { const required binding.value as string const hasPermission useAuthStore().permissions.includes(required) if (!hasPermission) { el.parentNode?.removeChild(el) } } }页面里这样用el-button v-permissionproduct:delete删除/el-button这里我踩过一个反复斟酌的问题动态路由的刷新丢失。因为动态路由是在登录后根据角色生成并addRoute的一旦刷新页面Pinia内存里的路由数据就没了。解决办法是登录成功后把角色码和菜单数据持久化到localStorage刷新时重新构建路由再放行。// 路由守卫里 router.beforeEach(async (to) { const authStore useAuthStore() if (!authStore.token) return { path: /login } if (!authStore.routesLoaded) { await authStore.loadRoutes() return { ...to, replace: true } } })3.2 商品与订单后台里最重的两块业务商品管理里最难的其实是SKU规格设计。手机型号、颜色、内存、套餐这些规格自由组合形成SKU矩阵前端要表达这种“规格矩阵”交互。我用的是“规格组”数据结构规格名数组加规格值二维数组通过笛卡尔积生成SKU列表。function generateSkuList(specs: SpecGroup[]) { return specs.reduce((acc, group) { return acc.flatMap(item group.values.map(value ({ ...item, [${group.name}]: value })) ) }, [{}]) }这个函数生成表格里的每一行SKU再让运营按行填价格、库存、货号。交互层面的细节是规格组支持动态增删管住“删规格时数据不残留”的问题。我加了一句校验任一规格组被删除时对应SKU行整体移除。订单管理的核心是状态机。订单状态不能乱跳我定义枚举待支付、待发货、已发货、已完成、已取消、售后中用状态流转表约束每个状态到下一个状态是否允许。前端做的是根据当前状态渲染可操作按钮比如待发货状态只显示“发货”和“关闭订单”。3.3 列表页的组件化沉淀ProTable的抽象过程七天业务做下来我数了一下后台80%的页面都是“搜索区表格分页弹窗”结构。于是我在第13天开始做一件重要的事抽象一个ProTable组件。ProTable接收三个核心配置请求函数、列配置、搜索表单配置。核心代码思路ProTable :fetch-apifetchProductList :columnscolumns :search-fieldssearchFields selectedhandleSelection /组件内部做了四件事统一管理loading统一处理分页参数page/pageSize自动拼接搜索条件触发“重置”时回到第一页。做完这个组件后新增一个列表页的工作量从半天压缩到30分钟后面AI看板页面的表格直接复用了这套逻辑。这里要说一个经验组件抽象不是越早做越好。前三天先按正常方式写页面写到第三个相似页面时再抽公共组件这时候边界已经想清楚了不会因为过度设计做出一堆没人用的配置项。4. 第三个7天AI驱动架构的落地实践4.1 AI驱动架构不是“页面里塞个聊天框”说到AI驱动架构很多人第一反应是在后台加一个“AI助手”聊天窗。如果只是这样那它只是个工具谈不上驱动。我自己对“AI驱动架构”的定义是三层。第一层是能力接入层把AI能力统一沉淀成一个后端服务网关前端通过一个通用的AI模块调用不做零散的接口直连。第二层是语义交互层用户在后台界面里用自然语言发指令比如“找出本月未发货订单”前端把指令发到AI网关网关处理后返回结构化结果。第三层是流程重构层AI不只响应指令而是主动嵌入业务流比如AI先审核商品描述生成建议、AI先做数据异常预警人工只需确认。前21天我做到的“驱动”是三层里前两层的完整落地第三层做了一个商品审核的试点。为什么要有这三层因为如果把AI能力散落在每个页面每接一个模型就要改一次调用逻辑。统一封装之后业务代码不用感知模型换没换、走的是提示词工程还是微调模型前端只认一套协议。这是工程上对“AI驱动”最实在的理解。4.2 Vue侧接入AI的三种姿势与代码范式我整理出三种接入姿势对应不同场景姿势适用场景关键点同步请求生成标题、改文案、短文本摘要请求间加loading超时处理SSE流式长文本生成、对话式助手解析流式数据实时渲染异步任务数据分析、批量生成、涉及耗时长任务提交任务后轮询或WebSocket通知同步请求最简单复用axios封装即可。SSE流式需要单独写一个fetcher核心是处理ReadableStream。4.3 实操案例一商品AI助手我做了一个“商品AI助手”功能运营在商品编辑页输入几句关键词比如“北欧风、轻奢、真皮沙发”点击生成AI直接输出商品标题和描述文案以流式形式逐字渲染到输入框。前端的流式处理关键代码// src/api/ai.ts export async function streamChat(payload: ChatPayload, onMessage: (text: string) void) { const res await fetch(/api/ai/generate-product, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(payload) }) const reader res.body!.getReader() const decoder new TextDecoder(utf-8) while (true) { const { value, done } await reader.read() if (done) break const text decoder.decode(value) onMessage(text) } }页面里接收流式文本通过ref.value绑定到textarea实现打字机效果。这里有个性能注意点不要直接在onMessage里value text高频更新会卡顿。我用了一个小的缓冲策略每60ms合并一次文本再赋值。后端返回我给自己的规范是SSE每条数据是纯文本片段客户端拼起来渲染。流式输出的好处就是首字延迟低用户体感好。实测一个五段商品描述普通同步请求要等10秒流式大概1秒出第一个字运营人员体验完全不一样。4.4 实操案例二自然语言驱动的智能数据看板第二个场景是智能数据看板。传统报表页面是写死Excel式的统计图我这里做成了“自然语言查询”页面顶部一个输入框运营输入“上周各品类销量对比”AI网关把这句话转成分析意图让后端执行查询后返回结构化图表配置前端用ECharts渲染。我定义的约定后端返回格式{ chart: { type: bar, title: 上周各品类销量对比, data: [ { name: 沙发, value: 132 }, { name: 床垫, value: 98 } ] }, summary: 上周销量最高的品类是沙发共132件占全品类28% }前端只负责接收配置并渲染图表配置完全由后端生成。这样前端不感知AI模型的输出格式后端可以自由调整提示词或结果后处理逻辑。summary字段可以直接展示成自然语言结论表格和图表并存比一个干巴巴的表格信息密度高很多。这个模块我做了一天半难点不在读写JSON而在“把自然语言问题映射成数据库查询”的后端逻辑但前端也有坑模型输出不稳定会导致chart字段缺失前端要做兜底渲染打不了图就渲染一个表格。我把这个兜底逻辑写成了公共组件后续AI输出的场景都用它。4.5 流程重构从“工具”到“驱动”的过渡第三个7天的后半段我做了一次流程重构试点的设计商品审核流程。原来的人工审核是运营上传商品信息审核员逐条看耗时又容易漏。现在设计成AI预审AI自动检查标题长度、描述完整性、类目匹配度、违禁词产出“建议通过/建议修改/疑似违规”的预审结果审核员只需要复核AI判定“疑似违规”的商品。前端流程是这样的商品审核列表页增加“AI预审”按钮点击后提交商品ID后端执行审核并返回预审结论。前端展示预审结论与置信度审核员一键确认。这一步就是“AI不再只是聊天工具而是嵌入了业务流程的决策节点”。但我的原则很明确AI输出必须经过人工确认不能直接上架。涉及库存、价格、违规判定的动作AI只做“建议”不做“决策”。这个边界在架构上要落实后端AI网关只返回“建议”级别的结果写库操作永远走业务接口。5. 21天踩坑实录这些问题我替你趟过了5.1 Vite工程化阶段的坑现象原因解决方案启动报错“ERR_OSSL_EVP_UNSUPPORTED”Node版本低于18OpenSSL不兼容新依赖升级Node统一用nvm管理依赖装完启动时“You installed esbuild for another platform”包管理器版本混乱依赖缓存不一致删除node_modules和lock文件重装接口代理不生效改成/api/abc 404代理path和目标path没匹配给代理目标加rewrite去掉/api前缀开发环境访问历史路由直接404Vite没有启用history fallback配置 appType: spaNginx里也补try_files其中代理rewrite的坑我印象最深// vite.config.ts server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) } } }写第一版没加rewrite后端日志里看到一堆/api/product/list明显是路径没被正确处理。小细节但卡了我一晚上。5.2 权限与业务模块的坑第一个坑是动态路由刷新后白屏。解决办法前面提过路由数据进store并持久化。这里要补一句持久化不要存整个route对象存后端返回的原始菜单JSON刷新后用菜单JSON重新buildRoutes避免缓存了带组件引用的路由对象导致序列化失败。第二个坑是按钮权限指令在动态渲染的表格操作列里不生效。因为v-permission绑定在操作列上的时候该按钮所在的父级容器还没挂载directive的mounted钩子里removeChild会抛错。处理办法是换成函数式用法表格的列配置里用hasPermission(product:delete)来判断而不是依赖指令。第三个坑是订单状态流转时多标签页同时操作导致状态不同步。点击“发货”之后另一个标签页还停留在“待发货”状态。产品设计上是同一订单不能多标签操作技术方案上是发货成功后触发当前订单模块的公用更新方法统一刷新。5.3 AI接入过程的坑AI接入的坑比前面两类都要隐蔽。第一个坑是SSE流式接口在部分浏览器里被缓冲收到的不是完整数据而是攒了好几段才推一次。原因是对端服务器没设置Content-Type: text/event-stream或者响应被代理层缓存了。解决方向是检查后端返回头开发联调时后端加cache-control: no-cache。第二个坑是流式渲染打字机的闪烁问题。逐字追加时浏览器对textarea整体reflow导致光标跳动和输入延迟。我的解决方法是服务端返回的流里带一个“阶段结束”标记按句子缓冲再渲染视觉上更平滑也减少频繁的DOM赋值开销。第三个坑是AI接口超时。商品AI生成耗时长超过axios默认10秒直接被掐断。由于流式接口不走axios这个坑只出现在非流式的AI同步接口上。统一处理是同步AI接口把超时设到30秒前端配一个“生成中”的loading状态并且给一个取消按钮用AbortController中断请求。const controller new AbortController() const timer setTimeout(() controller.abort(), 30000)这些坑都不算深但没做过一遍的人容易在联调时被磨到怀疑人生。我把它们整理成了自己的checklist后续接AI功能先过一遍这张表。6. 写在最后关于这21天的一些心里话6.1 计划之外的三点收获第一个收获是“可演示的进度”比“完美的代码”重要。第7天结束时我的代码还谈不上完美但已经能跑通登录、菜单、列表、表单给业务方看的时候大家能对着页面聊需求聊出来的细节比纯文档具体得多。这比闷头写三周再拿出来对需求高效得多。第二个收获是“删代码”是工程化的一部分。第二阶段结束时我砍掉了一个自认为很帅但没人用的通用组件第三阶段结束又重构了一次AI模块的接口。敢于删是因为见识过“为未来预留”写出来的代码最后都变成了无人维护的债务。第三个收获是“21天”的真正意义不是学完某项技术而是把一个模糊的需求变成一个可运行的系统。过程中技术选型可以调整但节奏不能乱。每天留出半小时复盘记录今天“为什么这么做”比记录“做了什么”有用十倍。6.2 如果你也想照着做送你三个建议第一这个计划的前提是你已经会Vue的基础用法组件、路由、生命周期。如果连ref和reactive都还分不清先花三天过一遍官方教程再开始不要逆着基础硬上一个21天计划。第二动手过程中不要执着于“把每行代码写完美”有些早期的代码到了第14天注定会被重构。我的策略是第一个阶段只求跑通第二个阶段求规范第三个阶段才谈优化。提前进入优化阶段只会拖慢进度。第三AI部分不要把线上模型参数调太久。我用的是本地起一个通用的AI网关服务把提示词和后端逻辑放在一起迭代前端只改调用参数。真实项目里也一样先让链路完整跑起来再优化响应质量。链路不通模型再强也出不了活。这套21天计划我后来又照着做了一次加了一些业务模块整个过程缩短到16天因为没有前面那些“第一次”的踩坑时间。你照着走一遍大概率不会比我快太多但至少不用再趟一遍我已经标出来的那些坑。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进