mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-17 17:26:38 +08:00
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
This commit is contained in:
parent
8ba71a0857
commit
f238fcbd47
@ -4,7 +4,6 @@ import { NConfigProvider, darkTheme } from 'naive-ui';
|
|||||||
import type { WatermarkProps } from 'naive-ui';
|
import type { WatermarkProps } from 'naive-ui';
|
||||||
import { useAppStore } from './store/modules/app';
|
import { useAppStore } from './store/modules/app';
|
||||||
import { useThemeStore } from './store/modules/theme';
|
import { useThemeStore } from './store/modules/theme';
|
||||||
import { useAuthStore } from './store/modules/auth';
|
|
||||||
import { naiveDateLocales, naiveLocales } from './locales/naive';
|
import { naiveDateLocales, naiveLocales } from './locales/naive';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -13,7 +12,6 @@ defineOptions({
|
|||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const authStore = useAuthStore();
|
|
||||||
|
|
||||||
const naiveDarkTheme = computed(() => (themeStore.darkMode ? darkTheme : undefined));
|
const naiveDarkTheme = computed(() => (themeStore.darkMode ? darkTheme : undefined));
|
||||||
|
|
||||||
@ -26,13 +24,8 @@ const naiveDateLocale = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const watermarkProps = computed<WatermarkProps>(() => {
|
const watermarkProps = computed<WatermarkProps>(() => {
|
||||||
const content =
|
|
||||||
themeStore.watermark.enableUserName && authStore.userInfo.userName
|
|
||||||
? authStore.userInfo.userName
|
|
||||||
: themeStore.watermark.text;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content,
|
content: themeStore.watermarkContent,
|
||||||
cross: true,
|
cross: true,
|
||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
@ -63,3 +63,13 @@ export const resetCacheStrategyRecord: Record<UnionKey.ResetCacheStrategy, App.I
|
|||||||
export const resetCacheStrategyOptions = transformRecordToOption(resetCacheStrategyRecord);
|
export const resetCacheStrategyOptions = transformRecordToOption(resetCacheStrategyRecord);
|
||||||
|
|
||||||
export const DARK_CLASS = 'dark';
|
export const DARK_CLASS = 'dark';
|
||||||
|
|
||||||
|
export const watermarkTimeFormatOptions = [
|
||||||
|
{ label: 'YYYY-MM-DD HH:mm', value: 'YYYY-MM-DD HH:mm' },
|
||||||
|
{ label: 'YYYY-MM-DD HH:mm:ss', value: 'YYYY-MM-DD HH:mm:ss' },
|
||||||
|
{ label: 'YYYY/MM/DD HH:mm', value: 'YYYY/MM/DD HH:mm' },
|
||||||
|
{ label: 'YYYY/MM/DD HH:mm:ss', value: 'YYYY/MM/DD HH:mm:ss' },
|
||||||
|
{ label: 'HH:mm', value: 'HH:mm' },
|
||||||
|
{ label: 'HH:mm:ss', value: 'HH:mm:ss' },
|
||||||
|
{ label: 'MM-DD HH:mm', value: 'MM-DD HH:mm' }
|
||||||
|
];
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import GlobalSettings from './modules/global-settings.vue';
|
import GlobalSettings from './modules/global-settings.vue';
|
||||||
|
import WatermarkSettings from './modules/watermark-settings.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'GeneralSettings'
|
name: 'GeneralSettings'
|
||||||
@ -9,6 +10,7 @@ defineOptions({
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex-col-stretch gap-16px">
|
<div class="flex-col-stretch gap-16px">
|
||||||
<GlobalSettings />
|
<GlobalSettings />
|
||||||
|
<WatermarkSettings />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ const themeStore = useThemeStore();
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex-col-stretch gap-16px">
|
<NDivider>{{ $t('theme.general.title') }}</NDivider>
|
||||||
<SettingItem :label="$t('theme.general.multilingual.visible')">
|
<SettingItem :label="$t('theme.general.multilingual.visible')">
|
||||||
<NSwitch v-model:value="themeStore.header.multilingual.visible" />
|
<NSwitch v-model:value="themeStore.header.multilingual.visible" />
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
@ -19,26 +19,6 @@ const themeStore = useThemeStore();
|
|||||||
<SettingItem :label="$t('theme.general.globalSearch.visible')">
|
<SettingItem :label="$t('theme.general.globalSearch.visible')">
|
||||||
<NSwitch v-model:value="themeStore.header.globalSearch.visible" />
|
<NSwitch v-model:value="themeStore.header.globalSearch.visible" />
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
|
||||||
<SettingItem key="1" :label="$t('theme.general.watermark.visible')">
|
|
||||||
<NSwitch v-model:value="themeStore.watermark.visible" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="themeStore.watermark.visible" key="2" :label="$t('theme.general.watermark.enableUserName')">
|
|
||||||
<NSwitch v-model:value="themeStore.watermark.enableUserName" />
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem v-if="themeStore.watermark.visible" key="3" :label="$t('theme.general.watermark.text')">
|
|
||||||
<NInput
|
|
||||||
v-model:value="themeStore.watermark.text"
|
|
||||||
autosize
|
|
||||||
type="text"
|
|
||||||
size="small"
|
|
||||||
class="w-120px"
|
|
||||||
placeholder="SoybeanAdmin"
|
|
||||||
/>
|
|
||||||
</SettingItem>
|
|
||||||
</TransitionGroup>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { watermarkTimeFormatOptions } from '@/constants/app';
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
import SettingItem from '../../../components/setting-item.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'WatermarkSettings'
|
||||||
|
});
|
||||||
|
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
|
||||||
|
const isWatermarkTextVisible = computed(
|
||||||
|
() => themeStore.watermark.visible && !themeStore.watermark.enableUserName && !themeStore.watermark.enableTime
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDivider>{{ $t('theme.general.watermark.title') }}</NDivider>
|
||||||
|
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
||||||
|
<SettingItem key="1" :label="$t('theme.general.watermark.visible')">
|
||||||
|
<NSwitch v-model:value="themeStore.watermark.visible" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="themeStore.watermark.visible" key="2" :label="$t('theme.general.watermark.enableUserName')">
|
||||||
|
<NSwitch :value="themeStore.watermark.enableUserName" @update:value="themeStore.setWatermarkEnableUserName" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="themeStore.watermark.visible" key="3" :label="$t('theme.general.watermark.enableTime')">
|
||||||
|
<NSwitch :value="themeStore.watermark.enableTime" @update:value="themeStore.setWatermarkEnableTime" />
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem
|
||||||
|
v-if="themeStore.watermark.visible && themeStore.watermark.enableTime"
|
||||||
|
key="4"
|
||||||
|
:label="$t('theme.general.watermark.timeFormat')"
|
||||||
|
>
|
||||||
|
<NSelect
|
||||||
|
v-model:value="themeStore.watermark.timeFormat"
|
||||||
|
:options="watermarkTimeFormatOptions"
|
||||||
|
size="small"
|
||||||
|
class="w-210px"
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem v-if="isWatermarkTextVisible" key="5" :label="$t('theme.general.watermark.text')">
|
||||||
|
<NInput
|
||||||
|
v-model:value="themeStore.watermark.text"
|
||||||
|
autosize
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
class="w-120px"
|
||||||
|
placeholder="SoybeanAdmin"
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
</TransitionGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.setting-list-move,
|
||||||
|
.setting-list-enter-active,
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: transition-all-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-enter-from,
|
||||||
|
.setting-list-leave-to {
|
||||||
|
--uno: opacity-0 -translate-x-30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-list-leave-active {
|
||||||
|
--uno: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
@ -158,10 +158,14 @@ const local: App.I18n.Schema = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
title: 'General Settings',
|
||||||
watermark: {
|
watermark: {
|
||||||
|
title: 'Watermark Settings',
|
||||||
visible: 'Watermark Full Screen Visible',
|
visible: 'Watermark Full Screen Visible',
|
||||||
text: 'Watermark Text',
|
text: 'Custom Watermark Text',
|
||||||
enableUserName: 'Enable User Name Watermark'
|
enableUserName: 'Enable User Name Watermark',
|
||||||
|
enableTime: 'Show Current Time',
|
||||||
|
timeFormat: 'Time Format'
|
||||||
},
|
},
|
||||||
multilingual: {
|
multilingual: {
|
||||||
title: 'Multilingual Settings',
|
title: 'Multilingual Settings',
|
||||||
|
@ -158,10 +158,14 @@ const local: App.I18n.Schema = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
title: '通用设置',
|
||||||
watermark: {
|
watermark: {
|
||||||
|
title: '水印设置',
|
||||||
visible: '显示全屏水印',
|
visible: '显示全屏水印',
|
||||||
text: '水印文本',
|
text: '自定义水印文本',
|
||||||
enableUserName: '启用用户名水印'
|
enableUserName: '启用用户名水印',
|
||||||
|
enableTime: '显示当前时间',
|
||||||
|
timeFormat: '时间格式'
|
||||||
},
|
},
|
||||||
multilingual: {
|
multilingual: {
|
||||||
title: '多语言设置',
|
title: '多语言设置',
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { computed, effectScope, onScopeDispose, ref, toRefs, watch } from 'vue';
|
import { computed, effectScope, onScopeDispose, ref, toRefs, watch } from 'vue';
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
import { useEventListener, usePreferredColorScheme } from '@vueuse/core';
|
import { useDateFormat, useEventListener, useNow, usePreferredColorScheme } from '@vueuse/core';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { getPaletteColorByNumber } from '@sa/color';
|
import { getPaletteColorByNumber } from '@sa/color';
|
||||||
import { localStg } from '@/utils/storage';
|
import { localStg } from '@/utils/storage';
|
||||||
import { SetupStoreId } from '@/enum';
|
import { SetupStoreId } from '@/enum';
|
||||||
|
import { useAuthStore } from '../auth';
|
||||||
import {
|
import {
|
||||||
addThemeVarsToGlobal,
|
addThemeVarsToGlobal,
|
||||||
createThemeToken,
|
createThemeToken,
|
||||||
@ -18,10 +19,14 @@ import {
|
|||||||
export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
||||||
const scope = effectScope();
|
const scope = effectScope();
|
||||||
const osTheme = usePreferredColorScheme();
|
const osTheme = usePreferredColorScheme();
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
/** Theme settings */
|
/** Theme settings */
|
||||||
const settings: Ref<App.Theme.ThemeSetting> = ref(initThemeSettings());
|
const settings: Ref<App.Theme.ThemeSetting> = ref(initThemeSettings());
|
||||||
|
|
||||||
|
/** Watermark time instance with controls */
|
||||||
|
const { now: watermarkTime, pause: pauseWatermarkTime, resume: resumeWatermarkTime } = useNow({ controls: true });
|
||||||
|
|
||||||
/** Dark mode */
|
/** Dark mode */
|
||||||
const darkMode = computed(() => {
|
const darkMode = computed(() => {
|
||||||
if (settings.value.themeScheme === 'auto') {
|
if (settings.value.themeScheme === 'auto') {
|
||||||
@ -57,6 +62,28 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
*/
|
*/
|
||||||
const settingsJson = computed(() => JSON.stringify(settings.value));
|
const settingsJson = computed(() => JSON.stringify(settings.value));
|
||||||
|
|
||||||
|
/** Watermark time date formatter */
|
||||||
|
const formattedWatermarkTime = computed(() => {
|
||||||
|
const { watermark } = settings.value;
|
||||||
|
const date = useDateFormat(watermarkTime, watermark.timeFormat);
|
||||||
|
return date.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Watermark content */
|
||||||
|
const watermarkContent = computed(() => {
|
||||||
|
const { watermark } = settings.value;
|
||||||
|
|
||||||
|
if (watermark.enableUserName && authStore.userInfo.userName) {
|
||||||
|
return authStore.userInfo.userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watermark.enableTime) {
|
||||||
|
return formattedWatermarkTime.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return watermark.text;
|
||||||
|
});
|
||||||
|
|
||||||
/** Reset store */
|
/** Reset store */
|
||||||
function resetStore() {
|
function resetStore() {
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
@ -153,6 +180,44 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
settings.value.layout.reverseHorizontalMix = reverse;
|
settings.value.layout.reverseHorizontalMix = reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set watermark enable user name
|
||||||
|
*
|
||||||
|
* @param enable Whether to enable user name watermark
|
||||||
|
*/
|
||||||
|
function setWatermarkEnableUserName(enable: boolean) {
|
||||||
|
settings.value.watermark.enableUserName = enable;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
settings.value.watermark.enableTime = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set watermark enable time
|
||||||
|
*
|
||||||
|
* @param enable Whether to enable time watermark
|
||||||
|
*/
|
||||||
|
function setWatermarkEnableTime(enable: boolean) {
|
||||||
|
settings.value.watermark.enableTime = enable;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
settings.value.watermark.enableUserName = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Only run timer when watermark is visible and time display is enabled */
|
||||||
|
function updateWatermarkTimer() {
|
||||||
|
const { watermark } = settings.value;
|
||||||
|
const shouldRunTimer = watermark.visible && watermark.enableTime;
|
||||||
|
|
||||||
|
if (shouldRunTimer) {
|
||||||
|
resumeWatermarkTime();
|
||||||
|
} else {
|
||||||
|
pauseWatermarkTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Cache theme settings */
|
/** Cache theme settings */
|
||||||
function cacheThemeSettings() {
|
function cacheThemeSettings() {
|
||||||
const isProd = import.meta.env.PROD;
|
const isProd = import.meta.env.PROD;
|
||||||
@ -196,6 +261,15 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// watch watermark settings to control timer
|
||||||
|
watch(
|
||||||
|
() => [settings.value.watermark.visible, settings.value.watermark.enableTime],
|
||||||
|
() => {
|
||||||
|
updateWatermarkTimer();
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/** On scope dispose */
|
/** On scope dispose */
|
||||||
@ -209,6 +283,7 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
themeColors,
|
themeColors,
|
||||||
naiveTheme,
|
naiveTheme,
|
||||||
settingsJson,
|
settingsJson,
|
||||||
|
watermarkContent,
|
||||||
setGrayscale,
|
setGrayscale,
|
||||||
setColourWeakness,
|
setColourWeakness,
|
||||||
resetStore,
|
resetStore,
|
||||||
@ -216,6 +291,8 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
toggleThemeScheme,
|
toggleThemeScheme,
|
||||||
updateThemeColors,
|
updateThemeColors,
|
||||||
setThemeLayout,
|
setThemeLayout,
|
||||||
setLayoutReverseHorizontalMix
|
setLayoutReverseHorizontalMix,
|
||||||
|
setWatermarkEnableUserName,
|
||||||
|
setWatermarkEnableTime
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -59,7 +59,9 @@ export const themeSettings: App.Theme.ThemeSetting = {
|
|||||||
watermark: {
|
watermark: {
|
||||||
visible: false,
|
visible: false,
|
||||||
text: 'SoybeanAdmin',
|
text: 'SoybeanAdmin',
|
||||||
enableUserName: false
|
enableUserName: false,
|
||||||
|
enableTime: false,
|
||||||
|
timeFormat: 'YYYY-MM-DD HH:mm'
|
||||||
},
|
},
|
||||||
tokens: {
|
tokens: {
|
||||||
light: {
|
light: {
|
||||||
|
8
src/typings/app.d.ts
vendored
8
src/typings/app.d.ts
vendored
@ -114,6 +114,10 @@ declare namespace App {
|
|||||||
text: string;
|
text: string;
|
||||||
/** Whether to use user name as watermark text */
|
/** Whether to use user name as watermark text */
|
||||||
enableUserName: boolean;
|
enableUserName: boolean;
|
||||||
|
/** Whether to use current time as watermark text */
|
||||||
|
enableTime: boolean;
|
||||||
|
/** Time format for watermark text */
|
||||||
|
timeFormat: string;
|
||||||
};
|
};
|
||||||
/** define some theme settings tokens, will transform to css variables */
|
/** define some theme settings tokens, will transform to css variables */
|
||||||
tokens: {
|
tokens: {
|
||||||
@ -420,10 +424,14 @@ declare namespace App {
|
|||||||
resetCacheStrategy: { title: string } & Record<UnionKey.ResetCacheStrategy, string>;
|
resetCacheStrategy: { title: string } & Record<UnionKey.ResetCacheStrategy, string>;
|
||||||
};
|
};
|
||||||
general: {
|
general: {
|
||||||
|
title: string;
|
||||||
watermark: {
|
watermark: {
|
||||||
|
title: string;
|
||||||
visible: string;
|
visible: string;
|
||||||
text: string;
|
text: string;
|
||||||
enableUserName: string;
|
enableUserName: string;
|
||||||
|
enableTime: string;
|
||||||
|
timeFormat: string;
|
||||||
};
|
};
|
||||||
multilingual: {
|
multilingual: {
|
||||||
title: string;
|
title: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user