☰
Vibe Coding实践:AI辅助构建低代码后台管理模板
2026/10/19 20:10:22 网站建设 项目流程

用 Vibe Coding 方式写项目的第一个落地目标,我选择了后台管理模板。原因很简单:后台管理模板几乎包含了一个业务系统最常见的工程点,布局、权限、动态路由、表单、CRUD、接口对接,再加上低代码配置和 AI 中心,就是一个能实际用于内部系统的起点。这篇文章会围绕这个模板说明 Vibe Coding 的核心工作方式、低代码 Schema 设计、AI 中心的接口实现,以及真正把项目跑起来后最容易踩的坑。

模板的定位不是简单换肤,而是一个可以继续扩展的中后台基础工程:前端负责动态渲染和交互,后端提供配置下发和 AI 会话能力。低代码部分通过 JSON Schema 驱动表单和页面,让新增业务页面时不再重复写表单组件;AI 中心则统一封装大模型接口,提供会话、技能和轻量知识库能力。最终目标是让后续业务项目直接基于这套模板生长,而不是每次都从零开始。

1. Vibe Coding 是什么,为什么先做后台管理模板

1.1 Vibe Coding 的核心理念

Vibe Coding 不是某个特定工具,而是一种更接近“和 AI 结对编程”的开发方式:开发者用自然语言描述需求,AI 辅助生成代码、修改逻辑、解释报错,甚至帮助重构。重点不在于让 AI 一次性生成整个系统,而在于把需求拆成可验证的小步骤,让 AI 在每一小步里产出可运行、可审查的代码。

这种方式的效率来自两点。第一,重复性编码速度明显更快,比如表格页、表单页、增删改查接口、类型定义,这些样板代码对 AI 来说几乎没有难度。第二,调试过程更自然,把报错信息贴给 AI,让 AI 先给出定位思路,再决定是否让 AI 直接修改。

但 Vibe Coding 不等于“随便描述几句就交付”。越复杂的项目,越需要开发者先把架构边界想清楚:哪些模块是固定的、哪些配置是动态的、数据存在哪里、权限怎么控制。AI 适合完成已知方案的执行,不适合在需求模糊时替人类做产品决策。因此,这套工作流可以归纳为:人类定边界,AI 写实现,测试做验证,审查守质量。

1.2 为什么第一个项目选择后台管理模板

后台管理模板适合作为 Vibe Coding 系列的第一个项目,因为它覆盖面广,但风险可控。一个典型的模板至少涉及登录认证、布局、权限、菜单、动态路由、表格页、表单页、接口联调,这些都是中后台系统的通用能力;同时它又不像电商秒杀、实时音视频那样对性能和并发有苛刻要求,比较容易在本地跑通。

从复用角度看,模板的收益也最直接。做完第一套后,后续内部系统、运营后台、数据看板都可以直接复制改造。模板中的低代码表单引擎,能显著减少重复页面开发;AI 中心则是一个独立模块,可以承担智能问答、文本生成、报表解读等场景。

这个项目特别适合演示 Vibe Coding 的完整流程:

  • 第一步,给 AI 一个清晰的业务目标,让它生成可运行脚手架。
  • 第二步,逐轮补充权限、路由、低代码、AI 中心等细节。
  • 第三步,把生成的代码放入真实页面验证,发现问题后再通过提示词驱动修复。
  • 第四步,把每次有效提示词沉淀成可复用模版。

因此,这个项目的技术价值并不仅仅在模板本身,还在于它示范了“如何用自然语言驱动一个多模块项目落地”。

1.3 低代码、AI 中心与后台模板的关系

这里需要先区分三个概念,避免后续实现时混淆。

低代码是一种开发模式,指的是用可视化配置或配置化数据替代部分手写代码。在本项目中,低代码的核心是:把表单字段、页面布局、校验规则、接口映射保存为 JSON 配置,前端根据配置动态渲染,后端根据配置动态处理数据。这样,新增一个“项目信息页”时可以只配置数据,不再改前端页面代码。

