初始版本提交

This commit is contained in:
yaoyn
2024-02-05 09:15:37 +08:00
parent b52d4414be
commit 445292105f
1848 changed files with 236859 additions and 75 deletions

View 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);
}

View 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();
}

View 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);
}
});
});
}

View 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
View 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
View 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],
});
}

View 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,
},
});
}

View 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);
}

View 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
View 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);
}