mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-08-30 12:45:32 +08:00
feat(mcp): MCP 密钥管理后台(CURD 重组 Base/App,密钥仅创建时明文展示)
This commit is contained in:
14
app/admin/controller/system/McpKey.php
Normal file
14
app/admin/controller/system/McpKey.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller\system;
|
||||
|
||||
use app\admin\service\annotation\ControllerAnnotation;
|
||||
use base\admin\controller\system\McpKeyBase;
|
||||
|
||||
/**
|
||||
* @ControllerAnnotation(title="MCP密钥管理",module="系统")
|
||||
* Class McpKey
|
||||
*/
|
||||
class McpKey extends McpKeyBase
|
||||
{
|
||||
}
|
||||
33
database/migrations/20260816203001_system_mcp_key.php
Normal file
33
database/migrations/20260816203001_system_mcp_key.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use think\migration\Migrator;
|
||||
|
||||
class SystemMcpKey extends Migrator
|
||||
{
|
||||
/**
|
||||
* MCP密钥表.
|
||||
*
|
||||
* 明文密钥永不出现在本表:
|
||||
* - key 存 SHA-256 哈希(char 64,唯一索引)
|
||||
* - key_prefix 存明文前 16 位(char 16,展示用)
|
||||
*/
|
||||
public function change()
|
||||
{
|
||||
$table = $this->table('system_mcp_key')
|
||||
->setComment('MCP密钥表')
|
||||
->addColumn('title', 'char', ['limit' => 50, 'null' => true, 'comment' => '密钥名称'])
|
||||
->addColumn('key', 'char', ['limit' => 64, 'null' => true, 'comment' => '密钥SHA-256哈希'])
|
||||
->addColumn('key_prefix', 'char', ['limit' => 16, 'null' => true, 'default' => '', 'comment' => '密钥前缀(展示用)'])
|
||||
->addColumn('bind_admin_id', 'biginteger', ['limit' => 11, 'null' => true, 'signed' => false, 'comment' => '绑定创建者ID'])
|
||||
->addColumn('status', 'integer', ['limit' => 11, 'null' => true, 'default' => 1, 'comment' => '状态 {radio} (0:禁用,1:启用)'])
|
||||
->addColumn('remark', 'char', ['limit' => 255, 'null' => true, 'default' => '', 'comment' => ''])
|
||||
->addColumn('use_num', 'biginteger', ['limit' => 11, 'null' => true, 'default' => 0, 'signed' => false, 'comment' => '调用次数'])
|
||||
->addColumn('last_use_time', 'integer', ['limit' => 11, 'null' => true, 'default' => 0, 'signed' => false, 'comment' => '最后使用时间'])
|
||||
->addColumn('create_time', 'integer', ['limit' => 11, 'null' => true, 'default' => 0, 'signed' => false, 'comment' => '创建时间'])
|
||||
->addColumn('update_time', 'integer', ['limit' => 11, 'null' => true, 'default' => 0, 'signed' => false, 'comment' => '更新时间'])
|
||||
->addColumn('delete_time', 'integer', ['limit' => 11, 'null' => true, 'default' => 0, 'signed' => false, 'comment' => '删除时间'])
|
||||
->addIndex(['key'], ['unique' => true, 'name' => 'key'])
|
||||
->addIndex(['delete_time'], ['name' => 'delete_time'])
|
||||
->create();
|
||||
}
|
||||
}
|
||||
21
database/migrations/20260816203002_system_mcp_key_node.php
Normal file
21
database/migrations/20260816203002_system_mcp_key_node.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
use think\migration\Migrator;
|
||||
|
||||
class SystemMcpKeyNode extends Migrator
|
||||
{
|
||||
/**
|
||||
* MCP密钥与节点关系表.
|
||||
*
|
||||
* 纯关系表:无任何时间戳字段。
|
||||
*/
|
||||
public function change()
|
||||
{
|
||||
$table = $this->table('system_mcp_key_node')
|
||||
->setComment('MCP密钥与节点关系表')
|
||||
->addColumn('key_id', 'biginteger', ['limit' => 11, 'null' => true, 'signed' => false, 'comment' => '密钥ID'])
|
||||
->addColumn('node', 'char', ['limit' => 100, 'null' => true, 'default' => '', 'comment' => ''])
|
||||
->addIndex(['key_id'], ['name' => 'key_id'])
|
||||
->create();
|
||||
}
|
||||
}
|
||||
25
database/migrations/20260816203003_system_mcp_log.php
Normal file
25
database/migrations/20260816203003_system_mcp_log.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use think\migration\Migrator;
|
||||
|
||||
class SystemMcpLog extends Migrator
|
||||
{
|
||||
/**
|
||||
* MCP调用日志表.
|
||||
*
|
||||
* 仅 create_time(写入后不变),无 update_time / delete_time。
|
||||
*/
|
||||
public function change()
|
||||
{
|
||||
$table = $this->table('system_mcp_log')
|
||||
->setComment('MCP调用日志表')
|
||||
->addColumn('key_id', 'biginteger', ['limit' => 11, 'null' => true, 'signed' => false, 'comment' => '密钥ID'])
|
||||
->addColumn('node', 'char', ['limit' => 100, 'null' => true, 'comment' => '调用节点'])
|
||||
->addColumn('arguments', 'text', ['null' => true, 'comment' => '参数摘要JSON'])
|
||||
->addColumn('is_success', 'integer', ['limit' => 1, 'null' => true, 'default' => 1, 'comment' => '是否成功:0=失败,1=成功'])
|
||||
->addColumn('cost_ms', 'integer', ['limit' => 11, 'null' => true, 'default' => 0, 'comment' => '耗时毫秒'])
|
||||
->addColumn('create_time', 'integer', ['limit' => 11, 'null' => true, 'default' => 0, 'signed' => false, 'comment' => '创建时间'])
|
||||
->addIndex(['key_id'], ['name' => 'key_id'])
|
||||
->create();
|
||||
}
|
||||
}
|
||||
142
extend/base/admin/controller/system/McpKeyBase.php
Normal file
142
extend/base/admin/controller/system/McpKeyBase.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace base\admin\controller\system;
|
||||
|
||||
use app\admin\model\SystemAdmin;
|
||||
use app\admin\service\annotation\ControllerAnnotation;
|
||||
use app\admin\service\annotation\NodeAnotation;
|
||||
use app\common\controller\AdminController;
|
||||
use think\App;
|
||||
|
||||
/**
|
||||
* Class McpKeyBase.
|
||||
* @ControllerAnnotation(title="MCP密钥管理")
|
||||
*/
|
||||
class McpKeyBase extends AdminController
|
||||
{
|
||||
use \app\admin\traits\Curd;
|
||||
|
||||
protected $sort = [
|
||||
'id' => 'desc',
|
||||
];
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
|
||||
$this->model = new \app\admin\model\SystemMcpKey();
|
||||
|
||||
$this->assign('select_list_status', $this->model::SELECT_LIST_STATUS, true);
|
||||
|
||||
// 行内修改仅允许 status;key/key_prefix/bind_admin_id 为敏感字段,一律排除
|
||||
$this->allowModifyFields = [
|
||||
'status',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @NodeAnotation(title="列表")
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isAjax()) {
|
||||
if (input('selectFields')) {
|
||||
return $this->selectList();
|
||||
}
|
||||
list($page, $limit, $where, $excludes, $request_options, $group) = $this->buildTableParames();
|
||||
$count = $this->model
|
||||
->where($where)
|
||||
->group($group)
|
||||
->count();
|
||||
$list = $this->model
|
||||
->where($where)
|
||||
->page($page, $limit)
|
||||
->order($this->sort)
|
||||
->group($group)
|
||||
->select();
|
||||
|
||||
// 附加创建者用户名:不用 withJoin(system_admin 与主表有 id/status 等同名字段会歧义),
|
||||
// 一次 IN 查询建立 id => username 映射后回填,避免 N+1。
|
||||
$adminIds = [];
|
||||
foreach ($list as $vo) {
|
||||
!empty($vo->bind_admin_id) && $adminIds[] = $vo->bind_admin_id;
|
||||
}
|
||||
$adminNames = empty($adminIds) ? [] : SystemAdmin::whereIn('id', array_unique($adminIds))->column('username', 'id');
|
||||
foreach ($list as $vo) {
|
||||
$vo->bind_admin_username = $adminNames[$vo->bind_admin_id] ?? '';
|
||||
}
|
||||
|
||||
$data = [
|
||||
'code' => 0,
|
||||
'msg' => '',
|
||||
'count' => $count,
|
||||
'data' => $list,
|
||||
];
|
||||
|
||||
return json($data);
|
||||
}
|
||||
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* @NodeAnotation(title="添加")
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$post = $this->request->post();
|
||||
// 表单仅提交 title/status/remark;密钥三要素与统计字段一律服务端生成,忽略客户端传入
|
||||
unset($post['id'], $post['key'], $post['key_prefix'], $post['bind_admin_id'], $post['use_num'], $post['last_use_time']);
|
||||
$rule = [
|
||||
'title|密钥名称' => 'require|max:50',
|
||||
];
|
||||
$this->validate($post, $rule);
|
||||
|
||||
// 明文密钥仅此一次出现在内存与响应中:不落库、不写日志
|
||||
$secretKey = 'sk-mcp-' . bin2hex(random_bytes(24));
|
||||
$post['key'] = hash('sha256', $secretKey);
|
||||
$post['key_prefix'] = substr($secretKey, 0, 16);
|
||||
$post['bind_admin_id'] = $this->getAdminId();
|
||||
try {
|
||||
$save = $this->model->save($post);
|
||||
} catch (\Exception $e) {
|
||||
$this->error('保存失败:' . $e->getMessage());
|
||||
}
|
||||
if ($save) {
|
||||
// data 携带明文密钥,前端弹层展示一次后即丢弃
|
||||
$this->success('保存成功', ['secret_key' => $secretKey]);
|
||||
}
|
||||
$this->error('保存失败');
|
||||
}
|
||||
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* @NodeAnotation(title="编辑")
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$row = $this->model->find($id);
|
||||
empty($row) && $this->error('数据不存在');
|
||||
if ($this->request->isPost()) {
|
||||
$post = $this->request->post();
|
||||
// 白名单:仅允许改 title/status/remark;key/key_prefix/bind_admin_id 及统计字段不可改
|
||||
$post = array_intersect_key($post, array_flip(['title', 'status', 'remark']));
|
||||
$rule = [
|
||||
'title|密钥名称' => 'require|max:50',
|
||||
];
|
||||
$this->validate($post, $rule);
|
||||
try {
|
||||
$save = $row->save($post);
|
||||
} catch (\Exception $e) {
|
||||
$this->error('保存失败:' . $e->getMessage());
|
||||
}
|
||||
$save ? $this->success('保存成功') : $this->error('保存失败');
|
||||
}
|
||||
$this->assign('row', $row);
|
||||
|
||||
return $this->fetch();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,33 @@ namespace base\admin\model;
|
||||
|
||||
use app\common\model\TimeModel;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $title 密钥名称
|
||||
* @property string $key 密钥SHA-256哈希
|
||||
* @property string $key_prefix 密钥前缀(展示用)
|
||||
* @property int $bind_admin_id 绑定创建者ID
|
||||
* @property int $status 状态 0:禁用,1:启用
|
||||
* @property string $remark 备注
|
||||
* @property int $use_num 调用次数
|
||||
* @property int $last_use_time 最后使用时间
|
||||
* @property int $create_time 创建时间
|
||||
*/
|
||||
class SystemMcpKeyBase extends TimeModel
|
||||
{
|
||||
protected $name = 'system_mcp_key';
|
||||
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
public const SELECT_LIST_STATUS = ['0' => '禁用', '1' => '启用'];
|
||||
|
||||
/**
|
||||
* 创建者(后台用户).
|
||||
* 仅按需显式调用时才查询;列表页请勿用 withJoin 预载
|
||||
* (system_admin 与主表存在 id/status 等同名字段,join 会产生歧义)。
|
||||
*/
|
||||
public function bindAdmin()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\SystemAdmin', 'bind_admin_id', 'id');
|
||||
}
|
||||
}
|
||||
|
||||
11
extend/base/admin/view/system/mcp_key/_common.js
Normal file
11
extend/base/admin/view/system/mcp_key/_common.js
Normal file
@@ -0,0 +1,11 @@
|
||||
var init = {
|
||||
tableElem: '#currentTable',
|
||||
tableRenderId: 'currentTableRenderId',
|
||||
indexUrl: 'system.mcp_key/index',
|
||||
addUrl: 'system.mcp_key/add' + location.search,
|
||||
editUrl: 'system.mcp_key/edit',
|
||||
readUrl: 'system.mcp_key/read',
|
||||
deleteUrl: 'system.mcp_key/delete',
|
||||
exportUrl: 'system.mcp_key/export',
|
||||
modifyUrl: 'system.mcp_key/modify',
|
||||
};
|
||||
40
extend/base/admin/view/system/mcp_key/add.html
Normal file
40
extend/base/admin/view/system/mcp_key/add.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<div class="layuimini-container">
|
||||
<form id="app-form" class="layui-form layuimini-form">
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">密钥名称</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入密钥名称" value="{$Request.param.title|default=''}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
{foreach $select_list_status as $k=>$v}
|
||||
<input type="radio" name="status" value="{$k}" title="{$v}" {in name="k" value="$Request.param.status"}checked=""{/in}>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">备注</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="remark" class="layui-textarea" placeholder="请输入备注">{$Request.param.remark|default=''}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<blockquote class="layui-elem-quote layui-quote-nm">
|
||||
密钥由服务端自动生成,提交后仅展示一次,请立即复制保存。
|
||||
</blockquote>
|
||||
|
||||
<div class="hr-line"></div>
|
||||
<div class="layui-form-item text-center">
|
||||
{notempty name='$Request.param.backTagId'}
|
||||
<div class="layui-btn layui-btn-sm page-back-button" layuimini-content-href="{$Request.param.backTagId}" data-back="1">返回</div>
|
||||
{/notempty}
|
||||
<button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit>确认</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary layui-btn-sm">重置</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
43
extend/base/admin/view/system/mcp_key/add.js
Normal file
43
extend/base/admin/view/system/mcp_key/add.js
Normal file
@@ -0,0 +1,43 @@
|
||||
$(function(){
|
||||
// add 成功响应的 data 携带一次性明文密钥(secret_key):
|
||||
// 用独立 layer.alert 展示 + 复制按钮,替代默认的"成功提示后直接关窗"流程。
|
||||
ua.listen(null, function (res) {
|
||||
var secretKey = res.data && res.data.secret_key ? res.data.secret_key : '';
|
||||
if (secretKey) {
|
||||
var html = ''
|
||||
+ '密钥已创建,<span style="color:#e74c3c;font-weight:bold;">仅展示一次,请立即复制保存</span>:'
|
||||
+ '<br><br>'
|
||||
+ '<span style="user-select:all;word-break:break-all;font-family:monospace;font-size:14px;background:#f2f2f2;padding:8px 12px;display:inline-block;max-width:100%;">' + secretKey + '</span>'
|
||||
+ ' <button type="button" class="layui-btn layui-btn-primary layui-btn-xs" data-toggle="copy-text" data-clipboard-text="' + secretKey + '"><i class="fa fa-copy"></i> 复制</button>'
|
||||
+ '<br><br>'
|
||||
+ '<span style="color:#999;">关闭后将无法再次查看明文,遗失只能删除后重建。</span>';
|
||||
layer.alert(html, {
|
||||
title: '密钥创建成功',
|
||||
icon: 1,
|
||||
area: ['520px', 'auto'],
|
||||
success: function (layero) {
|
||||
// 弹层内绑定复制按钮(ClipboardJS)
|
||||
ua.api.copyText(layero);
|
||||
}
|
||||
}, function (index) {
|
||||
layer.close(index);
|
||||
// 关闭密钥弹窗后再走默认的关窗/刷新流程
|
||||
if (ua.checkMobile()) {
|
||||
location.href = ua.url(init.indexUrl);
|
||||
} else {
|
||||
ua.api.closeCurrentOpen({ refreshTable: true });
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
// 兜底:无 secret_key 时走默认成功流程
|
||||
ua.msg.success(res.msg || '保存成功', function () {
|
||||
if (ua.checkMobile()) {
|
||||
location.href = ua.url(init.indexUrl);
|
||||
} else {
|
||||
ua.api.closeCurrentOpen({ refreshTable: true });
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
})
|
||||
40
extend/base/admin/view/system/mcp_key/edit.html
Normal file
40
extend/base/admin/view/system/mcp_key/edit.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<div class="layuimini-container">
|
||||
<form id="app-form" class="layui-form layuimini-form">
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">密钥名称</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入密钥名称" value="{$row.title|default=''}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
{foreach $select_list_status as $k=>$v}
|
||||
<input type="radio" name="status" value="{$k}" title="{$v}" {in name="k" value="$row.status"}checked=""{/in}>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">备注</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="remark" class="layui-textarea" placeholder="请输入备注">{$row.remark|raw|default=''}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<blockquote class="layui-elem-quote layui-quote-nm">
|
||||
密钥内容不支持修改;如需更换密钥,请删除后重新创建。
|
||||
</blockquote>
|
||||
|
||||
<div class="hr-line"></div>
|
||||
<div class="layui-form-item text-center">
|
||||
{notempty name='$Request.param.backTagId'}
|
||||
<div class="layui-btn layui-btn-sm page-back-button" layuimini-content-href="{$Request.param.backTagId}" data-back="1">返回</div>
|
||||
{/notempty}
|
||||
<button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit>确认</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary layui-btn-sm">重置</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
3
extend/base/admin/view/system/mcp_key/edit.js
Normal file
3
extend/base/admin/view/system/mcp_key/edit.js
Normal file
@@ -0,0 +1,3 @@
|
||||
$(function(){
|
||||
ua.listen();
|
||||
})
|
||||
12
extend/base/admin/view/system/mcp_key/index.html
Normal file
12
extend/base/admin/view/system/mcp_key/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<div class="layuimini-container">
|
||||
<div class="layuimini-main">
|
||||
<table id="currentTable" class="layui-table layui-hide"
|
||||
data-auth-index="{:auth('system.mcp_key/index')}"
|
||||
data-auth-add="{:auth('system.mcp_key/add')}"
|
||||
data-auth-edit="{:auth('system.mcp_key/edit')}"
|
||||
data-auth-delete="{:auth('system.mcp_key/delete')}"
|
||||
data-auth-modify="{:auth('system.mcp_key/modify')}"
|
||||
lay-filter="currentTable">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
52
extend/base/admin/view/system/mcp_key/index.js
Normal file
52
extend/base/admin/view/system/mcp_key/index.js
Normal file
@@ -0,0 +1,52 @@
|
||||
$(function(){
|
||||
ua.table.render({
|
||||
init: init,
|
||||
toolbar: ['refresh', 'add', 'delete'],
|
||||
cols: [[
|
||||
{type: 'checkbox'},
|
||||
{field: 'id', title: 'ID', width: 80},
|
||||
{field: 'title', title: '密钥名称', minWidth: 150},
|
||||
{
|
||||
// 密钥脱敏展示:前缀 + ****(明文与完整哈希均不出现在列表)
|
||||
field: 'key_prefix',
|
||||
title: '密钥',
|
||||
width: 200,
|
||||
search: false,
|
||||
templet: function (d) {
|
||||
return d.key_prefix ? d.key_prefix + '****' : '-';
|
||||
}
|
||||
},
|
||||
{field: 'bind_admin_username', title: '创建者', width: 120, search: false},
|
||||
{field: 'use_num', title: '调用次数', width: 100, search: false},
|
||||
{
|
||||
field: 'last_use_time',
|
||||
title: '最后使用时间',
|
||||
width: 170,
|
||||
search: false,
|
||||
templet: function (d) {
|
||||
if (!d.last_use_time || d.last_use_time == 0) {
|
||||
return '<span class="layui-text-em">未使用</span>';
|
||||
}
|
||||
var date = new Date(d.last_use_time * 1000);
|
||||
var Y = date.getFullYear();
|
||||
var m = ('0' + (date.getMonth() + 1)).slice(-2);
|
||||
var day = ('0' + date.getDate()).slice(-2);
|
||||
var H = ('0' + date.getHours()).slice(-2);
|
||||
var i = ('0' + date.getMinutes()).slice(-2);
|
||||
var s = ('0' + date.getSeconds()).slice(-2);
|
||||
return Y + '-' + m + '-' + day + ' ' + H + ':' + i + ':' + s;
|
||||
}
|
||||
},
|
||||
{field: 'status', search: 'select', selectList: ua.getDataBrage('select_list_status'), title: '状态', width: 100, templet: ua.table.switch},
|
||||
{field: 'remark', title: '备注', templet: ua.table.text},
|
||||
{
|
||||
width: 150, title: '操作', templet: ua.table.tool, fixed: 'right', operat: [
|
||||
'edit',
|
||||
'delete'
|
||||
]
|
||||
},
|
||||
]],
|
||||
});
|
||||
|
||||
ua.listen();
|
||||
})
|
||||
109
extend/base/admin/view/system/mcp_key/read.html
Normal file
109
extend/base/admin/view/system/mcp_key/read.html
Normal file
@@ -0,0 +1,109 @@
|
||||
<div class="layuimini-container detail-container">
|
||||
<div class="layuimini-main">
|
||||
<div class="layui-card detail-card">
|
||||
<div class="layui-card-header detail-header">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md9">
|
||||
<h2 class="detail-title">#{$row.id} {$row.title}</h2>
|
||||
<div class="detail-id">ID: {$row.id}</div>
|
||||
</div>
|
||||
<div class="layui-col-md3 text-right detail-actions">
|
||||
<button class="layui-btn layui-btn-primary" layuimini-content-href="{$Request.param.backTagId}" data-back="1">返回</button>
|
||||
<button class="layui-btn" onclick="location.href='{:url("edit", ["id" => $row.id])}'">编辑</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body detail-content">
|
||||
<div class="layui-row layui-col-space12">
|
||||
<!-- 左侧主体内容 -->
|
||||
<div class="layui-col-md8 detail-main">
|
||||
<div class="detail-field-group">
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">密钥名称</div>
|
||||
<div class="detail-field-value">
|
||||
{notempty name="row.title"}
|
||||
{$row.title}
|
||||
{else/}
|
||||
<span class="layui-text-em">暂无数据</span>
|
||||
{/notempty}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">密钥(脱敏)</div>
|
||||
<div class="detail-field-value">
|
||||
{notempty name="row.key_prefix"}
|
||||
{$row.key_prefix}****
|
||||
{else/}
|
||||
<span class="layui-text-em">暂无数据</span>
|
||||
{/notempty}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">绑定创建者ID</div>
|
||||
<div class="detail-field-value">
|
||||
{notempty name="row.bind_admin_id"}
|
||||
{$row.bind_admin_id}
|
||||
{else/}
|
||||
<span class="layui-text-em">暂无数据</span>
|
||||
{/notempty}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">备注</div>
|
||||
<div class="detail-field-value" style="white-space: pre-wrap;">
|
||||
{notempty name="row.remark"}
|
||||
{$row.remark|raw}
|
||||
{else/}
|
||||
<span class="layui-text-em">暂无内容</span>
|
||||
{/notempty}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">调用次数</div>
|
||||
<div class="detail-field-value">{$row.use_num|default=0}</div>
|
||||
</div>
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">最后使用时间</div>
|
||||
<div class="detail-field-value">
|
||||
{if condition="$row.last_use_time && $row.last_use_time > 0"}
|
||||
{:date('Y-m-d H:i:s', $row.last_use_time)}
|
||||
{else/}
|
||||
<span class="layui-text-em">未使用</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧基础信息 -->
|
||||
<div class="layui-col-md4 detail-side">
|
||||
<h3 class="detail-side-title">基础信息</h3>
|
||||
<div class="detail-field-group">
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">ID</div>
|
||||
<div class="detail-field-value">{$row.id}</div>
|
||||
</div>
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">状态</div>
|
||||
<div class="detail-field-value">
|
||||
<span class="layui-badge">{$select_list_status[$row.status]|default=''}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">创建时间</div>
|
||||
<div class="detail-field-value">
|
||||
{if condition="$row.create_time && $row.create_time > 0"}
|
||||
{:date('Y-m-d H:i:s', $row.create_time)}
|
||||
{else/}
|
||||
<span class="layui-text-em">暂无数据</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
22
extend/base/admin/view/system/mcp_key/read.js
Normal file
22
extend/base/admin/view/system/mcp_key/read.js
Normal file
@@ -0,0 +1,22 @@
|
||||
$(function(){
|
||||
// 删除数据
|
||||
window.deleteData = function(id) {
|
||||
layer.confirm('确定要删除这条数据吗?', {
|
||||
icon: 3,
|
||||
title: '提示'
|
||||
}, function(index) {
|
||||
$.post('{{:url("delete")}}', {id: id}, function(res) {
|
||||
if (res.code == 0) {
|
||||
layer.msg('删除成功', {icon: 1}, function() {
|
||||
location.href = '{{:url("index")}}';
|
||||
});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}, 'json');
|
||||
layer.close(index);
|
||||
});
|
||||
};
|
||||
|
||||
ua.listen();
|
||||
})
|
||||
Reference in New Issue
Block a user