2025-07-30 18:25:58 +08:00

661 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router';
import type { App, Plugin } from 'vue';
import type { FormSchema, FormActionType } from "@/components/Form";
import { unref } from 'vue';
import { isObject, isFunction, isString } from '/@/utils/is';
import Big from 'big.js';
import dayjs from "dayjs";
// update-begin--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开原因是带了#号,需要替换一下
export const URL_HASH_TAB = `__AGWE4H__HASH__TAG__PWHRG__`;
// update-end--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开原因是带了#号,需要替换一下
export const noop = () => {};
/**
* @description: Set ui mount node
*/
export function getPopupContainer(node?: HTMLElement): HTMLElement {
return (node?.parentNode as HTMLElement) ?? document.body;
}
/**
* Add the object as a parameter to the URL
* @param baseUrl url
* @param obj
* @returns {string}
* eg:
* let obj = {a: '3', b: '4'}
* setObjToUrlParams('www.baidu.com', obj)
* ==>www.baidu.com?a=3&b=4
*/
export function setObjToUrlParams(baseUrl: string, obj: any): string {
let parameters = '';
for (const key in obj) {
parameters += key + '=' + encodeURIComponent(obj[key]) + '&';
}
parameters = parameters.replace(/&$/, '');
return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters;
}
export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
let key: string;
for (key in target) {
// update-begin--author:liaozhiyang---date:20240329---for【QQYUN-7872】online表单label较长优化
if (isObject(src[key]) && isObject(target[key])) {
src[key] = deepMerge(src[key], target[key]);
} else {
// update-begin--author:liaozhiyang---date:20250318---for【issues/7940】componentProps写成函数形式时updateSchema写成对象时参数没合并
try {
if (isFunction(src[key]) && isObject(src[key]()) && isObject(target[key])) {
// src[key]是函数且返回对象且target[key]是对象
src[key] = deepMerge(src[key](), target[key]);
} else if (isObject(src[key]) && isFunction(target[key]) && isObject(target[key]())) {
// target[key]是函数且返回对象且src[key]是对象
src[key] = deepMerge(src[key], target[key]());
} else if (isFunction(src[key]) && isFunction(target[key]) && isObject(src[key]()) && isObject(target[key]())) {
// src[key]是函数且返回对象target[key]是函数且返回对象
src[key] = deepMerge(src[key](), target[key]());
} else {
src[key] = target[key];
}
} catch (error) {
src[key] = target[key];
}
// update-end--author:liaozhiyang---date:20250318---for【issues/7940】componentProps写成函数形式时updateSchema写成对象时参数没合并
}
// update-end--author:liaozhiyang---date:20240329---for【QQYUN-7872】online表单label较长优化
}
return src;
}
export function openWindow(url: string, opt?: { target?: TargetContext | string; noopener?: boolean; noreferrer?: boolean }) {
const { target = '__blank', noopener = true, noreferrer = true } = opt || {};
const feature: string[] = [];
noopener && feature.push('noopener=yes');
noreferrer && feature.push('noreferrer=yes');
window.open(url, target, feature.join(','));
}
// dynamic use hook props
export function getDynamicProps<T, U>(props: T): Partial<U> {
const ret: Recordable = {};
// @ts-ignore
Object.keys(props).map((key) => {
ret[key] = unref((props as Recordable)[key]);
});
return ret as Partial<U>;
}
/**
* 获取表单字段值数据类型
* @param props
* @param field
* @updateBy:zyf
*/
export function getValueType(props, field) {
let formSchema = unref(unref(props)?.schemas);
let valueType = 'string';
if (formSchema) {
let schema = formSchema.filter((item) => item.field === field)[0];
valueType = schema && schema.componentProps && schema.componentProps.valueType ? schema.componentProps.valueType : valueType;
}
return valueType;
}
/**
* 获取表单字段值数据类型
* @param schema
* @param formAction
*/
export function getValueTypeBySchema(schema: FormSchema, formAction: FormActionType) {
let valueType = 'string';
if (schema) {
const componentProps = formAction.getSchemaComponentProps(schema);
valueType = componentProps?.valueType ? componentProps?.valueType : valueType;
}
return valueType;
}
/**
* 通过picker属性获取日期数据
* @param data
* @param picker
*/
export function getDateByPicker(data, picker) {
if (!data || !picker) {
return data;
}
/**
* 需要把年、年月、设置成这段时间内的第一天([年季度]不需要处理antd回传的就是该季度的第一天[年周]也不处理)
* 例如日期格式是年传给数据库的时间必须是20240101
* 例如日期格式是年月选择了202502传给数据库的时间必须是20250201
*/
if (picker === 'year') {
return dayjs(data).set('month', 0).set('date', 1).format('YYYY-MM-DD');
} else if (picker === 'month') {
return dayjs(data).set('date', 1).format('YYYY-MM-DD');
} else if (picker === 'week') {
return dayjs(data).startOf('week').format('YYYY-MM-DD');
}
return data;
}
export function getRawRoute(route: RouteLocationNormalized): RouteLocationNormalized {
if (!route) return route;
const { matched, ...opt } = route;
return {
...opt,
matched: (matched
? matched.map((item) => ({
meta: item.meta,
name: item.name,
path: item.path,
}))
: undefined) as RouteRecordNormalized[],
};
}
/**
* 深度克隆对象、数组
* @param obj 被克隆的对象
* @return 克隆后的对象
*/
export function cloneObject(obj) {
return JSON.parse(JSON.stringify(obj));
}
export const withInstall = <T>(component: T, alias?: string) => {
//console.log("---初始化---", component)
const comp = component as any;
comp.install = (app: App) => {
// @ts-ignore
app.component(comp.name || comp.displayName, component);
if (alias) {
app.config.globalProperties[alias] = component;
}
};
return component as T & Plugin;
};
/**
* 获取url地址参数
* @param paraName
*/
export function getUrlParam(paraName) {
let url = document.location.toString();
let arrObj = url.split('?');
if (arrObj.length > 1) {
let arrPara = arrObj[1].split('&');
let arr;
for (let i = 0; i < arrPara.length; i++) {
arr = arrPara[i].split('=');
if (arr != null && arr[0] == paraName) {
return arr[1];
}
}
return '';
} else {
return '';
}
}
/**
* 休眠setTimeout的promise版
* @param ms 要休眠的时间,单位:毫秒
* @param fn callback可空
* @return Promise
*/
export function sleep(ms: number, fn?: Fn) {
return new Promise<void>((resolve) =>
setTimeout(() => {
fn && fn();
resolve();
}, ms)
);
}
/**
* 不用正则的方式替换所有值
* @param text 被替换的字符串
* @param checker 替换前的内容
* @param replacer 替换后的内容
* @returns {String} 替换后的字符串
*/
export function replaceAll(text, checker, replacer) {
let lastText = text;
text = text.replace(checker, replacer);
if (lastText !== text) {
return replaceAll(text, checker, replacer);
}
return text;
}
/**
* 获取URL上参数
* @param url
*/
export function getQueryVariable(url) {
if (!url) return;
var t,
n,
r,
i = url.split('?')[1],
s = {};
(t = i.split('&')), (r = null), (n = null);
for (var o in t) {
var u = t[o].indexOf('=');
u !== -1 && ((r = t[o].substr(0, u)), (n = t[o].substr(u + 1)), (s[r] = n));
}
return s;
}
/**
* 判断是否显示办理按钮
* @param bpmStatus
* @returns {*}
*/
export function showDealBtn(bpmStatus) {
if (bpmStatus != '1' && bpmStatus != '3' && bpmStatus != '4') {
return true;
}
return false;
}
/**
* 数字转大写
* @param value
* @returns {*}
*/
export function numToUpper(value) {
if (value != '') {
let unit = new Array('仟', '佰', '拾', '', '仟', '佰', '拾', '', '角', '分');
const toDx = (n) => {
switch (n) {
case '0':
return '零';
case '1':
return '壹';
case '2':
return '贰';
case '3':
return '叁';
case '4':
return '肆';
case '5':
return '伍';
case '6':
return '陆';
case '7':
return '柒';
case '8':
return '捌';
case '9':
return '玖';
}
};
let lth = value.toString().length;
// update-begin--author:liaozhiyang---date:20241202---for【issues/7493】numToUpper方法返回解决错误
value = new Big(value).times(100);
// update-end--author:liaozhiyang---date:20241202---for【issues/7493】numToUpper方法返回解决错误
value += '';
let length = value.length;
if (lth <= 8) {
let result = '';
for (let i = 0; i < length; i++) {
if (i == 2) {
result = '元' + result;
} else if (i == 6) {
result = '万' + result;
}
if (value.charAt(length - i - 1) == 0) {
if (i != 0 && i != 1) {
if (result.charAt(0) != '零' && result.charAt(0) != '元' && result.charAt(0) != '万') {
result = '零' + result;
}
}
continue;
}
result = toDx(value.charAt(length - i - 1)) + unit[unit.length - i - 1] + result;
}
result += result.charAt(result.length - 1) == '元' ? '整' : '';
return result;
} else {
return null;
}
}
return null;
}
//update-begin-author:taoyan date:2022-6-8 for:解决老的vue2动态导入文件语法 vite不支持的问题
const allModules = import.meta.glob('../views/**/*.vue');
export function importViewsFile(path): Promise<any> {
if (path.startsWith('/')) {
path = path.substring(1);
}
let page = '';
if (path.endsWith('.vue')) {
page = `../views/${path}`;
} else {
page = `../views/${path}.vue`;
}
return new Promise((resolve, reject) => {
let flag = true;
for (const path in allModules) {
if (path == page) {
flag = false;
allModules[path]().then((mod) => {
console.log(path, mod);
resolve(mod);
});
}
}
if (flag) {
reject('该文件不存在:' + page);
}
});
}
//update-end-author:taoyan date:2022-6-8 for:解决老的vue2动态导入文件语法 vite不支持的问题
/**
* 跳转至积木报表的 预览页面
* @param url
* @param id
* @param token
*/
export function goJmReportViewPage(url, id, token) {
// update-begin--author:liaozhiyang---date:20230904---for【QQYUN-6390】eval替换成new Function解决build警告
// URL支持{{ window.xxx }}占位符变量
url = url.replace(/{{([^}]+)?}}/g, (_s1, s2) => _eval(s2))
// update-end--author:liaozhiyang---date:20230904---for【QQYUN-6390】eval替换成new Function解决build警告
if (url.includes('?')) {
url += '&'
} else {
url += '?'
}
url += `id=${id}`
url += `&token=${token}`
window.open(url)
}
/**
* 获取随机颜色
*/
export function getRandomColor(index?) {
const colors = [
'rgb(100, 181, 246)',
'rgb(77, 182, 172)',
'rgb(255, 183, 77)',
'rgb(229, 115, 115)',
'rgb(149, 117, 205)',
'rgb(161, 136, 127)',
'rgb(144, 164, 174)',
'rgb(77, 208, 225)',
'rgb(129, 199, 132)',
'rgb(255, 138, 101)',
'rgb(133, 202, 205)',
'rgb(167, 214, 118)',
'rgb(254, 225, 89)',
'rgb(251, 199, 142)',
'rgb(239, 145, 139)',
'rgb(169, 181, 255)',
'rgb(231, 218, 202)',
'rgb(252, 128, 58)',
'rgb(254, 161, 172)',
'rgb(194, 163, 205)',
];
return index && index < 19 ? colors[index] : colors[Math.floor((Math.random()*(colors.length-1)))];
}
export function getRefPromise(componentRef) {
return new Promise((resolve) => {
(function next() {
const ref = componentRef.value;
if (ref) {
resolve(ref);
} else {
setTimeout(() => {
next();
}, 100);
}
})();
});
}
/**
* 2023-09-04
* liaozhiyang
* 用new Function替换eval
*/
export function _eval(str: string) {
return new Function(`return ${str}`)();
}
/**
* 2024-04-30
* liaozhiyang
* 通过时间或者时间戳获取对应antd的年、月、周、季度。
*/
export function getWeekMonthQuarterYear(date) {
// 获取 ISO 周数的函数
const getISOWeek = (date) => {
const jan4 = new Date(date.getFullYear(), 0, 4);
const oneDay = 86400000; // 一天的毫秒数
return Math.ceil(((date - jan4.getTime()) / oneDay + jan4.getDay() + 1) / 7);
};
// 将时间戳转换为日期对象
const dateObj = new Date(date);
// 计算周
const week = getISOWeek(dateObj);
// 计算月
const month = dateObj.getMonth() + 1; // 月份是从0开始的所以要加1
// 计算季度
const quarter = Math.floor(dateObj.getMonth() / 3) + 1;
// 计算年
const year = dateObj.getFullYear();
return {
year: `${year}`,
month: `${year}-${month.toString().padStart(2, '0')}`,
week: `${year}-${week}`,
quarter: `${year}-Q${quarter}`,
};
}
/**
* 2024-05-17
* liaozhiyang
* 设置挂载的modal元素有可能会有多个需要找到对应的。
*/
export const setPopContainer = (node, selector) => {
if (typeof selector === 'string') {
const targetEles = Array.from(document.querySelectorAll(selector));
if (targetEles.length > 1) {
const retrospect = (node, elems) => {
let ele = node.parentNode;
while (ele) {
const findParentNode = elems.find(item => item === ele);
if (findParentNode) {
ele = null;
return findParentNode;
} else {
ele = ele.parentNode;
}
}
return null;
};
const elem = retrospect(node, targetEles);
if (elem) {
return elem;
} else {
return document.querySelector(selector);
}
} else {
return document.querySelector(selector);
}
} else {
return selector;
}
};
/**
* 2024-06-14
* liaozhiyang
* 根据控件显示条件
* label、value通用title、val给权限管理用的
*/
export function useConditionFilter() {
// 通用条件
const commonConditionOptions = [
{label: '为空', value: 'empty', val: 'EMPTY'},
{label: '不为空', value: 'not_empty', val: 'NOT_EMPTY'},
]
// 数值、日期
const numberConditionOptions = [
{ label: '等于', value: 'eq', val: '=' },
{ label: '在...中', value: 'in', val: 'IN', title: '包含' },
{ label: '不等于', value: 'ne', val: '!=' },
{ label: '大于', value: 'gt', val: '>' },
{ label: '大于等于', value: 'ge', val: '>=' },
{ label: '小于', value: 'lt', val: '<' },
{ label: '小于等于', value: 'le', val: '<=' },
...commonConditionOptions,
];
// 文本、密码、多行文本、富文本、markdown
const inputConditionOptions = [
{ label: '等于', value: 'eq', val: '=' },
{ label: '模糊', value: 'like', val: 'LIKE' },
{ label: '以..开始', value: 'right_like', title: '右模糊', val: 'RIGHT_LIKE' },
{ label: '以..结尾', value: 'left_like', title: '左模糊', val: 'LEFT_LIKE' },
{ label: '在...中', value: 'in', val: 'IN', title: '包含' },
{ label: '不等于', value: 'ne', val: '!=' },
...commonConditionOptions,
];
// 下拉、单选、多选、开关、用户、部门、关联记录、省市区、popup、popupDict、下拉多选、下拉搜索、分类字典、自定义树
const selectConditionOptions = [
{ label: '等于', value: 'eq', val: '=' },
{ label: '在...中', value: 'in', val: 'IN', title: '包含' },
{ label: '不等于', value: 'ne', val: '!=' },
...commonConditionOptions,
];
const def = [
{ label: '等于', value: 'eq', val: '=' },
{ label: '模糊', value: 'like', val: 'LIKE' },
{ label: '以..开始', value: 'right_like', title: '右模糊', val: 'RIGHT_LIKE' },
{ label: '以..结尾', value: 'left_like', title: '左模糊', val: 'LEFT_LIKE' },
{ label: '在...中', value: 'in', val: 'IN', title: '包含' },
{ label: '不等于', value: 'ne', val: '!=' },
{ label: '大于', value: 'gt', val: '>' },
{ label: '大于等于', value: 'ge', val: '>=' },
{ label: '小于', value: 'lt', val: '<' },
{ label: '小于等于', value: 'le', val: '<=' },
...commonConditionOptions,
];
const filterCondition = (data) => {
if (data.view == 'text' && data.fieldType == 'number') {
data.view = 'number';
}
switch (data.view) {
case 'file':
case 'image':
case 'password':
return commonConditionOptions;
case 'text':
case 'textarea':
case 'umeditor':
case 'markdown':
case 'pca':
case 'popup':
return inputConditionOptions;
case 'list':
case 'radio':
case 'checkbox':
case 'switch':
case 'sel_user':
case 'sel_depart':
case 'link_table':
case 'popup_dict':
case 'list_multi':
case 'sel_search':
case 'cat_tree':
case 'sel_tree':
return selectConditionOptions;
case 'date':
// number是虚拟的
case 'number':
return numberConditionOptions;
default:
return def;
}
};
return { filterCondition };
}
// 获取url中的参数
export const getUrlParams = (url) => {
const result = {
url: '',
params: {},
};
const list = url.split('?');
result.url = list[0];
const params = list[1];
if (params) {
const list = params.split('&');
list.forEach((ele) => {
const dic = ele.split('=');
const label = dic[0];
result.params[label] = dic[1];
});
}
return result;
};
/* 20250325
* liaozhiyang
* 分割url字符成数组
* 【issues/7990】图片参数中包含逗号会错误的识别成多张图
* */
export const split = (str) => {
if (isString(str)) {
const text = str.trim();
if (text.startsWith('http')) {
const parts = str.split(',');
const urls: any = [];
let currentUrl = '';
for (const part of parts) {
if (part.startsWith('http://') || part.startsWith('https://')) {
// 如果遇到新的URL开头保存当前URL并开始新的URL
if (currentUrl) {
urls.push(currentUrl);
}
currentUrl = part;
} else {
// 否则是当前URL的一部分如参数
currentUrl += ',' + part;
}
}
// 添加最后一个URL
if (currentUrl) {
urls.push(currentUrl);
}
return urls;
} else {
return str.split(',');
}
}
return str;
};