123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- import { useGlobSetting } from '/@/hooks/setting';
- import { merge, random } from 'lodash-es';
- import { isArray } from '/@/utils/is';
- import { FormSchema } from '/@/components/Form';
- import { reactive } from 'vue';
- import { getTenantId, getToken } from '/@/utils/auth';
- import { useUserStoreWithOut } from '/@/store/modules/user';
- import { Modal } from 'ant-design-vue';
- import { defHttp } from '@/utils/http/axios';
- const globSetting = useGlobSetting();
- const baseApiUrl = globSetting.domainUrl;
- /**
- * 获取文件服务访问路径
- * @param fileUrl 文件路径
- * @param prefix(默认http) 文件路径前缀 http/https
- */
- export const getFileAccessHttpUrl = (fileUrl, prefix = 'http') => {
- let result = fileUrl;
- try {
- if (fileUrl && fileUrl.length > 0 && !fileUrl.startsWith(prefix)) {
- //判断是否是数组格式
- const isArray = fileUrl.indexOf('[') != -1;
- if (!isArray) {
- const prefix = `${baseApiUrl}/sys/common/static/`;
- // 判断是否已包含前缀
- if (!fileUrl.startsWith(prefix)) {
- result = `${prefix}${fileUrl}`;
- }
- }
- }
- } catch (err) {}
- return result;
- };
- /**
- * 触发 window.resize
- */
- export function triggerWindowResizeEvent() {
- const event: any = document.createEvent('HTMLEvents');
- event.initEvent('resize', true, true);
- event.eventType = 'message';
- window.dispatchEvent(event);
- }
- /**
- * 获取随机数
- * @param length 数字位数
- */
- export const getRandom = (length: number = 1) => {
- return '-' + parseInt(String(Math.random() * 10000 + 1), length);
- };
- /**
- * 随机生成字符串
- * @param length 字符串的长度
- * @param chats 可选字符串区间(只会生成传入的字符串中的字符)
- * @return string 生成的字符串
- */
- export function randomString(length: number, chats?: string) {
- if (!length) length = 1;
- if (!chats) {
- // noinspection SpellCheckingInspection
- chats = '0123456789qwertyuioplkjhgfdsazxcvbnm';
- }
- let str = '';
- for (let i = 0; i < length; i++) {
- const num = random(0, chats.length - 1);
- str += chats[num];
- }
- return str;
- }
- /**
- * 将普通列表数据转化为tree结构
- * @param array tree数据
- * @param opt 配置参数
- * @param startPid 父节点
- */
- export const listToTree = (array, opt, startPid) => {
- const obj = {
- primaryKey: opt.primaryKey || 'key',
- parentKey: opt.parentKey || 'parentId',
- titleKey: opt.titleKey || 'title',
- startPid: opt.startPid || '',
- currentDept: opt.currentDept || 0,
- maxDept: opt.maxDept || 100,
- childKey: opt.childKey || 'children',
- };
- if (startPid) {
- obj.startPid = startPid;
- }
- return toTree(array, obj.startPid, obj.currentDept, obj);
- };
- /**
- * 递归构建tree
- * @param list
- * @param startPid
- * @param currentDept
- * @param opt
- * @returns {Array}
- */
- export const toTree = (array, startPid, currentDept, opt) => {
- if (opt.maxDept < currentDept) {
- return [];
- }
- let child = [];
- if (array && array.length > 0) {
- child = array
- .map((item) => {
- // 筛查符合条件的数据(主键 = startPid)
- if (typeof item[opt.parentKey] !== 'undefined' && item[opt.parentKey] === startPid) {
- // 满足条件则递归
- const nextChild = toTree(array, item[opt.primaryKey], currentDept + 1, opt);
- // 节点信息保存
- if (nextChild.length > 0) {
- item['isLeaf'] = false;
- item[opt.childKey] = nextChild;
- } else {
- item['isLeaf'] = true;
- }
- item['title'] = item[opt.titleKey];
- item['label'] = item[opt.titleKey];
- item['key'] = item[opt.primaryKey];
- item['value'] = item[opt.primaryKey];
- return item;
- }
- })
- .filter((item) => {
- return item !== undefined;
- });
- }
- return child;
- };
- /**
- * 表格底部合计工具方法
- * @param tableData 表格数据
- * @param fieldKeys 要计算合计的列字段
- */
- export function mapTableTotalSummary(tableData: Recordable[], fieldKeys: string[]) {
- const totals: any = { _row: '合计', _index: '合计' };
- fieldKeys.forEach((key) => {
- totals[key] = tableData.reduce((prev, next) => {
- prev += next[key];
- return prev;
- }, 0);
- });
- return totals;
- }
- /**
- * 简单实现防抖方法
- *
- * 防抖(debounce)函数在第一次触发给定的函数时,不立即执行函数,而是给出一个期限值(delay),比如100ms。
- * 如果100ms内再次执行函数,就重新开始计时,直到计时结束后再真正执行函数。
- * 这样做的好处是如果短时间内大量触发同一事件,只会执行一次函数。
- *
- * @param fn 要防抖的函数
- * @param delay 防抖的毫秒数
- * @returns {Function}
- */
- export function simpleDebounce(fn, delay = 100) {
- let timer: any | null = null;
- return function () {
- const args = arguments;
- if (timer) {
- clearTimeout(timer);
- }
- timer = setTimeout(() => {
- // @ts-ignore
- fn.apply(this, args);
- }, delay);
- };
- }
- /**
- * 日期格式化
- * @param date 日期
- * @param block 格式化字符串
- */
- export function dateFormat(date, block) {
- if (!date) {
- return '';
- }
- let format = block || 'yyyy-MM-dd';
- date = new Date(date);
- const map = {
- M: date.getMonth() + 1, // 月份
- d: date.getDate(), // 日
- h: date.getHours(), // 小时
- m: date.getMinutes(), // 分
- s: date.getSeconds(), // 秒
- q: Math.floor((date.getMonth() + 3) / 3), // 季度
- S: date.getMilliseconds(), // 毫秒
- };
- format = format.replace(/([yMdhmsqS])+/g, (all, t) => {
- let v = map[t];
- if (v !== undefined) {
- if (all.length > 1) {
- v = `0${v}`;
- v = v.substr(v.length - 2);
- }
- return v;
- } else if (t === 'y') {
- return date
- .getFullYear()
- .toString()
- .substr(4 - all.length);
- }
- return all;
- });
- return format;
- }
- /**
- * 获取事件冒泡路径,兼容 IE11,Edge,Chrome,Firefox,Safari
- * 目前使用的地方:JVxeTable Span模式
- */
- export function getEventPath(event) {
- const target = event.target;
- const path = (event.composedPath && event.composedPath()) || event.path;
- if (path != null) {
- return path.indexOf(window) < 0 ? path.concat(window) : path;
- }
- if (target === window) {
- return [window];
- }
- const getParents = (node, memo) => {
- const parentNode = node.parentNode;
- if (!parentNode) {
- return memo;
- } else {
- return getParents(parentNode, memo.concat(parentNode));
- }
- };
- return [target].concat(getParents(target, []), window);
- }
- /**
- * 如果值不存在就 push 进数组,反之不处理
- * @param array 要操作的数据
- * @param value 要添加的值
- * @param key 可空,如果比较的是对象,可能存在地址不一样但值实际上是一样的情况,可以传此字段判断对象中唯一的字段,例如 id。不传则直接比较实际值
- * @returns {boolean} 成功 push 返回 true,不处理返回 false
- */
- export function pushIfNotExist(array, value, key?) {
- for (const item of array) {
- if (key && item[key] === value[key]) {
- return false;
- } else if (item === value) {
- return false;
- }
- }
- array.push(value);
- return true;
- }
- /**
- * 过滤对象中为空的属性
- * @param obj
- * @returns {*}
- */
- export function filterObj(obj) {
- if (!(typeof obj == 'object')) {
- return;
- }
- for (const key in obj) {
- if (obj.hasOwnProperty(key) && (obj[key] == null || obj[key] == undefined || obj[key] === '')) {
- delete obj[key];
- }
- }
- return obj;
- }
- /**
- * 下划线转驼峰
- * @param string
- */
- export function underLine2CamelCase(string: string) {
- return string.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
- }
- /**
- * 查找树结构
- * @param treeList
- * @param fn 查找方法
- * @param childrenKey
- */
- export function findTree(treeList: any[], fn: Fn, childrenKey = 'children') {
- for (let i = 0; i < treeList.length; i++) {
- const item = treeList[i];
- if (fn(item, i, treeList)) {
- return item;
- }
- const children = item[childrenKey];
- if (isArray(children)) {
- const findResult = findTree(children, fn, childrenKey);
- if (findResult) {
- return findResult;
- }
- }
- }
- return null;
- }
- /** 获取 mapFormSchema 方法 */
- export function bindMapFormSchema<T>(spanMap, spanTypeDef: T) {
- return function (s: FormSchema, spanType: T = spanTypeDef) {
- return merge(
- {
- disabledLabelWidth: true,
- } as FormSchema,
- spanMap[spanType],
- s
- );
- };
- }
- /**
- * 字符串是否为null或null字符串
- * @param str
- * @return {boolean}
- */
- export function stringIsNull(str) {
- // 两个 == 可以同时判断 null 和 undefined
- return str == null || str === 'null' || str === 'undefined';
- }
- /**
- * 【组件多了可能存在性能问题】获取弹窗div,将下拉框、日期等组件挂载到modal上,解决弹窗遮盖问题
- * @param node
- */
- export function getAutoScrollContainer(node: HTMLElement) {
- let element: Nullable<HTMLElement> = node;
- while (element != null) {
- if (element.classList.contains('scrollbar__view')) {
- // 判断是否有滚动条
- if (element.clientHeight < element.scrollHeight) {
- // 有滚动条时,挂载到父级,解决滚动问题
- return node.parentElement;
- } else {
- // 无滚动条时,挂载到body上,解决下拉框遮盖问题
- return document.body;
- }
- } else {
- element = element.parentElement;
- }
- }
- // 不在弹窗内,走默认逻辑
- return node.parentElement;
- }
- /**
- * 判断子菜单是否全部隐藏
- * @param menuTreeItem
- */
- export function checkChildrenHidden(menuTreeItem) {
- //是否是聚合路由
- const alwaysShow = menuTreeItem.alwaysShow;
- if (alwaysShow) {
- return false;
- }
- if (!menuTreeItem.children) {
- return false;
- }
- return menuTreeItem.children?.find((item) => item.hideMenu == false) != null;
- }
- /**
- * 计算文件大小
- * @param fileSize
- * @param unit
- * @return 返回大小及后缀
- */
- export function calculateFileSize(fileSize, unit?) {
- let unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
- if (unit && unit.length > 0) {
- unitArr = unit;
- }
- let size = fileSize;
- let unitIndex = 0;
- while (size >= 1024 && unitIndex < unitArr.length - 1) {
- size /= 1024;
- unitIndex++;
- }
- //保留两位小数,四舍五入
- size = Math.round(size * 100) / 100;
- return size + unitArr[unitIndex];
- }
- /**
- * 获取上传header
- */
- export function getHeaders() {
- const tenantId = getTenantId();
- return reactive({
- 'X-Access-Token': getToken(),
- 'X-Tenant-Id': tenantId ? tenantId : '0',
- });
- }
- /** 根据表达式获取相应的用户信息 */
- export function getUserInfoByExpression(expression) {
- if (!expression) {
- return expression;
- }
- const userStore = useUserStoreWithOut();
- const userInfo = userStore.getUserInfo;
- if (userInfo) {
- switch (expression) {
- case 'sysUserId':
- return userInfo.id;
- // 当前登录用户登录账号
- case 'sysUserCode':
- case 'sys_user_code':
- return userInfo.username;
- // 当前登录用户真实名称
- case 'sysUserName':
- return userInfo.realname;
- // 当前登录用户部门编号
- case 'sysOrgCode':
- case 'sys_org_code':
- return userInfo.orgCode;
- }
- }
- return expression;
- }
- /**
- * 替换表达式(#{xxx})为用户信息
- * @param expression
- */
- export function replaceUserInfoByExpression(expression: string | any[]) {
- if (!expression) {
- return expression;
- }
- const isString = typeof expression === 'string';
- const isArray = Array.isArray(expression);
- if (!isString && !isArray) {
- return expression;
- }
- const reg = /#{(.*?)}/g;
- const replace = (str) => {
- if (typeof str !== 'string') {
- return str;
- }
- const result = str.match(reg);
- if (result && result.length > 0) {
- result.forEach((item) => {
- const userInfo = getUserInfoByExpression(item.substring(2, item.length - 1));
- str = str.replace(item, userInfo);
- });
- }
- return str;
- };
- // @ts-ignore
- return isString ? replace(expression) : expression.map(replace);
- }
- /**
- * 设置租户缓存,当租户退出的时候
- *
- * @param tenantId
- */
- export async function userExitChangeLoginTenantId(tenantId) {
- const userStore = useUserStoreWithOut();
- //step 1 获取用户租户
- const url = '/sys/tenant/getCurrentUserTenant';
- let currentTenantId = null;
- const data = await defHttp.get({ url });
- if (data && data.list) {
- const arr = data.list;
- if (arr.length > 0) {
- //step 2.判断当前id是否存在用户租户中
- const filterTenantId = arr.filter((item) => item.id == tenantId);
- //存在说明不是退出的不是当前租户,还用用来的租户即可
- if (filterTenantId && filterTenantId.length > 0) {
- currentTenantId = tenantId;
- } else {
- //不存在默认第一个
- currentTenantId = arr[0].id;
- }
- }
- }
- userStore.setTenant(currentTenantId);
- //切换租户后要刷新首页
- window.location.reload();
- }
- /**
- * 我的租户模块需要开启多租户提示
- *
- * @param title 标题
- */
- export function tenantSaasMessage(title) {
- const tenantId = getTenantId();
- if (!tenantId) {
- Modal.confirm({
- title: title,
- content: '此菜单需要在多租户模式下使用,否则数据会出现混乱',
- okText: '确认',
- okType: 'danger',
- // @ts-ignore
- cancelButtonProps: { style: { display: 'none' } },
- });
- }
- }
|