diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index 5bd96e7c..44bb0016 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -1,6 +1,6 @@ { "name": "@vben/web-antd", - "version": "1.3.0", + "version": "1.3.1", "homepage": "https://vben.pro", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/apps/web-antd/src/router/routes/local.ts b/apps/web-antd/src/router/routes/local.ts index 6a0f4c29..fcde9398 100644 --- a/apps/web-antd/src/router/routes/local.ts +++ b/apps/web-antd/src/router/routes/local.ts @@ -2,6 +2,11 @@ import type { RouteRecordStringComponent } from '@vben/types'; import { $t } from '@vben/locales'; +const { + version, + // vite inject-metadata 插件注入的全局变量 +} = __VBEN_ADMIN_METADATA__ || {}; + /** * 该文件放非后台返回的路由 比如个人中心 等需要跳转显示的页面 */ @@ -134,8 +139,8 @@ export const localMenuList: RouteRecordStringComponent[] = [ icon: 'lucide:book-open-text', keepAlive: true, title: '更新记录', - badge: '1.3.0', - badgeVariants: '#CC0033', + badge: `当前: ${version}`, + badgeVariants: 'bg-primary', }, }, ], diff --git a/apps/web-antd/src/views/system/dict/data/dict-data-drawer.vue b/apps/web-antd/src/views/system/dict/data/dict-data-drawer.vue index 2a5092a3..8556caf6 100644 --- a/apps/web-antd/src/views/system/dict/data/dict-data-drawer.vue +++ b/apps/web-antd/src/views/system/dict/data/dict-data-drawer.vue @@ -77,7 +77,7 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({ const { dictCode, dictType } = drawerApi.getData() as DrawerProps; isUpdate.value = !!dictCode; - formApi.setFieldValue('dictType', dictType); + await formApi.setFieldValue('dictType', dictType); if (dictCode && isUpdate.value) { const record = await dictDetailInfo(dictCode); diff --git a/apps/web-antd/src/views/system/post/post-drawer.vue b/apps/web-antd/src/views/system/post/post-drawer.vue index 1b411838..ed4b963a 100644 --- a/apps/web-antd/src/views/system/post/post-drawer.vue +++ b/apps/web-antd/src/views/system/post/post-drawer.vue @@ -8,6 +8,7 @@ import { addFullName, cloneDeep } from '@vben/utils'; import { useVbenForm } from '#/adapter/form'; import { postAdd, postInfo, postUpdate } from '#/api/system/post'; import { getDeptTree } from '#/api/system/user'; +import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup'; import { drawerSchema } from './data'; @@ -50,8 +51,16 @@ async function setupDeptSelect() { ]); } +const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff( + { + initializedGetter: defaultFormValueGetter(formApi), + currentGetter: defaultFormValueGetter(formApi), + }, +); + const [BasicDrawer, drawerApi] = useVbenDrawer({ - onCancel: handleCancel, + onBeforeClose, + onClosed: handleClosed, onConfirm: handleConfirm, async onOpenChange(isOpen) { if (!isOpen) { @@ -67,31 +76,33 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({ const record = await postInfo(id); await formApi.setValues(record); } + await markInitialized(); drawerApi.drawerLoading(false); }, }); async function handleConfirm() { try { - drawerApi.drawerLoading(true); + drawerApi.lock(true); const { valid } = await formApi.validate(); if (!valid) { return; } const data = cloneDeep(await formApi.getValues()); await (isUpdate.value ? postUpdate(data) : postAdd(data)); + resetInitialized(); emit('reload'); - await handleCancel(); + drawerApi.close(); } catch (error) { console.error(error); } finally { - drawerApi.drawerLoading(false); + drawerApi.lock(false); } } -async function handleCancel() { - drawerApi.close(); +async function handleClosed() { await formApi.resetForm(); + resetInitialized(); } diff --git a/apps/web-antd/src/views/system/role/data.tsx b/apps/web-antd/src/views/system/role/data.tsx index 722e7179..4fe79453 100644 --- a/apps/web-antd/src/views/system/role/data.tsx +++ b/apps/web-antd/src/views/system/role/data.tsx @@ -94,7 +94,8 @@ export const columns: VxeGridProps['columns'] = [ fixed: 'right', slots: { default: 'action' }, title: '操作', - width: 180, + resizable: false, + width: 'auto', }, ]; diff --git a/apps/web-antd/src/views/system/role/index.vue b/apps/web-antd/src/views/system/role/index.vue index 32c199d6..e3a9198c 100644 --- a/apps/web-antd/src/views/system/role/index.vue +++ b/apps/web-antd/src/views/system/role/index.vue @@ -226,7 +226,11 @@ function handleAssignRole(record: Role) { - + {{ $t('pages.common.more') }} diff --git a/apps/web-antd/src/views/system/role/role-auth-modal.vue b/apps/web-antd/src/views/system/role/role-auth-modal.vue index 8ac16d73..63efb57b 100644 --- a/apps/web-antd/src/views/system/role/role-auth-modal.vue +++ b/apps/web-antd/src/views/system/role/role-auth-modal.vue @@ -52,7 +52,7 @@ const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff( const [BasicModal, modalApi] = useVbenModal({ fullscreenButton: false, onBeforeClose, - onCancel: handleClosed, + onClosed: handleClosed, onConfirm: handleConfirm, onOpenChange: async (isOpen) => { if (!isOpen) { diff --git a/apps/web-antd/src/views/system/user/data.tsx b/apps/web-antd/src/views/system/user/data.tsx index af1ca307..a84e0792 100644 --- a/apps/web-antd/src/views/system/user/data.tsx +++ b/apps/web-antd/src/views/system/user/data.tsx @@ -87,7 +87,7 @@ export const columns: VxeGridProps['columns'] = [ slots: { default: 'action' }, title: '操作', resizable: false, - width: 180, + width: 'auto', }, ]; diff --git a/apps/web-antd/src/views/workflow/processDefinition/process-definition-modal.vue b/apps/web-antd/src/views/workflow/processDefinition/process-definition-modal.vue index d4b024ea..c73efbd8 100644 --- a/apps/web-antd/src/views/workflow/processDefinition/process-definition-modal.vue +++ b/apps/web-antd/src/views/workflow/processDefinition/process-definition-modal.vue @@ -75,7 +75,7 @@ const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff( const [BasicDrawer, modalApi] = useVbenModal({ onBeforeClose, - onCancel: handleClosed, + onClosed: handleClosed, onConfirm: handleConfirm, async onOpenChange(isOpen) { if (!isOpen) { diff --git a/packages/@core/preferences/src/preferences.ts b/packages/@core/preferences/src/preferences.ts index 26f1d711..23c5f8be 100644 --- a/packages/@core/preferences/src/preferences.ts +++ b/packages/@core/preferences/src/preferences.ts @@ -2,14 +2,16 @@ import type { DeepPartial } from '@vben-core/typings'; import type { InitialOptions, Preferences } from './types'; +import { markRaw, reactive, readonly, watch } from 'vue'; + import { StorageManager } from '@vben-core/shared/cache'; import { isMacOs, merge } from '@vben-core/shared/utils'; + import { breakpointsTailwind, useBreakpoints, useDebounceFn, } from '@vueuse/core'; -import { markRaw, reactive, readonly, watch } from 'vue'; import { defaultPreferences } from './config'; import { updateCSSVariables } from './update-css-variables'; @@ -37,106 +39,6 @@ class PreferenceManager { ); } - /** - * 保存偏好设置 - * @param {Preferences} preference - 需要保存的偏好设置 - */ - private _savePreferences(preference: Preferences) { - this.cache?.setItem(STORAGE_KEY, preference); - this.cache?.setItem(STORAGE_KEY_LOCALE, preference.app.locale); - this.cache?.setItem(STORAGE_KEY_THEME, preference.theme.mode); - } - - /** - * 处理更新的键值 - * 根据更新的键值执行相应的操作。 - * @param {DeepPartial} updates - 部分更新的偏好设置 - */ - private handleUpdates(updates: DeepPartial) { - const themeUpdates = updates.theme || {}; - const appUpdates = updates.app || {}; - if (themeUpdates && Object.keys(themeUpdates).length > 0) { - updateCSSVariables(this.state); - } - - if ( - Reflect.has(appUpdates, 'colorGrayMode') || - Reflect.has(appUpdates, 'colorWeakMode') - ) { - this.updateColorMode(this.state); - } - } - - private initPlatform() { - const dom = document.documentElement; - dom.dataset.platform = isMacOs() ? 'macOs' : 'window'; - } - - /** - * 从缓存中加载偏好设置。如果缓存中没有找到对应的偏好设置,则返回默认偏好设置。 - */ - private loadCachedPreferences() { - return this.cache?.getItem(STORAGE_KEY); - } - - /** - * 加载偏好设置 - * @returns {Preferences} 加载的偏好设置 - */ - private loadPreferences(): Preferences { - return this.loadCachedPreferences() || { ...defaultPreferences }; - } - - /** - * 监听状态和系统偏好设置的变化。 - */ - private setupWatcher() { - if (this.isInitialized) { - return; - } - - // 监听断点,判断是否移动端 - const breakpoints = useBreakpoints(breakpointsTailwind); - const isMobile = breakpoints.smaller('md'); - watch( - () => isMobile.value, - (val) => { - this.updatePreferences({ - app: { isMobile: val }, - }); - }, - { immediate: true }, - ); - - // 监听系统主题偏好设置变化 - window - .matchMedia('(prefers-color-scheme: dark)') - .addEventListener('change', ({ matches: isDark }) => { - this.updatePreferences({ - theme: { mode: isDark ? 'dark' : 'light' }, - }); - }); - } - - /** - * 更新页面颜色模式(灰色、色弱) - * @param preference - */ - private updateColorMode(preference: Preferences) { - if (preference.app) { - const { colorGrayMode, colorWeakMode } = preference.app; - const dom = document.documentElement; - const COLOR_WEAK = 'invert-mode'; - const COLOR_GRAY = 'grayscale-mode'; - colorWeakMode - ? dom.classList.add(COLOR_WEAK) - : dom.classList.remove(COLOR_WEAK); - colorGrayMode - ? dom.classList.add(COLOR_GRAY) - : dom.classList.remove(COLOR_GRAY); - } - } - clearCache() { [STORAGE_KEY, STORAGE_KEY_LOCALE, STORAGE_KEY_THEME].forEach((key) => { this.cache?.removeItem(key); @@ -220,6 +122,113 @@ class PreferenceManager { this.handleUpdates(updates); this.savePreferences(this.state); } + + /** + * 保存偏好设置 + * @param {Preferences} preference - 需要保存的偏好设置 + */ + private _savePreferences(preference: Preferences) { + this.cache?.setItem(STORAGE_KEY, preference); + this.cache?.setItem(STORAGE_KEY_LOCALE, preference.app.locale); + this.cache?.setItem(STORAGE_KEY_THEME, preference.theme.mode); + } + + /** + * 处理更新的键值 + * 根据更新的键值执行相应的操作。 + * @param {DeepPartial} updates - 部分更新的偏好设置 + */ + private handleUpdates(updates: DeepPartial) { + const themeUpdates = updates.theme || {}; + const appUpdates = updates.app || {}; + if (themeUpdates && Object.keys(themeUpdates).length > 0) { + updateCSSVariables(this.state); + } + + if ( + Reflect.has(appUpdates, 'colorGrayMode') || + Reflect.has(appUpdates, 'colorWeakMode') + ) { + this.updateColorMode(this.state); + } + } + + private initPlatform() { + const dom = document.documentElement; + dom.dataset.platform = isMacOs() ? 'macOs' : 'window'; + } + + /** + * 从缓存中加载偏好设置。如果缓存中没有找到对应的偏好设置,则返回默认偏好设置。 + */ + private loadCachedPreferences() { + return this.cache?.getItem(STORAGE_KEY); + } + + /** + * 加载偏好设置 + * @returns {Preferences} 加载的偏好设置 + */ + private loadPreferences(): Preferences { + return this.loadCachedPreferences() || { ...defaultPreferences }; + } + + /** + * 监听状态和系统偏好设置的变化。 + */ + private setupWatcher() { + if (this.isInitialized) { + return; + } + + // 监听断点,判断是否移动端 + const breakpoints = useBreakpoints(breakpointsTailwind); + const isMobile = breakpoints.smaller('md'); + watch( + () => isMobile.value, + (val) => { + this.updatePreferences({ + app: { isMobile: val }, + }); + }, + { immediate: true }, + ); + + // 监听系统主题偏好设置变化 + window + .matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', ({ matches: isDark }) => { + // 如果偏好设置中主题模式为auto,则跟随系统更新 + if (this.state.theme.mode === 'auto') { + this.updatePreferences({ + theme: { mode: isDark ? 'dark' : 'light' }, + }); + // 恢复为auto模式 + this.updatePreferences({ + theme: { mode: 'auto' }, + }); + } + }); + } + + /** + * 更新页面颜色模式(灰色、色弱) + * @param preference + */ + private updateColorMode(preference: Preferences) { + if (preference.app) { + const { colorGrayMode, colorWeakMode } = preference.app; + const dom = document.documentElement; + const COLOR_WEAK = 'invert-mode'; + const COLOR_GRAY = 'grayscale-mode'; + colorWeakMode + ? dom.classList.add(COLOR_WEAK) + : dom.classList.remove(COLOR_WEAK); + colorGrayMode + ? dom.classList.add(COLOR_GRAY) + : dom.classList.remove(COLOR_GRAY); + } + } } const preferencesManager = new PreferenceManager(); diff --git a/packages/@core/ui-kit/popup-ui/src/alert/alert.vue b/packages/@core/ui-kit/popup-ui/src/alert/alert.vue index 8c7c4541..5c02ed2a 100644 --- a/packages/@core/ui-kit/popup-ui/src/alert/alert.vue +++ b/packages/@core/ui-kit/popup-ui/src/alert/alert.vue @@ -138,7 +138,7 @@ async function handleOpenChange(val: boolean) {
{{ $t(title) }} - +
- + {{ cancelText || $t('cancel') }} - + ({ default: 'default' }); const themeColorPrimary = defineModel('themeColorPrimary'); +const updateThemeColorPrimary = useThrottleFn( + (value: string) => { + themeColorPrimary.value = value; + }, + 300, + true, + true, +); + const inputValue = computed(() => { return new TinyColor(themeColorPrimary.value || '').toHexString(); }); @@ -84,7 +95,7 @@ function handleSelect(theme: BuiltinThemePreset) { function handleInputChange(e: Event) { const target = e.target as HTMLInputElement; - themeColorPrimary.value = convertToHsl(target.value); + updateThemeColorPrimary(convertToHsl(target.value)); } function selectColor() { diff --git a/packages/effects/plugins/src/vxe-table/init.ts b/packages/effects/plugins/src/vxe-table/init.ts index 11488f46..00e9d8ab 100644 --- a/packages/effects/plugins/src/vxe-table/init.ts +++ b/packages/effects/plugins/src/vxe-table/init.ts @@ -1,8 +1,11 @@ import type { SetupVxeTable } from './types'; -import { usePreferences } from '@vben/preferences'; -import { useVbenForm } from '@vben-core/form-ui'; import { defineComponent, watch } from 'vue'; + +import { usePreferences } from '@vben/preferences'; + +import { useVbenForm } from '@vben-core/form-ui'; + import { VxeButton, VxeCheckbox, @@ -103,7 +106,7 @@ export function setupVbenVxeTable(setupOptions: SetupVxeTable) { initVxeTable(); useTableForm = useVbenForm; - const preference = usePreferences(); + const { isDark, locale } = usePreferences(); const localMap = { 'zh-CN': zhCN, @@ -111,11 +114,11 @@ export function setupVbenVxeTable(setupOptions: SetupVxeTable) { }; watch( - [() => preference.theme.value, () => preference.locale.value], - ([theme, locale]) => { - VxeUI.setTheme(theme === 'dark' ? 'dark' : 'light'); - VxeUI.setI18n(locale, localMap[locale]); - VxeUI.setLanguage(locale); + [() => isDark.value, () => locale.value], + ([isDarkValue, localeValue]) => { + VxeUI.setTheme(isDarkValue ? 'dark' : 'light'); + VxeUI.setI18n(localeValue, localMap[localeValue]); + VxeUI.setLanguage(localeValue); }, { immediate: true, diff --git a/playground/src/views/examples/modal/index.vue b/playground/src/views/examples/modal/index.vue index 5674843e..2137227b 100644 --- a/playground/src/views/examples/modal/index.vue +++ b/playground/src/views/examples/modal/index.vue @@ -261,6 +261,9 @@ async function openPrompt() { +

通过快捷方法创建动态提示弹窗,适合一些轻量的提示和确认、输入等