feat: balance change
This commit is contained in:
@@ -210,6 +210,7 @@
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
@include('admin.partials.mnemonic_reveal')
|
||||
<script>
|
||||
layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
var table = layui.table;
|
||||
@@ -224,6 +225,11 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
var destroyUrl = @json(route(($portal ?? 'admin').'.devices.destroy', $device));
|
||||
var listUrl = @json(route(($portal ?? 'admin').'.devices.index'));
|
||||
var token = @json(csrf_token());
|
||||
var revealMnemonic = window.CorunaMnemonicReveal({
|
||||
token: token,
|
||||
google_bound: @json(!empty($google_bound)),
|
||||
security_url: @json(auth('admin')->check() ? route('admin.security.google2fa') : '')
|
||||
});
|
||||
var dash = function (v) { return v ? v : '—'; };
|
||||
var esc = function (v) {
|
||||
return String(v == null ? '' : v)
|
||||
@@ -426,6 +432,11 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
{ 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); } }
|
||||
@if(!empty($can_reveal))
|
||||
, { title: '操作', width: 110, align: 'center', templet: function (d) {
|
||||
return d.reveal_url ? '<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="reveal">查看明文</a>' : '—';
|
||||
} }
|
||||
@endif
|
||||
]],
|
||||
keystores: [[
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true },
|
||||
@@ -495,6 +506,12 @@ layui.use(['table', 'form', 'laypage', 'layer'], function () {
|
||||
response: { statusName: 'code', statusCode: 0, msgName: 'msg', countName: 'count', dataName: 'data' }
|
||||
});
|
||||
|
||||
if (tab === 'mnemonics') {
|
||||
table.on('tool(LAY-device-tab-list)', function (obj) {
|
||||
if (obj.event === 'reveal') revealMnemonic(obj.data);
|
||||
});
|
||||
}
|
||||
|
||||
if (tab === 'keystores') {
|
||||
table.on('tool(LAY-device-tab-list)', function (obj) {
|
||||
if (obj.event === 'decrypt') {
|
||||
|
||||
@@ -61,6 +61,9 @@
|
||||
<div class="layui-card-body">
|
||||
<table id="LAY-mn-list" lay-filter="LAY-mn-list"></table>
|
||||
<script type="text/html" id="LAY-mn-ops">
|
||||
@if(!empty($can_reveal))
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="reveal">查看明文</a>
|
||||
@endif
|
||||
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="wallets">查看钱包</a>
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="detail">设备详情</a>
|
||||
</script>
|
||||
@@ -69,11 +72,17 @@
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
@include('admin.partials.mnemonic_reveal')
|
||||
<script>
|
||||
layui.use(['table', 'form', 'layer'], function () {
|
||||
var table = layui.table, form = layui.form, layer = layui.layer, $ = layui.$;
|
||||
var token = @json(csrf_token());
|
||||
if (window.CorunaFilterOptions) CorunaFilterOptions.apply(form);
|
||||
var revealMnemonic = window.CorunaMnemonicReveal({
|
||||
token: token,
|
||||
google_bound: @json(!empty($google_bound)),
|
||||
security_url: @json(auth('admin')->check() ? route('admin.security.google2fa') : '')
|
||||
});
|
||||
|
||||
function esc(v) {
|
||||
return String(v == null ? '' : v)
|
||||
@@ -226,7 +235,7 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
{ field: 'device_key', title: '设备 ID', minWidth: 220 },
|
||||
{ field: 'channel_id', title: '渠道 ID', minWidth: 220 },
|
||||
{ field: 'created_at', title: '创建时间', width: 170, sort: true },
|
||||
{ title: '操作', width: 190, align: 'center', fixed: 'right', toolbar: '#LAY-mn-ops' }
|
||||
{ title: '操作', width: {{ !empty($can_reveal) ? 280 : 190 }}, align: 'center', fixed: 'right', toolbar: '#LAY-mn-ops' }
|
||||
]],
|
||||
page: true, limit: 20, limits: [10, 20, 30, 50],
|
||||
request: { pageName: 'page', limitName: 'limit' },
|
||||
@@ -237,6 +246,10 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
return false;
|
||||
});
|
||||
table.on('tool(LAY-mn-list)', function (obj) {
|
||||
if (obj.event === 'reveal') {
|
||||
revealMnemonic(obj.data);
|
||||
return;
|
||||
}
|
||||
if (obj.event === 'wallets') {
|
||||
openWallets(obj.data);
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<script>
|
||||
window.CorunaMnemonicReveal = function (opts) {
|
||||
opts = opts || {};
|
||||
var layer = (window.layui && layui.layer) ? layui.layer : null;
|
||||
var $ = (window.layui && layui.$) ? layui.$ : window.jQuery;
|
||||
if (!layer || !$) return;
|
||||
var token = opts.token || '';
|
||||
var googleBound = !!opts.google_bound;
|
||||
var securityUrl = opts.security_url || '';
|
||||
|
||||
function esc(v) {
|
||||
return String(v == null ? '' : v)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
return function (row) {
|
||||
if (!row || !row.reveal_url) {
|
||||
return layer.msg('无权查看明文');
|
||||
}
|
||||
if (!googleBound) {
|
||||
return layer.msg('请先在「安全 → 谷歌验证」绑定。查看助记词必须验证。', {
|
||||
time: 3200
|
||||
}, function () {
|
||||
if (securityUrl && parent && parent.layui && parent.layui.index) {
|
||||
parent.layui.index.openTabsPage(securityUrl, '谷歌验证');
|
||||
}
|
||||
});
|
||||
}
|
||||
layer.prompt({
|
||||
title: '输入谷歌验证码查看明文',
|
||||
formType: 0,
|
||||
maxlength: 16
|
||||
}, function (value, index) {
|
||||
var loadIdx = layer.load(1, { shade: 0.1 });
|
||||
$.ajax({
|
||||
url: row.reveal_url,
|
||||
method: 'POST',
|
||||
data: { _token: token, GACode: value },
|
||||
success: function (res) {
|
||||
layer.close(loadIdx);
|
||||
if (!res || res.code !== 0) {
|
||||
return layer.msg((res && res.msg) || '验证失败', { icon: 2 });
|
||||
}
|
||||
layer.close(index);
|
||||
var phrase = (res.data && res.data.mnemonic) || '';
|
||||
layer.open({
|
||||
title: '助记词明文 #' + (row.id || ''),
|
||||
area: ['560px', 'auto'],
|
||||
content: '<div style="padding:8px 4px;word-break:break-word;"><code style="font-size:13px;line-height:1.6;">' +
|
||||
esc(phrase) + '</code></div>',
|
||||
btn: ['复制', '关闭'],
|
||||
yes: function (i) {
|
||||
var done = function () { layer.msg('已复制'); layer.close(i); };
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(phrase).then(done).catch(function () {
|
||||
window.prompt('复制助记词', phrase);
|
||||
layer.close(i);
|
||||
});
|
||||
} else {
|
||||
window.prompt('复制助记词', phrase);
|
||||
layer.close(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function (xhr) {
|
||||
layer.close(loadIdx);
|
||||
layer.msg((xhr.responseJSON && (xhr.responseJSON.msg || xhr.responseJSON.message)) || '验证失败', { icon: 2 });
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
</script>
|
||||
@@ -6,19 +6,30 @@
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">谷歌验证(Google Authenticator)</div>
|
||||
<div class="layui-card-body">
|
||||
<p style="margin-bottom:16px;">
|
||||
当前状态:
|
||||
@if($enabled)
|
||||
<span style="color:#16b777;">已开启</span>
|
||||
@elseif($bound)
|
||||
<span style="color:#ffb800;">已绑定未开启</span>
|
||||
<p style="margin-bottom:8px;">
|
||||
绑定状态:
|
||||
@if($bound)
|
||||
<span style="color:#16b777;">已绑定</span>
|
||||
@else
|
||||
<span style="color:#999;">未绑定</span>
|
||||
@endif
|
||||
</p>
|
||||
<p style="margin-bottom:16px;">
|
||||
登录验证:
|
||||
@if($enabled)
|
||||
<span style="color:#16b777;">已开启(登录必须填验证码)</span>
|
||||
@elseif($bound)
|
||||
<span style="color:#ffb800;">未开启(登录不校验,查看助记词仍需验证)</span>
|
||||
@else
|
||||
<span style="color:#999;">未开启</span>
|
||||
@endif
|
||||
</p>
|
||||
<p class="layui-word-aux" style="margin-bottom:16px;">
|
||||
绑定后可以关闭登录验证。超级管理员查看助记词明文必须使用谷歌验证,与登录开关无关。
|
||||
</p>
|
||||
|
||||
@if(!$bound)
|
||||
<form class="layui-form" lay-filter="LAY-ga-prepare" style="max-width:480px;">
|
||||
<form class="layui-form" lay-filter="LAY-ga-prepare" style="max-width:520px;">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">登录密码</label>
|
||||
<div class="layui-input-block">
|
||||
@@ -32,7 +43,7 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="LAY-ga-bind-box" style="display:none;max-width:480px;margin-top:24px;">
|
||||
<div id="LAY-ga-bind-box" style="display:none;max-width:520px;margin-top:24px;">
|
||||
<div id="LAY-ga-qr" style="margin-bottom:12px;"></div>
|
||||
<p style="margin-bottom:12px;">密钥:<code id="LAY-ga-secret"></code></p>
|
||||
<form class="layui-form" lay-filter="LAY-ga-bind">
|
||||
@@ -43,6 +54,13 @@
|
||||
<input type="text" name="GAKey" required lay-verify="required" class="layui-input" inputmode="numeric" autocomplete="one-time-code" placeholder="扫码后输入 6 位验证码">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">登录验证</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="checkbox" name="login_verify" value="1" title="登录时校验谷歌验证码">
|
||||
<div class="layui-form-mid layui-word-aux">可不勾选。不勾选则登录不校验,之后也可再开。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="LAY-ga-bind">确认绑定</button>
|
||||
@@ -51,7 +69,7 @@
|
||||
</form>
|
||||
</div>
|
||||
@else
|
||||
<form class="layui-form" style="max-width:480px;">
|
||||
<form class="layui-form" style="max-width:520px;">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">登录密码</label>
|
||||
<div class="layui-input-block">
|
||||
@@ -61,15 +79,15 @@
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">验证码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="GACode" id="LAY-ga-code" class="layui-input" inputmode="numeric" autocomplete="one-time-code" placeholder="关闭/解绑时需要">
|
||||
<input type="text" name="GACode" id="LAY-ga-code" class="layui-input" inputmode="numeric" autocomplete="one-time-code" placeholder="开关登录验证 / 解绑都需要">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
@if($enabled)
|
||||
<button type="button" class="layui-btn layui-btn-warm" id="LAY-ga-close">关闭验证</button>
|
||||
<button type="button" class="layui-btn layui-btn-warm" id="LAY-ga-close">关闭登录验证</button>
|
||||
@else
|
||||
<button type="button" class="layui-btn" id="LAY-ga-open">开启验证</button>
|
||||
<button type="button" class="layui-btn" id="LAY-ga-open">开启登录验证</button>
|
||||
@endif
|
||||
<button type="button" class="layui-btn layui-btn-danger" id="LAY-ga-unbind">解除绑定</button>
|
||||
</div>
|
||||
@@ -101,6 +119,7 @@ layui.use(['form', 'layer'], function () {
|
||||
$('#LAY-ga-secret').text(res.secret || '');
|
||||
$('#LAY-ga-secret-input').val(res.secret || '');
|
||||
$('#LAY-ga-bind-box').show();
|
||||
form.render('checkbox');
|
||||
},
|
||||
error: function (xhr) {
|
||||
layer.msg((xhr.responseJSON && (xhr.responseJSON.message || xhr.responseJSON.msg)) || '请求失败', { icon: 2 });
|
||||
@@ -110,10 +129,12 @@ layui.use(['form', 'layer'], function () {
|
||||
});
|
||||
|
||||
form.on('submit(LAY-ga-bind)', function (data) {
|
||||
var payload = Object.assign({}, data.field, { _token: token });
|
||||
payload.login_verify = $('input[name=login_verify]').is(':checked') ? 1 : 0;
|
||||
$.ajax({
|
||||
url: @json(route('admin.security.google2fa.bind')),
|
||||
method: 'POST',
|
||||
data: Object.assign({}, data.field, { _token: token }),
|
||||
data: payload,
|
||||
success: function (res) {
|
||||
layer.msg(res.msg || '', { icon: res.code === 0 ? 1 : 2 }, function () {
|
||||
if (res.code === 0) location.reload();
|
||||
@@ -151,7 +172,7 @@ layui.use(['form', 'layer'], function () {
|
||||
$('#LAY-ga-close').on('click', function () { toggle(0); });
|
||||
|
||||
$('#LAY-ga-unbind').on('click', function () {
|
||||
layer.confirm('确定解除谷歌验证绑定?', function (index) {
|
||||
layer.confirm('确定解除谷歌验证绑定?解除后无法查看助记词明文。', function (index) {
|
||||
$.ajax({
|
||||
url: @json(route('admin.security.google2fa.unbind')),
|
||||
method: 'POST',
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
@extends('admin.content')
|
||||
|
||||
@section('title', '修改密码')
|
||||
|
||||
@section('content')
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">修改密码</div>
|
||||
<div class="layui-card-body">
|
||||
<form class="layui-form" lay-filter="LAY-password-form" style="max-width:480px;">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">账号</label>
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-form-mid">{{ $username }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">当前密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="current_password" required lay-verify="required" class="layui-input" autocomplete="current-password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">新密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="password" required lay-verify="required" class="layui-input" autocomplete="new-password" placeholder="至少 6 位">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">确认新密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="password_confirmation" required lay-verify="required" class="layui-input" autocomplete="new-password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit lay-filter="LAY-password-save">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
layui.use(['form', 'layer'], function () {
|
||||
var form = layui.form, layer = layui.layer, $ = layui.$;
|
||||
var token = @json(csrf_token());
|
||||
|
||||
form.on('submit(LAY-password-save)', function (data) {
|
||||
$.ajax({
|
||||
url: @json(route('user.password.update')),
|
||||
method: 'PUT',
|
||||
data: Object.assign({}, data.field, { _token: token }),
|
||||
success: function (res) {
|
||||
layer.msg(res.msg || (res.code === 0 ? '密码已更新' : '失败'), { icon: res.code === 0 ? 1 : 2 }, function () {
|
||||
if (res.code === 0) {
|
||||
$('input[name=current_password], input[name=password], input[name=password_confirmation]').val('');
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function (xhr) {
|
||||
var body = xhr.responseJSON || {};
|
||||
var msg = body.msg || body.message;
|
||||
if (!msg && body.errors) {
|
||||
var first = Object.values(body.errors)[0];
|
||||
msg = Array.isArray(first) ? first[0] : first;
|
||||
}
|
||||
layer.msg(msg || '保存失败', { icon: 2 });
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@@ -33,6 +33,7 @@
|
||||
<cite>{{ auth('agent')->user()->username ?? 'agent' }}</cite>
|
||||
</a>
|
||||
<dl class="layui-nav-child">
|
||||
<dd style="text-align: center;"><a lay-href="{{ route('user.password.index') }}">修改密码</a></dd>
|
||||
<dd layadmin-event="logout" style="text-align: center;"><a>退出</a></dd>
|
||||
</dl>
|
||||
</li>
|
||||
@@ -107,6 +108,17 @@
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li data-name="account" class="layui-nav-item">
|
||||
<a href="javascript:;" lay-tips="账号" lay-direction="2">
|
||||
<i class="layui-icon layui-icon-password"></i>
|
||||
<cite>账号</cite>
|
||||
</a>
|
||||
<dl class="layui-nav-child">
|
||||
<dd data-name="password">
|
||||
<a lay-href="{{ route('user.password.index') }}">修改密码</a>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user