feat: initial release
Publish to npm / publish (push) Failing after 9s
Publish to npm / publish (push) Failing after 9s
This commit is contained in:
@@ -0,0 +1,62 @@
|
|||||||
|
name: Publish to npm
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*.*.*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: release # npm Trusted Publisher 环境
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write # OIDC 认证
|
||||||
|
contents: read # 读取仓库
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # 完整 history 用于版本分析
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
|
# 从 tag 提取版本号
|
||||||
|
- name: Get version from tag
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/v}
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "Package version: $VERSION"
|
||||||
|
|
||||||
|
# 修改 package.json 版本号
|
||||||
|
- name: Update package.json version
|
||||||
|
run: |
|
||||||
|
npm pkg set version=${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
# Trusted Publisher 发布(无需 token)
|
||||||
|
- name: Publish to npm
|
||||||
|
run: npm publish --access public
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
body: |
|
||||||
|
## @simcu/simapi v${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
|
See [changelog](https://github.com/simcu/simapi-vue/releases) for details.
|
||||||
|
draft: false
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Test
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
# 源码不发布
|
||||||
|
src/
|
||||||
|
|
||||||
|
# 开发文件
|
||||||
|
tsconfig.json
|
||||||
|
vite.config.ts
|
||||||
|
vue.config.js
|
||||||
|
|
||||||
|
# 测试
|
||||||
|
**/*.test.ts
|
||||||
|
**/*.spec.ts
|
||||||
|
__tests__/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# 其他
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
# SimApi - 轻量 API 请求库
|
||||||
|
|
||||||
|
基于 Axios 的 Vue 3 + Pinia HTTP 客户端库。
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @simcu/simapi
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @simcu/simapi pinia
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 初始化配置
|
||||||
|
|
||||||
|
在 Vue 应用入口(如 `main.ts` 或 `App.vue`)中配置端点和调试模式:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { useSimApi } from 'simapi'
|
||||||
|
|
||||||
|
const api = useSimApi()
|
||||||
|
|
||||||
|
// 设置 API 端点
|
||||||
|
api.setEndpoints({
|
||||||
|
default: 'https://api.example.com'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 设置调试模式(默认 true)
|
||||||
|
api.setDebug(true)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 发起请求
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { useSimApi } from 'simapi'
|
||||||
|
|
||||||
|
const api = useSimApi()
|
||||||
|
|
||||||
|
// 简单请求
|
||||||
|
const result = await api.query('/users/list', { page: 1 })
|
||||||
|
|
||||||
|
// 带 Token 认证的请求
|
||||||
|
const result = await api.query('/protected/resource', { id: 123 })
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 登录/登出
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 登录
|
||||||
|
const result = await api.login({
|
||||||
|
phone: '13800138000',
|
||||||
|
code: '123456'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 登出
|
||||||
|
await api.logout()
|
||||||
|
```
|
||||||
|
|
||||||
|
## API 参考
|
||||||
|
|
||||||
|
### 配置方法
|
||||||
|
|
||||||
|
| 方法 | 说明 |
|
||||||
|
| ------------------------------------- | ---------------------------------------------------------- |
|
||||||
|
| `setEndpoints(endpoints)` | 设置 API 端点,如 `{ default: 'https://api.example.com' }` |
|
||||||
|
| `setDebug(debug: boolean)` | 开启/关闭调试模式 |
|
||||||
|
| `setBusinessCallback(code, callback)` | 设置业务错误码回调 |
|
||||||
|
|
||||||
|
### 请求方法
|
||||||
|
|
||||||
|
| 方法 | 说明 |
|
||||||
|
| ------------------------ | -------------- |
|
||||||
|
| `api.query(uri, params)` | 发起 POST 请求 |
|
||||||
|
| `api.login(data)` | 登录 |
|
||||||
|
| `api.logout()` | 登出 |
|
||||||
|
|
||||||
|
### 工具方法
|
||||||
|
|
||||||
|
| 方法 | 说明 |
|
||||||
|
| ------------------------ | -------------- |
|
||||||
|
| `api.getEndpoint(name?)` | 获取端点地址 |
|
||||||
|
| `api.getToken()` | 获取当前 Token |
|
||||||
|
|
||||||
|
### Getters
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
| ---------------- | -------------------- |
|
||||||
|
| `api.token` | 当前 Token(响应式) |
|
||||||
|
| `api.isLoggedIn` | 是否已登录(响应式) |
|
||||||
|
|
||||||
|
## 调试日志
|
||||||
|
|
||||||
|
启用调试模式后,控制台会输出:
|
||||||
|
|
||||||
|
```
|
||||||
|
[REQUEST*] queryId -> /uri AUTH: token
|
||||||
|
[RESPONSE] queryId -> {data: {...}, code: 200}
|
||||||
|
```
|
||||||
|
|
||||||
|
设置端点后会自动打印版本信息:
|
||||||
|
|
||||||
|
```
|
||||||
|
UI主应用版本: 1.0.0
|
||||||
|
UISimApi版本: 1.0.0
|
||||||
|
API主应用版本: x.x.x
|
||||||
|
APISimApi版本: x.x.x
|
||||||
|
```
|
||||||
|
|
||||||
|
## 业务错误处理
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const api = useSimApi()
|
||||||
|
|
||||||
|
// 处理 401 未授权
|
||||||
|
api.setBusinessCallback(401, (data) => {
|
||||||
|
console.log('Token 过期', data)
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
router.push('/login')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理 403 禁止访问
|
||||||
|
api.setBusinessCallback(403, (data) => {
|
||||||
|
ElMessage.error('没有权限')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理所有非 200 错误
|
||||||
|
api.setBusinessCallback('common', (data) => {
|
||||||
|
ElMessage.error(data.message)
|
||||||
|
})
|
||||||
|
```
|
||||||
Generated
+1728
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "@simcu/simapi",
|
||||||
|
"version": "1.0.1",
|
||||||
|
"description": "轻量 Vue3 HTTP 客户端库,基于 Axios + Pinia",
|
||||||
|
"main": "dist/simapi.umd.cjs",
|
||||||
|
"module": "dist/simapi.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "vite build",
|
||||||
|
"dev": "vite build --watch",
|
||||||
|
"types": "vue-tsc --declaration --emitDeclarationOnly",
|
||||||
|
"lint": "vue-tsc --noEmit"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"vue3",
|
||||||
|
"pinia",
|
||||||
|
"http",
|
||||||
|
"axios",
|
||||||
|
"api",
|
||||||
|
"rest"
|
||||||
|
],
|
||||||
|
"author": "simcu",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git@github.com:simcu/simapi-vue.git"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.6.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.0.0",
|
||||||
|
"pinia": "^2.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.0.0",
|
||||||
|
"typescript": "^5.0.0",
|
||||||
|
"vite": "^5.0.0",
|
||||||
|
"vue": "^3.4.0",
|
||||||
|
"vue-tsc": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* SimApi - 轻量 API 请求库
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { useSimApi } from './simapi'
|
||||||
|
export type { SimApiAuthConfig, SimApiConfig, Versions } from './simapi'
|
||||||
|
export { SimApiVersion, AppVersion } from './simapi'
|
||||||
+207
@@ -0,0 +1,207 @@
|
|||||||
|
/**
|
||||||
|
* SimApi Pinia Store
|
||||||
|
*
|
||||||
|
* 使用方法:
|
||||||
|
* import { useSimApi } from 'simapi'
|
||||||
|
* const api = useSimApi()
|
||||||
|
* await api.query('/api/xxx', data)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import axios, { AxiosRequestHeaders } from 'axios'
|
||||||
|
|
||||||
|
// ============ 类型定义 ============
|
||||||
|
|
||||||
|
export const SimApiVersion = '1.0.0'
|
||||||
|
export const AppVersion = '1.0.0'
|
||||||
|
|
||||||
|
export interface Versions {
|
||||||
|
uiApp: string
|
||||||
|
uiSimApi: string
|
||||||
|
apiApp: string
|
||||||
|
apiSimApi: string
|
||||||
|
apiAppFull: string
|
||||||
|
apiSimApiFull: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BusinessCallback {
|
||||||
|
[key: number | string]: (data: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SimApiAuthConfig {
|
||||||
|
token_name: string
|
||||||
|
check_url: string
|
||||||
|
logout_url: string
|
||||||
|
login_url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SimApiConfig {
|
||||||
|
endpoints: { [name: string]: string }
|
||||||
|
defaultEndpoint: string
|
||||||
|
businessCallback: BusinessCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Store 定义 ============
|
||||||
|
|
||||||
|
export const useSimApi = defineStore('simapi', {
|
||||||
|
// ============ State ============
|
||||||
|
state: () => ({
|
||||||
|
debug: true,
|
||||||
|
|
||||||
|
auth: {
|
||||||
|
token_name: 'simapi-auth-token',
|
||||||
|
check_url: '/auth/check',
|
||||||
|
logout_url: '/auth/logout',
|
||||||
|
login_url: '/auth/login'
|
||||||
|
} as SimApiAuthConfig,
|
||||||
|
|
||||||
|
api: {
|
||||||
|
endpoints: { default: '' },
|
||||||
|
defaultEndpoint: 'default',
|
||||||
|
businessCallback: {
|
||||||
|
401: () => localStorage.removeItem('simapi-auth-token'),
|
||||||
|
common: () => {}
|
||||||
|
}
|
||||||
|
} as SimApiConfig,
|
||||||
|
|
||||||
|
versions: {
|
||||||
|
uiApp: AppVersion,
|
||||||
|
uiSimApi: SimApiVersion,
|
||||||
|
apiApp: '0.0.0',
|
||||||
|
apiSimApi: '0.0.0',
|
||||||
|
apiAppFull: '0.0.0',
|
||||||
|
apiSimApiFull: '0.0.0'
|
||||||
|
} as Versions
|
||||||
|
}),
|
||||||
|
|
||||||
|
// ============ Getters ============
|
||||||
|
getters: {
|
||||||
|
token: (state) => localStorage.getItem(state.auth.token_name) || '',
|
||||||
|
isLoggedIn: (state) => !!localStorage.getItem(state.auth.token_name)
|
||||||
|
},
|
||||||
|
|
||||||
|
// ============ Actions ============
|
||||||
|
actions: {
|
||||||
|
/** 设置端点配置 */
|
||||||
|
setEndpoints(endpoints: { [name: string]: string }): void {
|
||||||
|
this.api.endpoints = { ...this.api.endpoints, ...endpoints }
|
||||||
|
// 获取并打印版本信息
|
||||||
|
this.getVersions()
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 设置业务错误回调 */
|
||||||
|
setBusinessCallback(code: number | string, callback: (data: any) => void): void {
|
||||||
|
this.api.businessCallback[code] = callback
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 设置调试模式 */
|
||||||
|
setDebug(debug: boolean): void {
|
||||||
|
this.debug = debug
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 打印调试日志 */
|
||||||
|
debug(title: string, data: any): void {
|
||||||
|
if (this.debug) {
|
||||||
|
console.log('[DEBUG]', title, data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 生成随机字符串 */
|
||||||
|
genS4(): string {
|
||||||
|
return (((1 + Math.random()) * 0x10000 * Date.parse(new Date())) | 0).toString(16).substring(1)
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 获取端点地址 */
|
||||||
|
getEndpoint(name?: string): string {
|
||||||
|
return this.api.endpoints[name || this.api.defaultEndpoint] || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 获取并打印版本信息 */
|
||||||
|
async getVersions(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const resp = await axios.post(this.getEndpoint() + '/versions', {}, { timeout: 5000 })
|
||||||
|
if (resp.data?.data) {
|
||||||
|
this.versions = {
|
||||||
|
uiApp: AppVersion,
|
||||||
|
uiSimApi: SimApiVersion,
|
||||||
|
apiApp: resp.data.data.App?.split('+')[0] || '0.0.0',
|
||||||
|
apiSimApi: resp.data.data.SimApi?.split('+')[0] || '0.0.0',
|
||||||
|
apiAppFull: resp.data.data.App || '0.0.0',
|
||||||
|
apiSimApiFull: resp.data.data.SimApi || '0.0.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 版本获取失败不影响主流程
|
||||||
|
}
|
||||||
|
// 打印版本信息,格式与 Angular 一致
|
||||||
|
console.log(`UI主应用版本: ${this.versions.uiApp}\nUISimApi版本: ${this.versions.uiSimApi}\nAPI主应用版本: ${this.versions.apiApp}\nAPISimApi版本: ${this.versions.apiSimApi}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 发起请求 */
|
||||||
|
async query(uri: string, params: any = {}): Promise<any> {
|
||||||
|
const headers: Record<string, string> = {}
|
||||||
|
const queryId = this.genS4()
|
||||||
|
|
||||||
|
if (!(params instanceof FormData)) {
|
||||||
|
headers['Content-Type'] = 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = this.token
|
||||||
|
if (token) {
|
||||||
|
headers['Token'] = token
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.debug) {
|
||||||
|
headers['Query-Id'] = queryId
|
||||||
|
console.log('[REQUEST*]', queryId, '->', uri, 'AUTH:', token)
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = this.getEndpoint() + uri
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post(url, params, { headers: headers as AxiosRequestHeaders })
|
||||||
|
if (this.debug) {
|
||||||
|
console.log('[RESPONSE]', queryId, '->', response.data)
|
||||||
|
}
|
||||||
|
return this.handleResponse(response.data)
|
||||||
|
} catch (error) {
|
||||||
|
if (this.debug) {
|
||||||
|
console.log('[RESPONSE]', queryId, '->', error)
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 处理响应 */
|
||||||
|
handleResponse(data: any): any {
|
||||||
|
if (data.code !== 200) {
|
||||||
|
const callback = this.api.businessCallback[data.code] || this.api.businessCallback['common']
|
||||||
|
callback?.(data)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 登录 */
|
||||||
|
async login(request: Record<string, any>): Promise<any> {
|
||||||
|
const result = await this.query(this.auth.login_url, request)
|
||||||
|
if (result.data) {
|
||||||
|
localStorage.setItem(this.auth.token_name, result.data)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 登出 */
|
||||||
|
async logout(url?: string | null): Promise<any> {
|
||||||
|
localStorage.removeItem(this.auth.token_name)
|
||||||
|
if (url !== null) {
|
||||||
|
return this.query(url ?? this.auth.logout_url).catch(() => true)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 获取 Token */
|
||||||
|
getToken(): string {
|
||||||
|
return localStorage.getItem(this.auth.token_name) || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.d.ts"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
build: {
|
||||||
|
lib: {
|
||||||
|
entry: 'src/index.ts',
|
||||||
|
name: 'SimApi',
|
||||||
|
formats: ['es', 'umd'],
|
||||||
|
fileName: (format) => `simapi.${format}.${format === 'es' ? 'js' : 'cjs'}`
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
external: ['vue'],
|
||||||
|
output: {
|
||||||
|
globals: {
|
||||||
|
vue: 'Vue'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user