AI 中心是一个业务模块,它把大模型能力封装成统一服务。在本项目中,AI 中心包含会话管理、消息存储、技能编排、知识库检索等能力。它和后台模板的其他模块不一样,因为它的核心输入是自然语言,输出是模型推理结果,所以接口设计要考虑流式返回、超时、上下文长度限制等问题。

后台管理模板则是承载两者的壳。它提供统一布局、登录认证、权限菜单、操作日志等基础设施;低代码模块和 AI 中心作为模板里的两个独立业务域存在。这样设计的好处是:模板的通用能力可以服务于低代码页面,也可以服务于 AI 中心的运维管理页面,形成互相支撑的结构。

2. 项目设计:后台模板的模块边界和数据模型

2.1 业务模块划分

这套模板建议拆成三个端到端业务域,而不是把所有功能堆在同一个页面集合里。

模块主要职责典型页面
系统管理登录认证、用户管理、角色权限、菜单管理登录页、用户列表、角色分配、菜单配置
低代码中心表单配置、页面配置、接口配置、预览发布表单设计器、页面配置列表、预览页
AI 中心会话管理、Prompt 管理、技能编排、知识库管理对话页、技能配置、知识库上传、会话历史

三个模块之间的数据关系需要提前约定。低代码模块通过page_config和form_schema存储配置;AI 中心通过ai_session、ai_message、ai_skill存储会话和技能;系统管理提供用户和权限边界。

2.2 核心数据模型

先看基础表结构。以下 SQL 示例用于说明字段设计思路,实际项目需要根据数据库类型调整。

-- 菜单表 CREATE TABLE sys_menu ( id BIGINT PRIMARY KEY AUTO_INCREMENT, parent_id BIGINT DEFAULT 0, name VARCHAR(64) NOT NULL, path VARCHAR(128) NOT NULL, component VARCHAR(128), icon VARCHAR(64), sort INT DEFAULT 0, visible TINYINT DEFAULT 1 ); -- 用户表 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(64) NOT NULL UNIQUE, password VARCHAR(128) NOT NULL, nickname VARCHAR(64), status TINYINT DEFAULT 1 ); -- 页面配置表 CREATE TABLE lowcode_page_config ( id BIGINT PRIMARY KEY AUTO_INCREMENT, page_code VARCHAR(64) NOT NULL UNIQUE, page_name VARCHAR(128) NOT NULL, route_path VARCHAR(128), schema_config JSON, status TINYINT DEFAULT 0 ); -- AI 会话表 CREATE TABLE ai_session ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, title VARCHAR(128), skill_code VARCHAR(64), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- AI 消息表 CREATE TABLE ai_message ( id BIGINT PRIMARY KEY AUTO_INCREMENT, session_id BIGINT NOT NULL, role VARCHAR(16) NOT NULL, content TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );

这里有两个设计点值得注意。第一,schema_config使用 JSON 类型存储,是为了保留表单配置的灵活性;但灵活性也意味着校验责任,保存前必须做 JSON 结构和字段名合法性校验。第二,AI 消息表使用role区分用户消息和模型回复,便于后续按会话维度重放上下文。

2.3 技术选型考虑

技术选型应以团队熟悉度和生态成熟度为准,不需要追求最新版本。一个常见组合如下。

层选型职责
前端框架Vue 3 + TypeScript组件化和类型安全
构建工具Vite快速启动、热更新
状态管理Pinia用户状态、菜单、缓存配置
路由Vue Router静态路由 + 动态注册
UI 组件Element Plus表格、表单、弹窗等
后端Node.js + Express提供 API 服务
数据库PostgreSQL / MySQL / SQLite开发环境可用 SQLite

需要说明的是,本文示例后端使用 Node.js 是为了让整个项目在同一个语言环境里跑通。如果团队技术栈以 Java 为主,可以把控制器层替换为 Spring Boot,前端接口约定不变。

2.4 目录结构

一个便于扩展的前后端分离目录如下:

