diff --git a/src/service/api/ai.ts b/src/service/api/ai.ts index 6079e7e..1eb4d59 100644 --- a/src/service/api/ai.ts +++ b/src/service/api/ai.ts @@ -8,6 +8,7 @@ */ /** ai-server 统一响应 */ +import { request } from '../request/ai'; interface AiResp { code: number; message: string; @@ -16,9 +17,7 @@ interface AiResp { export class AiError extends Error { code: number; - data: any; - constructor(code: number, message: string, data?: any) { super(message || `AI 服务错误(${code})`); this.code = code; @@ -182,12 +181,24 @@ export interface RecognizeResult { materials_suggest: string[]; } -export const aiRecognize = (content: string, deviceTags: string[] = []) => - aiFetch('/open/v1/moc/change/recognize', { - request_id: nextRequestId('rec'), - content, - device_tags: deviceTags +export function aiRecognize(content: string, deviceTags: string[] = []){ + return request({ + url: '/open/v1/moc/change/recognize', + method: 'get', + params: { + request_id: nextRequestId('rec'), + content, + device_tags: deviceTags + } }); +} + // aiFetch('/open/v1/moc/change/recognize', { + // request_id: nextRequestId('rec'), + // content, + // device_tags: deviceTags + // }); + + export interface ApplicationResult { name: string; diff --git a/src/service/api/change.ts b/src/service/api/change.ts new file mode 100644 index 0000000..aef4282 --- /dev/null +++ b/src/service/api/change.ts @@ -0,0 +1,68 @@ +import { request } from '../request'; + +/** 变更预识别查询 + * + */ +export function precheckApi(id: number) { + return request({ + url: `/api/changes/${id}/precheck`, + method: 'get', + }); +} +/** + * 变更预识别保存 + * + */ +export function precheckSaveApi(params: { + change_id: number, + title: string, + description: string, + severity: string, + probability: string, + protection: string, + compare_rows: {item: string, before_text: string, after_text: string}[] +}) { + return request({ + url: '/api/changes/precheck', + method: 'post', + data: params + }); +} + +/** 变更申请表查询 + * + */ +export function applicationApi(id: number) { + return request({ + url: `/api/changes/${id}/apply-form`, + method: 'get', + }); +} +/** + * 变更申请表保存 + * + */ +export function applicationSaveApi(id: number, params: { + change_type: number, + change_level: number, + duration_type: number, + restore_deadline: string, + org_id: number, + urgent: number, + purposes: string[], + effect: string, + main_changes: string[], + related_changes: string[], + materials: string, + update_docs: string[], + disciplines: string[], + manage_dept: string, + plan_use_date: string, + risk_tools: string[], +}) { + return request({ + url: `/api/changes/${id}/apply-form`, + method: 'post', + data: params + }); +} diff --git a/src/service/request/ai.ts b/src/service/request/ai.ts new file mode 100644 index 0000000..e6cb89a --- /dev/null +++ b/src/service/request/ai.ts @@ -0,0 +1,124 @@ +import type { AxiosResponse } from 'axios'; +import { BACKEND_ERROR_CODE, createFlatRequest } from '@sa/axios'; +import { useAuthStore } from '@/store/modules/auth'; +import { getServiceAiURL } from '@/utils/service'; +import { $t } from '@/locales'; +import { getAiAuthorization, handleExpiredRequest, showErrorMsg } from './shared'; +import type { RequestInstanceState } from './type'; + +const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y'; +const { baseURL } = getServiceAiURL(import.meta.env, isHttpProxy); + +export const request = createFlatRequest( + { + baseURL, + headers: {} + }, + { + defaultState: { + errMsgStack: [], + refreshTokenPromise: null + } as RequestInstanceState, + transform(response: AxiosResponse>) { + return response.data.data; + }, + async onRequest(config) { + const Authorization = getAiAuthorization(); + Object.assign(config.headers, { Authorization }); + + return config; + }, + isBackendSuccess(response) { + // when the backend response code is "0000"(default), it means the request is success + // to change this logic by yourself, you can modify the `VITE_SERVICE_SUCCESS_CODE` in `.env` file + return String(response.data.code) === import.meta.env.VITE_SERVICE_SUCCESS_CODE; + }, + async onBackendFail(response, instance) { + const authStore = useAuthStore(); + const responseCode = String(response.data.code); + + function handleLogout() { + authStore.resetStore(); + } + + function logoutAndCleanup() { + handleLogout(); + window.removeEventListener('beforeunload', handleLogout); + + request.state.errMsgStack = request.state.errMsgStack.filter(msg => msg !== response.data.msg); + } + + // when the backend response code is in `logoutCodes`, it means the user will be logged out and redirected to login page + const logoutCodes = import.meta.env.VITE_SERVICE_LOGOUT_CODES?.split(',') || []; + if (logoutCodes.includes(responseCode)) { + handleLogout(); + return null; + } + + // when the backend response code is in `modalLogoutCodes`, it means the user will be logged out by displaying a modal + const modalLogoutCodes = import.meta.env.VITE_SERVICE_MODAL_LOGOUT_CODES?.split(',') || []; + if (modalLogoutCodes.includes(responseCode) && !request.state.errMsgStack?.includes(response.data.msg)) { + request.state.errMsgStack = [...(request.state.errMsgStack || []), response.data.msg]; + + // prevent the user from refreshing the page + window.addEventListener('beforeunload', handleLogout); + + window.$dialog?.error({ + title: $t('common.error'), + content: response.data.msg, + positiveText: $t('common.confirm'), + maskClosable: false, + closeOnEsc: false, + onPositiveClick() { + logoutAndCleanup(); + }, + onClose() { + logoutAndCleanup(); + } + }); + + return null; + } + + // when the backend response code is in `expiredTokenCodes`, it means the token is expired, and refresh token + // the api `refreshToken` can not return error code in `expiredTokenCodes`, otherwise it will be a dead loop, should return `logoutCodes` or `modalLogoutCodes` + const expiredTokenCodes = import.meta.env.VITE_SERVICE_EXPIRED_TOKEN_CODES?.split(',') || []; + if (expiredTokenCodes.includes(responseCode)) { + const success = await handleExpiredRequest(request.state); + if (success) { + const Authorization = getAuthorization(); + Object.assign(response.config.headers, { Authorization }); + + return instance.request(response.config) as Promise; + } + } + + return null; + }, + onError(error) { + // when the request is fail, you can show error message + let message = error.message; + let backendErrorCode = ''; + + // get backend error message and code + if (error.code === BACKEND_ERROR_CODE) { + message = error.response?.data?.message || message; + backendErrorCode = String(error.response?.data?.code || ''); + } + + // the error message is displayed in the modal + const modalLogoutCodes = import.meta.env.VITE_SERVICE_MODAL_LOGOUT_CODES?.split(',') || []; + if (modalLogoutCodes.includes(backendErrorCode)) { + return; + } + + // when the token is expired, refresh token and retry request, so no need to show error message + const expiredTokenCodes = import.meta.env.VITE_SERVICE_EXPIRED_TOKEN_CODES?.split(',') || []; + if (expiredTokenCodes.includes(backendErrorCode)) { + return; + } + + showErrorMsg(request.state, message); + } + } +); diff --git a/src/service/request/index.ts b/src/service/request/index.ts index 949e00e..17226f6 100644 --- a/src/service/request/index.ts +++ b/src/service/request/index.ts @@ -12,9 +12,7 @@ const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy); export const request = createFlatRequest( { baseURL, - headers: { - apifoxToken: 'XL299LiMEDZ0H5h3A29PxwQXdMJqWyY2' - } + headers: {} }, { defaultState: { diff --git a/src/service/request/shared.ts b/src/service/request/shared.ts index 9d773b6..4aa459f 100644 --- a/src/service/request/shared.ts +++ b/src/service/request/shared.ts @@ -3,12 +3,20 @@ import { localStg } from '@/utils/storage'; import { fetchRefreshToken } from '../api'; import type { RequestInstanceState } from './type'; +// 获取token export function getAuthorization() { const token = localStg.get('token'); const Authorization = token ? `Bearer ${token}` : null; return Authorization; } +// 获取ai token +export function getAiAuthorization() { + const token = localStg.get('aiToken'); + const Authorization = token ? `Bearer ${token}` : null; + + return Authorization; +} /** refresh token */ async function handleRefreshToken() { diff --git a/src/utils/service.ts b/src/utils/service.ts index e386cd4..f43d734 100644 --- a/src/utils/service.ts +++ b/src/utils/service.ts @@ -45,6 +45,46 @@ export function createServiceConfig(env: Env.ImportMeta) { return config; } +let aiUrl = ''; +export function createAiServiceConfig(env: Env.ImportMeta) { + const { VITE_SERVICE_BASE_URL, VITE_OTHER_SERVICE_BASE_URL } = env; + + let other = {} as Record; + try { + other = json5.parse(VITE_OTHER_SERVICE_BASE_URL); + } catch { + // eslint-disable-next-line no-console + console.error('VITE_OTHER_SERVICE_BASE_URL is not a valid json5 string'); + } + if(env.DEV){ + aiUrl = 'http://192.168.0.230:8085/proxy-ai'; + }else{ + // url = window.location.origin; + aiUrl = '/api'; + } + const httpConfig: App.Service.SimpleServiceConfig = { + baseURL: aiUrl, + other + }; + + const otherHttpKeys = Object.keys(httpConfig.other) as App.Service.OtherBaseURLKey[]; + + const otherConfig: App.Service.OtherServiceConfigItem[] = otherHttpKeys.map(key => { + return { + key, + baseURL: httpConfig.other[key], + proxyPattern: createProxyPattern(key) + }; + }); + + const config: App.Service.ServiceConfig = { + baseURL: httpConfig.baseURL, + proxyPattern: createProxyPattern(), + other: otherConfig + }; + + return config; +} export function getBaseUrl() { return url; } @@ -69,6 +109,20 @@ export function getServiceBaseURL(env: Env.ImportMeta, isProxy: boolean) { otherBaseURL }; } +export function getServiceAiURL(env: Env.ImportMeta, isProxy: boolean) { + const { baseURL, other } = createAiServiceConfig(env); + + const otherBaseURL = {} as Record; + + other.forEach(item => { + otherBaseURL[item.key] = isProxy ? item.proxyPattern : item.baseURL; + }); + + return { + baseURL: isProxy ? createProxyPattern() : baseURL, + otherBaseURL + }; +} /** * Get proxy pattern of backend service base url diff --git a/src/views/my/initiateChange/index.vue b/src/views/my/initiateChange/index.vue index 0d12ba9..a90d6a7 100644 --- a/src/views/my/initiateChange/index.vue +++ b/src/views/my/initiateChange/index.vue @@ -695,6 +695,19 @@ const onBack = () => { } +const currentId = ref(0); +// tab 切换 +const tabChange = (id: string) => { + if(form.value.name.trim() === '') { + return window.$message?.warning('请先填写变更名称'); + } + if(desc.value.trim() === '') { + return window.$message?.warning('请先填写变更内容描述'); + } + tab.value = id; +} + +