Skip to content

Repository files navigation

@simcu/simapi — SimApi Vue 前端库(AI 编码参考)

包名: @simcu/simapi | 技术栈: TypeScript + Vue3 + Pinia + 原生 fetch 后端对应: simapi-netSimcu.SimApi NuGet 包) 性质: simapi-net 的官方前端 HTTP 客户端,专为其统一响应格式设计

使用方式: 将本文档作为上下文提供给 AI,或粘贴到对话开头。AI 阅读本文档后应能正确编写调用 simapi-net 接口的前端代码。


0. 一句话定位

simapi-vue 是 simapi-net 的前端搭档。后端用 Simcu.SimApi 写接口,前端用 @simcu/simapi 调接口。两者共享同一套响应格式、认证方式和错误处理约定。


1. 核心概念(必读)

1.1 统一响应格式

simapi-net 所有接口的响应格式固定如下(HTTP 状态码始终 200):

{ "code": 200, "message": "成功", "data": { ... } }
code 含义
200 成功
401 需要登录

1.2 Token 认证

  • 前端通过请求头 Token: <value> 传递认证令牌
  • Token 存储在 Cookie 中,key 默认为 simapi-auth-token
  • Cookie 属性: path=/; secure; samesite=none
  • 不使用 Authorization: Bearer

1.3 请求方式

  • 默认全部 POST,body 为 JSON
  • 使用原生 fetch,不依赖 axios

2. 项目结构

simapi-vue/
├── src/
│ ├── types.ts # 所有类型定义
│ ├── simapi.core.ts # 纯 TS 核心(SimApiCore 类)
│ └── simapi.pinia.ts # Vue3 Pinia Store 封装(useSimApi)
├── dist/ # 构建产物(ESM)
├── package.json # 包名 @simcu/simapi
└── vite.config.ts # Vite 构建配置

导出路径(Subpath Exports)

导入路径内容适用场景
@simcu/simapiSimApiCore + 类型纯 TS / Node.js / 任意 JS 环境
@simcu/simapi/piniauseSimApi Pinia StoreVue3 项目(推荐)

3. 快速开始(Vue3 项目标准用法)

3.1 安装

npm install @simcu/simapi pinia

pinia 是 peerDependency,Vue3 项目必须安装。

3.2 第一步:public/config.json — 外部配置文件

在项目的 public/ 目录下创建 config.json,定义 API 地址:

{
"debug": true,
"endpoints": {
"default": "http://127.0.0.1:5210"
}
}

为什么用 config.json 而不是硬编码?

  • 前后端分离部署时,API 地址可能变化
  • public/ 下的文件 Vite 会直接复制到输出目录,不经过构建
  • 打包后运维人员可以直接修改 config.json 切换环境,无需重新构建

config.json 支持的字段:

字段类型说明默认值
debugboolean是否打印请求/响应日志false
endpointsobject多端点地址映射{ "default": "" }
defaultEndpointstring默认使用的端点名称"default"

3.3 第二步:main.ts — 创建 Pinia 实例

// src/main.tsimport{createApp}from'vue'import{createPinia}from'pinia'// ← 必须安装并注册 PiniaimportAppfrom'./App.vue'importrouterfrom'./router'constapp=createApp(App)app.use(createPinia())// ← useSimApi 依赖 Pinia,必须先注册app.use(router)app.mount('#app')

顺序很重要: createPinia() 必须在 useSimApi() 调用之前完成注册。

3.4 第三步:App.vue — 初始化 SimApi 并设置回调

<!-- src/App.vue -->
<template>
<router-view></router-view>
</template>
<script setup lang="ts">import { useSimApi } from'@simcu/simapi/pinia'// ← 注意 /pinia 子路径import { onMounted } from'vue'import { useRouter } from'vue-router'const api =useSimApi()const router =useRouter()onMounted(async () => {// ① 从 config.json 加载配置(endpoints、debug 等)awaitapi.loadFromFile()// ② 注册业务错误码回调 —— 401 时跳转登录页api.setBusinessCallback(401, () => {api.logout()router.replace({ path: '/login' }) })// ③ 注册通用兜底回调 —— 其他所有非 200 错误统一提示api.setBusinessCallback('common', (data:any) => {console.error('[SimApi]', data.code, data.message) })})</script>

关键点说明:

要点说明
import from '@simcu/simapi/pinia'Vue3 项目必须/pinia 子路径导入
onMounted 中初始化确保 DOM 已加载、Pinia 已就绪
await api.loadFromFile()config.json 加载 endpoints、defaultEndpoint、debug
setBusinessCallback(401, fn)当后端返回 code=401 时自动执行
setBusinessCallback('common', fn)兜底回调,任何非 200 且未匹配其他回调时触发

3.5 第四步:在组件中使用

<!-- src/views/UserList.vue -->
<template>
<div>
<button@click="loadUsers">加载用户</button>
<ulv-for="user in users":key="user.id">{{ user.name }}</ul>
</div>
</template>
<script setup lang="ts">import { ref } from'vue'import { useSimApi } from'@simcu/simapi/pinia'interfaceUser { id:string name:string}const api =useSimApi()const users =ref<User[]>([])asyncfunction loadUsers() {const res =awaitapi.query<User[]>('/user/list', { page: 1 })users.value=res.data?? []}</script>

