diff --git a/mayfly_go_web/src/common/Api.ts b/mayfly_go_web/src/common/Api.ts index baa03dbc..06d1436d 100644 --- a/mayfly_go_web/src/common/Api.ts +++ b/mayfly_go_web/src/common/Api.ts @@ -1,5 +1,5 @@ import request from './request'; -import { randomUuid } from './utils/string'; +import { useApiFetch } from './useRequest'; /** * 可用于各模块定义各自api请求 @@ -21,8 +21,6 @@ class Api { */ beforeHandler: Function; - static abortControllers: Map = new Map(); - constructor(url: string, method: string) { this.url = url; this.method = method; @@ -46,68 +44,38 @@ class Api { } /** - * 请求对应的该api + * 响应式使用该api + * @param params 响应式params + * @param reqOptions 其他可选值 + * @returns + */ + useApi(params: any = null, reqOptions: RequestInit = {}) { + return useApiFetch(this, params, reqOptions); + } + + /** + * fetch 请求对应的该api * @param {Object} param 请求该api的参数 */ - request(param: any = null, options: any = {}): Promise { + async request(param: any = null, options: any = {}): Promise { + const { execute, data } = this.useApi(param, options); + await execute(); + return data.value; + } + + /** + * xhr 请求对应的该api + * @param {Object} param 请求该api的参数 + */ + async xhrReq(param: any = null, options: any = {}): Promise { if (this.beforeHandler) { this.beforeHandler(param); } - return request.request(this.method, this.url, param, options); - } - - /** - * 允许取消的请求, 使用Api.cancelReq(key) 取消请求 - * @param key 用于取消该key关联的请求 - * @param {Object} param 请求该api的参数 - */ - allowCancelReq(key: string, param: any = null, options: RequestInit = {}): Promise { - let controller = Api.abortControllers.get(key); - if (!controller) { - controller = new AbortController(); - Api.abortControllers.set(key, controller); - } - options.signal = controller.signal; - - return this.request(param, options); + return request.xhrReq(this.method, this.url, param, options); } /** 静态方法 **/ - /** - * 取消请求 - * @param key 请求key - */ - static cancelReq(key: string) { - let controller = Api.abortControllers.get(key); - if (controller) { - controller.abort(); - Api.removeAbortKey(key); - } - } - - static removeAbortKey(key: string) { - if (key) { - console.log('remove abort key: ', key); - Api.abortControllers.delete(key); - } - } - - /** - * 根据旧key生成新的abort key,可能旧key未取消,造成多余无用对象 - * @param oldKey 旧key - * @returns key - */ - static genAbortKey(oldKey: string) { - if (!oldKey) { - return randomUuid(); - } - if (Api.abortControllers.get(oldKey)) { - return oldKey; - } - return randomUuid(); - } - /** * 静态工厂,返回Api对象,并设置url与method属性 * @param url url @@ -151,3 +119,8 @@ class Api { } export default Api; + +export class PageRes { + list: any[] = []; + total: number = 0; +} diff --git a/mayfly_go_web/src/common/request.ts b/mayfly_go_web/src/common/request.ts index 52e520e0..93f12a58 100755 --- a/mayfly_go_web/src/common/request.ts +++ b/mayfly_go_web/src/common/request.ts @@ -4,10 +4,12 @@ import { getClientId, getToken } from './utils/storage'; import { templateResolve } from './utils/string'; import { ElMessage } from 'element-plus'; import axios from 'axios'; +import { useApiFetch } from './useRequest'; +import Api from './Api'; export default { request, - fetchReq, + xhrReq, get, post, put, @@ -30,7 +32,7 @@ export interface Result { data?: any; } -enum ResultEnum { +export enum ResultEnum { SUCCESS = 200, ERROR = 400, PARAM_ERROR = 405, @@ -38,7 +40,7 @@ enum ResultEnum { NO_PERMISSION = 501, } -const baseUrl: string = config.baseApiUrl; +export const baseUrl: string = config.baseApiUrl; // const baseUrl: string = 'http://localhost:18888/api'; // const baseWsUrl: string = config.baseWsUrl; @@ -115,14 +117,15 @@ axiosInst.interceptors.response.use( ); /** - * 请求uri - * 该方法已处理请求结果中code != 200的message提示,如需其他错误处理(取消加载状态,重置对象状态等等),可catch继续处理 + * xhr请求url * - * @param {Object} method 请求方法(GET,POST,PUT,DELTE等) - * @param {Object} uri uri - * @param {Object} params 参数 + * @param method 请求方法 + * @param url url + * @param params 参数 + * @param options 可选 + * @returns */ -function request(method: string, url: string, params: any = null, options: any = {}): Promise { +export function xhrReq(method: string, url: string, params: any = null, options: any = {}) { if (!url) { throw new Error('请求url不能为空'); } @@ -157,6 +160,21 @@ function request(method: string, url: string, params: any = null, options: any = }); } +/** + * fetch请求url + * + * 该方法已处理请求结果中code != 200的message提示,如需其他错误处理(取消加载状态,重置对象状态等等),可catch继续处理 + * + * @param {Object} method 请求方法(GET,POST,PUT,DELTE等) + * @param {Object} uri uri + * @param {Object} params 参数 + */ +async function request(method: string, url: string, params: any = null, options: any = {}): Promise { + const { execute, data } = useApiFetch(Api.create(url, method), params, options); + await execute(); + return data.value; +} + /** * get请求uri * 该方法已处理请求结果中code != 200的message提示,如需其他错误处理(取消加载状态,重置对象状态等等),可catch继续处理 @@ -190,64 +208,6 @@ export function joinClientParams(): string { return `token=${getToken()}&clientId=${getClientId()}`; } -async function fetchReq(method: string, url: string, params: any = null, options: RequestInit = {}): Promise { - options.method = method; - - if (params) { - // post和put使用json格式传参 - if (method === 'post' || method === 'put') { - options.body = JSON.stringify(params); - } else { - const searchParam = new URLSearchParams(); - Object.keys(params).forEach((key) => { - const val = params[key]; - if (val) { - searchParam.append(key, val); - } - }); - url = `${url}?${searchParam.toString()}`; - } - } - - // Part 1: Add headers and attach auth token - const headers = new Headers(options.headers || {}); - - const token = getToken(); - if (token) { - headers.set('Authorization', token); - headers.set('ClientId', getClientId()); - } - options.headers = headers; - - try { - const res: Response = await fetch(`${baseUrl}${url}`, options); - if (!res.ok) { - throw new Error(`请求响应错误: 状态码=${res.status}`); - } - - const jsonRes = await res.json(); - // 获取请求返回结果 - const result: Result = jsonRes; - return parseResult(result); - } catch (e: any) { - const rejectPromise = Promise.reject(e); - - if (e?.name == 'AbortError') { - console.log('请求已取消'); - return rejectPromise; - } - - if (e.message) { - notifyErrorMsg(e.message); - return rejectPromise; - } - - notifyErrorMsg('网络请求错误'); - console.error(e); - return rejectPromise; - } -} - function parseResult(result: Result) { if (result.code === ResultEnum.SUCCESS) { return result.data; diff --git a/mayfly_go_web/src/common/useRequest.ts b/mayfly_go_web/src/common/useRequest.ts new file mode 100644 index 00000000..7a6b3392 --- /dev/null +++ b/mayfly_go_web/src/common/useRequest.ts @@ -0,0 +1,134 @@ +import router from '../router'; +import { getClientId, getToken } from './utils/storage'; +import { templateResolve } from './utils/string'; +import { ElMessage } from 'element-plus'; +import { createFetch } from '@vueuse/core'; +import Api from './Api'; +import { Result, ResultEnum } from './request'; +import config from './config'; +import { unref } from 'vue'; + +const baseUrl: string = config.baseApiUrl; + +const useCustomFetch = createFetch({ + baseUrl: baseUrl, + combination: 'chain', + options: { + immediate: false, + timeout: 60000, + // beforeFetch in pre-configured instance will only run when the newly spawned instance do not pass beforeFetch + async beforeFetch({ options }) { + const token = getToken(); + + const headers = new Headers(options.headers || {}); + if (token) { + headers.set('Authorization', token); + headers.set('ClientId', getClientId()); + } + options.headers = headers; + + return { options }; + }, + async afterFetch(ctx) { + const result: Result = await ctx.response.json(); + ctx.data = result; + return ctx; + }, + }, +}); + +export function useApiFetch(api: Api, params: any = null, reqOptions: RequestInit = {}) { + const uaf = useCustomFetch(api.url, { + beforeFetch({ url, options }) { + options.method = api.method; + if (!params) { + return; + } + + let paramsValue = unref(params); + if (api.beforeHandler) { + paramsValue = api.beforeHandler(paramsValue); + } + + let apiUrl = url; + // 简单判断该url是否是restful风格 + if (apiUrl.indexOf('{') != -1) { + apiUrl = templateResolve(apiUrl, paramsValue); + } + + if (paramsValue) { + const method = options.method?.toLowerCase(); + // post和put使用json格式传参 + if (method === 'post' || method === 'put') { + options.body = JSON.stringify(paramsValue); + } else { + const searchParam = new URLSearchParams(); + Object.keys(paramsValue).forEach((key) => { + const val = paramsValue[key]; + if (val) { + searchParam.append(key, val); + } + }); + apiUrl = `${apiUrl}?${searchParam.toString()}`; + } + } + + return { + url: apiUrl, + options: { + ...options, + ...reqOptions, + }, + }; + }, + }); + + return { + execute: async function () { + try { + await uaf.execute(true); + } catch (e: any) { + const rejectPromise = Promise.reject(e); + + if (e?.name == 'AbortError') { + console.log('请求已取消'); + return rejectPromise; + } + + console.error(e); + ElMessage.error('网络请求错误'); + return rejectPromise; + } + + const result: Result = uaf.data.value as any; + if (!result) { + ElMessage.error('网络请求失败'); + return Promise.reject(result); + } + + // 如果返回为成功结果,则将结果的data赋值给响应式data + if (result.code === ResultEnum.SUCCESS) { + uaf.data.value = result.data; + return; + } + + // 如果提示没有权限,则移除token,使其重新登录 + if (result.code === ResultEnum.NO_PERMISSION) { + router.push({ + path: '/401', + }); + } + + // 如果返回的code不为成功,则会返回对应的错误msg,则直接统一通知即可。忽略登录超时或没有权限的提示(直接跳转至401页面) + if (result.msg && result?.code != ResultEnum.NO_PERMISSION) { + ElMessage.error(result.msg); + uaf.error.value = new Error(result.msg); + } + + return Promise.reject(result); + }, + isFetching: uaf.isFetching, + data: uaf.data, + abort: uaf.abort, + }; +} diff --git a/mayfly_go_web/src/components/pagetable/PageTable.vue b/mayfly_go_web/src/components/pagetable/PageTable.vue index aa8729ff..2a0a5014 100644 --- a/mayfly_go_web/src/components/pagetable/PageTable.vue +++ b/mayfly_go_web/src/components/pagetable/PageTable.vue @@ -11,7 +11,7 @@
- + @@ -171,9 +171,9 @@ @size-change="handleSizeChange" style="text-align: right" layout="prev, pager, next, total, sizes, jumper" - :total="props.total" - v-model:current-page="state.pageNum" - v-model:page-size="state.pageSize" + :total="state.total" + v-model:current-page="queryForm_.pageNum" + v-model:page-size="queryForm_.pageSize" :page-sizes="pageSizes" /> @@ -182,11 +182,13 @@ diff --git a/mayfly_go_web/src/views/ops/redis/RedisEdit.vue b/mayfly_go_web/src/views/ops/redis/RedisEdit.vue index c00e1165..89e5714f 100644 --- a/mayfly_go_web/src/views/ops/redis/RedisEdit.vue +++ b/mayfly_go_web/src/views/ops/redis/RedisEdit.vue @@ -84,9 +84,9 @@ @@ -173,13 +173,15 @@ const state = reactive({ remark: '', sshTunnelMachineId: -1, }, + submitForm: {} as any, dbList: [0], pwd: '', - btnLoading: false, - testConnBtnLoading: false, }); -const { dialogVisible, tabActiveName, form, dbList, pwd, btnLoading } = toRefs(state); +const { dialogVisible, tabActiveName, form, submitForm, dbList, pwd } = toRefs(state); + +const { isFetching: testConnBtnLoading, execute: testConnExec } = redisApi.testConn.useApi(submitForm); +const { isFetching: saveBtnLoading, execute: saveRedisExec } = redisApi.saveRedis.useApi(submitForm); watch(props, async (newValue: any) => { state.dialogVisible = newValue.visible; @@ -226,38 +228,28 @@ const getReqForm = async () => { const testConn = async () => { redisForm.value.validate(async (valid: boolean) => { - if (valid) { - state.testConnBtnLoading = true; - try { - await redisApi.testConn.request(await getReqForm()); - ElMessage.success('连接成功'); - } finally { - state.testConnBtnLoading = false; - } - } else { + if (!valid) { ElMessage.error('请正确填写信息'); return false; } + + state.submitForm = await getReqForm(); + await testConnExec(); + ElMessage.success('连接成功'); }); }; const btnOk = async () => { redisForm.value.validate(async (valid: boolean) => { - if (valid) { - redisApi.saveRedis.request(await getReqForm()).then(() => { - ElMessage.success('保存成功'); - emit('val-change', state.form); - state.btnLoading = true; - setTimeout(() => { - state.btnLoading = false; - }, 1000); - - cancel(); - }); - } else { + if (!valid) { ElMessage.error('请正确填写信息'); return false; } + state.submitForm = await getReqForm(); + await saveRedisExec(); + ElMessage.success('保存成功'); + emit('val-change', state.form); + cancel(); }); }; diff --git a/mayfly_go_web/src/views/ops/redis/RedisList.vue b/mayfly_go_web/src/views/ops/redis/RedisList.vue index 1b405891..6a5002bc 100644 --- a/mayfly_go_web/src/views/ops/redis/RedisList.vue +++ b/mayfly_go_web/src/views/ops/redis/RedisList.vue @@ -2,16 +2,12 @@