Initial commit

This commit is contained in:
南宁网宿科技
2024-04-24 16:36:46 +08:00
commit 121348e011
991 changed files with 158700 additions and 0 deletions
+322
View File
@@ -0,0 +1,322 @@
import { message, SelectProps } from 'ant-design-vue';
import { isExternalLink, random, toDateString } from 'ele-admin-pro';
import router from '@/router';
import { listDictionaryData } from '@/api/system/dictionary-data';
import { ref } from 'vue';
import { getJson } from '@/api/json';
import { APP_SECRET, FILE_SERVER } from '@/config/setting';
import mitt from 'mitt';
import { ChatMessage } from '@/api/system/chat/model';
import { useUserStore } from '@/store/modules/user';
import CryptoJS from 'crypto-js';
type Events = {
message: ChatMessage;
};
export const emitter = mitt<Events>();
/**
* 常用函数封装
*/
// 生成编号
export function createCode(): string {
const data = new Date();
const code = `${data.getFullYear()}${data.getMonth()}${data.getDate()}${data.getHours()}${data.getMilliseconds()}`;
return code.slice(0);
}
// 生成商户编号
export function createMerchantCode(): string {
const data = new Date();
const code = `${data.getFullYear()}${data.getMonth()}${data.getDate()}${data.getSeconds()}`;
return code.slice(3);
}
// 生成订单编号
export function createOrderNo(): string {
const data = new Date();
const code = `${data.getFullYear()}${data.getMonth()}${data.getDate()}${data.getHours()}${data.getMilliseconds()}${random(
8000,
12000
)}`;
return code.slice(0);
}
// 跳转页面函数
export function openUrl(url: string, params?: any): void {
const isExternal = isExternalLink(url);
if (isExternal) {
window.open(url);
} else {
if (params) {
router.push({ path: url, query: params });
} else {
router.push(url);
}
}
}
/**
* 弹出新窗口
* @param url
* @constructor
*/
export function openNew(url: string) {
if (url.slice(0, 4) == 'http') {
return window.open(url);
}
window.open(`http://${url}`);
}
/**
* 预览地址
* @param url
* @constructor
*/
export function openPreview(url: string) {
if (url.slice(0, 4) == 'http') {
return window.open(url);
}
window.open(getDomainPath(url));
}
/**
* 获取网站域名
* @param path
*/
export const getDomainPath = (path: string) => {
const domain = localStorage.getItem('Domain');
return domain + path;
};
// 预览云存储文件
export function getUrl(url: string) {
const isExternal = isExternalLink(url);
// const uploadMethod = localStorage.getItem('UploadMethod');
// const bucketDomain = localStorage.getItem('BucketDomain');
// if (uploadMethod == 'oss') {
// return bucketDomain + url;
// }
if (!isExternal) {
return FILE_SERVER + url;
}
return url;
}
// 跳转页面(不弹窗)
export function navTo(url: string): void {
window.location.href = url;
}
// 手机号脱敏
export function getMobile(tel: string) {
const reg = /^(\d{3})\d{4}(\d{4})$/;
return tel.replace(reg, '$1****$2');
}
// 复制文本
export const copyText = (text) => {
// 模拟 输入框
const cInput = document.createElement('input');
cInput.value = text;
document.body.appendChild(cInput);
cInput.select(); // 选取文本框内容
// 执行浏览器复制命令
// 复制命令会将当前选中的内容复制到剪切板中(这里就是创建的input标签)
// Input要在正常的编辑状态下原生复制方法才会生效
message.success('复制成功');
document.execCommand('copy');
// 复制成功后再将构造的标签 移除
document.body.removeChild(cInput);
};
/**
* 计算剩余时间
* @param endTime
*/
export const getEndTime = (endTime) => {
const setTime = new Date(endTime);
const nowTime = new Date();
const restSec = setTime.getTime() - nowTime.getTime();
// 剩余天数
const lastDay = parseInt(String(restSec / (60 * 60 * 24 * 1000) + 1));
// let lastHour = parseInt(String((restSec / (60 * 60 * 1000)) % 24));
// let lastMinu = parseInt(String((restSec / (60 * 1000)) % 60));
// let lastSec = parseInt(String((restSec / 1000) % 60));
// 过期状态
if (lastDay < 30 && lastDay > 0) {
return `<div class="ele-text-warning">${toDateString(
endTime,
'yyyy-MM-dd'
)}(${lastDay}天后过期)</div>`;
}
if (lastDay < 0) {
return `<div class="ele-text-danger">${toDateString(
endTime,
'yyyy-MM-dd'
)}(已过期)</div>`;
}
return `<div class="ele-text-info">${toDateString(
endTime,
'yyyy-MM-dd'
)}</div>`;
};
/**
* 获取字典数据作为下拉选项数据
* @param dictCode
*/
export const getDictionaryOptions = (dictCode) => {
const dictOptions = ref<SelectProps['options']>([]);
// const key = dictCode + ':' + localStorage.getItem('TenantId');
// const storageData = localStorage.getItem(key);
listDictionaryData({
dictCode
})
.then((list) => {
// 获取远程字典数据
if (list.length > 0) {
dictOptions.value = list.map((d) => {
return {
key: d.dictDataCode,
value: d.dictDataName,
label: d.dictDataName,
comments: d.comments
};
});
} else {
// 未定义则取默认的json数据
dictOptions.value = getJson(dictCode);
}
})
.catch((e) => {
message.error(e.message);
});
// if (!storageData) {
// listDictionaryData({
// dictCode
// })
// .then((list) => {
// // 获取远程字典数据
// if (list.length > 0) {
// dictOptions.value = list.map((d) => {
// return {
// value: d.dictDataCode,
// label: d.dictDataName,
// text: d.dictDataName,
// comments: d.comments
// };
// });
// // 写入缓存
// localStorage.setItem(key, JSON.stringify(dictOptions.value));
// } else {
// // 未定义则取默认的json数据
// dictOptions.value = getJson(dictCode);
// }
// })
// .catch((e) => {
// message.error(e.message);
// });
// } else {
// dictOptions.value = JSON.parse(storageData);
// }
return <any>dictOptions;
};
// 判断是否为图片
/*
* @param: fileName - 文件名称
*/
export const isImage = (fileName) => {
const split = fileName.split('?');
// 后缀获取
let suffix = '';
try {
const flieArr = split[0].split('.');
suffix = flieArr[flieArr.length - 1];
} catch (err) {
suffix = '';
}
const imgList = ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp'];
return imgList.some((item) => {
return item == suffix;
});
};
export const getWeek = (text) => {
const week = [
'星期日',
'星期一',
'星期二',
'星期三',
'星期四',
'星期五',
'星期六'
];
return week[text];
};
/**
* 文件大小转换
* @param text
*/
export const getFileSize = (text) => {
if (text < 1024) {
return text + 'B';
} else if (text < 1024 * 1024) {
return (text / 1024).toFixed(1) + 'KB';
} else if (text < 1024 * 1024 * 1024) {
return (text / 1024 / 1024).toFixed(1) + 'M';
} else {
return (text / 1024 / 1024 / 1024).toFixed(1) + 'G';
}
};
/* 原图转缩列图 */
export const thumbnail = (url) => {
if (url.indexOf('/thumbnail') < 0) {
return url.replace(FILE_SERVER, FILE_SERVER + '/thumbnail');
}
return url;
};
/* 缩列转图原图 */
export const original = (url) => {
if (url.indexOf('/thumbnail') == 0) {
return url.replace('/thumbnail', '');
}
return url;
};
export const getCompanyInfo = () => {
const user = useUserStore();
if (user.info?.companyInfo) {
return user.info?.companyInfo;
}
return null;
};
export const getVersion = () => {
const companyInfo = getCompanyInfo();
if (companyInfo?.version) {
return companyInfo?.version;
}
return null;
};
// AES加密
export const encrypt = (text) => {
return CryptoJS.AES.encrypt(text, APP_SECRET).toString();
};
// AES解密
export const decrypt = (encrypt) => {
CryptoJS.AES.decrypt(encrypt, APP_SECRET);
const bytes = CryptoJS.AES.decrypt(encrypt, APP_SECRET);
return bytes.toString(CryptoJS.enc.Utf8);
};
+71
View File
@@ -0,0 +1,71 @@
import { watch } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import type { RouteLocationNormalizedLoaded } from 'vue-router';
import {
routeI18nKey,
findTabByPath
} from 'ele-admin-pro/es/ele-pro-layout/util';
import { storeToRefs } from 'pinia';
import { useThemeStore } from '@/store/modules/theme';
import { PROJECT_NAME, REDIRECT_PATH, I18N_ENABLE } from '@/config/setting';
/**
* 修改浏览器标题
* @param title 标题
*/
export function setDocumentTitle(title: string) {
const names: string[] = [];
const tenantName = localStorage.getItem('TenantName');
if (title) {
names.push(title);
}
if (tenantName) {
names.push(tenantName);
} else {
names.push(PROJECT_NAME);
}
document.title = names.join(' - ');
}
/**
* 路由切换更新浏览器标题
*/
export function useSetDocumentTitle() {
const { currentRoute } = useRouter();
const { t, locale } = useI18n();
const themeStore = useThemeStore();
const { tabs } = storeToRefs(themeStore);
const updateTitle = (route: RouteLocationNormalizedLoaded) => {
const { path, meta, fullPath } = route;
if (path.includes(REDIRECT_PATH)) {
return;
}
const pathKey = routeI18nKey(path);
if (!pathKey) {
return;
}
const tab = findTabByPath(fullPath, tabs.value);
const title = tab?.title || (meta?.title as string);
if (!I18N_ENABLE) {
setDocumentTitle(title);
return;
}
const k = `route.${pathKey}._name`;
const v = t(k);
setDocumentTitle(v === k || !v ? title : v);
};
watch(
currentRoute,
(route) => {
updateTitle(route);
},
{ immediate: true }
);
watch(locale, () => {
updateTitle(currentRoute.value);
});
}
+98
View File
@@ -0,0 +1,98 @@
import { isNumber } from 'ele-admin-pro';
// 解析域名结构
export function getHost(): any {
const host = window.location.host;
return host.split('.');
}
// 是否https
export function isHttps() {
const protocol = window.location.protocol;
if (protocol == 'https:') {
return true;
}
return false;
}
/**
* 获取原始域名
* @return http://www.domain.com
*/
export function getOriginDomain(): string {
return window.origin;
}
/**
* 域名的第一部分
* 获取tenantId
* @return 10140
*/
export function getDomainPart1(): any {
const split = getHost();
if (split[0] == '127') {
return undefined;
}
if (isNumber(split[0])) {
return split[0];
}
return undefined;
}
/**
* 通过解析泛域名获取租户ID
* https://10140.wsdns.cn
* @return 10140
*/
export function getTenantId() {
return getDomainPart1();
}
/**
* 获取域名
* @return https://www.domain.com
*/
export function getDomain(): string {
return window.location.protocol + '//www.' + getRootDomain();
}
/**
* 获取根域名
* abc.com
*/
export function getRootDomain(): string {
const split = getHost();
return split[split.length - 2] + '.' + split[split.length - 1];
}
/**
* 获取二级域名
* @return abc.com
*/
export function getSubDomainPath(): string {
const split = getHost();
if (split.length == 2) {
return '';
}
return split[split.length - 3];
}
/**
* 获取产品标识
* @return 10048
*/
export function getProductCode(): string | null {
const subDomain = getSubDomainPath();
if (subDomain == undefined) {
return null;
}
const split = subDomain.split('-');
return split[0];
}
/**
* 控制台域名
*/
export function navSubDomain(path): string {
return `${window.location.protocol}//${path}.${getRootDomain()}`;
}
+47
View File
@@ -0,0 +1,47 @@
import { ref } from 'vue';
import TinymceEditor from '@/components/TinymceEditor/index.vue';
const editorRef = ref<InstanceType<typeof TinymceEditor> | null>(null);
// 编辑器配置信息
export function editorConfig(height?: 500): void {
const resultData = ref<any>({
height: height,
// 自定义文件上传(这里使用把选择的文件转成 blob 演示)
file_picker_callback: (callback: any, _value: any, meta: any) => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
// 设定文件可选类型
if (meta.filetype === 'image') {
input.setAttribute('accept', 'image/*');
} else if (meta.filetype === 'media') {
input.setAttribute('accept', 'video/*');
}
input.onchange = () => {
const file = input.files?.[0];
if (!file) {
return;
}
if (meta.filetype === 'media') {
if (!file.type.startsWith('video/')) {
editorRef.value?.alert({ content: '只能选择视频文件' });
return;
}
}
if (file.size / 1024 / 1024 > 20) {
editorRef.value?.alert({ content: '大小不能超过 20MB' });
return;
}
const reader = new FileReader();
reader.onload = (e) => {
if (e.target?.result != null) {
const blob = new Blob([e.target.result], { type: file.type });
callback(URL.createObjectURL(blob));
}
};
reader.readAsArrayBuffer(file);
};
input.click();
}
});
return resultData;
}
+18
View File
@@ -0,0 +1,18 @@
/**
* 监听屏幕尺寸改变封装
*/
import { watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useThemeStore } from '@/store/modules/theme';
export function onSizeChange(hook: Function) {
if (!hook) {
return;
}
const themeStore = useThemeStore();
const { contentWidth } = storeToRefs(themeStore);
watch(contentWidth, () => {
hook();
});
}
+43
View File
@@ -0,0 +1,43 @@
import OSS from 'ali-oss';
const baseUrl = 'http://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com';
export const uploadAliOss = async (file, options) => {
// 构建上传文件参数
// 获取上传文件所需要的STS Token
// 直接通过node.js上传
// console.log(token)
const client = new OSS({
region: 'oss-cn-shenzhen',
accessKeyId: 'LTAI4GKGZ9Z2Z8JZ77c3GNZP',
accessKeySecret: 'BiDkpS7UXj72HWwDWaFZxiXjNFBNCM',
bucket: 'oss-aishangjia',
secure: true
});
const headers = {
// 指定该Object被下载时的网页缓存行为。
'Cache-Control': 'no-cache',
// 指定该Object被下载时的名称。
// 指定该Object被下载时的内容编码格式。
'Content-Encoding': 'utf-8',
// 指定过期时间,单位为毫秒。
Expires: '1000',
// 指定Object的存储类型。
'x-oss-storage-class': 'Standard',
// 指定初始化分片上传时是否覆盖同名Object。此处设置为true,表示禁止覆盖同名Object。
'x-oss-forbid-overwrite': 'true'
};
const suffix = file.name.substring(file.name.lastIndexOf('.')); // .txt
const objectName = Date.now() + suffix;
// object-name可以自定义为文件名(例如file.txt)或目录(例如abc/test/file.txt)的形式,实现将文件上传至当前Bucket或Bucket下的指定目录。
const result = await client.multipartUpload(objectName, file, {
...options,
parallel: 4,
partSize: 1024 * 1024 * 5
});
result.url = `${baseUrl}/${result.name}`;
return Promise.resolve(result);
};
+254
View File
@@ -0,0 +1,254 @@
/**
* 页签操作封装
*/
import { unref } from 'vue';
import type { RouteLocationNormalizedLoaded } from 'vue-router';
import type { TabItem, TabRemoveOption } from 'ele-admin-pro/es';
import { message } from 'ant-design-vue/es';
import router from '@/router';
import { useThemeStore } from '@/store/modules/theme';
import type { RouteReloadOption } from '@/store/modules/theme';
import { removeToken } from '@/utils/token-util';
import { setDocumentTitle } from '@/utils/document-title-util';
import {
HOME_PATH,
LAYOUT_PATH,
REDIRECT_PATH,
REPEATABLE_TABS
} from '@/config/setting';
const HOME_ROUTE = HOME_PATH || LAYOUT_PATH;
const BASE_URL = import.meta.env.BASE_URL;
/**
* 刷新页签参数类型
*/
export interface TabReloadOptions {
// 是否是主页
isHome?: boolean;
// 路由地址
fullPath?: string;
}
/**
* 刷新当前路由
*/
export function reloadPageTab(option?: TabReloadOptions) {
if (!option) {
// 刷新当前路由
const { path, fullPath, query } = unref(router.currentRoute);
if (path.includes(REDIRECT_PATH)) {
return;
}
const isHome = isHomeRoute(unref(router.currentRoute));
setRouteReload({
reloadHome: isHome,
reloadPath: isHome ? void 0 : fullPath
});
router.replace({
path: REDIRECT_PATH + path,
query
});
} else {
// 刷新指定页签
const { fullPath, isHome } = option;
setRouteReload({
reloadHome: isHome,
reloadPath: isHome ? void 0 : fullPath
});
router.replace(REDIRECT_PATH + fullPath);
}
}
/**
* 关闭当前页签
*/
export function finishPageTab() {
const key = getRouteTabKey();
removePageTab({ key, active: key });
}
/**
* 关闭页签
*/
export function removePageTab(option: TabRemoveOption) {
useThemeStore()
.tabRemove(option)
.then(({ path, home }) => {
if (path) {
router.push(path);
} else if (home) {
router.push(HOME_ROUTE);
}
})
.catch(() => {
message.error('当前页签不可关闭');
});
}
/**
* 关闭左侧页签
*/
export function removeLeftPageTab(option: TabRemoveOption) {
useThemeStore()
.tabRemoveLeft(option)
.then(({ path }) => {
if (path) {
router.push(path);
}
})
.catch(() => {
message.error('左侧没有可关闭的页签');
});
}
/**
* 关闭右侧页签
*/
export function removeRightPageTab(option: TabRemoveOption) {
useThemeStore()
.tabRemoveRight(option)
.then(({ path, home }) => {
if (path) {
router.push(path);
} else if (home) {
router.push(HOME_ROUTE);
}
})
.catch(() => {
message.error('右侧没有可关闭的页签');
});
}
/**
* 关闭其它页签
*/
export function removeOtherPageTab(option: TabRemoveOption) {
useThemeStore()
.tabRemoveOther(option)
.then(({ path, home }) => {
if (path) {
router.push(path);
} else if (home) {
router.push(HOME_ROUTE);
}
})
.catch(() => {
message.error('没有可关闭的页签');
});
}
/**
* 关闭全部页签
* @param active 当前选中页签
*/
export function removeAllPageTab(active: string) {
useThemeStore()
.tabRemoveAll(active)
.then(({ home }) => {
if (home) {
router.push(HOME_ROUTE);
}
})
.catch(() => {
message.error('没有可关闭的页签');
});
}
/**
* 登录成功后清空页签
*/
export function cleanPageTabs() {
useThemeStore().setTabs([]);
}
/**
* 添加页签
* @param data 页签数据
*/
export function addPageTab(data: TabItem | TabItem[]) {
useThemeStore().tabAdd(data);
}
/**
* 修改页签
* @param data 页签数据
*/
export function setPageTab(data: TabItem) {
useThemeStore().tabSetItem(data);
}
/**
* 修改页签标题
* @param title 标题
*/
export function setPageTabTitle(title: string) {
setPageTab({ key: getRouteTabKey(), title });
setDocumentTitle(title);
}
/**
* 获取当前路由对应的页签 key
*/
export function getRouteTabKey() {
const { path, fullPath, meta } = unref(router.currentRoute);
const isUnique = meta.tabUnique === false || REPEATABLE_TABS.includes(path);
return isUnique ? fullPath : path;
}
/**
* 设置主页的组件名称
* @param components 组件名称
*/
export function setHomeComponents(components: string[]) {
useThemeStore().setHomeComponents(components);
}
/**
* 设置路由刷新信息
* @param option 路由刷新参数
*/
export function setRouteReload(option: RouteReloadOption | null) {
return useThemeStore().setRouteReload(option);
}
/**
* 判断路由是否是主页
* @param route 路由信息
*/
export function isHomeRoute(route: RouteLocationNormalizedLoaded) {
const { path, matched } = route;
if (HOME_ROUTE === path) {
return true;
}
return (
matched[0] &&
matched[0].path === LAYOUT_PATH &&
matched[0].redirect === path
);
}
/**
* 登录成功后跳转首页
* @param from 登录前的地址
*/
export function goHomeRoute(from?: string) {
router.replace(from || HOME_ROUTE);
}
/**
* 退出登录
* @param route 是否使用路由跳转
* @param from 登录后跳转的地址
*/
export function logout(route?: boolean, from?: string) {
removeToken();
if (route) {
router.push({
path: '/login',
query: from ? { from } : void 0
});
} else {
// 这样跳转避免再次登录重复注册动态路由
location.replace(BASE_URL + 'login' + (from ? '?from=' + from : ''));
}
}
+119
View File
@@ -0,0 +1,119 @@
/**
* 按钮级权限控制
*/
import type { App } from 'vue';
import { useUserStore } from '@/store/modules/user';
/* 判断数组是否有某些值 */
function arrayHas(
array: (string | undefined)[],
value: string | string[]
): boolean {
if (!value) {
return true;
}
if (!array) {
return false;
}
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
if (array.indexOf(value[i]) === -1) {
return false;
}
}
return true;
}
return array.indexOf(value) !== -1;
}
/* 判断数组是否有任意值 */
function arrayHasAny(
array: (string | undefined)[],
value: string | string[]
): boolean {
if (!value) {
return true;
}
if (!array) {
return false;
}
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
if (array.indexOf(value[i]) !== -1) {
return true;
}
}
return false;
}
return array.indexOf(value) !== -1;
}
/**
* 是否有某些角色
* @param value 角色字符或字符数组
*/
export function hasRole(value: string | string[]): boolean {
const userStore = useUserStore();
return arrayHas(userStore?.roles, value);
}
/**
* 是否有任意角色
* @param value 角色字符或字符数组
*/
export function hasAnyRole(value: string | string[]): boolean {
const userStore = useUserStore();
return arrayHasAny(userStore?.roles, value);
}
/**
* 是否有某些权限
* @param value 权限字符或字符数组
*/
export function hasPermission(value: string | string[]): boolean {
const userStore = useUserStore();
return arrayHas(userStore?.authorities, value);
}
/**
* 是否有任意权限
* @param value 权限字符或字符数组
*/
export function hasAnyPermission(value: string | string[]): boolean {
const userStore = useUserStore();
return arrayHasAny(userStore?.authorities, value);
}
export default {
install(app: App) {
// 添加自定义指令
app.directive('role', {
mounted: (el, binding) => {
if (!hasRole(binding.value)) {
el.parentNode?.removeChild(el);
}
}
});
app.directive('any-role', {
mounted: (el, binding) => {
if (!hasAnyRole(binding.value)) {
el.parentNode?.removeChild(el);
}
}
});
app.directive('permission', {
mounted: (el, binding) => {
if (!hasPermission(binding.value)) {
el.parentNode?.removeChild(el);
}
}
});
app.directive('any-permission', {
mounted: (el, binding) => {
if (!hasAnyPermission(binding.value)) {
el.parentNode?.removeChild(el);
}
}
});
}
};
+52
View File
@@ -0,0 +1,52 @@
import { createVNode } from 'vue';
import { Company } from '@/api/system/company/model';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { messageLoading } from 'ele-admin-pro';
import { clone } from '@/api/system/menu';
import useFormData from '@/utils/use-form-data';
import { Menu } from '@/api/system/menu/model';
// 表单数据
const { form } = useFormData<Menu>({
title: '',
icon: '',
path: '',
component: '',
tenantId: undefined,
tenantName: ''
});
/**
* 一键克隆
* @param item
*/
export const onClone = (item: Company) => {
const tenantId = Number(localStorage.getItem('TenantId'));
if (tenantId == item.tenantId) {
message.error('不能克隆自己');
return false;
}
// 提交状态
Modal.confirm({
title: '确认操作吗?',
content: `将复制【${item.tenantName}】的所有菜单和权限(不含数据),原有企业数据不会删除。`,
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = messageLoading('模块安装中请稍等...', 0);
form.tenantId = item.tenantId;
clone(form)
.then((msg) => {
hide();
message.success(msg);
setTimeout(() => {
window.open('/', '_self');
}, 1000);
})
.catch((e) => {
message.error(e.message);
});
}
});
};
+91
View File
@@ -0,0 +1,91 @@
/**
* axios 实例
*/
import axios from 'axios';
import type { AxiosResponse } from 'axios';
import { unref } from 'vue';
import router from '@/router';
import { Modal } from 'ant-design-vue';
import { API_BASE_URL, TOKEN_HEADER_NAME, LAYOUT_PATH } from '@/config/setting';
import { getToken, setToken } from './token-util';
import { logout } from './page-tab-util';
import type { ApiResult } from '@/api';
import { getTenantId } from '@/utils/domain';
const service = axios.create({
baseURL: API_BASE_URL
});
/**
* 添加请求拦截器
*/
service.interceptors.request.use(
(config) => {
const TENANT_ID = localStorage.getItem('TenantId');
const token = getToken();
// 添加 token 到 header
if (token && config.headers) {
config.headers.common[TOKEN_HEADER_NAME] = token;
}
// 获取租户ID
if (config.headers) {
// 附加企业ID
const companyId = localStorage.getItem('CompanyId');
if (companyId) {
config.headers.common['CompanyId'] = companyId;
}
// 解析二级域名获取租户ID
if (getTenantId()) {
config.headers.common['TenantId'] = getTenantId();
console.log('domain');
return config;
}
if (TENANT_ID) {
config.headers.common['TenantId'] = TENANT_ID;
console.log('TENANT_ID');
return config;
}
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
/**
* 添加响应拦截器
*/
service.interceptors.response.use(
(res: AxiosResponse<ApiResult<unknown>>) => {
// 登录过期处理
if (res.data?.code === 401) {
const currentPath = unref(router.currentRoute).path;
if (currentPath == LAYOUT_PATH) {
logout(true);
} else {
Modal.destroyAll();
Modal.info({
title: '系统提示',
content: '登录状态已过期, 请退出重新登录!',
okText: '重新登录',
onOk: () => {
logout(false, currentPath);
}
});
}
return Promise.reject(new Error(res.data.message));
}
// token 自动续期
const token = res.headers[TOKEN_HEADER_NAME.toLowerCase()];
if (token) {
setToken(token);
}
return res;
},
(error) => {
return Promise.reject(error);
}
);
export default service;
+71
View File
@@ -0,0 +1,71 @@
/**
* token 操作封装
*/
import { APP_SECRET, TOKEN_STORE_NAME } from '@/config/setting';
import md5 from 'js-md5';
/**
* 获取缓存的 token
*/
export function getToken(): string | null {
const token = localStorage.getItem(TOKEN_STORE_NAME);
if (!token) {
return sessionStorage.getItem(TOKEN_STORE_NAME);
}
return token;
}
/**
* 缓存 token
* @param token token
* @param remember 是否永久存储
*/
export function setToken(token?: string, remember?: boolean) {
removeToken();
if (token) {
if (remember) {
localStorage.setItem(TOKEN_STORE_NAME, token);
} else {
sessionStorage.setItem(TOKEN_STORE_NAME, token);
}
}
}
/**
* 移除 token
*/
export function removeToken() {
localStorage.removeItem(TOKEN_STORE_NAME);
sessionStorage.removeItem(TOKEN_STORE_NAME);
localStorage.clear();
}
// 封装签名
export function getSign(form) {
if (form == null) {
return false;
}
let sign = '';
form.timestamp = new Date().getTime();
form.version = 'v3';
const arr = objKeySort(form);
Object.keys(arr).forEach((k) => {
if (form[k] != null && form[k] != '') {
sign = sign.concat(form[k]).concat('-');
}
});
sign = sign.concat(APP_SECRET);
return md5(sign);
}
// 参数按照字母顺序排序
export const objKeySort = (obj) => {
//排序的函数
const newkey = Object.keys(obj).sort();
//先用Object内置类的keys方法获取要排序对象的属性名,再利用Array原型上的sort方法对获取的属性名进行排序,newkey是一个数组
const newObj = {}; //创建一个新的对象,用于存放排好序的键值对
for (let i = 0; i < newkey.length; i++) {
//遍历newkey数组
newObj[newkey[i]] = obj[newkey[i]]; //向新创建的对象中按照排好的顺序依次增加键值对
}
return newObj; //返回排好序的新对象
};
+76
View File
@@ -0,0 +1,76 @@
/**
* echarts 自动切换主题、重置尺寸封装
*/
import type { Ref } from 'vue';
import {
ref,
reactive,
unref,
provide,
watch,
onActivated,
onDeactivated,
nextTick
} from 'vue';
import { storeToRefs } from 'pinia';
import { THEME_KEY } from 'vue-echarts';
import type VChart from 'vue-echarts';
import { ChartTheme, ChartThemeDark } from 'ele-admin-pro/es';
import { useThemeStore } from '@/store/modules/theme';
import { onSizeChange } from './on-size-change';
export default function (chartRefs: Ref<InstanceType<typeof VChart> | null>[]) {
// 当前框架是否是暗黑主题
const themeStore = useThemeStore();
const { darkMode } = storeToRefs(themeStore);
// 是否为 deactivated 状态
const deactivated = ref<boolean>(false);
// 当前图表是否是暗黑主题
const isDark = ref<boolean>(unref(darkMode));
// 当前图表主题
const chartsTheme = reactive({
...(unref(isDark) ? ChartThemeDark : ChartTheme)
});
// 设置图表主题
provide(THEME_KEY, chartsTheme);
/* 重置图表尺寸 */
const resizeCharts = () => {
chartRefs.forEach((chartRef) => {
unref(chartRef)?.resize();
});
};
/* 屏幕尺寸变化监听 */
onSizeChange(() => {
resizeCharts();
});
/* 更改图表主题 */
const changeTheme = (dark: boolean) => {
isDark.value = dark;
Object.assign(chartsTheme, dark ? ChartThemeDark : ChartTheme);
};
onActivated(() => {
deactivated.value = false;
nextTick(() => {
if (unref(isDark) !== unref(darkMode)) {
changeTheme(unref(darkMode));
} else {
resizeCharts();
}
});
});
onDeactivated(() => {
deactivated.value = true;
});
watch(darkMode, (dark) => {
if (!unref(deactivated)) {
changeTheme(dark);
}
});
}
+29
View File
@@ -0,0 +1,29 @@
import { reactive } from 'vue';
/**
* 表单数据 hook
* @param initValue 默认值
*/
export default function <T extends object>(initValue?: T) {
const form = reactive<T>({ ...initValue } as T);
const resetFields = () => {
Object.keys(form).forEach((key) => {
form[key] = initValue ? initValue[key] : void 0;
});
};
const assignFields = (data: object) => {
Object.keys(form).forEach((key) => {
form[key] = data[key];
});
};
return {
form,
// 重置为初始值
resetFields,
// 赋值不改变字段
assignFields
};
}
+27
View File
@@ -0,0 +1,27 @@
import { reactive } from 'vue';
/**
* 搜索表单 hook
* @param initValue 默认值
*/
export default function <T extends object>(initValue?: T) {
const where = reactive<T>({ ...initValue } as T);
const resetFields = () => {
Object.keys(where).forEach((key) => {
where[key] = initValue ? initValue[key] : void 0;
});
};
const assignFields = (data: object) => {
Object.keys(where).forEach((key) => {
where[key] = data[key];
});
};
return {
where,
resetFields,
assignFields
};
}