| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
- import { getWatermarkConfig } from '@/api/config'
- import { WHITE_LIST } from '@/config/whitelist.config.js'
- import { useAuthStore, useUserStore } from '@/store'
- /**
- * 水印配置
- */
- const defaultConfig = {
- enable: false,
- content: '系统水印',
- contentType: 'text', // text: 固定文本, dict: 字典配置
- opacity: 0.1,
- fontSize: 16,
- fontColor: '#cccccc',
- rotate: -20,
- gapX: 200,
- gapY: 200,
- offsetX: 0,
- offsetY: 0,
- zIndex: 1000,
- showTimestamp: false,
- timestampFormat: 'yyyy-MM-dd HH:mm:ss',
- }
- /**
- * 水印内容解析器映射
- * key: 字典code, value: 解析函数,返回显示文本
- */
- const contentParsers = {
- // 姓名+手机号
- name_phone: (userStore) => {
- const name = userStore.realName || userStore.username || ''
- const phone = userStore.phone || ''
- return phone ? `${name} ${phone}` : name
- },
- // 姓名
- name: (userStore) => {
- return userStore.realName || userStore.username || ''
- },
- // 用户名
- username: (userStore) => {
- return userStore.username || ''
- },
- // 手机号
- phone: (userStore) => {
- return userStore.phone || ''
- },
- // 邮箱
- email: (userStore) => {
- return userStore.email || ''
- },
- // 用户ID
- user_id: (userStore) => {
- return String(userStore.userId || '')
- },
- }
- /**
- * 格式化时间戳
- */
- function formatDate(date, format) {
- const year = date.getFullYear()
- const month = String(date.getMonth() + 1).padStart(2, '0')
- const day = String(date.getDate()).padStart(2, '0')
- const hours = String(date.getHours()).padStart(2, '0')
- const minutes = String(date.getMinutes()).padStart(2, '0')
- const seconds = String(date.getSeconds()).padStart(2, '0')
- return format
- .replace('yyyy', year)
- .replace('MM', month)
- .replace('dd', day)
- .replace('HH', hours)
- .replace('mm', minutes)
- .replace('ss', seconds)
- }
- /**
- * 创建水印
- */
- function createWatermarkCanvas(config) {
- const canvas = document.createElement('canvas')
- const ctx = canvas.getContext('2d')
- // 计算文本尺寸
- const font = `${config.fontSize}px Arial`
- ctx.font = font
- const textMetrics = ctx.measureText(config.content)
- const textWidth = textMetrics.width
- const textHeight = config.fontSize
- // 计算旋转后的画布尺寸
- const angle = (config.rotate * Math.PI) / 180
- const sin = Math.abs(Math.sin(angle))
- const cos = Math.abs(Math.cos(angle))
- // 画布尺寸 = 文本尺寸 + 间距
- const canvasWidth = textWidth * cos + textHeight * sin + config.gapX
- const canvasHeight = textWidth * sin + textHeight * cos + config.gapY
- canvas.width = canvasWidth
- canvas.height = canvasHeight
- // 绘制水印文本
- ctx.font = font
- ctx.fillStyle = config.fontColor
- ctx.globalAlpha = config.opacity
- ctx.textAlign = 'center'
- ctx.textBaseline = 'middle'
- // 保存上下文状态
- ctx.save()
- // 移动到画布中心并旋转
- const centerX = (textWidth * cos + textHeight * sin) / 2 + config.offsetX
- const centerY = (textWidth * sin + textHeight * cos) / 2 + config.offsetY
- ctx.translate(centerX, centerY)
- ctx.rotate(angle)
- // 绘制文本
- let displayContent = config.content
- if (config.showTimestamp) {
- const timestamp = formatDate(new Date(), config.timestampFormat)
- displayContent += `\n${timestamp}`
- }
- const lines = displayContent.split('\n')
- lines.forEach((line, index) => {
- const yOffset = (index - (lines.length - 1) / 2) * config.fontSize * 1.2
- ctx.fillText(line, 0, yOffset)
- })
- ctx.restore()
- return canvas.toDataURL('image/png')
- }
- /**
- * 获取水印显示内容
- */
- function getWatermarkDisplayContent(config, userStore) {
- // 如果内容类型是字典配置
- if (config.contentType === 'dict' || contentParsers[config.content]) {
- const parser = contentParsers[config.content]
- if (parser && userStore.userInfo) {
- return parser(userStore)
- }
- // 如果没有对应的解析器或用户未登录,返回空字符串
- return ''
- }
- // 固定文本
- return config.content || ''
- }
- /**
- * 水印 composable
- */
- export function useWatermark() {
- const userStore = useUserStore()
- const authStore = useAuthStore()
- const watermarkConfig = ref({ ...defaultConfig })
- const watermarkUrl = ref('')
- let refreshTimer = null
- // 计算实际显示的水印内容
- const displayContent = computed(() => {
- return getWatermarkDisplayContent(watermarkConfig.value, userStore)
- })
- /**
- * 加载水印配置
- */
- const loadWatermarkConfig = async () => {
- // 检查是否需要加载水印配置
- // 1. 必须有 token
- const hasToken = authStore.accessToken
- if (!hasToken) {
- return
- }
- // 2. 当前路由不在白名单中
- const currentPath = window.location.pathname
- if (WHITE_LIST.some(path => currentPath.startsWith(path))) {
- return
- }
- try {
- const res = await getWatermarkConfig()
- if (res.code === 200 && res.data) {
- watermarkConfig.value = { ...defaultConfig, ...res.data }
- updateWatermark()
- }
- }
- catch (error) {
- console.error('加载水印配置失败:', error)
- }
- }
- /**
- * 更新水印
- */
- const updateWatermark = () => {
- if (!watermarkConfig.value.enable) {
- watermarkUrl.value = ''
- return
- }
- // 如果没有显示内容,不显示水印
- const content = displayContent.value
- if (!content) {
- watermarkUrl.value = ''
- return
- }
- // 创建带实际显示内容的水印配置
- const configWithContent = {
- ...watermarkConfig.value,
- content,
- }
- const dataUrl = createWatermarkCanvas(configWithContent)
- watermarkUrl.value = dataUrl
- }
- /**
- * 启动定时刷新(用于更新时间戳)
- */
- const startRefreshTimer = () => {
- stopRefreshTimer()
- if (watermarkConfig.value.enable && watermarkConfig.value.showTimestamp) {
- refreshTimer = setInterval(() => {
- updateWatermark()
- }, 1000) // 每秒刷新一次
- }
- }
- /**
- * 停止定时刷新
- */
- const stopRefreshTimer = () => {
- if (refreshTimer) {
- clearInterval(refreshTimer)
- refreshTimer = null
- }
- }
- /**
- * 获取水印样式
- */
- const getWatermarkStyle = () => {
- if (!watermarkConfig.value.enable || !watermarkUrl.value) {
- return {}
- }
- return {
- position: 'fixed',
- top: 0,
- left: 0,
- width: '100%',
- height: '100%',
- pointerEvents: 'none',
- zIndex: watermarkConfig.value.zIndex,
- backgroundImage: `url(${watermarkUrl.value})`,
- backgroundRepeat: 'repeat',
- }
- }
- // 监听配置变化
- watch(
- () => watermarkConfig.value,
- () => {
- updateWatermark()
- startRefreshTimer()
- },
- { deep: true },
- )
- // 监听用户登录状态变化,更新水印内容
- watch(
- () => userStore.userInfo,
- () => {
- if (watermarkConfig.value.enable) {
- updateWatermark()
- }
- },
- { deep: true },
- )
- // 组件挂载时加载配置
- onMounted(() => {
- loadWatermarkConfig()
- })
- // 组件卸载时清理定时器
- onUnmounted(() => {
- stopRefreshTimer()
- })
- return {
- watermarkConfig,
- watermarkUrl,
- displayContent,
- loadWatermarkConfig,
- updateWatermark,
- getWatermarkStyle,
- // 注册自定义内容解析器
- registerContentParser: (code, parser) => {
- contentParsers[code] = parser
- },
- }
- }
- export default useWatermark
|