Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/layout/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<script>
import { Navbar, Sidebar, AppMain } from './components'
import ResizeMixin from './mixin/ResizeHandler'
import { setWatermark, removeWatermark } from '@/utils/watermark'

export default {
name: 'Layout',
Expand All @@ -33,6 +34,12 @@ export default {
fixedHeader() {
return this.$store.state.settings.fixedHeader
},
showWatermark() {
return this.$store.getters.showWatermark
},
userName() {
return this.$store.getters.name
},
classObj() {
return {
hideSidebar: !this.sidebar.opened,
Expand All @@ -42,6 +49,36 @@ export default {
}
}
},
watch: {
showWatermark: {
handler(val) {
if (val && this.userName) {
setWatermark({ text: this.userName })
} else {
removeWatermark()
}
},
immediate: true
},
userName: {
handler(val) {
if (this.showWatermark && val) {
setWatermark({ text: val })
} else {
removeWatermark()
}
},
immediate: true
}
},
created() {
if (this.showWatermark && this.userName) {
setWatermark({ text: this.userName })
}
},
beforeDestroy() {
removeWatermark()
},
methods: {
handleClickOutside() {
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
Expand Down
8 changes: 7 additions & 1 deletion src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@ module.exports = {
* @type {boolean} true | false
* @description Whether show the logo in sidebar
*/
sidebarLogo: false
sidebarLogo: false,

/**
* @type {boolean} true | false
* @description Whether show watermark
*/
showWatermark: true
}
3 changes: 2 additions & 1 deletion src/store/getters.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const getters = {
device: state => state.app.device,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name
name: state => state.user.name,
showWatermark: state => state.settings.showWatermark
}
export default getters
5 changes: 3 additions & 2 deletions src/store/modules/settings.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import defaultSettings from '@/settings'

const { showSettings, fixedHeader, sidebarLogo } = defaultSettings
const { showSettings, fixedHeader, sidebarLogo, showWatermark } = defaultSettings

const state = {
showSettings: showSettings,
fixedHeader: fixedHeader,
sidebarLogo: sidebarLogo
sidebarLogo: sidebarLogo,
showWatermark: showWatermark
}

const mutations = {
Expand Down
181 changes: 181 additions & 0 deletions src/utils/watermark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
let watermarkDOM = null
let observer = null
let isObserving = false

const watermarkId = 'global-watermark'
const watermarkClass = 'global-watermark-class'

function createWatermark({ text, width = 300, height = 200, angle = -30, fontSize = 16, color = 'rgba(0, 0, 0, 0.15)' }) {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = width
canvas.height = height
ctx.rotate((angle * Math.PI) / 180)
ctx.font = `${fontSize}px Arial`
ctx.fillStyle = color
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText(text, width / 2, height / 2)
return canvas.toDataURL()
}

function createWatermarkDOM(watermarkUrl, zIndex = 9999) {
const div = document.createElement('div')
div.id = watermarkId
div.className = watermarkClass
div.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: ${zIndex};
background-image: url(${watermarkUrl});
background-repeat: repeat;
`
return div
}

function observeWatermark() {
if (observer) {
observer.disconnect()
}

observer = new MutationObserver((mutations) => {
let shouldRecreate = false

for (const mutation of mutations) {
if (mutation.type === 'childList') {
for (const node of mutation.removedNodes) {
if (node.id === watermarkId || (node.classList && node.classList.contains(watermarkClass))) {
shouldRecreate = true
break
}
}
if (shouldRecreate) break
}

if (mutation.type === 'attributes') {
const target = mutation.target
if (target.id === watermarkId || (target.classList && target.classList.contains(watermarkClass))) {
if (mutation.attributeName === 'style' || mutation.attributeName === 'class' || mutation.attributeName === 'id') {
shouldRecreate = true
break
}
}
}
}

if (shouldRecreate && isObserving) {
recreateWatermark()
}
})

observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['style', 'class', 'id']
})
}

function recreateWatermark() {
if (!watermarkDOM || !isObserving) return

const currentText = watermarkDOM.dataset.text || ''
const currentWidth = parseInt(watermarkDOM.dataset.width) || 300
const currentHeight = parseInt(watermarkDOM.dataset.height) || 200
const currentAngle = parseInt(watermarkDOM.dataset.angle) || -30
const currentFontSize = parseInt(watermarkDOM.dataset.fontSize) || 16
const currentColor = watermarkDOM.dataset.color || 'rgba(0, 0, 0, 0.15)'
const currentZIndex = parseInt(watermarkDOM.dataset.zIndex) || 9999

const oldWatermark = document.getElementById(watermarkId)
if (oldWatermark) {
oldWatermark.remove()
}

const watermarkUrl = createWatermark({
text: currentText,
width: currentWidth,
height: currentHeight,
angle: currentAngle,
fontSize: currentFontSize,
color: currentColor
})

watermarkDOM = createWatermarkDOM(watermarkUrl, currentZIndex)
watermarkDOM.dataset.text = currentText
watermarkDOM.dataset.width = currentWidth
watermarkDOM.dataset.height = currentHeight
watermarkDOM.dataset.angle = currentAngle
watermarkDOM.dataset.fontSize = currentFontSize
watermarkDOM.dataset.color = currentColor
watermarkDOM.dataset.zIndex = currentZIndex

document.body.appendChild(watermarkDOM)
}

export function setWatermark(options = {}) {
const {
text = '',
width = 300,
height = 200,
angle = -30,
fontSize = 16,
color = 'rgba(0, 0, 0, 0.15)',
zIndex = 9999
} = options

if (!text) {
removeWatermark()
return
}

const oldWatermark = document.getElementById(watermarkId)
if (oldWatermark) {
if (oldWatermark.dataset.text === text) {
return
}
oldWatermark.remove()
}

const watermarkUrl = createWatermark({ text, width, height, angle, fontSize, color })
watermarkDOM = createWatermarkDOM(watermarkUrl, zIndex)

watermarkDOM.dataset.text = text
watermarkDOM.dataset.width = width
watermarkDOM.dataset.height = height
watermarkDOM.dataset.angle = angle
watermarkDOM.dataset.fontSize = fontSize
watermarkDOM.dataset.color = color
watermarkDOM.dataset.zIndex = zIndex

document.body.appendChild(watermarkDOM)

if (!isObserving) {
isObserving = true
observeWatermark()
}
}

export function removeWatermark() {
isObserving = false

if (observer) {
observer.disconnect()
observer = null
}

const watermark = document.getElementById(watermarkId)
if (watermark) {
watermark.remove()
}

watermarkDOM = null
}

export function updateWatermark(options = {}) {
setWatermark(options)
}