增加用户名水印,避免信息泄露
This commit is contained in:
parent
9ac36d7a41
commit
4a7376c318
@ -589,3 +589,30 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover{
|
|||||||
font-size: 14px;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -21,6 +21,58 @@
|
|||||||
} else if (avatarImg) {
|
} else if (avatarImg) {
|
||||||
avatarImg.src = './assets/user-avatar.svg';
|
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) => {
|
Router.onBeforeEach(async (path) => {
|
||||||
@ -84,6 +136,7 @@
|
|||||||
API.logout()
|
API.logout()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
currentUser = null;
|
currentUser = null;
|
||||||
|
removeWatermark();
|
||||||
updateUserDisplay(null);
|
updateUserDisplay(null);
|
||||||
userDropdown.style.display = 'none';
|
userDropdown.style.display = 'none';
|
||||||
location.hash = '#/login';
|
location.hash = '#/login';
|
||||||
@ -130,4 +183,14 @@
|
|||||||
|
|
||||||
// 暴露更新用户显示的函数,供设置页面使用
|
// 暴露更新用户显示的函数,供设置页面使用
|
||||||
window.updateUserDisplay = updateUserDisplay;
|
window.updateUserDisplay = updateUserDisplay;
|
||||||
|
|
||||||
|
// 暴露水印控制函数
|
||||||
|
window.toggleWatermark = function(enabled) {
|
||||||
|
localStorage.setItem('watermarkEnabled', enabled ? 'true' : 'false');
|
||||||
|
if (enabled && currentUser && currentUser.username) {
|
||||||
|
createWatermark(currentUser.username);
|
||||||
|
} else {
|
||||||
|
removeWatermark();
|
||||||
|
}
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
@ -64,6 +64,19 @@ Router.register('/settings', async () => {
|
|||||||
<div class="actions"><button id="change-btn" class="btn">修改密码</button></div>
|
<div class="actions"><button id="change-btn" class="btn">修改密码</button></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom:24px;padding-bottom:24px;border-bottom:1px solid var(--border)">
|
||||||
|
<div style="font-weight:500;margin-bottom:12px">水印设置</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:12px;padding:12px;background:var(--surface);border:1px solid var(--border);border-radius:8px">
|
||||||
|
<label style="flex:1;cursor:pointer;display:flex;align-items:center;gap:8px">
|
||||||
|
<input type="checkbox" id="watermark-toggle" ${localStorage.getItem('watermarkEnabled') !== 'false' ? 'checked' : ''} style="width:18px;height:18px;cursor:pointer" />
|
||||||
|
<span style="font-size:14px">启用页面水印(显示用户名和时间戳)</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top:8px;font-size:12px;color:var(--text-2)">
|
||||||
|
水印用于防止数据泄露和责任追溯,建议保持启用状态
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style="font-weight:500;margin-bottom:12px">超级管理员工具</div>
|
<div style="font-weight:500;margin-bottom:12px">超级管理员工具</div>
|
||||||
<div style="margin-top:12px"></div>
|
<div style="margin-top:12px"></div>
|
||||||
<div class="grid cols-2" style="margin-top:8px">
|
<div class="grid cols-2" style="margin-top:8px">
|
||||||
@ -288,6 +301,16 @@ Router.register('/settings', async () => {
|
|||||||
change.disabled = false;
|
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 => {
|
document.querySelectorAll('button[data-clear]')?.forEach(btn => {
|
||||||
btn.addEventListener('click', async () => {
|
btn.addEventListener('click', async () => {
|
||||||
const mod = btn.getAttribute('data-clear');
|
const mod = btn.getAttribute('data-clear');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user