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

1232 lines
57 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{ define "apps.html" }}
<section>
<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>
批量删除</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>
</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="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>
<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="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>
</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">
<label class="layui-form-label" style="cursor: pointer;" data-tips="app-name">应用名称</label>
<div class="layui-input-block">
<input type="text" name="name" placeholder="请输入应用名称" autocomplete="off" class="layui-input"
lay-verify="required" />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="cursor: pointer;" data-tips="app-version">应用版本</label>
<div class="layui-input-block">
<input type="text" name="version" placeholder="请输入应用版本默认1.0.0" autocomplete="off" class="layui-input" />
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="app-status">应用状态</label>
<div class="layui-input-block">
<input type="checkbox" name="status" lay-skin="switch" lay-text="启用|禁用" checked>
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="force-update">强制更新</label>
<div class="layui-input-block">
<input type="checkbox" name="force_update" lay-skin="switch" lay-text="开启|关闭">
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label" style="cursor: pointer;" data-tips="download-type">更新方式</label>
<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">
</div>
</div>
<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" />
</div>
</div>
</form>
</div>
<!-- 隐藏的表单弹层内容:多开配置 -->
<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" class="layui-input" placeholder="请输入"
lay-verify="required|number" min="1">
</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" class="layui-input" placeholder="请输入"
lay-verify="required|number" min="1">
</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" class="layui-input" placeholder="请输入允许的多开数量"
lay-verify="required|number" min="1">
</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" 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"
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="请输入重绑扣除时间(分钟)"
lay-verify="number" min="0">
</div>
</div>
<!-- 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" 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">
</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="请输入重绑扣除时间(分钟)"
lay-verify="number" min="0">
</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" class="layui-input" placeholder="请输入" lay-verify="required|number"
min="1">
</div>
</div>
<!-- 领取试用设置 -->
<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" class="layui-input" placeholder="请输入试用时间(分钟)" lay-verify="number"
min="0">
</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', '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.$;
// 格式化时间函数
function formatDateTime(dateStr) {
if (!dateStr) return '-';
return new Date(dateStr).toLocaleString();
}
// 渲染表格
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 || []
};
},
request: {
pageName: 'page', // 页码的参数名称默认page
limitName: 'page_size' // 每页数据量的参数名称默认limit
},
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: '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: 180 }
]]
});
// 搜索功能
$('#btnSearchApps').on('click', function () {
const search = $('input[name="search"]').val();
appsTable.reload({
where: {
search: search
},
page: {
curr: 1
}
});
});
// 重置搜索
$('#btnResetApps').on('click', function () {
$('#appFilterForm')[0].reset();
appsTable.reload({
where: {},
page: {
curr: 1
}
});
});
// 新增应用
$('#btnAddApp').on('click', function () {
$('#appForm')[0].reset();
$('input[name="id"]').val('');
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;
$.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();
},
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);
}
});
},
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',
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);
// 打开静态弹窗
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) {
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();
}
});
} 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 });
}
},
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 });
}
});
layer.close(index);
});
} else if (menudata.id === 'bind_settings') {
// 绑定设置
$.ajax({
url: '/admin/api/apps/get_bind_config?uuid=' + obj.data.uuid,
type: 'GET',
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 });
}
});
},
btn2: function (index) {
layer.close(index);
},
success: function () {
// 重新渲染表单
form.render();
}
});
} 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',
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;
}
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();
}
});
} 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;
}
const ids = data.map(item => item.id);
$.ajax({
url: '/admin/api/apps/batch_update_status',
type: 'POST',
data: JSON.stringify({ ids: ids, status: 1 }),
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 });
}
});
});
// 批量禁用
$('#btnBatchDisableApps').on('click', function () {
const checkStatus = table.checkStatus('appsTable');
const data = checkStatus.data;
if (data.length === 0) {
layer.msg('请选择要禁用的应用', { icon: 2 });
return;
}
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 });
}
});
});
// 应用状态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统一管理
});
});
</script>
</section>
{{ end }}