Skip to content

Commit f238fcb

Browse files
wenyuanwhonghuangdc
authored andcommitted
feat(projects): Add current time display option for watermark (#772)
* feat(projects): Add current time display option for watermark * perf(projects): add watermark timer controls
1 parent 8ba71a0 commit f238fcb

File tree

10 files changed

+194
-43
lines changed

10 files changed

+194
-43
lines changed

src/App.vue

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { NConfigProvider, darkTheme } from 'naive-ui';
44
import type { WatermarkProps } from 'naive-ui';
55
import { useAppStore } from './store/modules/app';
66
import { useThemeStore } from './store/modules/theme';
7-
import { useAuthStore } from './store/modules/auth';
87
import { naiveDateLocales, naiveLocales } from './locales/naive';
98
109
defineOptions({
@@ -13,7 +12,6 @@ defineOptions({
1312
1413
const appStore = useAppStore();
1514
const themeStore = useThemeStore();
16-
const authStore = useAuthStore();
1715
1816
const naiveDarkTheme = computed(() => (themeStore.darkMode ? darkTheme : undefined));
1917
@@ -26,13 +24,8 @@ const naiveDateLocale = computed(() => {
2624
});
2725
2826
const watermarkProps = computed<WatermarkProps>(() => {
29-
const content =
30-
themeStore.watermark.enableUserName && authStore.userInfo.userName
31-
? authStore.userInfo.userName
32-
: themeStore.watermark.text;
33-
3427
return {
35-
content,
28+
content: themeStore.watermarkContent,
3629
cross: true,
3730
fullscreen: true,
3831
fontSize: 16,

src/constants/app.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,13 @@ export const resetCacheStrategyRecord: Record<UnionKey.ResetCacheStrategy, App.I
6363
export const resetCacheStrategyOptions = transformRecordToOption(resetCacheStrategyRecord);
6464

6565
export const DARK_CLASS = 'dark';
66+
67+
export const watermarkTimeFormatOptions = [
68+
{ label: 'YYYY-MM-DD HH:mm', value: 'YYYY-MM-DD HH:mm' },
69+
{ label: 'YYYY-MM-DD HH:mm:ss', value: 'YYYY-MM-DD HH:mm:ss' },
70+
{ label: 'YYYY/MM/DD HH:mm', value: 'YYYY/MM/DD HH:mm' },
71+
{ label: 'YYYY/MM/DD HH:mm:ss', value: 'YYYY/MM/DD HH:mm:ss' },
72+
{ label: 'HH:mm', value: 'HH:mm' },
73+
{ label: 'HH:mm:ss', value: 'HH:mm:ss' },
74+
{ label: 'MM-DD HH:mm', value: 'MM-DD HH:mm' }
75+
];

src/layouts/modules/theme-drawer/modules/general/index.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
import GlobalSettings from './modules/global-settings.vue';
3+
import WatermarkSettings from './modules/watermark-settings.vue';
34
45
defineOptions({
56
name: 'GeneralSettings'
@@ -9,6 +10,7 @@ defineOptions({
910
<template>
1011
<div class="flex-col-stretch gap-16px">
1112
<GlobalSettings />
13+
<WatermarkSettings />
1214
</div>
1315
</template>
1416

src/layouts/modules/theme-drawer/modules/general/modules/global-settings.vue

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,14 @@ const themeStore = useThemeStore();
1111
</script>
1212

1313
<template>
14-
<div class="flex-col-stretch gap-16px">
15-
<SettingItem :label="$t('theme.general.multilingual.visible')">
16-
<NSwitch v-model:value="themeStore.header.multilingual.visible" />
17-
</SettingItem>
18-
19-
<SettingItem :label="$t('theme.general.globalSearch.visible')">
20-
<NSwitch v-model:value="themeStore.header.globalSearch.visible" />
21-
</SettingItem>
22-
23-
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
24-
<SettingItem key="1" :label="$t('theme.general.watermark.visible')">
25-
<NSwitch v-model:value="themeStore.watermark.visible" />
26-
</SettingItem>
27-
<SettingItem v-if="themeStore.watermark.visible" key="2" :label="$t('theme.general.watermark.enableUserName')">
28-
<NSwitch v-model:value="themeStore.watermark.enableUserName" />
29-
</SettingItem>
30-
<SettingItem v-if="themeStore.watermark.visible" key="3" :label="$t('theme.general.watermark.text')">
31-
<NInput
32-
v-model:value="themeStore.watermark.text"
33-
autosize
34-
type="text"
35-
size="small"
36-
class="w-120px"
37-
placeholder="SoybeanAdmin"
38-
/>
39-
</SettingItem>
40-
</TransitionGroup>
41-
</div>
14+
<NDivider>{{ $t('theme.general.title') }}</NDivider>
15+
<SettingItem :label="$t('theme.general.multilingual.visible')">
16+
<NSwitch v-model:value="themeStore.header.multilingual.visible" />
17+
</SettingItem>
18+
19+
<SettingItem :label="$t('theme.general.globalSearch.visible')">
20+
<NSwitch v-model:value="themeStore.header.globalSearch.visible" />
21+
</SettingItem>
4222
</template>
4323

4424
<style scoped>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<script setup lang="ts">
2+
import { computed } from 'vue';
3+
import { watermarkTimeFormatOptions } from '@/constants/app';
4+
import { useThemeStore } from '@/store/modules/theme';
5+
import { $t } from '@/locales';
6+
import SettingItem from '../../../components/setting-item.vue';
7+
8+
defineOptions({
9+
name: 'WatermarkSettings'
10+
});
11+
12+
const themeStore = useThemeStore();
13+
14+
const isWatermarkTextVisible = computed(
15+
() => themeStore.watermark.visible && !themeStore.watermark.enableUserName && !themeStore.watermark.enableTime
16+
);
17+
</script>
18+
19+
<template>
20+
<NDivider>{{ $t('theme.general.watermark.title') }}</NDivider>
21+
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
22+
<SettingItem key="1" :label="$t('theme.general.watermark.visible')">
23+
<NSwitch v-model:value="themeStore.watermark.visible" />
24+
</SettingItem>
25+
<SettingItem v-if="themeStore.watermark.visible" key="2" :label="$t('theme.general.watermark.enableUserName')">
26+
<NSwitch :value="themeStore.watermark.enableUserName" @update:value="themeStore.setWatermarkEnableUserName" />
27+
</SettingItem>
28+
<SettingItem v-if="themeStore.watermark.visible" key="3" :label="$t('theme.general.watermark.enableTime')">
29+
<NSwitch :value="themeStore.watermark.enableTime" @update:value="themeStore.setWatermarkEnableTime" />
30+
</SettingItem>
31+
<SettingItem
32+
v-if="themeStore.watermark.visible && themeStore.watermark.enableTime"
33+
key="4"
34+
:label="$t('theme.general.watermark.timeFormat')"
35+
>
36+
<NSelect
37+
v-model:value="themeStore.watermark.timeFormat"
38+
:options="watermarkTimeFormatOptions"
39+
size="small"
40+
class="w-210px"
41+
/>
42+
</SettingItem>
43+
<SettingItem v-if="isWatermarkTextVisible" key="5" :label="$t('theme.general.watermark.text')">
44+
<NInput
45+
v-model:value="themeStore.watermark.text"
46+
autosize
47+
type="text"
48+
size="small"
49+
class="w-120px"
50+
placeholder="SoybeanAdmin"
51+
/>
52+
</SettingItem>
53+
</TransitionGroup>
54+
</template>
55+
56+
<style scoped>
57+
.setting-list-move,
58+
.setting-list-enter-active,
59+
.setting-list-leave-active {
60+
--uno: transition-all-300;
61+
}
62+
63+
.setting-list-enter-from,
64+
.setting-list-leave-to {
65+
--uno: opacity-0 -translate-x-30px;
66+
}
67+
68+
.setting-list-leave-active {
69+
--uno: absolute;
70+
}
71+
</style>

src/locales/langs/en-us.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,14 @@ const local: App.I18n.Schema = {
158158
}
159159
},
160160
general: {
161+
title: 'General Settings',
161162
watermark: {
163+
title: 'Watermark Settings',
162164
visible: 'Watermark Full Screen Visible',
163-
text: 'Watermark Text',
164-
enableUserName: 'Enable User Name Watermark'
165+
text: 'Custom Watermark Text',
166+
enableUserName: 'Enable User Name Watermark',
167+
enableTime: 'Show Current Time',
168+
timeFormat: 'Time Format'
165169
},
166170
multilingual: {
167171
title: 'Multilingual Settings',

src/locales/langs/zh-cn.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,14 @@ const local: App.I18n.Schema = {
158158
}
159159
},
160160
general: {
161+
title: '通用设置',
161162
watermark: {
163+
title: '水印设置',
162164
visible: '显示全屏水印',
163-
text: '水印文本',
164-
enableUserName: '启用用户名水印'
165+
text: '自定义水印文本',
166+
enableUserName: '启用用户名水印',
167+
enableTime: '显示当前时间',
168+
timeFormat: '时间格式'
165169
},
166170
multilingual: {
167171
title: '多语言设置',

src/store/modules/theme/index.ts

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { computed, effectScope, onScopeDispose, ref, toRefs, watch } from 'vue';
22
import type { Ref } from 'vue';
3-
import { useEventListener, usePreferredColorScheme } from '@vueuse/core';
3+
import { useDateFormat, useEventListener, useNow, usePreferredColorScheme } from '@vueuse/core';
44
import { defineStore } from 'pinia';
55
import { getPaletteColorByNumber } from '@sa/color';
66
import { localStg } from '@/utils/storage';
77
import { SetupStoreId } from '@/enum';
8+
import { useAuthStore } from '../auth';
89
import {
910
addThemeVarsToGlobal,
1011
createThemeToken,
@@ -18,10 +19,14 @@ import {
1819
export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
1920
const scope = effectScope();
2021
const osTheme = usePreferredColorScheme();
22+
const authStore = useAuthStore();
2123

2224
/** Theme settings */
2325
const settings: Ref<App.Theme.ThemeSetting> = ref(initThemeSettings());
2426

27+
/** Watermark time instance with controls */
28+
const { now: watermarkTime, pause: pauseWatermarkTime, resume: resumeWatermarkTime } = useNow({ controls: true });
29+
2530
/** Dark mode */
2631
const darkMode = computed(() => {
2732
if (settings.value.themeScheme === 'auto') {
@@ -57,6 +62,28 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
5762
*/
5863
const settingsJson = computed(() => JSON.stringify(settings.value));
5964

65+
/** Watermark time date formatter */
66+
const formattedWatermarkTime = computed(() => {
67+
const { watermark } = settings.value;
68+
const date = useDateFormat(watermarkTime, watermark.timeFormat);
69+
return date.value;
70+
});
71+
72+
/** Watermark content */
73+
const watermarkContent = computed(() => {
74+
const { watermark } = settings.value;
75+
76+
if (watermark.enableUserName && authStore.userInfo.userName) {
77+
return authStore.userInfo.userName;
78+
}
79+
80+
if (watermark.enableTime) {
81+
return formattedWatermarkTime.value;
82+
}
83+
84+
return watermark.text;
85+
});
86+
6087
/** Reset store */
6188
function resetStore() {
6289
const themeStore = useThemeStore();
@@ -153,6 +180,44 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
153180
settings.value.layout.reverseHorizontalMix = reverse;
154181
}
155182

183+
/**
184+
* Set watermark enable user name
185+
*
186+
* @param enable Whether to enable user name watermark
187+
*/
188+
function setWatermarkEnableUserName(enable: boolean) {
189+
settings.value.watermark.enableUserName = enable;
190+
191+
if (enable) {
192+
settings.value.watermark.enableTime = false;
193+
}
194+
}
195+
196+
/**
197+
* Set watermark enable time
198+
*
199+
* @param enable Whether to enable time watermark
200+
*/
201+
function setWatermarkEnableTime(enable: boolean) {
202+
settings.value.watermark.enableTime = enable;
203+
204+
if (enable) {
205+
settings.value.watermark.enableUserName = false;
206+
}
207+
}
208+
209+
/** Only run timer when watermark is visible and time display is enabled */
210+
function updateWatermarkTimer() {
211+
const { watermark } = settings.value;
212+
const shouldRunTimer = watermark.visible && watermark.enableTime;
213+
214+
if (shouldRunTimer) {
215+
resumeWatermarkTime();
216+
} else {
217+
pauseWatermarkTime();
218+
}
219+
}
220+
156221
/** Cache theme settings */
157222
function cacheThemeSettings() {
158223
const isProd = import.meta.env.PROD;
@@ -196,6 +261,15 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
196261
},
197262
{ immediate: true }
198263
);
264+
265+
// watch watermark settings to control timer
266+
watch(
267+
() => [settings.value.watermark.visible, settings.value.watermark.enableTime],
268+
() => {
269+
updateWatermarkTimer();
270+
},
271+
{ immediate: true }
272+
);
199273
});
200274

201275
/** On scope dispose */
@@ -209,13 +283,16 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
209283
themeColors,
210284
naiveTheme,
211285
settingsJson,
286+
watermarkContent,
212287
setGrayscale,
213288
setColourWeakness,
214289
resetStore,
215290
setThemeScheme,
216291
toggleThemeScheme,
217292
updateThemeColors,
218293
setThemeLayout,
219-
setLayoutReverseHorizontalMix
294+
setLayoutReverseHorizontalMix,
295+
setWatermarkEnableUserName,
296+
setWatermarkEnableTime
220297
};
221298
});

src/theme/settings.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ export const themeSettings: App.Theme.ThemeSetting = {
5959
watermark: {
6060
visible: false,
6161
text: 'SoybeanAdmin',
62-
enableUserName: false
62+
enableUserName: false,
63+
enableTime: false,
64+
timeFormat: 'YYYY-MM-DD HH:mm'
6365
},
6466
tokens: {
6567
light: {

src/typings/app.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ declare namespace App {
114114
text: string;
115115
/** Whether to use user name as watermark text */
116116
enableUserName: boolean;
117+
/** Whether to use current time as watermark text */
118+
enableTime: boolean;
119+
/** Time format for watermark text */
120+
timeFormat: string;
117121
};
118122
/** define some theme settings tokens, will transform to css variables */
119123
tokens: {
@@ -420,10 +424,14 @@ declare namespace App {
420424
resetCacheStrategy: { title: string } & Record<UnionKey.ResetCacheStrategy, string>;
421425
};
422426
general: {
427+
title: string;
423428
watermark: {
429+
title: string;
424430
visible: string;
425431
text: string;
426432
enableUserName: string;
433+
enableTime: string;
434+
timeFormat: string;
427435
};
428436
multilingual: {
429437
title: string;

0 commit comments

Comments
 (0)