Add public functions

Fix a large number of bugs
Optimize the description information
This commit is contained in:
2025-10-27 21:06:41 +08:00
parent edff1bb839
commit 3990ec01c6
23 changed files with 5332 additions and 192 deletions

View File

@@ -291,6 +291,10 @@ loadScript(layuijs, function () {
// 获取Tips内容的统一函数
function getTipsContent(type) {
var tips = {
// 用户资料相关 (user.html)
'user-username': '用户名:用于登录的用户名,可以修改但需要保证唯一性',
'user-old-password': '旧密码:修改密码时需要输入当前密码进行验证,不修改密码时可留空',
'user-new-password': '新密码要设置的新密码长度至少6位不修改密码时可留空',
// 基本信息设置 (settings.html)
'site-title': '站点标题:网站的主标题,显示在浏览器标题栏和搜索引擎结果中',
'site-keywords': '关键词网站的SEO关键词用于搜索引擎优化多个关键词用逗号分隔',
@@ -342,19 +346,22 @@ loadScript(layuijs, function () {
'trial-enabled': '领取试用:控制是否允许用户领取试用时间',
'trial-limit-time': '限制时间:试用领取的时间限制周期',
'trial-time': '试用时间:用户可以领取的试用时长(分钟)',
// 用户资料相关 (user.html)
'user-id': '用户ID系统自动分配的唯一标识符不可修改',
'user-role': '用户角色:当前用户的权限级别,管理员拥有所有权限,普通成员权限有限',
'user-username': '用户名:用于登录的用户名,可以修改但需要保证唯一性',
'user-old-password': '旧密码:修改密码时需要输入当前密码进行验证,不修改密码时可留空',
'user-new-password': '新密码要设置的新密码长度至少6位不修改密码时可留空',
'user-confirm-password': '确认密码:再次输入新密码进行确认,必须与新密码一致',
// API接口管理相关 (apis.html)
'submit-algorithm': '提交算法:客户端向服务器提交数据时使用的加密算法<br/>• 不加密:数据明文传输,适用于内网环境<br/>• RC4对称加密速度快适用于一般场景<br/>• RSA非对称加密安全性高适用于敏感数据<br/>• RSA动态动态生成密钥的RSA加密安全性最高<br/>• 易加密自定义对称加密算法使用15-30位整数密钥数组',
'submit-keys': '提交密钥:用于加密客户端提交数据的密钥<br/>• RC416位十六进制密钥用于对称加密<br/>• RSA公钥用于客户端加密私钥用于服务器解密<br/>• 易加密15-30位整数数组逗号分隔<br/>• 密钥由系统自动生成,确保安全性',
'return-algorithm': '返回算法:服务器向客户端返回数据时使用的加密算法<br/>• 不加密:数据明文传输,适用于内网环境<br/>• RC4对称加密速度快适用于一般场景<br/>• RSA非对称加密安全性高适用于敏感数据<br/>• RSA动态动态生成密钥的RSA加密安全性最高<br/>• 易加密自定义对称加密算法使用15-30位整数密钥数组',
'return-keys': '返回密钥:用于加密服务器返回数据的密钥<br/>• RC416位十六进制密钥用于对称加密<br/>• RSA公钥用于服务器加密私钥用于客户端解密<br/>• 易加密15-30位整数数组逗号分隔<br/>• 密钥由系统自动生成,确保安全性',
'api-status': '接口状态控制当前API接口是否可用<br/>• 启用:接口正常工作,客户端可以调用<br/>• 禁用:接口暂停服务,客户端调用将返回错误'
'api-status': '接口状态控制当前API接口是否可用<br/>• 启用:接口正常工作,客户端可以调用<br/>• 禁用:接口暂停服务,客户端调用将返回错误',
// 变量管理相关 (variables.html)
'variable-alias': '变量别名:变量的唯一标识符,必须以英文字母开头,只能包含数字和英文字母,用于在代码中引用该变量',
'variable-app': '关联应用:选择变量所属的应用,选择"全局变量"表示该变量可在所有应用中使用',
'variable-data': '变量数据存储的具体数据内容可以是文本、数字、JSON等格式根据实际需要填写',
'variable-remark': '备注:对该变量的说明和描述,帮助理解变量的用途和使用场景,可选填写',
// 函数管理相关 (functions.html)
'function-alias': '函数别名:函数的唯一标识符,必须以英文字母开头,只能包含数字和英文字母,用于在代码中调用该函数',
'function-app': '关联应用:选择函数所属的应用,选择"全局函数"表示该函数可在所有应用中使用',
'function-code': '函数代码存储的JavaScript代码内容使用Goja引擎执行支持ES5语法和部分ES6特性',
'function-remark': '备注:对该函数的说明和描述,帮助理解函数的功能和使用场景,可选填写'
};
return tips[type] || '暂无说明';
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{{ define "apis.html" }}
<section>
<h2>接口管理</h2>
<h2>接口设置</h2>
<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;">
@@ -10,7 +10,7 @@
<label class="layui-form-label">应用</label>
<div class="layui-input-inline">
<select name="app_uuid" lay-filter="appSelect" lay-search="">
<option value="">请选择应用</option>
<option value="">全部应用</option>
</select>
</div>
</div>
@@ -268,14 +268,14 @@
width: 160,
templet: (d) => formatDateTime(d.updated_at)
},
{ fixed: 'right', title: '操作', toolbar: '#tpl-apis-ops', width: 100, align: 'center' }
{ fixed: 'right', title: '操作', toolbar: '#tpl-apis-ops', width: 80, align: 'center' }
]]
});
// 加载应用列表到筛选器
function loadApps() {
$.ajax({
url: '/admin/api/apis/apps',
url: '/admin/api/apps/simple',
type: 'GET',
success: function (res) {
if (res.code === 0 && res.data) {
@@ -284,7 +284,7 @@
filterSelect.find('option:not(:first)').remove();
// 添加应用选项(不默认选中,保持“请选择应用”以显示全部接口)
res.data.forEach(function (app) {
var option = '<option value="' + app.uuid + '">' + app.name + '</option>';
var option = '<option value="' + app.uuid + '">' + app.name + '(ID:' + app.id + ')' + '</option>';
filterSelect.append(option);
});
// 仅刷新下拉,不触发表格按应用过滤,默认显示全部接口

View File

@@ -1,6 +1,6 @@
{{ define "apps.html" }}
<section>
<h2>应用管理</h2>
<h2>应用程序</h2>
<div class="layui-btn-container" style="margin:12px 0">
<button class="layui-btn" id="btnAddApp"><i class="layui-icon layui-icon-add-1"></i> 新增应用</button>
<button class="layui-btn layui-btn-danger" id="btnBatchDeleteApps"><i class="layui-icon layui-icon-delete"></i>
@@ -94,7 +94,7 @@
<div class="layui-form-item" id="downloadUrlItem">
<label class="layui-form-label" style="cursor: pointer;" data-tips="download-url">下载地址</label>
<div class="layui-input-block">
<input type="text" name="download_url" placeholder="请输入下载地址" autocomplete="off" class="layui-input" />
<input type="text" name="download_url" placeholder="请输入下载/更新地址" autocomplete="off" class="layui-input" />
</div>
</div>
@@ -123,7 +123,7 @@
<div class="layui-inline">
<label class="layui-form-label" style="cursor: pointer;" data-tips="clean-interval">清理间隔</label>
<div class="layui-input-inline">
<input type="number" name="clean_interval" class="layui-input" placeholder="请输入"
<input type="number" name="clean_interval" lay-affix="number" class="layui-input" placeholder="请输入"
lay-verify="required|number" min="1">
</div>
<div class="layui-form-mid layui-text-em">小时</div>
@@ -133,7 +133,7 @@
<div class="layui-inline">
<label class="layui-form-label" style="cursor: pointer;" data-tips="check-interval">校验间隔</label>
<div class="layui-input-inline">
<input type="number" name="check_interval" class="layui-input" placeholder="请输入"
<input type="number" name="check_interval" lay-affix="number" class="layui-input" placeholder="请输入"
lay-verify="required|number" min="1">
</div>
<div class="layui-form-mid layui-text-em">分钟</div>
@@ -142,7 +142,7 @@
<div class="layui-form-item">
<label class="layui-form-label" style="cursor: pointer;" data-tips="multi-open-count">多开数量</label>
<div class="layui-input-block">
<input type="number" name="multi_open_count" class="layui-input" placeholder="请输入允许的多开数量"
<input type="number" name="multi_open_count" lay-affix="number" class="layui-input" placeholder="请输入允许的多开数量"
lay-verify="required|number" min="1">
</div>
</div>
@@ -180,21 +180,21 @@
<div class="layui-form-item">
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-free-count">免费次数</label>
<div class="layui-input-block">
<input type="number" name="machine_free_count" class="layui-input" placeholder="请输入" lay-verify="number"
<input type="number" name="machine_free_count" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number"
min="0">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-rebind-count">重绑次数</label>
<div class="layui-input-block">
<input type="number" name="machine_rebind_count" class="layui-input" placeholder="请输入" lay-verify="number"
<input type="number" name="machine_rebind_count" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number"
min="0">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-rebind-deduct">重绑扣除</label>
<div class="layui-input-block">
<input type="number" name="machine_rebind_deduct" class="layui-input" placeholder="请输入重绑扣除时间(分钟)"
<input type="number" name="machine_rebind_deduct" lay-affix="number" class="layui-input" placeholder="请输入重绑扣除时间(分钟)"
lay-verify="number" min="0">
</div>
</div>
@@ -229,19 +229,19 @@
<div class="layui-form-item">
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-free-count">免费次数</label>
<div class="layui-input-block">
<input type="number" name="ip_free_count" class="layui-input" placeholder="请输入" lay-verify="number" min="0">
<input type="number" name="ip_free_count" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number" min="0">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-rebind-count">重绑次数</label>
<div class="layui-input-block">
<input type="number" name="ip_rebind_count" class="layui-input" placeholder="请输入" lay-verify="number" min="0">
<input type="number" name="ip_rebind_count" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number" min="0">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-rebind-deduct">重绑扣除</label>
<div class="layui-input-block">
<input type="number" name="ip_rebind_deduct" class="layui-input" placeholder="请输入重绑扣除时间(分钟)"
<input type="number" name="ip_rebind_deduct" lay-affix="number" class="layui-input" placeholder="请输入重绑扣除时间(分钟)"
lay-verify="number" min="0">
</div>
</div>
@@ -279,7 +279,7 @@
<div class="layui-form-item">
<label class="layui-form-label" style="cursor: pointer;" data-tips="register-count">注册次数</label>
<div class="layui-input-block">
<input type="number" name="register_count" class="layui-input" placeholder="请输入" lay-verify="required|number"
<input type="number" name="register_count" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="required|number"
min="1">
</div>
</div>
@@ -305,7 +305,7 @@
<div class="layui-form-item">
<label class="layui-form-label" style="cursor: pointer;" data-tips="trial-time">试用时间</label>
<div class="layui-input-block">
<input type="number" name="trial_duration" class="layui-input" placeholder="请输入试用时间(分钟)" lay-verify="number"
<input type="number" name="trial_duration" lay-affix="number" class="layui-input" placeholder="请输入试用时间(分钟)" lay-verify="number"
min="0">
</div>
</div>
@@ -390,7 +390,7 @@
width: 180,
templet: (d) => formatDateTime(d.created_at)
},
{ fixed: 'right', title: '操作', toolbar: '#tpl-apps-ops', width: 180 }
{ fixed: 'right', title: '操作', toolbar: '#tpl-apps-ops', width: 185 }
]]
});

