feat(project): support dynamic title
							parent
							
								
									e83adf0697
								
							
						
					
					
						commit
						d1cdea430e
					
				| 
						 | 
					@ -35,6 +35,7 @@
 | 
				
			||||||
    "@vben/styles": "workspace:*",
 | 
					    "@vben/styles": "workspace:*",
 | 
				
			||||||
    "@vben/types": "workspace:*",
 | 
					    "@vben/types": "workspace:*",
 | 
				
			||||||
    "@vben/utils": "workspace:*",
 | 
					    "@vben/utils": "workspace:*",
 | 
				
			||||||
 | 
					    "@vueuse/core": "^10.9.0",
 | 
				
			||||||
    "ant-design-vue": "^4.2.1",
 | 
					    "ant-design-vue": "^4.2.1",
 | 
				
			||||||
    "axios": "^1.7.1",
 | 
					    "axios": "^1.7.1",
 | 
				
			||||||
    "dayjs": "^1.11.11",
 | 
					    "dayjs": "^1.11.11",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,9 @@
 | 
				
			||||||
import type { Router } from 'vue-router';
 | 
					import type { Router } from 'vue-router';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { $t } from '@vben/locales';
 | 
				
			||||||
import { preference } from '@vben/preference';
 | 
					import { preference } from '@vben/preference';
 | 
				
			||||||
