diff --git a/frontend/assets/styles.css b/frontend/assets/styles.css index 7167f27..cd2862a 100644 --- a/frontend/assets/styles.css +++ b/frontend/assets/styles.css @@ -589,3 +589,30 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover{ font-size: 14px; } } + +/* Watermark styles */ +.watermark { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 9999; + overflow: hidden; +} + +.watermark-text { + position: absolute; + font-size: 16px; + color: var(--text); + opacity: 0.08; + transform: rotate(-45deg); + white-space: nowrap; + user-select: none; + font-weight: 500; +} + +[data-theme="light"] .watermark-text { + opacity: 0.06; +} diff --git a/frontend/js/app.js b/frontend/js/app.js index a1aea0d..1e176bd 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -21,6 +21,58 @@ } else if (avatarImg) { avatarImg.src = './assets/user-avatar.svg'; } + + // 更新水印 + if (user && user.username) { + createWatermark(user.username); + } else { + removeWatermark(); + } + } + + // 创建水印 + function createWatermark(username) { + // 检查水印是否启用 + const watermarkEnabled = localStorage.getItem('watermarkEnabled'); + if (watermarkEnabled === 'false') { + return; + } + + removeWatermark(); + + const watermarkDiv = document.createElement('div'); + watermarkDiv.id = 'watermark-container'; + watermarkDiv.className = 'watermark'; + + const now = new Date(); + const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`; + const watermarkText = `${username} ${dateStr}`; + + // 计算水印平铺 + const spacing = 300; + const rows = Math.ceil(window.innerHeight / spacing) + 2; + const cols = Math.ceil(window.innerWidth / spacing) + 2; + + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + const span = document.createElement('span'); + span.className = 'watermark-text'; + span.textContent = watermarkText; + span.style.left = `${j * spacing - 100}px`; + span.style.top = `${i * spacing - 100}px`; + watermarkDiv.appendChild(span); + } + } + + document.body.appendChild(watermarkDiv); + } + + // 移除水印 + function removeWatermark() { + const existing = document.getElementById('watermark-container'); + if (existing) { + existing.remove(); + } } Router.onBeforeEach(async (path) => { @@ -84,6 +136,7 @@ API.logout() .then(() => { currentUser = null; + removeWatermark(); updateUserDisplay(null); userDropdown.style.display = 'none'; location.hash = '#/login'; @@ -130,4 +183,14 @@ // 暴露更新用户显示的函数,供设置页面使用 window.updateUserDisplay = updateUserDisplay; + + // 暴露水印控制函数 + window.toggleWatermark = function(enabled) { + localStorage.setItem('watermarkEnabled', enabled ? 'true' : 'false'); + if (enabled && currentUser && currentUser.username) { + createWatermark(currentUser.username); + } else { + removeWatermark(); + } + }; })(); \ No newline at end of file diff --git a/frontend/js/components/settings.js b/frontend/js/components/settings.js index 3f2c338..6457261 100644 --- a/frontend/js/components/settings.js +++ b/frontend/js/components/settings.js @@ -64,6 +64,19 @@ Router.register('/settings', async () => {
+
+
水印设置
+
+ +
+
+ 水印用于防止数据泄露和责任追溯,建议保持启用状态 +
+
+
超级管理员工具
@@ -288,6 +301,16 @@ Router.register('/settings', async () => { change.disabled = false; } }); + + // 水印开关 + const watermarkToggle = document.getElementById('watermark-toggle'); + watermarkToggle?.addEventListener('change', (e) => { + const enabled = e.target.checked; + if (window.toggleWatermark) { + window.toggleWatermark(enabled); + API.toast(enabled ? '水印已启用' : '水印已关闭'); + } + }); document.querySelectorAll('button[data-clear]')?.forEach(btn => { btn.addEventListener('click', async () => { const mod = btn.getAttribute('data-clear');