feat: Regular monitoring page update [deploy]

pull/48/MERGE
vben 2024-07-29 22:11:22 +08:00
parent 66fd052709
commit cd10eb9471
37 changed files with 491 additions and 261 deletions

1
.gitignore vendored
View File

@ -15,6 +15,7 @@ coverage
**/.vitepress/cache **/.vitepress/cache
.cache .cache
.turbo .turbo
.temp
dev-dist dev-dist
.stylelintcache .stylelintcache
yarn.lock yarn.lock

View File

@ -2,3 +2,4 @@ dist
public public
__tests__ __tests__
coverage coverage
ui-kit

View File

@ -7,7 +7,7 @@ VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
VITE_COMPRESS=none VITE_COMPRESS=none
# 是否开启 PWA # 是否开启 PWA
VITE_PWA=false VITE_PWA=true
# vue-router 的模式 # vue-router 的模式
VITE_ROUTER_HISTORY=hash VITE_ROUTER_HISTORY=hash

View File

@ -1,7 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed } from 'vue';
import { GlobalProvider } from '@vben/common-ui';
import { useDesignTokens } from '@vben/hooks'; import { useDesignTokens } from '@vben/hooks';
import { preferences, usePreferences } from '@vben/preferences'; import { preferences, usePreferences } from '@vben/preferences';
@ -26,17 +25,15 @@ const tokenTheme = computed(() => {
return { return {
algorithm, algorithm,
token: antDesignTokens.value, token: antDesignTokens,
}; };
}); });
</script> </script>
<template> <template>
<GlobalProvider> <ConfigProvider :locale="antdLocale" :theme="tokenTheme">
<ConfigProvider :locale="antdLocale" :theme="tokenTheme"> <App>
<App> <RouterView />
<RouterView /> </App>
</App> </ConfigProvider>
</ConfigProvider>
</GlobalProvider>
</template> </template>

View File

