Files
NetworkAuth/web/template/admin/apps.html

1232 lines
58 KiB
HTML
Raw Normal View History

2025-10-24 00:09:45 +08:00
{{ define "apps.html" }}
<section>
<h2>应用程序</h2>
2025-10-24 00:09:45 +08:00
<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>
批量删除</button>
<button class="layui-btn layui-btn-normal" id="btnBatchEnableApps"><i class="layui-icon layui-icon-ok-circle"></i>
批量启用</button>
<button class="layui-btn layui-btn-warm" id="btnBatchDisableApps"><i class="layui-icon layui-icon-close-fill"></i>
批量禁用</button>
2025-10-24 00:09:45 +08:00
</div>
2025-10-26 11:57:31 +08:00
<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;">
2025-10-24 00:09:45 +08:00
<form class="layui-form layui-form-pane" id="appFilterForm" lay-filter="appFilterForm">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">搜索</label>
<div class="layui-input-inline">
<input type="text" name="search" placeholder="应用名称/UUID" autocomplete="off" class="layui-input" />
</div>
</div>
<div class="layui-inline">
<button type="button" class="layui-btn" id="btnSearchApps">查询</button>
<button type="button" class="layui-btn layui-btn-primary" id="btnResetApps">重置</button>
</div>
</div>
</form>
</div>
</div>
2025-10-26 11:57:31 +08:00
<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;">
2025-10-24 00:09:45 +08:00
<table id="appsTable" lay-filter="appsTableFilter"></table>
<script type="text/html" id="tpl-apps-ops">
<div style="white-space: nowrap;">
<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>
<a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="more">
更多 <i class="layui-icon layui-icon-down"></i>
</a>
</div>
2025-10-24 00:09:45 +08:00
</script>
<script type="text/html" id="tpl-apps-status">
{{`{{# if(d.status === 1) { }}`}}
<span class="layui-badge layui-bg-green">启用</span>
{{`{{# } else { }}`}}
<span class="layui-badge">禁用</span>
{{`{{# } }}`}}
</script>
</div>
</div>
<!-- 隐藏的表单弹层内容:新增/编辑应用 -->
<div id="appFormModal" style="display:none;padding:16px">
<form class="layui-form layui-form-pane" id="appForm">
<input type="hidden" name="id" />
<div class="layui-form-item">
2025-10-24 13:00:30 +08:00
<label class="layui-form-label" style="cursor: pointer;" data-tips="app-name">应用名称</label>
2025-10-24 00:09:45 +08:00
<div class="layui-input-block">
<input type="text" name="name" placeholder="请输入应用名称" autocomplete="off" class="layui-input"
lay-verify="required" />
2025-10-24 00:09:45 +08:00
</div>
</div>
<div class="layui-form-item">
2025-10-24 13:00:30 +08:00
<label class="layui-form-label" style="cursor: pointer;" data-tips="app-version">应用版本</label>
2025-10-24 00:09:45 +08:00
<div class="layui-input-block">
<input type="text" name="version" placeholder="请输入应用版本默认1.0.0" autocomplete="off" class="layui-input" />
2025-10-24 00:09:45 +08:00
</div>
</div>
<div class="layui-form-item" pane>
2025-10-24 13:00:30 +08:00
<label class="layui-form-label" style="cursor: pointer;" data-tips="app-status">应用状态</label>
2025-10-24 00:09:45 +08:00
<div class="layui-input-block">
<input type="checkbox" name="status" lay-skin="switch" lay-text="启用|禁用" checked>
2025-10-24 00:09:45 +08:00
</div>
</div>
<div class="layui-form-item" pane>
2025-10-24 13:00:30 +08:00
<label class="layui-form-label" style="cursor: pointer;" data-tips="force-update">强制更新</label>
2025-10-24 00:09:45 +08:00
<div class="layui-input-block">
<input type="checkbox" name="force_update" lay-skin="switch" lay-text="开启|关闭">
2025-10-24 00:09:45 +08:00
</div>
</div>
<div class="layui-form-item" pane>
2025-10-24 13:00:30 +08:00
<label class="layui-form-label" style="cursor: pointer;" data-tips="download-type">更新方式</label>
2025-10-24 00:09:45 +08:00
<div class="layui-input-block">
<input type="radio" name="download_type" value="0" title="不启用" checked lay-filter="downloadTypeChange">
<input type="radio" name="download_type" value="1" title="自动更新" lay-filter="downloadTypeChange">
<input type="radio" name="download_type" value="2" title="手动下载" lay-filter="downloadTypeChange">
2025-10-24 00:09:45 +08:00
</div>
</div>
<div class="layui-form-item" id="downloadUrlItem">
2025-10-24 13:00:30 +08:00
<label class="layui-form-label" style="cursor: pointer;" data-tips="download-url">下载地址</label>
2025-10-24 00:09:45 +08:00
<div class="layui-input-block">
<input type="text" name="download_url" placeholder="请输入下载/更新地址" autocomplete="off" class="layui-input" />
2025-10-24 00:09:45 +08:00
</div>
</div>
2025-10-24 00:09:45 +08:00
</form>
</div>
2025-10-24 13:00:30 +08:00
<!-- 隐藏的表单弹层内容:多开配置 -->
<div id="multiConfigModal" style="display:none;padding:20px">
<form class="layui-form layui-form-pane" lay-filter="multiConfigForm">
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="login-type">登录方式</label>
<div class="layui-input-block">
<input type="radio" name="login_type" value="0" title="顶号登录">
<input type="radio" name="login_type" value="1" title="非顶号登录">
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="multi-open-scope">多开范围</label>
<div class="layui-input-block">
<input type="radio" name="multi_open_scope" value="0" title="单设备">
<input type="radio" name="multi_open_scope" value="1" title="单IP">
<input type="radio" name="multi_open_scope" value="2" title="全部设备">
</div>
</div>
<div class="layui-form-item">
<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" lay-affix="number" class="layui-input" placeholder="请输入"
lay-verify="required|number" min="1">
2025-10-24 13:00:30 +08:00
</div>
<div class="layui-form-mid layui-text-em">小时</div>
</div>
</div>
<div class="layui-form-item">
<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" lay-affix="number" class="layui-input" placeholder="请输入"
lay-verify="required|number" min="1">
2025-10-24 13:00:30 +08:00
</div>
<div class="layui-form-mid layui-text-em">分钟</div>
</div>
</div>
<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" lay-affix="number" class="layui-input" placeholder="请输入允许的多开数量"
lay-verify="required|number" min="1">
2025-10-24 13:00:30 +08:00
</div>
</div>
</form>
</div>
<!-- 隐藏的表单弹层内容:绑定设置 -->
<div id="bindConfigModal" style="display:none;padding:20px">
<form class="layui-form layui-form-pane" lay-filter="bindConfigForm">
<!-- 机器码验证设置 -->
<fieldset class="layui-elem-field layui-field-title">
<legend>机器验证设置</legend>
</fieldset>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-verify">机器码验证</label>
<div class="layui-input-block">
<input type="radio" name="machine_verify" value="0" title="关闭">
<input type="radio" name="machine_verify" value="1" title="开启">
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-rebind">机器码重绑</label>
<div class="layui-input-block">
<input type="radio" name="machine_rebind_enabled" value="0" title="关闭">
<input type="radio" name="machine_rebind_enabled" value="1" title="开启">
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="machine-rebind-limit">重绑限制</label>
<div class="layui-input-block">
<input type="radio" name="machine_rebind_limit" value="0" title="每天">
<input type="radio" name="machine_rebind_limit" value="1" title="永久">
</div>
</div>
<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" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number"
min="0">
2025-10-24 13:00:30 +08:00
</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" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number"
min="0">
2025-10-24 13:00:30 +08:00
</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" lay-affix="number" class="layui-input" placeholder="请输入重绑扣除时间(分钟)"
lay-verify="number" min="0">
2025-10-24 13:00:30 +08:00
</div>
</div>
2025-10-24 13:00:30 +08:00
<!-- IP地址验证设置 -->
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>IP地址验证设置</legend>
</fieldset>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-verify">IP地址验证</label>
<div class="layui-input-block">
<input type="radio" name="ip_verify" value="0" title="关闭">
<input type="radio" name="ip_verify" value="1" title="开启">
<input type="radio" name="ip_verify" value="2" title="开启(市)">
<input type="radio" name="ip_verify" value="3" title="开启(省)">
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-rebind">IP地址重绑</label>
<div class="layui-input-block">
<input type="radio" name="ip_rebind_enabled" value="0" title="关闭">
<input type="radio" name="ip_rebind_enabled" value="1" title="开启">
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="ip-rebind-limit">重绑限制</label>
<div class="layui-input-block">
<input type="radio" name="ip_rebind_limit" value="0" title="每天">
<input type="radio" name="ip_rebind_limit" value="1" title="永久">
</div>
</div>
<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" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number" min="0">
2025-10-24 13:00:30 +08:00
</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" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="number" min="0">
2025-10-24 13:00:30 +08:00
</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" lay-affix="number" class="layui-input" placeholder="请输入重绑扣除时间(分钟)"
lay-verify="number" min="0">
2025-10-24 13:00:30 +08:00
</div>
</div>
</form>
</div>
<!-- 隐藏的表单弹层内容:注册设置 -->
<div id="registerConfigModal" style="display:none;padding:20px">
<form class="layui-form layui-form-pane" lay-filter="registerConfigForm">
<!-- 账号注册设置 -->
<fieldset class="layui-elem-field layui-field-title">
<legend>账号注册设置</legend>
</fieldset>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="register-enabled">账号注册</label>
<div class="layui-input-block">
<input type="radio" name="register_enabled" value="0" title="关闭">
<input type="radio" name="register_enabled" value="1" title="开启">
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="register-limit">注册限制</label>
<div class="layui-input-block">
<input type="radio" name="register_limit_enabled" value="0" title="关闭">
<input type="radio" name="register_limit_enabled" value="1" title="开启">
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="register-limit-time">限制时间</label>
<div class="layui-input-block">
<input type="radio" name="register_limit_time" value="0" title="每天">
<input type="radio" name="register_limit_time" value="1" title="永久">
</div>
</div>
<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" lay-affix="number" class="layui-input" placeholder="请输入" lay-verify="required|number"
min="1">
2025-10-24 13:00:30 +08:00
</div>
</div>
2025-10-24 13:00:30 +08:00
<!-- 领取试用设置 -->
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>领取试用设置</legend>
</fieldset>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="trial-enabled">领取试用</label>
<div class="layui-input-block">
<input type="radio" name="trial_enabled" value="0" title="关闭">
<input type="radio" name="trial_enabled" value="1" title="开启">
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="trial-limit-time">限制时间</label>
<div class="layui-input-block">
<input type="radio" name="trial_limit_time" value="0" title="每天">
<input type="radio" name="trial_limit_time" value="1" title="永久">
</div>
</div>
<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" lay-affix="number" class="layui-input" placeholder="请输入试用时间(分钟)" lay-verify="number"
min="0">
2025-10-24 13:00:30 +08:00
</div>
</div>
</form>
</div>
2025-10-24 00:09:45 +08:00
<script>
2025-10-24 13:00:30 +08:00
// 等待layui加载完成
function waitForLayui(callback) {
if (typeof layui !== 'undefined') {
callback();
} else {
setTimeout(() => waitForLayui(callback), 100);
}
}
waitForLayui(function () {
layui.use(['table', 'form', 'layer', 'element', 'dropdown', 'util'], function () {
const table = layui.table;
const form = layui.form;
const layer = layui.layer;
const dropdown = layui.dropdown;
const util = layui.util;
const $ = layui.$;
2025-10-24 00:09:45 +08:00
// 格式化时间函数
function formatDateTime(dateStr) {
if (!dateStr) return '-';
return new Date(dateStr).toLocaleString();
}
2025-10-24 00:09:45 +08:00
// 渲染表格
const appsTable = table.render({
elem: '#appsTable',
id: 'appsTable',
url: '/admin/api/apps/list',
parseData: function (res) {
// 后端返回的数据结构处理
return {
code: res.code,
msg: res.msg || '',
count: res.count || 0,
data: res.data || []
};
2025-10-24 00:09:45 +08:00
},
request: {
pageName: 'page', // 页码的参数名称默认page
limitName: 'page_size' // 每页数据量的参数名称默认limit
2025-10-24 00:09:45 +08:00
},
method: 'GET',
page: true,
limit: 20,
limits: [10, 20, 50, 100],
loading: true,
done: function (res, curr, count) {
// 表格渲染完成后的回调
2025-10-24 00:09:45 +08:00
},
cols: [[
{ type: 'checkbox', width: 50 },
{ field: 'id', title: 'ID', width: 80, sort: true },
{ field: 'name', title: '应用名称', minWidth: 180 },
{ field: 'uuid', title: 'UUID', minWidth: 335 },
{ field: 'version', title: '应用版本', width: 100 },
{
field: 'status',
title: '应用状态',
width: 100,
templet: (d) => {
const checked = d.status === 1 ? 'checked' : '';
return `<input type="checkbox" name="app-status-${d.id}" lay-skin="switch" lay-text="启用|禁用" ${checked} lay-filter="app-status-switch" data-id="${d.id}">`;
}
},
{
field: 'secret',
title: '密钥',
minWidth: 320,
templet: (d) => '<span style="font-family: monospace;">' + d.secret + '</span>'
},
{
field: 'created_at',
title: '创建时间',
width: 180,
templet: (d) => formatDateTime(d.created_at)
},
{ fixed: 'right', title: '操作', toolbar: '#tpl-apps-ops', width: 185 }
]]
2025-10-24 00:09:45 +08:00
});
// 搜索功能
$('#btnSearchApps').on('click', function () {
const search = $('input[name="search"]').val();
appsTable.reload({
where: {
search: search
},
page: {
curr: 1
}
});
2025-10-24 00:09:45 +08:00
});
// 重置搜索
$('#btnResetApps').on('click', function () {
$('#appFilterForm')[0].reset();
appsTable.reload({
where: {},
page: {
curr: 1
}
});
2025-10-24 00:09:45 +08:00
});
// 新增应用
$('#btnAddApp').on('click', function () {
2025-10-24 00:09:45 +08:00
$('#appForm')[0].reset();
$('input[name="id"]').val('');
2025-10-24 00:09:45 +08:00
layer.open({
type: 1,
title: '新增应用',
2025-10-24 00:09:45 +08:00
content: $('#appFormModal'),
area: ['500px', '460px'],
btn: ['创建', '取消'],
yes: function (index, layero) {
// 手动触发表单提交验证
var formData = {};
$('#appForm').find('input, select, textarea').each(function () {
var $this = $(this);
var name = $this.attr('name');
if (name) {
if ($this.attr('type') === 'checkbox') {
if ($this.attr('lay-skin') === 'switch') {
formData[name] = $this.prop('checked') ? 1 : 0;
} else {
formData[name] = $this.prop('checked') ? $this.val() : '';
}
} else if ($this.attr('type') === 'radio') {
if ($this.prop('checked')) {
formData[name] = $this.val();
}
} else {
formData[name] = $this.val();
}
}
});
// 验证必填字段
if (!formData.name || formData.name.trim() === '') {
layer.msg('请输入应用名称', { icon: 2 });
return;
}
// 处理数据类型转换
formData.download_type = parseInt(formData.download_type) || 0;
$.ajax({
url: '/admin/api/apps/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);
appsTable.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();
},
2025-10-24 00:09:45 +08:00
shadeClose: false
});
});
// 监听更新方式切换(保留事件监听器以备将来扩展)
form.on('radio(downloadTypeChange)', function (data) {
// 下载地址字段现在始终显示,无需切换显示状态
});
// 表格工具栏事件
table.on('tool(appsTableFilter)', function (obj) {
const data = obj.data;
if (obj.event === 'edit') {
// 编辑
$('#appForm')[0].reset();
$('input[name="id"]').val(data.id);
$('input[name="name"]').val(data.name);
$('input[name="version"]').val(data.version);
// 设置应用状态开关
$('input[name="status"]').prop('checked', data.status === 1);
// 设置更新方式单选按钮
$('input[name="download_type"][value="' + (data.download_type || 0) + '"]').prop('checked', true);
$('input[name="download_url"]').val(data.download_url || '');
// 设置强制更新开关
$('input[name="force_update"]').prop('checked', data.force_update === 1);
layer.open({
type: 1,
title: '编辑应用',
content: $('#appFormModal'),
area: ['500px', '460px'],
btn: ['保存', '取消'],
yes: function (index, layero) {
// 手动触发表单提交验证
var formData = {};
$('#appForm').find('input, select, textarea').each(function () {
var $this = $(this);
var name = $this.attr('name');
if (name) {
if ($this.attr('type') === 'checkbox') {
if ($this.attr('lay-skin') === 'switch') {
formData[name] = $this.prop('checked') ? 1 : 0;
} else {
formData[name] = $this.prop('checked') ? $this.val() : '';
}
} else if ($this.attr('type') === 'radio') {
if ($this.prop('checked')) {
formData[name] = $this.val();
}
} else {
formData[name] = $this.val();
}
}
});
// 验证必填字段
if (!formData.name || formData.name.trim() === '') {
layer.msg('请输入应用名称', { icon: 2 });
return;
}
// 处理数据类型转换
formData.download_type = parseInt(formData.download_type) || 0;
formData.id = parseInt(formData.id);
$.ajax({
url: '/admin/api/apps/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);
appsTable.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/api/apps/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 });
appsTable.reload();
} else {
layer.msg(res.msg || '删除失败', { icon: 2 });
}
},
error: function (xhr) {
layer.msg(xhr.responseText || '删除失败', { icon: 2 });
}
});
layer.close(index);
});
} else if (obj.event === 'more') {
// 更多操作下拉菜单
dropdown.render({
elem: this, // 使用 this 而不是查找元素
show: true, // 外部事件触发即显示
data: [
{
title: '应用数据',
id: 'app_data'
},
{
title: '程序公告',
id: 'announcement'
},
{
title: '多开配置',
id: 'multi_instance'
},
{
title: '绑定设置',
id: 'bind_settings'
},
{
title: '注册设置',
id: 'register_settings'
},
{
title: '重置密钥',
id: 'reset_secret'
}
],
click: function (menudata, othis) {
if (menudata.id === 'app_data') {
// 应用数据
// 先获取当前应用数据内容
$.ajax({
url: '/admin/api/apps/get_app_data?uuid=' + obj.data.uuid,
type: 'GET',
success: function (res) {
var currentAppData = '';
if (res.code === 0 && res.data && res.data.app_data) {
currentAppData = res.data.app_data;
}
// 显示编辑弹窗
layer.open({
type: 1,
title: '编辑应用数据 - ' + obj.data.name,
area: ['600px', '400px'],
content: '<div style="padding: 20px;">' +
'<textarea id="appDataEditor" class="layui-textarea" placeholder="请输入应用数据内容..." style="height: 250px;">' +
currentAppData +
'</textarea>' +
'</div>',
btn: ['保存', '取消'],
yes: function (index, layero) {
var appDataContent = $('#appDataEditor').val();
// 发送更新请求
$.ajax({
url: '/admin/api/apps/update_app_data',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
uuid: obj.data.uuid,
app_data: appDataContent
}),
success: function (res) {
if (res.code === 0) {
layer.msg('应用数据更新成功!', {
icon: 1,
time: 2000
});
layer.close(index);
} else {
layer.msg(res.msg || '更新应用数据失败', { icon: 2 });
}
},
error: function () {
layer.msg('网络错误,请稍后重试', { icon: 2 });
}
});
},
btn2: function (index) {
layer.close(index);
}
});
},
error: function () {
layer.msg('获取应用数据失败,请稍后重试', { icon: 2 });
}
});
} else if (menudata.id === 'announcement') {
// 程序公告
// 先获取当前公告内容
$.ajax({
url: '/admin/api/apps/get_announcement?uuid=' + obj.data.uuid,
type: 'GET',
success: function (res) {
var currentAnnouncement = '';
if (res.code === 0 && res.data && res.data.announcement) {
currentAnnouncement = res.data.announcement;
}
// 显示编辑弹窗
layer.open({
type: 1,
title: '编辑程序公告 - ' + obj.data.name,
area: ['600px', '400px'],
content: '<div style="padding: 20px;">' +
'<textarea id="announcementEditor" class="layui-textarea" placeholder="请输入程序公告内容..." style="height: 250px;">' +
currentAnnouncement +
'</textarea>' +
'</div>',
btn: ['保存', '取消'],
yes: function (index, layero) {
var announcementContent = $('#announcementEditor').val();
// 发送更新请求
$.ajax({
url: '/admin/api/apps/update_announcement',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
uuid: obj.data.uuid,
announcement: announcementContent
}),
success: function (res) {
if (res.code === 0) {
layer.msg('程序公告更新成功!', {
icon: 1,
time: 2000
});
layer.close(index);
} else {
layer.msg(res.msg || '更新程序公告失败', { icon: 2 });
}
},
error: function () {
layer.msg('网络错误,请稍后重试', { icon: 2 });
}
});
},
btn2: function (index) {
layer.close(index);
2025-10-24 05:09:22 +08:00
}
});
},
error: function () {
layer.msg('获取程序公告失败,请稍后重试', { icon: 2 });
}
});
} else if (menudata.id === 'multi_instance') {
// 多开配置
$.ajax({
url: '/admin/api/apps/get_multi_config?uuid=' + obj.data.uuid,
type: 'GET',
2025-10-26 14:48:02 +08:00
success: function (res) {
if (res.code === 0 && res.data) {
var config = res.data;
// 填充表单数据
$('input[name="login_type"][value="' + config.login_type + '"]').prop('checked', true);
$('input[name="multi_open_scope"][value="' + config.multi_open_scope + '"]').prop('checked', true);
$('input[name="clean_interval"]').val(config.clean_interval);
$('input[name="check_interval"]').val(config.check_interval);
$('input[name="multi_open_count"]').val(config.multi_open_count);
2025-10-26 14:48:02 +08:00
// 打开静态弹窗
var multiConfigIndex = layer.open({
type: 1,
title: '多开配置 - ' + obj.data.name,
area: ['550px', '450px'],
content: $('#multiConfigModal'),
btn: ['保存', '取消'],
yes: function (index, layero) {
var formData = {
uuid: obj.data.uuid,
login_type: parseInt($('input[name="login_type"]:checked').val()),
multi_open_scope: parseInt($('input[name="multi_open_scope"]:checked').val()),
clean_interval: parseInt($('input[name="clean_interval"]').val()),
check_interval: parseInt($('input[name="check_interval"]').val()),
multi_open_count: parseInt($('input[name="multi_open_count"]').val())
};
// 验证数据
if (isNaN(formData.login_type) || formData.login_type < 0 || formData.login_type > 1) {
layer.msg('请选择登录方式', { icon: 2 });
return;
}
if (isNaN(formData.multi_open_scope) || formData.multi_open_scope < 0 || formData.multi_open_scope > 2) {
layer.msg('请选择多开范围', { icon: 2 });
return;
}
if (isNaN(formData.clean_interval) || formData.clean_interval < 1) {
layer.msg('清理间隔必须大于0', { icon: 2 });
return;
}
if (isNaN(formData.check_interval) || formData.check_interval < 1) {
layer.msg('校验间隔必须大于0', { icon: 2 });
return;
}
if (isNaN(formData.multi_open_count) || formData.multi_open_count < 1) {
layer.msg('多开数量必须大于0', { icon: 2 });
return;
}
// 发送更新请求
$.ajax({
url: '/admin/api/apps/update_multi_config',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(formData),
success: function (res) {
2025-10-26 14:48:02 +08:00
if (res.code === 0) {
layer.msg('多开配置更新成功', { icon: 1 });
layer.close(index);
table.reload('appsTable');
} else {
layer.msg(res.msg || '更新多开配置失败', { icon: 2 });
}
},
error: function () {
layer.msg('网络错误,请稍后重试', { icon: 2 });
}
});
},
btn2: function (index) {
layer.close(index);
},
success: function () {
// 重新渲染表单
form.render();
2025-10-24 08:25:16 +08:00
}
});
2025-10-26 14:48:02 +08:00
} else {
layer.msg(res.msg || '获取多开配置失败', { icon: 2 });
}
},
error: function () {
layer.msg('获取多开配置失败,请稍后重试', { icon: 2 });
}
});
} else if (menudata.id === 'reset_secret') {
// 重置密钥
layer.confirm('确定重置该应用的密钥吗?重置后原密钥将失效!', { icon: 3, title: '提示' }, function (index) {
// 发送重置密钥请求
$.ajax({
url: '/admin/api/apps/reset_secret',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
uuid: obj.data.uuid
}),
success: function (res) {
if (res.code === 0) {
layer.msg('密钥重置成功!', {
icon: 1,
time: 2000 // 显示2秒
});
// 刷新表格数据
table.reload('appsTable');
} else {
layer.msg(res.msg || '重置密钥失败', { icon: 2 });
2025-10-24 05:09:22 +08:00
}
},
error: function (xhr) {
let errorMsg = '重置密钥失败';
if (xhr.responseText) {
try {
const errorRes = JSON.parse(xhr.responseText);
errorMsg = errorRes.msg || errorMsg;
} catch (e) {
errorMsg = xhr.responseText;
}
}
layer.msg(errorMsg, { icon: 2 });
2025-10-24 05:09:22 +08:00
}
});
layer.close(index);
});
} else if (menudata.id === 'bind_settings') {
// 绑定设置
$.ajax({
url: '/admin/api/apps/get_bind_config?uuid=' + obj.data.uuid,
type: 'GET',
2025-10-26 14:48:02 +08:00
success: function (res) {
if (res.code === 0 && res.data) {
var config = res.data;
// 填充表单数据
$('#bindConfigModal input[name="machine_verify"][value="' + config.machine_verify + '"]').prop('checked', true);
$('#bindConfigModal input[name="machine_rebind_enabled"][value="' + config.machine_rebind_enabled + '"]').prop('checked', true);
$('#bindConfigModal input[name="machine_rebind_limit"][value="' + config.machine_rebind_limit + '"]').prop('checked', true);
$('#bindConfigModal input[name="machine_free_count"]').val(config.machine_free_count);
$('#bindConfigModal input[name="machine_rebind_count"]').val(config.machine_rebind_count);
$('#bindConfigModal input[name="machine_rebind_deduct"]').val(config.machine_rebind_deduct);
$('#bindConfigModal input[name="ip_verify"][value="' + config.ip_verify + '"]').prop('checked', true);
$('#bindConfigModal input[name="ip_rebind_enabled"][value="' + config.ip_rebind_enabled + '"]').prop('checked', true);
$('#bindConfigModal input[name="ip_rebind_limit"][value="' + config.ip_rebind_limit + '"]').prop('checked', true);
$('#bindConfigModal input[name="ip_free_count"]').val(config.ip_free_count);
$('#bindConfigModal input[name="ip_rebind_count"]').val(config.ip_rebind_count);
$('#bindConfigModal input[name="ip_rebind_deduct"]').val(config.ip_rebind_deduct);
// 打开静态弹窗
var bindConfigIndex = layer.open({
type: 1,
title: '绑定设置 - ' + obj.data.name,
area: ['650px', '600px'],
content: $('#bindConfigModal'),
btn: ['保存', '取消'],
yes: function (index, layero) {
var formData = {
uuid: obj.data.uuid,
machine_verify: parseInt($('#bindConfigModal input[name="machine_verify"]:checked').val()),
machine_rebind_enabled: parseInt($('#bindConfigModal input[name="machine_rebind_enabled"]:checked').val()),
machine_rebind_limit: parseInt($('#bindConfigModal input[name="machine_rebind_limit"]:checked').val()),
machine_free_count: parseInt($('#bindConfigModal input[name="machine_free_count"]').val()) || 0,
machine_rebind_count: parseInt($('#bindConfigModal input[name="machine_rebind_count"]').val()) || 0,
machine_rebind_deduct: parseInt($('#bindConfigModal input[name="machine_rebind_deduct"]').val()) || 0,
ip_verify: parseInt($('#bindConfigModal input[name="ip_verify"]:checked').val()),
ip_rebind_enabled: parseInt($('#bindConfigModal input[name="ip_rebind_enabled"]:checked').val()),
ip_rebind_limit: parseInt($('#bindConfigModal input[name="ip_rebind_limit"]:checked').val()),
ip_free_count: parseInt($('#bindConfigModal input[name="ip_free_count"]').val()) || 0,
ip_rebind_count: parseInt($('#bindConfigModal input[name="ip_rebind_count"]').val()) || 0,
ip_rebind_deduct: parseInt($('#bindConfigModal input[name="ip_rebind_deduct"]').val()) || 0
};
// 验证数据
if (isNaN(formData.machine_verify) || formData.machine_verify < 0 || formData.machine_verify > 1) {
layer.msg('请选择机器验证选项', { icon: 2 });
return;
}
if (isNaN(formData.machine_rebind_enabled) || formData.machine_rebind_enabled < 0 || formData.machine_rebind_enabled > 1) {
layer.msg('请选择机器重绑选项', { icon: 2 });
return;
}
if (isNaN(formData.machine_rebind_limit) || formData.machine_rebind_limit < 0 || formData.machine_rebind_limit > 1) {
layer.msg('请选择机器重绑限制', { icon: 2 });
return;
}
if (isNaN(formData.ip_verify) || formData.ip_verify < 0 || formData.ip_verify > 3) {
layer.msg('请选择IP地址验证选项', { icon: 2 });
return;
}
if (isNaN(formData.ip_rebind_enabled) || formData.ip_rebind_enabled < 0 || formData.ip_rebind_enabled > 1) {
layer.msg('请选择IP地址重绑选项', { icon: 2 });
return;
}
if (isNaN(formData.ip_rebind_limit) || formData.ip_rebind_limit < 0 || formData.ip_rebind_limit > 1) {
layer.msg('请选择IP地址重绑限制', { icon: 2 });
return;
}
// 发送更新请求
$.ajax({
url: '/admin/api/apps/update_bind_config',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(formData),
success: function (res) {
if (res.code === 0) {
layer.msg('绑定设置更新成功', { icon: 1 });
layer.close(index);
table.reload('appsTable');
} else {
layer.msg(res.msg || '更新绑定设置失败', { icon: 2 });
}
},
error: function () {
layer.msg('网络错误,请稍后重试', { icon: 2 });
2025-10-24 08:25:16 +08:00
}
});
},
btn2: function (index) {
layer.close(index);
},
success: function () {
// 重新渲染表单
form.render();
}
});
2025-10-26 14:48:02 +08:00
} else {
layer.msg(res.msg || '获取绑定设置失败', { icon: 2 });
}
},
error: function () {
layer.msg('获取绑定设置失败,请稍后重试', { icon: 2 });
}
});
} else if (menudata.id === 'register_settings') {
// 注册设置
$.ajax({
url: '/admin/api/apps/get_register_config?uuid=' + obj.data.uuid,
type: 'GET',
2025-10-26 14:48:02 +08:00
success: function (res) {
if (res.code === 0 && res.data) {
var config = res.data;
// 填充表单数据
$('#registerConfigModal input[name="register_enabled"][value="' + config.register_enabled + '"]').prop('checked', true);
$('#registerConfigModal input[name="register_limit_enabled"][value="' + config.register_limit_enabled + '"]').prop('checked', true);
$('#registerConfigModal input[name="register_limit_time"][value="' + config.register_limit_time + '"]').prop('checked', true);
$('#registerConfigModal input[name="register_count"]').val(config.register_count);
$('#registerConfigModal input[name="trial_enabled"][value="' + config.trial_enabled + '"]').prop('checked', true);
$('#registerConfigModal input[name="trial_limit_time"][value="' + config.trial_limit_time + '"]').prop('checked', true);
$('#registerConfigModal input[name="trial_duration"]').val(config.trial_duration);
// 打开静态弹窗
var registerConfigIndex = layer.open({
type: 1,
title: '注册设置 - ' + obj.data.name,
area: ['550px', '500px'],
content: $('#registerConfigModal'),
btn: ['保存', '取消'],
yes: function (index, layero) {
var formData = {
uuid: obj.data.uuid,
register_enabled: parseInt($('#registerConfigModal input[name="register_enabled"]:checked').val()),
register_limit_enabled: parseInt($('#registerConfigModal input[name="register_limit_enabled"]:checked').val()),
register_limit_time: parseInt($('#registerConfigModal input[name="register_limit_time"]:checked').val()),
register_count: parseInt($('#registerConfigModal input[name="register_count"]').val()) || 1,
trial_enabled: parseInt($('#registerConfigModal input[name="trial_enabled"]:checked').val()),
trial_limit_time: parseInt($('#registerConfigModal input[name="trial_limit_time"]:checked').val()),
trial_duration: parseInt($('#registerConfigModal input[name="trial_duration"]').val()) || 0
};
// 验证数据
if (isNaN(formData.register_enabled) || formData.register_enabled < 0 || formData.register_enabled > 1) {
layer.msg('请选择账号注册选项', { icon: 2 });
return;
2025-10-24 08:25:16 +08:00
}
if (isNaN(formData.register_limit_enabled) || formData.register_limit_enabled < 0 || formData.register_limit_enabled > 1) {
layer.msg('请选择注册限制选项', { icon: 2 });
return;
}
if (isNaN(formData.register_limit_time) || formData.register_limit_time < 0 || formData.register_limit_time > 1) {
layer.msg('请选择限制时间选项', { icon: 2 });
return;
}
if (isNaN(formData.register_count) || formData.register_count < 1) {
layer.msg('注册次数必须大于0', { icon: 2 });
return;
}
if (isNaN(formData.trial_enabled) || formData.trial_enabled < 0 || formData.trial_enabled > 1) {
layer.msg('请选择领取试用选项', { icon: 2 });
return;
}
if (isNaN(formData.trial_limit_time) || formData.trial_limit_time < 0 || formData.trial_limit_time > 1) {
layer.msg('请选择试用限制时间选项', { icon: 2 });
return;
}
if (isNaN(formData.trial_duration) || formData.trial_duration < 0) {
layer.msg('试用时间不能小于0', { icon: 2 });
return;
}
// 发送更新请求
$.ajax({
url: '/admin/api/apps/update_register_config',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(formData),
success: function (res) {
if (res.code === 0) {
layer.msg('注册设置更新成功', { icon: 1 });
layer.close(index);
table.reload('appsTable');
} else {
layer.msg(res.msg || '更新注册设置失败', { icon: 2 });
}
},
error: function () {
layer.msg('网络错误,请稍后重试', { icon: 2 });
}
});
},
btn2: function (index) {
layer.close(index);
},
success: function () {
// 重新渲染表单
form.render();
}
});
2025-10-26 14:48:02 +08:00
} else {
layer.msg(res.msg || '获取注册设置失败', { icon: 2 });
}
},
error: function () {
layer.msg('获取注册设置失败,请稍后重试', { icon: 2 });
}
});
}
},
align: 'right', // 右对齐弹出
style: 'box-shadow: 1px 1px 10px rgb(0 0 0 / 12%);' // 设置额外样式
});
}
});
// 批量删除
$('#btnBatchDeleteApps').on('click', function () {
const checkStatus = table.checkStatus('appsTable');
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/api/apps/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 });
appsTable.reload();
} else {
layer.msg(res.msg || '批量删除失败', { icon: 2 });
}
},
error: function (xhr) {
layer.msg(xhr.responseText || '批量删除失败', { icon: 2 });
}
});
layer.close(index);
});
});
// 批量启用
$('#btnBatchEnableApps').on('click', function () {
const checkStatus = table.checkStatus('appsTable');
const data = checkStatus.data;
if (data.length === 0) {
layer.msg('请选择要启用的应用', { icon: 2 });
return;
}
2025-10-24 00:09:45 +08:00
const ids = data.map(item => item.id);
$.ajax({
url: '/admin/api/apps/batch_update_status',
2025-10-24 00:09:45 +08:00
type: 'POST',
data: JSON.stringify({ ids: ids, status: 1 }),
2025-10-24 00:09:45 +08:00
contentType: 'application/json',
success: function (res) {
2025-10-24 00:09:45 +08:00
if (res.code === 0) {
layer.msg(res.msg, { icon: 1 });
2025-10-24 00:09:45 +08:00
appsTable.reload();
} else {
layer.msg(res.msg || '批量启用失败', { icon: 2 });
2025-10-24 00:09:45 +08:00
}
},
error: function (xhr) {
layer.msg(xhr.responseText || '批量启用失败', { icon: 2 });
2025-10-24 00:09:45 +08:00
}
});
});
// 批量禁用
$('#btnBatchDisableApps').on('click', function () {
const checkStatus = table.checkStatus('appsTable');
const data = checkStatus.data;
if (data.length === 0) {
layer.msg('请选择要禁用的应用', { icon: 2 });
return;
2025-10-24 00:09:45 +08:00
}
const ids = data.map(item => item.id);
$.ajax({
url: '/admin/api/apps/batch_update_status',
type: 'POST',
data: JSON.stringify({ ids: ids, status: 0 }),
contentType: 'application/json',
success: function (res) {
if (res.code === 0) {
layer.msg(res.msg, { icon: 1 });
appsTable.reload();
} else {
layer.msg(res.msg || '批量禁用失败', { icon: 2 });
}
},
error: function (xhr) {
layer.msg(xhr.responseText || '批量禁用失败', { icon: 2 });
2025-10-24 00:09:45 +08:00
}
});
2025-10-24 00:09:45 +08:00
});
2025-10-24 13:00:30 +08:00
// 应用状态switch开关事件监听
form.on('switch(app-status-switch)', function(data) {
const appId = data.elem.getAttribute('data-id');
const status = data.elem.checked ? 1 : 0;
$.ajax({
url: '/admin/api/apps/update_status',
type: 'POST',
data: JSON.stringify({ id: parseInt(appId), status: status }),
contentType: 'application/json',
success: function (res) {
if (res.code === 0) {
layer.msg(res.msg || '状态更新成功', { icon: 1 });
} else {
layer.msg(res.msg || '状态更新失败', { icon: 2 });
// 如果更新失败,恢复开关状态
data.elem.checked = !data.elem.checked;
form.render('checkbox');
}
},
error: function (xhr) {
layer.msg(xhr.responseText || '状态更新失败', { icon: 2 });
// 如果更新失败,恢复开关状态
data.elem.checked = !data.elem.checked;
form.render('checkbox');
}
});
});
// Tips提示功能已移至admin.js统一管理
2025-10-24 13:00:30 +08:00
});
2025-10-24 00:09:45 +08:00
});
</script>
</section>
{{ end }}