init
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
@extends('admin.content')
|
||||
|
||||
@section('title', '设备列表')
|
||||
|
||||
@section('content')
|
||||
@php $portal = $portal ?? 'admin'; @endphp
|
||||
<div class="layui-card">
|
||||
<form class="layui-form layui-card-header layuiadmin-card-header-auto" lay-filter="LAY-device-search">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">设备 ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="device_key" placeholder="设备 ID" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">渠道 ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="channel_id" placeholder="渠道 ID" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
@if ($portal === 'admin')
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">代理用户</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="agent_user_id">
|
||||
<option value="">全部</option>
|
||||
@foreach (($agents ?? []) as $a)
|
||||
<option value="{{ $a->id }}">{{ $a->username }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">设备型号</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="model" placeholder="iPhone9,1" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">IP</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="ip" placeholder="ip" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">iOS 版本</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="ios" placeholder="15.8" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">安装时间</label>
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-input-inline" style="width: 150px;">
|
||||
<input type="text" name="installed_from" id="LAY-device-installed-from" placeholder="开始日期" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-mid">-</div>
|
||||
<div class="layui-input-inline" style="width: 150px;">
|
||||
<input type="text" name="installed_to" id="LAY-device-installed-to" placeholder="结束日期" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layuiadmin-btn-list" lay-submit lay-filter="LAY-device-search">
|
||||
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
|
||||
</button>
|
||||
<button type="reset" class="layui-btn layuiadmin-btn-list" id="LAY-device-reset">
|
||||
<i class="layui-icon layui-icon-refresh-3 layuiadmin-button-btn"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="layui-card-body">
|
||||
<table id="LAY-device-list" lay-filter="LAY-device-list"></table>
|
||||
<script type="text/html" id="LAY-device-ops">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="detail"><i class="layui-icon layui-icon-form"></i> 详情</a>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
layui.use(['table', 'form', 'laydate'], function () {
|
||||
var table = layui.table;
|
||||
var form = layui.form;
|
||||
var laydate = layui.laydate;
|
||||
var $ = layui.$;
|
||||
var dash = function (v) { return v ? v : '—'; };
|
||||
var portal = @json($portal);
|
||||
|
||||
laydate.render({ elem: '#LAY-device-installed-from', type: 'date' });
|
||||
laydate.render({ elem: '#LAY-device-installed-to', type: 'date' });
|
||||
|
||||
table.render({
|
||||
elem: '#LAY-device-list',
|
||||
id: 'LAY-device-list',
|
||||
url: @json(route($portal.'.devices.data')),
|
||||
toolbar: true,
|
||||
defaultToolbar: ['filter', 'exports', 'print'],
|
||||
cols: [[
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||
{ field: 'device_id', title: '设备 ID', minWidth: 180, sort: true },
|
||||
{ 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); } },
|
||||
{ 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: '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' }
|
||||
]],
|
||||
page: true,
|
||||
limit: 15,
|
||||
limits: [10, 15, 20, 30, 50],
|
||||
height: 'full-220',
|
||||
text: { none: '暂无设备' },
|
||||
request: { pageName: 'page', limitName: 'limit' },
|
||||
response: { statusName: 'code', statusCode: 0, msgName: 'msg', countName: 'count', dataName: 'data' }
|
||||
});
|
||||
|
||||
form.on('submit(LAY-device-search)', function (data) {
|
||||
table.reload('LAY-device-list', {
|
||||
where: data.field,
|
||||
page: { curr: 1 }
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#LAY-device-reset').on('click', function () {
|
||||
setTimeout(function () {
|
||||
table.reload('LAY-device-list', {
|
||||
where: {
|
||||
device_key: '',
|
||||
channel_id: '',
|
||||
agent_user_id: '',
|
||||
model: '',
|
||||
ip: '',
|
||||
ios: '',
|
||||
installed_from: '',
|
||||
installed_to: ''
|
||||
},
|
||||
page: { curr: 1 }
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
|
||||
table.on('tool(LAY-device-list)', function (obj) {
|
||||
if (obj.event !== 'detail') return;
|
||||
var url = obj.data.detail_url;
|
||||
var title = '设备 #' + obj.data.id;
|
||||
try {
|
||||
if (parent && parent.layui && parent.layui.index) {
|
||||
parent.layui.index.openTabsPage(url, title);
|
||||
return;
|
||||
}
|
||||
} catch (e) {}
|
||||
location.href = url;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@@ -0,0 +1,317 @@
|
||||
@extends('admin.content')
|
||||
|
||||
@section('title', '设备 '.$device->device_id)
|
||||
|
||||
@section('content')
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-table" style="margin-bottom: 16px;">
|
||||
<tr>
|
||||
<th width="140">设备 ID</th>
|
||||
<td><code>{{ $device->device_id }}</code></td>
|
||||
<th width="140">渠道 ID</th>
|
||||
<td><code>{{ $device->channel_id ?: '—' }}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>设备型号</th>
|
||||
<td>{{ $device->device_model ?: '—' }}</td>
|
||||
<th>iOS 版本</th>
|
||||
<td>{{ $device->ios_version ?: '—' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>IP</th>
|
||||
<td colspan="3">{{ $device->ip ?: '—' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>User-Agent</th>
|
||||
<td colspan="3" class="wrap">{{ $device->user_agent ?: '—' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>安装 / 更新</th>
|
||||
<td colspan="3">{{ $device->created_at }} / {{ $device->updated_at }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="layui-tab layui-tab-brief" style="margin-bottom: 0;">
|
||||
<ul class="layui-tab-title">
|
||||
@php $tabs = [
|
||||
'wallets' => '钱包地址',
|
||||
'mnemonics' => '助记词',
|
||||
'photos' => '相册',
|
||||
'apps' => '已装 APP',
|
||||
'notes' => '备忘录',
|
||||
'events' => '日志',
|
||||
]; @endphp
|
||||
@foreach ($tabs as $key => $label)
|
||||
<li class="{{ $tab === $key ? 'layui-this' : '' }}">
|
||||
<a href="{{ route(($portal ?? 'admin').'.devices.show', [$device, 'tab' => $key]) }}">{{ $label }}</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="device-tab-body" style="padding-top: 16px;">
|
||||
@if ($tab === 'photos')
|
||||
<form class="layui-form" lay-filter="LAY-photo-filter" style="margin-bottom: 12px;">
|
||||
<div class="layui-form-item" style="margin-bottom: 0;">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label" style="width: auto;">敏感词</label>
|
||||
<div class="layui-input-inline" style="width: 180px;">
|
||||
<select name="sensitive">
|
||||
<option value="">全部</option>
|
||||
<option value="1">含敏感词 (x-hit>0)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="LAY-photo-grid" class="photo-grid"></div>
|
||||
<div id="LAY-photo-empty" style="display:none;color:#999;padding:12px 0;">暂无相册</div>
|
||||
<div id="LAY-photo-page" style="margin-top: 16px; text-align: right;"></div>
|
||||
@else
|
||||
@if ($tab === 'wallets')
|
||||
<form class="layui-form" lay-filter="LAY-wallet-filter" style="margin-bottom: 12px;">
|
||||
<div class="layui-form-item" style="margin-bottom: 0;">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label" style="width: auto;">Source</label>
|
||||
<div class="layui-input-inline" style="width: 180px;">
|
||||
<select name="source">
|
||||
<option value="">全部</option>
|
||||
@foreach ($addressSources as $src)
|
||||
<option value="{{ $src }}">{{ $src }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label" style="width: auto;">Chain</label>
|
||||
<div class="layui-input-inline" style="width: 160px;">
|
||||
<select name="chain_type">
|
||||
<option value="">全部</option>
|
||||
@foreach ($addressChains as $chain)
|
||||
<option value="{{ $chain }}">{{ $chain }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn" lay-submit lay-filter="LAY-wallet-search">筛选</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary" id="LAY-wallet-reset">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
<table id="LAY-device-tab-list" lay-filter="LAY-device-tab-list"></table>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
var table = layui.table;
|
||||
var form = layui.form;
|
||||
var laypage = layui.laypage;
|
||||
var layer = layui.layer;
|
||||
var $ = layui.$;
|
||||
var tab = @json($tab);
|
||||
var tabDataUrl = @json(route(($portal ?? 'admin').'.devices.tabData', $device));
|
||||
var dash = function (v) { return v ? v : '—'; };
|
||||
var esc = function (v) {
|
||||
return String(v == null ? '' : v)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"');
|
||||
};
|
||||
|
||||
if (tab === 'photos') {
|
||||
var photoState = { page: 1, limit: 15, sensitive: '' };
|
||||
|
||||
var renderPhotos = function () {
|
||||
$.getJSON(tabDataUrl, {
|
||||
tab: 'photos',
|
||||
page: photoState.page,
|
||||
limit: photoState.limit,
|
||||
sensitive: photoState.sensitive
|
||||
}, function (res) {
|
||||
var $grid = $('#LAY-photo-grid').empty();
|
||||
var list = (res && res.data) ? res.data : [];
|
||||
if (!list.length) {
|
||||
$('#LAY-photo-empty').show();
|
||||
} else {
|
||||
$('#LAY-photo-empty').hide();
|
||||
list.forEach(function (p) {
|
||||
var hit = (p.x_hit == null || p.x_hit === '') ? '—' : p.x_hit;
|
||||
var card = $(
|
||||
'<a class="photo-card" href="javascript:;">' +
|
||||
'<img alt="">' +
|
||||
'<div></div>' +
|
||||
'<div class="muted" style="font-size:12px;color:#888;"></div>' +
|
||||
'</a>'
|
||||
);
|
||||
card.find('img').attr('src', p.url).attr('alt', p.sha256 || '');
|
||||
card.find('div').eq(0).text((p.size == null ? '—' : p.size) + ' B · x-hit ' + hit);
|
||||
card.find('div').eq(1).text(
|
||||
'up ' + dash(p.upload_count) +
|
||||
' · idx ' + dash(p.process_index) +
|
||||
' · text ' + dash(p.text_count) +
|
||||
' · qr ' + dash(p.barcode_count)
|
||||
);
|
||||
card.on('click', function (e) {
|
||||
e.preventDefault();
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '相册预览',
|
||||
area: ['80%', '85%'],
|
||||
shadeClose: true,
|
||||
content: '<div style="padding:12px;text-align:center;background:#111;min-height:100%;box-sizing:border-box;">' +
|
||||
'<img src="' + p.url + '" style="max-width:100%;max-height:75vh;object-fit:contain;">' +
|
||||
'<div style="color:#ccc;margin-top:10px;font-size:12px;word-break:break-all;">' +
|
||||
esc(p.sha256 || '') + '</div></div>'
|
||||
});
|
||||
});
|
||||
$grid.append(card);
|
||||
});
|
||||
}
|
||||
|
||||
laypage.render({
|
||||
elem: 'LAY-photo-page',
|
||||
count: (res && res.count) ? res.count : 0,
|
||||
curr: photoState.page,
|
||||
limit: photoState.limit,
|
||||
limits: [10, 15, 20, 30, 50],
|
||||
layout: ['count', 'prev', 'page', 'next', 'limit'],
|
||||
jump: function (obj, first) {
|
||||
if (first) return;
|
||||
photoState.page = obj.curr;
|
||||
photoState.limit = obj.limit;
|
||||
renderPhotos();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
form.on('submit(LAY-photo-search)', function (data) {
|
||||
photoState.sensitive = data.field.sensitive || '';
|
||||
photoState.page = 1;
|
||||
renderPhotos();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#LAY-photo-reset').on('click', function () {
|
||||
setTimeout(function () {
|
||||
photoState.sensitive = '';
|
||||
photoState.page = 1;
|
||||
renderPhotos();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
form.render('select');
|
||||
renderPhotos();
|
||||
return;
|
||||
}
|
||||
|
||||
var colsMap = {
|
||||
wallets: [[
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||
{ field: 'source', title: 'Source', width: 140, sort: true, templet: function (d) { return dash(d.source); } },
|
||||
{ field: 'chain_type', title: 'Chain', width: 120, sort: true, templet: function (d) { return dash(d.chain_type); } },
|
||||
{ field: 'address', title: 'Address', minWidth: 220, sort: true, templet: function (d) { return '<code>' + esc(d.address) + '</code>'; } },
|
||||
{ field: 'usdt', title: 'USDT', width: 90 },
|
||||
{ field: 'trx', title: 'TRX', width: 90 },
|
||||
{ field: 'eth', title: 'ETH', width: 90 },
|
||||
{ field: 'btc', title: 'BTC', width: 100 },
|
||||
{ field: 'bnb', title: 'BNB', width: 90 },
|
||||
{ field: 'monitor', title: '监听', width: 70, templet: function (d) {
|
||||
return Number(d.monitor) === 1 ? '开' : '关';
|
||||
} },
|
||||
{ field: 'updated_at', title: '更新时间', width: 170, sort: true, templet: function (d) { return dash(d.updated_at); } }
|
||||
]],
|
||||
mnemonics: [[
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||
{ field: 'source', title: 'Source', width: 160, sort: true, templet: function (d) { return dash(d.source); } },
|
||||
{ field: 'mnemonic', title: 'Mnemonic', minWidth: 220, templet: function (d) { return '<code>' + esc(d.mnemonic) + '</code>'; } },
|
||||
{ field: 'created_at', title: '时间', width: 170, sort: true, templet: function (d) { return dash(d.created_at); } }
|
||||
]],
|
||||
apps: [[
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||
{ field: 'name', title: '名称', minWidth: 140, sort: true, templet: function (d) { return dash(d.name); } },
|
||||
{ field: 'bundle_id', title: 'Bundle', minWidth: 200, sort: true, templet: function (d) { return d.bundle_id ? '<code>' + esc(d.bundle_id) + '</code>' : '—'; } },
|
||||
{ field: 'version', title: '版本', width: 120, sort: true, templet: function (d) { return dash(d.version); } },
|
||||
{ field: 'is_wallet', title: 'Wallet', width: 100, sort: true, templet: function (d) {
|
||||
return d.is_wallet ? '<span class="tag-wallet">wallet</span>' : '—';
|
||||
} }
|
||||
]],
|
||||
notes: [[
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||
{ field: 'content', title: 'Content', minWidth: 320, templet: function (d) {
|
||||
return d.content ? '<span class="wrap">' + esc(d.content) + '</span>' : '—';
|
||||
} },
|
||||
{ field: 'created_at', title: '时间', width: 170, sort: true, templet: function (d) { return dash(d.created_at); } }
|
||||
]],
|
||||
events: [[
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||
{ field: 'created_at', title: '时间', width: 170, sort: true, templet: function (d) { return dash(d.created_at); } },
|
||||
{ field: 'event_name', title: 'Event', width: 160, sort: true, templet: function (d) { return d.event_name ? '<code>' + esc(d.event_name) + '</code>' : '—'; } },
|
||||
{ field: 'desc', title: 'Desc', minWidth: 180, templet: function (d) { return dash(d.desc); } },
|
||||
{ field: 'context', title: 'Context', minWidth: 220, templet: function (d) {
|
||||
return d.context ? '<span class="wrap">' + esc(d.context) + '</span>' : '—';
|
||||
} }
|
||||
]]
|
||||
};
|
||||
var emptyMap = {
|
||||
wallets: '暂无地址',
|
||||
mnemonics: '暂无助记词',
|
||||
apps: '暂无应用',
|
||||
notes: '暂无备忘录',
|
||||
events: '暂无日志'
|
||||
};
|
||||
|
||||
table.render({
|
||||
elem: '#LAY-device-tab-list',
|
||||
id: 'LAY-device-tab-list',
|
||||
url: tabDataUrl,
|
||||
where: { tab: tab },
|
||||
toolbar: true,
|
||||
defaultToolbar: ['filter', 'exports', 'print'],
|
||||
cols: colsMap[tab] || colsMap.wallets,
|
||||
page: true,
|
||||
limit: 15,
|
||||
limits: [10, 15, 20, 30, 50],
|
||||
text: { none: emptyMap[tab] || '暂无数据' },
|
||||
request: { pageName: 'page', limitName: 'limit' },
|
||||
response: { statusName: 'code', statusCode: 0, msgName: 'msg', countName: 'count', dataName: 'data' }
|
||||
});
|
||||
|
||||
if (tab === 'wallets') {
|
||||
form.render('select');
|
||||
form.on('submit(LAY-wallet-search)', function (data) {
|
||||
table.reload('LAY-device-tab-list', {
|
||||
where: {
|
||||
tab: 'wallets',
|
||||
source: data.field.source || '',
|
||||
chain_type: data.field.chain_type || ''
|
||||
},
|
||||
page: { curr: 1 }
|
||||
});
|
||||
return false;
|
||||
});
|
||||
$('#LAY-wallet-reset').on('click', function () {
|
||||
setTimeout(function () {
|
||||
table.reload('LAY-device-tab-list', {
|
||||
where: { tab: 'wallets', source: '', chain_type: '' },
|
||||
page: { curr: 1 }
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
Reference in New Issue
Block a user