@ -3,7 +3,7 @@ import { computed, ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { AuthenticationLoginExpiredModal } from '@vben/common-ui'; import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
import { LOGIN_PATH } from '@vben/constants'; import { LOGIN_PATH, VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons'; import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
import { import {
BasicLayout, BasicLayout,
@ -57,7 +57,7 @@ const showDot = computed(() =>
const menus = computed(() => [ const menus = computed(() => [
{ {
handler: () => { handler: () => {
openWindow('https://github.com/vbenjs/vue-vben-admin', { openWindow(VBEN_DOC_URL, {
target: '_blank', target: '_blank',
}); });
}, },
@ -66,7 +66,7 @@ const menus = computed(() => [
}, },
{ {
handler: () => { handler: () => {
openWindow('https://github.com/vbenjs/vue-vben-admin', { openWindow(VBEN_GITHUB_URL, {
target: '_blank', target: '_blank',
}); });
}, },
@ -75,7 +75,7 @@ const menus = computed(() => [
}, },
{ {
handler: () => { handler: () => {
openWindow('https://github.com/vbenjs/vue-vben-admin/issues', { openWindow(`${VBEN_GITHUB_URL}/issues`, {
target: '_blank', target: '_blank',
}); });
}, },

View File

@ -1,6 +1,6 @@
import type { RouteRecordRaw } from 'vue-router'; import type { RouteRecordRaw } from 'vue-router';
import { VBEN_GITHUB_URL, VBEN_LOGO_URL } from '@vben/constants'; import { VBEN_DOC_URL, VBEN_GITHUB_URL, VBEN_LOGO_URL } from '@vben/constants';
import { BasicLayout, IFrameView } from '#/layouts'; import { BasicLayout, IFrameView } from '#/layouts';
import { $t } from '#/locales'; import { $t } from '#/locales';
@ -35,7 +35,7 @@ const routes: RouteRecordRaw[] = [
component: IFrameView, component: IFrameView,
meta: { meta: {
icon: 'lucide:book-open-text', icon: 'lucide:book-open-text',
iframeSrc: 'https://doc.vvbin.cn/', iframeSrc: VBEN_DOC_URL,
keepAlive: true, keepAlive: true,
title: $t('page.vben.document'), title: $t('page.vben.document'),
}, },

View File

@ -42,7 +42,8 @@
"ependencies", "ependencies",
"vite", "vite",
"echarts", "echarts",
"sortablejs" "sortablejs",
"etag"
], ],
"ignorePaths": [ "ignorePaths": [
"**/node_modules/**", "**/node_modules/**",

View File

@ -39,7 +39,7 @@
"eslint": "^9.8.0", "eslint": "^9.8.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-jsdoc": "^48.8.3", "eslint-plugin-jsdoc": "^48.9.2",
"eslint-plugin-jsonc": "^2.16.0", "eslint-plugin-jsonc": "^2.16.0",
"eslint-plugin-n": "^17.10.1", "eslint-plugin-n": "^17.10.1",
"eslint-plugin-no-only-tests": "^3.1.0", "eslint-plugin-no-only-tests": "^3.1.0",

View File

@ -43,11 +43,6 @@ const customConfig: Linter.FlatConfig[] = [
message: message:
'The #/stores package cannot be imported, please use the @core package itself', 'The #/stores package cannot be imported, please use the @core package itself',
}, },
{
group: ['#/forward/*'],
message:
'The #/forward package cannot be imported, please use the @core package itself',
},
], ],
}, },
], ],
@ -99,6 +94,9 @@ const customConfig: Linter.FlatConfig[] = [
'packages/icons/**/**', 'packages/icons/**/**',
'packages/constants/**/**', 'packages/constants/**/**',
'packages/styles/**/**', 'packages/styles/**/**',
'packages/stores/**/**',
'packages/preferences/**/**',
'packages/locales/**/**',
], ],
ignores: restrictedImportIgnores, ignores: restrictedImportIgnores,
rules: { rules: {
@ -118,11 +116,12 @@ const customConfig: Linter.FlatConfig[] = [
}, },
// 后端模拟代码,不需要太多规则 // 后端模拟代码,不需要太多规则
{ {
files: ['apps/backend-mock/**/**'], files: ['apps/backend-mock/**/**', 'website/**/**'],
rules: { rules: {
'@typescript-eslint/no-extraneous-class': 'off', '@typescript-eslint/no-extraneous-class': 'off',
'n/no-extraneous-import': 'off', 'n/no-extraneous-import': 'off',
'n/prefer-global/buffer': 'off', 'n/prefer-global/buffer': 'off',
'n/prefer-global/process': 'off',
'no-console': 'off', 'no-console': 'off',
'unicorn/prefer-module': 'off', 'unicorn/prefer-module': 'off',
}, },

View File

@ -37,7 +37,7 @@
"postcss-html": "^1.7.0", "postcss-html": "^1.7.0",
"postcss-scss": "^4.0.9", "postcss-scss": "^4.0.9",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"stylelint": "^16.7.0", "stylelint": "^16.8.0",
"stylelint-config-recommended": "^14.0.1", "stylelint-config-recommended": "^14.0.1",
"stylelint-config-recommended-scss": "^14.1.0", "stylelint-config-recommended-scss": "^14.1.0",
"stylelint-config-recommended-vue": "^1.5.0", "stylelint-config-recommended-vue": "^1.5.0",

View File

@ -46,7 +46,7 @@
"tailwindcss": "^3.4.3" "tailwindcss": "^3.4.3"
}, },
"dependencies": { "dependencies": {
"@iconify/json": "^2.2.231", "@iconify/json": "^2.2.232",
"@iconify/tailwind": "^1.1.2", "@iconify/tailwind": "^1.1.2",
"@tailwindcss/nesting": "0.0.0-insiders.565cd3e", "@tailwindcss/nesting": "0.0.0-insiders.565cd3e",
"@tailwindcss/typography": "^0.5.13", "@tailwindcss/typography": "^0.5.13",

View File

@ -13,7 +13,7 @@ import { loadApplicationPlugins } from '../plugins';
import { loadAndConvertEnv } from '../utils/env'; import { loadAndConvertEnv } from '../utils/env';
import { getCommonConfig } from './common'; import { getCommonConfig } from './common';
function defineApplicationConfig(userConfigPromise: DefineApplicationOptions) { function defineApplicationConfig(userConfigPromise?: DefineApplicationOptions) {
return defineConfig(async (config) => { return defineConfig(async (config) => {
const { appTitle, base, port, ...envConfig } = await loadAndConvertEnv(); const { appTitle, base, port, ...envConfig } = await loadAndConvertEnv();
const options = await userConfigPromise?.(config); const options = await userConfigPromise?.(config);

View File

@ -9,7 +9,7 @@ import { defineConfig, mergeConfig } from 'vite';
import { loadLibraryPlugins } from '../plugins'; import { loadLibraryPlugins } from '../plugins';
import { getCommonConfig } from './common'; import { getCommonConfig } from './common';
function defineLibraryConfig(userConfigPromise: DefineLibraryOptions) { function defineLibraryConfig(userConfigPromise?: DefineLibraryOptions) {
return defineConfig(async (config: ConfigEnv) => { return defineConfig(async (config: ConfigEnv) => {
const options = await userConfigPromise?.(config); const options = await userConfigPromise?.(config);
const { command, mode } = config; const { command, mode } = config;

View File

@ -20,7 +20,7 @@ const getDefaultPwaOptions = (name: string): Partial<PwaPluginOptions> => ({
type: 'image/png', type: 'image/png',
}, },
], ],
name: `${name}o${isDevelopment ? ' dev' : ''}`, name: `${name}${isDevelopment ? ' dev' : ''}`,
short_name: `${name}${isDevelopment ? ' dev' : ''}`, short_name: `${name}${isDevelopment ? ' dev' : ''}`,
}, },
}); });

View File

@ -150,10 +150,6 @@ async function loadApplicationPlugins(
condition: pwa, condition: pwa,
plugins: () => plugins: () =>
VitePWA({ VitePWA({
devOptions: {
enabled: true,
type: 'module',
},
injectRegister: false, injectRegister: false,
workbox: { workbox: {
globPatterns: [], globPatterns: [],

View File

@ -4,6 +4,7 @@ const defaultPreferences: Preferences = {
app: { app: {
accessMode: 'frontend', accessMode: 'frontend',
authPageLayout: 'panel-right', authPageLayout: 'panel-right',
checkUpdatesPollingTime: 1,
colorGrayMode: false, colorGrayMode: false,
colorWeakMode: false, colorWeakMode: false,
compact: false, compact: false,
@ -11,6 +12,7 @@ const defaultPreferences: Preferences = {
defaultAvatar: defaultAvatar:
'https://unpkg.com/@vbenjs/static-source@0.1.5/source/avatar-v1.webp', 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/avatar-v1.webp',
dynamicTitle: true, dynamicTitle: true,
enableCheckUpdates: true,
enablePreferences: true, enablePreferences: true,
isMobile: false, isMobile: false,
layout: 'sidebar-nav', layout: 'sidebar-nav',
@ -27,7 +29,7 @@ const defaultPreferences: Preferences = {
styleType: 'normal', styleType: 'normal',
}, },
copyright: { copyright: {
companyName: 'Vben Admin', companyName: 'Vben',
companySiteLink: 'https://www.vben.pro', companySiteLink: 'https://www.vben.pro',
date: '2024', date: '2024',
enable: true, enable: true,

View File

@ -21,6 +21,8 @@ interface AppPreferences {
accessMode: AccessModeType; accessMode: AccessModeType;
/** 登录注册页面布局 */ /** 登录注册页面布局 */
authPageLayout: AuthPageLayoutType; authPageLayout: AuthPageLayoutType;
/** 检查更新轮询时间 */
checkUpdatesPollingTime: number;
/** 是否开启灰色模式 */ /** 是否开启灰色模式 */
colorGrayMode: boolean; colorGrayMode: boolean;
/** 是否开启色弱模式 */ /** 是否开启色弱模式 */
@ -33,6 +35,8 @@ interface AppPreferences {
defaultAvatar: string; defaultAvatar: string;
// /** 开启动态标题 */ // /** 开启动态标题 */
dynamicTitle: boolean; dynamicTitle: boolean;
/** 是否开启检查更新 */
enableCheckUpdates: boolean;
/** 是否显示偏好设置 */ /** 是否显示偏好设置 */
enablePreferences: boolean; enablePreferences: boolean;
/** 是否移动端 */ /** 是否移动端 */

View File

@ -1,9 +0,0 @@
<script setup lang="ts">
import { Toaster } from '@vben-core/shadcn-ui';
defineOptions({ name: 'GlobalProvider' });
</script>
<template>
<Toaster />
<slot></slot>
</template>

View File

@ -1 +0,0 @@
export { default as GlobalProvider } from './global-provider.vue';

View File

@ -2,5 +2,4 @@ export * from './about';
export * from './authentication'; export * from './authentication';
export * from './dashboard'; export * from './dashboard';
export * from './fallback'; export * from './fallback';
export * from './global-provider';
export { useToast } from '@vben-core/shadcn-ui'; export { useToast } from '@vben-core/shadcn-ui';

View File

@ -1,23 +1,25 @@
import { computed, ref, watch } from 'vue'; import { reactive, watch } from 'vue';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
export function useDesignTokens() { export function useDesignTokens() {
const rootStyles = getComputedStyle(document.documentElement); const rootStyles = getComputedStyle(document.documentElement);
const colorPrimary = ref(''); const antDesignTokens = reactive({
const colorError = ref(''); borderRadius: '' as any,
const colorSuccess = ref(''); colorBgBase: '',
const colorWarning = ref(''); colorBgContainer: '',
const colorInfo = ref(''); colorBgElevated: '',
const colorBgBase = ref(''); colorBgLayout: '',
const colorTextBase = ref(''); colorBgMask: '',
const colorBgContainer = ref(''); colorBorder: '',
const colorBgElevated = ref(''); colorError: '',
const colorBgLayout = ref(''); colorInfo: '',
const colorBgMask = ref(''); colorPrimary: '',
const colorBorder = ref(''); colorSuccess: '',
const borderRadius = ref<any>(''); colorTextBase: '',
colorWarning: '',
});
const getCssVariableValue = (variable: string, isColor: boolean = true) => { const getCssVariableValue = (variable: string, isColor: boolean = true) => {
const value = rootStyles.getPropertyValue(variable); const value = rootStyles.getPropertyValue(variable);
@ -27,52 +29,23 @@ export function useDesignTokens() {
watch( watch(
() => preferences.theme, () => preferences.theme,
() => { () => {
colorInfo.value = colorPrimary.value = getCssVariableValue('--primary'); antDesignTokens.colorPrimary = getCssVariableValue('--primary');
colorError.value = getCssVariableValue('--destructive'); antDesignTokens.colorError = getCssVariableValue('--destructive');
colorWarning.value = getCssVariableValue('--warning'); antDesignTokens.colorWarning = getCssVariableValue('--warning');
colorSuccess.value = getCssVariableValue('--success'); antDesignTokens.colorSuccess = getCssVariableValue('--success');
colorBgBase.value = getCssVariableValue('--background'); antDesignTokens.colorBgBase = getCssVariableValue('--background');
colorBgLayout.value = getCssVariableValue('--background-deep'); antDesignTokens.colorBgLayout = getCssVariableValue('--background-deep');
colorBgMask.value = getCssVariableValue('--overlay'); antDesignTokens.colorBgMask = getCssVariableValue('--overlay');
colorBorder.value = getCssVariableValue('--border'); antDesignTokens.colorBorder = getCssVariableValue('--border');
colorTextBase.value = getCssVariableValue('--foreground'); antDesignTokens.colorTextBase = getCssVariableValue('--foreground');
colorBgElevated.value = getCssVariableValue('--popover'); antDesignTokens.colorBgElevated = getCssVariableValue('--popover');
colorBgContainer.value = getCssVariableValue('--card'); antDesignTokens.colorBgContainer = getCssVariableValue('--card');
borderRadius.value = getCssVariableValue('--radius', false); antDesignTokens.borderRadius = getCssVariableValue('--radius', false);
}, },
{ immediate: true }, { immediate: true },
); );
const antDesignTokens = computed(() => {
return {
borderRadius: borderRadius.value,
colorBgBase: colorBgBase.value,
colorBgContainer: colorBgContainer.value,
colorBgElevated: colorBgElevated.value,
colorBgLayout: colorBgLayout.value,
colorBgMask: colorBgMask.value,
colorBorder: colorBorder.value,
colorError: colorError.value,
colorInfo: colorInfo.value,
colorPrimary: colorPrimary.value,
colorSuccess: colorSuccess.value,
colorTextBase: colorTextBase.value,
colorWarning: colorWarning.value,
};
});
return { return {
antDesignTokens, antDesignTokens,
borderRadius,
colorBgBase,
colorBgContainer,
colorBgElevated,
colorBorder,
colorError,
colorInfo,
colorPrimary,
colorSuccess,
colorTextBase,
colorWarning,
}; };
} }

View File

@ -12,9 +12,9 @@ import { useCoreAccessStore, useCoreLockStore } from '@vben/stores';
import { MenuRecordRaw } from '@vben/types'; import { MenuRecordRaw } from '@vben/types';
import { mapTree } from '@vben/utils'; import { mapTree } from '@vben/utils';
import { VbenAdminLayout } from '@vben-core/layout-ui'; import { VbenAdminLayout } from '@vben-core/layout-ui';
import { VbenBackTop, VbenLogo } from '@vben-core/shadcn-ui'; import { Toaster, VbenBackTop, VbenLogo } from '@vben-core/shadcn-ui';
import { Breadcrumb, Preferences } from '../widgets'; import { Breadcrumb, CheckUpdates, Preferences } from '../widgets';
import { LayoutContent } from './content'; import { LayoutContent } from './content';
import { Copyright } from './copyright'; import { Copyright } from './copyright';
import { LayoutFooter } from './footer'; import { LayoutFooter } from './footer';
@ -310,6 +310,12 @@ watch(
<template #extra> <template #extra>
<slot name="extra"></slot> <slot name="extra"></slot>
<Toaster />
<CheckUpdates
v-if="preferences.app.enableCheckUpdates"
:polling-time="preferences.app.checkUpdatesPollingTime"
/>
<Transition v-if="preferences.widget.lockScreen" name="slide-up"> <Transition v-if="preferences.widget.lockScreen" name="slide-up">
<slot v-if="coreLockStore.isLockScreen" name="lock-screen"></slot> <slot v-if="coreLockStore.isLockScreen" name="lock-screen"></slot>
</Transition> </Transition>

View File

@ -0,0 +1,126 @@
<script setup lang="ts">
import { h, onMounted, onUnmounted, ref } from 'vue';
import { $t } from '@vben/locales';
import { ToastAction, useToast } from '@vben-core/shadcn-ui';
interface Props {
//
pollingTime?: number;
}
defineOptions({ name: 'CheckUpdates' });
const props = withDefaults(defineProps<Props>(), {
pollingTime: 1,
});
const lastVersionTag = ref('');
let isCheckingUpdates = false;
const timer = ref<ReturnType<typeof setInterval>>();
const { toast } = useToast();
async function getVersionTag() {
try {
const response = await fetch('/', {
cache: 'no-cache',
method: 'HEAD',
});
return (
response.headers.get('etag') || response.headers.get('last-modified')
);
} catch {
console.error('Failed to fetch version tag');
return null;
}
}
async function checkForUpdates() {
const versionTag = await getVersionTag();
if (!versionTag) {
return;
}
//
if (!lastVersionTag.value) {
lastVersionTag.value = versionTag;
return;
}
if (lastVersionTag.value !== versionTag) {
lastVersionTag.value = versionTag;
clearInterval(timer.value);
handleNotice();
}
}
function handleNotice() {
const { dismiss } = toast({
action: h('div', [
h(
ToastAction,
{
altText: $t('common.cancel'),
onClick: () => dismiss(),
},
{
default: () => $t('common.cancel'),
},
),
h(
ToastAction,
{
altText: $t('common.refresh'),
class: 'bg-primary hover:bg-primary-hover mx-1',
onClick: () => {
window.location.reload();
},
},
{
default: () => $t('common.refresh'),
},
),
]),
description: $t('widgets.checkUpdatesDescription'),
duration: 0,
title: $t('widgets.checkUpdatesTitle'),
});
}
function start() {
// 5
timer.value = setInterval(checkForUpdates, props.pollingTime * 60 * 1000);
}
function handleVisibilitychange() {
if (document.hidden) {
stop();
} else {
if (!isCheckingUpdates) {
isCheckingUpdates = true;
checkForUpdates().finally(() => {
isCheckingUpdates = false;
start();
});
}
}
}
function stop() {
clearInterval(timer.value);
}
onMounted(() => {
start();
document.addEventListener('visibilitychange', handleVisibilitychange);
});
onUnmounted(() => {
stop();
document.removeEventListener('visibilitychange', handleVisibilitychange);
});
</script>
<template>
<slot></slot>
</template>

View File

@ -0,0 +1 @@
export { default as CheckUpdates } from './check-updates.vue';

View File

@ -1,4 +1,5 @@
export { default as Breadcrumb } from './breadcrumb.vue'; export { default as Breadcrumb } from './breadcrumb.vue';
export * from './check-updates';
export { default as AuthenticationColorToggle } from './color-toggle.vue'; export { default as AuthenticationColorToggle } from './color-toggle.vue';
export * from './global-search'; export * from './global-search';
export { default as LanguageToggle } from './language-toggle.vue'; export { default as LanguageToggle } from './language-toggle.vue';

View File

@ -12,6 +12,7 @@ defineOptions({
const appLocale = defineModel<string>('appLocale'); const appLocale = defineModel<string>('appLocale');
const appDynamicTitle = defineModel<boolean>('appDynamicTitle'); const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
const appWatermark = defineModel<boolean>('appWatermark'); const appWatermark = defineModel<boolean>('appWatermark');
const appEnableCheckUpdates = defineModel<boolean>('appEnableCheckUpdates');
</script> </script>
<template> <template>
@ -24,4 +25,7 @@ const appWatermark = defineModel<boolean>('appWatermark');
<SwitchItem v-model="appWatermark"> <SwitchItem v-model="appWatermark">
{{ $t('preferences.watermark') }} {{ $t('preferences.watermark') }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="appEnableCheckUpdates">
{{ $t('preferences.checkUpdates') }}
</SwitchItem>
</template> </template>

View File

@ -62,6 +62,7 @@ const appColorGrayMode = defineModel<boolean>('appColorGrayMode');
const appColorWeakMode = defineModel<boolean>('appColorWeakMode'); const appColorWeakMode = defineModel<boolean>('appColorWeakMode');
const appContentCompact = defineModel<ContentCompactType>('appContentCompact'); const appContentCompact = defineModel<ContentCompactType>('appContentCompact');
const appWatermark = defineModel<boolean>('appWatermark'); const appWatermark = defineModel<boolean>('appWatermark');
const appEnableCheckUpdates = defineModel<boolean>('appEnableCheckUpdates');
const transitionProgress = defineModel<boolean>('transitionProgress'); const transitionProgress = defineModel<boolean>('transitionProgress');
const transitionName = defineModel<string>('transitionName'); const transitionName = defineModel<string>('transitionName');
@ -254,6 +255,7 @@ async function handleReset() {
<Block :title="$t('preferences.general')"> <Block :title="$t('preferences.general')">
<General <General
v-model:app-dynamic-title="appDynamicTitle" v-model:app-dynamic-title="appDynamicTitle"
v-model:app-enable-check-updates="appEnableCheckUpdates"
v-model:app-locale="appLocale" v-model:app-locale="appLocale"
v-model:app-watermark="appWatermark" v-model:app-watermark="appWatermark"
/> />

View File

@ -60,6 +60,8 @@
"notifications": "Notifications", "notifications": "Notifications",
"markAllAsRead": "Make All as Read", "markAllAsRead": "Make All as Read",
"clearNotifications": "Clear", "clearNotifications": "Clear",
"checkUpdatesTitle": "New Version Available",
"checkUpdatesDescription": "Click to refresh and get the latest version",
"search": { "search": {
"title": "Search", "title": "Search",
"searchNavigate": "Search Navigation", "searchNavigate": "Search Navigation",
@ -166,6 +168,7 @@
"language": "Language", "language": "Language",
"dynamicTitle": "Dynamic Title", "dynamicTitle": "Dynamic Title",
"watermark": "Watermark", "watermark": "Watermark",
"checkUpdates": "Periodic update check",
"sidebar": { "sidebar": {
"title": "Sidebar", "title": "Sidebar",
"width": "Width", "width": "Width",

View File

@ -60,6 +60,8 @@
"notifications": "通知", "notifications": "通知",
"markAllAsRead": "全部标记为已读", "markAllAsRead": "全部标记为已读",
"clearNotifications": "清空", "clearNotifications": "清空",
"checkUpdatesTitle": "新版本可用",
"checkUpdatesDescription": "点击刷新以获取最新版本",
"search": { "search": {
"title": "搜索", "title": "搜索",
"searchNavigate": "搜索导航菜单", "searchNavigate": "搜索导航菜单",
@ -166,6 +168,7 @@
"language": "语言", "language": "语言",
"dynamicTitle": "动态标题", "dynamicTitle": "动态标题",
"watermark": "水印", "watermark": "水印",
"checkUpdates": "定时检查更新",
"sidebar": { "sidebar": {
"title": "侧边栏", "title": "侧边栏",
"width": "宽度", "width": "宽度",

View File

@ -219,8 +219,8 @@ importers:
specifier: ^3.2.0 specifier: ^3.2.0
version: 3.2.0(eslint@9.8.0) version: 3.2.0(eslint@9.8.0)
eslint-plugin-jsdoc: eslint-plugin-jsdoc:
specifier: ^48.8.3 specifier: ^48.9.2
version: 48.8.3(eslint@9.8.0) version: 48.9.2(eslint@9.8.0)
eslint-plugin-jsonc: eslint-plugin-jsonc:
specifier: ^2.16.0 specifier: ^2.16.0
version: 2.16.0(eslint@9.8.0) version: 2.16.0(eslint@9.8.0)
@ -280,13 +280,13 @@ importers:
dependencies: dependencies:
'@stylistic/stylelint-plugin': '@stylistic/stylelint-plugin':
specifier: ^2.1.2 specifier: ^2.1.2
version: 2.1.2(stylelint@16.7.0(typescript@5.5.4)) version: 2.1.2(stylelint@16.8.0(typescript@5.5.4))
stylelint-config-recess-order: stylelint-config-recess-order:
specifier: ^5.0.1 specifier: ^5.0.1
version: 5.0.1(stylelint@16.7.0(typescript@5.5.4)) version: 5.0.1(stylelint@16.8.0(typescript@5.5.4))
stylelint-scss: stylelint-scss:
specifier: ^6.4.1 specifier: ^6.4.1
version: 6.4.1(stylelint@16.7.0(typescript@5.5.4)) version: 6.4.1(stylelint@16.8.0(typescript@5.5.4))
devDependencies: devDependencies:
postcss: postcss:
specifier: ^8.4.40 specifier: ^8.4.40
@ -301,26 +301,26 @@ importers:
specifier: ^3.3.3 specifier: ^3.3.3
version: 3.3.3 version: 3.3.3
stylelint: stylelint:
specifier: ^16.7.0 specifier: ^16.8.0
version: 16.7.0(typescript@5.5.4) version: 16.8.0(typescript@5.5.4)
stylelint-config-recommended: stylelint-config-recommended:
specifier: ^14.0.1 specifier: ^14.0.1
version: 14.0.1(stylelint@16.7.0(typescript@5.5.4)) version: 14.0.1(stylelint@16.8.0(typescript@5.5.4))
stylelint-config-recommended-scss: stylelint-config-recommended-scss:
specifier: ^14.1.0 specifier: ^14.1.0
version: 14.1.0(postcss@8.4.40)(stylelint@16.7.0(typescript@5.5.4)) version: 14.1.0(postcss@8.4.40)(stylelint@16.8.0(typescript@5.5.4))
stylelint-config-recommended-vue: stylelint-config-recommended-vue:
specifier: ^1.5.0 specifier: ^1.5.0
version: 1.5.0(postcss-html@1.7.0)(stylelint@16.7.0(typescript@5.5.4)) version: 1.5.0(postcss-html@1.7.0)(stylelint@16.8.0(typescript@5.5.4))
stylelint-config-standard: stylelint-config-standard:
specifier: ^36.0.1 specifier: ^36.0.1
version: 36.0.1(stylelint@16.7.0(typescript@5.5.4)) version: 36.0.1(stylelint@16.8.0(typescript@5.5.4))
stylelint-order: stylelint-order:
specifier: ^6.0.4 specifier: ^6.0.4
version: 6.0.4(stylelint@16.7.0(typescript@5.5.4)) version: 6.0.4(stylelint@16.8.0(typescript@5.5.4))
stylelint-prettier: stylelint-prettier:
specifier: ^5.0.2 specifier: ^5.0.2
version: 5.0.2(prettier@3.3.3)(stylelint@16.7.0(typescript@5.5.4)) version: 5.0.2(prettier@3.3.3)(stylelint@16.8.0(typescript@5.5.4))
internal/node-utils: internal/node-utils:
dependencies: dependencies:
@ -358,8 +358,8 @@ importers:
internal/tailwind-config: internal/tailwind-config:
dependencies: dependencies:
'@iconify/json': '@iconify/json':
specifier: ^2.2.231 specifier: ^2.2.232
version: 2.2.231 version: 2.2.232
'@iconify/tailwind': '@iconify/tailwind':
specifier: ^1.1.2 specifier: ^1.1.2
version: 1.1.2 version: 1.1.2
@ -944,6 +944,9 @@ importers:
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.0 version: 1.1.0
devDependencies: devDependencies:
'@vite-pwa/vitepress':
specifier: ^0.5.0
version: 0.5.0(vite-plugin-pwa@0.20.1(vite@5.3.5(@types/node@22.0.0)(sass@1.77.8)(terser@5.31.3))(workbox-build@7.1.1)(workbox-window@7.1.0))
vitepress: vitepress:
specifier: ^1.3.1 specifier: ^1.3.1
version: 1.3.1(@algolia/client-search@4.24.0)(@types/node@22.0.0)(async-validator@4.2.5)(axios@1.7.2)(nprogress@0.2.0)(postcss@8.4.40)(qrcode@1.5.3)(sass@1.77.8)(search-insights@2.15.0)(sortablejs@1.15.2)(terser@5.31.3)(typescript@5.5.4) version: 1.3.1(@algolia/client-search@4.24.0)(@types/node@22.0.0)(async-validator@4.2.5)(axios@1.7.2)(nprogress@0.2.0)(postcss@8.4.40)(qrcode@1.5.3)(sass@1.77.8)(search-insights@2.15.0)(sortablejs@1.15.2)(terser@5.31.3)(typescript@5.5.4)
@ -2988,8 +2991,8 @@ packages:
resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
engines: {node: '>=18.18'} engines: {node: '>=18.18'}
'@iconify/json@2.2.231': '@iconify/json@2.2.232':
resolution: {integrity: sha512-+KlOkI3CuwSuG8H3EIeC7f5LTsm73aggoh1GA9Uh4YCl65zvTgYyFwCxJXnR2vVeCoAlO2UtCtjHjNwOWchf4g==} resolution: {integrity: sha512-o1W5bNDuXTd3ugywfX9uRmhuJTKYOLhH4qk+eUpkR8WEYdTmMW+FSiFO5R2KqD94Tzoi8Ef8u/R7felU5xk3eQ==}
'@iconify/tailwind@1.1.2': '@iconify/tailwind@1.1.2':
resolution: {integrity: sha512-ZgToKxxd7zF5T9NXPnY9APRF06ZjFF21H/bINzcbKTdeJzLrNLIoVaoePIUbWVQ2HAac5cAYEHPZO8ILSUe3bQ==} resolution: {integrity: sha512-ZgToKxxd7zF5T9NXPnY9APRF06ZjFF21H/bINzcbKTdeJzLrNLIoVaoePIUbWVQ2HAac5cAYEHPZO8ILSUe3bQ==}
@ -3780,6 +3783,15 @@ packages:
engines: {node: '>=16'} engines: {node: '>=16'}
hasBin: true hasBin: true
'@vite-pwa/vitepress@0.5.0':
resolution: {integrity: sha512-a+BnACLMYOf/u2o6RhOJIdJgOW9wym9mTJGWbOLzFHE+fPjg+z1t/Xqm9LvOBQYEDIkrGrf+KxN4COQ0B8hbHg==}
peerDependencies:
'@vite-pwa/assets-generator': ^0.2.4
vite-plugin-pwa: '>=0.20.0 <1'
peerDependenciesMeta:
'@vite-pwa/assets-generator':
optional: true
'@vitejs/plugin-vue-jsx@4.0.0': '@vitejs/plugin-vue-jsx@4.0.0':
resolution: {integrity: sha512-A+6wL2AdQhDsLsDnY+2v4rRDI1HLJGIMc97a8FURO9tqKsH5QvjWrzsa5DH3NlZsM742W2wODl2fF+bfcTWtXw==} resolution: {integrity: sha512-A+6wL2AdQhDsLsDnY+2v4rRDI1HLJGIMc97a8FURO9tqKsH5QvjWrzsa5DH3NlZsM742W2wODl2fF+bfcTWtXw==}
engines: {node: ^18.0.0 || >=20.0.0} engines: {node: ^18.0.0 || >=20.0.0}
@ -4880,6 +4892,15 @@ packages:
supports-color: supports-color:
optional: true optional: true
debug@4.3.6:
resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
decamelize@1.2.0: decamelize@1.2.0:
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -5247,8 +5268,8 @@ packages:
peerDependencies: peerDependencies:
eslint: ^8.56.0 || ^9.0.0-0 eslint: ^8.56.0 || ^9.0.0-0
eslint-plugin-jsdoc@48.8.3: eslint-plugin-jsdoc@48.9.2:
resolution: {integrity: sha512-AtIvwwW9D17MRkM0Z0y3/xZYaa9mdAvJrkY6fU/HNUwGbmMtHVvK4qRM9CDixGVtfNrQitb8c6zQtdh6cTOvLg==} resolution: {integrity: sha512-ydqg2lEY/WxhMXEb1ZAn+yRbc43DlKYdMP/nUreF5ODE1P9mgeff8atL16lYNNKOvYxNOzL85/5gFVeGylSioA==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
@ -7451,6 +7472,9 @@ packages:
postcss-resolve-nested-selector@0.1.1: postcss-resolve-nested-selector@0.1.1:
resolution: {integrity: sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==} resolution: {integrity: sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==}
postcss-resolve-nested-selector@0.1.4:
resolution: {integrity: sha512-R6vHqZWgVnTAPq0C+xjyHfEZqfIYboCBVSy24MjxEDm+tIh1BU4O6o7DP7AA7kHzf136d+Qc5duI4tlpHjixDw==}
postcss-safe-parser@6.0.0: postcss-safe-parser@6.0.0:
resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==}
engines: {node: '>=12.0'} engines: {node: '>=12.0'}
@ -8310,8 +8334,8 @@ packages:
peerDependencies: peerDependencies:
stylelint: ^16.0.2 stylelint: ^16.0.2
stylelint@16.7.0: stylelint@16.8.0:
resolution: {integrity: sha512-Q1ATiXlz+wYr37a7TGsfvqYn2nSR3T/isw3IWlZQzFzCNoACHuGBb6xBplZXz56/uDRJHIygxjh7jbV/8isewA==} resolution: {integrity: sha512-Jjr40w3PXDiJVW6c9swLM0a1e0DgDm/XkFozc4XgAcREFas+/nchzmDmeVxazbzXgpDrwm+cW6W6iGtZqYUh+g==}
engines: {node: '>=18.12.0'} engines: {node: '>=18.12.0'}
hasBin: true hasBin: true
@ -11402,7 +11426,7 @@ snapshots:
'@humanwhocodes/retry@0.3.0': {} '@humanwhocodes/retry@0.3.0': {}
'@iconify/json@2.2.231': '@iconify/json@2.2.232':
dependencies: dependencies:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
pathe: 1.1.2 pathe: 1.1.2
@ -12046,7 +12070,7 @@ snapshots:
'@sindresorhus/merge-streams@2.3.0': {} '@sindresorhus/merge-streams@2.3.0': {}
'@stylistic/stylelint-plugin@2.1.2(stylelint@16.7.0(typescript@5.5.4))': '@stylistic/stylelint-plugin@2.1.2(stylelint@16.8.0(typescript@5.5.4))':
dependencies: dependencies:
'@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1) '@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1)
'@csstools/css-tokenizer': 2.4.1 '@csstools/css-tokenizer': 2.4.1
@ -12055,7 +12079,7 @@ snapshots:
postcss-selector-parser: 6.1.1 postcss-selector-parser: 6.1.1
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0
style-search: 0.1.0 style-search: 0.1.0
stylelint: 16.7.0(typescript@5.5.4) stylelint: 16.8.0(typescript@5.5.4)
'@surma/rollup-plugin-off-main-thread@2.2.3': '@surma/rollup-plugin-off-main-thread@2.2.3':
dependencies: dependencies:
@ -12310,6 +12334,10 @@ snapshots:
- encoding - encoding
- supports-color - supports-color
'@vite-pwa/vitepress@0.5.0(vite-plugin-pwa@0.20.1(vite@5.3.5(@types/node@22.0.0)(sass@1.77.8)(terser@5.31.3))(workbox-build@7.1.1)(workbox-window@7.1.0))':
dependencies:
vite-plugin-pwa: 0.20.1(vite@5.3.5(@types/node@22.0.0)(sass@1.77.8)(terser@5.31.3))(workbox-build@7.1.1)(workbox-window@7.1.0)
'@vitejs/plugin-vue-jsx@4.0.0(vite@5.3.5(@types/node@22.0.0)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34(typescript@5.5.4))': '@vitejs/plugin-vue-jsx@4.0.0(vite@5.3.5(@types/node@22.0.0)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34(typescript@5.5.4))':
dependencies: dependencies:
'@babel/core': 7.24.9 '@babel/core': 7.24.9
@ -13635,6 +13663,10 @@ snapshots:
dependencies: dependencies:
ms: 2.1.2 ms: 2.1.2
debug@4.3.6:
dependencies:
ms: 2.1.2
decamelize@1.2.0: {} decamelize@1.2.0: {}
decimal.js@10.4.3: {} decimal.js@10.4.3: {}
@ -14119,7 +14151,7 @@ snapshots:
- supports-color - supports-color
- typescript - typescript
eslint-plugin-jsdoc@48.8.3(eslint@9.8.0): eslint-plugin-jsdoc@48.9.2(eslint@9.8.0):
dependencies: dependencies:
'@es-joy/jsdoccomment': 0.46.0 '@es-joy/jsdoccomment': 0.46.0
are-docs-informative: 0.0.2 are-docs-informative: 0.0.2
@ -16487,6 +16519,8 @@ snapshots:
postcss-resolve-nested-selector@0.1.1: {} postcss-resolve-nested-selector@0.1.1: {}
postcss-resolve-nested-selector@0.1.4: {}
postcss-safe-parser@6.0.0(postcss@8.4.40): postcss-safe-parser@6.0.0(postcss@8.4.40):
dependencies: dependencies:
postcss: 8.4.40 postcss: 8.4.40
@ -17296,64 +17330,64 @@ snapshots:
postcss: 8.4.40 postcss: 8.4.40
postcss-selector-parser: 6.1.1 postcss-selector-parser: 6.1.1
stylelint-config-html@1.1.0(postcss-html@1.7.0)(stylelint@16.7.0(typescript@5.5.4)): stylelint-config-html@1.1.0(postcss-html@1.7.0)(stylelint@16.8.0(typescript@5.5.4)):
dependencies: dependencies:
postcss-html: 1.7.0 postcss-html: 1.7.0
stylelint: 16.7.0(typescript@5.5.4) stylelint: 16.8.0(typescript@5.5.4)
stylelint-config-recess-order@5.0.1(stylelint@16.7.0(typescript@5.5.4)): stylelint-config-recess-order@5.0.1(stylelint@16.8.0(typescript@5.5.4)):
dependencies: dependencies:
stylelint: 16.7.0(typescript@5.5.4) stylelint: 16.8.0(typescript@5.5.4)
stylelint-order: 6.0.4(stylelint@16.7.0(typescript@5.5.4)) stylelint-order: 6.0.4(stylelint@16.8.0(typescript@5.5.4))
stylelint-config-recommended-scss@14.1.0(postcss@8.4.40)(stylelint@16.7.0(typescript@5.5.4)): stylelint-config-recommended-scss@14.1.0(postcss@8.4.40)(stylelint@16.8.0(typescript@5.5.4)):
dependencies: dependencies:
postcss-scss: 4.0.9(postcss@8.4.40) postcss-scss: 4.0.9(postcss@8.4.40)
stylelint: 16.7.0(typescript@5.5.4) stylelint: 16.8.0(typescript@5.5.4)
stylelint-config-recommended: 14.0.1(stylelint@16.7.0(typescript@5.5.4)) stylelint-config-recommended: 14.0.1(stylelint@16.8.0(typescript@5.5.4))
stylelint-scss: 6.4.1(stylelint@16.7.0(typescript@5.5.4)) stylelint-scss: 6.4.1(stylelint@16.8.0(typescript@5.5.4))
optionalDependencies: optionalDependencies:
postcss: 8.4.40 postcss: 8.4.40
stylelint-config-recommended-vue@1.5.0(postcss-html@1.7.0)(stylelint@16.7.0(typescript@5.5.4)): stylelint-config-recommended-vue@1.5.0(postcss-html@1.7.0)(stylelint@16.8.0(typescript@5.5.4)):
dependencies: dependencies:
postcss-html: 1.7.0 postcss-html: 1.7.0
semver: 7.6.3 semver: 7.6.3
stylelint: 16.7.0(typescript@5.5.4) stylelint: 16.8.0(typescript@5.5.4)
stylelint-config-html: 1.1.0(postcss-html@1.7.0)(stylelint@16.7.0(typescript@5.5.4)) stylelint-config-html: 1.1.0(postcss-html@1.7.0)(stylelint@16.8.0(typescript@5.5.4))
stylelint-config-recommended: 14.0.1(stylelint@16.7.0(typescript@5.5.4)) stylelint-config-recommended: 14.0.1(stylelint@16.8.0(typescript@5.5.4))
stylelint-config-recommended@14.0.1(stylelint@16.7.0(typescript@5.5.4)): stylelint-config-recommended@14.0.1(stylelint@16.8.0(typescript@5.5.4)):
dependencies: dependencies:
stylelint: 16.7.0(typescript@5.5.4) stylelint: 16.8.0(typescript@5.5.4)
stylelint-config-standard@36.0.1(stylelint@16.7.0(typescript@5.5.4)): stylelint-config-standard@36.0.1(stylelint@16.8.0(typescript@5.5.4)):
dependencies: dependencies:
stylelint: 16.7.0(typescript@5.5.4) stylelint: 16.8.0(typescript@5.5.4)
stylelint-config-recommended: 14.0.1(stylelint@16.7.0(typescript@5.5.4)) stylelint-config-recommended: 14.0.1(stylelint@16.8.0(typescript@5.5.4))
stylelint-order@6.0.4(stylelint@16.7.0(typescript@5.5.4)): stylelint-order@6.0.4(stylelint@16.8.0(typescript@5.5.4)):
dependencies: dependencies:
postcss: 8.4.40 postcss: 8.4.40
postcss-sorting: 8.0.2(postcss@8.4.40) postcss-sorting: 8.0.2(postcss@8.4.40)
stylelint: 16.7.0(typescript@5.5.4) stylelint: 16.8.0(typescript@5.5.4)
stylelint-prettier@5.0.2(prettier@3.3.3)(stylelint@16.7.0(typescript@5.5.4)): stylelint-prettier@5.0.2(prettier@3.3.3)(stylelint@16.8.0(typescript@5.5.4)):
dependencies: dependencies:
prettier: 3.3.3 prettier: 3.3.3
prettier-linter-helpers: 1.0.0 prettier-linter-helpers: 1.0.0
stylelint: 16.7.0(typescript@5.5.4) stylelint: 16.8.0(typescript@5.5.4)
stylelint-scss@6.4.1(stylelint@16.7.0(typescript@5.5.4)): stylelint-scss@6.4.1(stylelint@16.8.0(typescript@5.5.4)):
dependencies: dependencies:
known-css-properties: 0.34.0 known-css-properties: 0.34.0
postcss-media-query-parser: 0.2.3 postcss-media-query-parser: 0.2.3
postcss-resolve-nested-selector: 0.1.1 postcss-resolve-nested-selector: 0.1.1
postcss-selector-parser: 6.1.1 postcss-selector-parser: 6.1.1
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0
stylelint: 16.7.0(typescript@5.5.4) stylelint: 16.8.0(typescript@5.5.4)
stylelint@16.7.0(typescript@5.5.4): stylelint@16.8.0(typescript@5.5.4):
dependencies: dependencies:
'@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1) '@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1)
'@csstools/css-tokenizer': 2.4.1 '@csstools/css-tokenizer': 2.4.1
@ -17365,7 +17399,7 @@ snapshots:
cosmiconfig: 9.0.0(typescript@5.5.4) cosmiconfig: 9.0.0(typescript@5.5.4)
css-functions-list: 3.2.2 css-functions-list: 3.2.2
css-tree: 2.3.1 css-tree: 2.3.1
debug: 4.3.5 debug: 4.3.6
fast-glob: 3.3.2 fast-glob: 3.3.2
fastest-levenshtein: 1.0.16 fastest-levenshtein: 1.0.16
file-entry-cache: 9.0.0 file-entry-cache: 9.0.0
@ -17383,7 +17417,7 @@ snapshots:
normalize-path: 3.0.0 normalize-path: 3.0.0
picocolors: 1.0.1 picocolors: 1.0.1
postcss: 8.4.40 postcss: 8.4.40
postcss-resolve-nested-selector: 0.1.1 postcss-resolve-nested-selector: 0.1.4
postcss-safe-parser: 7.0.0(postcss@8.4.40) postcss-safe-parser: 7.0.0(postcss@8.4.40)
postcss-selector-parser: 6.1.1 postcss-selector-parser: 6.1.1
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0

View File

@ -1,109 +1,116 @@
import type { DefaultTheme, HeadConfig } from 'vitepress'; import type { DefaultTheme, HeadConfig } from 'vitepress';
import { resolve } from 'node:path';
import { type PwaOptions, withPwa } from '@vite-pwa/vitepress';
import { defineConfigWithTheme } from 'vitepress'; import { defineConfigWithTheme } from 'vitepress';
import { version } from '../../package.json'; import { version } from '../../package.json';
export default defineConfigWithTheme({ export default withPwa(
description: 'Vben Admin& 企业级管理系统框架', defineConfigWithTheme({
head: head(), description: 'Vben Admin& 企业级管理系统框架',
lang: 'zh', head: head(),
srcDir: 'src', lang: 'zh',
// locales: { pwa: pwa(),
// en: { // locales: {
// label: 'English', // en: {
// lang: 'en', // label: 'English',
// link: '/en/', // lang: 'en',
// }, // link: '/en/',
// root: { // },
// label: '简体中文', // root: {
// lang: 'zh-CN', // label: '简体中文',
// }, // lang: 'zh-CN',
themeConfig: { srcDir: 'src',
darkModeSwitchLabel: '主题', // },
darkModeSwitchTitle: '切换到深色模式', themeConfig: {
docFooter: { darkModeSwitchLabel: '主题',
next: '下一页', darkModeSwitchTitle: '切换到深色模式',
prev: '上一页', docFooter: {
}, next: '下一页',
editLink: { prev: '上一页',
pattern: 'https://github.com/vbenjs/vue-vben-admin/edit/main/docs/:path',
text: '在 GitHub 上编辑此页面',
},
footer: {
copyright: `Copyright © 2020-${new Date().getFullYear()} Vben`,
message: '基于 MIT 许可发布.',
},
i18nRouting: true,
langMenuLabel: '多语言',
lastUpdated: {
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium',
}, },
text: '最后更新于', editLink: {
}, pattern:
lightModeSwitchTitle: '切换到浅色模式', 'https://github.com/vbenjs/vue-vben-admin/edit/main/docs/:path',
logo: 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/logo-v1.webp', text: '在 GitHub 上编辑此页面',
nav: nav(), },
outline: { footer: {
label: '页面导航', copyright: `Copyright © 2020-${new Date().getFullYear()} Vben`,
}, message: '基于 MIT 许可发布.',
returnToTopLabel: '回到顶部', },
search: { i18nRouting: true,
options: { langMenuLabel: '多语言',
locales: { lastUpdated: {
zh: { formatOptions: {
translations: { dateStyle: 'short',
button: { timeStyle: 'medium',
buttonAriaLabel: '搜索文档', },
buttonText: '搜索文档', text: '最后更新于',
}, },
modal: { lightModeSwitchTitle: '切换到浅色模式',
footer: { logo: 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/logo-v1.webp',
navigateText: '切换', nav: nav(),
selectText: '选择', outline: {
label: '页面导航',
},
returnToTopLabel: '回到顶部',
search: {
options: {
locales: {
zh: {
translations: {
button: {
buttonAriaLabel: '搜索文档',
buttonText: '搜索文档',
},
modal: {
footer: {
navigateText: '切换',
selectText: '选择',
},
noResultsText: '无法找到相关结果',
resetButtonTitle: '清除查询条件',
}, },
noResultsText: '无法找到相关结果',
resetButtonTitle: '清除查询条件',
}, },
}, },
}, },
}, },
provider: 'local',
}, },
provider: 'local', sidebar: {
}, '/commercial/': { base: '/commercial/', items: sidebarCommercial() },
sidebar: { '/guide/': { base: '/guide/', items: sidebarGuide() },
'/commercial/': { base: '/commercial/', items: sidebarCommercial() },
'/guide/': { base: '/guide/', items: sidebarGuide() },
},
sidebarMenuLabel: '菜单',
siteTitle: 'Vben Admin',
socialLinks: [
{ icon: 'github', link: 'https://github.com/vbenjs/vue-vben-admin' },
],
},
title: 'Vben Admin',
vite: {
build: {
chunkSizeWarningLimit: Infinity,
minify: 'terser',
},
json: {
stringify: true,
},
server: {
fs: {
allow: ['../..'],
}, },
host: true, sidebarMenuLabel: '菜单',
port: 6173, siteTitle: 'Vben Admin',
socialLinks: [
{ icon: 'github', link: 'https://github.com/vbenjs/vue-vben-admin' },
],
}, },
ssr: { title: 'Vben Admin',
external: ['@vue/repl'], vite: {
build: {
chunkSizeWarningLimit: Infinity,
minify: 'terser',
},
json: {
stringify: true,
},
server: {
fs: {
allow: ['../..'],
},
host: true,
port: 6173,
},
ssr: {
external: ['@vue/repl'],
},
}, },
}, }),
}); );
function nav(): DefaultTheme.NavItem[] { function nav(): DefaultTheme.NavItem[] {
return [ return [
@ -306,18 +313,36 @@ function head(): HeadConfig[] {
// src: 'https://cdn.tailwindcss.com', // src: 'https://cdn.tailwindcss.com',
// }, // },
// ], // ],
[
'script',
{},
`
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?2e443a834727c065877c01d89921545e";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
`,
],
]; ];
} }
function pwa(): PwaOptions {
return {
includeManifestIcons: false,
manifest: {
description:
'Vben Admin is a modern admin dashboard template based on Vue 3. ',
icons: [
{
sizes: '192x192',
src: 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/pwa-icon-192.png',
type: 'image/png',
},
{
sizes: '512x512',
src: 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/pwa-icon-512.png',
type: 'image/png',
},
],
id: '/',
name: 'Vben Admin Doc',
short_name: 'vben_admin_doc',
theme_color: '#ffffff',
},
outDir: resolve(process.cwd(), '.vitepress/dist'),
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{css,js,html,svg,png,ico,txt,woff2}'],
},
};
}

View File

@ -5,14 +5,17 @@ import DefaultTheme from 'vitepress/theme';
import SiteLayout from './components/site-layout.vue'; import SiteLayout from './components/site-layout.vue';
import VbenContributors from './components/vben-contributors.vue'; import VbenContributors from './components/vben-contributors.vue';
import { initHmPlugin } from './plugins/hm';
import './styles'; import './styles';
export default { export default {
// eslint-disable-next-line @typescript-eslint/no-unused-vars enhanceApp({ app }) {
enhanceApp({ app, router, siteData }) {
// ... // ...
app.component('VbenContributors', VbenContributors); app.component('VbenContributors', VbenContributors);
// 百度统计
initHmPlugin();
}, },
extends: DefaultTheme, extends: DefaultTheme,
Layout: SiteLayout, Layout: SiteLayout,

View File

@ -0,0 +1,28 @@
import { inBrowser } from 'vitepress';
const SITE_ID = '2e443a834727c065877c01d89921545e';
declare global {
interface Window {
_hmt: any;
}
}
function registerAnalytics() {
window._hmt = window._hmt || [];
const script = document.createElement('script');
script.innerHTML = `var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?${SITE_ID}";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})()`;
document.querySelector('head')?.append(script);
}
export function initHmPlugin() {
if (inBrowser && import.meta.env.PROD) {
registerAnalytics(SITE_ID);
}
}

View File

@ -11,6 +11,7 @@
"medium-zoom": "^1.1.0" "medium-zoom": "^1.1.0"
}, },
"devDependencies": { "devDependencies": {
"@vite-pwa/vitepress": "^0.5.0",
"vitepress": "^1.3.1", "vitepress": "^1.3.1",
"vue": "^3.4.34" "vue": "^3.4.34"
} }

View File

@ -174,6 +174,7 @@ const defaultPreferences: Preferences = {
app: { app: {
accessMode: 'frontend', accessMode: 'frontend',
authPageLayout: 'panel-right', authPageLayout: 'panel-right',
checkUpdatesPollingTime: 1,
colorGrayMode: false, colorGrayMode: false,
colorWeakMode: false, colorWeakMode: false,
compact: false, compact: false,
@ -181,11 +182,12 @@ const defaultPreferences: Preferences = {
defaultAvatar: defaultAvatar:
'https://unpkg.com/@vbenjs/static-source@0.1.5/source/avatar-v1.webp', 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/avatar-v1.webp',
dynamicTitle: true, dynamicTitle: true,
enableCheckUpdates: true,
enablePreferences: true, enablePreferences: true,
isMobile: false, isMobile: false,
layout: 'sidebar-nav', layout: 'sidebar-nav',
locale: 'zh-CN', locale: 'zh-CN',
loginExpiredMode: 'page', loginExpiredMode: 'modal',
name: 'Vben Admin', name: 'Vben Admin',
watermark: false, watermark: false,
}, },
@ -288,6 +290,8 @@ interface AppPreferences {
accessMode: AccessModeType; accessMode: AccessModeType;
/** 登录注册页面布局 */ /** 登录注册页面布局 */
authPageLayout: AuthPageLayoutType; authPageLayout: AuthPageLayoutType;
/** 检查更新轮询时间 */
checkUpdatesPollingTime: number;
/** 是否开启灰色模式 */ /** 是否开启灰色模式 */
colorGrayMode: boolean; colorGrayMode: boolean;
/** 是否开启色弱模式 */ /** 是否开启色弱模式 */
@ -300,6 +304,8 @@ interface AppPreferences {
defaultAvatar: string; defaultAvatar: string;
// /** 开启动态标题 */ // /** 开启动态标题 */
dynamicTitle: boolean; dynamicTitle: boolean;
/** 是否开启检查更新 */
enableCheckUpdates: boolean;
/** 是否显示偏好设置 */ /** 是否显示偏好设置 */
enablePreferences: boolean; enablePreferences: boolean;
/** 是否移动端 */ /** 是否移动端 */

View File

@ -16,7 +16,7 @@
4. 在下面列表找不到问题可以到 issue 提问 [issues](https://github.com/vbenjs/vue-vben-admin/issues) 4. 在下面列表找不到问题可以到 issue 提问 [issues](https://github.com/vbenjs/vue-vben-admin/issues)
5. 如果不是问题类型的,需要讨论的,请到 [discussions](https://github.com/vbenjs/vue-vben-admin/discussions) 讨论 5. 如果不是问题类型的,需要讨论的,请到 [discussions](https://github.com/vbenjs/vue-vben-admin/discussions) 讨论
## 依赖安装问题 ## 依赖问题
`Monorepo` 项目下,需要养成每次 `git pull`代码都要执行`pnpm install`的习惯,因为经常会有新的依赖包加入,项目在`.husky/git-merge`已经配置了自动执行`pnpm install`,但是有时候会出现问题,如果没有自动执行,建议手动执行一次。 `Monorepo` 项目下,需要养成每次 `git pull`代码都要执行`pnpm install`的习惯,因为经常会有新的依赖包加入,项目在`.husky/git-merge`已经配置了自动执行`pnpm install`,但是有时候会出现问题,如果没有自动执行,建议手动执行一次。
@ -135,3 +135,21 @@ at Object.extractor (vue-vben-admin-main\node_modules@purge-icons\core\dist\inde
at Extract (vue-vben-admin-main\node_modules@purge-icons\core\dist\index.js:173:54) at Extract (vue-vben-admin-main\node_modules@purge-icons\core\dist\index.js:173:54)
``` ```
## nginx 部署
部署到 `nginx`后,可能会出现以下错误:
```bash
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "application/octet-stream". Strict MIME type checking is enforced for module scripts per HTML spec.
```
解决方式:
```bash
http {
types {
application/javascript js mjs;
}
}
```

6
website/tsconfig.json Normal file
View File

@ -0,0 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web.json",
"include": [".vitepress/*.mts", ".vitepress/**/*.ts", ".vitepress/**/*.vue"],
"exclude": ["node_modules"]
}