View File

@@ -0,0 +1,489 @@
{{ 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 }}

View File

@@ -42,7 +42,7 @@
<div class="layui-side layui-bg-black">
<div class="layui-side-scroll">
<!-- 左侧导航区域 -->
<div class="layui-logo layui-bg-black logo-enhanced">{{ .SystemName }}</div>
<div class="layui-logo layui-bg-black logo-enhanced">{{ .Title }}</div>
<ul class="layui-nav layui-nav-tree" lay-shrink="all" lay-unselect lay-filter="nav-side" id="ws-nav-side">
<li class="layui-nav-item">
<a class="" href="javascript:;">系统管理</a>
@@ -55,9 +55,10 @@
<li class="layui-nav-item">
<a href="javascript:;">应用管理</a>
<dl class="layui-nav-child">
<dd><a data-path="apps" href="javascript:;">应用列表</a></dd>
<dd><a data-path="apis" href="javascript:;">接口列表</a></dd>
<dd><a data-path="variables" href="javascript:;">变量列表</a></dd>
<dd><a data-path="apps" href="javascript:;">应用程序</a></dd>
<dd><a data-path="apis" href="javascript:;">接口设置</a></dd>
<dd><a data-path="variables" href="javascript:;">公共变量</a></dd>
<dd><a data-path="functions" href="javascript:;">公共函数</a></dd>
</dl>
</li>
</ul>

