feat: prompt/confirm 替换为自定义弹窗
- shared/utils.js 新增 showConfirm()/showPrompt() 通用函数(inline CSS,无需额外样式) - admin/common.js 移除重复 showConfirm(),统一使用 utils.js - admin/posts.js: 3 处 confirm/prompt 替换(审核/退回/锁定) - admin/comments.js: 1 处 confirm 替换 - settings/index.html: 4 处 confirm + 1 处 prompt 替换 - studio/posts.html + drafts.html: 3 处 confirm 替换 - post-view.js + login.js + post-favorite.js: 3 处 confirm 替换
This commit is contained in:
@ -783,37 +783,37 @@
|
|||||||
showSettingsToast('请输入密码确认', true);
|
showSettingsToast('请输入密码确认', true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!confirm('确定要注销账号吗?\n\n注销后 7 天内重新登录可撤销注销,期满后将永久删除。')) {
|
showConfirm('注销账号', '确定要注销账号吗?\n\n注销后 7 天内重新登录可撤销注销,期满后将永久删除。').then(function(ok) {
|
||||||
return;
|
if (!ok) return;
|
||||||
}
|
deleteAccountBtn.disabled = true;
|
||||||
deleteAccountBtn.disabled = true;
|
deleteAccountBtn.textContent = '注销中...';
|
||||||
deleteAccountBtn.textContent = '注销中...';
|
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open('POST', '/api/settings/delete-account', true);
|
xhr.open('POST', '/api/settings/delete-account', true);
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
xhr.onreadystatechange = function () {
|
xhr.onreadystatechange = function () {
|
||||||
if (xhr.readyState !== 4) return;
|
if (xhr.readyState !== 4) return;
|
||||||
deleteAccountBtn.disabled = false;
|
deleteAccountBtn.disabled = false;
|
||||||
deleteAccountBtn.textContent = '确认注销';
|
deleteAccountBtn.textContent = '确认注销';
|
||||||
try {
|
try {
|
||||||
var res = JSON.parse(xhr.responseText);
|
var res = JSON.parse(xhr.responseText);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
showSettingsToast(res.message || '账号已注销');
|
showSettingsToast(res.message || '账号已注销');
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} else {
|
} else {
|
||||||
showSettingsToast(res.message || '操作失败', true);
|
showSettingsToast(res.message || '操作失败', true);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showSettingsToast('操作失败', true);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
};
|
||||||
showSettingsToast('操作失败', true);
|
xhr.send(JSON.stringify({
|
||||||
}
|
password: deletePwd.value,
|
||||||
};
|
reason: deleteReason ? deleteReason.value : ''
|
||||||
xhr.send(JSON.stringify({
|
}));
|
||||||
password: deletePwd.value,
|
});
|
||||||
reason: deleteReason ? deleteReason.value : ''
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -829,36 +829,38 @@
|
|||||||
btn.addEventListener('click', function () {
|
btn.addEventListener('click', function () {
|
||||||
var sid = btn.getAttribute('data-sid');
|
var sid = btn.getAttribute('data-sid');
|
||||||
if (!sid) return;
|
if (!sid) return;
|
||||||
if (!confirm('确定要踢出该设备吗?')) return;
|
showConfirm('确认', '确定要踢出该设备吗?').then(function(ok) {
|
||||||
btn.disabled = true;
|
if (!ok) return;
|
||||||
btn.textContent = '踢出中...';
|
btn.disabled = true;
|
||||||
|
btn.textContent = '踢出中...';
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open('DELETE', '/api/settings/sessions/' + encodeURIComponent(sid), true);
|
xhr.open('DELETE', '/api/settings/sessions/' + encodeURIComponent(sid), true);
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
xhr.onreadystatechange = function () {
|
xhr.onreadystatechange = function () {
|
||||||
if (xhr.readyState !== 4) return;
|
if (xhr.readyState !== 4) return;
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.textContent = '踢出';
|
btn.textContent = '踢出';
|
||||||
try {
|
try {
|
||||||
var res = JSON.parse(xhr.responseText);
|
var res = JSON.parse(xhr.responseText);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
showSettingsToast(res.message || '已踢出该设备');
|
showSettingsToast(res.message || '已踢出该设备');
|
||||||
var item = btn.closest('.session-item');
|
var item = btn.closest('.session-item');
|
||||||
if (item) {
|
if (item) {
|
||||||
item.style.transition = 'opacity .3s ease, transform .3s ease';
|
item.style.transition = 'opacity .3s ease, transform .3s ease';
|
||||||
item.style.opacity = '0';
|
item.style.opacity = '0';
|
||||||
item.style.transform = 'translateX(20px)';
|
item.style.transform = 'translateX(20px)';
|
||||||
setTimeout(function () { item.remove(); }, 300);
|
setTimeout(function () { item.remove(); }, 300);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showSettingsToast(res.message || '操作失败', true);
|
||||||
}
|
}
|
||||||
} else {
|
} catch (e) {
|
||||||
showSettingsToast(res.message || '操作失败', true);
|
showSettingsToast('操作失败', true);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
};
|
||||||
showSettingsToast('操作失败', true);
|
xhr.send();
|
||||||
}
|
});
|
||||||
};
|
|
||||||
xhr.send();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -870,38 +872,40 @@
|
|||||||
showSettingsToast('没有其他在线设备');
|
showSettingsToast('没有其他在线设备');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!confirm('确定要踢出所有其他设备(共 ' + otherCount + ' 台)吗?')) return;
|
showConfirm('确认', '确定要踢出所有其他设备(共 ' + otherCount + ' 台)吗?').then(function(ok) {
|
||||||
kickOthersBtn.disabled = true;
|
if (!ok) return;
|
||||||
kickOthersBtn.textContent = '处理中...';
|
kickOthersBtn.disabled = true;
|
||||||
|
kickOthersBtn.textContent = '处理中...';
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open('POST', '/api/settings/sessions/destroy-others', true);
|
xhr.open('POST', '/api/settings/sessions/destroy-others', true);
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
xhr.onreadystatechange = function () {
|
xhr.onreadystatechange = function () {
|
||||||
if (xhr.readyState !== 4) return;
|
if (xhr.readyState !== 4) return;
|
||||||
kickOthersBtn.disabled = false;
|
kickOthersBtn.disabled = false;
|
||||||
kickOthersBtn.textContent = '一键踢出其他设备';
|
kickOthersBtn.textContent = '一键踢出其他设备';
|
||||||
try {
|
try {
|
||||||
var res = JSON.parse(xhr.responseText);
|
var res = JSON.parse(xhr.responseText);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
showSettingsToast(res.message || '已踢出所有其他设备');
|
showSettingsToast(res.message || '已踢出所有其他设备');
|
||||||
var others = document.querySelectorAll('.session-item:not(.session-current)');
|
var others = document.querySelectorAll('.session-item:not(.session-current)');
|
||||||
others.forEach(function (item, i) {
|
others.forEach(function (item, i) {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
item.style.transition = 'opacity .3s ease, transform .3s ease';
|
item.style.transition = 'opacity .3s ease, transform .3s ease';
|
||||||
item.style.opacity = '0';
|
item.style.opacity = '0';
|
||||||
item.style.transform = 'translateX(20px)';
|
item.style.transform = 'translateX(20px)';
|
||||||
setTimeout(function () { item.remove(); }, 300);
|
setTimeout(function () { item.remove(); }, 300);
|
||||||
}, i * 100);
|
}, i * 100);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
showSettingsToast(res.message || '操作失败', true);
|
showSettingsToast(res.message || '操作失败', true);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showSettingsToast('操作失败', true);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
};
|
||||||
showSettingsToast('操作失败', true);
|
xhr.send();
|
||||||
}
|
});
|
||||||
};
|
|
||||||
xhr.send();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1056,19 +1060,21 @@
|
|||||||
listEl.querySelectorAll('.fav-folder-del').forEach(function (btn) {
|
listEl.querySelectorAll('.fav-folder-del').forEach(function (btn) {
|
||||||
btn.addEventListener('click', function () {
|
btn.addEventListener('click', function () {
|
||||||
var id = btn.getAttribute('data-id');
|
var id = btn.getAttribute('data-id');
|
||||||
if (!confirm('确定要删除此收藏夹?其中的收藏内容也将被清除。')) return;
|
showConfirm('确认', '确定要删除此收藏夹?其中的收藏内容也将被清除。').then(function(ok) {
|
||||||
api('DELETE', '/api/folders/' + id)
|
if (!ok) return;
|
||||||
.then(function (r) {
|
api('DELETE', '/api/folders/' + id)
|
||||||
if (r.success) {
|
.then(function (r) {
|
||||||
showSettingsToast('收藏夹已删除');
|
if (r.success) {
|
||||||
loadFolders();
|
showSettingsToast('收藏夹已删除');
|
||||||
} else {
|
loadFolders();
|
||||||
showSettingsToast(r.message || '删除失败', true);
|
} else {
|
||||||
}
|
showSettingsToast(r.message || '删除失败', true);
|
||||||
})
|
}
|
||||||
.catch(function () {
|
})
|
||||||
showSettingsToast('删除失败', true);
|
.catch(function () {
|
||||||
});
|
showSettingsToast('删除失败', true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -1079,24 +1085,25 @@
|
|||||||
|
|
||||||
if (createBtn) {
|
if (createBtn) {
|
||||||
createBtn.addEventListener('click', function () {
|
createBtn.addEventListener('click', function () {
|
||||||
var name = prompt('请输入收藏夹名称(最多50个字符):');
|
showPrompt('请输入收藏夹名称(最多50个字符):').then(function(name) {
|
||||||
if (!name || !name.trim()) return;
|
if (!name || !name.trim()) return;
|
||||||
if (name.trim().length > 50) {
|
if (name.trim().length > 50) {
|
||||||
showSettingsToast('收藏夹名称不能超过 50 个字符', true);
|
showSettingsToast('收藏夹名称不能超过 50 个字符', true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
api('POST', '/api/folders', { name: name.trim(), description: '', is_public: false })
|
api('POST', '/api/folders', { name: name.trim(), description: '', is_public: false })
|
||||||
.then(function (r) {
|
.then(function (r) {
|
||||||
if (r.success) {
|
if (r.success) {
|
||||||
showSettingsToast('收藏夹已创建');
|
showSettingsToast('收藏夹已创建');
|
||||||
loadFolders();
|
loadFolders();
|
||||||
} else {
|
} else {
|
||||||
showSettingsToast(r.message || '创建失败', true);
|
showSettingsToast(r.message || '创建失败', true);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
showSettingsToast('创建失败', true);
|
showSettingsToast('创建失败', true);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -59,42 +59,44 @@
|
|||||||
|
|
||||||
<script nonce="{{.CSPNonce}}">
|
<script nonce="{{.CSPNonce}}">
|
||||||
function submitPost(id) {
|
function submitPost(id) {
|
||||||
if (!confirm('确定要提交这篇草稿进行审核吗?')) return;
|
showConfirm('确认', '确定要提交这篇草稿进行审核吗?').then(function(ok) {
|
||||||
|
if (!ok) return;
|
||||||
fetch('/api/studio/posts/' + id + '/submit', {
|
fetch('/api/studio/posts/' + id + '/submit', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-CSRF-Token': getCSRFToken()
|
'X-CSRF-Token': getCSRFToken()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
location.reload();
|
location.reload();
|
||||||
} else {
|
} else {
|
||||||
alert(data.message || '提交失败');
|
alert(data.message || '提交失败');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function deletePost(id) {
|
function deletePost(id) {
|
||||||
if (!confirm('确定要删除这篇草稿吗?此操作不可恢复。')) return;
|
showConfirm('确认', '确定要删除这篇草稿吗?此操作不可恢复。').then(function(ok) {
|
||||||
|
if (!ok) return;
|
||||||
fetch('/api/studio/posts/' + id, {
|
fetch('/api/studio/posts/' + id, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-CSRF-Token': getCSRFToken()
|
'X-CSRF-Token': getCSRFToken()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
location.reload();
|
location.reload();
|
||||||
} else {
|
} else {
|
||||||
alert(data.message || '删除失败');
|
alert(data.message || '删除失败');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -80,22 +80,23 @@
|
|||||||
|
|
||||||
<script nonce="{{.CSPNonce}}">
|
<script nonce="{{.CSPNonce}}">
|
||||||
function deletePost(id) {
|
function deletePost(id) {
|
||||||
if (!confirm('确定要删除这篇文章吗?')) return;
|
showConfirm('确认', '确定要删除这篇文章吗?').then(function(ok) {
|
||||||
|
if (!ok) return;
|
||||||
fetch('/api/studio/posts/' + id, {
|
fetch('/api/studio/posts/' + id, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-CSRF-Token': getCSRFToken()
|
'X-CSRF-Token': getCSRFToken()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
location.reload();
|
location.reload();
|
||||||
} else {
|
} else {
|
||||||
alert(data.message || '删除失败');
|
alert(data.message || '删除失败');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -95,9 +95,9 @@
|
|||||||
loginInProgress = false;
|
loginInProgress = false;
|
||||||
submitBtn.disabled = false;
|
submitBtn.disabled = false;
|
||||||
submitBtn.textContent = '登录';
|
submitBtn.textContent = '登录';
|
||||||
if (confirm(resp.message + '\n\n确认登录将撤销注销并恢复账号。')) {
|
showConfirm('确认', resp.message + '\n\n确认登录将撤销注销并恢复账号。').then(function(ok) {
|
||||||
doConfirmRestore(email, password);
|
if (ok) doConfirmRestore(email, password);
|
||||||
}
|
});
|
||||||
} else if (xhr.status === 200 && resp && resp.success) {
|
} else if (xhr.status === 200 && resp && resp.success) {
|
||||||
window.MetaLab.toast(toast, '登录成功', false);
|
window.MetaLab.toast(toast, '登录成功', false);
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
|
|||||||
@ -119,21 +119,23 @@
|
|||||||
|
|
||||||
function removeFromFolder(folderId) {
|
function removeFromFolder(folderId) {
|
||||||
if (loading) return;
|
if (loading) return;
|
||||||
if (!confirm('确定取消收藏吗?')) return;
|
showConfirm('确认', '确定取消收藏吗?').then(function(ok) {
|
||||||
loading = true;
|
if (!ok) return;
|
||||||
api('DELETE', '/api/folders/' + folderId + '/posts/' + postId)
|
loading = true;
|
||||||
.then(function(d) {
|
api('DELETE', '/api/folders/' + folderId + '/posts/' + postId)
|
||||||
loading = false;
|
.then(function(d) {
|
||||||
if (d.success) {
|
loading = false;
|
||||||
currentFolderId = null;
|
if (d.success) {
|
||||||
updateBtn(false);
|
currentFolderId = null;
|
||||||
if (favCountEl) favCountEl.textContent = Math.max(0, (parseInt(favCountEl.textContent) || 0) - 1);
|
updateBtn(false);
|
||||||
hideModal();
|
if (favCountEl) favCountEl.textContent = Math.max(0, (parseInt(favCountEl.textContent) || 0) - 1);
|
||||||
} else {
|
hideModal();
|
||||||
|
} else {
|
||||||
alert(d.message || '操作失败');
|
alert(d.message || '操作失败');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function() { loading = false; });
|
.catch(function() { loading = false; });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndAdd(name) {
|
function createAndAdd(name) {
|
||||||
|
|||||||
@ -29,20 +29,22 @@
|
|||||||
var submitBtn = document.getElementById('submitAuditBtn');
|
var submitBtn = document.getElementById('submitAuditBtn');
|
||||||
if (submitBtn) {
|
if (submitBtn) {
|
||||||
submitBtn.addEventListener('click', function() {
|
submitBtn.addEventListener('click', function() {
|
||||||
if (!confirm('确认提交审核?审核通过后将公开发布。')) return;
|
showConfirm('确认', '确认提交审核?审核通过后将公开发布。').then(function(ok) {
|
||||||
fetch('/api/posts/' + submitBtn.dataset.id + '/submit', {
|
if (!ok) return;
|
||||||
method: 'POST',
|
fetch('/api/posts/' + submitBtn.dataset.id + '/submit', {
|
||||||
headers: {
|
method: 'POST',
|
||||||
'Content-Type': 'application/json',
|
headers: {
|
||||||
'X-CSRF-Token': getCSRFToken()
|
'Content-Type': 'application/json',
|
||||||
}
|
'X-CSRF-Token': getCSRFToken()
|
||||||
})
|
}
|
||||||
.then(function(r) { return r.json(); })
|
})
|
||||||
.then(function(d) {
|
.then(function(r) { return r.json(); })
|
||||||
if (d.success) { window.location.reload(); }
|
.then(function(d) {
|
||||||
else { alert(d.message || '提交失败'); }
|
if (d.success) { window.location.reload(); }
|
||||||
})
|
else { alert(d.message || '提交失败'); }
|
||||||
.catch(function() { alert('请求失败'); });
|
})
|
||||||
|
.catch(function() { alert('请求失败'); });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
@ -55,9 +55,10 @@
|
|||||||
for (var i = 0; i < btns.length; i++) {
|
for (var i = 0; i < btns.length; i++) {
|
||||||
if (btns[i]._bound) continue;
|
if (btns[i]._bound) continue;
|
||||||
btns[i]._bound = true;
|
btns[i]._bound = true;
|
||||||
btns[i].addEventListener('click', function() {
|
btns[i].addEventListener('click', async function() {
|
||||||
var id = this.dataset.id;
|
var id = this.dataset.id;
|
||||||
if (!confirm('确认删除此评论?')) return;
|
var ok = await showConfirm('确认', '确认删除此评论?');
|
||||||
|
if (!ok) return;
|
||||||
fetch('/api/admin/comments/' + id, {
|
fetch('/api/admin/comments/' + id, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: { 'X-CSRF-Token': csrfToken }
|
headers: { 'X-CSRF-Token': csrfToken }
|
||||||
|
|||||||
@ -37,36 +37,6 @@ const api = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 确认弹窗 ---
|
|
||||||
function showConfirm(title, message) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const overlay = document.createElement('div');
|
|
||||||
overlay.className = 'modal-overlay show';
|
|
||||||
overlay.innerHTML = `
|
|
||||||
<div class="modal-box">
|
|
||||||
<h3>${escapeHtml(title)}</h3>
|
|
||||||
<p>${escapeHtml(message)}</p>
|
|
||||||
<div class="modal-actions">
|
|
||||||
<button class="btn btn-outline" id="modalCancel">取消</button>
|
|
||||||
<button class="btn btn-danger" id="modalConfirm">确认</button>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
overlay.querySelector('#modalCancel').onclick = () => {
|
|
||||||
overlay.remove();
|
|
||||||
resolve(false);
|
|
||||||
};
|
|
||||||
overlay.querySelector('#modalConfirm').onclick = () => {
|
|
||||||
overlay.remove();
|
|
||||||
resolve(true);
|
|
||||||
};
|
|
||||||
overlay.addEventListener('click', (e) => {
|
|
||||||
if (e.target === overlay) { overlay.remove(); resolve(false); }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 角色标签 ---
|
// --- 角色标签 ---
|
||||||
// 数据源由服务端 model.RoleDisplayNames 注入为 window.__ROLE_NAMES
|
// 数据源由服务端 model.RoleDisplayNames 注入为 window.__ROLE_NAMES
|
||||||
function roleLabel(role) {
|
function roleLabel(role) {
|
||||||
|
|||||||
@ -1,48 +1,53 @@
|
|||||||
(function() {
|
(function() {
|
||||||
// CSRF token 由 shared/utils.js 的 getCSRFToken() 统一提供
|
// CSRF token 由 shared/utils.js 的 getCSRFToken() 统一提供
|
||||||
|
// showConfirm / showPrompt / escapeHtml 由 shared/utils.js 统一提供
|
||||||
// api 对象 (api.get/api.post/api.put) 由 admin/common.js 统一提供
|
// api 对象 (api.get/api.post/api.put) 由 admin/common.js 统一提供
|
||||||
|
|
||||||
function refreshPage() { window.location.reload(); }
|
function refreshPage() { window.location.reload(); }
|
||||||
|
|
||||||
function handleClick(sel, actionFn) {
|
function handleClick(sel, confirmMsg, actionFn) {
|
||||||
var btns = document.querySelectorAll(sel);
|
var btns = document.querySelectorAll(sel);
|
||||||
btns.forEach(function(btn) {
|
btns.forEach(function(btn) {
|
||||||
btn.addEventListener('click', function() {
|
btn.addEventListener('click', async function() {
|
||||||
var id = btn.getAttribute('data-id');
|
var id = btn.getAttribute('data-id');
|
||||||
if (confirm('确定执行此操作吗?')) {
|
if (confirmMsg) {
|
||||||
actionFn(id);
|
var ok = await showConfirm('确认', confirmMsg);
|
||||||
|
if (!ok) return;
|
||||||
}
|
}
|
||||||
|
actionFn(id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 审核通过
|
// 审核通过
|
||||||
handleClick('.post-approve', function(id) {
|
handleClick('.post-approve', '确定审核通过此文章吗?', function(id) {
|
||||||
api.post('/api/admin/posts/' + id + '/approve')
|
api.post('/api/admin/posts/' + id + '/approve')
|
||||||
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
||||||
.catch(function() { alert('操作失败'); });
|
.catch(function() { alert('操作失败'); });
|
||||||
});
|
});
|
||||||
|
|
||||||
// 退回
|
// 退回
|
||||||
handleClick('.post-reject', function(id) {
|
handleClick('.post-reject', null, function(id) {
|
||||||
var reason = prompt('请输入退回理由(必填):');
|
showPrompt('请输入退回理由(必填):').then(function(reason) {
|
||||||
if (!reason || !reason.trim()) return;
|
if (!reason || !reason.trim()) return;
|
||||||
api.post('/api/admin/posts/' + id + '/reject', { reason: reason.trim() })
|
api.post('/api/admin/posts/' + id + '/reject', { reason: reason.trim() })
|
||||||
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
||||||
.catch(function() { alert('操作失败'); });
|
.catch(function() { alert('操作失败'); });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 锁定
|
// 锁定
|
||||||
handleClick('.post-lock', function(id) {
|
handleClick('.post-lock', null, function(id) {
|
||||||
var reason = prompt('请输入锁定理由(必填):');
|
showPrompt('请输入锁定理由(必填):').then(function(reason) {
|
||||||
if (!reason || !reason.trim()) return;
|
if (!reason || !reason.trim()) return;
|
||||||
api.post('/api/admin/posts/' + id + '/lock', { reason: reason.trim() })
|
api.post('/api/admin/posts/' + id + '/lock', { reason: reason.trim() })
|
||||||
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
||||||
.catch(function() { alert('操作失败'); });
|
.catch(function() { alert('操作失败'); });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 解锁
|
// 解锁
|
||||||
handleClick('.post-unlock', function(id) {
|
handleClick('.post-unlock', '确定解锁此文章吗?', function(id) {
|
||||||
api.post('/api/admin/posts/' + id + '/unlock')
|
api.post('/api/admin/posts/' + id + '/unlock')
|
||||||
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
.then(function(d) { if (d.success) refreshPage(); else alert(d.message); })
|
||||||
.catch(function() { alert('操作失败'); });
|
.catch(function() { alert('操作失败'); });
|
||||||
|
|||||||
@ -91,6 +91,91 @@ function api(method, url, data) {
|
|||||||
return fetch(url, opts).then(function (r) { return r.json(); });
|
return fetch(url, opts).then(function (r) { return r.json(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确认弹窗 — 替代原生 confirm(),统一全站风格
|
||||||
|
* @param {string} title — 弹窗标题
|
||||||
|
* @param {string} message — 提示消息(支持 \n 换行)
|
||||||
|
* @returns {Promise<boolean>} 用户确认返回 true,取消返回 false
|
||||||
|
*/
|
||||||
|
function showConfirm(title, message) {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
var safeTitle = escapeHtml(title);
|
||||||
|
var safeMsg = escapeHtml(message).replace(/\n/g, '<br>');
|
||||||
|
|
||||||
|
var overlay = document.createElement('div');
|
||||||
|
overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.4);z-index:9998;display:flex;align-items:center;justify-content:center;';
|
||||||
|
overlay.innerHTML = '<div style="background:#fff;border-radius:8px;padding:28px 32px;min-width:320px;max-width:440px;box-shadow:0 8px 30px rgba(0,0,0,0.15);">'
|
||||||
|
+ '<h3 style="margin:0 0 12px 0;font-size:17px;color:#2d3436;">' + safeTitle + '</h3>'
|
||||||
|
+ '<p style="margin:0 0 20px 0;font-size:14px;line-height:1.6;color:#636e72;">' + safeMsg + '</p>'
|
||||||
|
+ '<div style="display:flex;gap:10px;justify-content:flex-end;">'
|
||||||
|
+ '<button id="mlbModalCancel" style="padding:8px 20px;border:1px solid #d1d5db;border-radius:6px;background:#fff;color:#374151;font-size:14px;cursor:pointer;">取消</button>'
|
||||||
|
+ '<button id="mlbModalConfirm" style="padding:8px 20px;border:none;border-radius:6px;background:#dc2626;color:#fff;font-size:14px;cursor:pointer;">确认</button>'
|
||||||
|
+ '</div></div>';
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
|
function close(val) {
|
||||||
|
overlay.remove();
|
||||||
|
resolve(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay.querySelector('#mlbModalCancel').onclick = function() { close(false); };
|
||||||
|
overlay.querySelector('#mlbModalConfirm').onclick = function() { close(true); };
|
||||||
|
overlay.addEventListener('click', function(e) {
|
||||||
|
if (e.target === overlay) close(false);
|
||||||
|
});
|
||||||
|
// Enter 键确认
|
||||||
|
var onKey = function(e) {
|
||||||
|
if (e.key === 'Enter') { document.removeEventListener('keydown', onKey); close(true); }
|
||||||
|
if (e.key === 'Escape') { document.removeEventListener('keydown', onKey); close(false); }
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', onKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输入弹窗 — 替代原生 prompt(),统一全站风格
|
||||||
|
* @param {string} message — 提示文本
|
||||||
|
* @param {string} [defaultValue=''] — 输入框默认值
|
||||||
|
* @returns {Promise<string|null>} 用户输入的值,取消返回 null
|
||||||
|
*/
|
||||||
|
function showPrompt(message, defaultValue) {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
var safeMsg = escapeHtml(message);
|
||||||
|
|
||||||
|
var overlay = document.createElement('div');
|
||||||
|
overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.4);z-index:9998;display:flex;align-items:center;justify-content:center;';
|
||||||
|
overlay.innerHTML = '<div style="background:#fff;border-radius:8px;padding:28px 32px;min-width:320px;max-width:440px;box-shadow:0 8px 30px rgba(0,0,0,0.15);">'
|
||||||
|
+ '<p style="margin:0 0 16px 0;font-size:14px;line-height:1.6;color:#2d3436;">' + safeMsg + '</p>'
|
||||||
|
+ '<input id="mlbPromptInput" type="text" style="width:100%;padding:10px 12px;border:1px solid #d1d5db;border-radius:6px;font-size:14px;color:#2d3436;outline:none;box-sizing:border-box;" value="' + escapeHtml(defaultValue || '') + '" placeholder="">'
|
||||||
|
+ '<div style="display:flex;gap:10px;justify-content:flex-end;margin-top:16px;">'
|
||||||
|
+ '<button id="mlbPromptCancel" style="padding:8px 20px;border:1px solid #d1d5db;border-radius:6px;background:#fff;color:#374151;font-size:14px;cursor:pointer;">取消</button>'
|
||||||
|
+ '<button id="mlbPromptConfirm" style="padding:8px 20px;border:none;border-radius:6px;background:#0984e3;color:#fff;font-size:14px;cursor:pointer;">确认</button>'
|
||||||
|
+ '</div></div>';
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
|
var input = overlay.querySelector('#mlbPromptInput');
|
||||||
|
|
||||||
|
function close(val) {
|
||||||
|
overlay.remove();
|
||||||
|
resolve(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay.querySelector('#mlbPromptCancel').onclick = function() { close(null); };
|
||||||
|
overlay.querySelector('#mlbPromptConfirm').onclick = function() { close(input.value); };
|
||||||
|
overlay.addEventListener('click', function(e) {
|
||||||
|
if (e.target === overlay) close(null);
|
||||||
|
});
|
||||||
|
// Enter 确认,Escape 取消
|
||||||
|
var onKey = function(e) {
|
||||||
|
if (e.key === 'Enter') { document.removeEventListener('keydown', onKey); close(input.value); }
|
||||||
|
if (e.key === 'Escape') { document.removeEventListener('keydown', onKey); close(null); }
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', onKey);
|
||||||
|
// 自动聚焦输入框
|
||||||
|
setTimeout(function() { input.focus(); input.select(); }, 50);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 全局头像图片加载失败回退(替代 onerror 内联事件,满足 CSP 无 unsafe-inline)
|
// 全局头像图片加载失败回退(替代 onerror 内联事件,满足 CSP 无 unsafe-inline)
|
||||||
document.addEventListener('error', function(e) {
|
document.addEventListener('error', function(e) {
|
||||||
var img = e.target;
|
var img = e.target;
|
||||||
|
|||||||
Reference in New Issue
Block a user