'use strict';
layui.use(['table', 'layer', 'util'], function () {
const table = layui.table;
const layer = layui.layer;
const util = layui.util;
const tableId = 'authorizationTable';
table.render({
elem: '#authorizationTable',
url: '/AuthorizationManage/Page',
page: true,
limit: 20,
cellMinWidth: 120,
cols: [[
{
field: 'applicationName',
title: '应用名称',
minWidth: 200,
templet: d => util.escape(d.applicationName ?? '-')
},
{
field: 'scopes',
title: '授权范围',
minWidth: 240,
templet: d => renderScopes(d.scopes)
},
{
field: 'typeName',
title: '类型',
width: 140,
templet: d => util.escape(d.typeName ?? '-')
},
{
field: 'statusName',
title: '状态',
width: 140,
templet: d => util.escape(d.statusName ?? '-')
},
{
field: 'creationDate',
title: '创建时间',
width: 190,
templet: d => formatDate(d.creationDate)
},
{
fixed: 'right',
title: '操作',
width: 160,
toolbar: '#authorizationTableActions'
}
]]
});
function renderScopes(scopes) {
if (!Array.isArray(scopes) || scopes.length === 0) {
return '<span class="dpz-tag-muted">-</span>';
}
return scopes
.filter(Boolean)
.map(scope => `<span class="dpz-tag-badge">${util.escape(scope)}</span>`)
.join(' ');
}
function formatDate(dateValue) {
if (!dateValue) {
return '-';
}
const date = new Date(dateValue);
if (Number.isNaN(date.getTime())) {
return '-';
}
return util.toDateString(date, 'yyyy-MM-dd HH:mm:ss');
}
function openPinDialog(options) {
const {
title = '操作确认',
description = '',
confirmText = '确认',
cancelText = '取消',
onConfirm
} = options || {};
const overlay = document.createElement('div');
overlay.style.cssText = `
position: fixed; inset: 0; display: flex; align-items: center; justify-content: center;
background: rgba(15, 23, 42, .35); z-index: 9999; padding: 16px;`;
const safeTitle = util.escape(title);
overlay.innerHTML = `
<div style="
max-width: 460px; width: 100%; background: var(--bg-card, #ffffff);
border-radius: 16px; box-shadow: 0 22px 55px rgba(15, 23, 42, .28); padding: 28px;">
<div style="margin-bottom: 18px;">
<h2 style="font-size: 20px; margin: 0 0 8px 0;">${safeTitle}</h2>
<p style="font-size: 14px; margin: 0; color: var(--bs-secondary-color, #64748b);">${description}</p>
</div>
<div style="display:flex; flex-direction:column; gap:10px;">
<label for="pinInput" style="font-size: 13px; color: var(--bs-secondary-color, #64748b);">2FA PIN</label>
<input id="pinInput" class="pin-input" placeholder="6位验证码"
inputmode="numeric" pattern="[0-9]{6}" maxlength="6"
style="
padding: 10px 12px; border-radius: 10px; border: 1px solid rgba(99,102,241,.35);
background: rgba(99,102,241,.06); outline: none; font-size: 16px; letter-spacing: 3px;">
<div style="font-size: 12px; color: var(--bs-secondary-color, #94a3b8);">
为安全起见,需验证双因素 PIN
</div>
</div>
<div style="display:flex; justify-content:flex-end; gap: 10px; margin-top: 22px;">
<button type="button" data-role="cancel"
style="padding: 8px 18px; border-radius: 10px; border: 1px solid rgba(148, 163, 184, .45); background: transparent;">
${cancelText}
</button>
<button type="button" data-role="confirm"
style="padding: 9px 20px; border-radius: 10px; border: none;
background: linear-gradient(135deg, #4f46e5, #6366f1); color: #fff;">
${confirmText}
</button>
</div>
</div>`;
document.body.appendChild(overlay);
const pinInput = overlay.querySelector('#pinInput');
const cancelBtn = overlay.querySelector('[data-role="cancel"]');
const confirmBtn = overlay.querySelector('[data-role="confirm"]');
function close() {
if (overlay.parentElement) {
overlay.parentElement.removeChild(overlay);
}
}
cancelBtn?.addEventListener('click', close);
overlay.addEventListener('click', event => {
if (event.target === overlay) {
close();
}
});
confirmBtn?.addEventListener('click', async () => {
const pin = pinInput?.value.trim() ?? '';
if (!/^\d{6}$/.test(pin)) {
layer.msg('请输入有效的 6 位 PIN');
pinInput?.focus();
return;
}
if (typeof onConfirm !== 'function') {
close();
return;
}
confirmBtn.disabled = true;
const originalText = confirmBtn.textContent;
confirmBtn.textContent = '处理中...';
try {
const success = await onConfirm(pin);
if (success !== false) {
close();
} else {
confirmBtn.disabled = false;
confirmBtn.textContent = originalText;
pinInput?.focus();
}
} catch (error) {
console.warn('pin dialog confirm error', error);
confirmBtn.disabled = false;
confirmBtn.textContent = originalText;
}
});
pinInput?.addEventListener('keydown', event => {
if (event.key === 'Enter') {
confirmBtn?.click();
}
});
pinInput?.focus();
}
function reloadTable() {
table.reload(tableId, {
page: { curr: 1 },
where: {
keyword: document.getElementById('keyword')?.value?.trim(),
applicationId: document.getElementById('applicationFilter')?.value,
status: document.getElementById('statusFilter')?.value,
type: document.getElementById('typeFilter')?.value
}
});
}
document.getElementById('btnSearch')?.addEventListener('click', reloadTable);
document.getElementById('btnClear')?.addEventListener('click', function () {
const keyword = document.getElementById('keyword');
const application = document.getElementById('applicationFilter');
const status = document.getElementById('statusFilter');
const type = document.getElementById('typeFilter');
if (keyword) keyword.value = '';
if (application) application.value = '';
if (status) status.value = '';
if (type) type.value = '';
reloadTable();
});
table.on('tool(authorizationTable)', function (obj) {
if (obj.event !== 'revoke') {
return;
}
promptRevoke(obj.data);
});
function promptRevoke(data) {
const appName = util.escape(data.applicationName ?? '');
openPinDialog({
title: '撤销授权',
description: `将撤销 <strong>${appName}</strong> 的所有关联授权记录,操作不可恢复。`,
confirmText: '确认撤销',
cancelText: '取消',
onConfirm: pin =>
submitForm('/AuthorizationManage/Revoke', {
ApplicationId: data.applicationId,
Status: data.status,
Type: data.type,
PinCode: pin
})
});
}
async function submitForm(action, fields) {
const formData = new FormData();
Object.entries(fields).forEach(([key, value]) => formData.append(key, value ?? ''));
try {
const response = await fetch(action, {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
layer.msg(result.message || '操作成功');
reloadTable();
return true;
}
layer.alert(result.message || '操作失败');
return false;
} catch (error) {
console.warn('authorization revoke fail', error);
layer.alert('请求失败,请稍后再试');
return false;
}
}
});