View File

@@ -60,7 +60,7 @@
<label class="layui-form-label" style="cursor: pointer;" data-tips="session-timeout">会话超时</label>
<div class="layui-input-block">
<div style="display: flex; align-items: center; gap: 10px;">
<input type="number" name="session_timeout" placeholder="3600" min="300" max="86400" class="layui-input"
<input type="number" name="session_timeout" placeholder="3600" min="300" max="86400" lay-affix="number" class="layui-input"
style="width: 120px;" />
<span class="layui-form-mid">300-86400秒</span>
</div>

View File

@@ -14,24 +14,30 @@
<div style="padding: 20px;">
<form class="layui-form" id="passwordForm" lay-filter="passwordForm" onsubmit="return false">
<div class="layui-form-item">
<label class="layui-form-label">当前密码</label>
<label class="layui-form-label" style="cursor: pointer;" data-tips="user-old-password">当前密码</label>
<div class="layui-input-block">
<input type="password" name="old_password" placeholder="请输入当前密码" autocomplete="off"
class="layui-input" lay-verify="required" />
<div class="layui-input-wrap">
<input type="password" name="old_password" placeholder="请输入当前密码" autocomplete="off"
class="layui-input" lay-verify="required" lay-affix="eye" />
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">新的密码</label>
<label class="layui-form-label" style="cursor: pointer;" data-tips="user-new-password">新的密码</label>
<div class="layui-input-block">
<input type="password" name="new_password" placeholder="请输入新密码至少6位" autocomplete="off"
class="layui-input" lay-verify="required" />
<div class="layui-input-wrap">
<input type="password" name="new_password" placeholder="请输入新密码至少6位" autocomplete="off"
class="layui-input" lay-verify="required" lay-affix="eye" />
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">确认密码</label>
<div class="layui-input-block">
<input type="password" name="confirm_password" placeholder="请再次输入新密码" autocomplete="off"
class="layui-input" lay-verify="required" />
<div class="layui-input-wrap">
<input type="password" name="confirm_password" placeholder="请再次输入新密码" autocomplete="off"
class="layui-input" lay-verify="required" lay-affix="eye" />
</div>
</div>
</div>
<div class="layui-form-item">
@@ -62,17 +68,19 @@
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">新用户名</label>
<label class="layui-form-label" style="cursor: pointer;" data-tips="user-username">新用户名</label>
<div class="layui-input-block">
<input type="text" name="new_username" placeholder="请输入新用户名" autocomplete="off" class="layui-input"
lay-verify="required" />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">当前密码</label>
<label class="layui-form-label" style="cursor: pointer;" data-tips="user-old-password">当前密码</label>
<div class="layui-input-block">
<input type="password" name="password" placeholder="请输入当前密码以确认身份" autocomplete="off"
class="layui-input" lay-verify="required" />
<div class="layui-input-wrap">
<input type="password" name="password" placeholder="请输入当前密码以确认身份" autocomplete="off"
class="layui-input" lay-verify="required" lay-affix="eye" />
</div>
</div>
</div>
<div class="layui-form-item">

