refactor(sj_1.2.0-beta1): 重构水印
This commit is contained in:
parent
f804e18e93
commit
b5bd40c25e
@ -30,6 +30,7 @@ const ContextHolder = defineComponent({
|
||||
<NMessageProvider>
|
||||
<ContextHolder />
|
||||
<slot></slot>
|
||||
<AppWatermark />
|
||||
</NMessageProvider>
|
||||
</NNotificationProvider>
|
||||
</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')">
|
||||
<NSwitch v-model:value="themeStore.watermark.visible" />
|
||||
</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>
|
||||
</template>
|
||||
|
||||
|
@ -10,14 +10,11 @@ import { $t } from '@/locales';
|
||||
import { roleTypeRecord } from '@/constants/business';
|
||||
import { useRouteStore } from '../route';
|
||||
import { useTabStore } from '../tab';
|
||||
import { useThemeStore } from '../theme';
|
||||
import { clearAuthStorage, getToken } from './shared';
|
||||
|
||||
export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
||||
const appTitle = import.meta.env.VITE_APP_TITLE || 'Snail Job';
|
||||
const route = useRoute();
|
||||
const routeStore = useRouteStore();
|
||||
const themeStore = useThemeStore();
|
||||
const tabStore = useTabStore();
|
||||
const { toLogin, redirectFromLogin } = useRouterPush(false);
|
||||
const { loading: loginLoading, startLoading, endLoading } = useLoading();
|
||||
@ -58,8 +55,6 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
||||
|
||||
authStore.$reset();
|
||||
|
||||
themeStore.setWatermarkText(appTitle);
|
||||
|
||||
if (!route.meta.constant) {
|
||||
await toLogin();
|
||||
}
|
||||
@ -143,7 +138,6 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
||||
localStg.set('userInfo', info);
|
||||
localStg.set('userInfo', info);
|
||||
Object.assign(userInfo, info);
|
||||
themeStore.setWatermarkText(`${userInfo.userName}@${appTitle}`);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import { useEventListener, usePreferredColorScheme } from '@vueuse/core';
|
||||
import { getPaletteColorByNumber } from '@sa/color';
|
||||
import { SetupStoreId } from '@/enum';
|
||||
import { localStg } from '@/utils/storage';
|
||||
import { useWatermark } from '@/hooks/common/watermark';
|
||||
import {
|
||||
addThemeVarsToGlobal,
|
||||
createThemeToken,
|
||||
@ -55,24 +54,13 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
||||
*/
|
||||
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) {
|
||||
visible ? setWatermark(settings.value?.watermark.text) : clearWatermark();
|
||||
}
|
||||
|
||||
/** 修改水印文案 */
|
||||
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);
|
||||
}
|
||||
settings.value.watermark.visible = visible;
|
||||
}
|
||||
|
||||
/** Reset store */
|
||||
@ -204,15 +192,6 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
settings.value?.watermark,
|
||||
val => {
|
||||
toggleWatermark(val?.visible);
|
||||
setWatermarkText(val?.text);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
});
|
||||
|
||||
/** On scope dispose */
|
||||
@ -233,7 +212,6 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
|
||||
updateThemeColors,
|
||||
setThemeLayout,
|
||||
setLayoutReverseHorizontalMix,
|
||||
setWatermarkText,
|
||||
toggleWatermark
|
||||
};
|
||||
});
|
||||
|
@ -71,8 +71,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
|
||||
}
|
||||
},
|
||||
watermark: {
|
||||
visible: true,
|
||||
text: import.meta.env.VITE_APP_TITLE || 'Snail Job'
|
||||
visible: true
|
||||
}
|
||||
};
|
||||
|
||||
|
2
src/typings/app.d.ts
vendored
2
src/typings/app.d.ts
vendored
@ -104,8 +104,6 @@ declare namespace App {
|
||||
watermark: {
|
||||
/** Whether to show the watermark */
|
||||
visible: boolean;
|
||||
/** WatermarkText */
|
||||
text: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user