111
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
|||||||
node_modules
|
node_modules
|
||||||
.nuxt
|
.nuxt
|
||||||
|
.output
|
||||||
|
.vs
|
||||||
[Bb]in
|
[Bb]in
|
||||||
[Oo]bj
|
[Oo]bj
|
||||||
[Ll]og/
|
[Ll]og/
|
||||||
|
|||||||
@@ -22,5 +22,13 @@ namespace Application.Web.Controllers.Login
|
|||||||
{
|
{
|
||||||
return PoAction.Ok(parms.name);
|
return PoAction.Ok(parms.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IPoAction> Test(string name)
|
||||||
|
{
|
||||||
|
return PoAction.Ok(name);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ export default defineNuxtConfig({
|
|||||||
'stores',
|
'stores',
|
||||||
'composables',
|
'composables',
|
||||||
'extends',
|
'extends',
|
||||||
'services'
|
'services',
|
||||||
|
"model"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
// 项目根入口文件
|
// 项目根入口文件
|
||||||
// Nuxt4会自动注入,无需手动配置
|
// Nuxt4会自动注入,无需手动配置
|
||||||
// 全局样式已移至 src/assets/css/style.css 并在 nuxt.config.ts 中全局引用
|
// 全局样式已移至 src/assets/css/style.css 并在 nuxt.config.ts 中全局引用
|
||||||
@@ -33,6 +34,7 @@ onMounted(() => {
|
|||||||
appStore.setOnlineStatus(false)
|
appStore.setOnlineStatus(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
alert("main");
|
||||||
})
|
})
|
||||||
</script>
|
</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 @@
|
|||||||
/**
|
type RequestConfig = {
|
||||||
* 网络请求工具类(支持实例化)
|
url: string
|
||||||
* 基于ofetch封装统一的请求逻辑,支持拦截器配置
|
method: string
|
||||||
*/
|
headers: Record<string, string>
|
||||||
export class RequestEXTEND {
|
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 baseURL: string
|
||||||
private timeout: number
|
private timeout: number
|
||||||
private headers: Record<string, string>
|
private headers: Record<string, string>
|
||||||
|
|
||||||
// 静态默认配置
|
|
||||||
private static defaultBaseURL = ''
|
private static defaultBaseURL = ''
|
||||||
private static defaultTimeout = 30000
|
private static defaultTimeout = 30000
|
||||||
private static defaultHeaders: Record<string, string> = {
|
private static defaultHeaders: Record<string, string> = {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 静态拦截器
|
private static requestInterceptors: RequestInterceptor[] = []
|
||||||
private static requestInterceptors: Array<{
|
private static responseInterceptors: ResponseInterceptor[] = []
|
||||||
onFulfilled?: (config: any) => any
|
|
||||||
onRejected?: (error: any) => any
|
|
||||||
}> = []
|
|
||||||
|
|
||||||
private static responseInterceptors: Array<{
|
|
||||||
onFulfilled?: (response: any) => any
|
|
||||||
onRejected?: (error: any) => any
|
|
||||||
}> = []
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造函数:初始化请求配置
|
|
||||||
* @param config 自定义请求配置(可选)
|
|
||||||
*/
|
|
||||||
constructor(config?: {
|
constructor(config?: {
|
||||||
baseURL?: string
|
baseURL?: string
|
||||||
timeout?: number
|
timeout?: number
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
}) {
|
}) {
|
||||||
this.baseURL = config?.baseURL || RequestEXTEND.defaultBaseURL
|
this.baseURL = config?.baseURL || RequestExtend.defaultBaseURL
|
||||||
this.timeout = config?.timeout || RequestEXTEND.defaultTimeout
|
this.timeout = config?.timeout || RequestExtend.defaultTimeout
|
||||||
this.headers = {
|
this.headers = {
|
||||||
...RequestEXTEND.defaultHeaders,
|
...RequestExtend.defaultHeaders,
|
||||||
...config?.headers
|
...config?.headers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置静态默认配置
|
|
||||||
*/
|
|
||||||
static setDefaultConfig(config: {
|
static setDefaultConfig(config: {
|
||||||
baseURL?: string
|
baseURL?: string
|
||||||
timeout?: number
|
timeout?: number
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
}) {
|
}) {
|
||||||
if (config.baseURL) this.defaultBaseURL = config.baseURL
|
if (config.baseURL) {
|
||||||
if (config.timeout) this.defaultTimeout = config.timeout
|
this.defaultBaseURL = config.baseURL
|
||||||
if (config.headers) this.defaultHeaders = { ...this.defaultHeaders, ...config.headers }
|
}
|
||||||
|
|
||||||
|
if (config.timeout) {
|
||||||
|
this.defaultTimeout = config.timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.headers) {
|
||||||
|
this.defaultHeaders = { ...this.defaultHeaders, ...config.headers }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static addRequestInterceptor(interceptor: RequestInterceptor) {
|
||||||
* 添加请求拦截器(静态方法)
|
|
||||||
*/
|
|
||||||
static addRequestInterceptor(interceptor: {
|
|
||||||
onFulfilled?: (config: any) => any
|
|
||||||
onRejected?: (error: any) => any
|
|
||||||
}) {
|
|
||||||
this.requestInterceptors.push(interceptor)
|
this.requestInterceptors.push(interceptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static addResponseInterceptor(interceptor: ResponseInterceptor) {
|
||||||
* 添加响应拦截器(静态方法)
|
|
||||||
*/
|
|
||||||
static addResponseInterceptor(interceptor: {
|
|
||||||
onFulfilled?: (response: any) => any
|
|
||||||
onRejected?: (error: any) => any
|
|
||||||
}) {
|
|
||||||
this.responseInterceptors.push(interceptor)
|
this.responseInterceptors.push(interceptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private async executeRequestInterceptors(config: RequestConfig): Promise<RequestConfig> {
|
||||||
* 执行请求拦截器链
|
|
||||||
*/
|
|
||||||
private async executeRequestInterceptors(config: any): Promise<any> {
|
|
||||||
let result = config
|
let result = config
|
||||||
for (const interceptor of RequestEXTEND.requestInterceptors) {
|
|
||||||
|
for (const interceptor of RequestExtend.requestInterceptors) {
|
||||||
try {
|
try {
|
||||||
if (interceptor.onFulfilled) {
|
if (interceptor.onFulfilled) {
|
||||||
result = await interceptor.onFulfilled(result)
|
result = await interceptor.onFulfilled(result)
|
||||||
@@ -93,81 +94,136 @@ export class RequestEXTEND {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private async executeResponseInterceptors(
|
||||||
* 执行响应拦截器链
|
response: ResponseWrapper<unknown>
|
||||||
*/
|
): Promise<ResponseWrapper<unknown>> {
|
||||||
private async executeResponseInterceptors(response: any): Promise<any> {
|
|
||||||
let result = response
|
let result = response
|
||||||
for (const interceptor of RequestEXTEND.responseInterceptors) {
|
|
||||||
|
for (const interceptor of RequestExtend.responseInterceptors) {
|
||||||
try {
|
try {
|
||||||
if (interceptor.onFulfilled) {
|
if (interceptor.onFulfilled) {
|
||||||
result = await interceptor.onFulfilled(result)
|
result = await interceptor.onFulfilled(result)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (interceptor.onRejected) {
|
if (interceptor.onRejected) {
|
||||||
result = await interceptor.onRejected(error)
|
const interceptedError = await interceptor.onRejected(error)
|
||||||
} else {
|
throw interceptedError
|
||||||
throw error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private async executeResponseErrorInterceptors(error: unknown): Promise<unknown> {
|
||||||
* 构建请求URL
|
let result = error
|
||||||
*/
|
|
||||||
private buildURL(url: string, params?: Record<string, any>): string {
|
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
|
let fullURL = url
|
||||||
// 处理相对路径
|
|
||||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||||
fullURL = this.baseURL + url
|
fullURL = this.baseURL + url
|
||||||
}
|
}
|
||||||
// 处理查询参数
|
|
||||||
if (params && Object.keys(params).length > 0) {
|
if (params && Object.keys(params).length > 0) {
|
||||||
const searchParams = new URLSearchParams()
|
const searchParams = new URLSearchParams()
|
||||||
|
|
||||||
for (const key in params) {
|
for (const key in params) {
|
||||||
const value = params[key]
|
const value = params[key]
|
||||||
if (value !== undefined && value !== null) {
|
if (value !== undefined && value !== null) {
|
||||||
searchParams.append(key, String(value))
|
searchParams.append(key, String(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryString = searchParams.toString()
|
const queryString = searchParams.toString()
|
||||||
if (queryString) {
|
if (queryString) {
|
||||||
fullURL += (fullURL.includes('?') ? '&' : '?') + queryString
|
fullURL += (fullURL.includes('?') ? '&' : '?') + queryString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fullURL
|
return fullURL
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private async parseResponseBody(response: Response): Promise<unknown> {
|
||||||
* 通用请求方法
|
const contentType = response.headers.get('content-type') || ''
|
||||||
*/
|
|
||||||
private async request<T = any>(
|
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,
|
method: string,
|
||||||
url: string,
|
url: string,
|
||||||
options: {
|
options: {
|
||||||
params?: Record<string, any>
|
params?: Record<string, unknown>
|
||||||
data?: any
|
data?: unknown
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
} = {}
|
} = {}
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const { params, data, headers } = options
|
const { params, data, headers } = options
|
||||||
|
|
||||||
// 构建配置
|
const config: RequestConfig = {
|
||||||
const config: any = {
|
|
||||||
method: method.toUpperCase(),
|
method: method.toUpperCase(),
|
||||||
headers: {
|
headers: {
|
||||||
...this.headers,
|
...this.headers,
|
||||||
...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 && method.toUpperCase() !== 'GET' && method.toUpperCase() !== 'HEAD') {
|
||||||
if (data instanceof FormData) {
|
if (data instanceof FormData) {
|
||||||
config.body = data
|
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)
|
const interceptedConfig = await this.executeRequestInterceptors(config)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 发起请求
|
|
||||||
const response = await fetch(interceptedConfig.url, {
|
const response = await fetch(interceptedConfig.url, {
|
||||||
method: interceptedConfig.method,
|
method: interceptedConfig.method,
|
||||||
headers: interceptedConfig.headers,
|
headers: interceptedConfig.headers,
|
||||||
body: interceptedConfig.body,
|
body: interceptedConfig.body,
|
||||||
signal: interceptedConfig.timeout ? AbortSignal.timeout(interceptedConfig.timeout) : undefined
|
signal: interceptedConfig.timeout
|
||||||
|
? AbortSignal.timeout(interceptedConfig.timeout)
|
||||||
|
: undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
// 处理响应
|
const result = await this.parseResponseBody(response)
|
||||||
let result
|
const wrappedResponse: ResponseWrapper<unknown> = {
|
||||||
const contentType = response.headers.get('content-type')
|
|
||||||
if (contentType && contentType.includes('application/json')) {
|
|
||||||
result = await response.json()
|
|
||||||
} else {
|
|
||||||
result = await response.text()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 包装响应
|
|
||||||
const wrappedResponse = {
|
|
||||||
data: result,
|
data: result,
|
||||||
status: response.status,
|
status: response.status,
|
||||||
statusText: response.statusText,
|
statusText: response.statusText,
|
||||||
headers: response.headers
|
headers: response.headers
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行响应拦截器
|
if (!response.ok) {
|
||||||
return await this.executeResponseInterceptors(wrappedResponse)
|
const handledError = await this.executeResponseErrorInterceptors({
|
||||||
} catch (error: any) {
|
...wrappedResponse,
|
||||||
// 执行错误拦截器
|
message: this.getErrorMessage(result, response.statusText || 'Request failed')
|
||||||
const errorResponse = {
|
})
|
||||||
message: error.message || '网络请求失败',
|
throw handledError
|
||||||
code: error.code || 'NETWORK_ERROR',
|
|
||||||
status: error.status || 0
|
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async get<T = unknown>(url: string, options?: {
|
||||||
* GET请求
|
params?: Record<string, unknown>
|
||||||
*/
|
|
||||||
async get<T = any>(url: string, options?: {
|
|
||||||
params?: Record<string, any>
|
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
}): Promise<T> {
|
}): Promise<T> {
|
||||||
return this.request<T>('GET', url, options)
|
return this.request<T>('GET', url, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async post<T = unknown>(url: string, data?: unknown, options?: {
|
||||||
* POST请求
|
params?: Record<string, unknown>
|
||||||
*/
|
|
||||||
async post<T = any>(url: string, data?: any, options?: {
|
|
||||||
params?: Record<string, any>
|
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
}): Promise<T> {
|
}): Promise<T> {
|
||||||
return this.request<T>('POST', url, { ...options, data })
|
return this.request<T>('POST', url, { ...options, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async put<T = unknown>(url: string, data?: unknown, options?: {
|
||||||
* PUT请求
|
params?: Record<string, unknown>
|
||||||
*/
|
|
||||||
async put<T = any>(url: string, data?: any, options?: {
|
|
||||||
params?: Record<string, any>
|
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
}): Promise<T> {
|
}): Promise<T> {
|
||||||
return this.request<T>('PUT', url, { ...options, data })
|
return this.request<T>('PUT', url, { ...options, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async delete<T = unknown>(url: string, options?: {
|
||||||
* DELETE请求
|
params?: Record<string, unknown>
|
||||||
*/
|
|
||||||
async delete<T = any>(url: string, options?: {
|
|
||||||
params?: Record<string, any>
|
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
}): Promise<T> {
|
}): Promise<T> {
|
||||||
return this.request<T>('DELETE', url, options)
|
return this.request<T>('DELETE', url, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async patch<T = unknown>(url: string, data?: unknown, options?: {
|
||||||
* PATCH请求
|
params?: Record<string, unknown>
|
||||||
*/
|
|
||||||
async patch<T = any>(url: string, data?: any, options?: {
|
|
||||||
params?: Record<string, any>
|
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
}): Promise<T> {
|
}): Promise<T> {
|
||||||
return this.request<T>('PATCH', url, { ...options, data })
|
return this.request<T>('PATCH', url, { ...options, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async upload<T = unknown>(url: string, file: File | FormData, options?: {
|
||||||
* 文件上传
|
params?: Record<string, unknown>
|
||||||
*/
|
|
||||||
async upload<T = any>(url: string, file: File | FormData, options?: {
|
|
||||||
params?: Record<string, any>
|
|
||||||
headers?: Record<string, string>
|
headers?: Record<string, string>
|
||||||
}): Promise<T> {
|
}): Promise<T> {
|
||||||
const formData = file instanceof FormData ? file : new FormData()
|
const formData = file instanceof FormData ? file : new FormData()
|
||||||
|
|
||||||
if (file instanceof File) {
|
if (file instanceof File) {
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.request<T>('POST', url, {
|
return this.request<T>('POST', url, {
|
||||||
...options,
|
...options,
|
||||||
data: formData
|
data: formData
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件下载
|
|
||||||
*/
|
|
||||||
async download(url: string, filename?: string): Promise<void> {
|
async download(url: string, filename?: string): Promise<void> {
|
||||||
const response = await fetch(this.buildURL(url), {
|
const response = await fetch(this.buildURL(url), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@@ -318,39 +356,3 @@ export class RequestEXTEND {
|
|||||||
window.URL.revokeObjectURL(downloadURL)
|
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>
|
<template>
|
||||||
<slot />
|
<slot />
|
||||||
<div class="footer">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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({
|
definePageMeta({
|
||||||
layout: layout.default
|
layout: layout.default
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<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>
|
||||||
@@ -3,79 +3,88 @@
|
|||||||
<img src="http://gree.pccsh.com/images/site/logo.png" class="logo" /><br />
|
<img src="http://gree.pccsh.com/images/site/logo.png" class="logo" /><br />
|
||||||
【驰骋四海·社区版】
|
【驰骋四海·社区版】
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<Abar url="/home" _class="test">11111</Abar>
|
||||||
|
✧当前在线<strong> 139 </strong>位玩家在驰骋四海✧
|
||||||
|
</div>
|
||||||
|
<div class="content" style="font-size:17px">
|
||||||
<div>
|
<div>
|
||||||
|
亲爱的 <strong style="color:red">探玩玩家</strong>,欢迎来到驰骋四海·社区版!
|
||||||
✧当前在线<strong> 139 </strong>位玩家在驰骋四海✧
|
|
||||||
</div>
|
</div>
|
||||||
<div class="content" style="font-size:17px">
|
<div style="margin-top:5px;">
|
||||||
<div>
|
<div>
|
||||||
亲爱的 <strong style="color:red">探玩玩家</strong>,欢迎来到驰骋四海·社区版!
|
➢<a href="http://m.twbar.cn/Home/Index?sid=KrWxKypJuDO0zFKrTig0bG">返回探玩驿站</a>
|
||||||
</div>
|
|
||||||
<div style="margin-top:5px;">
|
|
||||||
<div>
|
|
||||||
➢<a href="http://m.twbar.cn/Home/Index?sid=KrWxKypJuDO0zFKrTig0bG">返回探玩驿站</a>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
➸<a href="http://m.twbar.cn/b/1145?sid=KrWxKypJuDO0zFKrTig0bG">游戏论坛</a> ➸<a class="" href="/Login/LoginOut?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">退出游戏</a>
|
➸<a href="http://m.twbar.cn/b/1145?sid=KrWxKypJuDO0zFKrTig0bG">游戏论坛</a> ➸<a class=""
|
||||||
</div>
|
href="/Login/LoginOut?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">退出游戏</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="common">
|
</div>
|
||||||
<div class="title">
|
<div class="common">
|
||||||
=====☆<a class="" href="/Pallet/GameOpen/GameUser?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">我的区服</a>☆=====
|
<div class="title">
|
||||||
</div>
|
=====☆<a class="" href="/Pallet/GameOpen/GameUser?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">我的区服</a>☆=====
|
||||||
<div class="content">
|
</div>
|
||||||
<div class="item">
|
<div class="content">
|
||||||
<a href="/LoginGame/LoginOk?sid=W6Wg8iH9gY7wIBNSEdtFcQ3KbI5YiKDo">✧【1区】新手村✰村长(男)</a>
|
<div class="item">
|
||||||
</div>
|
<a
|
||||||
|
href="/LoginGame/LoginOk?sid=W6Wg8iH9gY7wIBNSEdtFcQ3KbI5YiKDo">✧【1区】新手村✰村长(男)</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="common">
|
</div>
|
||||||
<div class="title">
|
<div class="common">
|
||||||
=====☆其他区服☆=====
|
<div class="title">
|
||||||
</div>
|
=====☆其他区服☆=====
|
||||||
<div class="content">
|
|
||||||
<span>暂无区服.</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span>暂无区服.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="title">
|
<div class="title">
|
||||||
=====☆<a class="" href="/Pallet/Notice/Index?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">官方公告</a>☆=====
|
=====☆<a class="" href="/Pallet/Notice/Index?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">官方公告</a>☆=====
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
1.
|
1.
|
||||||
<a class="" href="/Pallet/Notice/Detail?nt=2026041901&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">[招募令]航海时代2设计专员招募</a>
|
<a class=""
|
||||||
</div>
|
href="/Pallet/Notice/Detail?nt=2026041901&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">[招募令]航海时代2设计专员招募</a>
|
||||||
<div class="item">
|
</div>
|
||||||
2.
|
<div class="item">
|
||||||
<a class="" href="/Pallet/Notice/Detail?nt=2026041701&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">[活动]4月份活动集锦</a>
|
2.
|
||||||
</div>
|
<a class=""
|
||||||
<div class="item">
|
href="/Pallet/Notice/Detail?nt=2026041701&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">[活动]4月份活动集锦</a>
|
||||||
3.
|
</div>
|
||||||
<a class="" href="/Pallet/Notice/Detail?nt=2026040901&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">[推广] 4月份推广</a>
|
<div class="item">
|
||||||
</div>
|
3.
|
||||||
<div class="item">
|
<a class="" href="/Pallet/Notice/Detail?nt=2026040901&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">[推广]
|
||||||
4.
|
4月份推广</a>
|
||||||
<a class="" href="/Pallet/Notice/Detail?nt=2026030101&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">[推广] 3月份推广</a>
|
</div>
|
||||||
</div>
|
<div class="item">
|
||||||
<div class="item">
|
4.
|
||||||
5.
|
<a class="" href="/Pallet/Notice/Detail?nt=2026030101&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">[推广]
|
||||||
<a class="" href="/Pallet/Notice/Detail?nt=2025080002&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">【驰骋四海】卡片の攻略(2.24)</a>
|
3月份推广</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
5.
|
||||||
|
<a class=""
|
||||||
|
href="/Pallet/Notice/Detail?nt=2025080002&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">【驰骋四海】卡片の攻略(2.24)</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
=====☆服务导航☆=====
|
=====☆服务导航☆=====
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<a class="" href="/Index/Kefu?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">客服</a>.<a class="" href="/Index/About?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">关于</a>.<a class="" href="/Index/Cooperation?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">合作</a>
|
<a class="" href="/Index/Kefu?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">客服</a>.<a class=""
|
||||||
|
href="/Index/About?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">关于</a>.<a class=""
|
||||||
|
href="/Index/Cooperation?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">合作</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
切换线路:
|
切换线路:
|
||||||
<span class="game_line">
|
<span class="game_line">
|
||||||
<a class="" href="http://g.pccsh.com:5016/Index/Index?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">1</a>
|
<a class="" href="http://g.pccsh.com:5016/Index/Index?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">1</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="foot">
|
<div class="foot">
|
||||||
<div class="timeService">
|
<div class="timeService">
|
||||||
@@ -86,8 +95,22 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: layout.empty
|
layout: layout.empty
|
||||||
})
|
})
|
||||||
showNotify({ message: '提示' });
|
|
||||||
|
const req = (async () => {
|
||||||
|
try {
|
||||||
|
const test = await ApiService.ApiRequest("get", "/Login/Test", {name:"putoo",age:30});
|
||||||
|
console.log(test.data);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//showNotify({ message: '提示' });
|
||||||
// await navigateTo('/auth/login', { replace: true })
|
// await navigateTo('/auth/login', { replace: true })
|
||||||
|
onMounted(() => {
|
||||||
|
req();
|
||||||
|
alert(1);
|
||||||
|
})
|
||||||
</script>
|
</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