View File

@@ -1,6 +1,6 @@
{{ define "variables.html" }}
<section>
<h2>变量管理</h2>
<h2>公共变量</h2>
<div class="layui-btn-container" style="margin:12px 0">
<button class="layui-btn" id="btnAddVariable"><i class="layui-icon layui-icon-add-1"></i> 新增变量</button>
<button class="layui-btn layui-btn-danger" id="btnBatchDeleteVariables"><i class="layui-icon layui-icon-delete"></i>
@@ -12,6 +12,15 @@
<div style="padding: 20px;">
<form class="layui-form layui-form-pane" id="variableFilterForm" lay-filter="variableFilterForm">
<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">
@@ -45,20 +54,28 @@
<form class="layui-form layui-form-pane" lay-filter="variableForm" id="variableForm">
<input type="hidden" name="uuid">
<div class="layui-form-item">
<label class="layui-form-label">变量别名</label>
<label class="layui-form-label" style="cursor: pointer;" data-tips="variable-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">变量数据</label>
<label class="layui-form-label" style="cursor: pointer;" data-tips="variable-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="variable-data">变量数据</label>
<div class="layui-input-block">
<textarea name="data" placeholder="请输入变量数据" lay-verify="required" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">备注</label>
<label class="layui-form-label" style="cursor: pointer;" data-tips="variable-remark">备注</label>
<div class="layui-input-block">
<textarea name="remark" placeholder="请输入备注信息" class="layui-textarea"></textarea>
</div>
@@ -83,6 +100,9 @@
const layer = layui.layer;
const $ = layui.$;
// 全局应用列表
let appsList = [];
// 自定义验证规则
form.verify({
alias: function (value) {
@@ -100,7 +120,55 @@
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="variableFilterForm"] select[name="filter_app_uuid"]');
const formSelect = $('#variableForm 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 variablesTable = table.render({
@@ -131,6 +199,14 @@
{ 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: 'data',
@@ -164,16 +240,24 @@
return formatDateTime(d.created_at);
}
},
{ title: '操作', width: 180, align: 'center', toolbar: '#tpl-variables-ops', fixed: 'right' }
{ title: '操作', width: 120, align: 'center', toolbar: '#tpl-variables-ops', fixed: 'right' }
]]
});
// 搜索功能
$('#btnSearchVariables').on('click', function () {
const searchData = {
search: $('input[name="search"]').val()
};
// 添加应用筛选
const appUUID = $('select[name="filter_app_uuid"]').val();
if (appUUID) {
searchData.app_uuid = appUUID;
}
variablesTable.reload({
where: {
search: $('input[name="search"]').val()
},
where: searchData,
page: {
curr: 1
}
@@ -192,10 +276,31 @@
});
});
// 监听应用选择变化,实现联动筛选
form.on('select(appSelect)', function (data) {
const searchData = {
search: $('input[name="search"]').val()
};
// 添加应用筛选
if (data.value) {
searchData.app_uuid = data.value;
}
variablesTable.reload({
where: searchData,
page: {
curr: 1
}
});
});
// 新增变量
$('#btnAddVariable').on('click', function () {
$('#variableForm')[0].reset();
$('input[name="id"]').val('');
// 确保新增模式下别名输入框是启用的
$('input[name="alias"]').prop('disabled', false);
layer.open({
type: 1,
@@ -295,6 +400,9 @@
$('#variableForm')[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="data"]').val(data.data);
$('textarea[name="remark"]').val(data.remark);
@@ -310,16 +418,13 @@
$('#variableForm').find('input, select, textarea').each(function () {
var $this = $(this);
var name = $this.attr('name');
if (name && name !== 'id') {
// 编辑模式下排除alias字段避免修改别名
if (name && name !== 'id' && name !== 'alias') {
formData[name] = $this.val();
}
});
// 验证必填字段
if (!formData.alias || formData.alias.trim() === '') {
layer.msg('请输入变量别名', { icon: 2 });
return;
}
// 验证必填字段编辑模式下不验证alias
if (!formData.data || formData.data.trim() === '') {
layer.msg('请输入变量数据', { icon: 2 });
return;