4. 完整项目模板(可直接复制使用)

public/config.json

{
"debug": true,
"endpoints": {
"default": "http://localhost:5000"
}
}

src/main.ts

import{createApp}from'vue'import{createPinia}from'pinia'importAppfrom'./App.vue'importrouterfrom'./router'createApp(App).use(createPinia()).use(router).mount('#app')

src/App.vue

<template><router-view /></template>
<script setup lang="ts">import { useSimApi } from'@simcu/simapi/pinia'import { onMounted } from'vue'import { useRouter } from'vue-router'const api =useSimApi()const router =useRouter()onMounted(async () => {awaitapi.loadFromFile()api.setBusinessCallback(401, () => {api.logout()router.replace('/login') })})</script>

src/views/Login.vue

<template>
<form@submit.prevent="handleLogin">
<inputv-model="phone"placeholder="手机号" />
<inputv-model="code"placeholder="验证码" />
<buttontype="submit">登录</button>
</form>
</template>
<script setup lang="ts">import { ref } from'vue'import { useRouter } from'vue-router'import { useSimApi } from'@simcu/simapi/pinia'const api =useSimApi()const router =useRouter()const phone =ref('')const code =ref('')asyncfunction handleLogin() {awaitapi.login({ phone: phone.value, code: code.value })router.push('/')}</script>

5. API 参考

5.1 import 方式

// ✅ Vue3 项目 — 用 Pinia Store(推荐)import{useSimApi}from'@simcu/simapi/pinia'// ✅ 非 Vue 项目 / 纯 TS — 用 Core 类import{SimApiCore}from'@simcu/simapi'

5.2 useSimApi Store — 方法与属性一览

获取实例:

constapi=useSimApi()// 单例模式,全局状态共享

属性(Getters)

属性类型说明
api.tokenstring当前存储的 Token(只读)
api.isLoggedInboolean是否已登录(Token 是否存在)
api.debugboolean调试开关
api.apiSimApiApiConfigAPI 配置对象(一般不直接操作)
api.authSimApiAuthConfig认证配置对象(一般不直接操作)

方法(Actions)

方法参数返回值说明
loadFromFile(file?)string (默认 'config.json')Promise<void>从 JSON 文件加载 endpoints/debug 配置
configure(options)SimApiOptionsvoid手动配置(深合并)
setDebug(bool)booleanvoid设置调试模式
setEndpoints(map){[name]: url}void设置多端点映射
query(uri, params?, endpoint?, headers?)见下方详解Promise<SimApiBaseResponse<T>>核心方法:发送 POST 请求
login(request){[key]: any}Promise<SimApiBaseResponse<string>>登录,成功后自动存 Token
logout(url?)string?Promise<any>登出,清除 Token 并调后端接口
checkLogin(url?)string?Promise<void>检查登录态,过期则触发 401 回调
setBusinessCallback(code, fn)number|string, callbackvoid注册业务错误码回调
getToken()string获取当前 Token
setToken(token)stringvoid手动设置 Token
removeToken()void清除 Token
getVersion(endpoint?)string?Promise<SimApiVersions>获取前后端版本信息
getEndpoint(name?)string?string获取某端点的 baseURL

5.3 query 方法详解

这是最核心的方法——几乎所有数据交互都通过它:

