网站首页 网站源码
website
站点相关全部源代码,隐藏了一些关于服务器的信息
'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;
        }
    }
});

loading