moc变更管理系统框架
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import { h } from 'vue';
|
||||
import type { App } from 'vue';
|
||||
import { NButton } from 'naive-ui';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
export function setupAppErrorHandle(app: App) {
|
||||
app.config.errorHandler = (err, vm, info) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err, vm, info);
|
||||
};
|
||||
}
|
||||
|
||||
export function setupAppVersionNotification() {
|
||||
// Update check interval in milliseconds
|
||||
const UPDATE_CHECK_INTERVAL = 3 * 60 * 1000;
|
||||
|
||||
const canAutoUpdateApp = import.meta.env.VITE_AUTOMATICALLY_DETECT_UPDATE === 'Y' && import.meta.env.PROD;
|
||||
if (!canAutoUpdateApp) return;
|
||||
|
||||
let isShow = false;
|
||||
let updateInterval: ReturnType<typeof setInterval> | undefined;
|
||||
|
||||
const checkForUpdates = async () => {
|
||||
if (isShow) return;
|
||||
|
||||
const buildTime = await getHtmlBuildTime();
|
||||
|
||||
// If failed to get build time or build time hasn't changed, no update is needed.
|
||||
if (!buildTime || buildTime === BUILD_TIME) {
|
||||
return;
|
||||
}
|
||||
|
||||
isShow = true;
|
||||
|
||||
// Show update notification
|
||||
const n = window.$notification?.create({
|
||||
title: $t('system.updateTitle'),
|
||||
content: $t('system.updateContent'),
|
||||
action() {
|
||||
return h('div', { style: { display: 'flex', justifyContent: 'end', gap: '12px', width: '325px' } }, [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
onClick() {
|
||||
n?.destroy();
|
||||
isShow = false;
|
||||
}
|
||||
},
|
||||
() => $t('system.updateCancel')
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
onClick() {
|
||||
location.reload();
|
||||
}
|
||||
},
|
||||
() => $t('system.updateConfirm')
|
||||
)
|
||||
]);
|
||||
},
|
||||
onClose() {
|
||||
isShow = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const startUpdateInterval = () => {
|
||||
if (updateInterval) {
|
||||
clearInterval(updateInterval);
|
||||
}
|
||||
updateInterval = setInterval(checkForUpdates, UPDATE_CHECK_INTERVAL);
|
||||
};
|
||||
|
||||
// If updates should be checked, set up the visibility change listener and start the update interval
|
||||
if (!isShow && document.visibilityState === 'visible') {
|
||||
// Check for updates when the document is visible
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
checkForUpdates();
|
||||
startUpdateInterval();
|
||||
}
|
||||
});
|
||||
|
||||
// Start the update interval
|
||||
startUpdateInterval();
|
||||
}
|
||||
}
|
||||
|
||||
async function getHtmlBuildTime(): Promise<string | null> {
|
||||
const baseUrl = import.meta.env.VITE_BASE_URL || '/';
|
||||
|
||||
try {
|
||||
const res = await fetch(`${baseUrl}index.html?time=${Date.now()}`);
|
||||
|
||||
if (!res.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const html = await res.text();
|
||||
const match = html.match(/<meta name="buildTime" content="(.*)">/);
|
||||
return match?.[1] || null;
|
||||
} catch (error) {
|
||||
window.console.error('getHtmlBuildTime error:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// @ts-expect-error ignore type error of svg icons
|
||||
import 'virtual:svg-icons-register';
|
||||
import 'uno.css';
|
||||
import '../styles/css/global.css';
|
||||
import 'swiper/css';
|
||||
import 'swiper/css/navigation';
|
||||
import 'swiper/css/pagination';
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { App } from 'vue';
|
||||
import { ProDate, ProDateTime, ProInput, ProRate, create } from 'pro-naive-ui';
|
||||
|
||||
/** pro-naive-ui 支持配置表单的按需加载,所以需要注册 */
|
||||
export function setupProNaiveComponents(app: App) {
|
||||
const proNaive = create({
|
||||
components: [ProInput, ProDate, ProDateTime, ProRate]
|
||||
});
|
||||
app.use(proNaive);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { extend } from 'dayjs';
|
||||
import localeData from 'dayjs/plugin/localeData';
|
||||
import { setDayjsLocale } from '../locales/dayjs';
|
||||
|
||||
export function setupDayjs() {
|
||||
extend(localeData);
|
||||
|
||||
setDayjsLocale();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { addAPIProvider } from '@iconify/vue';
|
||||
|
||||
/** Setup the iconify offline */
|
||||
export function setupIconifyOffline() {
|
||||
const { VITE_ICONIFY_URL } = import.meta.env;
|
||||
|
||||
if (VITE_ICONIFY_URL) {
|
||||
addAPIProvider('', { resources: [VITE_ICONIFY_URL] });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export * from './loading';
|
||||
export * from './nprogress';
|
||||
export * from './components';
|
||||
export * from './iconify';
|
||||
export * from './dayjs';
|
||||
export * from './app';
|
||||
@@ -0,0 +1,210 @@
|
||||
// @unocss-include
|
||||
import { getColorPalette, getRgb } from '@sa/color';
|
||||
import { DARK_CLASS } from '@/constants/app';
|
||||
import { localStg } from '@/utils/storage';
|
||||
import { toggleHtmlClass } from '@/utils/common';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
export function setupLoading() {
|
||||
const themeColor = localStg.get('themeColor') || '#646cff';
|
||||
const darkMode = localStg.get('darkMode') || false;
|
||||
const palette = getColorPalette(themeColor);
|
||||
|
||||
const { r, g, b } = getRgb(themeColor);
|
||||
|
||||
const primaryColor = `--primary-color: ${r} ${g} ${b}`;
|
||||
|
||||
const svgCssVars = Array.from(palette.entries())
|
||||
.map(([key, value]) => `--logo-color-${key}: ${value}`)
|
||||
.join(';');
|
||||
|
||||
const cssVars = `${primaryColor}; ${svgCssVars}`;
|
||||
|
||||
if (darkMode) {
|
||||
toggleHtmlClass(DARK_CLASS).add();
|
||||
}
|
||||
|
||||
const loadingClasses = [
|
||||
'left-0 top-0',
|
||||
'left-0 bottom-0 animate-delay-500',
|
||||
'right-0 top-0 animate-delay-1000',
|
||||
'right-0 bottom-0 animate-delay-1500'
|
||||
];
|
||||
|
||||
const dot = loadingClasses
|
||||
.map(item => {
|
||||
return `<div class="absolute w-16px h-16px bg-primary rounded-8px animate-pulse ${item}"></div>`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
const loading = `
|
||||
<div class="fixed-center flex-col bg-layout" style="${cssVars}">
|
||||
<div class="w-128px h-128px">
|
||||
${getLogoSvg()}
|
||||
</div>
|
||||
<div class="w-56px h-56px my-36px">
|
||||
<div class="relative h-full animate-spin">
|
||||
${dot}
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-28px font-500 text-primary">${$t('system.title')}</h2>
|
||||
</div>`;
|
||||
|
||||
const app = document.getElementById('app');
|
||||
|
||||
if (app) {
|
||||
app.innerHTML = loading;
|
||||
}
|
||||
}
|
||||
|
||||
function getLogoSvg() {
|
||||
const logoSvg = `<svg
|
||||
width="100%"
|
||||
height="100%"
|
||||
version="1.1"
|
||||
viewBox="0 0 1000 1000"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M 200,866 C 100,866 50,779.4 100,692.8 L 200,519.6 C 220,485 240,490 265,499.6 S 360,542.68 360,542.68 C 480.5,601 498,642.5 500,720 C 498,811 462,856 420,866"
|
||||
fill="url(#LinearGradient)"
|
||||
fill-rule="nonzero"
|
||||
opacity="1"
|
||||
stroke="none"
|
||||
/>
|
||||
<path
|
||||
d="M 420,866 C 455,861 478,846 500,827 C 614,696 615,597 500,517 C 394,444 333,374 380,207.82 L 260,415.67 C 240.22,450 254.37,465.1 275.28,481.79 S 360,542.68 360,542.68 C 480.5,601 498,642.5 500,720 C 498,811 462,856 420,866"
|
||||
fill="url(#LinearGradient_2)"
|
||||
fill-rule="nonzero"
|
||||
opacity="1"
|
||||
stroke="none"
|
||||
/>
|
||||
<path
|
||||
d="M 500,517 C 394,444 333,374 380,207.82 L 400,173.2 C 367,295 421,350 603,428 C 572,440 524,474 500,517"
|
||||
fill="url(#LinearGradient_3)"
|
||||
fill-rule="nonzero"
|
||||
opacity="1"
|
||||
stroke="none"
|
||||
/>
|
||||
<path
|
||||
d="M 500,827 L 660,660 C 738,589 710,482 603,428 C 572,440 524,474 500,517 C 615,597 614,696 500,827"
|
||||
fill="url(#LinearGradient_4)"
|
||||
fill-rule="nonzero"
|
||||
opacity="1"
|
||||
stroke="none"
|
||||
/>
|
||||
<path
|
||||
d="M 400,173.2 C 367,295 421,350 603,428 C 690,389, 750,445 788,500 L 600,173.2 C 550,86.6 450,86.6 400,173.2"
|
||||
fill="url(#LinearGradient_5)"
|
||||
fill-rule="nonzero"
|
||||
opacity="1"
|
||||
stroke="none"
|
||||
/>
|
||||
<path
|
||||
d="M 500,827 L 660,660 C 738,589 710,482 603,428 C 690,389, 750,445 788,500 C 816,554 797,606 750,640 L 500,827"
|
||||
fill="url(#LinearGradient_6)"
|
||||
fill-rule="nonzero"
|
||||
opacity="1"
|
||||
stroke="none"
|
||||
/>
|
||||
<path
|
||||
d="M 788,500 C 816,554 797,606 750,640 L 500,827 C 497,851 513,862 540,866 L 800,866 C 900,866 950,779.4 900,692.8 L 788,500"
|
||||
fill="url(#LinearGradient_7)"
|
||||
fill-rule="nonzero"
|
||||
opacity="1"
|
||||
stroke="none"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="LinearGradient"
|
||||
gradientTransform="matrix(104.391 -73.3432 73.3432 104.391 277.441 710.122)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
y2="0"
|
||||
>
|
||||
<stop offset="0" stop-color="var(--logo-color-700)" />
|
||||
<stop offset="1" stop-color="var(--logo-color-600)" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="LinearGradient_2"
|
||||
gradientTransform="matrix(-173.747 557.324 -557.324 -173.747 508.829 258.172)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
y2="0"
|
||||
>
|
||||
<stop offset="0" stop-color="var(--logo-color-300)" />
|
||||
<stop offset="1" stop-color="var(--logo-color-500)" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="LinearGradient_3"
|
||||
gradientTransform="matrix(157.951 295.666 -295.666 157.951 382.944 193.642)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
y2="0"
|
||||
>
|
||||
<stop offset="0" stop-color="var(--logo-color-600)" />
|
||||
<stop offset="1" stop-color="var(--logo-color-700)" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="LinearGradient_4"
|
||||
gradientTransform="matrix(-44.3023 219.578 -219.578 -44.3023 619.69 469.652)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
y2="0"
|
||||
>
|
||||
<stop offset="0" stop-color="var(--logo-color-400)" />
|
||||
<stop offset="1" stop-color="var(--logo-color-600)" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="LinearGradient_5"
|
||||
gradientTransform="matrix(125.52 334.256 -334.256 125.52 539.723 235.139)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
y2="0"
|
||||
>
|
||||
<stop offset="0" stop-color="var(--logo-color-500)" />
|
||||
<stop offset="1" stop-color="var(--logo-color-300)" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="LinearGradient_6"
|
||||
gradientTransform="matrix(-241.23 357.206 -357.206 -241.23 754.054 449.312)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
y2="0"
|
||||
>
|
||||
<stop offset="0" stop-color="var(--logo-color-300)" />
|
||||
<stop offset="1" stop-color="var(--logo-color-500)" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="LinearGradient_7"
|
||||
gradientTransform="matrix(125.978 210.065 -210.065 125.978 596.433 613.665)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
y2="0"
|
||||
>
|
||||
<stop offset="0" stop-color="var(--logo-color-700)" />
|
||||
<stop offset="1" stop-color="var(--logo-color-600)" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
return logoSvg;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import NProgress from 'nprogress';
|
||||
|
||||
/** Setup plugin NProgress */
|
||||
export function setupNProgress() {
|
||||
NProgress.configure({ easing: 'ease', speed: 500 });
|
||||
|
||||
// mount on window
|
||||
window.NProgress = NProgress;
|
||||
}
|
||||
Reference in New Issue
Block a user