gin-vue-admin 前端代码示例指南:API 封装、Pinia Store、页面组件与工具函数复用规范
2026/9/20 14:34:55 网站建设 项目流程
  • 后端
  • 前端
  • 认证鉴权
  • 低代码
  • 任务调度

【免费下载链接】gin-vue-admin

🚀Vite+Vue3+Gin拥有AI辅助的基础开发平台,企业级业务AI+开发解决方案,内置mcp辅助服务,内置skills管理,支持TS和JS混用。它集成了JWT鉴权、权限管理、动态路由、显隐可控组件、分页封装、多点登录拦截、资源权限、上传下载、代码生成器、表单生成器和可配置的导入导出等开发必备功能。

项目地址:https://gitcode.com/gh_mirrors/gi/gin-vue-admin
点击查看免费下载

本篇指南以 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 中deleteInfofindInfo的写法。

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定义userInfotoken,用async函数实现LoginInLoginOutGetUserInfoClearStorage等动作,并通过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,调用侧更干净。示例中的fetchListtry/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-paginationlayout="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 层);
  • 组件过大,不拆查询区、表格区、弹窗区(弹窗/抽屉建议独立子组件或独立区块);
  • 页面状态命名混乱,不区分searchInfotableDataform

五、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 格式字符串)、formatBooleanfilterDict等;
  • 字典: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 的emitterrequest.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鉴权、权限管理、动态路由、显隐可控组件、分页封装、多点登录拦截、资源权限、上传下载、代码生成器、表单生成器和可配置的导入导出等开发必备功能。

项目地址:https://gitcode.com/gh_mirrors/gi/gin-vue-admin
点击查看免费下载

相关推荐

上一篇:基于vis-three的全自定义Web3D场景编辑器:如何打破传统3D编辑工具的限制
下一篇:CanvasBlocker未来路线图:即将推出的7大隐私保护新功能预览

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询