diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 6dfb2de9..25a49448 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -13,7 +13,7 @@ import { version } from '../../package.json'; export default withPwa( defineConfigWithTheme({ - description: 'Vben Admin& 企业级管理系统框架', + description: 'Vben Admin & 企业级管理系统框架', head: head(), lang: 'zh', pwa: pwa(), @@ -284,6 +284,7 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] { { text: '深入', items: [ + { link: 'in-depth/login', text: '登录' }, // { link: 'in-depth/layout', text: '布局' }, { link: 'in-depth/theme', text: '主题' }, { link: 'in-depth/access', text: '权限' }, diff --git a/docs/src/guide/in-depth/login.md b/docs/src/guide/in-depth/login.md new file mode 100644 index 00000000..8155b9bf --- /dev/null +++ b/docs/src/guide/in-depth/login.md @@ -0,0 +1,131 @@ +# 登录 + +本文介绍如何去改造自己的应用程序登录页。 + +## 登录页面调整 + +如果你想调整登录页面的标题、描述和图标以及工具栏,你可以通过配置 `AuthPageLayout` 组件的 `props` 参数来实现。 + +![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'], + } + // ... + }, +``` + +::: tip + +如果这些配置不能满足你的需求,你可以自行实现登录页面。直接实现自己的 `AuthPageLayout`即可。 + +::: + +## 登录表单调整 + +如果你想调整登录表单的相关内容,你可以在应用下的 `src/views/_core/authentication/login.vue` 内,配置`AuthenticationLogin` 组件参数即可: + +```vue + +``` + +::: details AuthenticationLogin 组件参数 + +```ts +{ + /** + * @zh_CN 验证码登录路径 + */ + codeLoginPath?: string; + /** + * @zh_CN 忘记密码路径 + */ + forgetPasswordPath?: string; + + /** + * @zh_CN 是否处于加载处理状态 + */ + loading?: boolean; + + /** + * @zh_CN 密码占位符 + */ + passwordPlaceholder?: string; + + /** + * @zh_CN 二维码登录路径 + */ + qrCodeLoginPath?: string; + + /** + * @zh_CN 注册路径 + */ + registerPath?: string; + + /** + * @zh_CN 是否显示验证码登录 + */ + showCodeLogin?: boolean; + /** + * @zh_CN 是否显示忘记密码 + */ + showForgetPassword?: boolean; + + /** + * @zh_CN 是否显示二维码登录 + */ + showQrcodeLogin?: boolean; + + /** + * @zh_CN 是否显示注册按钮 + */ + showRegister?: boolean; + + /** + * @zh_CN 是否显示记住账号 + */ + showRememberMe?: boolean; + + /** + * @zh_CN 是否显示第三方登录 + */ + showThirdPartyLogin?: boolean; + + /** + * @zh_CN 登录框子标题 + */ + subTitle?: string; + + /** + * @zh_CN 登录框标题 + */ + title?: string; + + /** + * @zh_CN 用户名占位符 + */ + usernamePlaceholder?: string; +} +``` + +::: + +::: tip + +如果这些配置不能满足你的需求,你可以自行实现登录表单及相关登录逻辑。 + +::: diff --git a/docs/src/public/guide/login.png b/docs/src/public/guide/login.png new file mode 100644 index 00000000..3774b0bd Binary files /dev/null and b/docs/src/public/guide/login.png differ diff --git a/packages/@core/base/shared/build.config.ts b/packages/@core/base/shared/build.config.ts index 4192eb26..cd8b6c5c 100644 --- a/packages/@core/base/shared/build.config.ts +++ b/packages/@core/base/shared/build.config.ts @@ -5,6 +5,7 @@ export default defineBuildConfig({ declaration: true, entries: [ 'src/index', + 'src/store', 'src/constants/index', 'src/utils/index', 'src/color/index', diff --git a/packages/@core/base/shared/package.json b/packages/@core/base/shared/package.json index cc4d490f..4316e7bf 100644 --- a/packages/@core/base/shared/package.json +++ b/packages/@core/base/shared/package.json @@ -44,6 +44,11 @@ "types": "./src/cache/index.ts", "development": "./src/cache/index.ts", "default": "./dist/cache/index.mjs" + }, + "./store": { + "types": "./src/store.ts", + "development": "./src/store.ts", + "default": "./dist/store.mjs" } }, "publishConfig": { @@ -56,6 +61,7 @@ }, "dependencies": { "@ctrl/tinycolor": "^4.1.0", + "@tanstack/vue-store": "^0.5.5", "@vue/shared": "^3.4.37", "clsx": "^2.1.1", "defu": "^6.1.4", diff --git a/packages/@core/base/shared/src/index.ts b/packages/@core/base/shared/src/index.ts index 55bf8502..3ec6612f 100644 --- a/packages/@core/base/shared/src/index.ts +++ b/packages/@core/base/shared/src/index.ts @@ -1,4 +1,5 @@ export * from './cache'; export * from './color'; export * from './constants'; +export * from './store'; export * from './utils'; diff --git a/packages/@core/base/shared/src/store.ts b/packages/@core/base/shared/src/store.ts new file mode 100644 index 00000000..4b03afb0 --- /dev/null +++ b/packages/@core/base/shared/src/store.ts @@ -0,0 +1 @@ +export * from '@tanstack/vue-store'; diff --git a/packages/@core/preferences/src/preferences.ts b/packages/@core/preferences/src/preferences.ts index f164a6b9..4670c07c 100644 --- a/packages/@core/preferences/src/preferences.ts +++ b/packages/@core/preferences/src/preferences.ts @@ -116,7 +116,6 @@ class PreferenceManager { this.updatePreferences({ theme: { mode: isDark ? 'dark' : 'light' }, }); - // updateCSSVariables(this.state); }); } diff --git a/packages/effects/layouts/src/authentication/authentication.vue b/packages/effects/layouts/src/authentication/authentication.vue index be6dfe34..d37bab67 100644 --- a/packages/effects/layouts/src/authentication/authentication.vue +++ b/packages/effects/layouts/src/authentication/authentication.vue @@ -6,9 +6,26 @@ import { preferences, usePreferences } from '@vben/preferences'; import AuthenticationFormView from './form.vue'; import SloganIcon from './icons/slogan.vue'; +import Toolbar from './toolbar.vue'; + +interface Props { + pageTitle?: string; + pageDescription?: string; + sloganImage?: string; + toolbar?: boolean; + toolbarList?: ('color' | 'language' | 'layout' | 'theme')[]; +} defineOptions({ name: 'Authentication' }); +withDefaults(defineProps(), { + pageDescription: '', + pageTitle: '', + sloganImage: '', + toolbar: true, + toolbarList: () => ['color', 'language', 'layout', 'theme'], +}); + const { authPanelCenter, authPanelLeft, authPanelRight } = usePreferences(); const appName = computed(() => preferences.app.name); const logoSource = computed(() => preferences.logo.source); @@ -21,7 +38,11 @@ const logoSource = computed(() => preferences.logo.source); v-if="authPanelLeft" class="min-h-full w-2/5" transition-name="slide-left" - /> + > + +
@@ -41,12 +62,19 @@ const logoSource = computed(() => preferences.logo.source);
- + +
- {{ $t('authentication.pageTitle') }} + {{ pageTitle || $t('authentication.pageTitle') }}
- {{ $t('authentication.pageDesc') }} + {{ pageDescription || $t('authentication.pageDesc') }}
@@ -57,14 +85,22 @@ const logoSource = computed(() => preferences.logo.source); + > + +
+ > + + diff --git a/packages/effects/layouts/src/authentication/form.vue b/packages/effects/layouts/src/authentication/form.vue index 686020e9..6bf46893 100644 --- a/packages/effects/layouts/src/authentication/form.vue +++ b/packages/effects/layouts/src/authentication/form.vue @@ -2,7 +2,6 @@ import { preferences } from '@vben/preferences'; import { Copyright } from '../basic/copyright'; -import Toolbar from './toolbar.vue'; defineOptions({ name: 'AuthenticationFormView', @@ -14,9 +13,7 @@ defineOptions({ class="flex-col-center bg-background-deep relative px-6 py-10 lg:flex-initial lg:px-8" > - - - + diff --git a/packages/effects/layouts/src/authentication/toolbar.vue b/packages/effects/layouts/src/authentication/toolbar.vue index 2d7972d5..cfd9bd7e 100644 --- a/packages/effects/layouts/src/authentication/toolbar.vue +++ b/packages/effects/layouts/src/authentication/toolbar.vue @@ -1,4 +1,6 @@ diff --git a/packages/effects/layouts/src/basic/layout.vue b/packages/effects/layouts/src/basic/layout.vue index 82abc6b5..f575ca5a 100644 --- a/packages/effects/layouts/src/basic/layout.vue +++ b/packages/effects/layouts/src/basic/layout.vue @@ -139,7 +139,7 @@ watch( async (val) => { if (val) { await updateWatermark({ - content: `${preferences.app.name} 用户名: ${userStore.userInfo?.username}`, + content: `${userStore.userInfo?.username}`, }); } }, diff --git a/packages/effects/layouts/src/basic/tabbar/use-tab-view-scroll.ts b/packages/effects/layouts/src/basic/tabbar/use-tab-view-scroll.ts deleted file mode 100644 index 571b1152..00000000 --- a/packages/effects/layouts/src/basic/tabbar/use-tab-view-scroll.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { ref } from 'vue'; - -type El = HTMLElement | null | undefined; - -export function useTabViewScroll(scrollDistance: number = 150) { - const scrollbarEl = ref(null); - const scrollViewportEl = ref(null); - - function setScrollBarEl(el: El) { - scrollbarEl.value = el; - } - - function setScrollViewEl(el: El) { - scrollViewportEl.value = el; - } - - function getScrollClientWidth() { - if (!scrollbarEl.value || !scrollViewportEl.value) return {}; - - const scrollbarWidth = scrollbarEl.value.clientWidth; - const scrollViewWidth = scrollViewportEl.value.clientWidth; - - return { - scrollbarWidth, - scrollViewWidth, - }; - } - - function scrollDirection( - direction: 'left' | 'right', - distance: number = scrollDistance, - ) { - const { scrollbarWidth, scrollViewWidth } = getScrollClientWidth(); - - if (!scrollbarWidth || !scrollViewWidth) return; - - if (scrollbarWidth > scrollViewWidth) return; - - scrollViewportEl.value?.scrollBy({ - behavior: 'smooth', - left: direction === 'left' ? -distance : +distance, - }); - } - - return { - scrollDirection, - setScrollBarEl, - setScrollViewEl, - }; -} diff --git a/packages/effects/layouts/src/iframe/iframe-view.vue b/packages/effects/layouts/src/iframe/iframe-view.vue index c3d43d0b..7b8b46cb 100644 --- a/packages/effects/layouts/src/iframe/iframe-view.vue +++ b/packages/effects/layouts/src/iframe/iframe-view.vue @@ -1,6 +1,3 @@ - diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 832032f9..8ee7770d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -671,6 +671,9 @@ importers: '@ctrl/tinycolor': specifier: 4.1.0 version: 4.1.0 + '@tanstack/vue-store': + specifier: ^0.5.5 + version: 0.5.5(vue@3.4.37(typescript@5.5.4)) '@vue/shared': specifier: ^3.4.37 version: 3.4.38 @@ -3375,6 +3378,7 @@ packages: '@ls-lint/ls-lint@2.2.3': resolution: {integrity: sha512-ekM12jNm/7O2I/hsRv9HvYkRdfrHpiV1epVuI2NP+eTIcEgdIdKkKCs9KgQydu/8R5YXTov9aHdOgplmCHLupw==} + cpu: [x64, arm64, s390x] os: [darwin, linux, win32] hasBin: true @@ -3823,9 +3827,21 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders' + '@tanstack/store@0.5.5': + resolution: {integrity: sha512-EOSrgdDAJExbvRZEQ/Xhh9iZchXpMN+ga1Bnk8Nmygzs8TfiE6hbzThF+Pr2G19uHL6+DTDTHhJ8VQiOd7l4tA==} + '@tanstack/virtual-core@3.9.0': resolution: {integrity: sha512-Saga7/QRGej/IDCVP5BgJ1oDqlDT2d9rQyoflS3fgMS8ntJ8JGw/LBqK2GorHa06+VrNFc0tGz65XQHJQJetFQ==} + '@tanstack/vue-store@0.5.5': + resolution: {integrity: sha512-j+CDrxVhtQQNOjWzLmCqJeDwmmTAQGvEaNbLr1uPJ9rxJITodJtFNdBFj7l+Nd5o34v2ayEv64Ugh6+1BtuGNg==} + peerDependencies: + '@vue/composition-api': ^1.2.1 + vue: 3.4.37 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + '@tanstack/vue-virtual@3.9.0': resolution: {integrity: sha512-MVJhQh57OR3wg2pWL/25IN1/nITFNnpFaz4gOvRCqnxhsH0WRePBBKvixOaFTgiyYfmrjFbb4d0nRMTvsjZZdQ==} peerDependencies: @@ -12638,8 +12654,16 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 3.4.10 + '@tanstack/store@0.5.5': {} + '@tanstack/virtual-core@3.9.0': {} + '@tanstack/vue-store@0.5.5(vue@3.4.37(typescript@5.5.4))': + dependencies: + '@tanstack/store': 0.5.5 + vue: 3.4.37(typescript@5.5.4) + vue-demi: 0.14.10(vue@3.4.37(typescript@5.5.4)) + '@tanstack/vue-virtual@3.9.0(vue@3.4.37(typescript@5.5.4))': dependencies: '@tanstack/virtual-core': 3.9.0