初始版本提交
This commit is contained in:
28
src/logics/desktop/setupDesktop.ts
Normal file
28
src/logics/desktop/setupDesktop.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { App } from 'vue';
|
||||
import VueGridLayout from 'vue-grid-layout';
|
||||
|
||||
export function setupDesktop(app: App) {
|
||||
app.use(VueGridLayout);
|
||||
|
||||
const requireComponent = import.meta.glob('../../views/desktop/components/*/*.vue', {
|
||||
eager: true,
|
||||
});
|
||||
|
||||
Object.keys(requireComponent).forEach((key) => {
|
||||
if (!requireComponent[key].default.name) return;
|
||||
|
||||
if (requireComponent[key].default.__file.includes('config')) {
|
||||
app.component(
|
||||
requireComponent[key].default.name.toLowerCase() + '-config',
|
||||
requireComponent[key].default,
|
||||
);
|
||||
} else {
|
||||
app.component(
|
||||
requireComponent[key].default.name.toLowerCase() + '-view',
|
||||
requireComponent[key].default,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(app);
|
||||
}
|
||||
184
src/logics/error-handle/index.ts
Normal file
184
src/logics/error-handle/index.ts
Normal file
@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Used to configure the global error handling function, which can monitor vue errors, script errors, static resource errors and Promise errors
|
||||
*/
|
||||
|
||||
import type { ErrorLogInfo } from '/#/store';
|
||||
|
||||
import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog';
|
||||
|
||||
import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
|
||||
import { App } from 'vue';
|
||||
import projectSetting from '/@/settings/projectSetting';
|
||||
|
||||
/**
|
||||
* Handling error stack information
|
||||
* @param error
|
||||
*/
|
||||
function processStackMsg(error: Error) {
|
||||
if (!error.stack) {
|
||||
return '';
|
||||
}
|
||||
let stack = error.stack
|
||||
.replace(/\n/gi, '') // Remove line breaks to save the size of the transmitted content
|
||||
.replace(/\bat\b/gi, '@') // At in chrome, @ in ff
|
||||
.split('@') // Split information with @
|
||||
.slice(0, 9) // The maximum stack length (Error.stackTraceLimit = 10), so only take the first 10
|
||||
.map((v) => v.replace(/^\s*|\s*$/g, '')) // Remove extra spaces
|
||||
.join('~') // Manually add separators for later display
|
||||
.replace(/\?[^:]+/gi, ''); // Remove redundant parameters of js file links (?x=1 and the like)
|
||||
const msg = error.toString();
|
||||
if (stack.indexOf(msg) < 0) {
|
||||
stack = msg + '@' + stack;
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* get comp name
|
||||
* @param vm
|
||||
*/
|
||||
function formatComponentName(vm: any) {
|
||||
if (vm.$root === vm) {
|
||||
return {
|
||||
name: 'root',
|
||||
path: 'root',
|
||||
};
|
||||
}
|
||||
|
||||
const options = vm.$options as any;
|
||||
if (!options) {
|
||||
return {
|
||||
name: 'anonymous',
|
||||
path: 'anonymous',
|
||||
};
|
||||
}
|
||||
const name = options.name || options._componentTag;
|
||||
return {
|
||||
name: name,
|
||||
path: options.__file,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure Vue error handling function
|
||||
*/
|
||||
|
||||
function vueErrorHandler(err: Error, vm: any, info: string) {
|
||||
const errorLogStore = useErrorLogStoreWithOut();
|
||||
const { name, path } = formatComponentName(vm);
|
||||
errorLogStore.addErrorLogInfo({
|
||||
type: ErrorTypeEnum.VUE,
|
||||
name,
|
||||
file: path,
|
||||
message: err.message,
|
||||
stack: processStackMsg(err),
|
||||
detail: info,
|
||||
url: window.location.href,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure script error handling function
|
||||
*/
|
||||
export function scriptErrorHandler(
|
||||
event: Event | string,
|
||||
source?: string,
|
||||
lineno?: number,
|
||||
colno?: number,
|
||||
error?: Error,
|
||||
) {
|
||||
if (event === 'Script error.' && !source) {
|
||||
return false;
|
||||
}
|
||||
const errorInfo: Partial<ErrorLogInfo> = {};
|
||||
colno = colno || (window.event && (window.event as any).errorCharacter) || 0;
|
||||
errorInfo.message = event as string;
|
||||
if (error?.stack) {
|
||||
errorInfo.stack = error.stack;
|
||||
} else {
|
||||
errorInfo.stack = '';
|
||||
}
|
||||
const name = source ? source.slice(source.lastIndexOf('/') + 1) : 'script';
|
||||
const errorLogStore = useErrorLogStoreWithOut();
|
||||
errorLogStore.addErrorLogInfo({
|
||||
type: ErrorTypeEnum.SCRIPT,
|
||||
name: name,
|
||||
file: source as string,
|
||||
detail: 'lineno' + lineno,
|
||||
url: window.location.href,
|
||||
...(errorInfo as Pick<ErrorLogInfo, 'message' | 'stack'>),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure Promise error handling function
|
||||
*/
|
||||
function registerPromiseErrorHandler() {
|
||||
window.addEventListener(
|
||||
'unhandledrejection',
|
||||
function (event) {
|
||||
const errorLogStore = useErrorLogStoreWithOut();
|
||||
errorLogStore.addErrorLogInfo({
|
||||
type: ErrorTypeEnum.PROMISE,
|
||||
name: 'Promise Error!',
|
||||
file: 'none',
|
||||
detail: 'promise error!',
|
||||
url: window.location.href,
|
||||
stack: 'promise error!',
|
||||
message: event.reason,
|
||||
});
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure monitoring resource loading error handling function
|
||||
*/
|
||||
function registerResourceErrorHandler() {
|
||||
// Monitoring resource loading error(img,script,css,and jsonp)
|
||||
window.addEventListener(
|
||||
'error',
|
||||
function (e: Event) {
|
||||
const target = e.target ? e.target : (e.srcElement as any);
|
||||
const errorLogStore = useErrorLogStoreWithOut();
|
||||
errorLogStore.addErrorLogInfo({
|
||||
type: ErrorTypeEnum.RESOURCE,
|
||||
name: 'Resource Error!',
|
||||
file: (e.target || ({} as any)).currentSrc,
|
||||
detail: JSON.stringify({
|
||||
tagName: target.localName,
|
||||
html: target.outerHTML,
|
||||
type: e.type,
|
||||
}),
|
||||
url: window.location.href,
|
||||
stack: 'resource is not found',
|
||||
message: (e.target || ({} as any)).localName + ' is load error',
|
||||
});
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure global error handling
|
||||
* @param app
|
||||
*/
|
||||
export function setupErrorHandle(app: App) {
|
||||
const { useErrorHandle } = projectSetting;
|
||||
if (!useErrorHandle) {
|
||||
return;
|
||||
}
|
||||
// Vue exception monitoring;
|
||||
app.config.errorHandler = vueErrorHandler;
|
||||
|
||||
// script error
|
||||
window.onerror = scriptErrorHandler;
|
||||
|
||||
// promise exception
|
||||
registerPromiseErrorHandler();
|
||||
|
||||
// Static resource exception
|
||||
registerResourceErrorHandler();
|
||||
}
|
||||
84
src/logics/initAppConfig.ts
Normal file
84
src/logics/initAppConfig.ts
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Application configuration
|
||||
*/
|
||||
import type { ProjectConfig } from '/#/config';
|
||||
|
||||
import { PROJ_CFG_KEY } from '/@/enums/cacheEnum';
|
||||
import projectSetting from '/@/settings/projectSetting';
|
||||
|
||||
import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updateBackground';
|
||||
import { updateColorWeak } from '/@/logics/theme/updateColorWeak';
|
||||
import { updateGrayMode } from '/@/logics/theme/updateGrayMode';
|
||||
import { updateDarkTheme } from '/@/logics/theme/dark';
|
||||
import { changeTheme } from '/@/logics/theme';
|
||||
|
||||
import { useAppStore } from '/@/store/modules/app';
|
||||
import { useLocaleStore } from '/@/store/modules/locale';
|
||||
|
||||
import { getCommonStoragePrefix, getStorageShortName } from '/@/utils/env';
|
||||
|
||||
import { primaryColor } from '../../build/config/themeConfig';
|
||||
import { Persistent } from '/@/utils/cache/persistent';
|
||||
import { deepMerge } from '/@/utils';
|
||||
import { ThemeEnum } from '/@/enums/appEnum';
|
||||
|
||||
// Initial project configuration
|
||||
export function initAppConfigStore() {
|
||||
const localeStore = useLocaleStore();
|
||||
const appStore = useAppStore();
|
||||
let projCfg: ProjectConfig = Persistent.getLocal(PROJ_CFG_KEY) as ProjectConfig;
|
||||
projCfg = deepMerge(projectSetting, projCfg || {});
|
||||
const darkMode = appStore.getDarkMode;
|
||||
const {
|
||||
colorWeak,
|
||||
grayMode,
|
||||
themeColor,
|
||||
|
||||
headerSetting: { bgColor: headerBgColor } = {},
|
||||
menuSetting: { bgColor } = {},
|
||||
} = projCfg;
|
||||
try {
|
||||
if (themeColor && themeColor !== primaryColor) {
|
||||
changeTheme(themeColor);
|
||||
}
|
||||
|
||||
grayMode && updateGrayMode(grayMode);
|
||||
colorWeak && updateColorWeak(colorWeak);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
appStore.setProjectConfig(projCfg);
|
||||
|
||||
// init dark mode
|
||||
updateDarkTheme(darkMode);
|
||||
if (darkMode === ThemeEnum.DARK) {
|
||||
updateHeaderBgColor();
|
||||
updateSidebarBgColor();
|
||||
} else {
|
||||
headerBgColor && updateHeaderBgColor(headerBgColor);
|
||||
bgColor && updateSidebarBgColor(bgColor);
|
||||
}
|
||||
// init store
|
||||
localeStore.initLocale();
|
||||
|
||||
setTimeout(() => {
|
||||
clearObsoleteStorage();
|
||||
}, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* As the version continues to iterate, there will be more and more cache keys stored in localStorage.
|
||||
* This method is used to delete useless keys
|
||||
*/
|
||||
export function clearObsoleteStorage() {
|
||||
const commonPrefix = getCommonStoragePrefix();
|
||||
const shortPrefix = getStorageShortName();
|
||||
|
||||
[localStorage, sessionStorage].forEach((item: Storage) => {
|
||||
Object.keys(item).forEach((key) => {
|
||||
if (key && key.startsWith(commonPrefix) && !key.startsWith(shortPrefix)) {
|
||||
item.removeItem(key);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
31
src/logics/mitt/routeChange.ts
Normal file
31
src/logics/mitt/routeChange.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Used to monitor routing changes to change the status of menus and tabs. There is no need to monitor the route, because the route status change is affected by the page rendering time, which will be slow
|
||||
*/
|
||||
|
||||
import mitt from '/@/utils/mitt';
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
import { getRawRoute } from '/@/utils';
|
||||
|
||||
const emitter = mitt();
|
||||
|
||||
const key = Symbol();
|
||||
|
||||
let lastChangeTab: RouteLocationNormalized;
|
||||
|
||||
export function setRouteChange(lastChangeRoute: RouteLocationNormalized) {
|
||||
const r = getRawRoute(lastChangeRoute);
|
||||
emitter.emit(key, r);
|
||||
lastChangeTab = r;
|
||||
}
|
||||
|
||||
export function listenerRouteChange(
|
||||
callback: (route: RouteLocationNormalized) => void,
|
||||
immediate = true,
|
||||
) {
|
||||
emitter.on(key, callback);
|
||||
immediate && lastChangeTab && callback(lastChangeTab);
|
||||
}
|
||||
|
||||
export function removeTabChangeListener() {
|
||||
emitter.clear();
|
||||
}
|
||||
24
src/logics/theme/dark.ts
Normal file
24
src/logics/theme/dark.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { darkCssIsReady, loadDarkThemeCss } from 'vite-vue-plugin-theme/es/client';
|
||||
import { addClass, hasClass, removeClass } from '/@/utils/domUtils';
|
||||
|
||||
export async function updateDarkTheme(mode: string | null = 'light') {
|
||||
const htmlRoot = document.getElementById('htmlRoot');
|
||||
if (!htmlRoot) {
|
||||
return;
|
||||
}
|
||||
const hasDarkClass = hasClass(htmlRoot, 'dark');
|
||||
if (mode === 'dark') {
|
||||
if (import.meta.env.PROD && !darkCssIsReady) {
|
||||
await loadDarkThemeCss();
|
||||
}
|
||||
htmlRoot.setAttribute('data-theme', 'dark');
|
||||
if (!hasDarkClass) {
|
||||
addClass(htmlRoot, 'dark');
|
||||
}
|
||||
} else {
|
||||
htmlRoot.setAttribute('data-theme', 'light');
|
||||
if (hasDarkClass) {
|
||||
removeClass(htmlRoot, 'dark');
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/logics/theme/index.ts
Normal file
17
src/logics/theme/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { getThemeColors, generateColors } from '../../../build/config/themeConfig';
|
||||
|
||||
import { replaceStyleVariables } from 'vite-vue-plugin-theme/es/client';
|
||||
import { mixLighten, mixDarken, tinycolor } from 'vite-vue-plugin-theme/es/colorUtils';
|
||||
|
||||
export async function changeTheme(color: string) {
|
||||
const colors = generateColors({
|
||||
mixDarken,
|
||||
mixLighten,
|
||||
tinycolor,
|
||||
color,
|
||||
});
|
||||
|
||||
return await replaceStyleVariables({
|
||||
colorVariables: [...getThemeColors(color), ...colors],
|
||||
});
|
||||
}
|
||||
75
src/logics/theme/updateBackground.ts
Normal file
75
src/logics/theme/updateBackground.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { colorIsDark, lighten, darken } from '/@/utils/color';
|
||||
import { useAppStore } from '/@/store/modules/app';
|
||||
import { ThemeEnum } from '/@/enums/appEnum';
|
||||
import { setCssVar } from './util';
|
||||
|
||||
const HEADER_BG_COLOR_VAR = '--header-bg-color';
|
||||
const HEADER_BG_HOVER_COLOR_VAR = '--header-bg-hover-color';
|
||||
const HEADER_MENU_ACTIVE_BG_COLOR_VAR = '--header-active-menu-bg-color';
|
||||
|
||||
const SIDER_DARK_BG_COLOR = '--sider-dark-bg-color';
|
||||
const SIDER_DARK_DARKEN_BG_COLOR = '--sider-dark-darken-bg-color';
|
||||
const SIDER_LIGHTEN_BG_COLOR = '--sider-dark-lighten-bg-color';
|
||||
|
||||
/**
|
||||
* Change the background color of the top header
|
||||
* @param color
|
||||
*/
|
||||
export function updateHeaderBgColor(color?: string) {
|
||||
const appStore = useAppStore();
|
||||
const darkMode = appStore.getDarkMode === ThemeEnum.DARK;
|
||||
if (!color) {
|
||||
if (darkMode) {
|
||||
color = '#151515';
|
||||
} else {
|
||||
color = appStore.getHeaderSetting.bgColor;
|
||||
}
|
||||
}
|
||||
// bg color
|
||||
setCssVar(HEADER_BG_COLOR_VAR, color);
|
||||
|
||||
// hover color
|
||||
const hoverColor = lighten(color!, 6);
|
||||
setCssVar(HEADER_BG_HOVER_COLOR_VAR, hoverColor);
|
||||
setCssVar(HEADER_MENU_ACTIVE_BG_COLOR_VAR, hoverColor);
|
||||
|
||||
// Determine the depth of the color value and automatically switch the theme
|
||||
const isDark = colorIsDark(color!);
|
||||
|
||||
appStore.setProjectConfig({
|
||||
headerSetting: {
|
||||
theme: isDark || darkMode ? ThemeEnum.DARK : ThemeEnum.LIGHT,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the background color of the left menu
|
||||
* @param color bg color
|
||||
*/
|
||||
export function updateSidebarBgColor(color?: string) {
|
||||
const appStore = useAppStore();
|
||||
|
||||
// if (!isHexColor(color)) return;
|
||||
const darkMode = appStore.getDarkMode === ThemeEnum.DARK;
|
||||
if (!color) {
|
||||
if (darkMode) {
|
||||
color = '#212121';
|
||||
} else {
|
||||
color = appStore.getMenuSetting.bgColor;
|
||||
}
|
||||
}
|
||||
setCssVar(SIDER_DARK_BG_COLOR, color);
|
||||
setCssVar(SIDER_DARK_DARKEN_BG_COLOR, darken(color!, 6));
|
||||
setCssVar(SIDER_LIGHTEN_BG_COLOR, lighten(color!, 5));
|
||||
|
||||
// only #ffffff is light
|
||||
// Only when the background color is #fff, the theme of the menu will be changed to light
|
||||
const isLight = ['#fff', '#ffffff'].includes(color!.toLowerCase());
|
||||
|
||||
appStore.setProjectConfig({
|
||||
menuSetting: {
|
||||
theme: isLight && !darkMode ? ThemeEnum.LIGHT : ThemeEnum.DARK,
|
||||
},
|
||||
});
|
||||
}
|
||||
9
src/logics/theme/updateColorWeak.ts
Normal file
9
src/logics/theme/updateColorWeak.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { toggleClass } from './util';
|
||||
|
||||
/**
|
||||
* Change the status of the project's color weakness mode
|
||||
* @param colorWeak
|
||||
*/
|
||||
export function updateColorWeak(colorWeak: boolean) {
|
||||
toggleClass(colorWeak, 'color-weak', document.documentElement);
|
||||
}
|
||||
9
src/logics/theme/updateGrayMode.ts
Normal file
9
src/logics/theme/updateGrayMode.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { toggleClass } from './util';
|
||||
|
||||
/**
|
||||
* Change project gray mode status
|
||||
* @param gray
|
||||
*/
|
||||
export function updateGrayMode(gray: boolean) {
|
||||
toggleClass(gray, 'gray-mode', document.documentElement);
|
||||
}
|
||||
11
src/logics/theme/util.ts
Normal file
11
src/logics/theme/util.ts
Normal file
@ -0,0 +1,11 @@
|
||||
const docEle = document.documentElement;
|
||||
export function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
|
||||
const targetEl = target || document.body;
|
||||
let { className } = targetEl;
|
||||
className = className.replace(clsName, '');
|
||||
targetEl.className = flag ? `${className} ${clsName} ` : className;
|
||||
}
|
||||
|
||||
export function setCssVar(prop: string, val: any, dom = docEle) {
|
||||
dom.style.setProperty(prop, val);
|
||||
}
|
||||
Reference in New Issue
Block a user