diff --git a/index.html b/index.html index a732a19..6cbd66f 100644 --- a/index.html +++ b/index.html @@ -212,5 +212,28 @@ + diff --git a/src/assets/large/outscreen.png b/src/assets/large/outscreen.png new file mode 100644 index 0000000..0e13961 Binary files /dev/null and b/src/assets/large/outscreen.png differ diff --git a/src/permission.ts b/src/permission.ts index 7649149..c8c1acb 100644 --- a/src/permission.ts +++ b/src/permission.ts @@ -22,6 +22,11 @@ const isWhiteList = (path: string) => { router.beforeEach(async (to, from) => { NProgress.start(); + // 特殊页面放行 + if (['/ueScreen'].includes(to.path)) { + return true; + } + // 已登录 if (getToken()) { if (to.meta.title) useSettingsStore().setTitle(to.meta.title); diff --git a/src/utils/request.ts b/src/utils/request.ts index ca0e341..e53f32c 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -14,6 +14,10 @@ import router from '@/router'; const encryptHeader = 'encrypt-key'; let downloadLoadingInstance: LoadingInstance; + +let silentLoginPromise: Promise | null = null; +let retryQueue: Array<() => void> = []; // 等待重试的请求队列 + // 是否显示重新登录 export const isRelogin = { show: false }; export const globalHeaders = () => { @@ -105,7 +109,7 @@ service.interceptors.request.use( // 响应拦截器 service.interceptors.response.use( - (res: AxiosResponse) => { + async (res: AxiosResponse) => { if (import.meta.env.VITE_APP_ENCRYPT === 'true') { // 加密后的 AES 秘钥 const keyStr = res.headers[encryptHeader]; @@ -131,6 +135,65 @@ service.interceptors.response.use( return res.data; } if (code === 401) { + // 处理ueScreen未登录 + const isInExternalPage = window.self !== window.top || window.location.pathname.includes('/ueScreen'); + + if (isInExternalPage) { + console.log('[外链页] 检测到 401'); + + // 统一处理并发401 + if (silentLoginPromise) { + // 登录正在进行时,等待完成 + console.log('[外链页] 等待静默登录完成'); + return new Promise((resolve) => { + retryQueue.push(() => { + const newToken = getToken(); + if (newToken) { + res.config.headers.Authorization = 'Bearer ' + newToken; + } + resolve(axios.request(res.config)); + }); + }); + } + + // 启动新的静默登录 + if (typeof window['silentLogin'] === 'function') { + silentLoginPromise = window + .silentLogin(true) + .catch(() => false) + .finally(() => { + silentLoginPromise = null; + }); + + const ok = await silentLoginPromise; + const newToken = getToken(); + + if (ok && newToken) { + axios.defaults.headers.common['Authorization'] = 'Bearer ' + newToken; + + // ✅ 每个请求返回自己的 Promise + return new Promise((resolve) => { + retryQueue.push((config) => { + axios.request(config).then((newRes) => { + console.log('🚀 ~ config:', newRes.data); + resolve(newRes.data); // 保持统一结构 + }); + }); + + // 如果登录刚好完成,就立即执行回调 + retryQueue.forEach((cb) => cb(res.config)); + retryQueue = []; + }); + } else { + retryQueue = []; + console.warn('[外链页] 静默登录失败'); + return Promise.reject('静默登录失败'); + } + } + + return Promise.reject('外链页未配置静默登录'); + } + // prettier-ignore if (!isRelogin.show) { isRelogin.show = true; @@ -184,31 +247,31 @@ export function download(url: string, params: any, fileName: string) { downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' }); // prettier-ignore return service.post(url, params, { - transformRequest: [ - (params: any) => { - return tansParams(params); - } - ], - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - responseType: 'blob' - }).then(async (resp: any) => { - const isLogin = blobValidate(resp); - if (isLogin) { - const blob = new Blob([resp]); - FileSaver.saveAs(blob, fileName); - } else { - const blob = new Blob([resp]); - const resText = await blob.text(); - const rspObj = JSON.parse(resText); - const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']; - ElMessage.error(errMsg); + transformRequest: [ + (params: any) => { + return tansParams(params); } - downloadLoadingInstance.close(); - }).catch((r: any) => { - console.error(r); - ElMessage.error('下载文件出现错误,请联系管理员!'); - downloadLoadingInstance.close(); - }); + ], + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + responseType: 'blob' + }).then(async (resp: any) => { + const isLogin = blobValidate(resp); + if (isLogin) { + const blob = new Blob([resp]); + FileSaver.saveAs(blob, fileName); + } else { + const blob = new Blob([resp]); + const resText = await blob.text(); + const rspObj = JSON.parse(resText); + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']; + ElMessage.error(errMsg); + } + downloadLoadingInstance.close(); + }).catch((r: any) => { + console.error(r); + ElMessage.error('下载文件出现错误,请联系管理员!'); + downloadLoadingInstance.close(); + }); } // 导出 axios 实例 export default service; diff --git a/src/views/ueScreen/components/date.vue b/src/views/ueScreen/components/date.vue new file mode 100644 index 0000000..682ef36 --- /dev/null +++ b/src/views/ueScreen/components/date.vue @@ -0,0 +1,255 @@ + + + + + \ No newline at end of file diff --git a/src/views/ueScreen/components/header.vue b/src/views/ueScreen/components/header.vue index a4a8e44..5f71bda 100644 --- a/src/views/ueScreen/components/header.vue +++ b/src/views/ueScreen/components/header.vue @@ -1,38 +1,79 @@ + + \ No newline at end of file diff --git a/src/views/ueScreen/components/smalltitle.vue b/src/views/ueScreen/components/smalltitle.vue index 69d5af2..4f2bb25 100644 --- a/src/views/ueScreen/components/smalltitle.vue +++ b/src/views/ueScreen/components/smalltitle.vue @@ -18,7 +18,18 @@ const props = defineProps({ })