admin-template/ ├── frontend/ │ ├── src/ │ │ ├── api/ # 接口请求封装 │ │ │ ├── auth.ts │ │ │ ├── lowcode.ts │ │ │ └── ai.ts │ │ ├── components/ │ │ │ └── schema-renderer/ # 低代码渲染器 │ │ ├── layouts/ # 后台布局 │ │ ├── router/ # 路由配置 │ │ ├── stores/ # Pinia stores │ │ ├── views/ │ │ │ ├── login/ │ │ │ ├── system/ │ │ │ ├── lowcode/ │ │ │ └── ai-center/ │ │ ├── types/ # 全局类型定义 │ │ └── utils/ # 权限、请求等工具 │ ├── package.json │ └── vite.config.ts └── server/ ├── src/ │ ├── routes/ # 接口路由 │ ├── services/ # 业务逻辑 │ ├── models/ # 数据模型 │ ├── middleware/ # 鉴权、日志中间件 │ ├── config/ # 环境配置 │ └── index.ts # 服务入口 ├── .env.example └── package.json

目录结构是由 Vibe Coding 第一轮提示词生成后人工调整得到的。生成目录后,不要急着写业务代码,先确认入口文件、环境变量、依赖关系是否正确。

3. 用 Vibe Coding 搭脚手架:从需求描述到可运行项目

3.1 第一轮提示词:初始化项目

Vibe Coding 的第一条提示词很关键,它决定了生成结果的整体形态。初始提示词可以这样写:

请使用 Vue 3 + TypeScript + Vite 创建一个后台管理模板前端项目,要求: 1. 包含登录页、主页、404 页面,使用 Vue Router。 2. 使用 Pinia 管理用户状态,登录后保存 token 和用户信息。 3. 布局采用顶部导航 + 左侧菜单 + 内容区域。 4. 使用 Element Plus 作为 UI 组件库。 5. 提供请求工具封装,统一处理 baseURL、token 注入和错误提示。 6. 目录结构按 api、components、router、stores、views、utils 组织。

生成完成后,进入项目目录执行安装和启动,确认最基础的页面能打开。

cd frontend npm install npm run dev

这一轮的核心检查点是:依赖版本是否互相兼容、路由是否正确配置、登录页是否引用正确的组件。不要因为页面能打开就跳过检查,后续所有业务都建立在这套脚手架上。

3.2 第二轮提示词:补布局与权限

脚手架跑通后,进入第二轮提示词,目标是把登录流程、路由守卫、动态菜单补完整。

请基于现有 Vue 3 项目补充权限功能: 1. 登录接口返回 token、userId、username、menus。 2. 在 localStorage 保存 token,在 Pinia 保存用户信息和菜单。 3. 增加全局前置守卫:没有 token 时跳转登录页,已有 token 时放行。 4. 根据菜单数据生成侧边栏菜单。 5. 提供退出登录方法,清除 token 和用户状态。

这里会生成一段路由守卫代码,建议人工再确认一次。典型实现如下:

// src/router/index.ts 片段 import { createRouter, createWebHistory } from 'vue-router' import { useUserStore } from '@/stores/user' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/login', component: () => import('@/views/login/index.vue') }, { path: '/', component: () => import('@/layouts/index.vue'), children: [] }, { path: '/:pathMatch(.*)*', component: () => import('@/views/error/404.vue') }, ], }) router.beforeEach((to) => { const userStore = useUserStore() if (to.path === '/login') { return true } if (!userStore.token) { return { path: '/login', query: { redirect: to.fullPath } } } if (userStore.menus.length === 0) { return userStore.fetchMenus().then(() => true).catch(() => false) } return true })

这里有几个关键点。第一,获取菜单的逻辑放在守卫里,是为了解决刷新后用户状态丢失的问题。第二,redirect参数保留原始目标路径,登录成功后可以直接跳回。第三,不要在前端直接判断角色编码来决定菜单显示,而应该由后端返回菜单列表,前端只做渲染。

3.3 如何审查 AI 生成的代码

Vibe Coding 最容易犯的错误是全盘接受 AI 输出。无论生成速度多快,都应该按下表做一次快速审查。

审查项检查点
依赖安全是否引入了未知第三方包,版本是否匹配
登录安全token 是否保存在可被 XSS 读取的位置,是否使用了 HttpOnly Cookie
路由守卫是否有死循环跳转,是否处理了菜单拉取失败
权限逻辑是否只判断了菜单存在,而没有判断接口级权限
错误处理接口失败时是否有统一错误提示,登录态失效是否会跳登录
环境配置baseURL 是否通过环境变量控制,是否把密钥写进了前端代码

