feat: app

This commit is contained in:
hashbro
2026-09-28 06:12:47 +08:00
parent ad09fdff88
commit e0b06e0d24
14 changed files with 1609 additions and 275 deletions
+53 -8
View File
@@ -56,6 +56,7 @@ layui.use(['table', 'form', 'layer'], function () {
{ field: 'id', title: 'ID', width: 70, sort: true },
{ field: 'channel_id', title: '渠道 ID', minWidth: 220, sort: true },
{ field: 'builder_type', title: '类型', width: 80, templet: function (d) {
if (d.builder_type === 'app') return '<span style="color:#ea580c;font-weight:600;">App</span>';
return d.builder_type === 'new' ? '新版' : '旧版';
}},
];
@@ -63,6 +64,8 @@ layui.use(['table', 'form', 'layer'], function () {
cols.push({ field: 'agent_username', title: '代理', width: 120 });
}
cols = cols.concat([
{ field: 'app_name', title: 'App', width: 90, templet: function (d) { return d.app_name || '—'; } },
{ field: 'bundle_id', title: 'Bundle ID', minWidth: 200, templet: function (d) { return d.bundle_id ? '<code>' + d.bundle_id + '</code>' : '—'; } },
{ field: 'remark', title: '备注', minWidth: 140, templet: function (d) { return d.remark || '—'; } },
{ field: 'status', title: '状态', width: 90, templet: function (d) {
return d.status == 1
@@ -190,17 +193,41 @@ layui.use(['table', 'form', 'layer'], function () {
function openForm(title, values, creating) {
values = values || {};
var isApp = values.builder_type === 'app';
var agentBlock = isAdmin
? '<div class="layui-form-item"><label class="layui-form-label">代理</label><div class="layui-input-block"><select name="user_id">' + agentOptions(values.user_id) + '</select></div></div>'
: '';
// Version selector (only on create for admin)
var versionBlock = (isAdmin && creating)
? '<div class="layui-form-item"><label class="layui-form-label">版本</label><div class="layui-input-block">' +
'<select name="builder_type" id="LAY-ch-builder-type" lay-filter="LAY-ch-builder-type">' +
'<option value="new"' + (!isApp ? ' selected' : '') + '>新版(生成静态资源)</option>' +
'<option value="app"' + (isApp ? ' selected' : '') + '>App(仅记录,UUID 渠道 ID)</option>' +
'</select></div></div>'
: '';
// Channel ID block: App uses auto-generated UUID (read-only); new uses X.Y.ZZ input
var channelBlock = creating
? '<div class="layui-form-item"><label class="layui-form-label">渠道 ID</label><div class="layui-input-block">' +
'<input name="channel_id" class="layui-input" maxlength="6" id="LAY-ch-id-input" style="width:70%;display:inline-block;" value="' + (values.channel_id || '') + '" placeholder="例如 A.B.C1 或 3.1.07">' +
'<button type="button" class="layui-btn layui-btn-primary" id="LAY-ch-rand" style="margin-left:6px;">随机</button></div></div>'
? (isApp
? '<div class="layui-form-item"><label class="layui-form-label">渠道 ID</label><div class="layui-input-block">' +
'<input name="channel_id" class="layui-input" readonly id="LAY-ch-id-input" value="' + (values.channel_id || '') + '" placeholder="自动生成 UUID">' +
'<button type="button" class="layui-btn layui-btn-primary" id="LAY-ch-rand" style="margin-left:6px;">随机</button></div></div>'
: '<div class="layui-form-item"><label class="layui-form-label">渠道 ID</label><div class="layui-input-block">' +
'<input name="channel_id" class="layui-input" maxlength="6" id="LAY-ch-id-input" style="width:70%;display:inline-block;" value="' + (values.channel_id || '') + '" placeholder="例如 A.B.C1 或 3.1.07">' +
'<button type="button" class="layui-btn layui-btn-primary" id="LAY-ch-rand" style="margin-left:6px;">随机</button></div></div>')
: '<div class="layui-form-item"><label class="layui-form-label">渠道 ID</label><div class="layui-input-block"><input class="layui-input" readonly value="' + (values.channel_id || '') + '"></div></div>';
// App-specific fields: app_name + bundle_id
var appFieldsBlock = (isApp || (creating && isAdmin))
? '<div class="layui-form-item LAY-ch-app-only" style="' + (isApp ? '' : 'display:none;') + '"><label class="layui-form-label">App 名称</label><div class="layui-input-block">' +
'<input name="app_name" class="layui-input" value="' + (values.app_name || '').replace(/"/g, '&quot;') + '" placeholder="例如 Ai"></div></div>' +
'<div class="layui-form-item LAY-ch-app-only" style="' + (isApp ? '' : 'display:none;') + '"><label class="layui-form-label">Bundle ID</label><div class="layui-input-block">' +
'<input name="bundle_id" class="layui-input" value="' + (values.bundle_id || '').replace(/"/g, '&quot;') + '" placeholder="例如 aai.AiAi168168AiAi.app"></div></div>'
: '';
var templateBlock = (isAdmin && creating)
? '<div class="layui-form-item" id="LAY-ch-template-item"><label class="layui-form-label">页面模板</label><div class="layui-input-block">' +
? '<div class="layui-form-item LAY-ch-new-only" id="LAY-ch-template-item"' + (isApp ? ' style="display:none;"' : '') + '><label class="layui-form-label">页面模板</label><div class="layui-input-block">' +
'<select name="support_template">' +
'<option value="blank"' + ((values.support_template || 'blank') === 'blank' ? ' selected' : '') + '>blank(空白页)</option>' +
'<option value="test"' + (values.support_template === 'test' ? ' selected' : '') + '>test(加载页 / 15s 倒计时)</option>' +
@@ -211,23 +238,41 @@ layui.use(['table', 'form', 'layer'], function () {
layer.open({
type: 1,
title: title,
area: ['560px', creating ? (isAdmin ? '500px' : '380px') : '420px'],
area: ['560px', creating ? (isAdmin ? '560px' : '380px') : '420px'],
content: '<form class="layui-form" style="padding:16px;" id="LAY-ch-form" lay-filter="LAY-ch-form">' +
channelBlock + agentBlock + templateBlock +
versionBlock + channelBlock + agentBlock + templateBlock + appFieldsBlock +
'<div class="layui-form-item"><label class="layui-form-label">备注</label><div class="layui-input-block">' +
'<input name="remark" class="layui-input" value="' + (values.remark || '').replace(/"/g, '&quot;') + '"></div></div>' +
'<div class="layui-form-item"><label class="layui-form-label">状态</label><div class="layui-input-block">' +
'<input type="checkbox" name="status_switch" lay-skin="switch" lay-text="启用|禁用" ' + ((values.status == null || values.status == 1) ? 'checked' : '') + '>' +
'</div></div>' +
(creating && isAdmin ? '<div class="layui-form-item"><div class="layui-input-block" style="color:#888;font-size:12px;">新建将调用新版 builder 生成静态资源。代理最多 ' + maxPerAgent + ' 条。</div></div>' : '') +
(creating && isAdmin ? '<div class="layui-form-item"><div class="layui-input-block" style="color:#888;font-size:12px;">新版将调用 builder 生成静态资源;App 仅创建数据库记录。代理最多 ' + maxPerAgent + ' 条。</div></div>' : '') +
'</form>',
success: function () {
form.render();
$('#LAY-ch-rand').on('click', function () {
$.getJSON(@json(route('admin.channels.randomId')), function (res) {
var bt = $('#LAY-ch-builder-type').val();
$.getJSON(@json(route('admin.channels.randomId')) + '?builder_type=' + bt, function (res) {
if (res.code === 0) $('#LAY-ch-form input[name=channel_id]').val(res.data.channel_id);
});
});
// Auto-generate UUID when switching to App
form.on('select(LAY-ch-builder-type)', function (data) {
var isAppNow = data.value === 'app';
$('.LAY-ch-app-only').toggle(isAppNow);
$('.LAY-ch-new-only').toggle(!isAppNow);
var \$input = $('#LAY-ch-id-input');
if (isAppNow) {
\$input.attr('readonly', true).attr('maxlength', '').attr('placeholder', '自动生成 UUID');
if (!\$input.val()) {
$.getJSON(@json(route('admin.channels.randomId')) + '?builder_type=app', function (res) {
if (res.code === 0) \$input.val(res.data.channel_id);
});
}
} else {
\$input.removeAttr('readonly').attr('maxlength', '6').attr('placeholder', '例如 A.B.C1 或 3.1.07').val('');
}
});
},
btn: ['保存', '取消'],
yes: function (index) {
+1
View File
@@ -19,6 +19,7 @@
.tag-chain{display:inline-block;color:#fff;padding:0 6px;border-radius:2px;font-size:12px;line-height:20px}
.tag-chain-coruna{background:#0d9488}
.tag-chain-darksword{background:#7c3aed}
.tag-chain-app{background:#ea580c}
.photo-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:12px}
.photo-card{display:block;position:relative;background:#fff;padding:8px;text-align:center;color:#333;border:1px solid #f0f0f0}
.photo-card img{width:100%;height:120px;object-fit:cover;background:#eee}
@@ -20,6 +20,7 @@
<option value="">全部</option>
<option value="1">Coruna</option>
<option value="2">DarkSword</option>
<option value="3">App</option>
</select>
</div>
</div>
@@ -132,9 +133,11 @@ layui.use(['table', 'form', 'laydate', 'layer'], function () {
{ field: 'id', title: 'ID', width: 80, sort: true },
{ field: 'device_id', title: '设备 ID', minWidth: 180, sort: true },
{ field: 'chain', title: '利用链', width: 120, templet: function (d) {
var ds = Number(d.chain) === 2;
return '<span class="tag-chain ' + (ds ? 'tag-chain-darksword' : 'tag-chain-coruna') + '">' +
(ds ? 'DarkSword' : 'Coruna') + '</span>';
var c = Number(d.chain);
var cls = 'tag-chain-coruna', label = 'Coruna';
if (c === 2) { cls = 'tag-chain-darksword'; label = 'DarkSword'; }
else if (c === 3) { cls = 'tag-chain-app'; label = 'App'; }
return '<span class="tag-chain ' + cls + '">' + label + '</span>';
} },
{ field: 'channel_id', title: '渠道 ID', width: 140, sort: true, templet: function (d) { return dash(d.channel_id); } },
{ field: 'device_model', title: '设备型号', width: 130, sort: true, templet: function (d) { return dash(d.device_model); } },
@@ -21,6 +21,8 @@
<td colspan="3">
@if ($device->isDarkSword())
<span class="tag-chain tag-chain-darksword">DarkSword</span>
@elseif ($device->isApp())
<span class="tag-chain tag-chain-app">App</span>
@else
<span class="tag-chain tag-chain-coruna">Coruna</span>
@endif