1
0
Эх сурвалжийг харах

refactor: refactor AuthLayout to configure the login page more freely (#4294)

Vben 6 сар өмнө
parent
commit
8404c12129
31 өөрчлөгдсөн 316 нэмэгдсэн , 117 устгасан
  1. 23 0
      apps/web-antd/src/layouts/auth.vue
  2. 1 3
      apps/web-antd/src/layouts/index.ts
  3. 19 1
      apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue
  4. 23 0
      apps/web-ele/src/layouts/auth.vue
  5. 1 3
      apps/web-ele/src/layouts/index.ts
  6. 19 1
      apps/web-ele/src/views/dashboard/analytics/analytics-trends.vue
  7. 23 0
      apps/web-naive/src/layouts/auth.vue
  8. 1 3
      apps/web-naive/src/layouts/index.ts
  9. 19 1
      apps/web-naive/src/views/dashboard/analytics/analytics-trends.vue
  10. 1 1
      docs/src/en/guide/essentials/settings.md
  11. 1 1
      docs/src/en/guide/in-depth/login.md
  12. 3 3
      docs/src/en/guide/in-depth/theme.md
  13. 1 1
      docs/src/guide/essentials/settings.md
  14. 13 20
      docs/src/guide/in-depth/login.md
  15. 3 3
      docs/src/guide/in-depth/theme.md
  16. 2 1
      packages/@core/base/design/src/design-tokens/default/index.css
  17. 1 1
      packages/@core/preferences/src/config.ts
  18. 2 2
      packages/@core/preferences/src/constants.ts
  19. 2 2
      packages/@core/ui-kit/popup-ui/src/modal/modal.vue
  20. 2 2
      packages/@core/ui-kit/shadcn-ui/src/components/input-password/input-password.vue
  21. 1 1
      packages/@core/ui-kit/shadcn-ui/src/components/spinner/loading.vue
  22. 1 1
      packages/@core/ui-kit/shadcn-ui/src/components/spinner/spinner.vue
  23. 33 21
      packages/effects/common-ui/src/ui/authentication/login.vue
  24. 65 26
      packages/effects/layouts/src/authentication/authentication.vue
  25. 4 12
      packages/effects/layouts/src/authentication/form.vue
  26. 4 3
      packages/effects/layouts/src/authentication/toolbar.vue
  27. 1 0
      packages/effects/layouts/src/authentication/types.ts
  28. 4 0
      packages/effects/layouts/src/widgets/check-updates/check-updates.vue
  29. 23 0
      playground/src/layouts/auth.vue
  30. 1 3
      playground/src/layouts/index.ts
  31. 19 1
      playground/src/views/dashboard/analytics/analytics-trends.vue

+ 23 - 0
apps/web-antd/src/layouts/auth.vue

@@ -0,0 +1,23 @@
+<script lang="ts" setup>
+import { computed } from 'vue';
+
+import { AuthPageLayout } from '@vben/layouts';
+import { preferences } from '@vben/preferences';
+
+import { $t } from '#/locales';
+
+const appName = computed(() => preferences.app.name);
+const logo = computed(() => preferences.logo.source);
+</script>
+
+<template>
+  <AuthPageLayout
+    :app-name="appName"
+    :logo="logo"
+    :page-description="$t('authentication.pageDesc')"
+    :page-title="$t('authentication.pageTitle')"
+  >
+    <!-- 自定义工具栏 -->
+    <!-- <template #toolbar></template> -->
+  </AuthPageLayout>
+</template>

+ 1 - 3
apps/web-antd/src/layouts/index.ts

@@ -1,8 +1,6 @@
 const BasicLayout = () => import('./basic.vue');
+const AuthPageLayout = () => import('./auth.vue');
 
 const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
 
-const AuthPageLayout = () =>
-  import('@vben/layouts').then((m) => m.AuthPageLayout);
-
 export { AuthPageLayout, BasicLayout, IFrameView };

+ 19 - 1
apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue

@@ -55,12 +55,27 @@ onMounted(() => {
       },
       trigger: 'axis',
     },
+    // xAxis: {
+    //   axisTick: {
+    //     show: false,
+    //   },
+    //   boundaryGap: false,
+    //   data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
+    //   type: 'category',
+    // },
     xAxis: {
       axisTick: {
         show: false,
       },
       boundaryGap: false,
       data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
+      splitLine: {
+        lineStyle: {
+          type: 'solid',
+          width: 1,
+        },
+        show: true,
+      },
       type: 'category',
     },
     yAxis: [
@@ -69,7 +84,10 @@ onMounted(() => {
           show: false,
         },
         max: 80_000,
-
+        splitArea: {
+          show: true,
+        },
+        splitNumber: 4,
         type: 'value',
       },
     ],

+ 23 - 0
apps/web-ele/src/layouts/auth.vue

@@ -0,0 +1,23 @@
+<script lang="ts" setup>
+import { computed } from 'vue';
+
+import { AuthPageLayout } from '@vben/layouts';
+import { preferences } from '@vben/preferences';
+
+import { $t } from '#/locales';
+
+const appName = computed(() => preferences.app.name);
+const logo = computed(() => preferences.logo.source);
+</script>
+
+<template>
+  <AuthPageLayout
+    :app-name="appName"
+    :logo="logo"
+    :page-description="$t('authentication.pageDesc')"
+    :page-title="$t('authentication.pageTitle')"
+  >
+    <!-- 自定义工具栏 -->
+    <!-- <template #toolbar></template> -->
+  </AuthPageLayout>
+</template>

+ 1 - 3
apps/web-ele/src/layouts/index.ts

@@ -1,8 +1,6 @@
 const BasicLayout = () => import('./basic.vue');
+const AuthPageLayout = () => import('./auth.vue');
 
 const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
 
-const AuthPageLayout = () =>
-  import('@vben/layouts').then((m) => m.AuthPageLayout);
-
 export { AuthPageLayout, BasicLayout, IFrameView };

+ 19 - 1
apps/web-ele/src/views/dashboard/analytics/analytics-trends.vue

@@ -55,12 +55,27 @@ onMounted(() => {
       },
       trigger: 'axis',
     },
+    // xAxis: {
+    //   axisTick: {
+    //     show: false,
+    //   },
+    //   boundaryGap: false,
+    //   data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
+    //   type: 'category',
+    // },
     xAxis: {
       axisTick: {
         show: false,
       },
       boundaryGap: false,
       data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
+      splitLine: {
+        lineStyle: {
+          type: 'solid',
+          width: 1,
+        },
+        show: true,
+      },
       type: 'category',
     },
     yAxis: [
@@ -69,7 +84,10 @@ onMounted(() => {
           show: false,
         },
         max: 80_000,
-
+        splitArea: {
+          show: true,
+        },
+        splitNumber: 4,
         type: 'value',
       },
     ],

+ 23 - 0
apps/web-naive/src/layouts/auth.vue

@@ -0,0 +1,23 @@
+<script lang="ts" setup>
+import { computed } from 'vue';
+
+import { AuthPageLayout } from '@vben/layouts';
+import { preferences } from '@vben/preferences';
+
+import { $t } from '#/locales';
+
+const appName = computed(() => preferences.app.name);
+const logo = computed(() => preferences.logo.source);
+</script>
+
+<template>
+  <AuthPageLayout
+    :app-name="appName"
+    :logo="logo"
+    :page-description="$t('authentication.pageDesc')"
+    :page-title="$t('authentication.pageTitle')"
+  >
+    <!-- 自定义工具栏 -->
+    <!-- <template #toolbar></template> -->
+  </AuthPageLayout>
+</template>

+ 1 - 3
apps/web-naive/src/layouts/index.ts

@@ -1,8 +1,6 @@
 const BasicLayout = () => import('./basic.vue');
+const AuthPageLayout = () => import('./auth.vue');
 
 const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
 
-const AuthPageLayout = () =>
-  import('@vben/layouts').then((m) => m.AuthPageLayout);
-
 export { AuthPageLayout, BasicLayout, IFrameView };

+ 19 - 1
apps/web-naive/src/views/dashboard/analytics/analytics-trends.vue

@@ -55,12 +55,27 @@ onMounted(() => {
       },
       trigger: 'axis',
     },
+    // xAxis: {
+    //   axisTick: {
+    //     show: false,
+    //   },
+    //   boundaryGap: false,
+    //   data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
+    //   type: 'category',
+    // },
     xAxis: {
       axisTick: {
         show: false,
       },
       boundaryGap: false,
       data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
+      splitLine: {
+        lineStyle: {
+          type: 'solid',
+          width: 1,
+        },
+        show: true,
+      },
       type: 'category',
     },
     yAxis: [
@@ -69,7 +84,10 @@ onMounted(() => {
           show: false,
         },
         max: 80_000,
-
+        splitArea: {
+          show: true,
+        },
+        splitNumber: 4,
         type: 'value',
       },
     ],

+ 1 - 1
docs/src/en/guide/essentials/settings.md

@@ -240,7 +240,7 @@ const defaultPreferences: Preferences = {
   theme: {
     builtinType: 'default',
     colorDestructive: 'hsl(348 100% 61%)',
-    colorPrimary: 'hsl(231 98% 65%)',
+    colorPrimary: 'hsl(212 100% 45%)',
     colorSuccess: 'hsl(144 57% 58%)',
     colorWarning: 'hsl(42 84% 61%)',
     mode: 'dark',

+ 1 - 1
docs/src/en/guide/in-depth/login.md

@@ -18,7 +18,7 @@ You just need to configure the `props` parameter of `AuthPageLayout` in `src/rou
       pageTitle: "开箱即用的大型中后台管理系统",
       pageDescription: "工程化、高性能、跨组件库的前端模版",
       toolbar: true,
-      toolbarList: () => ['color', 'language', 'layout', 'theme'],
+      toolbarList: ['color', 'language', 'layout', 'theme'],
     }
     // ...
   },

+ 3 - 3
docs/src/en/guide/in-depth/theme.md

@@ -53,7 +53,7 @@ You can check the list below to understand all the available variables.
 
   /* Theme Colors */
 
-  --primary: 231 98% 65%;
+  --primary: 212 100% 45%;
   --primary-foreground: 0 0% 98%;
 
   /* Used for destructive actions such as <Button variant="destructive"> */
@@ -264,7 +264,7 @@ export const overridesPreferences = defineOverridesPreferences({
     // Error color
     colorDestructive: 'hsl(348 100% 61%)',
     // Primary color
-    colorPrimary: 'hsl(231 98% 65%)',
+    colorPrimary: 'hsl(212 100% 45%)',
     // Success color
     colorSuccess: 'hsl(144 57% 58%)',
     // Warning color
@@ -351,7 +351,7 @@ type BuiltinThemeType =
 
   /* Theme Colors */
 
-  --primary: 231 98% 65%;
+  --primary: 212 100% 45%;
   --primary-foreground: 0 0% 98%;
 
   /* Used for destructive actions such as <Button variant="destructive"> */

+ 1 - 1
docs/src/guide/essentials/settings.md

@@ -262,7 +262,7 @@ const defaultPreferences: Preferences = {
   theme: {
     builtinType: 'default',
     colorDestructive: 'hsl(348 100% 61%)',
-    colorPrimary: 'hsl(231 98% 65%)',
+    colorPrimary: 'hsl(212 100% 45%)',
     colorSuccess: 'hsl(144 57% 58%)',
     colorWarning: 'hsl(42 84% 61%)',
     mode: 'dark',

+ 13 - 20
docs/src/guide/in-depth/login.md

@@ -8,28 +8,21 @@
 
 ![login](/guide/login.png)
 
-只需要在应用下的 `src/router/routes/core.ts` 内,配置`AuthPageLayout`的 `props`参数即可:
-
-```ts {4-8}
- {
-    component: AuthPageLayout,
-    props: {
-      sloganImage: "xxx/xxx.png",
-      pageTitle: "开箱即用的大型中后台管理系统",
-      pageDescription: "工程化、高性能、跨组件库的前端模版",
-      toolbar: true,
-      toolbarList: () => ['color', 'language', 'layout', 'theme'],
-    }
-    // ...
-  },
+只需要在应用下的 `src/layouts/auth.vue` 内,配置`AuthPageLayout`的 `props`参数即可:
+
+```vue {2-7}
+<AuthPageLayout
+  :copyright="true"
+  :toolbar="true"
+  :toolbarList="['color', 'language', 'layout', 'theme']"
+  :app-name="appName"
+  :logo="logo"
+  :page-description="$t('authentication.pageDesc')"
+  :page-title="$t('authentication.pageTitle')"
+>
+</AuthPageLayout>
 ```
 
-::: tip
-
-如果这些配置不能满足你的需求,你可以自行实现登录页面。直接实现自己的 `AuthPageLayout`即可。
-
-:::
-
 ## 登录表单调整
 
 如果你想调整登录表单的相关内容,你可以在应用下的 `src/views/_core/authentication/login.vue` 内,配置`AuthenticationLogin` 组件参数即可:

+ 3 - 3
docs/src/guide/in-depth/theme.md

@@ -53,7 +53,7 @@ css 变量内的颜色,必须使用 `hsl` 格式,如 `0 0% 100%`,不需要
 
   /* 主题颜色 */
 
-  --primary: 231 98% 65%;
+  --primary: 212 100% 45%;
   --primary-foreground: 0 0% 98%;
 
   /* Used for destructive actions such as <Button variant="destructive"> */
@@ -264,7 +264,7 @@ export const overridesPreferences = defineOverridesPreferences({
     // 错误色
     colorDestructive: 'hsl(348 100% 61%)',
     // 主题色
-    colorPrimary: 'hsl(231 98% 65%)',
+    colorPrimary: 'hsl(212 100% 45%)',
     // 成功色
     colorSuccess: 'hsl(144 57% 58%)',
     // 警告色
@@ -351,7 +351,7 @@ type BuiltinThemeType =
 
   /* 主题颜色 */
 
-  --primary: 231 98% 65%;
+  --primary: 212 100% 45%;
   --primary-foreground: 0 0% 98%;
 
   /* Used for destructive actions such as <Button variant="destructive"> */

+ 2 - 1
packages/@core/base/design/src/design-tokens/default/index.css

@@ -28,7 +28,7 @@
 
   /* 主题颜色 */
 
-  --primary: 231 98% 65%;
+  --primary: 212 100% 45%;
   --primary-foreground: 0 0% 98%;
 
   /* Used for destructive actions such as <Button variant="destructive"> */
@@ -78,6 +78,7 @@
 
   /* 遮罩颜色 */
   --overlay: 0 0% 0% / 45%;
+  --overlay-light: 0 0% 95% / 45%;
 
   /* 基本文字大小 */
   --font-size-base: 16px;

+ 1 - 1
packages/@core/preferences/src/config.ts

@@ -87,7 +87,7 @@ const defaultPreferences: Preferences = {
   theme: {
     builtinType: 'default',
     colorDestructive: 'hsl(348 100% 61%)',
-    colorPrimary: 'hsl(231 98% 65%)',
+    colorPrimary: 'hsl(212 100% 45%)',
     colorSuccess: 'hsl(144 57% 58%)',
     colorWarning: 'hsl(42 84% 61%)',
     mode: 'dark',

+ 2 - 2
packages/@core/preferences/src/constants.ts

@@ -9,7 +9,7 @@ interface BuiltinThemePreset {
 
 const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
   {
-    color: 'hsl(231 98% 65%)',
+    color: 'hsl(212 100% 45%)',
     type: 'default',
   },
   {
@@ -25,7 +25,7 @@ const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
     type: 'yellow',
   },
   {
-    color: 'hsl(212 100% 45%)',
+    color: 'hsl(231 98% 65%)',
     type: 'sky-blue',
   },
   {

+ 2 - 2
packages/@core/ui-kit/popup-ui/src/modal/modal.vue

@@ -159,7 +159,7 @@ function pointerDownOutside(e: Event) {
         )
       "
       :show-close="closable"
-      close-class="top-4"
+      close-class="top-3"
       @escape-key-down="escapeKeyDown"
       @interact-outside="interactOutside"
       @pointer-down-outside="pointerDownOutside"
@@ -219,7 +219,7 @@ function pointerDownOutside(e: Event) {
 
       <VbenIconButton
         v-if="fullscreenButton"
-        class="hover:bg-accent hover:text-accent-foreground text-foreground/80 flex-center absolute right-10 top-4 hidden size-6 rounded-full px-1 text-lg opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none sm:block"
+        class="hover:bg-accent hover:text-accent-foreground text-foreground/80 flex-center absolute right-10 top-3 hidden size-6 rounded-full px-1 text-lg opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none sm:block"
         @click="handleFullscreen"
       >
         <Shrink v-if="fullscreen" class="size-3.5" />

+ 2 - 2
packages/@core/ui-kit/shadcn-ui/src/components/input-password/input-password.vue

@@ -25,7 +25,7 @@ const show = ref(false);
 </script>
 
 <template>
-  <form class="relative">
+  <div class="relative">
     <VbenInput
       v-model="modelValue"
       v-bind="{ ...forward, ...$attrs }"
@@ -48,5 +48,5 @@ const show = ref(false);
       <Eye v-if="show" class="size-4" />
       <EyeOff v-else class="size-4" />
     </div>
-  </form>
+  </div>
 </template>

+ 1 - 1
packages/@core/ui-kit/shadcn-ui/src/components/spinner/loading.vue

@@ -69,7 +69,7 @@ function onTransitionEnd() {
   <div
     :class="
       cn(
-        'bg-overlay z-100 pointer-events-none absolute left-0 top-0 flex size-full flex-col items-center justify-center transition-all duration-500',
+        'z-100 pointer-events-none absolute left-0 top-0 flex size-full flex-col items-center justify-center bg-[hsl(var(--overlay-light))] transition-all duration-500',
         {
           'invisible opacity-0': !showSpinner,
         },

+ 1 - 1
packages/@core/ui-kit/shadcn-ui/src/components/spinner/spinner.vue

@@ -63,7 +63,7 @@ function onTransitionEnd() {
   <div
     :class="
       cn(
-        'flex-center bg-overlay z-100 absolute left-0 top-0 size-full backdrop-blur-sm transition-all duration-500',
+        'flex-center z-100 absolute left-0 top-0 size-full bg-[hsl(var(--overlay-light))] backdrop-blur-sm transition-all duration-500',
         {
           'invisible opacity-0': !showSpinner,
         },

+ 33 - 21
packages/effects/common-ui/src/ui/authentication/login.vue

@@ -92,14 +92,16 @@ function handleGo(path: string) {
 
 <template>
   <div @keydown.enter.prevent="handleSubmit">
-    <Title>
-      {{ title || `${$t('authentication.welcomeBack')} 👋🏻` }}
-      <template #desc>
-        <span class="text-muted-foreground">
-          {{ subTitle || $t('authentication.loginSubtitle') }}
-        </span>
-      </template>
-    </Title>
+    <slot name="title">
+      <Title>
+        {{ title || `${$t('authentication.welcomeBack')} 👋🏻` }}
+        <template #desc>
+          <span class="text-muted-foreground">
+            {{ subTitle || $t('authentication.loginSubtitle') }}
+          </span>
+        </template>
+      </Title>
+    </slot>
 
     <VbenInput
       v-model="formState.username"
@@ -123,7 +125,10 @@ function handleGo(path: string) {
       type="password"
     />
 
-    <div class="mb-6 mt-4 flex justify-between">
+    <div
+      v-if="showRememberMe || showForgetPassword"
+      class="mb-6 mt-4 flex justify-between"
+    >
       <div v-if="showRememberMe" class="flex-center">
         <VbenCheckbox v-model:checked="formState.rememberMe" name="rememberMe">
           {{ $t('authentication.rememberMe') }}
@@ -142,7 +147,10 @@ function handleGo(path: string) {
       {{ $t('common.login') }}
     </VbenButton>
 
-    <div class="mb-2 mt-4 flex items-center justify-between">
+    <div
+      v-if="showCodeLogin || showQrcodeLogin"
+      class="mb-2 mt-4 flex items-center justify-between"
+    >
       <VbenButton
         v-if="showCodeLogin"
         class="w-1/2"
@@ -162,16 +170,20 @@ function handleGo(path: string) {
     </div>
 
     <!-- 第三方登录 -->
-    <ThirdPartyLogin v-if="showThirdPartyLogin" />
-
-    <div v-if="showRegister" class="text-center text-sm">
-      {{ $t('authentication.accountTip') }}
-      <span
-        class="text-primary hover:text-primary-hover active:text-primary-active cursor-pointer text-sm font-normal"
-        @click="handleGo(registerPath)"
-      >
-        {{ $t('authentication.createAccount') }}
-      </span>
-    </div>
+    <slot name="third-party-login">
+      <ThirdPartyLogin v-if="showThirdPartyLogin" />
+    </slot>
+
+    <slot name="to-register">
+      <div v-if="showRegister" class="mt-3 text-center text-sm">
+        {{ $t('authentication.accountTip') }}
+        <span
+          class="text-primary hover:text-primary-hover active:text-primary-active cursor-pointer text-sm font-normal"
+          @click="handleGo(registerPath)"
+        >
+          {{ $t('authentication.createAccount') }}
+        </span>
+      </div>
+    </slot>
   </div>
 </template>

+ 65 - 26
packages/effects/layouts/src/authentication/authentication.vue

@@ -1,24 +1,28 @@
 <script setup lang="ts">
-import { computed } from 'vue';
+import type { ToolbarType } from './types';
 
-import { $t } from '@vben/locales';
 import { preferences, usePreferences } from '@vben/preferences';
 
+import { Copyright } from '../basic/copyright';
 import AuthenticationFormView from './form.vue';
 import SloganIcon from './icons/slogan.vue';
 import Toolbar from './toolbar.vue';
 
 interface Props {
+  appName?: string;
+  logo?: string;
   pageTitle?: string;
   pageDescription?: string;
   sloganImage?: string;
   toolbar?: boolean;
-  toolbarList?: ('color' | 'language' | 'layout' | 'theme')[];
+  copyright?: boolean;
+  toolbarList?: ToolbarType[];
 }
 
-defineOptions({ name: 'Authentication' });
-
 withDefaults(defineProps<Props>(), {
+  appName: '',
+  copyright: true,
+  logo: '',
   pageDescription: '',
   pageTitle: '',
   sloganImage: '',
@@ -26,31 +30,42 @@ withDefaults(defineProps<Props>(), {
   toolbarList: () => ['color', 'language', 'layout', 'theme'],
 });
 
-const { authPanelCenter, authPanelLeft, authPanelRight } = usePreferences();
-const appName = computed(() => preferences.app.name);
-const logoSource = computed(() => preferences.logo.source);
+const { authPanelCenter, authPanelLeft, authPanelRight, isDark } =
+  usePreferences();
 </script>
 
 <template>
-  <div class="flex min-h-full flex-1 select-none overflow-x-hidden">
+  <div
+    :class="[isDark]"
+    class="flex min-h-full flex-1 select-none overflow-x-hidden"
+  >
+    <template v-if="toolbar">
+      <slot name="toolbar">
+        <Toolbar :toolbar-list="toolbarList" />
+      </slot>
+    </template>
     <!-- 左侧认证面板 -->
     <AuthenticationFormView
       v-if="authPanelLeft"
       class="min-h-full w-2/5 flex-1"
       transition-name="slide-left"
     >
-      <template v-if="toolbar" #toolbar>
-        <Toolbar :toolbar-list="toolbarList" />
+      <template v-if="copyright" #copyright>
+        <slot name="copyright">
+          <Copyright
+            v-if="preferences.copyright.enable"
+            v-bind="preferences.copyright"
+          />
+        </slot>
       </template>
     </AuthenticationFormView>
 
     <!-- 头部 Logo 和应用名称 -->
     <div class="absolute left-0 top-0 z-10 flex flex-1">
       <div
-        :class="authPanelRight ? 'lg:text-white' : 'lg:text-foreground'"
-        class="text-foreground ml-4 mt-4 flex flex-1 items-center sm:left-6 sm:top-6"
+        class="text-foreground lg:text-foreground ml-4 mt-4 flex flex-1 items-center sm:left-6 sm:top-6"
       >
-        <img :alt="appName" :src="logoSource" class="mr-2" width="42" />
+        <img :alt="appName" :src="logo" class="mr-2" width="42" />
         <p class="text-xl font-medium">
           {{ appName }}
         </p>
@@ -59,7 +74,9 @@ const logoSource = computed(() => preferences.logo.source);
 
     <!-- 系统介绍 -->
     <div v-if="!authPanelCenter" class="relative hidden w-0 flex-1 lg:block">
-      <div class="absolute inset-0 h-full w-full bg-[#070709]">
+      <div
+        class="bg-background-deep absolute inset-0 h-full w-full dark:bg-[#070709]"
+      >
         <div class="login-background absolute left-0 top-0 size-full"></div>
         <div class="flex-col-center -enter-x mr-20 h-full">
           <template v-if="sloganImage">
@@ -70,11 +87,11 @@ const logoSource = computed(() => preferences.logo.source);
             />
           </template>
           <SloganIcon v-else :alt="appName" class="animate-float h-64 w-2/5" />
-          <div class="text-1xl mt-6 font-sans text-white lg:text-2xl">
-            {{ pageTitle || $t('authentication.pageTitle') }}
+          <div class="text-1xl text-foreground mt-6 font-sans lg:text-2xl">
+            {{ pageTitle }}
           </div>
-          <div class="dark:text-muted-foreground mt-2 text-white/60">
-            {{ pageDescription || $t('authentication.pageDesc') }}
+          <div class="dark:text-muted-foreground mt-2">
+            {{ pageDescription }}
           </div>
         </div>
       </div>
@@ -84,10 +101,15 @@ const logoSource = computed(() => preferences.logo.source);
     <div v-if="authPanelCenter" class="flex-center relative w-full">
       <div class="login-background absolute left-0 top-0 size-full"></div>
       <AuthenticationFormView
-        class="md:bg-background shadow-primary/10 w-full rounded-3xl pb-20 shadow-2xl md:w-2/3 lg:w-1/2 xl:w-[36%]"
+        class="md:bg-background shadow-primary/5 shadow-float w-full rounded-3xl pb-20 md:w-2/3 lg:w-1/2 xl:w-[36%]"
       >
-        <template v-if="toolbar" #toolbar>
-          <Toolbar :toolbar-list="toolbarList" />
+        <template v-if="copyright" #copyright>
+          <slot name="copyright">
+            <Copyright
+              v-if="preferences.copyright.enable"
+              v-bind="preferences.copyright"
+            />
+          </slot>
         </template>
       </AuthenticationFormView>
     </div>
@@ -95,10 +117,15 @@ const logoSource = computed(() => preferences.logo.source);
     <!-- 右侧认证面板 -->
     <AuthenticationFormView
       v-if="authPanelRight"
-      class="min-h-full w-2/5 flex-1"
+      class="min-h-full w-[34%] flex-1"
     >
-      <template v-if="toolbar" #toolbar>
-        <Toolbar :toolbar-list="toolbarList" />
+      <template v-if="copyright" #copyright>
+        <slot name="copyright">
+          <Copyright
+            v-if="preferences.copyright.enable"
+            v-bind="preferences.copyright"
+          />
+        </slot>
       </template>
     </AuthenticationFormView>
   </div>
@@ -109,9 +136,21 @@ const logoSource = computed(() => preferences.logo.source);
   background: linear-gradient(
     154deg,
     #07070915 30%,
-    hsl(var(--primary) / 15%) 48%,
+    hsl(var(--primary) / 30%) 48%,
     #07070915 64%
   );
   filter: blur(100px);
 }
+
+.dark {
+  .login-background {
+    background: linear-gradient(
+      154deg,
+      #07070915 30%,
+      hsl(var(--primary) / 20%) 48%,
+      #07070915 64%
+    );
+    filter: blur(100px);
+  }
+}
 </style>

+ 4 - 12
packages/effects/layouts/src/authentication/form.vue

@@ -1,8 +1,4 @@
 <script setup lang="ts">
-import { preferences } from '@vben/preferences';
-
-import { Copyright } from '../basic/copyright';
-
 defineOptions({
   name: 'AuthenticationFormView',
 });
@@ -10,11 +6,9 @@ defineOptions({
 
 <template>
   <div
-    class="flex-col-center bg-background-deep relative px-6 py-10 lg:flex-initial lg:px-8"
+    class="flex-col-center dark:bg-background-deep bg-background relative px-6 py-10 lg:flex-initial lg:px-8"
   >
-    <!-- Toolbar Slot -->
-    <slot name="toolbar"> </slot>
-
+    <slot></slot>
     <!-- Router View with Transition and KeepAlive -->
     <RouterView v-slot="{ Component, route }">
       <Transition appear mode="out-in" name="slide-right">
@@ -29,13 +23,11 @@ defineOptions({
     </RouterView>
 
     <!-- Footer Copyright -->
+
     <div
       class="text-muted-foreground absolute bottom-3 flex text-center text-xs"
     >
-      <Copyright
-        v-if="preferences.copyright.enable"
-        v-bind="preferences.copyright"
-      />
+      <slot name="copyright"> </slot>
     </div>
   </div>
 </template>

+ 4 - 3
packages/effects/layouts/src/authentication/toolbar.vue

@@ -1,4 +1,6 @@
 <script setup lang="ts">
+import type { ToolbarType } from './types';
+
 import { computed } from 'vue';
 
 import {
@@ -9,7 +11,7 @@ import {
 } from '../widgets';
 
 interface Props {
-  toolbarList?: ('color' | 'language' | 'layout' | 'theme')[];
+  toolbarList?: ToolbarType[];
 }
 
 defineOptions({
@@ -29,8 +31,7 @@ const showTheme = computed(() => props.toolbarList.includes('theme'));
 <template>
   <div
     :class="{
-      'bg-background dark:bg-accent rounded-3xl px-3 py-1':
-        toolbarList.length > 1,
+      'bg-accent z-10 rounded-3xl px-3 py-1': toolbarList.length > 1,
     }"
     class="flex-center absolute right-2 top-4"
   >

+ 1 - 0
packages/effects/layouts/src/authentication/types.ts

@@ -0,0 +1 @@
+export type ToolbarType = 'color' | 'language' | 'layout' | 'theme';

+ 4 - 0
packages/effects/layouts/src/widgets/check-updates/check-updates.vue

@@ -95,6 +95,10 @@ function handleNotice(versionTag: string) {
 }
 
 function start() {
+  if (props.checkUpdatesInterval <= 0) {
+    return;
+  }
+
   // 每 checkUpdatesInterval(默认值为1) 分钟检查一次
   timer.value = setInterval(
     checkForUpdates,

+ 23 - 0
playground/src/layouts/auth.vue

@@ -0,0 +1,23 @@
+<script lang="ts" setup>
+import { computed } from 'vue';
+
+import { AuthPageLayout } from '@vben/layouts';
+import { preferences } from '@vben/preferences';
+
+import { $t } from '#/locales';
+
+const appName = computed(() => preferences.app.name);
+const logo = computed(() => preferences.logo.source);
+</script>
+
+<template>
+  <AuthPageLayout
+    :app-name="appName"
+    :logo="logo"
+    :page-description="$t('authentication.pageDesc')"
+    :page-title="$t('authentication.pageTitle')"
+  >
+    <!-- 自定义工具栏 -->
+    <!-- <template #toolbar></template> -->
+  </AuthPageLayout>
+</template>

+ 1 - 3
playground/src/layouts/index.ts

@@ -1,8 +1,6 @@
 const BasicLayout = () => import('./basic.vue');
+const AuthPageLayout = () => import('./auth.vue');
 
 const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
 
-const AuthPageLayout = () =>
-  import('@vben/layouts').then((m) => m.AuthPageLayout);
-
 export { AuthPageLayout, BasicLayout, IFrameView };

+ 19 - 1
playground/src/views/dashboard/analytics/analytics-trends.vue

@@ -55,12 +55,27 @@ onMounted(() => {
       },
       trigger: 'axis',
     },
+    // xAxis: {
+    //   axisTick: {
+    //     show: false,
+    //   },
+    //   boundaryGap: false,
+    //   data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
+    //   type: 'category',
+    // },
     xAxis: {
       axisTick: {
         show: false,
       },
       boundaryGap: false,
       data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
+      splitLine: {
+        lineStyle: {
+          type: 'solid',
+          width: 1,
+        },
+        show: true,
+      },
       type: 'category',
     },
     yAxis: [
@@ -69,7 +84,10 @@ onMounted(() => {
           show: false,
         },
         max: 80_000,
-
+        splitArea: {
+          show: true,
+        },
+        splitNumber: 4,
         type: 'value',
       },
     ],