asyncfunctionquery<T=any>(uri: string,// 接口路径,如 '/user/list'params?: any={},// 请求体(POST body),JSON 对象endpointKey?: string,// 可选:指定端点名(默认用 default)extraHeaders?: Record<string,string>// 可选:额外请求头): Promise<SimApiBaseResponse<T>>

使用示例:

// 基本查询constres=awaitapi.query<User[]>('/user/list',{page: 1,count: 20})console.log(res.data)// User[] 数组// 错误处理try{constres=awaitapi.query('/user/list')}catch(err: any){// err 是 SimApiBaseResponse 类型console.log(err.code)// 业务错误码,如 400/401/403/500console.log(err.message)// 错误消息}

⚠️ query 的行为要点:

  1. 自动在请求头添加 Token(如果存在)
  2. code === 200 → 正常返回 SimApiBaseResponse<T>
  3. code !== 200 → 先执行对应的 businessCallback,然后 throw 异常
  4. 网络错误 → 执行 responseCallback.error,throw 包装后的 {code: -1} 异常
  5. 所以调用方只需 try/catch 处理异常即可

5.4 login / logout 方法

// login:POST 到 auth.login_url(默认 /auth/login),自动保存返回的 Tokenawaitapi.login({phone: '13800138000',code: '123456'})// logout:清除本地 Token,可选调后端登出接口awaitapi.logout()// 调 /auth/logoutawaitapi.logout(null)// 只清本地 Token,不调后端

5.5 setBusinessCallback — 业务错误处理

api.setBusinessCallback(401,(data)=>{router.replace('/login')})api.setBusinessCallback(403,(data)=>{alert('无权限:'+data.message)})// 兜底:任何未单独处理的非 200 错误都会走 commonapi.setBusinessCallback('common',(data)=>{console.error('请求失败:',data.code,data.message)})

回调执行顺序: 匹配具体错误码 → 未匹配则走 'common' → 然后 throw


6. 类型定义速查

/** 标准响应 */interfaceSimApiBaseResponse<T=any>{code: numbermessage: stringdata?: T}/** 版本信息 */interfaceSimApiVersions{uiApp: stringuiSimApi: stringapiApp: stringapiSimApi: stringapiAppFull: stringapiSimApiFull: string}/** 认证配置 */interfaceSimApiAuthConfig{token_name: stringcheck_url: stringlogout_url: stringlogin_url: string}/** API 配置 */interfaceSimApiApiConfig{endpoints: {[name]: string}defaultEndpoint: stringbusinessCallback: SimApiBusinessCallbackresponseCallback: SimApiResponseCallbacktimeout?: number}/** 完整选项 */interfaceSimApiOptions{debug?: booleanauth?: Partial<SimApiAuthConfig>api?: Partial<SimApiApiConfig>}

7. 多端点支持

{
"debug": true,
"endpoints": {
"default": "https://api.example.com",
"admin": "https://admin.example.com",
"cdn": "https://cdn.example.com"
},
"defaultEndpoint": "default"
}
// 使用默认端点awaitapi.query('/user/list')// 指定端点awaitapi.query('/system/stats',{},'admin')

也可以运行时动态添加:

api.setEndpoints({backup: 'https://backup-api.example.com'})

8. configure — 手动完整配置

除了 loadFromFileconfig.json 读取外,也可以手动配置一切:

api.configure({debug: false,auth: {token_name: 'my-app-token'},api: {endpoints: {default: 'https://api.example.com'},defaultEndpoint: 'default',timeout: 15000,businessCallback: {401: ()=>router.replace('/login'),403: (data)=>alert('无权限'),'common': (data)=>MessagePlugin.error(data.message),},},})

loadFromFile vs configure 的关系:

  • loadFromFile() 只读 config.jsonendpointsdefaultEndpointdebug
  • configure() 可以覆盖所有字段,包括 auth 和 callbacks
  • 通常做法是:loadFromFile() 读基础配置 + setBusinessCallback() 补充回调

9. GOTCHAS(AI 最容易犯的错)

❌ 错误✅ 正确
import { useSimApi } from '@simcu/simapi' (Vue3)from '@simcu/simapi/pinia'(必须带 /pinia
忘记 app.use(createPinia())必须在 useSimApi() 之前注册 Pinia
Authorization: Bearer xxxToken 请求头(这是 simapi-net 约定)
期望 HTTP 4xx/5xx 表示错误所有错误都是 HTTP 200 + JSON code 字段
res.data 直接用而不判空res.data 可能是 undefined,用 res.data ?? []
在 setup 外部调用 useSimApi()useSimApi() 只能在 setup 上下文中调用
用 Authorization Bearer 传 TokenToken 通过 请求头 Token + Cookie 存储
new SimApiCore() 在 Vue 项目里用Vue 项目统一用 useSimApi() Pinia Store

10. 与 simapi-net 后端的对接约定

10.1 通信协议

[Vue 前端] -- POST(JSON) --> [simapi-net 后端]
Header: Token: <value>
Body: { key: value }
[Vue 前端] <-- JSON {code, message, data} -- [simapi-net 后端]
(HTTP Status 始终 200)

10.2 内置路由对照表

simapi-net 路由simapi-vue 方法触发条件
POST /auth/loginapi.login(request)EnableSimApiAuth = true
POST /auth/checkapi.checkLogin()EnableSimApiAuth = true
POST /auth/logoutapi.logout()EnableSimApiAuth = true
POST /user/infoapi.query('/user/info')EnableSimApiAuth = true(需登录)
GET /versionsapi.getVersion()EnableVersionUrl(默认开启)

11. 构建

npm run dev # 开发模式
npm run build # 生产构建

构建产物位于 dist/ 目录:

  • dist/index.mjs — 核心(SimApiCore)ESM 入口
  • dist/pinia.mjs — Pinia Store ESM 入口
  • dist/*.d.ts — TypeScript 类型声明

版本号注入

库使用 declare const 声明版本常量,构建时通过 Vite 的 define 注入。

SimApiVersion 由 simapi 库自身构建时从 package.json 注入。未配置时默认为 0.0.0-develop

AppVersion 由调用方项目在自己的 vite.config.ts 中注入。


12. 从 axios 迁移

- import axios from 'axios'- const res = await axios.post('/user/list', { page: 1 })+ import { useSimApi } from '@simcu/simapi/pinia'+ const api = useSimApi()+ const res = await api.query('/user/list', { page: 1 })
axiossimapi-vue
axios.post()api.query()
response.data 直接是业务数据SimApiBaseResponse<T>.data 是业务数据
HTTP 4xx/5xx 表示错误HTTP 200 + code 字段表示错误
interceptors.responsebusinessCallback + responseCallback
Authorization BearerToken header

About

SimApi 客户端库

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages