This commit is contained in:
吴红兵
2026-03-07 01:34:48 +08:00
parent adc511cfdc
commit 94c3473958
1211 changed files with 599405 additions and 322105 deletions

View File

@@ -31,15 +31,15 @@ const SECURITY_WARNING_TEMPLATE = `
* http://localhost/?ddtk=pig#/flow/task/started
*/
export function initAntiDebug(): void {
if (import.meta.env.VITE_ENABLE_ANTI_DEBUG !== 'true') {
return;
}
if (import.meta.env.VITE_ENABLE_ANTI_DEBUG !== 'true') {
return;
}
const debugKey = import.meta.env.VITE_ANTI_DEBUG_KEY || 'pig';
const debugKey = import.meta.env.VITE_ANTI_DEBUG_KEY || 'pig';
DisableDevtool({
md5: DisableDevtool.md5(debugKey),
disableMenu: false,
rewriteHTML: SECURITY_WARNING_TEMPLATE,
});
DisableDevtool({
md5: DisableDevtool.md5(debugKey),
disableMenu: false,
rewriteHTML: SECURITY_WARNING_TEMPLATE,
});
}

View File

@@ -2,7 +2,7 @@ import other from './other';
// 从环境变量获取加密密钥
const ENCRYPTION_KEY = import.meta.env.VITE_PWD_ENC_KEY;
const ENCRYPTION_ENABLED = import.meta.env.VITE_API_ENC_ENABLED === 'true' ?? false;
const ENCRYPTION_ENABLED = import.meta.env.VITE_API_ENC_ENABLED === 'true' ?? false;
/**
* 使用指定密钥加密数据
@@ -45,7 +45,7 @@ export function decrypt(encryptedData: string): any {
*/
export function encryptRequestParams(params: Record<string, any>): Record<string, string> {
if (!ENCRYPTION_ENABLED) return params;
const encryptedParams: Record<string, string> = {};
for (const [paramKey, value] of Object.entries(params)) {
if (value != null) {

View File

@@ -1,35 +1,23 @@
// 通用:根据字典列表和 value 获取 label 文本
export function getLabelValue(
list: Array<{ label: string; value: string | number }>,
value: string | number | null | undefined,
): string {
if (!list || !Array.isArray(list)) return ''
const item = list.find((it: any) => String(it.value) === String(value))
return item ? item.label : ''
export function getLabelValue(list: Array<{ label: string; value: string | number }>, value: string | number | null | undefined): string {
if (!list || !Array.isArray(list)) return '';
const item = list.find((it: any) => String(it.value) === String(value));
return item ? item.label : '';
}
// 通用:根据指定 key/value 字段,从列表中取 label原 global.getLabelValueByPropes
export function getLabelValueByProps(
list: any[],
key: string | number | null | undefined,
props: { key: string; value: string },
): string {
if (!list || !Array.isArray(list)) return ''
const item = list.find((it: any) => String(it[props.key]) === String(key))
return item ? item[props.value] : ''
export function getLabelValueByProps(list: any[], key: string | number | null | undefined, props: { key: string; value: string }): string {
if (!list || !Array.isArray(list)) return '';
const item = list.find((it: any) => String(it[props.key]) === String(key));
return item ? item[props.value] : '';
}
// 通用:根据 key 取“专业名称 || 学制年制”展示(原 global.getLabelValueByPropes2
export function getMajorLabelWithYears(
list: any[],
key: string | number | null | undefined,
props: { key: string; value: string },
): string {
if (!list || !Array.isArray(list)) return ''
const item = list.find((it: any) => String(it[props.key]) === String(key))
if (!item) return ''
const majorName = item[props.value] ?? ''
const years = item.xz != null ? `${item.xz} 年制` : ''
return years ? `${majorName} || ${years}` : majorName
export function getMajorLabelWithYears(list: any[], key: string | number | null | undefined, props: { key: string; value: string }): string {
if (!list || !Array.isArray(list)) return '';
const item = list.find((it: any) => String(it[props.key]) === String(key));
if (!item) return '';
const majorName = item[props.value] ?? '';
const years = item.xz != null ? `${item.xz} 年制` : '';
return years ? `${majorName} || ${years}` : majorName;
}

View File

@@ -65,15 +65,13 @@ const getAwesomeIconfont = () => {
// 也支持旧格式:.fa-xxx
if (
(rule.selectorText.indexOf('.fa-solid') === 0 ||
rule.selectorText.indexOf('.fa-regular') === 0 ||
rule.selectorText.indexOf('.fa-brands') === 0 ||
rule.selectorText.indexOf('.fa-') === 0) &&
rule.selectorText.indexOf('.fa-regular') === 0 ||
rule.selectorText.indexOf('.fa-brands') === 0 ||
rule.selectorText.indexOf('.fa-') === 0) &&
rule.selectorText.indexOf(',') === -1
) {
if (/::before/.test(rule.selectorText)) {
const iconClass = rule.selectorText
.substring(1, rule.selectorText.length)
.replace(/\:\:before/gi, '');
const iconClass = rule.selectorText.substring(1, rule.selectorText.length).replace(/\:\:before/gi, '');
// 如果是新格式fa-solid fa-xxx只取图标名部分
const iconName = iconClass.replace(/^(fa-solid|fa-regular|fa-brands)\s+/, '');
if (iconName && !sheetsIconList.includes(iconName)) {

View File

@@ -1,12 +1,12 @@
import {parseTime, parseDate, dateTimeStr, dateStr, timeStr} from './formatTime';
import {App} from "vue";
import { parseTime, parseDate, dateTimeStr, dateStr, timeStr } from './formatTime';
import { App } from 'vue';
// 定义全局可以使用属性方法
export const properties = (app: App) => {
app.config.globalProperties.parseTime = parseTime;
app.config.globalProperties.parseDate = parseDate;
app.config.globalProperties.dateTimeStr = dateTimeStr;
app.config.globalProperties.dateStr = dateStr;
app.config.globalProperties.timeStr = timeStr;
app.config.globalProperties.baseURL = import.meta.env.VITE_API_URL;
app.config.globalProperties.parseTime = parseTime;
app.config.globalProperties.parseDate = parseDate;
app.config.globalProperties.dateTimeStr = dateTimeStr;
app.config.globalProperties.dateStr = dateStr;
app.config.globalProperties.timeStr = timeStr;
app.config.globalProperties.baseURL = import.meta.env.VITE_API_URL;
};

View File

@@ -1,30 +1,29 @@
import {nextTick} from 'vue';
import { nextTick } from 'vue';
import router from '/@/router/index';
import pinia from '/@/stores/index';
import {storeToRefs} from 'pinia';
import {useThemeConfig} from '/@/stores/themeConfig';
import {i18n} from '/@/i18n/index';
import {Local} from '/@/utils/storage';
import {verifyUrl} from '/@/utils/toolsValidate';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { i18n } from '/@/i18n/index';
import { Local } from '/@/utils/storage';
import { verifyUrl } from '/@/utils/toolsValidate';
import request from '/@/utils/request';
import {useMessage} from '/@/hooks/message';
import { useMessage } from '/@/hooks/message';
import * as CryptoJS from 'crypto-js';
import { sm4, sm2 } from 'sm-crypto'
import {validateNull} from './validate';
import { sm4, sm2 } from 'sm-crypto';
import { validateNull } from './validate';
/**
* 设置浏览器标题国际化
* @method const title = useTitle(); ==> title()
*/
export function useTitle() {
const stores = useThemeConfig(pinia);
const {themeConfig} = storeToRefs(stores);
nextTick(() => {
let globalTitle: string = themeConfig.value.globalTitle;
let webTitle = setTagsViewNameI18n(router.currentRoute.value);
document.title = `${webTitle} - ${globalTitle}` || globalTitle;
});
const stores = useThemeConfig(pinia);
const { themeConfig } = storeToRefs(stores);
nextTick(() => {
let globalTitle: string = themeConfig.value.globalTitle;
let webTitle = setTagsViewNameI18n(router.currentRoute.value);
document.title = `${webTitle} - ${globalTitle}` || globalTitle;
});
}
/**
@@ -33,30 +32,30 @@ export function useTitle() {
* @returns 返回当前 tagsViewName 名称
*/
export function setTagsViewNameI18n(item: any) {
let tagsViewName: string = '';
const {query, params} = item;
//修复tagsViewName匹配到其他含下列单词的路由
const pattern = /^\{("(zh-cn|en|zh-tw)":"[^,]+",?){1,3}}$/;
if (query?.tagsViewName || params?.tagsViewName) {
if (pattern.test(query?.tagsViewName) || pattern.test(params?.tagsViewName)) {
// 国际化
const urlTagsParams = (query?.tagsViewName && JSON.parse(query?.tagsViewName)) || (params?.tagsViewName && JSON.parse(params?.tagsViewName));
tagsViewName = urlTagsParams[i18n.global.locale.value];
} else {
// 非国际化
tagsViewName = query?.tagsViewName || params?.tagsViewName;
}
} else {
let name=''
if (item.name && typeof item.name === 'string' && item.name.indexOf("_") >= 0) {
name=item.name.split("_")[0]
}else{
name=item.name || ''
}
// 非自定义 tagsView 名称
tagsViewName = i18n.global.t(name);
}
return tagsViewName;
let tagsViewName: string = '';
const { query, params } = item;
//修复tagsViewName匹配到其他含下列单词的路由
const pattern = /^\{("(zh-cn|en|zh-tw)":"[^,]+",?){1,3}}$/;
if (query?.tagsViewName || params?.tagsViewName) {
if (pattern.test(query?.tagsViewName) || pattern.test(params?.tagsViewName)) {
// 国际化
const urlTagsParams = (query?.tagsViewName && JSON.parse(query?.tagsViewName)) || (params?.tagsViewName && JSON.parse(params?.tagsViewName));
tagsViewName = urlTagsParams[i18n.global.locale.value];
} else {
// 非国际化
tagsViewName = query?.tagsViewName || params?.tagsViewName;
}
} else {
let name = '';
if (item.name && typeof item.name === 'string' && item.name.indexOf('_') >= 0) {
name = item.name.split('_')[0];
} else {
name = item.name || '';
}
// 非自定义 tagsView 名称
tagsViewName = i18n.global.t(name);
}
return tagsViewName;
}
/**
@@ -66,21 +65,21 @@ export function setTagsViewNameI18n(item: any) {
* @description data-xxx 属性用于存储页面或应用程序的私有自定义数据
*/
export const lazyImg = (el: string, arr: EmptyArrayType) => {
const io = new IntersectionObserver((res) => {
res.forEach((v: any) => {
if (v.isIntersecting) {
const {img, key} = v.target.dataset;
v.target.src = img;
v.target.onload = () => {
io.unobserve(v.target);
arr[key]['loading'] = false;
};
}
});
});
nextTick(() => {
document.querySelectorAll(el).forEach((img) => io.observe(img));
});
const io = new IntersectionObserver((res) => {
res.forEach((v: any) => {
if (v.isIntersecting) {
const { img, key } = v.target.dataset;
v.target.src = img;
v.target.onload = () => {
io.unobserve(v.target);
arr[key]['loading'] = false;
};
}
});
});
nextTick(() => {
document.querySelectorAll(el).forEach((img) => io.observe(img));
});
};
/**
@@ -88,9 +87,9 @@ export const lazyImg = (el: string, arr: EmptyArrayType) => {
* @returns 返回 `window.localStorage` 中读取的缓存值 `globalComponentSize`
*/
export const globalComponentSize = (): string => {
const stores = useThemeConfig(pinia);
const {themeConfig} = storeToRefs(stores);
return Local.get('themeConfig')?.globalComponentSize || themeConfig.value?.globalComponentSize;
const stores = useThemeConfig(pinia);
const { themeConfig } = storeToRefs(stores);
return Local.get('themeConfig')?.globalComponentSize || themeConfig.value?.globalComponentSize;
};
/**
@@ -99,35 +98,35 @@ export const globalComponentSize = (): string => {
* @returns 克隆后的对象
*/
export function deepClone(obj: EmptyObjectType) {
let newObj: EmptyObjectType;
try {
newObj = obj.push ? [] : {};
} catch (error) {
newObj = {};
}
for (let attr in obj) {
if (obj[attr] && typeof obj[attr] === 'object') {
newObj[attr] = deepClone(obj[attr]);
} else {
newObj[attr] = obj[attr];
}
}
return newObj;
let newObj: EmptyObjectType;
try {
newObj = obj.push ? [] : {};
} catch (error) {
newObj = {};
}
for (let attr in obj) {
if (obj[attr] && typeof obj[attr] === 'object') {
newObj[attr] = deepClone(obj[attr]);
} else {
newObj[attr] = obj[attr];
}
}
return newObj;
}
/**
* 判断是否是移动端
*/
export function isMobile() {
if (
navigator.userAgent.match(
/('phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone')/i
)
) {
return true;
} else {
return false;
}
if (
navigator.userAgent.match(
/('phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone')/i
)
) {
return true;
} else {
return false;
}
}
/**
@@ -137,18 +136,18 @@ export function isMobile() {
* @returns 删除空值后的数组对象
*/
export function handleEmpty(list: EmptyArrayType) {
const arr = [] as any[];
for (const i in list) {
const d = [] as any[];
for (const j in list[i]) {
d.push(list[i][j]);
}
const leng = d.filter((item) => item === '').length;
if (leng !== d.length) {
arr.push(list[i]);
}
}
return arr;
const arr = [] as any[];
for (const i in list) {
const d = [] as any[];
for (const j in list[i]) {
d.push(list[i][j]);
}
const leng = d.filter((item) => item === '').length;
if (leng !== d.length) {
arr.push(list[i]);
}
}
return arr;
}
/**
@@ -156,55 +155,55 @@ export function handleEmpty(list: EmptyArrayType) {
* @param val 当前点击项菜单
*/
export function handleOpenLink(val: RouteItem) {
router.push(val.path);
if (verifyUrl(<string>val.meta?.isLink)) window.open(val.meta?.isLink);
else window.open(`${val.meta?.isLink}`);
router.push(val.path);
if (verifyUrl(<string>val.meta?.isLink)) window.open(val.meta?.isLink);
else window.open(`${val.meta?.isLink}`);
}
/**
* 打开小窗口
*/
export const openWindow = (url: string, title: string, w: number, h: number) => {
// @ts-ignore
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
// @ts-ignore
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;
// @ts-ignore
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
// @ts-ignore
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;
const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;
const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
const height = window.innerHeight
? window.innerHeight
: document.documentElement.clientHeight
? document.documentElement.clientHeight
: screen.height;
const left = width / 2 - w / 2 + dualScreenLeft;
const top = height / 2 - h / 2 + dualScreenTop;
return window.open(
url,
title,
'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' +
w +
', height=' +
h +
', top=' +
top +
', left=' +
left
);
const left = width / 2 - w / 2 + dualScreenLeft;
const top = height / 2 - h / 2 + dualScreenTop;
return window.open(
url,
title,
'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' +
w +
', height=' +
h +
', top=' +
top +
', left=' +
left
);
};
/**
*加密处理
*/
export function encryption(src: string, keyWord: string) {
const key = CryptoJS.enc.Utf8.parse(keyWord);
// 加密
var encrypted = CryptoJS.AES.encrypt(src, key, {
iv: key,
mode: CryptoJS.mode.CFB,
padding: CryptoJS.pad.NoPadding,
});
return encrypted.toString();
const key = CryptoJS.enc.Utf8.parse(keyWord);
// 加密
var encrypted = CryptoJS.AES.encrypt(src, key, {
iv: key,
mode: CryptoJS.mode.CFB,
padding: CryptoJS.pad.NoPadding,
});
return encrypted.toString();
}
/**
@@ -213,22 +212,22 @@ export function encryption(src: string, keyWord: string) {
* @returns 明文
*/
export function decryption(src: string, keyWord: string) {
const key = CryptoJS.enc.Utf8.parse(keyWord);
// 解密逻辑
var decryptd = CryptoJS.AES.decrypt(src, key, {
iv: key,
mode: CryptoJS.mode.CFB,
padding: CryptoJS.pad.NoPadding,
});
const key = CryptoJS.enc.Utf8.parse(keyWord);
// 解密逻辑
var decryptd = CryptoJS.AES.decrypt(src, key, {
iv: key,
mode: CryptoJS.mode.CFB,
padding: CryptoJS.pad.NoPadding,
});
return decryptd.toString(CryptoJS.enc.Utf8);
return decryptd.toString(CryptoJS.enc.Utf8);
}
/**
* SM4加密处理
*/
export function sm4Encryption(src: string, keyWord: string) {
return sm4.encrypt(src, keyWord);
return sm4.encrypt(src, keyWord);
}
/**
@@ -237,7 +236,7 @@ export function sm4Encryption(src: string, keyWord: string) {
* @returns 明文
*/
export function sm4Decryption(src: string, keyWord: string) {
return sm4.decrypt(src, keyWord);
return sm4.decrypt(src, keyWord);
}
/**
@@ -247,8 +246,8 @@ export function sm4Decryption(src: string, keyWord: string) {
* @returns 十六进制密文,格式 C1C3C2
*/
export function sm2Encrypt(msg: string, publicKey: string): string {
if (!msg || !publicKey) return msg;
return sm2.doEncrypt(msg, publicKey, 1);
if (!msg || !publicKey) return msg;
return sm2.doEncrypt(msg, publicKey, 1);
}
/**
@@ -257,8 +256,8 @@ export function sm2Encrypt(msg: string, publicKey: string): string {
* @returns 密文
*/
export function base64Encrypt(src: string) {
const encodedWord = CryptoJS.enc.Utf8.parse(src);
return CryptoJS.enc.Base64.stringify(encodedWord);
const encodedWord = CryptoJS.enc.Utf8.parse(src);
return CryptoJS.enc.Base64.stringify(encodedWord);
}
/**
@@ -269,14 +268,14 @@ export function base64Encrypt(src: string) {
* @returns {*}
*/
export function downBlobFile(url: any, query: any, fileName: string) {
return request({
url: url,
method: 'get',
responseType: 'blob',
params: query,
}).then((response) => {
handleBlobFile(response, fileName);
});
return request({
url: url,
method: 'get',
responseType: 'blob',
params: query,
}).then((response) => {
handleBlobFile(response, fileName);
});
}
/**
@@ -285,26 +284,26 @@ export function downBlobFile(url: any, query: any, fileName: string) {
* @returns
*/
export function handleBlobFile(response: any, fileName: string) {
// 处理返回的文件流,支持 axios 响应结构 { data: blob } 或直接 blob
const blob = response?.data || response;
if (blob && blob.size === 0) {
useMessage().error('内容为空,无法下载');
return;
}
const link = document.createElement('a');
// 处理返回的文件流,支持 axios 响应结构 { data: blob } 或直接 blob
const blob = response?.data || response;
if (blob && blob.size === 0) {
useMessage().error('内容为空,无法下载');
return;
}
const link = document.createElement('a');
// 兼容一下 入参不是 File Blob 类型情况
var binaryData = [] as any;
binaryData.push(blob);
link.href = window.URL.createObjectURL(new Blob(binaryData));
link.download = fileName;
document.body.appendChild(link);
link.click();
window.setTimeout(function () {
// @ts-ignore
URL.revokeObjectURL(blob);
document.body.removeChild(link);
}, 0);
// 兼容一下 入参不是 File Blob 类型情况
var binaryData = [] as any;
binaryData.push(blob);
link.href = window.URL.createObjectURL(new Blob(binaryData));
link.download = fileName;
document.body.appendChild(link);
link.click();
window.setTimeout(function () {
// @ts-ignore
URL.revokeObjectURL(blob);
document.body.removeChild(link);
}, 0);
}
/**
@@ -312,31 +311,31 @@ export function handleBlobFile(response: any, fileName: string) {
* @return string
*/
export function generateUUID() {
if (typeof crypto === 'object') {
if (typeof crypto.randomUUID === 'function') {
return crypto.randomUUID();
}
if (typeof crypto.getRandomValues === 'function' && typeof Uint8Array === 'function') {
const callback = (c: any) => {
const num = Number(c);
return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString(16);
};
return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, callback);
}
}
let timestamp = new Date().getTime();
let performanceNow = (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0;
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
let random = Math.random() * 16;
if (timestamp > 0) {
random = (timestamp + random) % 16 | 0;
timestamp = Math.floor(timestamp / 16);
} else {
random = (performanceNow + random) % 16 | 0;
performanceNow = Math.floor(performanceNow / 16);
}
return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16);
});
if (typeof crypto === 'object') {
if (typeof crypto.randomUUID === 'function') {
return crypto.randomUUID();
}
if (typeof crypto.getRandomValues === 'function' && typeof Uint8Array === 'function') {
const callback = (c: any) => {
const num = Number(c);
return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString(16);
};
return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, callback);
}
}
let timestamp = new Date().getTime();
let performanceNow = (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0;
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
let random = Math.random() * 16;
if (timestamp > 0) {
random = (timestamp + random) % 16 | 0;
timestamp = Math.floor(timestamp / 16);
} else {
random = (performanceNow + random) % 16 | 0;
performanceNow = Math.floor(performanceNow / 16);
}
return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16);
});
}
/**
@@ -351,99 +350,99 @@ export function generateUUID() {
* @method handleOpenLink 打开外部链接
*/
const other = {
useTitle: () => {
useTitle();
},
setTagsViewNameI18n(route: RouteToFrom) {
return setTagsViewNameI18n(route);
},
lazyImg: (el: string, arr: EmptyArrayType) => {
lazyImg(el, arr);
},
globalComponentSize: () => {
return globalComponentSize();
},
deepClone: (obj: EmptyObjectType) => {
return deepClone(obj);
},
isMobile: () => {
return isMobile();
},
handleEmpty: (list: EmptyArrayType) => {
return handleEmpty(list);
},
handleOpenLink: (val: RouteItem) => {
handleOpenLink(val);
},
encryption: (src: string, keyWord: string) => {
return encryption(src, keyWord);
},
decryption: (src: string, keyWord: string) => {
return decryption(src, keyWord);
},
sm2Encrypt: (msg: string, publicKey: string) => {
return sm2Encrypt(msg, publicKey);
},
base64Encrypt: (data: any) => {
return base64Encrypt(data);
},
downBlobFile: (url: any, query: any, fileName: string) => {
return downBlobFile(url, query, fileName);
},
toUnderline: (str: string) => {
return toUnderline(str);
},
openWindow: (url: string, title: string, w: number, h: number) => {
return openWindow(url, title, w, h);
},
getQueryString: (url: string, paraName: string) => {
return getQueryString(url, paraName);
},
adaptationUrl: (url?: string) => {
return adaptationUrl(url);
},
resolveAllEunuchNodeId: (json: any[], idArr: any[], temp: any[] = []) => {
return resolveAllEunuchNodeId(json, idArr, temp);
},
getNonDuplicateID: () => {
return getNonDuplicateID();
},
useTitle: () => {
useTitle();
},
setTagsViewNameI18n(route: RouteToFrom) {
return setTagsViewNameI18n(route);
},
lazyImg: (el: string, arr: EmptyArrayType) => {
lazyImg(el, arr);
},
globalComponentSize: () => {
return globalComponentSize();
},
deepClone: (obj: EmptyObjectType) => {
return deepClone(obj);
},
isMobile: () => {
return isMobile();
},
handleEmpty: (list: EmptyArrayType) => {
return handleEmpty(list);
},
handleOpenLink: (val: RouteItem) => {
handleOpenLink(val);
},
encryption: (src: string, keyWord: string) => {
return encryption(src, keyWord);
},
decryption: (src: string, keyWord: string) => {
return decryption(src, keyWord);
},
sm2Encrypt: (msg: string, publicKey: string) => {
return sm2Encrypt(msg, publicKey);
},
base64Encrypt: (data: any) => {
return base64Encrypt(data);
},
downBlobFile: (url: any, query: any, fileName: string) => {
return downBlobFile(url, query, fileName);
},
toUnderline: (str: string) => {
return toUnderline(str);
},
openWindow: (url: string, title: string, w: number, h: number) => {
return openWindow(url, title, w, h);
},
getQueryString: (url: string, paraName: string) => {
return getQueryString(url, paraName);
},
adaptationUrl: (url?: string) => {
return adaptationUrl(url);
},
resolveAllEunuchNodeId: (json: any[], idArr: any[], temp: any[] = []) => {
return resolveAllEunuchNodeId(json, idArr, temp);
},
getNonDuplicateID: () => {
return getNonDuplicateID();
},
addUnit: (value: string | number, unit = 'px') => {
return addUnit(value, unit);
},
validateNull: (value: any) => {
return validateNull(value);
},
getNumberRadixNum: (input: Number) => {
return getNumberRadixNum(input);
}
addUnit: (value: string | number, unit = 'px') => {
return addUnit(value, unit);
},
validateNull: (value: any) => {
return validateNull(value);
},
getNumberRadixNum: (input: Number) => {
return getNumberRadixNum(input);
},
};
export function getNumberRadixNum(input: Number) {
let strings = input.toString().split(".");
if (strings.length <= 1) {
return 0;
}
return strings[1].toString().length;
let strings = input.toString().split('.');
if (strings.length <= 1) {
return 0;
}
return strings[1].toString().length;
}
export function getQueryString(url: string, paraName: string) {
const arrObj = url.split('?');
if (arrObj.length > 1) {
const arrPara = arrObj[1].split('&');
let arr;
for (let i = 0; i < arrPara.length; i++) {
arr = arrPara[i].split('=');
// eslint-disable-next-line eqeqeq
if (arr != null && arr[0] == paraName) {
return arr[1];
}
}
return '';
} else {
return '';
}
const arrObj = url.split('?');
if (arrObj.length > 1) {
const arrPara = arrObj[1].split('&');
let arr;
for (let i = 0; i < arrPara.length; i++) {
arr = arrPara[i].split('=');
// eslint-disable-next-line eqeqeq
if (arr != null && arr[0] == paraName) {
return arr[1];
}
}
return '';
} else {
return '';
}
}
/**
@@ -456,31 +455,31 @@ export function getQueryString(url: string, paraName: string) {
* @returns {*}
*/
export function handleTree(data: any, id: any, parentId: any, children: any, rootId: any) {
id = id || 'id';
parentId = parentId || 'parentId';
children = children || 'children';
rootId =
rootId ||
Math.min.apply(
Math,
data.map((item: any) => {
return item[parentId];
})
) ||
0;
//对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data));
//循环所有项
const treeData = cloneData.filter((father: any) => {
const branchArr = cloneData.filter((child: any) => {
//返回每一项的子级数组
return father[id] === child[parentId];
});
branchArr.length > 0 ? (father[children] = branchArr) : '';
//返回第一层
return father[parentId] === rootId;
});
return treeData !== '' ? treeData : data;
id = id || 'id';
parentId = parentId || 'parentId';
children = children || 'children';
rootId =
rootId ||
Math.min.apply(
Math,
data.map((item: any) => {
return item[parentId];
})
) ||
0;
//对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data));
//循环所有项
const treeData = cloneData.filter((father: any) => {
const branchArr = cloneData.filter((child: any) => {
//返回每一项的子级数组
return father[id] === child[parentId];
});
branchArr.length > 0 ? (father[children] = branchArr) : '';
//返回第一层
return father[parentId] === rootId;
});
return treeData !== '' ? treeData : data;
}
/**
@@ -488,14 +487,14 @@ export function handleTree(data: any, id: any, parentId: any, children: any, roo
* @returns
*/
const resolveAllEunuchNodeId = (json: any[], idArr: any[], temp: any[] = []) => {
for (const item of json) {
if (item.children && item.children.length !== 0) {
resolveAllEunuchNodeId(item.children, idArr, temp);
} else {
temp.push(...idArr.filter((id) => id === item.id));
}
}
return temp;
for (const item of json) {
if (item.children && item.children.length !== 0) {
resolveAllEunuchNodeId(item.children, idArr, temp);
} else {
temp.push(...idArr.filter((id) => id === item.id));
}
}
return temp;
};
/**
@@ -504,7 +503,7 @@ const resolveAllEunuchNodeId = (json: any[], idArr: any[], temp: any[] = []) =>
* @returns 下划线
*/
export function toUnderline(str: string) {
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
}
/**
@@ -515,14 +514,14 @@ export function toUnderline(str: string) {
* @param originUrl 原始路径
*/
export function adaptationUrl(originUrl?: string) {
// 微服务架构 不做路径转换,为空不做路径转换
const isMicro = import.meta.env.VITE_IS_MICRO;
if (validateNull(isMicro) || isMicro === 'true') {
return originUrl;
}
// 微服务架构 不做路径转换,为空不做路径转换
const isMicro = import.meta.env.VITE_IS_MICRO;
if (validateNull(isMicro) || isMicro === 'true') {
return originUrl;
}
// 转为 /admin 路由前缀的请求
return `/admin/${originUrl?.split('/').splice(2).join('/')}`;
// 转为 /admin 路由前缀的请求
return `/admin/${originUrl?.split('/').splice(2).join('/')}`;
}
/**
@@ -531,9 +530,9 @@ export function adaptationUrl(originUrl?: string) {
* @return { String } id
*/
const getNonDuplicateID = (length = 8) => {
let idStr = Date.now().toString(36);
idStr += Math.random().toString(36).substring(3, length);
return idStr;
let idStr = Date.now().toString(36);
idStr += Math.random().toString(36).substring(3, length);
return idStr;
};
/**
@@ -542,7 +541,7 @@ const getNonDuplicateID = (length = 8) => {
* @param {String} unit 单位 px em rem
*/
const addUnit = (value: string | number, unit = 'px') => {
return !Object.is(Number(value), NaN) ? `${value}${unit}` : value;
return !Object.is(Number(value), NaN) ? `${value}${unit}` : value;
};
// 统一批量导出

View File

@@ -1,14 +1,14 @@
import {getObjDetails} from '/@/api/admin/param';
import { getObjDetails } from '/@/api/admin/param';
/**
* 后端参数获取
*/
const params = {
async get(key: string) {
const result = await getObjDetails({publicKey: key})
return result.data;
},
}
async get(key: string) {
const result = await getObjDetails({ publicKey: key });
return result.data;
},
};
// 统一批量导出
export default params;

View File

@@ -3,7 +3,7 @@ import { Session } from '/@/utils/storage';
import { useMessage, useMessageBox } from '/@/hooks/message';
import qs from 'qs';
import other from './other';
import { paramsFilter } from "/@/flow";
import { paramsFilter } from '/@/flow';
import { wrapEncryption, encryptRequestParams, decrypt } from './apiCrypto';
import mittBus from '/@/utils/mitt';
import { needRoleSelection, isRoleDialogTriggered, setRoleDialogTriggered } from '/@/utils/roleSelect';
@@ -14,8 +14,8 @@ export enum CommonHeaderEnum {
'ENC_FLAG' = 'Enc-Flag',
'AUTHORIZATION' = 'Authorization',
'VERSION' = 'VERSION',
'ROLE_CODE'='ROLE_CODE',
'RRID'='RRID'
'ROLE_CODE' = 'ROLE_CODE',
'RRID' = 'RRID',
}
/**
@@ -40,8 +40,7 @@ const service: AxiosInstance = axios.create({
*/
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
config.headers['VERSION'] = 'A' // 目标版本
config.headers['VERSION'] = 'A'; // 目标版本
// 统一增加Authorization请求头, skipToken 跳过增加token
const token = Session.getToken();
@@ -55,12 +54,12 @@ service.interceptors.request.use(
config.headers![CommonHeaderEnum.TENANT_ID] = tenantId;
}
//统一增加 当前角色CODE
const roleCode = Session.getRoleCode()
if(roleCode){
const roleCode = Session.getRoleCode();
if (roleCode) {
config.headers![CommonHeaderEnum.ROLE_CODE] = roleCode;
}
const roleId = Session.getRoleId()
if(roleId){
const roleId = Session.getRoleId();
if (roleId) {
config.headers![CommonHeaderEnum.RRID] = roleId;
}

View File

@@ -80,10 +80,10 @@ export const Session = {
getTenant() {
return Local.get('tenantId') ? Local.get('tenantId') : 1;
},
getRoleCode(){
getRoleCode() {
return Local.get('roleCode') ? Local.get('roleCode') : '';
},
getRoleId(){
getRoleId() {
return Local.get('roleId') ? Local.get('roleId') : '';
}
},
};

View File

@@ -3,17 +3,17 @@
* 提供通用的列配置加载和保存逻辑
*/
import { ref, Ref, computed, ComputedRef } from 'vue'
import { useRoute } from 'vue-router'
import { ref, Ref, computed, ComputedRef } from 'vue';
import { useRoute } from 'vue-router';
export interface ColumnConfig {
prop?: string
label: string
alwaysShow?: boolean
fixed?: boolean | 'left' | 'right'
minWidth?: number | string
width?: number | string
[key: string]: any
prop?: string;
label: string;
alwaysShow?: boolean;
fixed?: boolean | 'left' | 'right';
minWidth?: number | string;
width?: number | string;
[key: string]: any;
}
/**
@@ -22,136 +22,123 @@ export interface ColumnConfig {
* @returns 返回 visibleColumns、columnOrder、loadSavedConfig、handleColumnChange、handleColumnOrderChange 等
*/
export function useTableColumnControl(tableColumns: ColumnConfig[]) {
const route = useRoute()
// 当前显示的列
const visibleColumns = ref<string[]>([])
// 列排序顺序
const columnOrder = ref<string[]>([])
const route = useRoute();
// 根据路由生成 storageKey
const getStorageKey = (suffix: string = '') => {
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
return `table-columns-${routePath}${suffix ? `-${suffix}` : ''}`
}
// 当前显示的列
const visibleColumns = ref<string[]>([]);
// 列排序顺序
const columnOrder = ref<string[]>([]);
// 立即从 localStorage 加载配置
const loadSavedConfig = () => {
const storageKey = getStorageKey()
const saved = localStorage.getItem(storageKey)
if (saved) {
try {
const savedColumns = JSON.parse(saved)
const validColumns = tableColumns
.filter(col => !col.alwaysShow && !col.fixed)
.map(col => col.prop || col.label)
const filteredSaved = savedColumns.filter((col: string) => validColumns.includes(col))
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
} catch (e) {
console.error('解析列配置失败:', e)
visibleColumns.value = tableColumns
.filter(col => !col.alwaysShow && !col.fixed)
.map(col => col.prop || col.label)
}
} else {
visibleColumns.value = tableColumns
.filter(col => !col.alwaysShow && !col.fixed)
.map(col => col.prop || col.label)
}
// 加载列排序配置
const orderKey = getStorageKey('order')
const savedOrder = localStorage.getItem(orderKey)
if (savedOrder) {
try {
const parsedOrder = JSON.parse(savedOrder)
const validColumns = tableColumns
.filter(col => !col.alwaysShow && !col.fixed)
.map(col => col.prop || col.label)
columnOrder.value = parsedOrder.filter((key: string) => validColumns.includes(key))
validColumns.forEach(key => {
if (!columnOrder.value.includes(key)) {
columnOrder.value.push(key)
}
})
} catch (e) {
console.error('解析列排序失败:', e)
columnOrder.value = tableColumns
.filter(col => !col.alwaysShow && !col.fixed)
.map(col => col.prop || col.label)
}
} else {
columnOrder.value = tableColumns
.filter(col => !col.alwaysShow && !col.fixed)
.map(col => col.prop || col.label)
}
}
// 根据路由生成 storageKey
const getStorageKey = (suffix: string = '') => {
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-');
return `table-columns-${routePath}${suffix ? `-${suffix}` : ''}`;
};
// 排序后的表格列
const sortedTableColumns = computed(() => {
const columns = tableColumns.filter(col => {
const key = col.prop || col.label
return col.alwaysShow || col.fixed || visibleColumns.value.includes(key)
})
if (columnOrder.value.length > 0) {
const orderedColumns: ColumnConfig[] = []
const unorderedColumns: ColumnConfig[] = []
columnOrder.value.forEach(key => {
const col = columns.find(c => (c.prop || c.label) === key)
if (col) {
orderedColumns.push(col)
}
})
columns.forEach(col => {
const key = col.prop || col.label
if (!columnOrder.value.includes(key)) {
unorderedColumns.push(col)
}
})
return [...orderedColumns, ...unorderedColumns]
}
return columns
})
// 立即从 localStorage 加载配置
const loadSavedConfig = () => {
const storageKey = getStorageKey();
const saved = localStorage.getItem(storageKey);
if (saved) {
try {
const savedColumns = JSON.parse(saved);
const validColumns = tableColumns.filter((col) => !col.alwaysShow && !col.fixed).map((col) => col.prop || col.label);
const filteredSaved = savedColumns.filter((col: string) => validColumns.includes(col));
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns;
} catch (e) {
console.error('解析列配置失败:', e);
visibleColumns.value = tableColumns.filter((col) => !col.alwaysShow && !col.fixed).map((col) => col.prop || col.label);
}
} else {
visibleColumns.value = tableColumns.filter((col) => !col.alwaysShow && !col.fixed).map((col) => col.prop || col.label);
}
// 列显示控制函数
const checkColumnVisible = (prop: string): boolean => {
if (visibleColumns.value.length === 0) {
return true
}
return visibleColumns.value.includes(prop)
}
// 加载列排序配置
const orderKey = getStorageKey('order');
const savedOrder = localStorage.getItem(orderKey);
if (savedOrder) {
try {
const parsedOrder = JSON.parse(savedOrder);
const validColumns = tableColumns.filter((col) => !col.alwaysShow && !col.fixed).map((col) => col.prop || col.label);
columnOrder.value = parsedOrder.filter((key: string) => validColumns.includes(key));
validColumns.forEach((key) => {
if (!columnOrder.value.includes(key)) {
columnOrder.value.push(key);
}
});
} catch (e) {
console.error('解析列排序失败:', e);
columnOrder.value = tableColumns.filter((col) => !col.alwaysShow && !col.fixed).map((col) => col.prop || col.label);
}
} else {
columnOrder.value = tableColumns.filter((col) => !col.alwaysShow && !col.fixed).map((col) => col.prop || col.label);
}
};
// 监听列变化
const handleColumnChange = (columns: string[]) => {
visibleColumns.value = columns
const storageKey = getStorageKey()
const selectableColumns = columns.filter(col => {
const column = tableColumns.find(c => (c.prop || c.label) === col)
return column && !column.alwaysShow && !column.fixed
})
localStorage.setItem(storageKey, JSON.stringify(selectableColumns))
}
// 排序后的表格列
const sortedTableColumns = computed(() => {
const columns = tableColumns.filter((col) => {
const key = col.prop || col.label;
return col.alwaysShow || col.fixed || visibleColumns.value.includes(key);
});
// 监听列排序变化
const handleColumnOrderChange = (order: string[]) => {
columnOrder.value = order
const storageKey = getStorageKey('order')
localStorage.setItem(storageKey, JSON.stringify(order))
}
if (columnOrder.value.length > 0) {
const orderedColumns: ColumnConfig[] = [];
const unorderedColumns: ColumnConfig[] = [];
return {
visibleColumns,
columnOrder,
sortedTableColumns,
checkColumnVisible,
loadSavedConfig,
handleColumnChange,
handleColumnOrderChange
}
columnOrder.value.forEach((key) => {
const col = columns.find((c) => (c.prop || c.label) === key);
if (col) {
orderedColumns.push(col);
}
});
columns.forEach((col) => {
const key = col.prop || col.label;
if (!columnOrder.value.includes(key)) {
unorderedColumns.push(col);
}
});
return [...orderedColumns, ...unorderedColumns];
}
return columns;
});
// 列显示控制函数
const checkColumnVisible = (prop: string): boolean => {
if (visibleColumns.value.length === 0) {
return true;
}
return visibleColumns.value.includes(prop);
};
// 监听列变化
const handleColumnChange = (columns: string[]) => {
visibleColumns.value = columns;
const storageKey = getStorageKey();
const selectableColumns = columns.filter((col) => {
const column = tableColumns.find((c) => (c.prop || c.label) === col);
return column && !column.alwaysShow && !column.fixed;
});
localStorage.setItem(storageKey, JSON.stringify(selectableColumns));
};
// 监听列排序变化
const handleColumnOrderChange = (order: string[]) => {
columnOrder.value = order;
const storageKey = getStorageKey('order');
localStorage.setItem(storageKey, JSON.stringify(order));
};
return {
visibleColumns,
columnOrder,
sortedTableColumns,
checkColumnVisible,
loadSavedConfig,
handleColumnChange,
handleColumnOrderChange,
};
}

View File

@@ -325,7 +325,7 @@ export function verifyIdCardAll(val: string) {
const macaoIdCard = /^[1-9]\d{0,6}$/;
// 台湾身份证1个字母+9位数字
const taiwanIdCard = /^[A-Z]\d{9}$/;
// 只要匹配其中一种即可
if (mainlandIdCard.test(val) || hkIdCard.test(val) || macaoIdCard.test(val) || taiwanIdCard.test(val)) {
return true;

View File

@@ -211,24 +211,23 @@ export const getRegExp = function (validatorName) {
noChinese: '^[^\u4e00-\u9fa5]+$',
chinese: '^[\u4e00-\u9fa5]+$',
email: '^([-_A-Za-z0-9.]+)@([_A-Za-z0-9]+\\.)+[A-Za-z0-9]{2,3}$',
url: '(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]'
url: '(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]',
};
return commonRegExp[validatorName];
};
const validateFn = (validatorName, rule, value, callback, defaultErrorMsg) => {
if (validateNull(value) || value.length <= 0) {
callback();
return;
}
if (validateNull(value) || value.length <= 0) {
callback();
return;
}
const reg = new RegExp(getRegExp(validatorName));
const reg = new RegExp(getRegExp(validatorName));
if (!reg.test(value)) {
const errTxt = rule.errorMsg || defaultErrorMsg;
callback(new Error(errTxt));
} else {
callback();
}
if (!reg.test(value)) {
const errTxt = rule.errorMsg || defaultErrorMsg;
callback(new Error(errTxt));
} else {
callback();
}
};