审查时建议直接查看生成的package.json、路由文件、store 文件和请求工具。AI 生成的代码往往“看起来完整”,但可能缺少异常分支或类型定义。

注意:Vibe Coding 的价值在于把样板代码的生成速度加快,不意味着可以跳过代码评审。越是自动生成的代码,越需要人工确认边界条件。

4. 低代码能力实现:Schema 驱动的表单与页面

4.1 为什么用 Schema 驱动

后台系统里表单页数量通常很多。如果每个表单都手写一遍,会发现大量代码只是字段名称不同。低代码方案的核心思路是:把“字段定义、校验规则、组件类型、布局信息”从代码中抽离成数据,用同一个渲染器去渲染。

这样做有三个好处:

  • 新增页面只需要新增一条配置,不需要写新的 .vue 页面。
  • 配置可以存数据库,运营和非研发人员也可以在受控范围内维护页面。
  • 配置可以版本化,对比代码变更更直观。

当然也有代价:渲染器需要维护更多组件类型,配置错误时需要做更完善的提示。因此,Schema 驱动更适合规则稳定的内部管理页面,不适合交互非常复杂、状态非常多变的页面。

4.2 表单 Schema 结构

下面是一个最小表单 Schema 示例。

{ "type": "form", "fields": [ { "name": "projectName", "label": "项目名称", "component": "input", "placeholder": "请输入项目名称", "rules": [ { "required": true, "message": "请输入项目名称" } ] }, { "name": "owner", "label": "负责人", "component": "select", "options": [ { "label": "张三", "value": "zhangsan" }, { "label": "李四", "value": "lisi" } ] }, { "name": "startDate", "label": "开始日期", "component": "date-picker", "valueFormat": "YYYY-MM-DD" } ] }

字段参数可以按功能分组,常用参数如下表。

参数含义示例值
name字段名,提交数据时的 keyprojectName
label表单标签项目名称
component渲染组件类型input / select / date-picker
placeholder占位提示请输入项目名称
options下拉选项[{ label: '张三', value: 'zhangsan' }]
rules校验规则[{ required: true, message: '请输入项目名称' }]
span栅格占位宽度12 表示半行

4.3 渲染器实现示例(Vue 3)

渲染器的目标很明确:输入 Schema,输出表单。先定义类型。

// src/types/schema.ts export interface FieldRule { required?: boolean message?: string pattern?: RegExp } export interface SchemaField { name: string label: string component: 'input' | 'select' | 'date-picker' | 'number' | 'switch' placeholder?: string options?: Array<{ label: string; value: string | number }> rules?: FieldRule[] valueFormat?: string span?: number } export interface FormSchema { type: 'form' fields: SchemaField[] }

然后实现渲染组件。核心是使用动态组件component :is,根据field.component渲染对应表单组件。

<!-- src/components/schema-renderer/SchemaForm.vue --> <template> <el-form ref="formRef" :model="formData" :rules="formRules" label-width="120px" > <el-row> <el-col v-for="field in schema.fields" :key="field.name" :span="field.span || 8" > <el-form-item :label="field.label" :prop="field.name"> <component :is="resolveComponent(field.component)" v-model="formData[field.name]" v-bind="componentProps(field)" > <el-option v-if="field.component === 'select'" v-for="opt in field.options" :key="opt.value" :label="opt.label" :value="opt.value" /> </component> </el-form-item> </el-col> </el-row> </el-form> </template> <script setup lang="ts"> import { reactive, computed } from 'vue' import type { FormInstance, FormRules } from 'element-plus' import type { FormSchema, SchemaField } from '@/types/schema' const props = defineProps<{ schema: FormSchema modelValue: Record<string, any> }>() const emit = defineEmits<{ (e: 'update:modelValue', value: Record<string, any>): void }>() const formData = reactive(props.modelValue) const formRules = computed<FormRules>(() => { const rules: FormRules = {} props.schema.fields.forEach((field: SchemaField) => { if (field.rules) { rules[field.name] = field.rules } }) return rules }) function resolveComponent(component: string) { const map: Record<string, string> = { input: 'el-input', select: 'el-select', 'date-picker': 'el-date-picker', number: 'el-input-number', switch: 'el-switch', } return map[component] || 'el-input' } function componentProps(field: SchemaField) { const props: Record<string, any> = { placeholder: field.placeholder || `请输入${field.label}`, } if (field.valueFormat) { props.valueFormat = field.valueFormat } return props } const formRef = ref<FormInstance>() async function validate() { if (!formRef.value) return false return formRef.value.validate() } defineExpose({ validate, formData }) </script>

