feat: new chain
This commit is contained in:
@@ -44,22 +44,34 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
var table = layui.table, form = layui.form, layer = layui.layer, $ = layui.$;
|
||||
var token = @json(csrf_token());
|
||||
|
||||
function esc(s) {
|
||||
return String(s == null ? '' : s)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<');
|
||||
}
|
||||
|
||||
table.render({
|
||||
elem: '#LAY-agent-list',
|
||||
id: 'LAY-agent-list',
|
||||
url: @json(route('admin.agents.data')),
|
||||
cols: [[
|
||||
{ field: 'id', title: 'ID', width: 70, sort: true },
|
||||
{ field: 'username', title: '账号', width: 140, sort: true },
|
||||
{ field: 'status', title: '状态', width: 90, templet: function (d) {
|
||||
{ field: 'username', title: '账号', width: 120, sort: true },
|
||||
{ field: 'status', title: '状态', width: 80, templet: function (d) {
|
||||
return d.status == 1
|
||||
? '<span style="color:#16a34a;font-weight:600;">启用</span>'
|
||||
: '<span style="color:#dc2626;font-weight:600;">禁用</span>';
|
||||
}},
|
||||
{ field: 'comment', title: '备注', minWidth: 120 },
|
||||
{ field: 'channels_count', title: '渠道链接数', width: 110 },
|
||||
{ field: 'created_at', title: '创建时间', width: 170, sort: true },
|
||||
{ field: 'updated_at', title: '更新时间', width: 170, sort: true },
|
||||
{ field: 'telegram_ready', title: 'TG', width: 70, templet: function (d) {
|
||||
return d.telegram_ready
|
||||
? '<span style="color:#16a34a;">已配</span>'
|
||||
: '<span style="color:#888;">未配</span>';
|
||||
}},
|
||||
{ field: 'chat_id', title: 'Chat ID', width: 140 },
|
||||
{ field: 'comment', title: '备注', minWidth: 100 },
|
||||
{ field: 'channels_count', title: '渠道数', width: 90 },
|
||||
{ field: 'created_at', title: '创建时间', width: 160, sort: true },
|
||||
{ title: '操作', width: 180, align: 'center', fixed: 'right', toolbar: '#LAY-agent-ops' }
|
||||
]],
|
||||
page: true, limit: 15, height: 'full-220',
|
||||
@@ -77,14 +89,19 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: title,
|
||||
area: ['460px', '360px'],
|
||||
area: ['520px', '520px'],
|
||||
content: '<form class="layui-form" style="padding:16px;" id="LAY-agent-form">' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">账号</label><div class="layui-input-block">' +
|
||||
'<input name="username" class="layui-input" ' + (values.id ? 'readonly' : '') + ' value="' + (values.username || '') + '"></div></div>' +
|
||||
'<input name="username" class="layui-input" ' + (values.id ? 'readonly' : '') + ' value="' + esc(values.username || '') + '"></div></div>' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">密码</label><div class="layui-input-block">' +
|
||||
'<input name="password" type="password" class="layui-input" placeholder="' + (values.id ? '留空不改' : '必填') + '"></div></div>' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">备注</label><div class="layui-input-block">' +
|
||||
'<input name="comment" class="layui-input" value="' + (values.comment || '').replace(/"/g, '"') + '"></div></div>' +
|
||||
'<input name="comment" class="layui-input" value="' + esc(values.comment || '') + '"></div></div>' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">Bot Token</label><div class="layui-input-block">' +
|
||||
'<input name="bot_token" class="layui-input" placeholder="代理 Telegram Bot Token" value="' + esc(values.bot_token || '') + '"></div></div>' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">Chat ID</label><div class="layui-input-block">' +
|
||||
'<input name="chat_id" class="layui-input" placeholder="代理通知群 chat_id" value="' + esc(values.chat_id || '') + '"></div></div>' +
|
||||
'<div class="layui-form-item"><div class="layui-input-block" style="color:#888;font-size:12px;line-height:1.5;">配置后该代理渠道通知发往此 Bot/群;指令仅可查本代理数据,不支持 /transfer。<br>Webhook: <code>/hooks/telegram/agent/{id}</code>,保存后执行 <code>php artisan telegram:set-webhook --agent={id}</code></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>' +
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
|
||||
@section('content')
|
||||
@php $portal = $portal ?? 'admin'; @endphp
|
||||
<style>
|
||||
.dash-metric { font-size: 28px; line-height: 1.2; }
|
||||
.dash-metric-hl { color: #009688; font-weight: 600; }
|
||||
.dash-metric-sep { color: #bbb; margin: 0 4px; font-weight: 400; }
|
||||
.dash-metric-total { color: #333; }
|
||||
</style>
|
||||
<div class="layui-card">
|
||||
<form class="layui-form layui-card-header layuiadmin-card-header-auto" lay-filter="LAY-dash-search">
|
||||
<div class="layui-form-item">
|
||||
@@ -47,54 +53,55 @@
|
||||
<div class="layui-col-md2">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">总设备数</div>
|
||||
<div class="layui-card-body" style="font-size:28px;" id="dash-total">—</div>
|
||||
<div class="layui-card-body dash-metric" id="dash-total">—</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md2">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">新增设备</div>
|
||||
<div class="layui-card-body" style="font-size:28px;" id="dash-new">—</div>
|
||||
<div class="layui-card-body dash-metric dash-metric-hl" id="dash-new">—</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md2">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">活跃设备</div>
|
||||
<div class="layui-card-body" style="font-size:28px;" id="dash-active">—</div>
|
||||
<div class="layui-card-body dash-metric" id="dash-active">—</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">页面 PV</div>
|
||||
<div class="layui-card-body" style="font-size:28px;" id="dash-pv">—</div>
|
||||
<div class="layui-card-body dash-metric" id="dash-pv">—</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md3">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">页面 UV</div>
|
||||
<div class="layui-card-body" style="font-size:28px;" id="dash-uv">—</div>
|
||||
<div class="layui-card-body dash-metric" id="dash-uv">—</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-word-aux" style="margin-top:4px;">有效口径:iOS < 17 且使用 Safari(左侧高亮 / 右侧为全部)</div>
|
||||
|
||||
<div class="layui-row layui-col-space15" style="margin-top:12px;">
|
||||
<div class="layui-col-md6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">按系统(PV / UV)</div>
|
||||
<div class="layui-card-header">按系统(PV / UV / 占比)</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-table" lay-size="sm">
|
||||
<thead><tr><th>系统</th><th>PV</th><th>UV</th></tr></thead>
|
||||
<tbody id="dash-by-os"><tr><td colspan="3">—</td></tr></tbody>
|
||||
<thead><tr><th>系统</th><th>PV</th><th>UV</th><th>占比</th></tr></thead>
|
||||
<tbody id="dash-by-os"><tr><td colspan="4">—</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">按浏览器(PV / UV)</div>
|
||||
<div class="layui-card-header">按 iOS 系统版本(PV / UV / 占比)</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-table" lay-size="sm">
|
||||
<thead><tr><th>浏览器</th><th>PV</th><th>UV</th></tr></thead>
|
||||
<tbody id="dash-by-browser"><tr><td colspan="3">—</td></tr></tbody>
|
||||
<thead><tr><th>版本</th><th>PV</th><th>UV</th><th>占比</th></tr></thead>
|
||||
<tbody id="dash-by-ios-version"><tr><td colspan="4">—</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -115,9 +122,16 @@ layui.use(['form'], function () {
|
||||
function fillRows(sel, rows) {
|
||||
var html = '';
|
||||
(rows || []).forEach(function (r) {
|
||||
html += '<tr><td>' + (r.label || '—') + '</td><td>' + r.pv + '</td><td>' + r.uv + '</td></tr>';
|
||||
var pct = (r.pct === undefined || r.pct === null) ? '—' : (r.pct + '%');
|
||||
html += '<tr><td>' + (r.label || '—') + '</td><td>' + r.pv + '</td><td>' + r.uv + '</td><td>' + pct + '</td></tr>';
|
||||
});
|
||||
$(sel).html(html || '<tr><td colspan="3">暂无</td></tr>');
|
||||
$(sel).html(html || '<tr><td colspan="4">暂无</td></tr>');
|
||||
}
|
||||
|
||||
function ratioHtml(effective, total) {
|
||||
return '<span class="dash-metric-hl">' + effective + '</span>' +
|
||||
'<span class="dash-metric-sep">/</span>' +
|
||||
'<span class="dash-metric-total">' + total + '</span>';
|
||||
}
|
||||
|
||||
function load(where) {
|
||||
@@ -126,10 +140,10 @@ layui.use(['form'], function () {
|
||||
$('#dash-total').text(res.data.total);
|
||||
$('#dash-new').text(res.data.new_count);
|
||||
$('#dash-active').text(res.data.active_count);
|
||||
$('#dash-pv').text(res.data.pv);
|
||||
$('#dash-uv').text(res.data.uv);
|
||||
$('#dash-pv').html(ratioHtml(res.data.effective_pv || 0, res.data.pv || 0));
|
||||
$('#dash-uv').html(ratioHtml(res.data.effective_uv || 0, res.data.uv || 0));
|
||||
fillRows('#dash-by-os', res.data.by_os);
|
||||
fillRows('#dash-by-browser', res.data.by_browser);
|
||||
fillRows('#dash-by-ios-version', res.data.by_ios_version);
|
||||
$('#dash-range').text('统计区间:' + res.data.from + ' ~ ' + res.data.to);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -84,13 +84,16 @@
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
layui.use(['table', 'form', 'laydate'], function () {
|
||||
layui.use(['table', 'form', 'laydate', 'layer'], function () {
|
||||
var table = layui.table;
|
||||
var form = layui.form;
|
||||
var laydate = layui.laydate;
|
||||
var layer = layui.layer;
|
||||
var $ = layui.$;
|
||||
var dash = function (v) { return v ? v : '—'; };
|
||||
var portal = @json($portal);
|
||||
var token = @json(csrf_token());
|
||||
var updateBase = @json(url('/'.$portal.'/devices'));
|
||||
|
||||
laydate.render({ elem: '#LAY-device-installed-from', type: 'date' });
|
||||
laydate.render({ elem: '#LAY-device-installed-to', type: 'date' });
|
||||
@@ -108,6 +111,11 @@ layui.use(['table', 'form', 'laydate'], function () {
|
||||
{ field: 'device_model', title: '设备型号', width: 130, sort: true, templet: function (d) { return dash(d.device_model); } },
|
||||
{ field: 'ios_version', title: 'iOS 版本', width: 110, sort: true, templet: function (d) { return dash(d.ios_version); } },
|
||||
{ field: 'ip', title: 'IP', width: 140, sort: true, templet: function (d) { return dash(d.ip); } },
|
||||
{ field: 'album_storage', title: '相册存储', width: 110, templet: function (d) {
|
||||
var on = Number(d.album_storage) === 1;
|
||||
return '<input type="checkbox" lay-skin="switch" lay-text="开|关" lay-filter="LAY-device-album-list"' +
|
||||
' data-id="' + d.id + '"' + (on ? ' checked' : '') + '>';
|
||||
} },
|
||||
{ field: 'created_at', title: '安装时间', width: 170, sort: true },
|
||||
{ field: 'updated_at', title: '更新时间', width: 170, sort: true },
|
||||
{ title: '操作', width: 100, align: 'center', fixed: 'right', toolbar: '#LAY-device-ops' }
|
||||
@@ -118,7 +126,33 @@ layui.use(['table', 'form', 'laydate'], function () {
|
||||
height: 'full-220',
|
||||
text: { none: '暂无设备' },
|
||||
request: { pageName: 'page', limitName: 'limit' },
|
||||
response: { statusName: 'code', statusCode: 0, msgName: 'msg', countName: 'count', dataName: 'data' }
|
||||
response: { statusName: 'code', statusCode: 0, msgName: 'msg', countName: 'count', dataName: 'data' },
|
||||
done: function () {
|
||||
form.render('checkbox');
|
||||
}
|
||||
});
|
||||
|
||||
form.on('switch(LAY-device-album-list)', function (obj) {
|
||||
var id = $(obj.elem).data('id');
|
||||
var on = obj.elem.checked ? 1 : 0;
|
||||
$.ajax({
|
||||
url: updateBase + '/' + id,
|
||||
method: 'POST',
|
||||
data: { album_storage: on, _method: 'PUT', _token: token },
|
||||
success: function (res) {
|
||||
if (!res || res.code !== 0) {
|
||||
obj.elem.checked = !obj.elem.checked;
|
||||
form.render('checkbox');
|
||||
return layer.msg((res && res.msg) || '保存失败');
|
||||
}
|
||||
layer.msg(on ? '已开启' : '已关闭');
|
||||
},
|
||||
error: function () {
|
||||
obj.elem.checked = !obj.elem.checked;
|
||||
form.render('checkbox');
|
||||
layer.msg('保存失败');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
form.on('submit(LAY-device-search)', function (data) {
|
||||
|
||||
@@ -20,7 +20,15 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th>IP</th>
|
||||
<td colspan="3">{{ $device->ip ?: '—' }}</td>
|
||||
<td>{{ $device->ip ?: '—' }}</td>
|
||||
<th>相册存储</th>
|
||||
<td>
|
||||
<form class="layui-form" lay-filter="LAY-device-album" style="margin:0;">
|
||||
<input type="checkbox" name="album_storage" lay-skin="switch" lay-text="开|关"
|
||||
lay-filter="LAY-device-album-storage"
|
||||
{{ $device->albumStorageEnabled() ? 'checked' : '' }}>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>User-Agent</th>
|
||||
@@ -66,6 +74,7 @@
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn" lay-submit lay-filter="LAY-photo-search">筛选</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary" id="LAY-photo-reset">重置</button>
|
||||
<button type="button" class="layui-btn layui-btn-danger" id="LAY-photo-clear">清理相册数据</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -122,6 +131,9 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
var $ = layui.$;
|
||||
var tab = @json($tab);
|
||||
var tabDataUrl = @json(route(($portal ?? 'admin').'.devices.tabData', $device));
|
||||
var updateUrl = @json(route(($portal ?? 'admin').'.devices.update', $device));
|
||||
var clearPhotosUrl = @json(route(($portal ?? 'admin').'.devices.photos.clear', $device));
|
||||
var token = @json(csrf_token());
|
||||
var dash = function (v) { return v ? v : '—'; };
|
||||
var esc = function (v) {
|
||||
return String(v == null ? '' : v)
|
||||
@@ -131,6 +143,29 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
.replace(/"/g, '"');
|
||||
};
|
||||
|
||||
form.on('switch(LAY-device-album-storage)', function (obj) {
|
||||
var on = obj.elem.checked ? 1 : 0;
|
||||
$.ajax({
|
||||
url: updateUrl,
|
||||
method: 'POST',
|
||||
data: { album_storage: on, _method: 'PUT', _token: token },
|
||||
success: function (res) {
|
||||
if (!res || res.code !== 0) {
|
||||
obj.elem.checked = !obj.elem.checked;
|
||||
form.render('checkbox');
|
||||
return layer.msg((res && res.msg) || '保存失败');
|
||||
}
|
||||
layer.msg(on ? '已开启相册存储' : '已关闭相册存储');
|
||||
},
|
||||
error: function () {
|
||||
obj.elem.checked = !obj.elem.checked;
|
||||
form.render('checkbox');
|
||||
layer.msg('保存失败');
|
||||
}
|
||||
});
|
||||
});
|
||||
form.render('checkbox');
|
||||
|
||||
if (tab === 'photos') {
|
||||
var photoState = { page: 1, limit: 15, sensitive: '' };
|
||||
|
||||
@@ -213,6 +248,33 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
}, 0);
|
||||
});
|
||||
|
||||
$('#LAY-photo-clear').on('click', function () {
|
||||
layer.confirm('确认清理该设备全部相册数据?将删除数据库记录及本地图片文件,且不可恢复。', {
|
||||
icon: 3,
|
||||
title: '清理相册'
|
||||
}, function (index) {
|
||||
layer.close(index);
|
||||
var loading = layer.load(1, { shade: 0.2 });
|
||||
$.ajax({
|
||||
url: clearPhotosUrl,
|
||||
method: 'POST',
|
||||
data: { _token: token },
|
||||
success: function (res) {
|
||||
layer.close(loading);
|
||||
if (!res || res.code !== 0) return layer.msg((res && res.msg) || '清理失败');
|
||||
var n = (res.data && res.data.deleted_rows) ? res.data.deleted_rows : 0;
|
||||
layer.msg('已清理 ' + n + ' 条相册记录');
|
||||
photoState.page = 1;
|
||||
renderPhotos();
|
||||
},
|
||||
error: function () {
|
||||
layer.close(loading);
|
||||
layer.msg('清理失败');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
form.render('select');
|
||||
renderPhotos();
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user