diff --git a/.env.development b/.env.development index 0b8d642f..bd608ea7 100644 --- a/.env.development +++ b/.env.development @@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC" VITE_APP_CODE = "OMC" # 应用版本 -VITE_APP_VERSION = "2.240809" +VITE_APP_VERSION = "2.240815" # 接口基础URL地址-不带/后缀 VITE_API_BASE_URL = "/omc-api" diff --git a/.env.production b/.env.production index 3732a1c9..338182a5 100644 --- a/.env.production +++ b/.env.production @@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC" VITE_APP_CODE = "OMC" # 应用版本 -VITE_APP_VERSION = "2.240809" +VITE_APP_VERSION = "2.240815" # 接口基础URL地址-不带/后缀 VITE_API_BASE_URL = "/omc-api" diff --git a/package.json b/package.json index 42b62bbf..697c5858 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "@codemirror/lang-yaml": "^6.1.1", "@codemirror/merge": "^6.6.3", "@codemirror/theme-one-dark": "^6.1.2", - "@tato30/vue-pdf": "~1.9.7", + "@tato30/vue-pdf": "^1.10.0", "@vueuse/core": "~10.10.1", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", @@ -26,10 +26,11 @@ "antdv-pro-layout": "~3.3.5", "antdv-pro-modal": "^3.1.0", "codemirror": "^6.0.1", + "crypto-js": "^4.2.0", "dayjs": "^1.11.11", "echarts": "~5.5.0", "file-saver": "^2.0.5", - "intl-tel-input": "~23.0.12", + "intl-tel-input": "^23.8.1", "js-base64": "^3.7.7", "js-cookie": "^3.0.5", "localforage": "^1.10.0", @@ -43,6 +44,7 @@ "xlsx": "~0.18.5" }, "devDependencies": { + "@types/crypto-js": "^4.2.2", "@types/file-saver": "^2.0.7", "@types/js-cookie": "^3.0.6", "@types/node": "^18.0.0", diff --git a/src/api/login.ts b/src/api/login.ts index 225c9327..e7ff947d 100644 --- a/src/api/login.ts +++ b/src/api/login.ts @@ -7,6 +7,7 @@ export function login(data: Record) { method: 'post', data: data, whithToken: false, + crypto: true, }); } @@ -21,6 +22,7 @@ export function register(data: Record) { method: 'post', data: data, whithToken: false, + crypto: true, }); } diff --git a/src/api/monitor/monitor.ts b/src/api/monitor/monitor.ts index dc5a893d..830f4cf5 100644 --- a/src/api/monitor/monitor.ts +++ b/src/api/monitor/monitor.ts @@ -6,5 +6,6 @@ export function getLoad(query: Record) { url: '/monitor/load', method: 'get', params: query, + timeout: 60_000, }); } diff --git a/src/api/monitor/system.ts b/src/api/monitor/system.ts index 4eaa354c..fbb19aab 100644 --- a/src/api/monitor/system.ts +++ b/src/api/monitor/system.ts @@ -5,5 +5,6 @@ export function getSystemInfo() { return request({ url: '/monitor/system-info', method: 'get', + timeout: 60_000, }); } diff --git a/src/api/ne/neInfo.ts b/src/api/ne/neInfo.ts index 49156044..2d66d90b 100644 --- a/src/api/ne/neInfo.ts +++ b/src/api/ne/neInfo.ts @@ -36,6 +36,8 @@ export function addNeInfo(data: Record) { url: `/ne/info`, method: 'post', data: data, + crypto: true, + timeout: 30_000, }); } @@ -49,6 +51,8 @@ export function updateNeInfo(data: Record) { url: `/ne/info`, method: 'put', data: data, + crypto: true, + timeout: 30_000, }); } diff --git a/src/constants/app-constants.ts b/src/constants/app-constants.ts index cea6637a..40f1082a 100644 --- a/src/constants/app-constants.ts +++ b/src/constants/app-constants.ts @@ -3,3 +3,6 @@ export const APP_REQUEST_HEADER_CODE = 'X-App-Code'; /**应用-请求头-系统版本 */ export const APP_REQUEST_HEADER_VERSION = 'X-App-Version'; + +/**应用-请求数据-密钥 */ +export const APP_DATA_API_KEY = 'T9ox2DCzpLfJIPzkH9pKhsOTMOEMJcFv'; diff --git a/src/constants/result-constants.ts b/src/constants/result-constants.ts index e2bccbd6..ae7dfeeb 100644 --- a/src/constants/result-constants.ts +++ b/src/constants/result-constants.ts @@ -1,3 +1,12 @@ +/**响应-code加密数据 */ +export const RESULT_CODE_ENCRYPT = 2; + +/**响应-msg加密数据 */ +export const RESULT_MSG_ENCRYPT: Record = { + zh_CN: '加密!', + en_US: 'encrypt!', +}; + /**响应-code正常成功 */ export const RESULT_CODE_SUCCESS = 1; diff --git a/src/plugins/http-fetch.ts b/src/plugins/http-fetch.ts index 9ea0ecab..09d0b96d 100644 --- a/src/plugins/http-fetch.ts +++ b/src/plugins/http-fetch.ts @@ -13,10 +13,13 @@ import { import { APP_REQUEST_HEADER_CODE, APP_REQUEST_HEADER_VERSION, + APP_DATA_API_KEY, } from '@/constants/app-constants'; import { + RESULT_CODE_ENCRYPT, RESULT_CODE_ERROR, RESULT_CODE_SUCCESS, + RESULT_MSG_ENCRYPT, RESULT_MSG_ERROR, RESULT_MSG_NOT_TYPE, RESULT_MSG_SERVER_ERROR, @@ -25,11 +28,12 @@ import { RESULT_MSG_URL_NOTFOUND, RESULT_MSG_URL_RESUBMIT, } from '@/constants/result-constants'; +import { decryptAES, encryptAES } from '@/utils/encrypt-utils'; /**响应结果类型 */ export type ResultType = { /**响应码 */ - code: number | 1 | 0; + code: number; /**信息 */ msg: string; /**数据 */ @@ -76,6 +80,8 @@ type OptionsType = { body?: BodyInit; /**防止数据重复提交 */ repeatSubmit?: boolean; + /**接口数据加密 */ + crypto?: boolean; /**携带授权Token请求头 */ whithToken?: boolean; /**中断控制信号,timeout不会生效 */ @@ -167,24 +173,40 @@ function beforeRequest(options: OptionsType): OptionsType | Promise { // 请求拼接地址栏参数 if (options.params) { - let paramStr = ''; const params = options.params; + const queryParams: string[] = []; for (const key in params) { const value = params[key]; // 空字符或未定义的值不作为参数发送 if (value === '' || value === undefined) continue; - paramStr += `&${encodeURIComponent(key)}=${encodeURIComponent(value)}`; + const str = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; + queryParams.push(str); } - if (paramStr && paramStr.startsWith('&')) { - options.url = `${options.url}?${paramStr.substring(1)}`; + const paramStr = queryParams.join('&'); + if (paramStr) { + const separator = options.url.includes('?') ? '&' : '?'; + // 请求加密 + if (options.crypto) { + debugger; + const data = encryptAES(JSON.stringify(paramStr), APP_DATA_API_KEY); + options.url += `${separator}data=${encodeURIComponent(data)}`; + } else { + options.url += `${separator}${paramStr}`; + } } } // 非get参数提交 - if (options.data instanceof FormData) { - options.body = options.data; - } else { - options.body = JSON.stringify(options.data); + let body = options.data + if (body instanceof FormData) { + options.body = body; + } else if (body) { + // 请求加密 + if (options.crypto) { + const data = encryptAES(JSON.stringify(body), APP_DATA_API_KEY); + body = { data }; + } + options.body = JSON.stringify(body); } return options; } @@ -199,6 +221,28 @@ function interceptorResponse(res: ResultType): ResultType | Promise { window.location.reload(); } + // 响应数据解密 + if (res.code === RESULT_CODE_ENCRYPT) { + const str = decryptAES(res.data, APP_DATA_API_KEY); + let data = {}; + try { + data = JSON.parse(str); + } catch (error) { + console.error(error); + } + if (Object.keys(data).length === 0) { + return Promise.resolve({ + code: RESULT_CODE_ERROR, + msg: RESULT_MSG_ENCRYPT[language], + }); + } + return Promise.resolve({ + code: RESULT_CODE_SUCCESS, + msg: RESULT_MSG_SUCCESS[language], + data, + }); + } + // 风格处理 if (!Reflect.has(res, 'code')) { return Promise.resolve({ @@ -266,7 +310,7 @@ export async function request(options: OptionsType): Promise { case 'text': // 文本数据 const str = await res.text(); return { - code: 1, + code: RESULT_CODE_SUCCESS, msg: str, }; case 'json': // json格式数据 diff --git a/src/utils/encrypt-utils.ts b/src/utils/encrypt-utils.ts new file mode 100644 index 00000000..351d125c --- /dev/null +++ b/src/utils/encrypt-utils.ts @@ -0,0 +1,50 @@ +import CryptoJS from 'crypto-js'; +import { isValid, decode } from 'js-base64'; + +/** + * AES 加密并转为 base64 + * @param plaintext 数据字符串 + * @param aeskey 密钥 + * @returns 加密字符串 + */ +export function encryptAES(plaintext: string, aeskey: string): string { + const nowRoaund = new Date().getTime().toString(6); + const key = CryptoJS.enc.Utf8.parse(aeskey); + const iv = CryptoJS.enc.Utf8.parse(nowRoaund); + const encrypted = CryptoJS.AES.encrypt(`${nowRoaund}${plaintext}`, key, { + iv: iv, + blockSize: 16, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + format: CryptoJS.format.OpenSSL, + }); + return encrypted.toString(); +} + +/** + * AES 解密 + * @param ciphertext 加密字符串 + * @param aeskey 密钥 + * @returns 数据字符串 + */ +export function decryptAES(ciphertext: string, aeskey: string): string { + const nowRoaund = new Date().getTime().toString(6); + const key = CryptoJS.enc.Utf8.parse(aeskey); + const iv = CryptoJS.enc.Utf8.parse(nowRoaund); + const decrypted = CryptoJS.AES.decrypt(ciphertext, key, { + iv: iv, + blockSize: 16, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + format: CryptoJS.format.OpenSSL, + }); + const base64Str = decrypted.toString(CryptoJS.enc.Base64); + if (isValid(base64Str)) { + const str = decode(base64Str); + const idx = str.indexOf(':)', 10); + if (idx > 10) { + return str.substring(idx + 2); + } + } + return ''; +} diff --git a/src/views/ne/neConfig/hooks/useConfigList.ts b/src/views/ne/neConfig/hooks/useConfigList.ts index ef77f5a4..277d7407 100644 --- a/src/views/ne/neConfig/hooks/useConfigList.ts +++ b/src/views/ne/neConfig/hooks/useConfigList.ts @@ -53,6 +53,13 @@ export default function useConfigList({ /**单列表编辑 */ function listEdit(row: Record) { + if ( + listState.confirmLoading || + ['read-only', 'read', 'ro'].includes(row.access) + ) { + return; + } + listState.editRecord = Object.assign({}, row); } diff --git a/src/views/ne/neConfig/index.vue b/src/views/ne/neConfig/index.vue index 39cd8a20..07fe2373 100644 --- a/src/views/ne/neConfig/index.vue +++ b/src/views/ne/neConfig/index.vue @@ -597,9 +597,8 @@ onMounted(() => { @click="listEdit(record)" style="margin-left: 18px" v-if=" - !['read-only', 'read', 'ro'].includes( - record.access - ) && !listState.confirmLoading + !listState.confirmLoading && + !['read-only', 'read', 'ro'].includes(record.access) " /> @@ -658,7 +657,14 @@ onMounted(() => { diff --git a/src/views/ne/neInfo/index.vue b/src/views/ne/neInfo/index.vue index 0c58214a..e1fb16d8 100644 --- a/src/views/ne/neInfo/index.vue +++ b/src/views/ne/neInfo/index.vue @@ -364,8 +364,18 @@ function fnGetList(pageNum?: number) { tablePagination.total = res.total; // 遍历处理资源情况数值 tableState.data = res.rows.map(item => { + let resouresUsage = { + sysDiskUsage: 0, + sysMemUsage: 0, + sysCpuUsage: 0, + nfCpuUsage: 0, + }; const neState = item.serverState; - const resouresUsage = parseResouresUsage(neState); + if (neState) { + resouresUsage = parseResouresUsage(neState); + } else { + item.serverState = { online: false }; + } Reflect.set(item, 'resoures', resouresUsage); return item; }); diff --git a/src/views/ne/neQuickSetup/index.vue b/src/views/ne/neQuickSetup/index.vue index c4f91521..e8f1c95c 100644 --- a/src/views/ne/neQuickSetup/index.vue +++ b/src/views/ne/neQuickSetup/index.vue @@ -47,13 +47,6 @@ function fnNext() {
- - {{ t('views.ne.neQuickSetup.stepNext') }} - {{ t('views.ne.neQuickSetup.reloadPara5G') }} + + {{ t('views.ne.neQuickSetup.stepNext') }} +
diff --git a/src/views/neUser/base5G/index.vue b/src/views/neUser/base5G/index.vue index 946c68e7..8ec5b667 100644 --- a/src/views/neUser/base5G/index.vue +++ b/src/views/neUser/base5G/index.vue @@ -157,6 +157,53 @@ function fnGet45GList(pageNum?: number) { if (pageNum) { queryParams.pageNum = pageNum; } + if (!queryParams.neType) { + promises.value = []; + //同时获取45G基站信息 且在每条信息中添加45G字段(原始数据没有) 已经筛选后的 + neCascaderOptions.value.map((item: any) => { + item.children.forEach((child: any) => { + promises.value.push( + listBase5G({ + neId: child.neId, + neType: child.neType, + nbId: queryParams.id, + pageNum: queryParams.pageNum, + pageSize: 10000, + }) + ); + }); + }); + + Promise.allSettled(promises.value).then(results => { + results.forEach(result => { + if (result.status === 'fulfilled') { + const allBaseData = result.value; + if ( + allBaseData.code === RESULT_CODE_SUCCESS && + Array.isArray(allBaseData.rows) + ) { + // 处理成功结果 + tablePagination.total += allBaseData.total; + tableState.data = [...tableState.data, ...allBaseData.rows]; + if ( + tablePagination.total <= + (queryParams.pageNum - 1) * tablePagination.pageSize && + queryParams.pageNum !== 1 + ) { + tableState.loading = false; + fnGetList(queryParams.pageNum - 1); + } + } else { + //AMF返回404是代表没找到这个数据 GNB_NOT_FOUND + tablePagination.total = 0; + tableState.data = []; + } + tableState.loading = false; + } + }); + }); + return; + } const typeArr: any = []; const typeMapping: any = { diff --git a/vite.config.ts b/vite.config.ts index 050aed13..f719d7f8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -20,7 +20,7 @@ export default defineConfig(({ mode }) => { proxy: { // https://cn.vitejs.dev/config/#server-proxy [env.VITE_API_BASE_URL]: { - // target: 'http://192.168.2.166:3030', + // target: 'http://192.168.2.166:33030', target: 'http://192.168.5.58:33040', changeOrigin: true, rewrite: p => p.replace(env.VITE_API_BASE_URL, ''), @@ -54,6 +54,7 @@ export default defineConfig(({ mode }) => { }, }, build: { + target: 'esnext', // Use 'esnext' to support the latest features sourcemap: false, chunkSizeWarningLimit: 500, // 调整区块大小警告限制(以kB为单位) rollupOptions: { @@ -74,6 +75,11 @@ export default defineConfig(({ mode }) => { }, }, optimizeDeps: { + esbuildOptions: { + supported: { + 'top-level-await': true, + }, + }, include: ['@ant-design/icons-vue', 'ant-design-vue'], }, plugins: [