这个渲染器的关键点有两个。第一,动态组件名必须注册或通过全局组件方式引入,否则会报“Failed to resolve component”;第二,prop绑定使用字段名,字段名改变时校验规则才能关联到对应表单项。

4.4 动态路由与菜单

低代码页面通常需要动态注册路由。常见做法是:后端在登录时返回菜单列表,前端把菜单转换成 Vue Router 的可注册路由,并在布局加载时完成注册。

// src/utils/dynamic-router.ts export function buildAsyncRoutes(menus: MenuItem[]) { return menus .filter((menu) => menu.path && menu.component) .map((menu) => ({ path: menu.path, name: menu.name, component: loadComponent(menu.component), meta: { title: menu.name, icon: menu.icon }, })) } function loadComponent(componentPath: string) { // componentPath 示例:lowcode/page-preview/index return () => import(`@/views/${componentPath}.vue`) }

这里要注意,动态导入路径在 Vite 中不能使用完全动态变量,可以通过import.meta.glob预先注册所有页面组件,再按映射关系取组件。生成代码时,AI 经常会忽略这一点,导致动态路由无法打包。

5. AI 中心实现:会话、Prompt 与技能编排

5.1 业务闭环

AI 中心的完整流程可以拆成五步:

  1. 用户发起提问,前端创建或选择会话。
  2. 前端根据技能选择,把请求发送到/api/ai/chat。
  3. 后端读取会话历史、技能配置和知识库检索结果。
  4. 后端调用大模型接口,返回流式数据或完整结果。
  5. 前端展示消息,并把消息保存到数据库。

这五步中,最容易出问题的是第 4 步。流式返回能显著提升对话体验,但它要求前后端使用 ReadableStream 或 SSE 协议;如果只是做最小可运行版本,也可以先让后端一次性返回完整结果,后续再升级为流式。

5.2 后端接口设计

接口设计要保持稳定,前端和低代码配置都依赖它。推荐的最小接口集如下。

方法路径说明
POST/api/ai/session创建会话
GET/api/ai/session查询当前用户会话列表
GET/api/ai/message?sessionId=1查询会话消息
POST/api/ai/chat发送消息并获取回复
POST/api/ai/knowledge/upload上传知识库文档

Express 后端示例:

// server/src/routes/ai.ts import { Router } from 'express' import { createSession, listSessions } from '../services/ai-session' import { chat } from '../services/ai-chat' const router = Router() router.post('/session', async (req, res) => { const userId = req.user.id const session = await createSession({ userId, title: req.body.title }) res.json({ code: 0, data: session }) }) router.get('/session', async (req, res) => { const sessions = await listSessions(req.user.id) res.json({ code: 0, data: sessions }) }) router.post('/chat', async (req, res) => { const { sessionId, content, skillCode } = req.body const reply = await chat({ sessionId, content, skillCode, userId: req.user.id }) res.json({ code: 0, data: { sessionId, reply } }) }) export default router

服务层的chat函数是核心,它负责组装请求参数。

// server/src/services/ai-chat.ts import { getSessionHistory } from '../models/ai-message' import { getSkill } from '../models/ai-skill' import { searchKnowledge } from './knowledge' export async function chat({ sessionId, content, skillCode, userId }) { const history = await getSessionHistory(sessionId) const skill = await getSkill(skillCode) const knowledge = await searchKnowledge(content, { limit: 3 }) const messages = [ { role: 'system', content: buildSystemPrompt(skill, knowledge), }, ...history.map((m) => ({ role: m.role, content: m.content })), { role: 'user', content }, ] const modelReply = await callModel({ messages }) await saveMessage({ sessionId, role: 'user', content }) await saveMessage({ sessionId, role: 'assistant', content: modelReply }) return modelReply }

注意,密钥必须保存在后端,不能出现在前端代码中。前端只传递content和skillCode。

5.3 前端会话交互

前端对话页的核心是消息列表 + 输入框 + 发送按钮。发送后先把用户消息插入列表,再调用接口获取回复,最后把回复追加到消息列表。

<!-- src/views/ai-center/Conversation.vue 片段 --> <template> <div class="conversation"> <div class="message-list"> <div v-for="message in messages" :key="message.id" class="message-item" :class="message.role" > {{ message.content }} </div> </div> <el-input v-model="draft" type="textarea" placeholder="输入你的问题" @keydown.enter.prevent="send" /> <el-button type="primary" @click="send">发送</el-button> </div> </template> <script setup lang="ts"> import { ref } from 'vue' import { chat } from '@/api/ai' const messages = ref<MessageItem[]>([]) const draft = ref('') async function send() { const content = draft.value.trim() if (!content) return messages.value.push({ id: Date.now(), role: 'user', content }) draft.value = '' const res = await chat({ sessionId, content, skillCode }) messages.value.push({ id: Date.now() + 1, role: 'assistant', content: res.data.reply }) } </script>

这段代码只实现了最小闭环。实际项目还需要处理:加载历史消息、接口异常提示、请求中禁用发送按钮、流式输出时的光标和滚动问题。

5.4 Prompt 与知识库最小实现

AI 中心如果只有对话,价值有限;加入“技能”和“知识库”后,才更像一个可编排的智能中心。技能可以理解为一组预设的系统提示词和调用参数。

{ "skillCode": "project-summary", "skillName": "项目总结生成", "systemPrompt": "你是一名资深项目经理,请根据用户提供的项目信息生成结构清晰的总结报告。", "temperature": 0.3, "maxTokens": 1000 }

知识库的最小实现可以不做向量检索,先用关键词匹配文档片段。比如后端把上传的文本切分成段落,根据用户问题中的关键词匹配最相关的片段,再把片段拼进 system prompt。这样在内容和数据量都不大的内部系统中,已经能提供可用的“引用来源”能力。

注意:大模型输出存在不确定性。AI 中心的回复应标记为“AI 生成”,涉及重要决策时不能直接代替人工审核。

6. 运行验证:从本地启动到接口连通

6.1 本地启动步骤

前端和后端需要分别启动。先复制环境变量文件,再启动后端,最后启动前端。

# 后端 cd server cp .env.example .env npm install npm run dev
# 前端 cd frontend npm install npm run dev

.env.example中至少要包含数据库连接、JWT 密钥、大模型接口地址等配置,结构如下。

# server/.env.example PORT=3000 DATABASE_URL=sqlite:./dev.db JWT_SECRET=please-change-me AI_API_URL=https://your-model-service.example.com/v1/chat/completions AI_API_KEY=your-model-api-key AI_MODEL=default-model-name

真实项目中,AI_API_KEY必须保存在服务端环境变量里,不要提交到 Git 仓库。

6.2 验证清单

启动后按功能模块依次验证,不要只看页面有没有打开。

功能操作预期结果
登录输入账号密码并登录跳转到首页,菜单正常显示
路由守卫未登录直接访问/跳转到登录页
动态菜单用不同角色登录看到不同菜单
低代码表单打开表单配置页并提交校验规则生效,提交数据正确
AI 对话创建会话并发送问题得到模型回复,消息写入数据库
知识库上传文档后提问相关关键词回复中引用知识库片段

验证时重点看浏览器 Network 面板。关注请求是否发出、状态码是否 2xx、返回体是否符合预期。

6.3 预期错误与确认方式

如果某个功能不通过,按先后顺序确认:

  1. 前端页面的 Console 是否有报错。
  2. Network 面板请求是否发出,请求地址是否正确。
  3. 后端日志是否打印了对应的路由和处理结果。
  4. 数据库中的数据是否写入,字段是否符合预期。