import { startProgress, stopProgress } from '@vben/utils';
 | 
					import { startProgress, stopProgress } from '@vben/utils';
 | 
				
			||||||
 | 
					import { useTitle } from '@vueuse/core';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { configAccessGuard } from './access';
 | 
					import { configAccessGuard } from './access';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +28,12 @@ function configCommonGuard(router: Router) {
 | 
				
			||||||
    if (preference.pageProgress) {
 | 
					    if (preference.pageProgress) {
 | 
				
			||||||
      stopProgress();
 | 
					      stopProgress();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 动态修改标题
 | 
				
			||||||
 | 
					    if (preference.dynamicTitle) {
 | 
				
			||||||
 | 
					      const { title } = to.meta;
 | 
				
			||||||
 | 
					      useTitle(`${$t(title)} - ${preference.appName}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ export default defineConfig({
 | 
				
			||||||
        { name: 'vue-demi' },
 | 
					        { name: 'vue-demi' },
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    visualizer: true,
 | 
					    visualizer: false,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  vite: {
 | 
					  vite: {
 | 
				
			||||||
    server: {
 | 
					    server: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
.dark {
 | 
					:root.dark {
 | 
				
			||||||
  /* 基础背景颜色颜色 */
 | 
					  /* 基础背景颜色颜色 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* --color-background: 240 6% 18%; */
 | 
					  /* --color-background: 240 6% 18%; */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,6 +55,8 @@ interface Preference {
 | 
				
			||||||
  copyright: string;
 | 
					  copyright: string;
 | 
				
			||||||
  /** 应用默认头像 */
 | 
					  /** 应用默认头像 */
 | 
				
			||||||
  defaultAvatar: string;
 | 
					  defaultAvatar: string;
 | 
				
			||||||
 | 
					  /** 开启动态标题 */
 | 
				
			||||||
 | 
					  dynamicTitle: boolean;
 | 
				
			||||||
  /** 页脚是否固定 */
 | 
					  /** 页脚是否固定 */
 | 
				
			||||||
  footerFixed: boolean;
 | 
					  footerFixed: boolean;
 | 
				
			||||||
  /** 页脚是否可见 */
 | 
					  /** 页脚是否可见 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import { type ComputedRef, type MaybeRef } from 'vue';
 | 
					import { type ComputedRef, type MaybeRef } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 深度部分类型
 | 
					 * 深层递归所有属性为可选
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
type DeepPartial<T> = T extends object
 | 
					type DeepPartial<T> = T extends object
 | 
				
			||||||
  ? {
 | 
					  ? {
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,13 @@ type DeepPartial<T> = T extends object
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  : T;
 | 
					  : T;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 深层递归所有属性为只读
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					type DeepReadonly<T> = {
 | 
				
			||||||
 | 
					  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 任意类型的异步函数
 | 
					 * 任意类型的异步函数
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -78,6 +85,7 @@ export {
 | 
				
			||||||
  type AnyNormalFunction,
 | 
					  type AnyNormalFunction,
 | 
				
			||||||
  type AnyPromiseFunction,
 | 
					  type AnyPromiseFunction,
 | 
				
			||||||
  type DeepPartial,
 | 
					  type DeepPartial,
 | 
				
			||||||
 | 
					  type DeepReadonly,
 | 
				
			||||||
  type IntervalHandle,
 | 
					  type IntervalHandle,
 | 
				
			||||||
  type MaybeComputedRef,
 | 
					  type MaybeComputedRef,
 | 
				
			||||||
  type MaybeReadonlyRef,
 | 
					  type MaybeReadonlyRef,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,6 +69,7 @@ const logoClass = computed(() => {
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      <span v-if="!collapse" class="truncate text-nowrap">
 | 
					      <span v-if="!collapse" class="truncate text-nowrap">
 | 
				
			||||||
        {{ text }}
 | 
					        {{ text }}
 | 
				
			||||||
 | 
					        <!-- <span class="text-primary ml-1 align-super text-[smaller]">Pro</span> -->
 | 
				
			||||||
      </span>
 | 
					      </span>
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,6 +42,9 @@
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  "peerDependencies": {
 | 
				
			||||||
 | 
					    "@vben-core/design": "workspace:*"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@vben-core/design": "workspace:*",
 | 
					    "@vben-core/design": "workspace:*",
 | 
				
			||||||
    "@vben-core/iconify": "workspace:*",
 | 
					    "@vben-core/iconify": "workspace:*",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,12 +5,14 @@ import { $t } from '@vben/locales';
 | 
				
			||||||
import { staticPreference } from '@vben/preference';
 | 
					import { staticPreference } from '@vben/preference';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import SelectItem from '../select-item.vue';
 | 
					import SelectItem from '../select-item.vue';
 | 
				
			||||||
 | 
					import SwitchItem from '../switch-item.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({
 | 
					defineOptions({
 | 
				
			||||||
  name: 'PreferenceGeneralConfig',
 | 
					  name: 'PreferenceGeneralConfig',
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const locale = defineModel<string>('locale');
 | 
					const locale = defineModel<string>('locale');
 | 
				
			||||||
 | 
					const dynamicTitle = defineModel<boolean>('dynamicTitle');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const localeItems: SelectListItem[] = staticPreference.supportLanguages.map(
 | 
					const localeItems: SelectListItem[] = staticPreference.supportLanguages.map(
 | 
				
			||||||
  (item) => ({
 | 
					  (item) => ({
 | 
				
			||||||
| 
						 | 
					@ -24,4 +26,7 @@ const localeItems: SelectListItem[] = staticPreference.supportLanguages.map(
 | 
				
			||||||
  <SelectItem v-model="locale" :items="localeItems">
 | 
					  <SelectItem v-model="locale" :items="localeItems">
 | 
				
			||||||
    {{ $t('preference.language') }}
 | 
					    {{ $t('preference.language') }}
 | 
				
			||||||
  </SelectItem>
 | 
					  </SelectItem>
 | 
				
			||||||
 | 
					  <SwitchItem v-model="dynamicTitle">
 | 
				
			||||||
 | 
					    {{ $t('preference.dynamic-title') }}
 | 
				
			||||||
 | 
					  </SwitchItem>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,7 @@ function updateLocale(value: string) {
 | 
				
			||||||
    :footer-fixed="preference.footerFixed"
 | 
					    :footer-fixed="preference.footerFixed"
 | 
				
			||||||
    :header-mode="preference.headerMode"
 | 
					    :header-mode="preference.headerMode"
 | 
				
			||||||
    :theme="preference.theme"
 | 
					    :theme="preference.theme"
 | 
				
			||||||
 | 
					    :dynamic-title="preference.dynamicTitle"
 | 
				
			||||||
    :breadcrumb-hide-only-one="preference.breadcrumbHideOnlyOne"
 | 
					    :breadcrumb-hide-only-one="preference.breadcrumbHideOnlyOne"
 | 
				
			||||||
    :page-transition="preference.pageTransition"
 | 
					    :page-transition="preference.pageTransition"
 | 
				
			||||||
    :page-progress="preference.pageProgress"
 | 
					    :page-progress="preference.pageProgress"
 | 
				
			||||||
| 
						 | 
					@ -55,6 +56,7 @@ function updateLocale(value: string) {
 | 
				
			||||||
    :side-collapse-show-title="preference.sideCollapseShowTitle"
 | 
					    :side-collapse-show-title="preference.sideCollapseShowTitle"
 | 
				
			||||||
    :page-transition-enable="preference.pageTransitionEnable"
 | 
					    :page-transition-enable="preference.pageTransitionEnable"
 | 
				
			||||||
    @update:navigation-style="(value) => handleUpdate('navigationStyle', value)"
 | 
					    @update:navigation-style="(value) => handleUpdate('navigationStyle', value)"
 | 
				
			||||||
 | 
					    @update:dynamic-title="(value) => handleUpdate('dynamicTitle', value)"
 | 
				
			||||||
    @update:tabs-icon="(value) => handleUpdate('tabsIcon', value)"
 | 
					    @update:tabs-icon="(value) => handleUpdate('tabsIcon', value)"
 | 
				
			||||||
    @update:side-collapse="(value) => handleUpdate('sideCollapse', value)"
 | 
					    @update:side-collapse="(value) => handleUpdate('sideCollapse', value)"
 | 
				
			||||||
    @update:locale="updateLocale"
 | 
					    @update:locale="updateLocale"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,7 @@ withDefaults(defineProps<{ colorPrimaryPresets: string[] }>(), {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const theme = defineModel<string>('theme');
 | 
					const theme = defineModel<string>('theme');
 | 
				
			||||||
const locale = defineModel<string>('locale');
 | 
					const locale = defineModel<string>('locale');
 | 
				
			||||||
 | 
					const dynamicTitle = defineModel<boolean>('dynamicTitle');
 | 
				
			||||||
const semiDarkMenu = defineModel<boolean>('semiDarkMenu');
 | 
					const semiDarkMenu = defineModel<boolean>('semiDarkMenu');
 | 
				
			||||||
const breadcrumbVisible = defineModel<boolean>('breadcrumbVisible');
 | 
					const breadcrumbVisible = defineModel<boolean>('breadcrumbVisible');
 | 
				
			||||||
const breadcrumbIcon = defineModel<boolean>('breadcrumbIcon');
 | 
					const breadcrumbIcon = defineModel<boolean>('breadcrumbIcon');
 | 
				
			||||||
| 
						 | 
					@ -210,7 +211,10 @@ function handleReset() {
 | 
				
			||||||
          </template>
 | 
					          </template>
 | 
				
			||||||
          <template #general>
 | 
					          <template #general>
 | 
				
			||||||
            <Block :title="$t('preference.general')">
 | 
					            <Block :title="$t('preference.general')">
 | 
				
			||||||
              <General v-model:locale="locale" />
 | 
					              <General
 | 
				
			||||||
 | 
					                v-model:locale="locale"
 | 
				
			||||||
 | 
					                v-model:dynamic-title="dynamicTitle"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
            </Block>
 | 
					            </Block>
 | 
				
			||||||
            <Block :title="$t('preference.navigation-menu')">
 | 
					            <Block :title="$t('preference.navigation-menu')">
 | 
				
			||||||
              <Navigation
 | 
					              <Navigation
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,6 +69,7 @@ preference:
 | 
				
			||||||
  gray-mode: Gray Mode
 | 
					  gray-mode: Gray Mode
 | 
				
			||||||
  animation: Animation
 | 
					  animation: Animation
 | 
				
			||||||
  language: Language
 | 
					  language: Language
 | 
				
			||||||
 | 
					  dynamic-title: Dynamic Title
 | 
				
			||||||
  normal: Normal
 | 
					  normal: Normal
 | 
				
			||||||
  rounded: Rounded
 | 
					  rounded: Rounded
 | 
				
			||||||
  collapse: Collpase Menu
 | 
					  collapse: Collpase Menu
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,6 +51,7 @@ preference:
 | 
				
			||||||
  dark: 深色
 | 
					  dark: 深色
 | 
				
			||||||
  dark-menu: 深色菜单
 | 
					  dark-menu: 深色菜单
 | 
				
			||||||
  language: 语言
 | 
					  language: 语言
 | 
				
			||||||
 | 
					  dynamic-title: 动态标题
 | 
				
			||||||
  collapse: 折叠菜单
 | 
					  collapse: 折叠菜单
 | 
				
			||||||
  collapse-show-title: 显示菜单名
 | 
					  collapse-show-title: 显示菜单名
 | 
				
			||||||
  wide: 流式
 | 
					  wide: 流式
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,7 @@ const defaultPreference: Preference = {
 | 
				
			||||||
  copyright: 'Copyright © 2024 Vben Admin PRO',
 | 
					  copyright: 'Copyright © 2024 Vben Admin PRO',
 | 
				
			||||||
  defaultAvatar:
 | 
					  defaultAvatar:
 | 
				
			||||||
    'https://cdn.jsdelivr.net/gh/vbenjs/vben-cdn-static@0.1.2/vben-admin/pro-avatar.webp',
 | 
					    'https://cdn.jsdelivr.net/gh/vbenjs/vben-cdn-static@0.1.2/vben-admin/pro-avatar.webp',
 | 
				
			||||||
 | 
					  dynamicTitle: true,
 | 
				
			||||||
  footerFixed: true,
 | 
					  footerFixed: true,
 | 
				
			||||||
  footerVisible: true,
 | 
					  footerVisible: true,
 | 
				
			||||||
  headerMode: 'fixed',
 | 
					  headerMode: 'fixed',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,6 +126,9 @@ importers:
 | 
				
			||||||
      '@vben/utils':
 | 
					      '@vben/utils':
 | 
				
			||||||
        specifier: workspace:*
 | 
					        specifier: workspace:*
 | 
				
			||||||
        version: link:../../packages/utils
 | 
					        version: link:../../packages/utils
 | 
				
			||||||
 | 
					      '@vueuse/core':
 | 
				
			||||||
 | 
					        specifier: ^10.9.0
 | 
				
			||||||
 | 
					        version: 10.9.0(vue@3.4.27(typescript@5.4.5))
 | 
				
			||||||
      ant-design-vue:
 | 
					      ant-design-vue:
 | 
				
			||||||
        specifier: ^4.2.1
 | 
					        specifier: ^4.2.1
 | 
				
			||||||
        version: 4.2.1(vue@3.4.27(typescript@5.4.5))
 | 
					        version: 4.2.1(vue@3.4.27(typescript@5.4.5))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue