refactor: 小程序端代码瘦身与功能补齐

- 清理冗余代码与配置:
  - 删除废弃的 mock 适配器、通用 mock 模块与示例API
  - 移除冗余的 ts 类型文件与 patch 目录
  - 简化 tsconfig 组件路径配置
- 新增功能与优化:
  - 新增全局水印组件与水印配置
  - 接入微信小程序登录/手机号登录
  - 新增系统参数配置 store 与国际化支持
  - 新增 markdown 渲染插件与样式配置
  - 优化 pinia 持久化逻辑修复
  - 新增微信小程序 access_token 缓存配置
  - 修复登录趋势数据结构定义
  - 优化全局请求防抖重定向逻辑
- 页面与路由优化:
  - 补全引导页首次启动逻辑
  - 统一页面底部安全区适配
  - 更新页面路由类型声明
- 代码质量优化:
  - 移除无用 console.log 调试代码
  - 修复全局弹窗重复重登录问题
  - 优化 mp-html 组件配置
This commit is contained in:
zhangtao
2026-08-05 23:47:49 +08:00
parent 372b277a9f
commit ffbe2453f2
126 changed files with 5288 additions and 12246 deletions
-46
View File
@@ -1,46 +0,0 @@
/// <reference types='./globals.d.ts' />
/* tslint:disable */
/* eslint-disable */
/**
* Swagger Petstore - OpenAPI 3.0 - version 1.0.27
*
* This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we&#x27;ve switched to the design first approach!
You can now help us improve the API whether it&#x27;s by making changes to the definition itself or to the code.
That way, with time, we can improve the API in general, and expose some of the new features in OAS3.
Some useful links:
- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)
- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)
*
* OpenAPI version: 3.0.4
*
* Contact:
*
* NOTE: This file is auto generated by the alova's vscode plugin.
*
* https://alova.js.org/devtools/vscode
*
* **Do not edit the file manually.**
*/
export default {
'pet.updatePet': ['PUT', '/pet'],
'pet.addPet': ['POST', '/pet'],
'pet.findPetsByStatus': ['GET', '/pet/findByStatus'],
'pet.findPetsByTags': ['GET', '/pet/findByTags'],
'pet.getPetById': ['GET', '/pet/{petId}'],
'pet.updatePetWithForm': ['POST', '/pet/{petId}'],
'pet.deletePet': ['DELETE', '/pet/{petId}'],
'pet.uploadFile': ['POST', '/pet/{petId}/uploadImage'],
'store.getInventory': ['GET', '/store/inventory'],
'store.placeOrder': ['POST', '/store/order'],
'store.getOrderById': ['GET', '/store/order/{orderId}'],
'store.deleteOrder': ['DELETE', '/store/order/{orderId}'],
'user.createUser': ['POST', '/user'],
'user.createUsersWithListInput': ['POST', '/user/createWithList'],
'user.loginUser': ['GET', '/user/login'],
'user.logoutUser': ['GET', '/user/logout'],
'user.getUserByName': ['GET', '/user/{username}'],
'user.updateUser': ['PUT', '/user/{username}'],
'user.deleteUser': ['DELETE', '/user/{username}']
};
-106
View File
@@ -1,106 +0,0 @@
/*
* @Author: weisheng
* @Date: 2025-04-17 15:58:11
* @LastEditTime: 2025-06-15 21:47:22
* @LastEditors: weisheng
* @Description: Alova response and error handlers
* @FilePath: /wot-starter/src/api/core/handlers.ts
*/
import type { Method } from 'alova'
import router from '@/router'
// Custom error class for API errors
export class ApiError extends Error {
code: number
data?: any
constructor(message: string, code: number, data?: any) {
super(message)
this.name = 'ApiError'
this.code = code
this.data = data
}
}
// Define a type for the expected API response structure
interface ApiResponse {
code: number
msg?: string
data?: any
success?: boolean
total?: number
more?: boolean
}
// Handle successful responses
export async function handleAlovaResponse(
response: UniApp.RequestSuccessCallbackResult | UniApp.UploadFileSuccessCallbackResult | UniApp.DownloadSuccessData,
) {
const globalToast = useGlobalToast()
// Extract status code and data from UniApp response
const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult
// 处理401/403错误(如果不是在handleAlovaResponse中处理的)
if ((statusCode === 401 || statusCode === 403)) {
// 如果是未授权错误,清除用户信息并跳转到登录页
globalToast.error({ msg: '登录已过期,请重新登录!', duration: 500 })
const timer = setTimeout(() => {
clearTimeout(timer)
router.replaceAll({ name: 'login' })
}, 500)
throw new ApiError('登录已过期,请重新登录!', statusCode, data)
}
// Handle HTTP error status codes
if (statusCode >= 400) {
globalToast.error(`Request failed with status: ${statusCode}`)
throw new ApiError(`Request failed with status: ${statusCode}`, statusCode, data)
}
// The data is already parsed by UniApp adapter
const json = data as ApiResponse
// Log response in development
if (import.meta.env.MODE === 'development') {
console.log('[Alova Response]', json)
}
// Return data for successful responses
return json
}
// Handle request errors
export function handleAlovaError(error: any, method: Method) {
const globalToast = useGlobalToast()
// Log error in development
if (import.meta.env.MODE === 'development') {
console.error('[Alova Error]', error, method)
}
// 处理401/403错误(如果不是在handleAlovaResponse中处理的)
if (error instanceof ApiError && (error.code === 401 || error.code === 403)) {
// 如果是未授权错误,清除用户信息并跳转到登录页
globalToast.error({ msg: '登录已过期,请重新登录!', duration: 500 })
const timer = setTimeout(() => {
clearTimeout(timer)
router.replaceAll({ name: 'login' })
}, 500)
throw new ApiError('登录已过期,请重新登录!', error.code, error.data)
}
// Handle different types of errors
if (error.name === 'NetworkError') {
globalToast.error('网络错误,请检查您的网络连接')
}
else if (error.name === 'TimeoutError') {
globalToast.error('请求超时,请重试')
}
else if (error instanceof ApiError) {
globalToast.error(error.message || '请求失败')
}
else {
globalToast.error('发生意外错误')
}
throw error
}
-55
View File
@@ -1,55 +0,0 @@
import AdapterUniapp from '@alova/adapter-uniapp'
import { createAlova } from 'alova'
import vueHook from 'alova/vue'
import mockAdapter from '../mock/mockAdapter'
import { handleAlovaError, handleAlovaResponse } from './handlers'
export const alovaInstance = createAlova({
baseURL: import.meta.env.VITE_API_BASE_URL || 'https://petstore3.swagger.io/api/v3',
...AdapterUniapp({
mockRequest: mockAdapter,
}),
statesHook: vueHook,
beforeRequest: (method) => {
// Add content type for POST/PUT/PATCH requests
if (['POST', 'PUT', 'PATCH'].includes(method.type)) {
method.config.headers['Content-Type'] = 'application/json'
}
// Add timestamp to prevent caching for GET requests
if (method.type === 'GET' && CommonUtil.isObj(method.config.params)) {
method.config.params._t = Date.now()
}
// Log request in development
if (import.meta.env.MODE === 'development') {
console.log(`[Alova Request] ${method.type} ${method.url}`, method.data || method.config.params)
console.log(`[API Base URL] ${import.meta.env.VITE_API_BASE_URL}`)
console.log(`[Environment] ${import.meta.env.VITE_ENV_NAME}`)
}
},
// Response handlers
responded: {
// Success handler
onSuccess: handleAlovaResponse,
// Error handler
onError: handleAlovaError,
// Complete handler - runs after success or error
onComplete: async () => {
// Any cleanup or logging can be done here
},
},
// We'll use the middleware in the hooks
// middleware is not directly supported in createAlova options
// Default request timeout (10 seconds)
timeout: 60000,
// 设置为null即可全局关闭全部请求缓存
cacheFor: null,
})
export default alovaInstance
-92
View File
@@ -1,92 +0,0 @@
/**
* 延迟加载中间件
* 延迟显示加载状态,防止快速请求导致的闪烁
* @param delay 显示加载状态前的延迟时间(毫秒)
* @returns Alova 中间件
*/
export function createDelayLoadingMiddleware(delay = 300) {
return async (context: any, next: any) => {
context.controlLoading()
const { loading } = context.proxyStates
const timer = setTimeout(() => {
loading.v = true
}, delay)
await next()
loading.v = false
clearTimeout(timer)
}
}
/**
* 全局加载中间件
* 为所有请求显示全局加载指示器,支持延迟显示
*
* 使用示例:
* ```typescript
* // 1. 基本用法
* const { send: submit } = useRequest(method, {
* middleware: createGlobalLoadingMiddleware()
* });
*
* // 2. 自定义延迟时间和加载文本
* const { send: submit } = useRequest(method, {
* middleware: createGlobalLoadingMiddleware({
* delay: 500, // 延迟 500ms 显示加载指示器,防止闪烁
* loadingText: '正在提交...', // 自定义加载文本
* })
* });
* ```
*
* @param options 加载选项
* @param options.delay 显示加载指示器前的延迟时间(毫秒),默认 300ms
* @param options.loadingText 加载指示器显示的文本,默认为 'Loading...'
* @returns Alova 中间件
*/
export function createGlobalLoadingMiddleware(options: {
delay?: number
loadingText?: string
} = {}) {
const {
delay = 0,
loadingText = 'Loading...',
} = options
return async (ctx: any, next: any) => {
// 自行控制loading
ctx.controlLoading()
const globalLoading = useGlobalLoading()
let timer: ReturnType<typeof setTimeout> | null = null
// 如果delay为0或未设置,直接显示loading
if (delay <= 0) {
globalLoading.loading(loadingText)
}
else {
// 延迟特定时间显示全局loading
timer = setTimeout(() => {
globalLoading.loading(loadingText)
}, delay)
}
try {
await next()
}
finally {
// 清除定时器并关闭loading
if (timer) {
clearTimeout(timer)
}
globalLoading.close()
}
}
}
// 导出延迟加载中间件作为默认中间件
export const defaultMiddleware = createDelayLoadingMiddleware()
export default defaultMiddleware
-118
View File
@@ -1,118 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/**
* Swagger Petstore - OpenAPI 3.0 - version 1.0.27
*
* This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we&#x27;ve switched to the design first approach!
You can now help us improve the API whether it&#x27;s by making changes to the definition itself or to the code.
That way, with time, we can improve the API in general, and expose some of the new features in OAS3.
Some useful links:
- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)
- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)
*
* OpenAPI version: 3.0.4
*
* Contact:
*
* NOTE: This file is auto generated by the alova's vscode plugin.
*
* https://alova.js.org/devtools/vscode
*
* **Do not edit the file manually.**
*/
import type { Alova, MethodType, AlovaGenerics, AlovaMethodCreateConfig } from 'alova';
import { Method } from 'alova';
import apiDefinitions from './apiDefinitions';
const cache = Object.create(null);
const createFunctionalProxy = (array: (string | symbol)[], alovaInstance: Alova<AlovaGenerics>, configMap: any) => {
const apiPathKey = array.join('.') as keyof typeof apiDefinitions;
if (cache[apiPathKey]) {
return cache[apiPathKey];
}
// create a new proxy instance
const proxy = new Proxy(function () {}, {
get(_, property) {
// record the target property, so that it can get the completed accessing paths
const newArray = [...array, property];
// always return a new proxy to continue recording accessing paths.
return createFunctionalProxy(newArray, alovaInstance, configMap);
},
apply(_, __, [config]) {
const apiItem = apiDefinitions[apiPathKey];
if (!apiItem) {
throw new Error(`the api path of \`${apiPathKey}\` is not found`);
}
const mergedConfig = {
...configMap[apiPathKey],
...config
};
const [method, url] = apiItem;
const pathParams = mergedConfig.pathParams;
const urlReplaced = url!.replace(/\{([^}]+)\}/g, (_, key) => {
const pathParam = pathParams[key];
return pathParam;
});
delete mergedConfig.pathParams;
let data = mergedConfig.data;
if (Object.prototype.toString.call(data) === '[object Object]' && typeof FormData !== 'undefined') {
let hasBlobData = false;
const formData = new FormData();
for (const key in data) {
if (Array.isArray(data[key])) {
for (const ele of data[key]) {
formData.append(key, ele);
if (ele instanceof Blob) {
hasBlobData = true;
}
}
} else {
formData.append(key, data[key]);
if (data[key] instanceof Blob) {
hasBlobData = true;
}
}
}
data = hasBlobData ? formData : data;
}
return new Method(method!.toUpperCase() as MethodType, alovaInstance, urlReplaced, mergedConfig, data);
}
});
cache[apiPathKey] = proxy;
return proxy;
};
export const createApis = (alovaInstance: Alova<AlovaGenerics>, configMap: any) => {
const Apis = new Proxy({} as Apis, {
get(_, property) {
return createFunctionalProxy([property], alovaInstance, configMap);
}
});
return Apis;
};
export const mountApis = (Apis: Apis) => {
// define global variable `Apis`
(globalThis as any).Apis = Apis;
};
type MethodConfig<T> = AlovaMethodCreateConfig<
(typeof import('./index'))['alovaInstance'] extends Alova<infer AG> ? AG : any,
any,
T
>;
type APISofParameters<Tag extends string, Url extends string> = Tag extends keyof Apis
? Url extends keyof Apis[Tag]
? Apis[Tag][Url] extends (...args: any) => any
? Parameters<Apis[Tag][Url]>
: any
: any
: any;
type MethodsConfigMap = {
[P in keyof typeof import('./apiDefinitions').default]?: MethodConfig<
P extends `${infer Tag}.${infer Url}` ? Parameters<NonNullable<APISofParameters<Tag, Url>[0]>['transform']>[0] : any
>;
};
export const withConfigType = <Config extends MethodsConfigMap>(config: Config) => config;
-1058
View File
File diff suppressed because it is too large Load Diff
-18
View File
@@ -1,18 +0,0 @@
// Import the core alova instance
import alovaInstance from './core/instance'
// Export the global Apis object from the generated code
import { createApis, withConfigType } from './createApis'
// Export the alova instance for direct use if needed
export { alovaInstance }
// Configure method options for specific APIs
export const $$userConfigMap = withConfigType({})
// Create the global Apis object
const Apis = createApis(alovaInstance, $$userConfigMap)
// Export both default and named export for AutoImport
export default Apis
export { Apis }
-107
View File
@@ -1,107 +0,0 @@
# API Mock 数据
本目录包含了项目中使用的 API 模拟数据,用于开发和测试环境。
## 目录结构
```
mock/
├── modules/ # 按模块分类的模拟数据
│ ├── common.ts # 通用模拟处理
│ ├── loginInterface.ts # 登录相关接口的模拟数据
│ ├── mdataInterface.ts # 主数据相关接口的模拟数据
│ ├── vehsaleusesignInterface.ts # 车销使用签收相关接口的模拟数据
│ ├── vehsaleusesignarvInterface.ts # 车销使用签收到货相关接口的模拟数据
│ ├── user.ts # 用户相关的模拟数据
│ ├── university.ts # 大学相关的模拟数据
│ └── feedback.ts # 反馈相关的模拟数据
├── utils/ # 工具目录
│ ├── index.ts # 工具导出文件
│ └── generators.ts # 模拟数据生成工具
└── mockAdapter.ts # 模拟适配器配置
```
## 使用方法
模拟数据已经通过 `@alova/mock` 和 `@alova/adapter-uniapp` 集成到项目中。在开发环境中,API 请求会自动使用模拟数据进行响应。
### 启用/禁用模拟
在 `mockAdapter.ts` 中,可以通过修改 `enable` 选项来启用或禁用模拟:
```typescript
const mockAdapter = createAlovaMockAdapter(allMocks, {
// ...
enable: true, // 设置为 false 可禁用模拟
// ...
})
```
### 添加新的模拟数据
1. 在 `modules` 目录下创建新的模块文件或在现有文件中添加
2. 使用 `defineMock` 函数定义模拟数据
3. 在 `mockAdapter.ts` 中导入并添加到 `allMocks` 数组中
示例:
```typescript
// modules/example.ts
import { defineMock } from '@alova/mock'
// mockAdapter.ts
import exampleMocks from './modules/example'
export default defineMock({
'[GET]/api/example': () => {
return {
code: 200,
data: { /* 模拟数据 */ },
message: 'success'
}
}
})
const allMocks = [
// ...
exampleMocks
]
```
## 模拟数据生成工具
在 `utils/generators.ts` 中提供了一系列用于生成模拟数据的工具函数,可以在各个模块中复用:
```typescript
import { generateMockData } from '../utils'
// 生成随机ID
const id = generateMockData.id()
// 生成随机名称
const name = generateMockData.name('前缀')
// 生成随机数组
const array = generateMockData.array(index => ({
id: generateMockData.id(),
name: generateMockData.name(`项目${index}`)
}), 10)
// 生成基础响应对象
const response = generateMockData.baseResponse(data)
// 生成列表响应对象
const listResponse = generateMockData.listResponse(items, total, more)
// 生成业务对象
const user = generateMockData.user()
const goods = generateMockData.goods(0)
const vehSaleEmp = generateMockData.vehSaleEmp(0)
```
## 注意事项
1. 模拟数据应尽量接近真实数据结构,以便于开发和测试
2. 对于需要保持一致性的数据(如ID引用),可以使用固定值而非随机生成
3. 可以使用请求参数来定制模拟响应,例如分页、筛选等
4. 模拟数据应包含各种场景,包括成功和失败的情况
-436
View File
@@ -1,436 +0,0 @@
import Apis from '@/api'
/**
* Pet模块演示
* 演示宠物相关API的使用
*/
export class PetDemo {
// 获取所有可用的宠物
static async getAvailablePets() {
console.log('=== 获取可用宠物 ===')
try {
const pets = await Apis.pet.findPetsByStatus({
params: { status: 'available' },
}).send()
console.log('可用宠物列表:', pets)
return pets
}
catch (error) {
console.error('获取宠物失败:', error)
}
}
// 获取特定宠物信息
static async getPetById(petId: number) {
console.log(`=== 获取宠物 ${petId} 的信息 ===`)
try {
const pet = await Apis.pet.getPetById({
pathParams: { petId },
}).send()
console.log('宠物信息:', pet)
return pet
}
catch (error) {
console.error('获取宠物信息失败:', error)
}
}
// 添加新宠物
static async addNewPet() {
console.log('=== 添加新宠物 ===')
try {
const newPet = await Apis.pet.addPet({
data: {
name: 'Buddy',
category: { id: 1, name: 'Dogs' },
status: 'available',
photoUrls: ['https://example.com/buddy.jpg'],
tags: [
{ id: 1, name: 'friendly' },
{ id: 2, name: 'trained' },
],
},
}).send()
console.log('新添加的宠物:', newPet)
return newPet
}
catch (error) {
console.error('添加宠物失败:', error)
}
}
// 测试宠物不存在的情况
static async testPetNotFound() {
console.log('=== 测试宠物不存在的情况 ===')
try {
await Apis.pet.getPetById({
pathParams: { petId: 404 },
}).send()
}
catch (error) {
console.log('预期的404错误:', error)
}
}
// 删除宠物(需要API密钥)
static async deletePet(petId: number, apiKey: string = 'special-key') {
console.log(`=== 删除宠物 ${petId} ===`)
try {
const result = await Apis.pet.deletePet({
pathParams: { petId },
headers: { api_key: apiKey },
}).send()
console.log('删除结果:', result)
return result
}
catch (error) {
console.error('删除宠物失败:', error)
}
}
}
/**
* Store模块演示
* 演示商店相关API的使用
*/
export class StoreDemo {
// 获取库存信息
static async getInventory() {
console.log('=== 获取库存信息 ===')
try {
const inventory = await Apis.store.getInventory().send()
console.log('库存信息:', inventory)
return inventory
}
catch (error) {
console.error('获取库存失败:', error)
}
}
// 下单购买宠物
static async placeOrder(petId: number, quantity: number = 1) {
console.log('=== 下单购买宠物 ===')
try {
const order = await Apis.store.placeOrder({
data: {
petId,
quantity,
shipDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), // 7天后发货
status: 'placed',
complete: false,
},
}).send()
console.log('订单信息:', order)
return order
}
catch (error) {
console.error('下单失败:', error)
}
}
// 获取订单信息
static async getOrderById(orderId: number) {
console.log(`=== 获取订单 ${orderId} 信息 ===`)
try {
const order = await Apis.store.getOrderById({
pathParams: { orderId },
}).send()
console.log('订单详情:', order)
return order
}
catch (error) {
console.error('获取订单失败:', error)
}
}
// 测试无效订单ID
static async testInvalidOrderId() {
console.log('=== 测试无效订单ID ===')
try {
await Apis.store.getOrderById({
pathParams: { orderId: 999 },
}).send()
}
catch (error) {
console.log('预期的400错误:', error)
}
}
// 删除订单
static async deleteOrder(orderId: number) {
console.log(`=== 删除订单 ${orderId} ===`)
try {
const result = await Apis.store.deleteOrder({
pathParams: { orderId },
}).send()
console.log('删除结果:', result)
return result
}
catch (error) {
console.error('删除订单失败:', error)
}
}
}
/**
* User模块演示
* 演示用户相关API的使用
*/
export class UserDemo {
// 用户登录
static async login(username: string = 'admin', password: string = 'admin') {
console.log('=== 用户登录 ===')
try {
const loginResult = await Apis.user.loginUser({
params: { username, password },
}).send()
console.log('登录结果:', loginResult)
return loginResult
}
catch (error) {
console.error('登录失败:', error)
}
}
// 获取用户信息
static async getUserInfo(username: string) {
console.log(`=== 获取用户 ${username} 信息 ===`)
try {
const user = await Apis.user.getUserByName({
pathParams: { username },
}).send()
console.log('用户信息:', user)
return user
}
catch (error) {
console.error('获取用户信息失败:', error)
}
}
// 创建新用户
static async createUser(userData: any = {}) {
console.log('=== 创建新用户 ===')
try {
const newUser = await Apis.user.createUser({
data: {
username: 'newuser',
firstName: 'John',
lastName: 'Doe',
email: 'john@example.com',
password: 'password123',
phone: '1234567890',
userStatus: 1,
...userData,
},
}).send()
console.log('新用户:', newUser)
return newUser
}
catch (error) {
console.error('创建用户失败:', error)
}
}
// 更新用户信息
static async updateUser(username: string, updateData: any) {
console.log(`=== 更新用户 ${username} 信息 ===`)
try {
const updatedUser = await Apis.user.updateUser({
pathParams: { username },
data: updateData,
}).send()
console.log('更新后的用户:', updatedUser)
return updatedUser
}
catch (error) {
console.error('更新用户失败:', error)
}
}
// 测试用户不存在的情况
static async testUserNotFound() {
console.log('=== 测试用户不存在的情况 ===')
try {
await Apis.user.getUserByName({
pathParams: { username: 'notfound' },
}).send()
}
catch (error) {
console.log('预期的404错误:', error)
}
}
// 批量创建用户
static async createUsersWithArray() {
console.log('=== 批量创建用户(数组) ===')
try {
const result = await Apis.user.createUsersWithListInput({
data: [
{
username: 'user1',
firstName: 'User',
lastName: 'One',
email: 'user1@example.com',
password: 'password',
phone: '1111111111',
userStatus: 1,
},
{
username: 'user2',
firstName: 'User',
lastName: 'Two',
email: 'user2@example.com',
password: 'password',
phone: '2222222222',
userStatus: 1,
},
],
}).send()
console.log('批量创建结果:', result)
return result
}
catch (error) {
console.error('批量创建用户失败:', error)
}
}
// 用户登出
static async logout() {
console.log('=== 用户登出 ===')
try {
const result = await Apis.user.logoutUser().send()
console.log('登出结果:', result)
return result
}
catch (error) {
console.error('登出失败:', error)
}
}
}
/**
* 综合演示
* 演示完整的业务流程
*/
export class FullDemo {
// 完整的宠物商店购买流程
static async completePetStorePurchaseFlow() {
console.log('\n🎯 开始完整的宠物商店购买流程演示\n')
// 1. 用户登录
const loginResult = await UserDemo.login('admin', 'admin')
if (!loginResult)
return
// 2. 查看库存
const inventory = await StoreDemo.getInventory()
console.log('\n📦 当前库存状态:', inventory)
// 3. 浏览可用宠物
const availablePets = await PetDemo.getAvailablePets()
if (!availablePets || availablePets.length === 0) {
console.log('❌ 没有可用的宠物')
return
}
// 4. 选择第一个宠物
const selectedPet = availablePets[0]
console.log('\n🐕 选择的宠物:', selectedPet)
// 5. 查看宠物详情
if (selectedPet?.id) {
await PetDemo.getPetById(selectedPet.id)
}
// 6. 下单购买
const order = selectedPet?.id ? await StoreDemo.placeOrder(selectedPet.id, 1) : null
if (!order)
return
// 7. 查看订单详情
if (order?.id) {
await StoreDemo.getOrderById(order.id)
}
// 8. 获取用户信息
await UserDemo.getUserInfo('admin')
console.log('\n✅ 完整流程演示结束')
}
// 错误处理演示
static async errorHandlingDemo() {
console.log('\n⚠️ 开始错误处理演示\n')
// 测试各种错误情况
await PetDemo.testPetNotFound()
await StoreDemo.testInvalidOrderId()
await UserDemo.testUserNotFound()
console.log('\n✅ 错误处理演示结束')
}
// CRUD操作演示
static async crudDemo() {
console.log('\n🔄 开始CRUD操作演示\n')
// 创建
const newPet = await PetDemo.addNewPet()
const newUser = await UserDemo.createUser({ username: 'testuser' })
// 读取
if (newPet?.id)
await PetDemo.getPetById(newPet.id)
if (newUser)
await UserDemo.getUserInfo('testuser')
// 更新
if (newUser) {
await UserDemo.updateUser('testuser', {
firstName: 'Updated',
lastName: 'User',
})
}
// 删除
if (newPet?.id)
await PetDemo.deletePet(newPet.id)
console.log('\n✅ CRUD操作演示结束')
}
}
// 导出演示运行器
export async function runMockDemo() {
console.log('🚀 开始Mock数据演示\n')
try {
// 运行完整流程演示
await FullDemo.completePetStorePurchaseFlow()
// 运行错误处理演示
await FullDemo.errorHandlingDemo()
// 运行CRUD演示
await FullDemo.crudDemo()
console.log('\n🎉 所有演示完成!')
}
catch (error) {
console.error('演示过程中发生错误:', error)
}
}
// 如果直接运行此文件,执行演示
if (typeof window !== 'undefined') {
// 在浏览器环境中,可以在控制台调用
(window as any).runMockDemo = runMockDemo;
(window as any).PetDemo = PetDemo;
(window as any).StoreDemo = StoreDemo;
(window as any).UserDemo = UserDemo
console.log('Mock演示函数已加载到全局对象,可在控制台调用:')
console.log('- runMockDemo() - 运行完整演示')
console.log('- PetDemo.* - 宠物模块演示')
console.log('- StoreDemo.* - 商店模块演示')
console.log('- UserDemo.* - 用户模块演示')
}
-52
View File
@@ -1,52 +0,0 @@
/*
* @Author: weisheng
* @Date: 2025-04-17 16:21:36
* @LastEditTime: 2025-06-15 22:48:04
* @LastEditors: weisheng
* @Description: Mock适配器配置 - 集成所有模块的mock数据
* @FilePath: /wot-starter/src/api/mock/mockAdapter.ts
* 记得注释
*/
import { uniappMockResponse, uniappRequestAdapter } from '@alova/adapter-uniapp'
import { createAlovaMockAdapter } from '@alova/mock'
// 导入所有mock模块
import commonMocks from './modules/common'
import petMocks from './modules/pet'
import storeMocks from './modules/store'
import userMocks from './modules/user'
const canUseGroupLogger
= typeof console !== 'undefined'
&& typeof console.groupCollapsed === 'function'
&& typeof console.groupEnd === 'function'
// 合并所有mock定义
const allMocks = [
commonMocks,
petMocks,
storeMocks,
userMocks,
]
// 创建mock适配器
const mockAdapter = createAlovaMockAdapter(allMocks, {
// 使用uniapp请求适配器处理非mock请求
httpAdapter: uniappRequestAdapter,
// 使用uniapp mock响应适配器
onMockResponse: uniappMockResponse,
// 根据环境变量启用/禁用mock
enable: true,
// 添加延迟以模拟网络延迟 (200-600ms)
delay: Math.random() * 400 + 200,
// 在开发环境下打印mock请求日志
mockRequestLogger: import.meta.env.MODE === 'development' && canUseGroupLogger,
// 路径匹配模式 - 使用完整路径匹配
matchMode: 'pathname',
})
export default mockAdapter
@@ -1,30 +0,0 @@
/*
* @Author: weisheng
* @Date: 2023-05-20 10:00:00
* @LastEditTime: 2025-06-26 21:59:35
* @LastEditors: weisheng
* @Description: 通用mock处理
* @FilePath: /wot-starter/src/api/mock/modules/common.ts
*/
import { defineMock } from '@alova/mock'
import { generateMockData } from '../utils/generators'
export default defineMock({
// 通用GET请求处理
'[GET]/*': (_params: any, matchedUrl: string) => {
console.log(`[Mock] GET ${matchedUrl}`, _params)
return generateMockData.baseResponse({
message: `Mock response for GET ${matchedUrl}`,
params: _params,
})
},
// 通用POST请求处理
'[POST]/*': (_params: any, matchedUrl: string) => {
console.log(`[Mock] POST ${matchedUrl}`, _params)
return generateMockData.baseResponse({
message: `Mock response for POST ${matchedUrl}`,
params: _params,
})
},
})
-239
View File
@@ -1,239 +0,0 @@
/*
* @Author: weisheng
* @Date: 2025-06-15 14:25:00
* @LastEditTime: 2025-06-26 21:59:38
* @LastEditors: weisheng
* @Description: Pet Store - Pet相关接口的mock数据
* @FilePath: /wot-starter/src/api/mock/modules/pet.ts
*/
import { defineMock } from '@alova/mock'
import { generateMockData } from '../utils/generators'
// 宠物状态枚举
const PET_STATUS = ['available', 'pending', 'sold'] as const
type PetStatus = typeof PET_STATUS[number]
// 宠物类别
const PET_CATEGORIES = [
{ id: 1, name: 'Dogs' },
{ id: 2, name: 'Cats' },
{ id: 3, name: 'Birds' },
{ id: 4, name: 'Fish' },
{ id: 5, name: 'Reptiles' },
]
// 宠物标签
const PET_TAGS = [
{ id: 1, name: 'friendly' },
{ id: 2, name: 'playful' },
{ id: 3, name: 'calm' },
{ id: 4, name: 'energetic' },
{ id: 5, name: 'trained' },
{ id: 6, name: 'house-trained' },
]
// 生成宠物对象
function generatePet(id?: number, status?: PetStatus) {
const petId = id || generateMockData.number(1, 10000)
const category = PET_CATEGORIES[generateMockData.number(0, PET_CATEGORIES.length - 1)]
const tags = generateMockData.array(() => PET_TAGS[generateMockData.number(0, PET_TAGS.length - 1)], generateMockData.number(1, 3))
return {
id: petId,
category,
name: generateMockData.name('Pet'),
photoUrls: generateMockData.array(
index => `https://example.com/pet/${petId}/photo${index + 1}.jpg`,
generateMockData.number(1, 3),
),
tags,
status: status || PET_STATUS[generateMockData.number(0, PET_STATUS.length - 1)],
}
}
export default defineMock({
// 上传宠物图片
'[POST]/pet/{petId}/uploadImage': ({ params, data }) => {
console.log(`[Mock] POST /pet/${params.petId}/uploadImage`, data)
return {
code: 200,
type: 'success',
message: `Image uploaded successfully for pet ${params.petId}`,
data: {
petId: params.petId,
imageUrl: `https://example.com/pet/${params.petId}/uploaded-${Date.now()}.jpg`,
},
}
},
// 添加新宠物
'[POST]/pet': ({ data }) => {
console.log('[Mock] POST /pet', data)
const newPet = {
...data,
id: generateMockData.number(10001, 20000),
}
return newPet
},
// 更新宠物信息
'[PUT]/pet': ({ data }) => {
console.log('[Mock] PUT /pet', data)
if (!data.id) {
return {
status: 400,
body: {
code: 400,
message: 'Pet ID is required',
},
}
}
return {
...data,
updatedAt: generateMockData.datetime(),
}
},
// 根据状态查找宠物
'[GET]/pet/findByStatus': ({ query }) => {
console.log('[Mock] GET /pet/findByStatus', query)
const status = query.status as PetStatus
const validStatuses = Array.isArray(status) ? status : [status]
// 验证状态
const invalidStatuses = validStatuses.filter(s => !PET_STATUS.includes(s as PetStatus))
if (invalidStatuses.length > 0) {
return {
status: 400,
body: {
code: 400,
message: `Invalid status value: ${invalidStatuses.join(', ')}`,
},
}
}
// 生成符合状态的宠物列表
const pets = generateMockData.array(
index => generatePet(undefined, validStatuses[index % validStatuses.length] as PetStatus),
generateMockData.number(5, 15),
)
return pets
},
// 根据ID获取宠物
'[GET]/pet/{petId}': ({ params }) => {
console.log(`[Mock] GET /pet/${params.petId}`)
const petId = Number.parseInt(params.petId)
if (Number.isNaN(petId)) {
return {
status: 400,
body: {
code: 400,
message: 'Invalid pet ID',
},
}
}
// 模拟宠物不存在的情况
if (petId === 404) {
return {
status: 404,
body: {
code: 404,
message: 'Pet not found',
},
}
}
return generatePet(petId)
},
// 使用表单数据更新宠物
'[POST]/pet/{petId}': ({ params, data }) => {
console.log(`[Mock] POST /pet/${params.petId}`, data)
const petId = Number.parseInt(params.petId)
if (Number.isNaN(petId)) {
return {
status: 400,
body: {
code: 400,
message: 'Invalid pet ID',
},
}
}
// 模拟宠物不存在的情况
if (petId === 404) {
return {
status: 404,
body: {
code: 404,
message: 'Pet not found',
},
}
}
const updatedPet = {
...generatePet(petId),
...data,
updatedAt: generateMockData.datetime(),
}
return updatedPet
},
// 删除宠物
'[DELETE]/pet/{petId}': ({ params, headers }) => {
console.log(`[Mock] DELETE /pet/${params.petId}`, headers)
const petId = Number.parseInt(params.petId)
if (Number.isNaN(petId)) {
return {
status: 400,
body: {
code: 400,
message: 'Invalid pet ID',
},
}
}
// 检查API密钥
if (!headers.api_key) {
return {
status: 401,
body: {
code: 401,
message: 'API key is required',
},
}
}
// 模拟宠物不存在的情况
if (petId === 404) {
return {
status: 404,
body: {
code: 404,
message: 'Pet not found',
},
}
}
return {
code: 200,
message: `Pet ${petId} deleted successfully`,
}
},
}, true)
-173
View File
@@ -1,173 +0,0 @@
/*
* @Author: weisheng
* @Date: 2025-06-15 14:30:00
* @LastEditTime: 2025-06-15 14:30:00
* @LastEditors: weisheng
* @Description: Pet Store - Store相关接口的mock数据
* @FilePath: /wot-starter/src/api/mock/modules/store.ts
*/
import { defineMock } from '@alova/mock'
import { generateMockData } from '../utils/generators'
// 订单状态枚举
const ORDER_STATUS = ['placed', 'approved', 'delivered'] as const
type OrderStatus = typeof ORDER_STATUS[number]
// 生成订单对象
function generateOrder(id?: number, status?: OrderStatus) {
const orderId = id || generateMockData.number(1, 10000)
return {
id: orderId,
petId: generateMockData.number(1, 1000),
quantity: generateMockData.number(1, 10),
shipDate: generateMockData.datetime(generateMockData.number(1, 30)), // 1-30天后发货
status: status || ORDER_STATUS[generateMockData.number(0, ORDER_STATUS.length - 1)],
complete: generateMockData.boolean(),
}
}
export default defineMock({
// 获取库存
'[GET]/store/inventory': () => {
console.log('[Mock] GET /store/inventory')
// 生成随机库存数据
const inventory: Record<string, number> = {}
// 为不同状态生成库存数量
ORDER_STATUS.forEach((status) => {
inventory[status] = generateMockData.number(0, 100)
})
// 添加一些额外的状态
inventory.pending = generateMockData.number(0, 50)
inventory.sold = generateMockData.number(0, 200)
inventory.available = generateMockData.number(10, 300)
return inventory
},
// 下单购买宠物
'[POST]/store/order': ({ data }) => {
console.log('[Mock] POST /store/order', data)
// 验证必填字段
if (!data.petId) {
return {
status: 400,
body: {
code: 400,
message: 'Pet ID is required',
},
}
}
if (!data.quantity || data.quantity <= 0) {
return {
status: 400,
body: {
code: 400,
message: 'Quantity must be greater than 0',
},
}
}
// 创建新订单
const newOrder = {
id: generateMockData.number(10001, 20000),
petId: data.petId,
quantity: data.quantity,
shipDate: data.shipDate || generateMockData.datetime(generateMockData.number(1, 7)), // 默认7天内发货
status: 'placed' as OrderStatus,
complete: false,
}
return newOrder
},
// 根据ID获取订单
'[GET]/store/order/{orderId}': ({ params }) => {
console.log(`[Mock] GET /store/order/${params.orderId}`)
const orderId = Number.parseInt(params.orderId)
if (Number.isNaN(orderId)) {
return {
status: 400,
body: {
code: 400,
message: 'Invalid order ID',
},
}
}
// 模拟订单不存在的情况
if (orderId === 404) {
return {
status: 404,
body: {
code: 404,
message: 'Order not found',
},
}
}
// 模拟无效订单ID的情况(订单ID必须在1-10之间)
if (orderId < 1 || orderId > 10) {
return {
status: 400,
body: {
code: 400,
message: 'Invalid ID supplied',
},
}
}
return generateOrder(orderId)
},
// 删除订单
'[DELETE]/store/order/{orderId}': ({ params }) => {
console.log(`[Mock] DELETE /store/order/${params.orderId}`)
const orderId = Number.parseInt(params.orderId)
if (Number.isNaN(orderId)) {
return {
status: 400,
body: {
code: 400,
message: 'Invalid order ID',
},
}
}
// 模拟订单不存在的情况
if (orderId === 404) {
return {
status: 404,
body: {
code: 404,
message: 'Order not found',
},
}
}
// 模拟无效订单ID的情况
if (orderId < 1) {
return {
status: 400,
body: {
code: 400,
message: 'Invalid ID supplied',
},
}
}
return {
code: 200,
message: `Order ${orderId} deleted successfully`,
}
},
}, true)
-304
View File
@@ -1,304 +0,0 @@
/*
* @Author: weisheng
* @Date: 2025-06-15 14:35:00
* @LastEditTime: 2025-06-27 09:43:25
* @LastEditors: weisheng
* @Description: Pet Store - User相关接口的mock数据
* @FilePath: /wot-starter/src/api/mock/modules/user.ts
*/
import { defineMock } from '@alova/mock'
import { generateMockData } from '../utils/generators'
// 用户状态枚举
const USER_STATUS = [0, 1, 2] // 0: 离线, 1: 在线, 2: 忙碌
type UserStatus = typeof USER_STATUS[number]
// 生成用户对象
function generateUser(username?: string, status?: UserStatus) {
const baseUsername = username || generateMockData.name('user').toLowerCase()
return {
id: generateMockData.number(1, 10000),
username: baseUsername,
firstName: generateMockData.name('First'),
lastName: generateMockData.name('Last'),
email: `${baseUsername}@example.com`,
password: 'password123', // 在实际应用中不应该返回密码
phone: `1${generateMockData.number(1000000000, 9999999999)}`,
userStatus: CommonUtil.isDef(status) ? status : USER_STATUS[generateMockData.number(0, USER_STATUS.length - 1)],
}
}
// 模拟用户数据库
const mockUsers = generateMockData.array(index => generateUser(`user${index + 1}`), 10)
export default defineMock({
// 批量创建用户(数组输入)
'[POST]/user/createWithArray': ({ data }) => {
console.log('[Mock] POST /user/createWithArray', data)
if (!Array.isArray(data)) {
return {
status: 400,
body: {
code: 400,
message: 'Input should be an array of users',
},
}
}
// 验证每个用户对象
for (const user of data) {
if (!user.username) {
return {
status: 400,
body: {
code: 400,
message: 'Username is required for all users',
},
}
}
}
return {
code: 200,
message: `Successfully created ${data.length} users`,
}
},
// 批量创建用户(列表输入)
'[POST]/user/createWithList': ({ data }) => {
console.log('[Mock] POST /user/createWithList', data)
if (!Array.isArray(data)) {
return {
status: 400,
body: {
code: 400,
message: 'Input should be a list of users',
},
}
}
// 验证每个用户对象
for (const user of data) {
if (!user.username) {
return {
status: 400,
body: {
code: 400,
message: 'Username is required for all users',
},
}
}
}
return {
code: 200,
message: `Successfully created ${data.length} users from list`,
}
},
// 用户登录
'[GET]/user/login': ({ query }) => {
console.log('[Mock] GET /user/login', query)
const { username, password } = query
if (!username || !password) {
return {
status: 400,
body: {
code: 400,
message: 'Username and password are required',
},
}
}
// 模拟登录验证
if (username === 'admin' && password === 'admin') {
return {
code: 200,
message: 'logged in user session',
token: `mock_token_${Date.now()}`,
expiresIn: 3600, // 1小时
user: generateUser('admin', 1),
}
}
// 模拟用户名密码错误
if (username === 'invalid' || password === 'invalid') {
return {
status: 400,
body: {
code: 400,
message: 'Invalid username/password supplied',
},
}
}
// 默认成功登录
return {
code: 200,
message: 'logged in user session',
token: `mock_token_${Date.now()}`,
expiresIn: 3600,
user: generateUser(username as string, 1),
}
},
// 用户登出
'[GET]/user/logout': () => {
console.log('[Mock] GET /user/logout')
return {
code: 200,
message: 'ok',
}
},
// 根据用户名获取用户
'[GET]/user/{username}': ({ params }) => {
console.log(`[Mock] GET /user/${params.username}`)
const username = params.username
if (!username) {
return {
status: 400,
body: {
code: 400,
message: 'Username is required',
},
}
}
// 模拟用户不存在的情况
if (username === 'notfound') {
return {
status: 404,
body: {
code: 404,
message: 'User not found',
},
}
}
// 从模拟数据库中查找用户
const existingUser = mockUsers.find(user => user.username === username)
if (existingUser) {
return existingUser
}
// 生成新用户
return generateUser(username)
},
// 更新用户信息
'[PUT]/user/{username}': ({ params, data }) => {
console.log(`[Mock] PUT /user/${params.username}`, data)
const username = params.username
if (!username) {
return {
status: 400,
body: {
code: 400,
message: 'Username is required',
},
}
}
// 模拟用户不存在的情况
if (username === 'notfound') {
return {
status: 404,
body: {
code: 404,
message: 'User not found',
},
}
}
// 更新用户信息
const updatedUser = {
...generateUser(username),
...data,
username, // 确保用户名不被修改
updatedAt: generateMockData.datetime(),
}
return updatedUser
},
// 删除用户
'[DELETE]/user/{username}': ({ params }) => {
console.log(`[Mock] DELETE /user/${params.username}`)
const username = params.username
if (!username) {
return {
status: 400,
body: {
code: 400,
message: 'Username is required',
},
}
}
// 模拟用户不存在的情况
if (username === 'notfound') {
return {
status: 404,
body: {
code: 404,
message: 'User not found',
},
}
}
return {
code: 200,
message: `User ${username} deleted successfully`,
}
},
// 创建用户
'[POST]/user': ({ data }) => {
console.log('[Mock] POST /user', data)
if (!data.username) {
return {
status: 400,
body: {
code: 400,
message: 'Username is required',
},
}
}
// 检查用户名是否已存在
const existingUser = mockUsers.find(user => user.username === data.username)
if (existingUser) {
return {
status: 409,
body: {
code: 409,
message: 'Username already exists',
},
}
}
// 创建新用户
const newUser = {
...generateUser(),
...data,
id: generateMockData.number(20001, 30000),
createdAt: generateMockData.datetime(),
}
return newUser
},
}, true)
@@ -1,142 +0,0 @@
/*
* @Author: weisheng
* @Date: 2023-05-20 10:00:00
* @LastEditTime: 2025-05-21 15:46:14
* @LastEditors: weisheng
* @Description: Mock数据生成工具
* @FilePath: /lsym-cx-mini/src/api/mock/utils/generators.ts
*/
// 模拟数据生成工具函数
export const generateMockData = {
// 生成随机ID
id: (): number => Math.floor(Math.random() * 10000),
// 生成随机名称
name: (prefix = '名称'): string => `${prefix}_${Math.floor(Math.random() * 1000)}`,
// 生成随机代码
code: (prefix = 'CODE'): string => `${prefix}_${Math.floor(Math.random() * 1000)}`,
// 生成随机日期
// 可以传入天数偏移,负数表示过去的日期,正数表示未来的日期
date: (dayOffset = 0): string => {
const date = new Date()
if (dayOffset !== 0) {
date.setDate(date.getDate() + dayOffset)
}
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
},
// 生成随机时间
// 可以传入天数偏移,负数表示过去的日期,正数表示未来的日期
datetime: (dayOffset = 0): string => {
const date = new Date()
if (dayOffset !== 0) {
date.setDate(date.getDate() + dayOffset)
}
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`
},
// 生成随机布尔值
boolean: (): boolean => Math.random() > 0.5,
// 生成随机数字
number: (min = 0, max = 100): number => Math.floor(Math.random() * (max - min)) + min,
// 生成随机数组
array: <T>(generator: (index: number) => T, length = 10): T[] => {
const result: T[] = []
for (let i = 0; i < length; i++) {
result.push(generator(i))
}
return result
},
// 生成基础响应对象
baseResponse: <T>(data: T = null as unknown as T, code = 2000, msg = '操作成功') => ({
code,
data,
msg,
}),
// 生成列表响应对象
listResponse: <T>(data: T[] = [], total = data.length, more = false, code = 2000, msg = '操作成功') => ({
code,
data,
total,
more,
msg,
}),
// 生成GCN对象
gcn: (_index?: number) => ({
gid: generateMockData.id(),
code: generateMockData.code('ORG'),
name: generateMockData.name('组织'),
}),
// 生成员工对象
faEmp: (index: number) => ({
gid: generateMockData.id(),
code: generateMockData.code('EMP'),
name: generateMockData.name('员工'),
org: generateMockData.gcn(index),
}),
// 生成车销业务员对象
vehSaleEmp: (index: number) => ({
gid: generateMockData.id(),
code: generateMockData.code('VSE'),
name: generateMockData.name('车销业务员'),
faEmp: generateMockData.faEmp(index),
org: generateMockData.gcn(index),
wms: generateMockData.gcn(index),
}),
// 生成权限对象
permission: (index: number) => ({
module: `module_${index}`,
moduleName: `模块${index}`,
roleId: `role_${index}`,
state: 1,
}),
// 生成代码名称对象
codeName: (index: number, prefix = '线路') => ({
code: generateMockData.code(`LINE_${index}`),
name: `${prefix}${index}`,
}),
// 生成用户对象
user: (roleCode = '01') => ({
permissions: generateMockData.array(generateMockData.permission, 5),
sortLines: generateMockData.array(generateMockData.codeName, 3),
token: `mock_token_${Date.now()}`,
roleCode, // 添加roleCode字段:01-车销业务员,02-仓管
vehSaleEmp: generateMockData.vehSaleEmp(0),
}),
// 生成商品对象
goods: (index: number) => ({
gid: generateMockData.id(),
code: generateMockData.code('GOODS'),
name: generateMockData.name('商品'),
gdCode: generateMockData.code('GDCODE'),
spec: `规格${index}`,
munit: '个',
price: generateMockData.number(1, 1000) / 100,
qpc: generateMockData.number(1, 10),
qpcStr: `${generateMockData.number(1, 10)}个/箱`,
qty: generateMockData.number(1, 100),
qtyStr: `${generateMockData.number(1, 100)}个`,
busInvQty: generateMockData.number(1, 100),
advUseSignQty: generateMockData.number(1, 100),
version: 1,
}),
// 单据状态,可选值为:0 | 100 | 1300 | 300 | 110 | 1310
stat: (): number => {
const stats = [100, 1300, 300, 110, 1310]
return stats[generateMockData.number(0, stats.length - 1)]
},
}
@@ -1,86 +0,0 @@
/**
* 示例 API 模块(对应后端 module_example 插件)
*/
import { http } from '@/http'
const DEMO_BASE = '/example/demo'
export const DemoAPI = {
/** 示例分页列表 */
getPage(query?: DemoPageQuery): Promise<PageResult<DemoTable>> {
return http.Get(`${DEMO_BASE}/list`, query)
},
/** 示例详情 */
getDetail(id: number): Promise<DemoTable> {
return http.Get(`${DEMO_BASE}/detail/${id}`)
},
/** 创建示例 */
create(data: DemoForm): Promise<void> {
return http.Post(`${DEMO_BASE}/create`, data)
},
/** 更新示例 */
update(id: number, data: DemoForm): Promise<void> {
return http.Put(`${DEMO_BASE}/update/${id}`, data)
},
/** 删除示例 */
remove(ids: number[]): Promise<void> {
return http.Delete(`${DEMO_BASE}/delete`, { ids: JSON.stringify(ids) })
},
/** 批量设置示例状态 */
batchStatus(data: BatchType): Promise<void> {
return http.Patch(`${DEMO_BASE}/status/batch`, data)
},
/** 导出示例 */
exportDemo(query?: DemoPageQuery): Promise<unknown> {
return http.Post(`${DEMO_BASE}/export`, query)
},
/** 下载导入模板 */
downloadTemplate(): Promise<unknown> {
return http.Post(`${DEMO_BASE}/download/template`)
},
/** 导入示例数据 */
importDemo(body: Record<string, any>): Promise<void> {
return http.Post(`${DEMO_BASE}/import`, body, {
headers: { 'Content-Type': 'multipart/form-data' },
})
},
}
/* ==================== 类型定义 ==================== */
export interface DemoPageQuery extends PageQuery {
name?: string
status?: number
}
export interface DemoTable {
id?: number
name?: string
status?: number
description?: string
int_val?: number
bigint_val?: number
float_val?: number
bool_val?: boolean
date_val?: string
time_val?: string
datetime_val?: string
text_val?: string
json_val?: Record<string, any>
}
export interface DemoForm {
id?: number
name?: string
status?: number
description?: string
int_val?: number
bigint_val?: number
float_val?: number
bool_val?: boolean
date_val?: string
time_val?: Date | string
datetime_val?: Date | string
text_val?: string
json_val?: Record<string, any>
}
@@ -26,5 +26,14 @@ export interface DashboardStats {
today_login_count: number
today_unique_users: number
week_user_created: number
login_trend: LoginTrendItem[]
recent_logins: RecentLoginItem[]
}
/** 登录趋势(按天聚合,近7天) */
export interface LoginTrendItem {
day: string
logins: number
unique_users: number
new_users: number
}
@@ -1,57 +0,0 @@
import { http } from '@/http'
const MONITOR_BASE = '/monitor'
/**
* 在线用户 API
* 与 web 端 module_monitor/online.ts 对齐(补全完整字段)
*/
export const OnlineAPI = {
getPage(params?: Record<string, any>): Promise<PageResult<OnlineUserItem>> {
return http.Get(`${MONITOR_BASE}/online/list`, params)
},
getCurrent(): Promise<OnlineUserItem[]> {
return http.Get(`${MONITOR_BASE}/online/current`)
},
forceLogout(token: string): Promise<void> {
return http.Delete(`${MONITOR_BASE}/online/delete`, { token })
},
clear(): Promise<void> {
return http.Delete(`${MONITOR_BASE}/online/clear`)
},
getStats(): Promise<DashboardStats> {
return http.Get(`${MONITOR_BASE}/online/stats`)
},
}
export interface OnlineUserItem {
session_id?: string
user_id?: number
is_superuser?: boolean
user_name?: string
name?: string
ipaddr?: string
login_location?: string
login_time?: string
login_type?: string
os?: string
browser?: string
user_status?: number
}
export interface DashboardStats {
online_users: number
total_users: number
today_login_count: number
today_unique_users: number
week_user_created: number
recent_logins: RecentLoginItem[]
}
export interface RecentLoginItem {
username: string
status: number
login_time: string
login_ip?: string
login_location?: string
}
+37 -23
View File
@@ -13,45 +13,59 @@ export const ServerAPI = {
}
export interface CpuInfo {
used_percent: number
cores: number
model: string
cpu_num?: number
used?: number
sys?: number
free?: number
/** 逻辑核心数 */
cpu_num: number
/** user 占用百分比 */
used: number
/** system 占用百分比 */
sys: number
/** idle 百分比 */
free: number
}
export interface MemoryInfo {
total: string
used: string
used_percent: number
free?: string
usage?: number
free: string
usage: number
}
export interface DiskInfo {
dir_name: string
sys_type_name: string
type_name: string
total: string
used: string
used_percent: number
free: string
/** 使用率百分比 */
usage: number
}
export interface OsInfo {
export interface SysInfo {
computer_ip: string
computer_name: string
os_arch: string
os_name: string
user_dir: string
}
export interface PyInfo {
name: string
hostname: string
uptime: string
}
export interface JvmInfo {
version: string
used: string
used_percent: number
start_time: string
run_time: string
home: string
memory_total: string
memory_used: string
memory_free: string
/** 进程内存占用率百分比(rss / available) */
memory_usage: number
}
export interface ServerInfo {
cpu?: CpuInfo
memory?: MemoryInfo
disk?: DiskInfo
os?: OsInfo
jvm?: JvmInfo
mem?: MemoryInfo
sys?: SysInfo
py?: PyInfo
disks?: DiskInfo[]
}
+69 -6
View File
@@ -47,10 +47,12 @@ const AuthAPI = {
/**
* 登出
* @param body 登出请求体
* 后端 logout 接口 body 为纯字符串(JWT 原文,Annotated[str, Body]),
* 需显式 JSON.stringify 使请求体成为合法 JSON 字符串(uni.request 对字符串原样发送)
* @param token 访问令牌
*/
logout(body: LogoutBody): Promise<void> {
return http.Post(`${AUTH_BASE_URL}/logout`, body)
logout(token: string): Promise<void> {
return http.Post(`${AUTH_BASE_URL}/logout`, JSON.stringify(token))
},
/**
@@ -73,6 +75,44 @@ const AuthAPI = {
completeSliderCaptcha(data: { captcha_key: string, x: number }): Promise<{ captcha_key: string, verified: boolean }> {
return http.Post(`${AUTH_BASE_URL}/captcha/slider/complete`, data, { meta: { ignoreAuth: true } })
},
/**
* 微信小程序登录
* 前端通过 uni.login 获取 code,后端调用 code2Session 换取 openid 后返回 JWT
* @param data 微信登录数据
* @param data.code uni.login 返回的 code
* @param data.nickname 用户昵称(可选,来自 getUserProfile)
* @param data.avatar 头像 URL(可选)
* @returns JWT 登录结果
*/
wxLogin(data: WxLoginData): Promise<LoginResult> {
return http.Post(`${AUTH_BASE_URL}/wx-login`, data, { meta: { ignoreAuth: true } })
},
/**
* 微信小程序手机号快速登录
* 用户点击<button open-type="getPhoneNumber">后,回调 e.detail.code 发送给后端
* 后端通过 getuserphonenumber API 直接获取手机号(2023+ 新方案,无需 AES 解密)
* @param data 手机号登录数据
* @param data.code getPhoneNumber 回调返回的动态令牌 code
* @returns JWT 登录结果
*/
wxPhoneLogin(data: WxPhoneLoginData): Promise<LoginResult> {
return http.Post(`${AUTH_BASE_URL}/wx-phone-login`, data, { meta: { ignoreAuth: true } })
},
/**
* 生成小程序码
* 调用后端接口,后端通过微信 getWXACodeUnlimit API 生成无限制小程序码
* @param data 生成参数
* @param data.scene 场景参数(最大32字符,如 invite_123)
* @param data.page 小程序页面路径(可选,默认主页)
* @param data.width 图片宽度(px,默认 430)
* @returns 包含 base64 图片 URL 的结果
*/
generateWxQrCode(data: WxQrCodeParams): Promise<WxQrCodeResult> {
return http.Post(`${AUTH_BASE_URL}/wx-qrcode/generate`, data)
},
}
export default AuthAPI
@@ -107,7 +147,30 @@ export interface CaptchaInfo {
img_base: string
}
/** 退出登录操作 */
export interface LogoutBody {
token: string
/** 微信小程序登录数据 */
export interface WxLoginData {
code: string
nickname?: string
avatar?: string
}
/** 微信手机号登录数据(2023+ 新方案:仅传 code) */
export interface WxPhoneLoginData {
code: string
}
/** 小程序码生成参数 */
export interface WxQrCodeParams {
/** 场景值(最大32字符,如 invite_123) */
scene: string
/** 目标页面路径(不带 /,如 pages/index/index),为空则默认主页 */
page?: string
/** 宽度(px),默认 430,范围 280-1280 */
width?: number
}
/** 小程序码生成结果 */
export interface WxQrCodeResult {
/** 小程序码图片 URL(data:image/png;base64,...) */
url: string
}
@@ -1,52 +0,0 @@
import type { BatchSetStatus } from './user'
import { http } from '@/http'
const SYSTEM_BASE = '/system'
/**
* 部门管理 API
* 与 web 端 module_system/dept.ts 对齐(完整字段定义)
*/
export const DeptAPI = {
getTree(search?: DeptQuery): Promise<DeptItem[]> {
return http.Get(`${SYSTEM_BASE}/dept/tree`, search)
},
getDetail(id: number): Promise<DeptItem> {
return http.Get(`${SYSTEM_BASE}/dept/detail/${id}`)
},
create(data: DeptForm): Promise<DeptItem> {
return http.Post(`${SYSTEM_BASE}/dept/create`, data)
},
update(id: number, data: DeptForm): Promise<DeptItem> {
return http.Put(`${SYSTEM_BASE}/dept/update/${id}`, data)
},
remove(ids: number[]): Promise<void> {
return http.Delete(`${SYSTEM_BASE}/dept/delete`, { ids: JSON.stringify(ids) })
},
batchStatus(data: BatchSetStatus): Promise<void> {
return http.Patch(`${SYSTEM_BASE}/dept/status/batch`, data)
},
}
export interface DeptQuery {
name?: string
status?: string
}
export interface DeptForm extends BaseFormType {
name?: string
code: string
parent_id?: number
order?: number
leader?: string
phone?: string
email?: string
status?: number
description?: string
}
export interface DeptItem extends BaseType, DeptForm {
id: number
parent_name?: string
children?: DeptItem[]
}
-57
View File
@@ -1,57 +0,0 @@
import { http } from '@/http'
const SYSTEM_BASE = '/system'
/**
* 日志管理 API(操作日志 + 登录日志)
* 与 web 端 module_system/log.ts 对齐(完整字段定义)
*/
export const OperationLogAPI = {
getPage(params?: Record<string, any>): Promise<PageResult<OplogItem>> {
return http.Get(`${SYSTEM_BASE}/log/operation/list`, params)
},
remove(ids: number[]): Promise<void> {
return http.Delete(`${SYSTEM_BASE}/log/operation/delete`, { ids: JSON.stringify(ids) })
},
getDetail(id: number): Promise<OplogItem> {
return http.Get(`${SYSTEM_BASE}/log/operation/detail/${id}`)
},
exportLog(params?: Record<string, any>): Promise<unknown> {
return http.Post(`${SYSTEM_BASE}/log/export`, params)
},
}
export const LoginLogAPI = {
getPage(params?: Record<string, any>): Promise<PageResult<LoginLogItem>> {
return http.Get(`${SYSTEM_BASE}/log/login/list`, params)
},
getDetail(id: number): Promise<LoginLogItem> {
return http.Get(`${SYSTEM_BASE}/log/login/detail/${id}`)
},
remove(ids: number[]): Promise<void> {
return http.Delete(`${SYSTEM_BASE}/log/login/delete`, { ids: JSON.stringify(ids) })
},
}
export interface OplogItem extends BaseType {
username?: string
status?: number
description?: string
request_path?: string
request_method?: string
request_payload?: Record<string, unknown> | string
request_ip?: string
response_code?: number
response_json?: Record<string, unknown> | string
process_time?: string
}
export interface LoginLogItem extends BaseType {
username?: string
login_ip?: string
login_location?: string
request_browser?: string
request_os?: string
status?: string | number
msg?: string
}
@@ -0,0 +1,34 @@
import { http } from '@/http'
const PARAM_BASE_URL = '/system/param'
/** 系统参数项(与后端 ParamsOutSchema 对齐) */
export interface ParamsItem {
id: number
config_name: string
config_key: string
config_value: string | null
config_type: boolean
status: number
description?: string | null
created_time?: string
update_time?: string
}
/** 系统参数管理 API */
const ParamsAPI = {
/**
* 获取初始化缓存参数(系统配置列表)
*
* 免认证接口:登录页(无 token)也会调用,需标记 meta.ignoreAuth 跳过鉴权注入
*
* @returns 系统配置项列表,config_key 唯一标识
*/
getInitConfig(): Promise<ParamsItem[]> {
return http.Get(`${PARAM_BASE_URL}/info`, {
meta: { ignoreAuth: true },
})
},
}
export default ParamsAPI
@@ -1,47 +0,0 @@
import type { BatchSetStatus } from './user'
import { http } from '@/http'
const SYSTEM_BASE = '/system'
/**
* 岗位管理 API
* 与 web 端 module_system/position.ts 对齐(完整字段定义)
*/
export const PositionAPI = {
getPage(params?: Record<string, any>): Promise<PageResult<PositionItem>> {
return http.Get(`${SYSTEM_BASE}/position/list`, params)
},
getDetail(id: number): Promise<PositionItem> {
return http.Get(`${SYSTEM_BASE}/position/detail/${id}`)
},
create(data: PositionForm): Promise<PositionItem> {
return http.Post(`${SYSTEM_BASE}/position/create`, data)
},
update(id: number, data: PositionForm): Promise<PositionItem> {
return http.Put(`${SYSTEM_BASE}/position/update/${id}`, data)
},
remove(ids: number[]): Promise<void> {
return http.Delete(`${SYSTEM_BASE}/position/delete`, { ids: JSON.stringify(ids) })
},
batchStatus(data: BatchSetStatus): Promise<void> {
return http.Patch(`${SYSTEM_BASE}/position/status/batch`, data)
},
getOptions(): Promise<{ value: number, label: string }[]> {
return http.Get(`${SYSTEM_BASE}/position/options`)
},
exportPositions(params?: Record<string, any>): Promise<unknown> {
return http.Post(`${SYSTEM_BASE}/position/export`, params)
},
}
export interface PositionForm extends BaseFormType {
name?: string
code?: string
order?: number
status?: number
description?: string
}
export interface PositionItem extends BaseType, PositionForm {
id: number
}
@@ -1,80 +0,0 @@
import type { BatchSetStatus } from './user'
import { http } from '@/http'
const SYSTEM_BASE = '/system'
/**
* 角色管理 API
* 与 web 端 module_system/role.ts 对齐(完整字段定义)
*/
export const RoleAPI = {
getPage(params?: Record<string, any>): Promise<PageResult<RoleItem>> {
return http.Get(`${SYSTEM_BASE}/role/list`, params)
},
getDetail(id: number): Promise<RoleItem> {
return http.Get(`${SYSTEM_BASE}/role/detail/${id}`)
},
create(data: RoleForm): Promise<RoleItem> {
return http.Post(`${SYSTEM_BASE}/role/create`, data)
},
update(id: number, data: RoleForm): Promise<RoleItem> {
return http.Put(`${SYSTEM_BASE}/role/update/${id}`, data)
},
remove(ids: number[]): Promise<void> {
return http.Delete(`${SYSTEM_BASE}/role/delete`, { ids: JSON.stringify(ids) })
},
batchStatus(data: BatchSetStatus): Promise<void> {
return http.Patch(`${SYSTEM_BASE}/role/status/batch`, data)
},
setPermission(data: RolePermissionForm): Promise<void> {
return http.Put(`${SYSTEM_BASE}/role/permission`, data)
},
getOptions(): Promise<{ value: number, label: string }[]> {
return http.Get(`${SYSTEM_BASE}/role/options`)
},
exportRoles(params?: Record<string, any>): Promise<unknown> {
return http.Post(`${SYSTEM_BASE}/role/export`, params)
},
}
export interface RoleForm extends BaseFormType {
name?: string
code: string
order?: number
data_scope?: number
status?: number
description?: string
}
export interface RoleItem extends BaseType, RoleForm {
id: number
menus?: MenuItem[]
depts?: DeptItem[]
}
export interface RolePermissionForm {
role_ids: number[]
menu_ids: number[]
dept_ids: number[]
data_scope: number
}
/* 菜单表(角色权限回显用) */
export interface MenuItem extends BaseType {
id?: number
name?: string
type?: number
permission?: string
parent_id?: number
status?: number
description?: string
children?: MenuItem[]
}
/* 部门树(角色权限回显用) */
export interface DeptItem extends BaseType {
id?: number
name?: string
parent_id?: number
children?: DeptItem[]
}
+22 -20
View File
@@ -1,5 +1,4 @@
import { http } from '@/http'
import { ContentTypeEnum } from '@/http/tools/enum'
const USER_BASE_URL = '/system/user'
@@ -20,15 +19,13 @@ const UserAPI = {
/**
* 当前用户头像上传
*
* @param body
* @returns 上传后的文件路径
* @param body 上传参数
* @param body.filePath 本地临时文件路径(uni.chooseImage 选择结果)
* @param body.name 上传字段名,后端约定为 file
* @returns uni.uploadFile 成功回调(statusCode + data 响应体字符串,需调用方解析)
*/
uploadCurrentUserAvatar(body: Record<string, any>): Promise<UploadFileResult> {
return http.Post(`${USER_BASE_URL}/current/avatar/upload`, body, {
headers: {
'Content-Type': ContentTypeEnum.FORM_DATA,
},
})
uploadCurrentUserAvatar(body: { filePath: string, name?: string }): Promise<{ statusCode: number, data: string }> {
return http.Post(`${USER_BASE_URL}/current/avatar/upload`, body, { requestType: 'upload' })
},
/**
@@ -52,23 +49,28 @@ const UserAPI = {
},
/**
* 注册用户
* 注册用户(公开接口)
*
* @param body
* @returns 忘记密码结果
* @param body 注册参数
* @param body.username 用户名(字母开头,3-32 位)
* @param body.password 密码(6-128 位)
* @param body.name 昵称(可选)
* @returns 注册结果
*/
registerUser(body: RegisterForm): Promise<void> {
return http.Post(`${USER_BASE_URL}/register`, body)
return http.Post(`${USER_BASE_URL}/register`, body, { meta: { ignoreAuth: true } })
},
/**
* 忘记密码
* 忘记密码(公开接口)
*
* @param body
* @returns 忘记密码结果
* @param body 重置参数
* @param body.username 用户名(字母开头,3-32 位)
* @param body.new_password 新密码(6-128 位)
* @returns 重置结果
*/
forgetPassword(body: ForgetPasswordForm): Promise<void> {
return http.Post(`${USER_BASE_URL}/password/forget`, body)
return http.Post(`${USER_BASE_URL}/password/forget`, body, { meta: { ignoreAuth: true } })
},
/**
@@ -167,18 +169,17 @@ const UserAPI = {
export default UserAPI
/* 忘记密码表单 */
/* 忘记密码表单(与后端 UserForgetPasswordSchema 一致,confirmPassword 为前端校验字段不提交) */
export interface ForgetPasswordForm {
username: string
new_password: string
confirmPassword: string
}
/* 注册表单 */
export interface RegisterForm {
username: string
password: string
confirmPassword: string
name?: string
}
/* 分页查询表单 */
@@ -213,6 +214,7 @@ export interface UserForm extends BaseFormType {
gender?: number
email?: string
mobile?: string
avatar?: string
is_superuser?: boolean
status?: number
description?: string