例如 AI 对话失败时,可以先看请求体里的skillCode是否为空,再看后端日志里调用模型接口的响应状态,最后看数据库里是否有用户消息写入。

7. 常见问题排查

7.1 动态路由刷新后白屏或菜单丢失

现象:登录后正常,刷新页面后白屏,或菜单全部消失。

原因:Vibe Coding 生成的路由守卫通常会在刷新后重新获取菜单,但如果顺序不对,路由注册发生在页面渲染之后,就会白屏。另一种常见原因是 Pinia 状态只在内存中,刷新后用户信息和菜单为空,而守卫里没有重新拉取。

排查路径:

  • 在beforeEach里打印to.path、userStore.menus和已注册路由。
  • 确认刷新后是否执行了fetchMenus。
  • 确认动态路由是否在菜单拉取完成后才执行router.addRoute。

解决方案:

  • 将菜单拉取放在全局守卫的await中。
  • 动态路由注册完成后,使用next({ ...to, replace: true })重新进入目标路由。
  • 不要依赖刷新前的内存状态,每次刷新都从后端获取菜单是最稳妥的方式。

7.2 AI 请求跨域或请求失败

现象:前端调用/api/ai/chat报 CORS 错误,或返回 500。

原因:前端开发服务器端口是 5173,后端端口是 3000,跨域问题在本地环境很常见。请求失败也可能是AI_API_URL配置错误,或者模型接口返回了不支持的格式。

排查路径:

  • 看浏览器 Network 中请求是否真正发出。
  • 看后端日志是否收到请求。
  • 看后端调用模型接口时返回了什么错误码。

解决方案:

  • 在 Express 中配置 CORS 中间件,或者通过 Vite proxy 转发/api请求。
  • 确认.env中AI_API_URL能被后端访问。
  • 在后端捕获模型异常时,把错误状态和响应体记录到日志,不要只返给前端“服务器错误”。

7.3 Schema 渲染器更新不生效或表单项状态错乱

现象:修改 Schema 后页面没有变化;切换表单类型后,表单项的值残留。

原因:渲染器把modelValue转成响应式对象时,数据来源不统一。如果组件内部直接修改了props.modelValue的引用,可能绕过父组件更新;v-for的 key 使用字段名时,字段删除后旧状态仍可能保留。

排查路径:

  • 检查父组件是否把新 Schema 传入渲染器。
  • 检查渲染器是否用watch监听props.schema变化。
  • 检查重置表单时是否对整个formData做重新赋值。

解决方案:

  • 使用watch(() => props.schema, () => { Object.assign(formData, emptyForm()) })在 Schema 变化时重建表单数据。
  • 渲染器的v-for使用field.name作为 key,但删除字段时会丢失对应状态,因此重置时需主动清理。
  • 对外暴露resetFields方法,让调用方明确控制表单重置。

7.4 Vibe Coding 生成代码依赖版本冲突

现象:安装依赖后启动报错,例如 Element Plus 与 Vue 版本不匹配,或 Vite 插件版本要求更高的 Node.js。

原因:Vibe Coding 生成的package.json往往使用“最新版本”或某个示例版本,不会自动适配本地 Node 环境。

排查路径:

  • 运行node -v和npm -v查看环境。
  • 查看npm install的警告日志。
  • 访问依赖官网或 npm 页面确认版本兼容要求。

解决方案:

  • 不要盲目升级所有依赖,先固定一个已知可用的组合。
  • 生成代码后立即查看package.json,把主版本号确认到位。
  • 使用npm ls检查无效依赖树。

7.5 综合排查顺序

遇到实际问题时,建议按以下顺序排查,避免在错误层反复打转。

  1. 输入是否符合预期,例如用户输入的 prompt、表单数据、请求参数。
  2. 文件路径和命名是否正确,包括路由路径、组件路径、导入路径。
  3. 依赖版本是否匹配,重点看前端框架和 UI 库。
  4. 配置是否生效,例如环境变量、代理配置、路由守卫。
  5. 权限、端口、网络、存储是否正确。
  6. 日志中的异常信息和堆栈位置,确定是前端还是后端问题。
  7. 框架或工具本身是否存在版本限制,例如 Vite 对动态 import 的限制。

