Merge branch 'master' of https://gitaa.cn/KxGame/Kg.SeaTime
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
// 项目根入口文件
|
||||
// Nuxt4会自动注入,无需手动配置
|
||||
// 全局样式已移至 src/assets/css/style.css 并在 nuxt.config.ts 中全局引用
|
||||
@@ -35,6 +36,7 @@ onMounted(() => {
|
||||
appStore.setOnlineStatus(false)
|
||||
})
|
||||
}
|
||||
alert("main");
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
120
Web/src/composables/ApiService.ts
Normal file
120
Web/src/composables/ApiService.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { navigateTo } from "#app";
|
||||
import { RequestExtend } from "@/extends/RequestExtend";
|
||||
import { BaseConfig } from "@/config/BaseConfig";
|
||||
import type { IResultData } from "@/model/common/ResultData";
|
||||
|
||||
type HttpMethod = "get" | "post" | "put" | "delete" | "patch";
|
||||
type RequestParams = Record<string, unknown>;
|
||||
|
||||
export type HandledRedirectError = {
|
||||
handled: true;
|
||||
redirectTo: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export class ApiService {
|
||||
private static initialized = false;
|
||||
|
||||
private static request = new RequestExtend({
|
||||
baseURL: BaseConfig.BaseUrl,
|
||||
timeout: 60000
|
||||
});
|
||||
|
||||
private static isResultData(value: unknown): value is IResultData {
|
||||
return typeof value === "object" && value !== null && "code" in value && "msg" in value;
|
||||
}
|
||||
|
||||
public static isHandledRedirectError(error: unknown): error is HandledRedirectError {
|
||||
return typeof error === "object" && error !== null && "handled" in error && error.handled === true;
|
||||
}
|
||||
|
||||
private static redirectToLogin() {
|
||||
if (typeof localStorage !== "undefined") {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("userInfo");
|
||||
}
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
void navigateTo("/home", { replace: true });
|
||||
}
|
||||
}
|
||||
|
||||
private static ensureInitialized() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
RequestExtend.addRequestInterceptor({
|
||||
onFulfilled: (config) => {
|
||||
const token = typeof localStorage !== "undefined" ? localStorage.getItem("token") : "";
|
||||
|
||||
if (token) {
|
||||
config.headers = {
|
||||
...config.headers,
|
||||
Authorization: `Bearer ${token}`
|
||||
};
|
||||
}
|
||||
|
||||
config.timeout = 60000;
|
||||
return config;
|
||||
}
|
||||
});
|
||||
|
||||
RequestExtend.addResponseInterceptor({
|
||||
onFulfilled: (response) => {
|
||||
if (!this.isResultData(response.data)) {
|
||||
return response;
|
||||
}
|
||||
const result = response.data;
|
||||
if (result.code === 401) {
|
||||
console.log(result.data);
|
||||
} else if (result.code === 40101) {
|
||||
this.redirectToLogin();
|
||||
throw {
|
||||
handled: true,
|
||||
redirectTo: "/login/login",
|
||||
message: result.msg || "登录已失效"
|
||||
} satisfies HandledRedirectError;
|
||||
} else if (result.code === 500) {
|
||||
// 跳转错误页面
|
||||
} else if (result.code === 404) {
|
||||
// 跳转不存在页面
|
||||
}
|
||||
|
||||
return response;
|
||||
},
|
||||
onRejected: (error) => {
|
||||
if (error && typeof error === "object" && "status" in error) {
|
||||
// console.log("接口错误:", error);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
});
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
public static async ApiRequest<T = unknown>(
|
||||
method: HttpMethod,
|
||||
url: string,
|
||||
params: RequestParams = {}
|
||||
): Promise<IResultData<T>> {
|
||||
this.ensureInitialized();
|
||||
|
||||
switch (method) {
|
||||
case "get":
|
||||
return await this.request.get<IResultData<T>>(url, { params });
|
||||
case "post":
|
||||
return await this.request.post<IResultData<T>>(url, params);
|
||||
case "put":
|
||||
return await this.request.put<IResultData<T>>(url, params);
|
||||
case "delete":
|
||||
return await this.request.delete<IResultData<T>>(url, { params });
|
||||
case "patch":
|
||||
return await this.request.patch<IResultData<T>>(url, params);
|
||||
default:
|
||||
throw new Error(`不支持的请求方法: ${method}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Web/src/config/BaseConfig.ts
Normal file
6
Web/src/config/BaseConfig.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
统一配置中心
|
||||
*/
|
||||
export class BaseConfig {
|
||||
public static BaseUrl:string="https://localhost:7198";
|
||||
}
|
||||
@@ -1,86 +1,87 @@
|
||||
/**
|
||||
* 网络请求工具类(支持实例化)
|
||||
* 基于ofetch封装统一的请求逻辑,支持拦截器配置
|
||||
*/
|
||||
export class RequestEXTEND {
|
||||
type RequestConfig = {
|
||||
url: string
|
||||
method: string
|
||||
headers: Record<string, string>
|
||||
timeout?: number
|
||||
body?: BodyInit | null
|
||||
}
|
||||
|
||||
type ResponseWrapper<T = unknown> = {
|
||||
data: T
|
||||
status: number
|
||||
statusText: string
|
||||
headers: Headers
|
||||
}
|
||||
|
||||
type RequestInterceptor = {
|
||||
onFulfilled?: (config: RequestConfig) => RequestConfig | Promise<RequestConfig>
|
||||
onRejected?: (error: unknown) => RequestConfig | Promise<RequestConfig>
|
||||
}
|
||||
|
||||
type ResponseInterceptor = {
|
||||
onFulfilled?: (
|
||||
response: ResponseWrapper<unknown>
|
||||
) => ResponseWrapper<unknown> | Promise<ResponseWrapper<unknown>>
|
||||
onRejected?: (error: unknown) => unknown | Promise<unknown>
|
||||
}
|
||||
|
||||
export class RequestExtend {
|
||||
private baseURL: string
|
||||
private timeout: number
|
||||
private headers: Record<string, string>
|
||||
|
||||
// 静态默认配置
|
||||
private static defaultBaseURL = ''
|
||||
private static defaultTimeout = 30000
|
||||
private static defaultHeaders: Record<string, string> = {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
// 静态拦截器
|
||||
private static requestInterceptors: Array<{
|
||||
onFulfilled?: (config: any) => any
|
||||
onRejected?: (error: any) => any
|
||||
}> = []
|
||||
private static requestInterceptors: RequestInterceptor[] = []
|
||||
private static responseInterceptors: ResponseInterceptor[] = []
|
||||
|
||||
private static responseInterceptors: Array<{
|
||||
onFulfilled?: (response: any) => any
|
||||
onRejected?: (error: any) => any
|
||||
}> = []
|
||||
|
||||
/**
|
||||
* 构造函数:初始化请求配置
|
||||
* @param config 自定义请求配置(可选)
|
||||
*/
|
||||
constructor(config?: {
|
||||
baseURL?: string
|
||||
timeout?: number
|
||||
headers?: Record<string, string>
|
||||
}) {
|
||||
this.baseURL = config?.baseURL || RequestEXTEND.defaultBaseURL
|
||||
this.timeout = config?.timeout || RequestEXTEND.defaultTimeout
|
||||
this.baseURL = config?.baseURL || RequestExtend.defaultBaseURL
|
||||
this.timeout = config?.timeout || RequestExtend.defaultTimeout
|
||||
this.headers = {
|
||||
...RequestEXTEND.defaultHeaders,
|
||||
...RequestExtend.defaultHeaders,
|
||||
...config?.headers
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置静态默认配置
|
||||
*/
|
||||
static setDefaultConfig(config: {
|
||||
baseURL?: string
|
||||
timeout?: number
|
||||
headers?: Record<string, string>
|
||||
}) {
|
||||
if (config.baseURL) this.defaultBaseURL = config.baseURL
|
||||
if (config.timeout) this.defaultTimeout = config.timeout
|
||||
if (config.headers) this.defaultHeaders = { ...this.defaultHeaders, ...config.headers }
|
||||
if (config.baseURL) {
|
||||
this.defaultBaseURL = config.baseURL
|
||||
}
|
||||
|
||||
if (config.timeout) {
|
||||
this.defaultTimeout = config.timeout
|
||||
}
|
||||
|
||||
if (config.headers) {
|
||||
this.defaultHeaders = { ...this.defaultHeaders, ...config.headers }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加请求拦截器(静态方法)
|
||||
*/
|
||||
static addRequestInterceptor(interceptor: {
|
||||
onFulfilled?: (config: any) => any
|
||||
onRejected?: (error: any) => any
|
||||
}) {
|
||||
static addRequestInterceptor(interceptor: RequestInterceptor) {
|
||||
this.requestInterceptors.push(interceptor)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加响应拦截器(静态方法)
|
||||
*/
|
||||
static addResponseInterceptor(interceptor: {
|
||||
onFulfilled?: (response: any) => any
|
||||
onRejected?: (error: any) => any
|
||||
}) {
|
||||
static addResponseInterceptor(interceptor: ResponseInterceptor) {
|
||||
this.responseInterceptors.push(interceptor)
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行请求拦截器链
|
||||
*/
|
||||
private async executeRequestInterceptors(config: any): Promise<any> {
|
||||
private async executeRequestInterceptors(config: RequestConfig): Promise<RequestConfig> {
|
||||
let result = config
|
||||
for (const interceptor of RequestEXTEND.requestInterceptors) {
|
||||
|
||||
for (const interceptor of RequestExtend.requestInterceptors) {
|
||||
try {
|
||||
if (interceptor.onFulfilled) {
|
||||
result = await interceptor.onFulfilled(result)
|
||||
@@ -93,81 +94,136 @@ export class RequestEXTEND {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行响应拦截器链
|
||||
*/
|
||||
private async executeResponseInterceptors(response: any): Promise<any> {
|
||||
private async executeResponseInterceptors(
|
||||
response: ResponseWrapper<unknown>
|
||||
): Promise<ResponseWrapper<unknown>> {
|
||||
let result = response
|
||||
for (const interceptor of RequestEXTEND.responseInterceptors) {
|
||||
|
||||
for (const interceptor of RequestExtend.responseInterceptors) {
|
||||
try {
|
||||
if (interceptor.onFulfilled) {
|
||||
result = await interceptor.onFulfilled(result)
|
||||
}
|
||||
} catch (error) {
|
||||
if (interceptor.onRejected) {
|
||||
result = await interceptor.onRejected(error)
|
||||
} else {
|
||||
throw error
|
||||
const interceptedError = await interceptor.onRejected(error)
|
||||
throw interceptedError
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建请求URL
|
||||
*/
|
||||
private buildURL(url: string, params?: Record<string, any>): string {
|
||||
private async executeResponseErrorInterceptors(error: unknown): Promise<unknown> {
|
||||
let result = error
|
||||
|
||||
for (const interceptor of RequestExtend.responseInterceptors) {
|
||||
if (!interceptor.onRejected) {
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
result = await interceptor.onRejected(result)
|
||||
} catch (interceptorError) {
|
||||
result = interceptorError
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private buildURL(url: string, params?: Record<string, unknown>): string {
|
||||
let fullURL = url
|
||||
// 处理相对路径
|
||||
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
fullURL = this.baseURL + url
|
||||
}
|
||||
// 处理查询参数
|
||||
|
||||
if (params && Object.keys(params).length > 0) {
|
||||
const searchParams = new URLSearchParams()
|
||||
|
||||
for (const key in params) {
|
||||
const value = params[key]
|
||||
if (value !== undefined && value !== null) {
|
||||
searchParams.append(key, String(value))
|
||||
}
|
||||
}
|
||||
|
||||
const queryString = searchParams.toString()
|
||||
if (queryString) {
|
||||
fullURL += (fullURL.includes('?') ? '&' : '?') + queryString
|
||||
}
|
||||
}
|
||||
|
||||
return fullURL
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用请求方法
|
||||
*/
|
||||
private async request<T = any>(
|
||||
private async parseResponseBody(response: Response): Promise<unknown> {
|
||||
const contentType = response.headers.get('content-type') || ''
|
||||
|
||||
if (contentType.includes('application/json')) {
|
||||
return response.json()
|
||||
}
|
||||
|
||||
if (contentType.includes('application/octet-stream')) {
|
||||
return response.blob()
|
||||
}
|
||||
|
||||
const text = await response.text()
|
||||
if (!text) {
|
||||
return text
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(text)
|
||||
} catch {
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
private getErrorMessage(result: unknown, fallback: string): string {
|
||||
if (typeof result === 'string' && result.trim()) {
|
||||
return result
|
||||
}
|
||||
|
||||
if (result && typeof result === 'object') {
|
||||
const payload = result as { msg?: string; message?: string }
|
||||
return payload.msg || payload.message || fallback
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
|
||||
private async request<T = unknown>(
|
||||
method: string,
|
||||
url: string,
|
||||
options: {
|
||||
params?: Record<string, any>
|
||||
data?: any
|
||||
params?: Record<string, unknown>
|
||||
data?: unknown
|
||||
headers?: Record<string, string>
|
||||
} = {}
|
||||
): Promise<T> {
|
||||
const { params, data, headers } = options
|
||||
|
||||
// 构建配置
|
||||
const config: any = {
|
||||
const config: RequestConfig = {
|
||||
method: method.toUpperCase(),
|
||||
headers: {
|
||||
...this.headers,
|
||||
...headers
|
||||
},
|
||||
timeout: this.timeout
|
||||
timeout: this.timeout,
|
||||
url: params && method.toUpperCase() === 'GET'
|
||||
? this.buildURL(url, params)
|
||||
: this.buildURL(url)
|
||||
}
|
||||
|
||||
// 添加Body(GET/HEAD请求不添加body)
|
||||
if (data && method.toUpperCase() !== 'GET' && method.toUpperCase() !== 'HEAD') {
|
||||
if (data instanceof FormData) {
|
||||
config.body = data
|
||||
@@ -177,126 +233,108 @@ export class RequestEXTEND {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理查询参数(GET请求)
|
||||
if (params && method.toUpperCase() === 'GET') {
|
||||
url = this.buildURL(url, params)
|
||||
} else {
|
||||
url = this.buildURL(url, undefined)
|
||||
}
|
||||
|
||||
// 执行请求拦截器
|
||||
config.url = url
|
||||
const interceptedConfig = await this.executeRequestInterceptors(config)
|
||||
|
||||
try {
|
||||
// 发起请求
|
||||
const response = await fetch(interceptedConfig.url, {
|
||||
method: interceptedConfig.method,
|
||||
headers: interceptedConfig.headers,
|
||||
body: interceptedConfig.body,
|
||||
signal: interceptedConfig.timeout ? AbortSignal.timeout(interceptedConfig.timeout) : undefined
|
||||
signal: interceptedConfig.timeout
|
||||
? AbortSignal.timeout(interceptedConfig.timeout)
|
||||
: undefined
|
||||
})
|
||||
|
||||
// 处理响应
|
||||
let result
|
||||
const contentType = response.headers.get('content-type')
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
result = await response.json()
|
||||
} else {
|
||||
result = await response.text()
|
||||
}
|
||||
|
||||
// 包装响应
|
||||
const wrappedResponse = {
|
||||
const result = await this.parseResponseBody(response)
|
||||
const wrappedResponse: ResponseWrapper<unknown> = {
|
||||
data: result,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: response.headers
|
||||
}
|
||||
|
||||
// 执行响应拦截器
|
||||
return await this.executeResponseInterceptors(wrappedResponse)
|
||||
} catch (error: any) {
|
||||
// 执行错误拦截器
|
||||
const errorResponse = {
|
||||
message: error.message || '网络请求失败',
|
||||
code: error.code || 'NETWORK_ERROR',
|
||||
status: error.status || 0
|
||||
if (!response.ok) {
|
||||
const handledError = await this.executeResponseErrorInterceptors({
|
||||
...wrappedResponse,
|
||||
message: this.getErrorMessage(result, response.statusText || 'Request failed')
|
||||
})
|
||||
throw handledError
|
||||
}
|
||||
throw errorResponse
|
||||
|
||||
const interceptedResponse = await this.executeResponseInterceptors(wrappedResponse)
|
||||
return interceptedResponse.data as T
|
||||
} catch (error) {
|
||||
if (error && typeof error === 'object' && ('status' in error || 'code' in error)) {
|
||||
throw error
|
||||
}
|
||||
|
||||
const handledError = await this.executeResponseErrorInterceptors({
|
||||
message: error instanceof Error ? error.message : '网络请求失败',
|
||||
code:
|
||||
error && typeof error === 'object' && 'code' in error
|
||||
? (error as { code?: string }).code || 'NETWORK_ERROR'
|
||||
: 'NETWORK_ERROR',
|
||||
status:
|
||||
error && typeof error === 'object' && 'status' in error
|
||||
? Number((error as { status?: number }).status || 0)
|
||||
: 0
|
||||
})
|
||||
|
||||
throw handledError
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET请求
|
||||
*/
|
||||
async get<T = any>(url: string, options?: {
|
||||
params?: Record<string, any>
|
||||
async get<T = unknown>(url: string, options?: {
|
||||
params?: Record<string, unknown>
|
||||
headers?: Record<string, string>
|
||||
}): Promise<T> {
|
||||
return this.request<T>('GET', url, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* POST请求
|
||||
*/
|
||||
async post<T = any>(url: string, data?: any, options?: {
|
||||
params?: Record<string, any>
|
||||
async post<T = unknown>(url: string, data?: unknown, options?: {
|
||||
params?: Record<string, unknown>
|
||||
headers?: Record<string, string>
|
||||
}): Promise<T> {
|
||||
return this.request<T>('POST', url, { ...options, data })
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT请求
|
||||
*/
|
||||
async put<T = any>(url: string, data?: any, options?: {
|
||||
params?: Record<string, any>
|
||||
async put<T = unknown>(url: string, data?: unknown, options?: {
|
||||
params?: Record<string, unknown>
|
||||
headers?: Record<string, string>
|
||||
}): Promise<T> {
|
||||
return this.request<T>('PUT', url, { ...options, data })
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE请求
|
||||
*/
|
||||
async delete<T = any>(url: string, options?: {
|
||||
params?: Record<string, any>
|
||||
async delete<T = unknown>(url: string, options?: {
|
||||
params?: Record<string, unknown>
|
||||
headers?: Record<string, string>
|
||||
}): Promise<T> {
|
||||
return this.request<T>('DELETE', url, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH请求
|
||||
*/
|
||||
async patch<T = any>(url: string, data?: any, options?: {
|
||||
params?: Record<string, any>
|
||||
async patch<T = unknown>(url: string, data?: unknown, options?: {
|
||||
params?: Record<string, unknown>
|
||||
headers?: Record<string, string>
|
||||
}): Promise<T> {
|
||||
return this.request<T>('PATCH', url, { ...options, data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*/
|
||||
async upload<T = any>(url: string, file: File | FormData, options?: {
|
||||
params?: Record<string, any>
|
||||
async upload<T = unknown>(url: string, file: File | FormData, options?: {
|
||||
params?: Record<string, unknown>
|
||||
headers?: Record<string, string>
|
||||
}): Promise<T> {
|
||||
const formData = file instanceof FormData ? file : new FormData()
|
||||
|
||||
if (file instanceof File) {
|
||||
formData.append('file', file)
|
||||
}
|
||||
|
||||
return this.request<T>('POST', url, {
|
||||
...options,
|
||||
data: formData
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*/
|
||||
async download(url: string, filename?: string): Promise<void> {
|
||||
const response = await fetch(this.buildURL(url), {
|
||||
method: 'GET',
|
||||
@@ -318,39 +356,3 @@ export class RequestEXTEND {
|
||||
window.URL.revokeObjectURL(downloadURL)
|
||||
}
|
||||
}
|
||||
|
||||
// 导出默认实例
|
||||
export const request = new RequestEXTEND()
|
||||
|
||||
// 添加默认的Token拦截器(示例)
|
||||
RequestEXTEND.addRequestInterceptor({
|
||||
onFulfilled: (config) => {
|
||||
// 从localStorage获取Token
|
||||
const token = typeof localStorage !== 'undefined' ? localStorage.getItem('token') : ''
|
||||
if (token) {
|
||||
config.headers = {
|
||||
...config.headers,
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
})
|
||||
|
||||
// 添加默认的响应错误处理拦截器
|
||||
RequestEXTEND.addResponseInterceptor({
|
||||
onRejected: (error: any) => {
|
||||
if (error.status === 401) {
|
||||
// Token过期,清除登录状态
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('userInfo')
|
||||
}
|
||||
// 跳转到登录页
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/login'
|
||||
}
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
})
|
||||
@@ -1,8 +1,6 @@
|
||||
<template>
|
||||
<slot />
|
||||
<div class="footer">
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
8
Web/src/model/common/ResultData.ts
Normal file
8
Web/src/model/common/ResultData.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
export interface IResultData<T = unknown> {
|
||||
code: number
|
||||
msg: string
|
||||
data?: T
|
||||
ErrorData?: unknown
|
||||
timestamp?: string
|
||||
}
|
||||
@@ -7,6 +7,9 @@
|
||||
definePageMeta({
|
||||
layout: layout.default
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
3
Web/src/pages/home/test.vue
Normal file
3
Web/src/pages/home/test.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
ddd
|
||||
</template>
|
||||
3
Web/src/pages/home/user/index.vue
Normal file
3
Web/src/pages/home/user/index.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
index
|
||||
</template>
|
||||
3
Web/src/pages/home/user/test.vue
Normal file
3
Web/src/pages/home/user/test.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
test
|
||||
</template>
|
||||
@@ -97,5 +97,10 @@ MessageExtend.notifyList('primary', ['获取装备'])
|
||||
definePageMeta({
|
||||
layout: layout.empty
|
||||
})
|
||||
showNotify({ message: '提示' });
|
||||
// await navigateTo('/auth/login', { replace: true })
|
||||
onMounted(() => {
|
||||
req();
|
||||
alert(1);
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
/**
|
||||
* 通用API服务
|
||||
* 提供字典查询、文件上传等通用接口
|
||||
*/
|
||||
import type { IDictParams, IDictResponse, IUploadParams } from '~/types/api'
|
||||
import type { IUploadResponse, IPageResponse, IPageParams } from '~/types/common'
|
||||
import { RequestEXTEND } from '~/extends/requestEXTEND'
|
||||
|
||||
export class CommonSERVICE {
|
||||
// 私有属性:请求工具实例
|
||||
private request: RequestEXTEND
|
||||
|
||||
/**
|
||||
* 构造函数:初始化请求工具
|
||||
* @param config 自定义请求配置(可选)
|
||||
*/
|
||||
constructor(config?: { timeout?: number; headers?: Record<string, string>; baseURL?: string }) {
|
||||
this.request = new RequestEXTEND(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典查询接口
|
||||
* @param type 字典类型
|
||||
* @returns 字典项列表
|
||||
*/
|
||||
async getDict(type: string): Promise<IDictResponse['data']> {
|
||||
try {
|
||||
const response = await this.request.get<IDictResponse>('/api/common/dict', {
|
||||
params: { type }
|
||||
})
|
||||
if (response.code !== 200 && response.code !== 0) {
|
||||
throw new Error(response.message || '获取字典失败')
|
||||
}
|
||||
return response.data || []
|
||||
} catch (error) {
|
||||
console.error('获取字典接口异常:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传接口
|
||||
* @param file 要上传的文件
|
||||
* @returns 上传后的文件信息
|
||||
*/
|
||||
async upload(file: File): Promise<IUploadResponse['data']> {
|
||||
try {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const response = await this.request.post<IUploadResponse>('/api/common/upload', formData)
|
||||
if (response.code !== 200 && response.code !== 0) {
|
||||
throw new Error(response.message || '文件上传失败')
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('文件上传接口异常:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多文件上传接口
|
||||
* @param files 要上传的文件数组
|
||||
* @returns 上传后的文件信息列表
|
||||
*/
|
||||
async uploadMultiple(files: File[]): Promise<IUploadResponse['data'][]> {
|
||||
try {
|
||||
const formData = new FormData()
|
||||
files.forEach((file, index) => {
|
||||
formData.append(`files_${index}`, file)
|
||||
})
|
||||
const response = await this.request.post<{ code: number; message: string; data: IUploadResponse['data'][] }>(
|
||||
'/api/common/uploadMultiple',
|
||||
formData
|
||||
)
|
||||
if (response.code !== 200 && response.code !== 0) {
|
||||
throw new Error(response.message || '文件上传失败')
|
||||
}
|
||||
return response.data || []
|
||||
} catch (error) {
|
||||
console.error('多文件上传接口异常:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传进度(需要自行实现)
|
||||
* @param file 要上传的文件
|
||||
* @param onProgress 上传进度回调
|
||||
* @returns 上传后的文件信息
|
||||
*/
|
||||
async uploadWithProgress(
|
||||
file: File,
|
||||
onProgress: (percent: number) => void
|
||||
): Promise<IUploadResponse['data']> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest()
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
xhr.upload.addEventListener('progress', (event) => {
|
||||
if (event.lengthComputable) {
|
||||
const percent = Math.round((event.loaded / event.total) * 100)
|
||||
onProgress(percent)
|
||||
}
|
||||
})
|
||||
|
||||
xhr.addEventListener('load', () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
try {
|
||||
const response = JSON.parse(xhr.responseText)
|
||||
if (response.code === 200 || response.code === 0) {
|
||||
resolve(response.data)
|
||||
} else {
|
||||
reject(new Error(response.message || '文件上传失败'))
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
} else {
|
||||
reject(new Error('文件上传失败'))
|
||||
}
|
||||
})
|
||||
|
||||
xhr.addEventListener('error', () => {
|
||||
reject(new Error('文件上传失败'))
|
||||
})
|
||||
|
||||
xhr.open('POST', '/api/common/upload')
|
||||
xhr.send(formData)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置信息
|
||||
* @param key 配置键
|
||||
* @returns 配置值
|
||||
*/
|
||||
async getConfig(key: string): Promise<string | null> {
|
||||
try {
|
||||
const response = await this.request.get<{ code: number; message: string; data: { value: string } }>(
|
||||
'/api/common/config',
|
||||
{ params: { key } }
|
||||
)
|
||||
if (response.code === 200 || response.code === 0) {
|
||||
return response.data?.value || null
|
||||
}
|
||||
return null
|
||||
} catch (error) {
|
||||
console.error('获取配置接口异常:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用版本信息
|
||||
* @returns 版本信息
|
||||
*/
|
||||
async getVersion(): Promise<{ version: string; buildTime: string }> {
|
||||
try {
|
||||
const response = await this.request.get<{ code: number; message: string; data: { version: string; buildTime: string } }>('/api/common/version')
|
||||
if (response.code === 200 || response.code === 0) {
|
||||
return response.data || { version: '1.0.0', buildTime: '' }
|
||||
}
|
||||
return { version: '1.0.0', buildTime: '' }
|
||||
} catch (error) {
|
||||
console.error('获取版本信息接口异常:', error)
|
||||
return { version: '1.0.0', buildTime: '' }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
* @param phone 手机号
|
||||
* @param type 验证码类型(login/register/reset)
|
||||
* @returns 发送结果
|
||||
*/
|
||||
async sendSmsCode(phone: string, type: string = 'login'): Promise<boolean> {
|
||||
try {
|
||||
const response = await this.request.post<{ code: number; message: string }>('/api/common/sms/send', {
|
||||
phone,
|
||||
type
|
||||
})
|
||||
return response.code === 200 || response.code === 0
|
||||
} catch (error) {
|
||||
console.error('发送短信验证码接口异常:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证短信验证码
|
||||
* @param phone 手机号
|
||||
* @param code 验证码
|
||||
* @returns 验证结果
|
||||
*/
|
||||
async verifySmsCode(phone: string, code: string): Promise<boolean> {
|
||||
try {
|
||||
const response = await this.request.post<{ code: number; message: string }>('/api/common/sms/verify', {
|
||||
phone,
|
||||
code
|
||||
})
|
||||
return response.code === 200 || response.code === 0
|
||||
} catch (error) {
|
||||
console.error('验证短信验证码接口异常:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/**
|
||||
* 用户相关API服务
|
||||
* 提供用户登录、获取用户信息、退出登录等接口
|
||||
*/
|
||||
import type { ILoginParams, ILoginResponse, IGetUserInfoResponse, ILogoutResponse } from '~/types/api'
|
||||
import { RequestEXTEND } from '~/extends/requestEXTEND'
|
||||
|
||||
export class UserSERVICE {
|
||||
// 私有属性:请求工具实例
|
||||
private request: RequestEXTEND
|
||||
|
||||
/**
|
||||
* 构造函数:初始化请求工具
|
||||
* @param config 自定义请求配置(可选)
|
||||
*/
|
||||
constructor(config?: { timeout?: number; headers?: Record<string, string>; baseURL?: string }) {
|
||||
this.request = new RequestEXTEND(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录接口
|
||||
* @param params 登录请求参数
|
||||
* @returns 登录响应数据
|
||||
*/
|
||||
async login(params: ILoginParams): Promise<ILoginResponse['data']> {
|
||||
try {
|
||||
const response = await this.request.post<ILoginResponse>('/api/user/login', params)
|
||||
if (response.code !== 200 && response.code !== 0) {
|
||||
throw new Error(response.message || '登录失败')
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('登录接口异常:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息接口(需携带Token)
|
||||
* @param userId 用户ID(可选,默认取当前登录用户ID)
|
||||
* @returns 用户信息
|
||||
*/
|
||||
async getInfo(userId?: number): Promise<IGetUserInfoResponse['data']> {
|
||||
try {
|
||||
const response = await this.request.get<IGetUserInfoResponse>('/api/user/info', {
|
||||
params: userId ? { userId } : undefined
|
||||
})
|
||||
if (response.code !== 200 && response.code !== 0) {
|
||||
throw new Error(response.message || '获取用户信息失败')
|
||||
}
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('获取用户信息接口异常:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录接口
|
||||
* @returns 退出结果
|
||||
*/
|
||||
async logout(): Promise<boolean> {
|
||||
try {
|
||||
const response = await this.request.post<ILogoutResponse>('/api/user/logout')
|
||||
return response.code === 200 || response.code === 0
|
||||
} catch (error) {
|
||||
console.error('退出登录接口异常:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
* @param data 用户信息
|
||||
* @returns 更新结果
|
||||
*/
|
||||
async updateInfo(data: Partial<IGetUserInfoResponse['data']>): Promise<boolean> {
|
||||
try {
|
||||
const response = await this.request.put<ILogoutResponse>('/api/user/update', data)
|
||||
return response.code === 200 || response.code === 0
|
||||
} catch (error) {
|
||||
console.error('更新用户信息接口异常:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param oldPassword 旧密码
|
||||
* @param newPassword 新密码
|
||||
* @returns 修改结果
|
||||
*/
|
||||
async changePassword(oldPassword: string, newPassword: string): Promise<boolean> {
|
||||
try {
|
||||
const response = await this.request.post<ILogoutResponse>('/api/user/changePassword', {
|
||||
oldPassword,
|
||||
newPassword
|
||||
})
|
||||
return response.code === 200 || response.code === 0
|
||||
} catch (error) {
|
||||
console.error('修改密码接口异常:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/**
|
||||
* API服务相关类型定义
|
||||
*/
|
||||
|
||||
import type { IUserInfo } from './user'
|
||||
import type { ICommonResponse, IPageParams, IDictItem } from './common'
|
||||
|
||||
// 用户登录请求参数
|
||||
export type ILoginParams = {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
// 用户登录响应体
|
||||
export type ILoginResponse = ICommonResponse<{
|
||||
token: string
|
||||
userInfo: IUserInfo
|
||||
}>
|
||||
|
||||
// 获取用户信息响应体
|
||||
export type IGetUserInfoResponse = ICommonResponse<IUserInfo>
|
||||
|
||||
// 退出登录响应体
|
||||
export type ILogoutResponse = ICommonResponse
|
||||
|
||||
// 字典查询请求参数
|
||||
export type IDictParams = {
|
||||
type: string
|
||||
}
|
||||
|
||||
// 字典查询响应体
|
||||
export type IDictResponse = ICommonResponse<IDictItem[]>
|
||||
|
||||
// 文件上传请求参数
|
||||
export type IUploadParams = FormData
|
||||
|
||||
// 请求配置选项
|
||||
export interface IRequestConfig {
|
||||
timeout?: number
|
||||
headers?: Record<string, string>
|
||||
baseURL?: string
|
||||
}
|
||||
|
||||
// 请求拦截器
|
||||
export interface IRequestInterceptor {
|
||||
onFulfilled?: (config: any) => any
|
||||
onRejected?: (error: any) => any
|
||||
}
|
||||
|
||||
// 响应拦截器
|
||||
export interface IResponseInterceptor {
|
||||
onFulfilled?: (response: any) => any
|
||||
onRejected?: (error: any) => any
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/**
|
||||
* 通用类型定义
|
||||
*/
|
||||
|
||||
// 分页请求参数
|
||||
export interface IPageParams {
|
||||
page: number
|
||||
pageSize: number
|
||||
keyword?: string
|
||||
}
|
||||
|
||||
// 分页响应数据
|
||||
export interface IPageData<T> {
|
||||
list: T[]
|
||||
total: number
|
||||
page: number
|
||||
pageSize: number
|
||||
}
|
||||
|
||||
// 分页响应体
|
||||
export interface IPageResponse<T> {
|
||||
code: number
|
||||
message: string
|
||||
data: IPageData<T>
|
||||
}
|
||||
|
||||
// 通用响应体
|
||||
export interface ICommonResponse<T = any> {
|
||||
code: number
|
||||
message: string
|
||||
data?: T
|
||||
}
|
||||
|
||||
// 字典项
|
||||
export interface IDictItem {
|
||||
label: string
|
||||
value: string | number
|
||||
}
|
||||
|
||||
// 字典响应
|
||||
export interface IDictResponse {
|
||||
code: number
|
||||
message: string
|
||||
data: IDictItem[]
|
||||
}
|
||||
|
||||
// 文件上传响应
|
||||
export interface IUploadResponse {
|
||||
code: number
|
||||
message: string
|
||||
data: {
|
||||
url: string
|
||||
filename: string
|
||||
}
|
||||
}
|
||||
|
||||
// 文件信息
|
||||
export interface IFileInfo {
|
||||
name: string
|
||||
url: string
|
||||
size: number
|
||||
type: string
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* 用户相关类型定义
|
||||
*/
|
||||
|
||||
export interface IUserInfo {
|
||||
id: number
|
||||
username: string
|
||||
nickname: string
|
||||
avatar?: string
|
||||
email?: string
|
||||
phone?: string
|
||||
role?: string
|
||||
createTime?: number
|
||||
}
|
||||
|
||||
export interface ILoginParams {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface ILoginResponse {
|
||||
code: number
|
||||
message: string
|
||||
data: {
|
||||
token: string
|
||||
userInfo: IUserInfo
|
||||
}
|
||||
}
|
||||
|
||||
export interface IGetUserInfoResponse {
|
||||
code: number
|
||||
message: string
|
||||
data: IUserInfo
|
||||
}
|
||||
|
||||
export interface ILogoutResponse {
|
||||
code: number
|
||||
message: string
|
||||
}
|
||||
Reference in New Issue
Block a user