refactor(sj_1.2.0-beta1): 重构水印
This commit is contained in:
parent
f804e18e93
commit
b5bd40c25e
@ -30,6 +30,7 @@ const ContextHolder = defineComponent({
|
|||||||
<NMessageProvider>
|
<NMessageProvider>
|
||||||
<ContextHolder />
|
<ContextHolder />
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
<AppWatermark />
|
||||||
</NMessageProvider>
|
</NMessageProvider>
|
||||||
</NNotificationProvider>
|
</NNotificationProvider>
|
||||||
</NDialogProvider>
|
</NDialogProvider>
|
||||||
|
37
src/components/common/app-watermark.vue
Normal file
37
src/components/common/app-watermark.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useAuthStore } from '@/store/modules/auth';
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'AppWatermark'
|
||||||
|
});
|
||||||
|
|
||||||
|
const { watermark } = useThemeStore();
|
||||||
|
const { userInfo } = useAuthStore();
|
||||||
|
|
||||||
|
const watermarkContent = computed(() => {
|
||||||
|
const appTitle = import.meta.env.VITE_APP_TITLE || 'Snail Job';
|
||||||
|
return userInfo.userName ? `${userInfo.userName}@${appTitle}` : appTitle;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NWatermark
|
||||||
|
v-if="watermark.visible"
|
||||||
|
:content="watermarkContent"
|
||||||
|
cross
|
||||||
|
fullscreen
|
||||||
|
:font-size="14"
|
||||||
|
:line-height="14"
|
||||||
|
:width="200"
|
||||||
|
:height="300"
|
||||||
|
:x-offset="12"
|
||||||
|
:y-offset="60"
|
||||||
|
:rotate="-18"
|
||||||
|
:z-index="999"
|
||||||
|
font-color="rgba(200, 200, 200, 0.3)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
@ -1,130 +0,0 @@
|
|||||||
import type { Ref } from 'vue';
|
|
||||||
import { ref, shallowRef, unref } from 'vue';
|
|
||||||
import { debounce } from '@/utils/common';
|
|
||||||
|
|
||||||
type Attrs = {
|
|
||||||
textStyles?: {
|
|
||||||
font?: string;
|
|
||||||
fillStyle?: string;
|
|
||||||
rotate?: number;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
};
|
|
||||||
styles?: { [key: string]: any };
|
|
||||||
};
|
|
||||||
|
|
||||||
type WatermarkOpts = {
|
|
||||||
appendEl?: Ref<HTMLElement | null>;
|
|
||||||
id?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function useWatermark(opts: WatermarkOpts = {}) {
|
|
||||||
// const id = `waterMark_${Math.random().toString().slice(-10)}_${+new Date()}`
|
|
||||||
const appendEl = opts.appendEl || (ref(document.body) as Ref<HTMLElement>);
|
|
||||||
const watermarkEl = shallowRef<HTMLElement>();
|
|
||||||
|
|
||||||
/** 绘制canvas文字背景图 */
|
|
||||||
const createCanvasBase64 = (str: string, attrs: Attrs = {}) => {
|
|
||||||
const can = document.createElement('canvas');
|
|
||||||
const { rotate, font, fillStyle, width = 200, height = 140 } = attrs.textStyles || {};
|
|
||||||
Object.assign(can, { width, height });
|
|
||||||
const cans = can.getContext('2d');
|
|
||||||
if (cans) {
|
|
||||||
cans.rotate((-(rotate ?? 20) * Math.PI) / 180);
|
|
||||||
cans.font = font || '12px Vedana';
|
|
||||||
cans.fillStyle = fillStyle || 'rgba(200, 200, 200, 0.3)';
|
|
||||||
cans.textAlign = 'left';
|
|
||||||
cans.textBaseline = 'middle';
|
|
||||||
cans.fillText(str, can.width / 10, can.height / 2);
|
|
||||||
}
|
|
||||||
return can.toDataURL('image/png');
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 页面随窗口调整更新水印 */
|
|
||||||
const updateWatermark = (
|
|
||||||
watermarkOpts: {
|
|
||||||
str?: string;
|
|
||||||
attrs?: Attrs;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
} = {}
|
|
||||||
) => {
|
|
||||||
const el = unref(watermarkEl);
|
|
||||||
if (!el) return;
|
|
||||||
if (typeof watermarkOpts.width !== 'undefined') {
|
|
||||||
el.style.width = `${watermarkOpts.width}px`;
|
|
||||||
}
|
|
||||||
if (typeof watermarkOpts.height !== 'undefined') {
|
|
||||||
el.style.height = `${watermarkOpts.height}px`;
|
|
||||||
}
|
|
||||||
if (typeof watermarkOpts.str !== 'undefined') {
|
|
||||||
el.style.background = `url(${createCanvasBase64(watermarkOpts.str, watermarkOpts.attrs)}) left top repeat`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 绘制水印层 */
|
|
||||||
const createWatermark = (str: string, attrs: Attrs = {}) => {
|
|
||||||
if (watermarkEl.value) {
|
|
||||||
updateWatermark({ str, attrs });
|
|
||||||
return watermarkEl;
|
|
||||||
}
|
|
||||||
const div = document.createElement('div');
|
|
||||||
watermarkEl.value = div;
|
|
||||||
if (opts.id) {
|
|
||||||
const last_el = document.getElementById(opts.id);
|
|
||||||
if (last_el) {
|
|
||||||
document.body.removeChild(last_el);
|
|
||||||
}
|
|
||||||
div.id = opts.id;
|
|
||||||
}
|
|
||||||
Object.assign(
|
|
||||||
div.style,
|
|
||||||
{
|
|
||||||
pointerEvents: 'none',
|
|
||||||
top: '0px',
|
|
||||||
left: '0px',
|
|
||||||
position: 'fixed',
|
|
||||||
zIndex: '100000'
|
|
||||||
},
|
|
||||||
attrs.styles || {}
|
|
||||||
);
|
|
||||||
const el = unref(appendEl);
|
|
||||||
if (!el) return watermarkEl;
|
|
||||||
const { clientHeight: height, clientWidth: width } = el;
|
|
||||||
updateWatermark({ str, attrs, width, height });
|
|
||||||
el.appendChild(div);
|
|
||||||
return watermarkEl;
|
|
||||||
};
|
|
||||||
|
|
||||||
const debounceUpdateResize = debounce(
|
|
||||||
() => {
|
|
||||||
const el = unref(appendEl);
|
|
||||||
if (!el) return;
|
|
||||||
const { clientHeight: height, clientWidth: width } = el;
|
|
||||||
updateWatermark({ width, height });
|
|
||||||
},
|
|
||||||
30,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
/** 对外提供的设置水印方法 */
|
|
||||||
const setWatermark = (str: string, attrs: Attrs = {}) => {
|
|
||||||
createWatermark(str, attrs);
|
|
||||||
window.addEventListener('resize', debounceUpdateResize);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 清除水印 */
|
|
||||||
const clearWatermark = () => {
|
|
||||||
let domId: HTMLElement | null | undefined = unref(watermarkEl);
|
|
||||||
if (!domId && opts.id) {
|
|
||||||
domId = document.getElementById(opts.id);
|
|
||||||
}
|
|
||||||
watermarkEl.value = undefined;
|
|
||||||
const el = unref(appendEl);
|
|
||||||
if (!el) return;
|
|
||||||
domId && el.removeChild(domId);
|
|
||||||
window.removeEventListener('resize', debounceUpdateResize);
|
|
||||||
};
|
|
||||||
|
|
||||||
return { setWatermark, clearWatermark };
|
|
||||||
}
|
|
@ -106,9 +106,6 @@ const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wra
|
|||||||
<SettingItem v-if="isDev" key="8" :label="$t('theme.watermark.visible')">
|
<SettingItem v-if="isDev" key="8" :label="$t('theme.watermark.visible')">
|
||||||
<NSwitch v-model:value="themeStore.watermark.visible" />
|
<NSwitch v-model:value="themeStore.watermark.visible" />
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
<SettingItem v-if="false" key="8-1" :label="$t('theme.watermark.text')">
|
|
||||||
<NInput v-model:value="themeStore.watermark.text" size="small" :step="1" class="max-w-180px" />
|
|
||||||
</SettingItem>
|
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -10,14 +10,11 @@ import { $t } from '@/locales';
|
|||||||
import { roleTypeRecord } from '@/constants/business';
|
import { roleTypeRecord } from '@/constants/business';
|
||||||
import { useRouteStore } from '../route';
|
import { useRouteStore } from '../route';
|
||||||
import { useTabStore } from '../tab';
|
import { useTabStore } from '../tab';
|
||||||
import { useThemeStore } from '../theme';
|
|
||||||
import { clearAuthStorage, getToken } from './shared';
|
import { clearAuthStorage, getToken } from './shared';
|
||||||
|
|
||||||
export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
||||||
const appTitle = import.meta.env.VITE_APP_TITLE || 'Snail Job';
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const routeStore = useRouteStore();
|
const routeStore = useRouteStore();
|
||||||
const themeStore = useThemeStore();
|
|
||||||
const tabStore = useTabStore();
|
const tabStore = useTabStore();
|
||||||
const { toLogin, redirectFromLogin } = useRouterPush(false);
|
const { toLogin, redirectFromLogin } = useRouterPush(false);
|
||||||
const { loading: loginLoading, startLoading, endLoading } = useLoading();
|
const { loading: loginLoading, startLoading, endLoading } = useLoading();
|
||||||
@ -58,8 +55,6 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
|||||||
|
|
||||||
authStore.$reset();
|
authStore.$reset();
|
||||||
|
|
||||||
themeStore.setWatermarkText(appTitle);
|
|
||||||
|
|
||||||
if (!route.meta.constant) {
|
if (!route.meta.constant) {
|
||||||
await toLogin();
|
await toLogin();
|
||||||
}
|
}
|
||||||
@ -143,7 +138,6 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
|||||||
localStg.set('userInfo', info);
|
localStg.set('userInfo', info);
|
||||||
localStg.set('userInfo', info);
|
localStg.set('userInfo', info);
|
||||||
Object.assign(userInfo, info);
|
Object.assign(userInfo, info);
|
||||||
themeStore.setWatermarkText(`${userInfo.userName}@${appTitle}`);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import { useEventListener, usePreferredColorScheme } from '@vueuse/core';
|
|||||||
import { getPaletteColorByNumber } from '@sa/color';
|
import { getPaletteColorByNumber } from '@sa/color';
|
||||||
import { SetupStoreId } from '@/enum';
|
import { SetupStoreId } from '@/enum';
|
||||||
import { localStg } from '@/utils/storage';
|
import { localStg } from '@/utils/storage';
|
||||||
import { useWatermark } from '@/hooks/common/watermark';
|
|
||||||
import {
|
import {
|
||||||
addThemeVarsToGlobal,
|
addThemeVarsToGlobal,
|
||||||
createThemeToken,
|
createThemeToken,
|
||||||
@ -55,24 +54,13 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
*/
|
*/
|
||||||
const settingsJson = computed(() => JSON.stringify(settings.value));
|
const settingsJson = computed(() => JSON.stringify(settings.value));
|
||||||
|
|
||||||
/** Watermarks */
|
/**
|
||||||
const { setWatermark, clearWatermark } = useWatermark({ id: 'global_watermark_id' });
|
* Set theme watermark
|
||||||
|
*
|
||||||
/** 开启水印 */
|
* @param visible
|
||||||
|
*/
|
||||||
function toggleWatermark(visible: boolean = false) {
|
function toggleWatermark(visible: boolean = false) {
|
||||||
visible ? setWatermark(settings.value?.watermark.text) : clearWatermark();
|
settings.value.watermark.visible = visible;
|
||||||
}
|
|
||||||
|
|
||||||
/** 修改水印文案 */
|
|
||||||
function setWatermarkText(text: string) {
|
|
||||||
if (!text) {
|
|
||||||
clearWatermark();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (settings.value.watermark && settings.value.watermark?.visible) {
|
|
||||||
settings.value.watermark.text = text;
|
|
||||||
setWatermark(settings.value.watermark.text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Reset store */
|
/** Reset store */
|
||||||
@ -204,15 +192,6 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
|
||||||
settings.value?.watermark,
|
|
||||||
val => {
|
|
||||||
toggleWatermark(val?.visible);
|
|
||||||
setWatermarkText(val?.text);
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/** On scope dispose */
|
/** On scope dispose */
|
||||||
@ -233,7 +212,6 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
|||||||
updateThemeColors,
|
updateThemeColors,
|
||||||
setThemeLayout,
|
setThemeLayout,
|
||||||
setLayoutReverseHorizontalMix,
|
setLayoutReverseHorizontalMix,
|
||||||
setWatermarkText,
|
|
||||||
toggleWatermark
|
toggleWatermark
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -71,8 +71,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watermark: {
|
watermark: {
|
||||||
visible: true,
|
visible: true
|
||||||
text: import.meta.env.VITE_APP_TITLE || 'Snail Job'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
2
src/typings/app.d.ts
vendored
2
src/typings/app.d.ts
vendored
@ -104,8 +104,6 @@ declare namespace App {
|
|||||||
watermark: {
|
watermark: {
|
||||||
/** Whether to show the watermark */
|
/** Whether to show the watermark */
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
/** WatermarkText */
|
|
||||||
text: string;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user