8. Vibe Coding 项目的最佳实践与下一步方向

8.1 提示词模板

把有效提示词沉淀成模板,能明显提高后续项目的启动效率。一个可复用的 Vibe Coding 提示词模板如下:

请基于 {技术栈} 实现 {模块名称},要求: 1. 功能边界:{列出核心功能,不要使用模糊的“尽量完善”} 2. 数据结构:{字段名、类型、关联关系} 3. 接口约定:{路径、方法、请求参数、返回格式} 4. 异常处理:{需要覆盖的错误场景} 5. 代码风格:{类型定义、命名规则、注释要求} 6. 完成后运行 {验证命令},并说明启动步骤。

提示词越具体,生成结果越可控。“请你写一个用户管理页面”这样的描述会得到泛泛的代码;而“请实现用户列表接口,字段 id、username、nickname、status,支持分页和关键词搜索,返回 { code, data, message } 结构”才会得到可运行的结果。

8.2 代码审查清单

每次 AI 生成代码后,至少按以下清单检查一遍。

审查维度检查内容
类型安全是否省略了接口返回类型,是否使用any绕过类型检查
接口一致性前端字段和后端字段是否对齐,是否缺少序列化处理
权限控制是否只做了前端菜单隐藏,而没有做后端接口鉴权
密钥安全是否有密钥、Token 被硬编码到前端或提交到 Git
异常处理接口失败、模型超时、数据库连接失败是否有兜底
日志关键业务操作是否留下可检索日志,敏感信息是否脱敏
回滚配置变更是否可以通过版本管理回滚

其中权限控制最容易出错。前端隐藏按钮并不等于安全,真正的权限判断必须发生在后端。

8.3 从模板到内部平台

这个模板可以继续扩展成更完整的中后台平台,推荐按这个顺序逐步增加能力。

第一个方向是可视化页面设计器。当前版本通过 JSON Schema 配置表单,但配置过程仍然在写 JSON。可以做一个拖拽式设计器,组件拖拽后自动生成 Schema,再把 Schema 保存到数据库。这样,非研发人员也能维护简单页面。

第二个方向是 AI 技能市场。把技能配置从代码中抽离,做成可被人为启用的模块。每个技能拥有独立系统提示词、知识库、参数模板和调用权限,不同部门可以维护自己的技能。

第三个方向是多租户支持。如果后续要服务多个内部团队,用户表、菜单表、配置表都要增加tenant_id字段,并在查询时强制带上租户过滤条件。做多租户时要特别注意数据隔离,避免通过修改链接参数访问其他租户数据。

8.4 学习环境与生产环境的差异

开发环境能跑通,不等于生产环境能稳定运行。两者之间还存在大量工程差异。

维度本地开发生产环境
配置使用本地.env配置中心或容器环境变量统一管理
数据库SQLite 便于启动MySQL / PostgreSQL 且需要备份
日志控制台输出结构化日志 + 集中采集
鉴权可省略或简化必须校验 token 过期和权限
模型调用可选 mock需要考虑超时、限流、费用告警
部署前后端分别启动构建产物 + CI/CD + 容器化
回滚本地改代码版本发布策略 + 数据库迁移回滚

在生产环境接入大模型时,还需要额外关注模型调用的超时时间、输入长度限制、敏感信息过滤和费用控制。不要把模型服务厂商的配置写在代码里,也不要在配置库中保存明文密钥。

这个系列把后台管理模板作为第一个项目,是因为它足够通用,也足够复杂。完成这个项目后,再写业务系统时就可以把精力集中在真正的业务字段和规则上,而不再重复处理布局、权限、路由、表单等基础工程问题。

如果只记住一个核心判断,那就是:Vibe Coding 不是把架构设计也交给 AI,而是让 AI 快速执行你确认过边界和方案的编码任务。低代码引擎和 AI 中心只是手段,这套模板真正沉淀下来的,是数据模型、接口约定和一套稳定的开发流程。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询