网站首页 网站源码
website
站点相关全部源代码,隐藏了一些关于服务器的信息
'use strict';

layui.use(['table', 'layer', 'util'], function () {
    const table = layui.table;
    const layer = layui.layer;
    const util = layui.util;
    const tableId = 'applicationTable';

    const columnWidth = {
        name: 220,
        clientId: 200,
        type: 160,
        actions: 220
    };

    table.render({
        elem: '#applicationTable',
        url: '/Application/Page',
        page: true,
        limit: 20,
        cellMinWidth: 120,
        cols: [[
            {
                field: 'displayName',
                title: '显示名称',
                minWidth: columnWidth.name
            },
            {
                field: 'clientId',
                title: 'ClientId',
                minWidth: columnWidth.clientId,
                templet: d => util.escape(d.clientId ?? '-')
            },
            {
                field: 'clientType',
                title: '应用类型',
                width: columnWidth.type,
                templet: d => renderTags([d.applicationType])
            },
            {
                field: 'clientType',
                title: '客户端类型',
                width: columnWidth.type,
                templet: d => renderTags([d.clientType])
            },
            {
                field: 'redirectUris',
                title: '重定向地址',
                minWidth: 260,
                templet: d => renderBlockList(d.redirectUris)
            },
            {
                field: 'postLogoutRedirectUri',
                title: '登出回调',
                minWidth: 220,
                templet: d => util.escape(d.postLogoutRedirectUri ?? '-')
            },
            {
                field: 'permissions',
                title: '权限',
                minWidth: 240,
                templet: d => renderTags(d.permissions)
            },
            {
                fixed: 'right',
                title: '操作',
                width: columnWidth.actions,
                toolbar: '#applicationTableActions'
            }
        ]]
    });

    function renderTags(items) {
        if (!Array.isArray(items) || items.length === 0) {
            return '<span class="dpz-tag-muted">-</span>';
        }
        return items
            /*.filter(Boolean)*/
            .map(item => isNullOrWhiteSpace(item)
                ? '<span class="dpz-tag-muted">-</span>'
                : `<span class="dpz-tag-badge">${util.escape(item)}</span>`)
            .join(' ');
    }

    function isNullOrWhiteSpace(str) {
        return str === null || typeof str !== 'string' || str.trim().length === 0;
    }

    function renderBlockList(items) {
        if (!Array.isArray(items) || items.length === 0) {
            return '<span class="dpz-tag-muted">-</span>';
        }
        return items
            .map(item => `<div class="layui-text layui-text-sm">${util.escape(item)}</div>`)
            .join('');
    }

    function reloadTable() {
        table.reload(tableId, {
            page: { curr: 1 },
            where: {
                keyword: document.getElementById('keyword')?.value?.trim()
            }
        });
    }

    document.getElementById('btnSearch')?.addEventListener('click', reloadTable);

    document.getElementById('btnClear')?.addEventListener('click', function () {
        const keyword = document.getElementById('keyword');
        if (keyword) {
            keyword.value = '';
        }
        reloadTable();
    });

    table.on('tool(applicationTable)', function (obj) {
        const data = obj.data;
        switch (obj.event) {
            case 'edit':
                window.location.href = `/Application/Upsert?id=${encodeURIComponent(data.id)}`;
                break;
            case 'copyId':
                copyClientId(data.clientId);
                break;
            case 'delete':
                confirmDelete(data);
                break;
            default:
                break;
        }
    });

    function copyClientId(clientId) {
        if (!clientId) {
            layer.msg('没有可复制的 ClientId');
            return;
        }
        if (navigator.clipboard?.writeText) {
            navigator.clipboard
                .writeText(clientId)
                .then(() => layer.msg('已复制 ClientId'))
                .catch(() => fallbackCopy(clientId));
        } else {
            fallbackCopy(clientId);
        }
    }

    function fallbackCopy(text) {
        const textarea = document.createElement('textarea');
        textarea.value = text;
        textarea.style.position = 'fixed';
        textarea.style.opacity = '0';
        document.body.appendChild(textarea);
        textarea.select();
        try {
            document.execCommand('copy');
            layer.msg('已复制 ClientId');
        } catch (e) {
            layer.alert('复制失败,请手动复制');
        } finally {
            document.body.removeChild(textarea);
        }
    }

    function confirmDelete(data) {
        const name = util.escape(data.displayName ?? '');
        if (!window.dpz?.openActionDialog) {
            layer.alert('对话框组件未加载,请稍后再试');
            return;
        }
        window.dpz.openActionDialog({
            title: '删除客户端',
            description: `将删除 <strong>${name}</strong> 的所有关联配置,操作不可恢复。`,
            confirmText: '确认删除',
            cancelText: '取消',
            fields: [
                {
                    id: 'pinCode',
                    type: 'pin',
                    label: '2FA PIN',
                    placeholder: '6位验证码',
                    help: '为安全起见,删除需验证双因素 PIN',
                    required: true,
                    validator: value => /^\d{6}$/.test(value) || '请输入 6 位 PIN',
                }
            ],
            onConfirm: values =>
                submitForm('/Application/Delete', {
                    id: data.id,
                    pinCode: values.pinCode
                })
        });
    }

    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 (e) {
            console.warn('application delete fail', e);
            layer.alert('请求失败,请稍后再试');
            return false;
        }
    }
});

loading