From b35d2f4f3becd5aa8f684520c7a7234716674ca4 Mon Sep 17 00:00:00 2001 From: Lgf <2246221508@qq.com> Date: Fri, 24 Apr 2026 15:55:59 +0800 Subject: [PATCH] =?UTF-8?q?refactor(watermark):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E4=B8=AD=E7=9A=84=E6=9A=97=E8=89=B2=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E5=A4=84=E7=90=86=E5=B9=B6=E9=9B=86=E4=B8=AD=E5=88=B0?= =?UTF-8?q?=E6=B0=B4=E5=8D=B0=E9=92=A9=E5=AD=90=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将暗色模式相关的颜色处理逻辑从各布局组件中移除,统一在use-watermark钩子中处理。 添加对主题变化的监听,自动更新水印颜色。 --- apps/web-antd/src/layouts/basic.vue | 23 +--- apps/web-antdv-next/src/layouts/basic.vue | 23 +--- apps/web-ele/src/layouts/basic.vue | 23 +--- apps/web-naive/src/layouts/basic.vue | 23 +--- apps/web-tdesign/src/layouts/basic.vue | 23 +--- packages/effects/hooks/src/use-watermark.ts | 137 ++++++++++++++------ playground/src/layouts/basic.vue | 23 +--- 7 files changed, 112 insertions(+), 163 deletions(-) diff --git a/apps/web-antd/src/layouts/basic.vue b/apps/web-antd/src/layouts/basic.vue index 1bb07ec8f..211ee7be3 100644 --- a/apps/web-antd/src/layouts/basic.vue +++ b/apps/web-antd/src/layouts/basic.vue @@ -14,7 +14,7 @@ import { Notification, UserDropdown, } from '@vben/layouts'; -import { preferences, usePreferences } from '@vben/preferences'; +import { preferences } from '@vben/preferences'; import { useAccessStore, useUserStore } from '@vben/stores'; import { openWindow } from '@vben/utils'; @@ -80,7 +80,6 @@ const userStore = useUserStore(); const authStore = useAuthStore(); const accessStore = useAccessStore(); const { destroyWatermark, updateWatermark } = useWatermark(); -const { isDark } = usePreferences(); const showDot = computed(() => notifications.value.some((item) => !item.isRead), ); @@ -180,28 +179,10 @@ watch( () => ({ enable: preferences.app.watermark, content: preferences.app.watermarkContent, - isDark: isDark.value, }), - async ({ enable, content, isDark: isDarkValue }) => { + async ({ enable, content }) => { if (enable) { - const watermarkColor = isDarkValue - ? 'rgba(255, 255, 255, 0.12)' - : 'rgba(0, 0, 0, 0.12)'; - await updateWatermark({ - advancedStyle: { - colorStops: [ - { - color: watermarkColor, - offset: 0, - }, - { - color: watermarkColor, - offset: 1, - }, - ], - type: 'linear', - }, content: content || `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`, diff --git a/apps/web-antdv-next/src/layouts/basic.vue b/apps/web-antdv-next/src/layouts/basic.vue index 1bb07ec8f..211ee7be3 100644 --- a/apps/web-antdv-next/src/layouts/basic.vue +++ b/apps/web-antdv-next/src/layouts/basic.vue @@ -14,7 +14,7 @@ import { Notification, UserDropdown, } from '@vben/layouts'; -import { preferences, usePreferences } from '@vben/preferences'; +import { preferences } from '@vben/preferences'; import { useAccessStore, useUserStore } from '@vben/stores'; import { openWindow } from '@vben/utils'; @@ -80,7 +80,6 @@ const userStore = useUserStore(); const authStore = useAuthStore(); const accessStore = useAccessStore(); const { destroyWatermark, updateWatermark } = useWatermark(); -const { isDark } = usePreferences(); const showDot = computed(() => notifications.value.some((item) => !item.isRead), ); @@ -180,28 +179,10 @@ watch( () => ({ enable: preferences.app.watermark, content: preferences.app.watermarkContent, - isDark: isDark.value, }), - async ({ enable, content, isDark: isDarkValue }) => { + async ({ enable, content }) => { if (enable) { - const watermarkColor = isDarkValue - ? 'rgba(255, 255, 255, 0.12)' - : 'rgba(0, 0, 0, 0.12)'; - await updateWatermark({ - advancedStyle: { - colorStops: [ - { - color: watermarkColor, - offset: 0, - }, - { - color: watermarkColor, - offset: 1, - }, - ], - type: 'linear', - }, content: content || `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`, diff --git a/apps/web-ele/src/layouts/basic.vue b/apps/web-ele/src/layouts/basic.vue index 1bb07ec8f..211ee7be3 100644 --- a/apps/web-ele/src/layouts/basic.vue +++ b/apps/web-ele/src/layouts/basic.vue @@ -14,7 +14,7 @@ import { Notification, UserDropdown, } from '@vben/layouts'; -import { preferences, usePreferences } from '@vben/preferences'; +import { preferences } from '@vben/preferences'; import { useAccessStore, useUserStore } from '@vben/stores'; import { openWindow } from '@vben/utils'; @@ -80,7 +80,6 @@ const userStore = useUserStore(); const authStore = useAuthStore(); const accessStore = useAccessStore(); const { destroyWatermark, updateWatermark } = useWatermark(); -const { isDark } = usePreferences(); const showDot = computed(() => notifications.value.some((item) => !item.isRead), ); @@ -180,28 +179,10 @@ watch( () => ({ enable: preferences.app.watermark, content: preferences.app.watermarkContent, - isDark: isDark.value, }), - async ({ enable, content, isDark: isDarkValue }) => { + async ({ enable, content }) => { if (enable) { - const watermarkColor = isDarkValue - ? 'rgba(255, 255, 255, 0.12)' - : 'rgba(0, 0, 0, 0.12)'; - await updateWatermark({ - advancedStyle: { - colorStops: [ - { - color: watermarkColor, - offset: 0, - }, - { - color: watermarkColor, - offset: 1, - }, - ], - type: 'linear', - }, content: content || `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`, diff --git a/apps/web-naive/src/layouts/basic.vue b/apps/web-naive/src/layouts/basic.vue index 1bb07ec8f..211ee7be3 100644 --- a/apps/web-naive/src/layouts/basic.vue +++ b/apps/web-naive/src/layouts/basic.vue @@ -14,7 +14,7 @@ import { Notification, UserDropdown, } from '@vben/layouts'; -import { preferences, usePreferences } from '@vben/preferences'; +import { preferences } from '@vben/preferences'; import { useAccessStore, useUserStore } from '@vben/stores'; import { openWindow } from '@vben/utils'; @@ -80,7 +80,6 @@ const userStore = useUserStore(); const authStore = useAuthStore(); const accessStore = useAccessStore(); const { destroyWatermark, updateWatermark } = useWatermark(); -const { isDark } = usePreferences(); const showDot = computed(() => notifications.value.some((item) => !item.isRead), ); @@ -180,28 +179,10 @@ watch( () => ({ enable: preferences.app.watermark, content: preferences.app.watermarkContent, - isDark: isDark.value, }), - async ({ enable, content, isDark: isDarkValue }) => { + async ({ enable, content }) => { if (enable) { - const watermarkColor = isDarkValue - ? 'rgba(255, 255, 255, 0.12)' - : 'rgba(0, 0, 0, 0.12)'; - await updateWatermark({ - advancedStyle: { - colorStops: [ - { - color: watermarkColor, - offset: 0, - }, - { - color: watermarkColor, - offset: 1, - }, - ], - type: 'linear', - }, content: content || `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`, diff --git a/apps/web-tdesign/src/layouts/basic.vue b/apps/web-tdesign/src/layouts/basic.vue index 1bb07ec8f..211ee7be3 100644 --- a/apps/web-tdesign/src/layouts/basic.vue +++ b/apps/web-tdesign/src/layouts/basic.vue @@ -14,7 +14,7 @@ import { Notification, UserDropdown, } from '@vben/layouts'; -import { preferences, usePreferences } from '@vben/preferences'; +import { preferences } from '@vben/preferences'; import { useAccessStore, useUserStore } from '@vben/stores'; import { openWindow } from '@vben/utils'; @@ -80,7 +80,6 @@ const userStore = useUserStore(); const authStore = useAuthStore(); const accessStore = useAccessStore(); const { destroyWatermark, updateWatermark } = useWatermark(); -const { isDark } = usePreferences(); const showDot = computed(() => notifications.value.some((item) => !item.isRead), ); @@ -180,28 +179,10 @@ watch( () => ({ enable: preferences.app.watermark, content: preferences.app.watermarkContent, - isDark: isDark.value, }), - async ({ enable, content, isDark: isDarkValue }) => { + async ({ enable, content }) => { if (enable) { - const watermarkColor = isDarkValue - ? 'rgba(255, 255, 255, 0.12)' - : 'rgba(0, 0, 0, 0.12)'; - await updateWatermark({ - advancedStyle: { - colorStops: [ - { - color: watermarkColor, - offset: 0, - }, - { - color: watermarkColor, - offset: 1, - }, - ], - type: 'linear', - }, content: content || `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`, diff --git a/packages/effects/hooks/src/use-watermark.ts b/packages/effects/hooks/src/use-watermark.ts index 8484a0824..533c05098 100644 --- a/packages/effects/hooks/src/use-watermark.ts +++ b/packages/effects/hooks/src/use-watermark.ts @@ -1,43 +1,84 @@ import type { Watermark, WatermarkOptions } from 'watermark-js-plus'; -import { nextTick, onUnmounted, readonly, ref } from 'vue'; +import { nextTick, onUnmounted, readonly, ref, watch } from 'vue'; + +import { usePreferences } from '@vben/preferences'; const watermark = ref(); const unmountedHooked = ref(false); -const cachedOptions = ref>({ - advancedStyle: { - colorStops: [ - { - color: 'gray', - offset: 0, - }, - { - color: 'gray', - offset: 1, - }, - ], - type: 'linear', - }, - // fontSize: '20px', - content: '', - contentType: 'multi-line-text', - globalAlpha: 0.25, - gridLayoutOptions: { - cols: 2, - gap: [20, 20], - matrix: [ - [1, 0], - [0, 1], - ], - rows: 2, - }, - height: 200, - layout: 'grid', - rotate: 30, - width: 160, -}); +const themeWatcherHooked = ref(false); + +function getDefaultWatermarkColor(isDark: boolean): string { + return isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.12)'; +} + +function getDefaultOptions(isDark: boolean): Partial { + const color = getDefaultWatermarkColor(isDark); + return { + advancedStyle: { + colorStops: [ + { + color, + offset: 0, + }, + { + color, + offset: 1, + }, + ], + type: 'linear', + }, + content: '', + contentType: 'multi-line-text', + globalAlpha: 0.25, + gridLayoutOptions: { + cols: 2, + gap: [20, 20], + matrix: [ + [1, 0], + [0, 1], + ], + rows: 2, + }, + height: 200, + layout: 'grid', + rotate: 30, + width: 160, + }; +} + +const cachedOptions = ref>({}); export function useWatermark() { + const { isDark } = usePreferences(); + + function mergeOptionsWithTheme( + options: Partial, + isDarkValue: boolean, + ): Partial { + const defaultOpts = getDefaultOptions(isDarkValue); + + if (options.advancedStyle?.colorStops) { + return { + ...defaultOpts, + ...cachedOptions.value, + ...options, + }; + } + + return { + ...defaultOpts, + ...cachedOptions.value, + ...options, + advancedStyle: { + ...defaultOpts.advancedStyle, + ...cachedOptions.value?.advancedStyle, + ...options?.advancedStyle, + colorStops: defaultOpts.advancedStyle?.colorStops, + }, + }; + } + async function initWatermark(options: Partial) { const { Watermark } = await import('watermark-js-plus'); @@ -45,17 +86,23 @@ export function useWatermark() { ...cachedOptions.value, ...options, }; - watermark.value = new Watermark(cachedOptions.value); + + const mergedOptions = mergeOptionsWithTheme(options, isDark.value); + watermark.value = new Watermark(mergedOptions); await watermark.value?.create(); } async function updateWatermark(options: Partial) { if (watermark.value) { await nextTick(); - await watermark.value?.changeOptions({ + + cachedOptions.value = { ...cachedOptions.value, ...options, - }); + }; + + const mergedOptions = mergeOptionsWithTheme(options, isDark.value); + await watermark.value?.changeOptions(mergedOptions); } else { await initWatermark(options); } @@ -68,7 +115,23 @@ export function useWatermark() { } } - // 只在第一次调用时注册卸载钩子,防止重复注册以致于在路由切换时销毁了水印 + if (!themeWatcherHooked.value) { + themeWatcherHooked.value = true; + + watch( + () => isDark.value, + () => { + if (watermark.value) { + const mergedOptions = mergeOptionsWithTheme( + cachedOptions.value, + isDark.value, + ); + watermark.value.changeOptions(mergedOptions); + } + }, + ); + } + if (!unmountedHooked.value) { unmountedHooked.value = true; onUnmounted(() => { diff --git a/playground/src/layouts/basic.vue b/playground/src/layouts/basic.vue index 62b641768..69fefbe33 100644 --- a/playground/src/layouts/basic.vue +++ b/playground/src/layouts/basic.vue @@ -14,7 +14,7 @@ import { Notification, UserDropdown, } from '@vben/layouts'; -import { preferences, usePreferences } from '@vben/preferences'; +import { preferences } from '@vben/preferences'; import { useAccessStore, useTabbarStore, useUserStore } from '@vben/stores'; import { openWindow } from '@vben/utils'; @@ -93,7 +93,6 @@ const userStore = useUserStore(); const authStore = useAuthStore(); const accessStore = useAccessStore(); const { destroyWatermark, updateWatermark } = useWatermark(); -const { isDark } = usePreferences(); const showDot = computed(() => notifications.value.some((item) => !item.isRead), ); @@ -195,28 +194,10 @@ watch( () => ({ enable: preferences.app.watermark, content: preferences.app.watermarkContent, - isDark: isDark.value, }), - async ({ enable, content, isDark: isDarkValue }) => { + async ({ enable, content }) => { if (enable) { - const watermarkColor = isDarkValue - ? 'rgba(255, 255, 255, 0.12)' - : 'rgba(0, 0, 0, 0.12)'; - await updateWatermark({ - advancedStyle: { - colorStops: [ - { - color: watermarkColor, - offset: 0, - }, - { - color: watermarkColor, - offset: 1, - }, - ], - type: 'linear', - }, content: content || `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,