mirror of
https://github.com/skyle1995/NetworkAuth.git
synced 2026-05-25 02:24:05 +08:00
489 lines
18 KiB
HTML
489 lines
18 KiB
HTML
|
|
{{ define "functions.html" }}
|
|||
|
|
<section>
|
|||
|
|
<h2>公共函数</h2>
|
|||
|
|
<div class="layui-btn-container" style="margin:12px 0">
|
|||
|
|
<button class="layui-btn" id="btnAddFunction"><i class="layui-icon layui-icon-add-1"></i> 新增函数</button>
|
|||
|
|
<button class="layui-btn layui-btn-danger" id="btnBatchDeleteFunctions"><i class="layui-icon layui-icon-delete"></i>
|
|||
|
|
批量删除</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="layui-panel" style="margin-top:12px">
|
|||
|
|
<h3 style="margin: 0; padding: 15px 20px; border-bottom: 1px solid var(--lay-color-border-2); padding-bottom: 10px; margin-bottom: 15px;">筛选</h3>
|
|||
|
|
<div style="padding: 20px;">
|
|||
|
|
<form class="layui-form layui-form-pane" id="functionFilterForm" lay-filter="functionFilterForm">
|
|||
|
|
<div class="layui-form-item">
|
|||
|
|
<div class="layui-inline">
|
|||
|
|
<label class="layui-form-label">应用筛选</label>
|
|||
|
|
<div class="layui-input-inline">
|
|||
|
|
<select name="filter_app_uuid" lay-search lay-filter="appSelect">
|
|||
|
|
<option value="">全部应用</option>
|
|||
|
|
<option value="0">全局函数</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="layui-inline">
|
|||
|
|
<label class="layui-form-label">搜索</label>
|
|||
|
|
<div class="layui-input-inline">
|
|||
|
|
<input type="text" name="search" placeholder="函数编号/别名/代码/备注" autocomplete="off" class="layui-input" />
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="layui-inline">
|
|||
|
|
<button type="button" class="layui-btn" id="btnSearchFunctions">查询</button>
|
|||
|
|
<button type="button" class="layui-btn layui-btn-primary" id="btnResetFunctions">重置</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="layui-panel" style="margin-top:12px">
|
|||
|
|
<h3 style="margin: 0; padding: 15px 20px; border-bottom: 1px solid var(--lay-color-border-2); padding-bottom: 10px; margin-bottom: 15px;">函数列表</h3>
|
|||
|
|
<div style="padding: 20px;">
|
|||
|
|
<table id="functionsTable" lay-filter="functionsTableFilter"></table>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 表格操作模板 -->
|
|||
|
|
<script type="text/html" id="tpl-functions-ops">
|
|||
|
|
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
|
|||
|
|
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<!-- 隐藏的表单弹层内容:新增/编辑函数 -->
|
|||
|
|
<div id="functionFormLayer" style="display:none;padding:20px">
|
|||
|
|
<form class="layui-form layui-form-pane" lay-filter="functionForm" id="functionForm">
|
|||
|
|
<input type="hidden" name="uuid">
|
|||
|
|
<div class="layui-form-item">
|
|||
|
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="function-alias">函数别名</label>
|
|||
|
|
<div class="layui-input-block">
|
|||
|
|
<input type="text" name="alias" lay-verify="required|alias" placeholder="请输入函数别名(英文开头,只能包含数字和英文字母)"
|
|||
|
|
autocomplete="off" class="layui-input" />
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="layui-form-item">
|
|||
|
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="function-app">关联应用</label>
|
|||
|
|
<div class="layui-input-block">
|
|||
|
|
<select name="app_uuid" lay-search>
|
|||
|
|
<option value="0">全局函数</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="layui-form-item">
|
|||
|
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="function-code">函数代码</label>
|
|||
|
|
<div class="layui-input-block">
|
|||
|
|
<textarea name="code" placeholder="请输入函数代码" lay-verify="required" class="layui-textarea"></textarea>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="layui-form-item">
|
|||
|
|
<label class="layui-form-label" style="cursor: pointer;" data-tips="function-remark">备注</label>
|
|||
|
|
<div class="layui-input-block">
|
|||
|
|
<textarea name="remark" placeholder="请输入备注信息" class="layui-textarea"></textarea>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
// 等待layui加载完成
|
|||
|
|
function waitForLayui(callback) {
|
|||
|
|
if (typeof layui !== 'undefined') {
|
|||
|
|
callback();
|
|||
|
|
} else {
|
|||
|
|
setTimeout(() => waitForLayui(callback), 100);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
waitForLayui(function () {
|
|||
|
|
layui.use(['table', 'form', 'layer', 'element'], function () {
|
|||
|
|
const table = layui.table;
|
|||
|
|
const form = layui.form;
|
|||
|
|
const layer = layui.layer;
|
|||
|
|
const $ = layui.$;
|
|||
|
|
|
|||
|
|
// 全局应用列表
|
|||
|
|
let appsList = [];
|
|||
|
|
|
|||
|
|
// 自定义验证规则
|
|||
|
|
form.verify({
|
|||
|
|
alias: function (value) {
|
|||
|
|
if (!value) return '别名不能为空';
|
|||
|
|
// 检查是否以英文字母开头,且只包含数字和英文字母
|
|||
|
|
if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(value)) {
|
|||
|
|
return '别名必须以英文字母开头,只能包含数字和英文字母';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 格式化时间函数
|
|||
|
|
function formatDateTime(dateStr) {
|
|||
|
|
if (!dateStr) return '-';
|
|||
|
|
return new Date(dateStr).toLocaleString();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 根据应用UUID获取应用名称和ID,并添加颜色徽章
|
|||
|
|
function getAppName(appUUID) {
|
|||
|
|
if (appUUID === '0') {
|
|||
|
|
return '<span class="layui-badge layui-bg-blue">全局函数</span>';
|
|||
|
|
}
|
|||
|
|
const app = appsList.find(app => app.uuid === appUUID);
|
|||
|
|
if (app) {
|
|||
|
|
return '<span class="layui-badge layui-bg-green">' + app.name + '(ID:' + app.id + ')' + '</span>';
|
|||
|
|
} else {
|
|||
|
|
return '<span class="layui-badge">未知应用</span>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 加载应用列表
|
|||
|
|
function loadAppList() {
|
|||
|
|
$.ajax({
|
|||
|
|
url: '/admin/api/apps/simple',
|
|||
|
|
type: 'GET',
|
|||
|
|
success: function (res) {
|
|||
|
|
if (res.code === 0 && res.data) {
|
|||
|
|
// 保存应用列表到全局变量
|
|||
|
|
appsList = res.data;
|
|||
|
|
|
|||
|
|
const filterSelect = $('form[lay-filter="functionFilterForm"] select[name="filter_app_uuid"]');
|
|||
|
|
const formSelect = $('#functionForm select[name="app_uuid"]');
|
|||
|
|
|
|||
|
|
// 清空现有选项(保留默认选项:全部应用和全局函数)
|
|||
|
|
filterSelect.find('option:not([value=""]):not([value="0"])').remove();
|
|||
|
|
formSelect.find('option:not([value=""]):not([value="0"])').remove();
|
|||
|
|
|
|||
|
|
// 添加应用选项
|
|||
|
|
res.data.forEach(function(app) {
|
|||
|
|
const option = '<option value="' + app.uuid + '">' + app.name + '(ID:' + app.id + ')' + '</option>';
|
|||
|
|
filterSelect.append(option);
|
|||
|
|
formSelect.append(option);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 重新渲染表单
|
|||
|
|
form.render('select');
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function(xhr) {
|
|||
|
|
console.log('加载应用列表失败:', xhr.responseText);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 页面加载时获取应用列表
|
|||
|
|
loadAppList();
|
|||
|
|
|
|||
|
|
// 渲染表格
|
|||
|
|
const functionsTable = table.render({
|
|||
|
|
elem: '#functionsTable',
|
|||
|
|
id: 'functionsTable',
|
|||
|
|
url: '/admin/function/list',
|
|||
|
|
parseData: function (res) {
|
|||
|
|
return {
|
|||
|
|
code: res.code,
|
|||
|
|
msg: res.msg || '',
|
|||
|
|
count: res.count || 0,
|
|||
|
|
data: res.data || []
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
request: {
|
|||
|
|
pageName: 'page',
|
|||
|
|
limitName: 'page_size'
|
|||
|
|
},
|
|||
|
|
method: 'GET',
|
|||
|
|
page: true,
|
|||
|
|
limit: 20,
|
|||
|
|
limits: [10, 20, 50, 100],
|
|||
|
|
loading: true,
|
|||
|
|
done: function (res, curr, count) {
|
|||
|
|
// 表格渲染完成后的回调
|
|||
|
|
},
|
|||
|
|
cols: [[
|
|||
|
|
{ type: 'checkbox', width: 50 },
|
|||
|
|
{ field: 'id', title: 'ID', width: 80, sort: true },
|
|||
|
|
{ field: 'number', title: '函数编号', width: 180 },
|
|||
|
|
{
|
|||
|
|
field: 'app_uuid',
|
|||
|
|
title: '关联应用',
|
|||
|
|
minWidth: 180,
|
|||
|
|
templet: function (d) {
|
|||
|
|
return getAppName(d.app_uuid);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{ field: 'alias', title: '函数别名', minWidth: 150 },
|
|||
|
|
{
|
|||
|
|
field: 'code',
|
|||
|
|
title: '函数代码',
|
|||
|
|
minWidth: 200,
|
|||
|
|
templet: function (d) {
|
|||
|
|
// 限制显示长度,避免内容过长影响布局
|
|||
|
|
if (d.code && d.code.length > 50) {
|
|||
|
|
return '<span title="' + d.code + '">' + d.code.substring(0, 50) + '...</span>';
|
|||
|
|
}
|
|||
|
|
return d.code || '-';
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
field: 'remark',
|
|||
|
|
title: '备注',
|
|||
|
|
minWidth: 150,
|
|||
|
|
templet: function (d) {
|
|||
|
|
// 限制显示长度,避免内容过长影响布局
|
|||
|
|
if (d.remark && d.remark.length > 30) {
|
|||
|
|
return '<span title="' + d.remark + '">' + d.remark.substring(0, 30) + '...</span>';
|
|||
|
|
}
|
|||
|
|
return d.remark || '-';
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
field: 'created_at',
|
|||
|
|
title: '创建时间',
|
|||
|
|
width: 180,
|
|||
|
|
templet: function (d) {
|
|||
|
|
return formatDateTime(d.created_at);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{ title: '操作', width: 120, align: 'center', toolbar: '#tpl-functions-ops', fixed: 'right' }
|
|||
|
|
]]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 搜索功能
|
|||
|
|
$('#btnSearchFunctions').on('click', function () {
|
|||
|
|
const searchData = {
|
|||
|
|
search: $('input[name="search"]').val()
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 添加应用筛选
|
|||
|
|
const appUUID = $('select[name="filter_app_uuid"]').val();
|
|||
|
|
if (appUUID) {
|
|||
|
|
searchData.app_uuid = appUUID;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
functionsTable.reload({
|
|||
|
|
where: searchData,
|
|||
|
|
page: {
|
|||
|
|
curr: 1
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 重置搜索
|
|||
|
|
$('#btnResetFunctions').on('click', function () {
|
|||
|
|
$('#functionFilterForm')[0].reset();
|
|||
|
|
form.render();
|
|||
|
|
functionsTable.reload({
|
|||
|
|
where: {},
|
|||
|
|
page: {
|
|||
|
|
curr: 1
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 监听应用选择变化,实现联动筛选
|
|||
|
|
form.on('select(appSelect)', function (data) {
|
|||
|
|
const searchData = {
|
|||
|
|
search: $('input[name="search"]').val()
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 添加应用筛选
|
|||
|
|
if (data.value) {
|
|||
|
|
searchData.app_uuid = data.value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
functionsTable.reload({
|
|||
|
|
where: searchData,
|
|||
|
|
page: {
|
|||
|
|
curr: 1
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 新增函数
|
|||
|
|
$('#btnAddFunction').on('click', function () {
|
|||
|
|
$('#functionForm')[0].reset();
|
|||
|
|
$('input[name="id"]').val('');
|
|||
|
|
// 确保新增模式下别名输入框是启用的
|
|||
|
|
$('input[name="alias"]').prop('disabled', false);
|
|||
|
|
|
|||
|
|
layer.open({
|
|||
|
|
type: 1,
|
|||
|
|
title: '新增函数',
|
|||
|
|
content: $('#functionFormLayer'),
|
|||
|
|
area: ['500px', '435px'],
|
|||
|
|
btn: ['创建', '取消'],
|
|||
|
|
yes: function (index, layero) {
|
|||
|
|
// 手动收集表单数据
|
|||
|
|
var formData = {};
|
|||
|
|
$('#functionForm').find('input, select, textarea').each(function () {
|
|||
|
|
var $this = $(this);
|
|||
|
|
var name = $this.attr('name');
|
|||
|
|
if (name && name !== 'id') {
|
|||
|
|
formData[name] = $this.val();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 验证必填字段
|
|||
|
|
if (!formData.alias || formData.alias.trim() === '') {
|
|||
|
|
layer.msg('请输入函数别名', { icon: 2 });
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (!formData.code || formData.code.trim() === '') {
|
|||
|
|
layer.msg('请输入函数代码', { icon: 2 });
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$.ajax({
|
|||
|
|
url: '/admin/function/create',
|
|||
|
|
type: 'POST',
|
|||
|
|
data: JSON.stringify(formData),
|
|||
|
|
contentType: 'application/json',
|
|||
|
|
success: function (res) {
|
|||
|
|
if (res.code === 0) {
|
|||
|
|
layer.msg(res.msg, { icon: 1 });
|
|||
|
|
layer.close(index);
|
|||
|
|
functionsTable.reload();
|
|||
|
|
} else {
|
|||
|
|
layer.msg(res.msg || '操作失败', { icon: 2 });
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function (xhr) {
|
|||
|
|
layer.msg(xhr.responseText || '操作失败', { icon: 2 });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
btn2: function (index) {
|
|||
|
|
layer.close(index);
|
|||
|
|
},
|
|||
|
|
success: function () {
|
|||
|
|
form.render();
|
|||
|
|
},
|
|||
|
|
shadeClose: false
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 批量删除
|
|||
|
|
$('#btnBatchDeleteFunctions').on('click', function () {
|
|||
|
|
const checkStatus = table.checkStatus('functionsTable');
|
|||
|
|
const data = checkStatus.data;
|
|||
|
|
|
|||
|
|
if (data.length === 0) {
|
|||
|
|
layer.msg('请选择要删除的函数', { icon: 2 });
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
layer.confirm('确定删除选中的 ' + data.length + ' 个函数吗?', { icon: 3, title: '提示' }, function (index) {
|
|||
|
|
const ids = data.map(item => item.id);
|
|||
|
|
$.ajax({
|
|||
|
|
url: '/admin/function/batch_delete',
|
|||
|
|
type: 'POST',
|
|||
|
|
data: JSON.stringify({ ids: ids }),
|
|||
|
|
contentType: 'application/json',
|
|||
|
|
success: function (res) {
|
|||
|
|
if (res.code === 0) {
|
|||
|
|
layer.msg(res.msg, { icon: 1 });
|
|||
|
|
functionsTable.reload();
|
|||
|
|
} else {
|
|||
|
|
layer.msg(res.msg || '批量删除失败', { icon: 2 });
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function (xhr) {
|
|||
|
|
layer.msg(xhr.responseText || '批量删除失败', { icon: 2 });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
layer.close(index);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 表格工具栏事件
|
|||
|
|
table.on('tool(functionsTableFilter)', function (obj) {
|
|||
|
|
const data = obj.data;
|
|||
|
|
|
|||
|
|
if (obj.event === 'edit') {
|
|||
|
|
// 编辑
|
|||
|
|
$('#functionForm')[0].reset();
|
|||
|
|
$('input[name="uuid"]').val(data.uuid);
|
|||
|
|
$('input[name="alias"]').val(data.alias);
|
|||
|
|
// 在编辑模式下禁用别名输入框
|
|||
|
|
$('input[name="alias"]').prop('disabled', true);
|
|||
|
|
$('select[name="app_uuid"]').val(data.app_uuid || '0');
|
|||
|
|
$('textarea[name="code"]').val(data.code);
|
|||
|
|
$('textarea[name="remark"]').val(data.remark);
|
|||
|
|
|
|||
|
|
layer.open({
|
|||
|
|
type: 1,
|
|||
|
|
title: '编辑函数',
|
|||
|
|
content: $('#functionFormLayer'),
|
|||
|
|
area: ['500px', '435px'],
|
|||
|
|
btn: ['保存', '取消'],
|
|||
|
|
yes: function (index, layero) {
|
|||
|
|
// 手动收集表单数据
|
|||
|
|
var formData = {};
|
|||
|
|
$('#functionForm').find('input, select, textarea').each(function () {
|
|||
|
|
var $this = $(this);
|
|||
|
|
var name = $this.attr('name');
|
|||
|
|
// 编辑模式下排除alias字段,避免修改别名
|
|||
|
|
if (name && name !== 'id' && name !== 'alias') {
|
|||
|
|
formData[name] = $this.val();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 验证必填字段(编辑模式下不验证alias)
|
|||
|
|
if (!formData.code || formData.code.trim() === '') {
|
|||
|
|
layer.msg('请输入函数代码', { icon: 2 });
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$.ajax({
|
|||
|
|
url: '/admin/function/update',
|
|||
|
|
type: 'POST',
|
|||
|
|
data: JSON.stringify(formData),
|
|||
|
|
contentType: 'application/json',
|
|||
|
|
success: function (res) {
|
|||
|
|
if (res.code === 0) {
|
|||
|
|
layer.msg(res.msg, { icon: 1 });
|
|||
|
|
layer.close(index);
|
|||
|
|
functionsTable.reload();
|
|||
|
|
} else {
|
|||
|
|
layer.msg(res.msg || '操作失败', { icon: 2 });
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function (xhr) {
|
|||
|
|
layer.msg(xhr.responseText || '操作失败', { icon: 2 });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
btn2: function (index) {
|
|||
|
|
layer.close(index);
|
|||
|
|
},
|
|||
|
|
success: function () {
|
|||
|
|
form.render();
|
|||
|
|
},
|
|||
|
|
shadeClose: false
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
} else if (obj.event === 'del') {
|
|||
|
|
// 删除
|
|||
|
|
layer.confirm('确定删除该函数吗?', { icon: 3, title: '提示' }, function (index) {
|
|||
|
|
$.ajax({
|
|||
|
|
url: '/admin/function/delete',
|
|||
|
|
type: 'POST',
|
|||
|
|
data: JSON.stringify({ id: data.id }),
|
|||
|
|
contentType: 'application/json',
|
|||
|
|
success: function (res) {
|
|||
|
|
if (res.code === 0) {
|
|||
|
|
layer.msg(res.msg, { icon: 1 });
|
|||
|
|
functionsTable.reload();
|
|||
|
|
} else {
|
|||
|
|
layer.msg(res.msg || '删除失败', { icon: 2 });
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function (xhr) {
|
|||
|
|
layer.msg(xhr.responseText || '删除失败', { icon: 2 });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
layer.close(index);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
</section>
|
|||
|
|
{{ end }}
|