- 后端
- 前端
- 认证鉴权
- 低代码
- 任务调度
【免费下载链接】gin-vue-admin
🚀Vite+Vue3+Gin拥有AI辅助的基础开发平台,企业级业务AI+开发解决方案,内置mcp辅助服务,内置skills管理,支持TS和JS混用。它集成了JWT鉴权、权限管理、动态路由、显隐可控组件、分页封装、多点登录拦截、资源权限、上传下载、代码生成器、表单生成器和可配置的导入导出等开发必备功能。
本篇指南以 gin-vue-admin 仓库 aiDoc/examples/frontend 目录下的讲解型示例为核心,面向「在新增或修改前端文件时需要遵循既有代码约定」的开发场景(包括 AI 辅助编码场景),系统梳理前端四类最常见文件的推荐写法:接口封装(API)、全局状态(Pinia)、页面组件(View)与工具函数复用(Utils)。读完本文,你将掌握 gin-vue-admin 前端分层边界、每一层的标准代码骨架、常见反模式,以及可以直接对照的真实参考文件。
一、目录定位:这组示例解决什么问题
aiDoc/examples/frontend/目录聚焦前端常见文件类型的讲解型示例,它本身是一份索引,指向四份独立的示例文档:
| 文档 | 讲解主题 | 相对路径 |
|---|---|---|
| API 示例 | 前端接口封装 | aiDoc/examples/frontend/api-example.md |
| Pinia 示例 | 全局状态管理 | aiDoc/examples/frontend/pinia-example.md |
| View 示例 | 页面组件 | aiDoc/examples/frontend/view-example.md |
| 工具函数示例 | 通用能力复用 | aiDoc/examples/frontend/utils-usage-example.md |
按照 aiDoc/examples/frontend/README.md 的适用范围说明,当需要新增或修改以下目录中的文件时,应优先阅读本目录下的对应示例:
web/src/api/**—— 后端接口封装层web/src/pinia/**—— 全局状态层web/src/view/**—— 页面视图层web/src/plugin/**—— 插件前端代码
这一约定把「前端分层规范」沉淀为可被检索、可被对照的示例文档,核心目标只有一个:新代码与既有代码保持同一套结构,接口契约不漂移,状态逻辑不散落,通用能力不重复造轮子。
二、API 层:统一走@/utils/request的接口封装
2.1 这一层负责什么
前端 API 文件负责把后端接口封装成可复用函数,统一走@/utils/request导出的service实例,不在组件里直接拼 axios 请求。需要这样写的场景包括:
- 新增模块接口;
- 给页面提供列表、详情、创建、更新、删除方法;
- 为插件页面补接口封装。
2.2 推荐写法
以订单模块为例,标准骨架如下(引自 aiDoc/examples/frontend/api-example.md):
import service from '@/utils/request' // @Summary 分页获取订单列表 // @Router /order/getOrderList [post] export const getOrderList = (data) => { return service({ url: '/order/getOrderList', method: 'post', data }) } // @Summary 创建订单 // @Router /order/createOrder [post] export const createOrder = (data) => { return service({ url: '/order/createOrder', method: 'post', data }) }2.3 为什么这样写(源码佐证)
第一,所有请求自动复用拦截器能力。仓库真实入口 web/src/utils/request.js 基于axios.create()创建service实例,并在请求/响应拦截器中统一处理了:
config.baseURL默认取import.meta.env.VITE_BASE_API;- 自动注入请求头
x-token(来自 userStore 的 token)与x-user-id,见 web/src/utils/request.js; - 统一的全局 Loading 展示与并发计数、以及 30 秒强制关闭兜底;
- 响应中
code === 0判定业务成功,new-token响应头自动续期 token; - 401 自动清理登录态并跳转登录页、密码过期强制跳转改密页、错误消息去重(最多同时展示 3 条),见 web/src/utils/request.js。
这些逻辑一旦在组件里直接用 axios 绕开service,就会全部失效,因此「统一走 service」不是风格偏好,而是安全与体验基线。
第二,JSDoc 风格注释成为接口契约的一部分。真实参考文件 web/src/api/user.js 中每个函数都带@Summary、@Router、@Param、@Success等注释,例如:
// @Tags User // @Summary 分页获取用户列表 // @Security ApiKeyAuth // @Param data body modelInterface.PageInfo true "分页获取用户列表" // @Router /user/getUserList [post] export const getUserList = (data) => { return service({ url: '/user/getUserList', method: 'post', data: data }) }这些注释能让 AI 和协作者在不翻后端代码的情况下快速理解每个接口的用途、路径与方法,也便于与后端 Swagger 注解(见 server/docs/docs.go)形成对照。注意不同接口的传参位置:POST/PUT 用data,DELETE/GET 查询参数用params,可对照插件参考 web/src/plugin/announcement/api/info.js 中deleteInfo与findInfo的写法。
2.4 常见错误
- 在页面组件里直接写 axios(绕过拦截器与 token 注入);
- 把页面状态逻辑混进 API 文件(API 层只描述接口,不管理 UI 状态);
- URL、method、参数位置写错,导致接口契约漂移(前后端不同步时尤其隐蔽)。
三、Pinia 层:ref + computed + async action的全局状态
3.1 这一层负责什么
Pinia store 负责全局状态、异步动作和跨页面共享数据,不负责页面渲染细节。典型适用场景:
- 用户信息、路由、字典、系统参数等共享状态;
- 多页面都会用到的业务状态;
- 需要统一缓存或集中副作用的场景。
3.2 推荐写法
setup 风格(Composition)的 store 骨架(引自 aiDoc/examples/frontend/pinia-example.md):
import { defineStore } from 'pinia' import { ref, computed } from 'vue' import { getOrderList } from '@/api/order' export const useOrderStore = defineStore('order', () => { const list = ref([]) const total = ref(0) const loading = ref(false) const hasData = computed(() => list.value.length > 0) const fetchList = async (params) => { loading.value = true try { const res = await getOrderList(params) if (res.code === 0) { list.value = res.data.list total.value = res.data.total } return res } finally { loading.value = false } } const reset = () => { list.value = [] total.value = 0 } return { list, total, loading, hasData, fetchList, reset } })3.3 为什么这样写(源码佐证)
ref + computed + async action是仓库内最自然的组织方式。真实用户 store web/src/pinia/modules/user.js 即完全采用 setup 风格:用ref定义userInfo、token,用async函数实现LoginIn、LoginOut、GetUserInfo、ClearStorage等动作,并通过return { ... }统一暴露。登录动作内部完整展示了「store 承担副作用」的典型流程:
const LoginIn = async (loginInfo) => { const res = await login(loginInfo) if (res.code !== 0) return false setUserInfo(res.data.user) setToken(res.data.token) // 密码过期强制跳转改密页 if (res.data.needChangePassword) { await router.push({ name: 'ForceChangePassword' }) return true } // 初始化并注册异步路由 const routerStore = useRouterStore() await routerStore.SetAsyncRouter() ... }可以看到,登录、登出、清理缓存、拉取用户信息这些跨页面共享的副作用全部收进 store,页面层只调用一个方法。字典 store web/src/pinia/modules/dictionary.js 则示范了「store 内做数据标准化与缓存」:它把后端树形字典数据统一规范为label / value / extend / children结构,并提供按深度过滤(filterTreeByDepth)与扁平化(flattenTree)等内部工具,供 web/src/utils/dictionary.js 的getDict(type, { depth, value })复用。
把 loading 和 reset 一并收进 store,调用侧更干净。示例中的fetchList用try/finally保证 loading 复位,reset提供状态清理入口——页面切换或组件卸载时不必再逐个字段手动归零。
3.4 常见错误
- 把所有局部页面状态都塞进全局 store(局部状态应留在页面里用
ref管理); - 在 store 里写大量 DOM 操作(store 不感知 DOM,与渲染解耦);
- 不做 loading / reset 管理,导致页面状态混乱。
四、View 层:查询区 + 表格区 + 弹窗区的页面骨架
4.1 这一层负责什么
页面组件负责查询表单、表格、弹窗、抽屉和交互流程,是用户真正接触到的界面层。适用场景:
- 新增后台管理页面;
- 新增列表页 + 搜索 + 分页;
- 新增表单弹窗或抽屉流程。
4.2 推荐写法
一个最小的「搜索 + 列表」页面骨架(引自 aiDoc/examples/frontend/view-example.md):
<template> <div> <div class="gva-search-box"> <el-form :inline="true" :model="searchInfo"> <el-form-item label="名称"> <el-input v-model="searchInfo.name" placeholder="请输入名称" /> </el-form-item> <el-form-item> <el-button type="primary" @click="onSubmit">查询</el-button> <el-button @click="onReset">重置</el-button> </el-form-item> </el-form> </div> <div class="gva-table-box"> <el-table :data="tableData" row-key="ID"> <el-table-column label="ID" prop="ID" width="80" /> <el-table-column label="名称" prop="name" /> </el-table> </div> </div> </template> <script setup> import { ref } from 'vue' import { getOrderList } from '@/api/order' const searchInfo = ref({}) const tableData = ref([]) const getTableData = async () => { const res = await getOrderList(searchInfo.value) if (res.code === 0) { tableData.value = res.data.list } } const onSubmit = () => { getTableData() } const onReset = () => { searchInfo.value = {} getTableData() } getTableData() </script>4.3 为什么这样写(源码佐证)
查询区和表格区结构清晰,符合项目后台页面习惯。真实参考文件 web/src/view/systemTools/apiToken/index.vue 完整呈现了这套约定:外层用gva-search-box包裹内联查询表单(含gva前缀的语义化 class),表格区用gva-table-box,操作按钮区用gva-btn-list,底部用gva-pagination挂载el-pagination(layout="total, sizes, prev, pager, next, jumper")。状态命名也保持一致:searchInfo(查询条件)、tableData(表格数据)、form(表单)、page/pageSize/total(分页)。另一参考文件 web/src/view/superAdmin/api/api.vue 同样遵循该骨架。
script setup下把「状态、请求、交互入口」放在一起,易读。页面只处理展示和交互,不在这里重写公共请求逻辑——数据获取一律调用 API 层封装的函数,全局 loading 与错误提示由request.js的拦截器统一完成,页面内无需重复实现。
4.4 常见错误
- 页面里直接写大量请求封装逻辑(应下沉到 API 层);
- 组件过大,不拆查询区、表格区、弹窗区(弹窗/抽屉建议独立子组件或独立区块);
- 页面状态命名混乱,不区分
searchInfo、tableData、form。
五、Utils 层:先复用src/utils/,不临时再造
5.1 这一层负责什么
当页面或组件需要通用能力时,应先复用src/utils/下已有工具,而不是临时再造一套。常见场景:
- 发送 HTTP 请求;
- 格式化日期;
- 获取字典数据;
- 处理按钮权限;
- 做命名转换;
- 跨组件通信。
5.2 推荐写法
组合复用多个工具(引自 aiDoc/examples/frontend/utils-usage-example.md):
import service from '@/utils/request' import { formatDate, CreateUUID } from '@/utils/format' import { getDict } from '@/utils/dictionary' import { useBtnAuth } from '@/utils/btnAuth' const token = CreateUUID() const createdAt = formatDate(new Date()) const loadStatusDict = async () => { return await getDict('order_status') } const btnAuth = useBtnAuth() export const fetchOrderList = (data) => { return service({ url: '/order/getOrderList', method: 'post', data }) }5.3 为什么这样写(源码佐证)
统一工具入口能减少重复实现,且这些工具已被项目广泛使用。逐一对照源码:
- 请求:
@/utils/request即 web/src/utils/request.js,是全部接口的出口; - 日期/命名:web/src/utils/format.js 提供
formatDate(内部委托formatTimeToStr,输出yyyy-MM-dd hh:mm:ss)、CreateUUID(基于时间戳 +performance.now()生成 UUID 格式字符串)、formatBoolean、filterDict等; - 字典:web/src/utils/dictionary.js 的
getDict(type, { depth, value })在调用字典 store 的同时,内置了generateCacheKey生成缓存键,按「类型 + 深度 + 节点 value」维度缓存,避免页面重复请求;还包含参数校验(type 必须为非空字符串、depth 必须为非负数)与失败回退; - 按钮权限:web/src/utils/btnAuth.js 的
useBtnAuth()直接读取当前路由的route.meta.btns,一行代码即可拿到当前页面的按钮权限集合; - 跨组件通信还可使用 web/src/utils/bus.js 的
emitter(request.js内部也在用emitter.emit('show-error')上报错误)。
复用已有工具比新造 helper 更利于 AI 和人协作——新代码与既有代码共享同一实现,修复一处即可全局生效。
5.4 常见错误
- 手写日期格式化逻辑(应使用 web/src/utils/format.js);
- 直接使用 axios 绕开
request(丢失 token 注入、loading、错误处理与 401 处理); - 自己再实现一套按钮权限判断(应使用 web/src/utils/btnAuth.js);
- 明明已有字典工具,却在页面里重复请求和缓存(应使用 web/src/utils/dictionary.js)。
六、分层边界速查与总结
四层职责可以概括为一句话:API 层只描述接口,Store 层管全局状态与副作用,View 层管展示与交互,Utils 层提供通用能力。对应的「就近原则」是:
| 能力 | 应该放在哪 | 不应该放在哪 |
|---|---|---|
| 接口请求 | API 层(web/src/api/**、web/src/plugin/**/api/**) | View 层、Store 层 |
| 全局共享状态与副作用 | Store 层(web/src/pinia/modules/**) | View 层、API 层 |
| 页面展示与交互 | View 层(web/src/view/**) | Store 层 |
| 通用能力(日期、字典、权限、请求) | Utils 层(web/src/utils/**) | 各页面临时重写 |
四份示例文档对应的真实参考文件汇总如下,可作为新代码的「标准答案」直接对照:
- API 封装:web/src/api/user.js、web/src/plugin/announcement/api/info.js
- Pinia store:web/src/pinia/modules/user.js、web/src/pinia/modules/router.js、web/src/pinia/modules/dictionary.js
- 页面组件:web/src/view/systemTools/apiToken/index.vue、web/src/view/superAdmin/api/api.vue
- 工具函数:web/src/utils/request.js、web/src/utils/format.js、web/src/utils/dictionary.js、web/src/utils/btnAuth.js
按这套规范产出的前端代码,接口契约清晰、状态逻辑集中、页面结构统一、通用能力零重复,无论是人工协作还是 AI 辅助编码,都能以最低的理解成本在 gin-vue-admin 前端中安全落地。
- 后端
- 前端
- 认证鉴权
- 低代码
- 任务调度
【免费下载链接】gin-vue-admin
🚀Vite+Vue3+Gin拥有AI辅助的基础开发平台,企业级业务AI+开发解决方案,内置mcp辅助服务,内置skills管理,支持TS和JS混用。它集成了JWT鉴权、权限管理、动态路由、显隐可控组件、分页封装、多点登录拦截、资源权限、上传下载、代码生成器、表单生成器和可配置的导入导出等开发必备功能。
相关推荐
gin-vue-admin前端组件:可复用组件开发与封装
gin vue admin前端组件:可复用组件开发与封装 引言 在现代前端开发中,组件化(Componentization)已成为构建复杂应用的核心范式。gin
后端前端认证鉴权低代码任务调度告别重复代码!gin-vue-admin前端分页组件开发指南
告别重复代码!gin vue admin前端分页组件开发指南 你是否还在每个页面重复编写分页逻辑?是否厌倦了复制粘贴pageSize、currentPage这些
后端前端认证鉴权低代码任务调度gin-vue-admin前端模块化开发:组件封装与API设计
gin vue admin前端模块化开发:组件封装与API设计 在现代前端开发中,模块化和组件化是提升代码复用性、可维护性的核心手段。gin vue admin
后端前端认证鉴权低代码任务调度
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考