mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-08-30 12:45:32 +08:00
refactor(timer): TimerConfig 控制器与视图迁移到 Base 层
控制器逻辑移入 extend/base/admin/controller/system/TimerConfigBase.php,app 层改为空壳继承。9 个视图文件迁移到 extend/base/admin/view/system/timer_config/。index.js 补 toolbar: ['refresh', 'export'] 声明(C 类页面,去掉默认的 add/delete)。 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
179
extend/base/admin/controller/system/TimerConfigBase.php
Normal file
179
extend/base/admin/controller/system/TimerConfigBase.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace base\admin\controller\system;
|
||||
|
||||
use app\common\controller\AdminController;
|
||||
use app\admin\service\annotation\ControllerAnnotation;
|
||||
use app\admin\service\annotation\NodeAnotation;
|
||||
use think\App;
|
||||
use think\facade\Cache;
|
||||
|
||||
/**
|
||||
* @ControllerAnnotation(title="定时任务协调配置表")
|
||||
*/
|
||||
class TimerConfigBase extends AdminController
|
||||
{
|
||||
use \app\admin\traits\Curd;
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
|
||||
$this->model = new \app\admin\model\SystemTimerConfig();
|
||||
|
||||
$this->assign('select_list_run_type', $this->model::SELECT_LIST_RUN_TYPE, true);
|
||||
$this->assign('select_list_status', $this->model::SELECT_LIST_STATUS, true);
|
||||
$this->assign('select_list_is_synced', $this->model::SELECT_LIST_IS_SYNCED, true);
|
||||
$this->assign('select_list_manual_trigger', $this->model::SELECT_LIST_MANUAL_TRIGGER, true);
|
||||
|
||||
// 允许通过行内修改的字段
|
||||
$this->allowModifyFields = [
|
||||
'status',
|
||||
'run_type',
|
||||
'concurrency',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @NodeAnotation(title="添加")
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$this->error('定时任务配置由系统同步生成,不支持手动添加');
|
||||
}
|
||||
|
||||
/**
|
||||
* @NodeAnotation(title="删除")
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
$this->error('定时任务配置由系统管理,不支持删除');
|
||||
}
|
||||
|
||||
/**
|
||||
* @NodeAnotation(title="编辑")
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$row = $this->model->find($id);
|
||||
empty($row) && $this->error('数据不存在');
|
||||
if ($this->request->isPost()) {
|
||||
$post = $this->request->post();
|
||||
// 只允许修改 run_type、status、concurrency
|
||||
$post = array_intersect_key($post, array_flip(['run_type', 'status', 'concurrency']));
|
||||
if (array_key_exists('concurrency', $post)) {
|
||||
// 空/null=继承代码默认写 NULL;正整数=覆盖;0或负数=拒绝(M5 防静默吞任务)
|
||||
$post['concurrency'] = $this->normalizeConcurrency($post['concurrency']);
|
||||
}
|
||||
try {
|
||||
$save = $row->save($post);
|
||||
} catch (\Exception $e) {
|
||||
$this->error('保存失败:' . $e->getMessage());
|
||||
}
|
||||
if ($save) {
|
||||
// D18:DB commit 后递增脏标记,触发 timer reload(edit 改 concurrency/run_type/status,与 trigger 一致)
|
||||
Cache::inc('timer_config_version');
|
||||
$this->success('保存成功');
|
||||
}
|
||||
$this->error('保存失败');
|
||||
}
|
||||
$this->assign('row', $row);
|
||||
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* @NodeAnotation(title="手动触发")
|
||||
*/
|
||||
public function trigger($id)
|
||||
{
|
||||
$this->checkPostRequest();
|
||||
$row = $this->model->find($id);
|
||||
if (!$row) {
|
||||
$this->error('数据不存在');
|
||||
}
|
||||
$trigger_node_id = $this->request->post('trigger_node_id');
|
||||
if (empty($trigger_node_id)) {
|
||||
$this->error('请选择节点');
|
||||
}
|
||||
try {
|
||||
// 手动触发跨 run_type、穿透 status(D19:调试工作流,关闭+手动触发+打开)
|
||||
$row->save([
|
||||
'manual_trigger' => 1,
|
||||
'trigger_node_id' => $trigger_node_id,
|
||||
'last_trigger_time' => time(),
|
||||
]);
|
||||
// D18:DB commit 成功后才递增 version,防脏读(commit 前递增→节点 reload 读旧值→永不 reload)
|
||||
Cache::inc('timer_config_version');
|
||||
} catch (\Exception $e) {
|
||||
$this->error('触发失败:' . $e->getMessage());
|
||||
}
|
||||
$this->success('触发成功,等待定时器执行');
|
||||
}
|
||||
|
||||
/**
|
||||
* @NodeAnotation(title="节点列表")
|
||||
*/
|
||||
public function hostList()
|
||||
{
|
||||
$list = \app\admin\model\SystemHost::where('status', 1)
|
||||
->field('node_id,is_master,ip_address,last_heartbeat_at')
|
||||
->order('is_master desc, node_id asc')
|
||||
->select();
|
||||
$this->success('', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NodeAnotation(title="属性修改")
|
||||
*/
|
||||
public function modify()
|
||||
{
|
||||
$this->checkPostRequest();
|
||||
$post = $this->request->post();
|
||||
$rule = [
|
||||
'id|ID' => 'require',
|
||||
'field|字段' => 'require',
|
||||
'value|值' => 'require',
|
||||
];
|
||||
$this->validate($post, $rule);
|
||||
if (!in_array($post['field'], $this->allowModifyFields)) {
|
||||
$this->error('该字段不允许修改:' . $post['field']);
|
||||
}
|
||||
// concurrency 行内修改必须为正整数(M5 防静默吞任务;继承默认请走 edit 表单传空值)
|
||||
if ($post['field'] === 'concurrency' && (!is_numeric($post['value']) || (int) $post['value'] < 1)) {
|
||||
$this->error('concurrency 必须是正整数');
|
||||
}
|
||||
$row = $this->model->find($post['id']);
|
||||
empty($row) && $this->error('数据不存在');
|
||||
$value = $post['field'] === 'concurrency' ? (int) $post['value'] : $post['value'];
|
||||
try {
|
||||
$save = $row->save([$post['field'] => $value]);
|
||||
} catch (\Exception $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
if ($save) {
|
||||
// D18:DB commit 后递增脏标记,触发 timer reload(行内改 concurrency/status/run_type,同 edit)
|
||||
Cache::inc('timer_config_version');
|
||||
}
|
||||
$this->success('保存成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 归一化 concurrency 入库值.
|
||||
* 空字符串/null → null(继承代码默认);正整数 → int;0或负数/非数字 → 拒绝.
|
||||
*
|
||||
* @param mixed $value 提交值
|
||||
* @return int|null
|
||||
*/
|
||||
protected function normalizeConcurrency($value)
|
||||
{
|
||||
if ($value === '' || $value === null) {
|
||||
return null;
|
||||
}
|
||||
if (!is_numeric($value) || (int) $value < 1) {
|
||||
$this->error('concurrency 必须是正整数或空(继承默认)');
|
||||
}
|
||||
|
||||
return (int) $value;
|
||||
}
|
||||
}
|
||||
12
extend/base/admin/view/system/timer_config/_common.js
Normal file
12
extend/base/admin/view/system/timer_config/_common.js
Normal file
@@ -0,0 +1,12 @@
|
||||
var init = {
|
||||
tableElem: '#currentTable',
|
||||
tableRenderId: 'currentTableRenderId',
|
||||
indexUrl: 'system.timer_config/index',
|
||||
addUrl: 'system.timer_config/add' + location.search,
|
||||
editUrl: 'system.timer_config/edit',
|
||||
readUrl: 'system.timer_config/read',
|
||||
deleteUrl: 'system.timer_config/delete',
|
||||
exportUrl: 'system.timer_config/export',
|
||||
modifyUrl: 'system.timer_config/modify',
|
||||
triggerUrl: 'system.timer_config/trigger',
|
||||
};
|
||||
70
extend/base/admin/view/system/timer_config/add.html
Normal file
70
extend/base/admin/view/system/timer_config/add.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<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="task_name" class="layui-input" lay-verify="required" placeholder="请输入任务名称" value="{$Request.param.task_name|default=''}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">运行类型:main/auto/all/manual</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="run_type" >
|
||||
<option value=''></option>
|
||||
{foreach $select_list_run_type as $k=>$v}
|
||||
<option value='{$k}' {in name="k" value="$Request.param.run_type"}selected=""{/in}>{$v}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态:0=停用,1=启用</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">
|
||||
<label class="layui-form-label">是否已同步:0=未同步,1=已同步</label>
|
||||
<div class="layui-input-block">
|
||||
{foreach $select_list_is_synced as $k=>$v}
|
||||
<input type="radio" name="is_synced" value="{$k}" title="{$v}" {in name="k" value="$Request.param.is_synced"}checked=""{/in}>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">最后执行节点ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="last_execute_node" class="layui-input" placeholder="请输入最后执行节点ID" value="{$Request.param.last_execute_node|default=''}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">最后执行时间戳</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="last_execute_time" data-date="" data-date-type="datetime" class="layui-input" placeholder="请输入最后执行时间戳" value="{$Request.param.last_execute_time|default='0'}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">手动触发标记:0=未触发,1=已触发</label>
|
||||
<div class="layui-input-block">
|
||||
{foreach $select_list_manual_trigger as $k=>$v}
|
||||
<input type="radio" name="manual_trigger" value="{$k}" title="{$v}" {in name="k" value="$Request.param.manual_trigger"}checked=""{/in}>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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/timer_config/add.js
Normal file
3
extend/base/admin/view/system/timer_config/add.js
Normal file
@@ -0,0 +1,3 @@
|
||||
$(function(){
|
||||
ua.listen();
|
||||
})
|
||||
86
extend/base/admin/view/system/timer_config/edit.html
Normal file
86
extend/base/admin/view/system/timer_config/edit.html
Normal file
@@ -0,0 +1,86 @@
|
||||
<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="task_name" class="layui-input layui-disabled" disabled value="{$row.task_name|default=''}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">运行类型</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="run_type" lay-verify="required">
|
||||
<option value=''></option>
|
||||
{foreach $select_list_run_type as $k=>$v}
|
||||
<option value='{$k}' {in name="k" value="$row.run_type"}selected=""{/in}>{$v}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</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">
|
||||
<label class="layui-form-label">并发数</label>
|
||||
<div class="layui-input-block" style="padding-top:8px;">
|
||||
<div class="layui-inline" style="margin-right:15px;">
|
||||
<input type="radio" name="concurrency_type" lay-filter="concurrency_type" value="default" title="默认(继承代码)" {if condition="$row.concurrency eq null"}checked{/if}>
|
||||
<input type="radio" name="concurrency_type" lay-filter="concurrency_type" value="custom" title="自定义" {if condition="$row.concurrency neq null"}checked{/if}>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<input type="number" name="concurrency" value="{$row.concurrency}" min="1" step="1" placeholder="正整数" class="layui-input" style="display:inline-block;width:120px;" lay-affix="concurrency_type">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">
|
||||
<span class="layui-badge layui-bg-gray">默认=继承代码</span>
|
||||
空值=继承;正整数=覆盖;0或负数=拒绝(防静默吞任务)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">同步状态</label>
|
||||
<div class="layui-input-block" style="padding-top: 8px;">
|
||||
{if $row.is_synced == 1}
|
||||
<span class="layui-badge layui-bg-green">已同步</span>
|
||||
{else/}
|
||||
<span class="layui-badge">未同步</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">最后执行节点</label>
|
||||
<div class="layui-input-block" style="padding-top: 8px;">
|
||||
{$row.last_execute_node|default='<span class="layui-text-em">暂无</span>'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">最后执行时间</label>
|
||||
<div class="layui-input-block" style="padding-top: 8px;">
|
||||
{notempty name="row.last_execute_time"}
|
||||
{$row.last_execute_time|date="Y-m-d H:i:s"}
|
||||
{else/}
|
||||
<span class="layui-text-em">暂无</span>
|
||||
{/notempty}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
36
extend/base/admin/view/system/timer_config/edit.js
Normal file
36
extend/base/admin/view/system/timer_config/edit.js
Normal file
@@ -0,0 +1,36 @@
|
||||
$(function(){
|
||||
var form = layui.form;
|
||||
|
||||
// ============ 并发数:默认/自定义切换联动 ============
|
||||
// 默认 → number input 禁用 + 提交时清空(写 NULL 继承)
|
||||
// 自定义 → number input 启用,必须正整数
|
||||
function syncConcurrencyInput() {
|
||||
var type = $('input[name="concurrency_type"]:checked').val();
|
||||
var $input = $('input[name="concurrency"]');
|
||||
if (type === 'default') {
|
||||
$input.prop('disabled', true).val('');
|
||||
} else {
|
||||
$input.prop('disabled', false);
|
||||
}
|
||||
form.render();
|
||||
}
|
||||
|
||||
form.on('radio(concurrency_type)', function () {
|
||||
syncConcurrencyInput();
|
||||
});
|
||||
|
||||
// 初始化(根据模板 checked 状态同步)
|
||||
syncConcurrencyInput();
|
||||
|
||||
// ============ 提交前归一化 ============
|
||||
// 默认 → concurrency=''(后端 normalizeConcurrency 写 NULL)
|
||||
// 自定义 → 保留输入值(后端校验正整数,0/负数/非数字被拒)
|
||||
ua.listen(function (dataField) {
|
||||
if (dataField.concurrency_type === 'default') {
|
||||
dataField.concurrency = '';
|
||||
}
|
||||
// 移除辅助字段,避免传给后端
|
||||
delete dataField.concurrency_type;
|
||||
return dataField;
|
||||
});
|
||||
});
|
||||
29
extend/base/admin/view/system/timer_config/index.html
Normal file
29
extend/base/admin/view/system/timer_config/index.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<div class="layuimini-container">
|
||||
<div class="layuimini-main">
|
||||
<!-- 节点叠加层:选择节点后显示归属/触发;不选=纯配置视图(配置基础层永远在) -->
|
||||
<div class="layui-form timer-node-filter" lay-filter="timerNodeFilter" style="margin: 10px 0;">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label" style="width:90px;">节点视角</label>
|
||||
<div class="layui-input-inline" style="width:240px;">
|
||||
<select name="node_filter" id="node_filter" lay-filter="node_filter">
|
||||
<option value="">全局配置(不选节点)</option>
|
||||
<!-- 动态填充:调 hostList 接口 -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline" style="line-height:38px;">
|
||||
<span class="layui-badge layui-bg-blue" id="node_hint" style="display:none;"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id="currentTable" class="layui-table layui-hide"
|
||||
data-auth-index="{:auth('system.timer_config/index')}"
|
||||
data-auth-edit="{:auth('system.timer_config/edit')}"
|
||||
data-auth-read="{:auth('system.timer_config/read')}"
|
||||
data-auth-export="{:auth('system.timer_config/export')}"
|
||||
data-auth-modify="{:auth('system.timer_config/modify')}"
|
||||
data-auth-trigger="{:auth('system.timer_config/trigger')}"
|
||||
lay-filter="currentTable">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
311
extend/base/admin/view/system/timer_config/index.js
Normal file
311
extend/base/admin/view/system/timer_config/index.js
Normal file
@@ -0,0 +1,311 @@
|
||||
$(function(){
|
||||
// ============ 节点叠加层状态(模块级) ============
|
||||
// currentNodeId: 当前选中的节点 ID;为空表示纯配置视图(配置基础层)
|
||||
// nodeMap: { node_id: {node_id, is_master, ip_address, last_heartbeat_at} }
|
||||
// onlineNodeCount: 在线节点总数(用于 all 模式全网预估提示)
|
||||
var currentNodeId = '';
|
||||
var nodeMap = {};
|
||||
var onlineNodeCount = 0;
|
||||
var tableId = init.tableRenderId;
|
||||
|
||||
// ============ 归属可视化(D19 + run_type + 主节点判断)============
|
||||
// 入参:run_type、status、当前选中节点是否主节点
|
||||
// 返回:{text, class} 用于渲染徽章
|
||||
function calcOwnership(runType, status, isMaster) {
|
||||
if (status == 0) {
|
||||
return { text: '已停用', cls: 'layui-badge layui-bg-gray' };
|
||||
}
|
||||
switch (runType) {
|
||||
case 'all':
|
||||
// 所有节点都跑(含当前节点)
|
||||
return { text: '当前自动', cls: 'layui-badge layui-bg-green' };
|
||||
case 'main':
|
||||
if (isMaster == 1) {
|
||||
return { text: '当前(主)自动', cls: 'layui-badge layui-bg-green' };
|
||||
}
|
||||
// 非主节点:此任务不会在当前节点调度
|
||||
return { text: '仅主节点', cls: 'layui-badge layui-bg-orange' };
|
||||
case 'auto':
|
||||
// 多节点竞争执行(谁抢到谁跑)
|
||||
return { text: '竞争', cls: 'layui-badge layui-bg-blue' };
|
||||
case 'manual':
|
||||
// 仅手动触发
|
||||
return { text: '仅手动', cls: 'layui-badge layui-bg-orange' };
|
||||
default:
|
||||
return { text: runType || '-', cls: 'layui-badge' };
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 加载节点列表(hostList 接口)============
|
||||
function loadNodeList(callback) {
|
||||
ua.request.get({
|
||||
url: ua.url('system.timer_config/hostList'),
|
||||
prefix: true,
|
||||
}, function (res) {
|
||||
var list = (res && res.data) ? res.data : [];
|
||||
onlineNodeCount = list.length;
|
||||
nodeMap = {};
|
||||
var $sel = $('#node_filter');
|
||||
// 保留首项(全局配置)
|
||||
$sel.find('option').not(':first').remove();
|
||||
$.each(list, function (i, item) {
|
||||
nodeMap[item.node_id] = item;
|
||||
var masterTag = item.is_master == 1 ? ' [主]' : '';
|
||||
var ip = item.ip_address || '';
|
||||
$sel.append('<option value="' + item.node_id + '">' + item.node_id + masterTag + ' (' + ip + ')</option>');
|
||||
});
|
||||
// 重新渲染 select
|
||||
if (window.layui && layui.form) {
|
||||
layui.form.render('select', 'timerNodeFilter');
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
callback(list);
|
||||
}
|
||||
}, function (res) {
|
||||
// hostList 失败:保持纯配置视图(不阻塞页面)
|
||||
$('#node_hint').attr('class', 'layui-badge').text('节点列表加载失败:' + (res && res.msg ? res.msg : '未知错误')).show();
|
||||
});
|
||||
}
|
||||
|
||||
// ============ 更新节点 hint 提示 ============
|
||||
function updateNodeHint() {
|
||||
var $hint = $('#node_hint');
|
||||
if (!currentNodeId) {
|
||||
$hint.hide();
|
||||
return;
|
||||
}
|
||||
var node = nodeMap[currentNodeId];
|
||||
if (!node) {
|
||||
$hint.hide();
|
||||
return;
|
||||
}
|
||||
var master = node.is_master == 1 ? '主节点' : '非主节点';
|
||||
var ip = node.ip_address || '';
|
||||
$hint.attr('class', 'layui-badge layui-bg-blue')
|
||||
.text('当前视角:' + node.node_id + ' / ' + master + (ip ? ' / ' + ip : ''))
|
||||
.show();
|
||||
}
|
||||
|
||||
// ============ 表格 cols(基础配置层 + 节点叠加列始终在,按节点上下文渲染)============
|
||||
var cols = [[
|
||||
{ type: 'checkbox' },
|
||||
{ field: 'id', title: 'ID', width: 80 },
|
||||
{ field: 'task_name', title: '任务名称', minWidth: 180 },
|
||||
{
|
||||
field: 'run_type',
|
||||
search: 'select',
|
||||
selectList: ua.getDataBrage('select_list_run_type'),
|
||||
title: '运行类型',
|
||||
width: 110,
|
||||
templet: function (d) {
|
||||
var map = ua.getDataBrage('select_list_run_type');
|
||||
return map[d.run_type] || d.run_type;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
search: 'select',
|
||||
selectList: ua.getDataBrage('select_list_status'),
|
||||
title: '状态',
|
||||
width: 90,
|
||||
templet: ua.table.switch
|
||||
},
|
||||
// 并发数:NULL=继承代码默认;正整数=覆盖;行内可改(edit:'text' → modify)
|
||||
{
|
||||
field: 'concurrency',
|
||||
title: '并发',
|
||||
width: 110,
|
||||
align: 'center',
|
||||
search: false,
|
||||
edit: 'text',
|
||||
templet: function (d) {
|
||||
if (d.concurrency === null || d.concurrency === undefined || d.concurrency === '') {
|
||||
return '<span class="layui-badge layui-bg-gray">默认</span>';
|
||||
}
|
||||
return d.concurrency;
|
||||
}
|
||||
},
|
||||
// 手动触发目标节点(只读展示)
|
||||
{
|
||||
field: 'trigger_node_id',
|
||||
title: '触发节点',
|
||||
width: 140,
|
||||
search: false,
|
||||
templet: function (d) {
|
||||
if (!d.trigger_node_id) {
|
||||
return '<span class="layui-text-em">-</span>';
|
||||
}
|
||||
return d.trigger_node_id;
|
||||
}
|
||||
},
|
||||
// 归属列(节点叠加):无选中节点时显示"全局"占位,有节点时按 run_type+主节点算
|
||||
{
|
||||
field: '_ownership',
|
||||
title: '归属',
|
||||
width: 130,
|
||||
align: 'center',
|
||||
search: false,
|
||||
templet: function (d) {
|
||||
if (!currentNodeId) {
|
||||
return '<span class="layui-badge layui-bg-gray">全局</span>';
|
||||
}
|
||||
var node = nodeMap[currentNodeId];
|
||||
var isMaster = node ? node.is_master : 0;
|
||||
var info = calcOwnership(d.run_type, d.status, isMaster);
|
||||
return '<span class="' + info.cls + '">' + info.text + '</span>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'is_synced',
|
||||
search: 'select',
|
||||
selectList: ua.getDataBrage('select_list_is_synced'),
|
||||
title: '同步状态',
|
||||
width: 100,
|
||||
templet: function (d) {
|
||||
if (d.is_synced == 1) {
|
||||
return '<span class="layui-badge layui-bg-green">已同步</span>';
|
||||
}
|
||||
return '<span class="layui-badge">未同步</span>';
|
||||
}
|
||||
},
|
||||
{ field: 'last_execute_node', title: '最后执行节点', width: 140, search: false },
|
||||
{
|
||||
field: 'last_execute_time',
|
||||
title: '最后执行时间',
|
||||
width: 170,
|
||||
search: 'range',
|
||||
templet: function (d) {
|
||||
if (!d.last_execute_time || d.last_execute_time == 0) {
|
||||
return '<span class="layui-text-em">暂无</span>';
|
||||
}
|
||||
var date = new Date(d.last_execute_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;
|
||||
}
|
||||
},
|
||||
{
|
||||
width: 250, title: '操作', templet: ua.table.tool, fixed: 'right', operat: [
|
||||
[{
|
||||
class: 'layui-btn layui-btn-primary layui-btn-xs',
|
||||
method: 'tab',
|
||||
field: 'id',
|
||||
text: '详情',
|
||||
title: '查看详情',
|
||||
auth: 'read',
|
||||
url: init.readUrl,
|
||||
icon: ''
|
||||
}],
|
||||
'edit',
|
||||
[{
|
||||
// 手动触发:定向选中节点(method:none,自处理 click + POST body 带 trigger_node_id)
|
||||
// D19:跨 run_type、穿透 status(调试工作流)
|
||||
class: 'layui-btn layui-btn-warm layui-btn-xs timer-trigger-btn',
|
||||
method: 'none',
|
||||
field: 'id',
|
||||
text: '触发',
|
||||
title: '手动触发到选中节点',
|
||||
auth: 'trigger',
|
||||
url: init.triggerUrl,
|
||||
icon: '',
|
||||
extend: '',
|
||||
// 节点叠加层启用:必须选中节点才显示触发按钮
|
||||
_if: function () {
|
||||
return currentNodeId !== '';
|
||||
}
|
||||
}]
|
||||
]
|
||||
},
|
||||
]];
|
||||
|
||||
// ============ 初始化表格(配置基础层,永远渲染)============
|
||||
// C 类页面:有限操作(add/delete 已在控制器禁用),仅保留 refresh + export
|
||||
ua.table.render({
|
||||
init: init,
|
||||
toolbar: ['refresh', 'export'],
|
||||
cols: cols,
|
||||
});
|
||||
|
||||
ua.listen();
|
||||
|
||||
// ============ 行内编辑(concurrency)校验 + all 模式预估提示 ============
|
||||
// ua.table 已通过 listenEdit 绑定 edit 事件并 POST 到 modifyUrl;
|
||||
// 这里挂钩 layui table edit 事件做前置校验 + all 模式预估提示
|
||||
if (window.layui && layui.table) {
|
||||
layui.table.on('edit(' + tableId + '_LayFilter)', function (obj) {
|
||||
if (obj.field === 'concurrency') {
|
||||
var val = obj.value;
|
||||
// 空:行内编辑不允许置空(继承默认请走 edit 表单),modify 接口也会拒绝
|
||||
if (val === '' || !/^[1-9]\d*$/.test(val)) {
|
||||
ua.msg.error('concurrency 必须是正整数(继承默认请走编辑表单)');
|
||||
layui.table.reloadData(tableId);
|
||||
return false;
|
||||
}
|
||||
// all 模式预估提示:全网并发 = 在线节点数 × 此值
|
||||
var rowData = obj.data;
|
||||
if (rowData.run_type === 'all' && onlineNodeCount > 0) {
|
||||
var total = onlineNodeCount * parseInt(val, 10);
|
||||
setTimeout(function () {
|
||||
layer.tips(
|
||||
'all 模式:全网预估 = ' + onlineNodeCount + ' 节点 × ' + val + ' = ' + total + ' 并发',
|
||||
'.layui-table-edit-tips',
|
||||
{ tips: [2, '#FF5722'], time: 6000 }
|
||||
);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============ 手动触发按钮(定向选中节点)============
|
||||
// method:'none' 不绑定默认行为,自处理 click
|
||||
$(document).on('click', '.timer-trigger-btn', function (e) {
|
||||
e.preventDefault();
|
||||
var taskId = $(this).attr('data-id');
|
||||
if (!taskId) {
|
||||
ua.msg.error('未找到任务 ID');
|
||||
return;
|
||||
}
|
||||
if (!currentNodeId) {
|
||||
// 兜底(_if 已阻止渲染,但用户切节点后表格未 reload 时仍可能进入)
|
||||
ua.msg.error('请先在顶部选择要触发的节点');
|
||||
return;
|
||||
}
|
||||
var node = nodeMap[currentNodeId] || {};
|
||||
var master = node.is_master == 1 ? '主节点' : '非主节点';
|
||||
var ip = node.ip_address || '';
|
||||
ua.msg.confirm(
|
||||
'确定将此任务触发到节点 [' + currentNodeId + ' / ' + master + (ip ? ' / ' + ip : '') + '] 执行?\n(跨 run_type、穿透 status)',
|
||||
function () {
|
||||
ua.request.post({
|
||||
url: ua.url(init.triggerUrl + '?id=' + taskId),
|
||||
prefix: true,
|
||||
data: { trigger_node_id: currentNodeId },
|
||||
}, function (res) {
|
||||
ua.msg.success(res.msg || '触发成功,等待定时器执行', function () {
|
||||
layui.table.reloadData(tableId);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// ============ 节点选择器联动 ============
|
||||
if (window.layui && layui.form) {
|
||||
layui.form.on('select(node_filter)', function (data) {
|
||||
currentNodeId = data.value || '';
|
||||
updateNodeHint();
|
||||
// 通知后端 reload(后端可选记录 node 上下文;归属列由前端 JS 算,不依赖后端)
|
||||
layui.table.reloadData(tableId, {
|
||||
where: { node_id: currentNodeId }
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ============ 首次加载节点列表(异步,不阻塞表格渲染)============
|
||||
loadNodeList();
|
||||
});
|
||||
103
extend/base/admin/view/system/timer_config/read.html
Normal file
103
extend/base/admin/view/system/timer_config/read.html
Normal file
@@ -0,0 +1,103 @@
|
||||
<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} {$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.task_name"}
|
||||
{$row.task_name}
|
||||
{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">
|
||||
{$select_list_run_type[$row.run_type]|default=''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">同步状态</div>
|
||||
<div class="detail-field-value">
|
||||
{if $row.is_synced == 1}
|
||||
<span class="layui-badge layui-bg-green">已同步</span>
|
||||
{else/}
|
||||
<span class="layui-badge">未同步</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">最后执行节点</div>
|
||||
<div class="detail-field-value">
|
||||
{notempty name="row.last_execute_node"}
|
||||
{$row.last_execute_node}
|
||||
{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.last_execute_time"}
|
||||
{$row.last_execute_time|date="Y-m-d H:i:s"}
|
||||
{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">
|
||||
<span class="layui-badge">{$select_list_manual_trigger[$row.manual_trigger]|default=''}</span>
|
||||
</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">
|
||||
{notempty name="row.create_time"}
|
||||
{$row.create_time|date="Y-m-d H:i:s"}
|
||||
{else/}
|
||||
<span class="layui-text-em">暂无数据</span>
|
||||
{/notempty}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
3
extend/base/admin/view/system/timer_config/read.js
Normal file
3
extend/base/admin/view/system/timer_config/read.js
Normal file
@@ -0,0 +1,3 @@
|
||||
$(function(){
|
||||
ua.listen();
|
||||
})
|
||||
11
extend/base/admin/view/system/timer_log/_common.js
Normal file
11
extend/base/admin/view/system/timer_log/_common.js
Normal file
@@ -0,0 +1,11 @@
|
||||
var init = {
|
||||
tableElem: '#currentTable',
|
||||
tableRenderId: 'currentTableRenderId',
|
||||
indexUrl: 'system.timer_log/index',
|
||||
addUrl: '',
|
||||
editUrl: '',
|
||||
readUrl: 'system.timer_log/read',
|
||||
deleteUrl: '',
|
||||
exportUrl: '',
|
||||
modifyUrl: '',
|
||||
};
|
||||
9
extend/base/admin/view/system/timer_log/index.html
Normal file
9
extend/base/admin/view/system/timer_log/index.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<div class="layuimini-container">
|
||||
<div class="layuimini-main">
|
||||
<table id="currentTable" class="layui-table layui-hide"
|
||||
data-auth-index="{:auth('system.timer_log/index')}"
|
||||
data-auth-read="{:auth('system.timer_log/read')}"
|
||||
lay-filter="currentTable">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
99
extend/base/admin/view/system/timer_log/index.js
Normal file
99
extend/base/admin/view/system/timer_log/index.js
Normal file
@@ -0,0 +1,99 @@
|
||||
$(function(){
|
||||
// 状态徽章模板
|
||||
var statusTemplet = function(d) {
|
||||
var statusMap = {
|
||||
'success': {text: '成功', color: '#5FB878', bgColor: '#e8f8ef'},
|
||||
'error': {text: '失败', color: '#FF5722', bgColor: '#ffe8e2'},
|
||||
'running': {text: '运行中', color: '#1E9FFF', bgColor: '#e2f1ff'},
|
||||
};
|
||||
var item = statusMap[d.status] || {text: d.status, color: '#999', bgColor: '#f5f5f5'};
|
||||
return '<span style="display:inline-block;padding:2px 10px;border-radius:3px;color:'+item.color+';background:'+item.bgColor+';font-size:12px;">'+item.text+'</span>';
|
||||
};
|
||||
|
||||
// 时间格式化模板
|
||||
var timeTemplet = function(d) {
|
||||
if (!d.start_time || d.start_time === 0) return '<span style="color:#999">-</span>';
|
||||
var date = new Date(d.start_time * 1000);
|
||||
var Y = date.getFullYear();
|
||||
var M = (date.getMonth()+1).toString().padStart(2,'0');
|
||||
var D = date.getDate().toString().padStart(2,'0');
|
||||
var h = date.getHours().toString().padStart(2,'0');
|
||||
var m = date.getMinutes().toString().padStart(2,'0');
|
||||
var s = date.getSeconds().toString().padStart(2,'0');
|
||||
return Y+'-'+M+'-'+D+' '+h+':'+m+':'+s;
|
||||
};
|
||||
|
||||
var endTimeTemplet = function(d) {
|
||||
if (!d.end_time || d.end_time === 0) return '<span style="color:#999">-</span>';
|
||||
var date = new Date(d.end_time * 1000);
|
||||
var Y = date.getFullYear();
|
||||
var M = (date.getMonth()+1).toString().padStart(2,'0');
|
||||
var D = date.getDate().toString().padStart(2,'0');
|
||||
var h = date.getHours().toString().padStart(2,'0');
|
||||
var m = date.getMinutes().toString().padStart(2,'0');
|
||||
var s = date.getSeconds().toString().padStart(2,'0');
|
||||
return Y+'-'+M+'-'+D+' '+h+':'+m+':'+s;
|
||||
};
|
||||
|
||||
// 耗时格式化模板
|
||||
var durationTemplet = function(d) {
|
||||
if (!d.duration || d.duration === 0) return '<span style="color:#999">-</span>';
|
||||
if (d.duration < 1000) {
|
||||
return d.duration + ' ms';
|
||||
}
|
||||
return (d.duration / 1000).toFixed(2) + ' s';
|
||||
};
|
||||
|
||||
// 错误信息截断模板
|
||||
var errorMsgTemplet = function(d) {
|
||||
if (!d.error_message) return '<span style="color:#999">-</span>';
|
||||
var text = d.error_message;
|
||||
if (text.length > 50) {
|
||||
return '<span title="'+text.replace(/"/g,'"').replace(/'/g,''')+'" style="cursor:pointer;" lay-event="showError">'+text.substring(0,50)+'...</span>';
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
ua.table.render({
|
||||
init: init,
|
||||
cols: [[
|
||||
{type: 'checkbox'},
|
||||
{field: 'id', title: 'ID', width: 80, sort: true},
|
||||
{field: 'task_name', title: '任务名称', width: 160, search: 'select', searchUrl: 'system.timer_log/index?selectFields=task_name'},
|
||||
{field: 'node_id', title: '节点ID', width: 120, search: 'select', searchUrl: 'system.timer_log/index?selectFields=node_id'},
|
||||
{field: 'run_type', title: '运行类型', width: 100},
|
||||
{field: 'start_time', title: '开始时间', width: 170, templet: timeTemplet, search: 'range'},
|
||||
{field: 'end_time', title: '结束时间', width: 170, templet: endTimeTemplet},
|
||||
{field: 'duration', title: '耗时', width: 100, templet: durationTemplet},
|
||||
{field: 'status', title: '状态', width: 100, templet: statusTemplet, search: 'select', selectList: {success:'成功', error:'失败', running:'运行中'}},
|
||||
{field: 'error_message', title: '错误信息', minWidth: 200, templet: errorMsgTemplet},
|
||||
{
|
||||
width: 100, title: '操作', templet: ua.table.tool, fixed: 'right', operat: [
|
||||
[{
|
||||
class: 'layui-btn layui-btn-primary layui-btn-xs',
|
||||
method: 'tab',
|
||||
field: 'id',
|
||||
text: '详情',
|
||||
title: '查看详情',
|
||||
auth: 'read',
|
||||
url: init.readUrl,
|
||||
icon: ''
|
||||
}]
|
||||
]
|
||||
},
|
||||
]],
|
||||
});
|
||||
|
||||
// 点击错误信息查看完整内容
|
||||
ua.table.on('tool(currentTable)', function(obj) {
|
||||
if (obj.event === 'showError') {
|
||||
var text = obj.data.error_message || '';
|
||||
layer.alert('<pre style="white-space:pre-wrap;word-break:break-all;max-height:400px;overflow-y:auto;">'+text.replace(/</g,'<').replace(/>/g,'>')+'</pre>', {
|
||||
title: '错误信息详情',
|
||||
area: ['600px', '400px']
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ua.listen();
|
||||
})
|
||||
139
extend/base/admin/view/system/timer_log/read.html
Normal file
139
extend/base/admin/view/system/timer_log/read.html
Normal file
@@ -0,0 +1,139 @@
|
||||
<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} {$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>
|
||||
</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.task_name"}
|
||||
{$row.task_name}
|
||||
{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.node_id"}
|
||||
{$row.node_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">
|
||||
{notempty name="row.run_type"}
|
||||
{$row.run_type}
|
||||
{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.start_time"}
|
||||
{:date('Y-m-d H:i:s', $row['start_time'])}
|
||||
{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.end_time"}
|
||||
{:date('Y-m-d H:i:s', $row['end_time'])}
|
||||
{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.duration"}
|
||||
{:php echo ($row['duration'] < 1000) ? $row['duration'].' ms' : round($row['duration']/1000, 2).' s';}
|
||||
{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.error_message"}
|
||||
{$row.error_message|raw}
|
||||
{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.concurrency_id"}
|
||||
{$row.concurrency_id}
|
||||
{else/}
|
||||
<span class="layui-text-em">暂无数据</span>
|
||||
{/notempty}
|
||||
</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">
|
||||
{switch name="row.status"}
|
||||
{case value="success"}<span style="display:inline-block;padding:2px 10px;border-radius:3px;color:#5FB878;background:#e8f8ef;font-size:12px;">成功</span>{/case}
|
||||
{case value="error"}<span style="display:inline-block;padding:2px 10px;border-radius:3px;color:#FF5722;background:#ffe8e2;font-size:12px;">失败</span>{/case}
|
||||
{case value="running"}<span style="display:inline-block;padding:2px 10px;border-radius:3px;color:#1E9FFF;background:#e2f1ff;font-size:12px;">运行中</span>{/case}
|
||||
{default /}<span style="color:#999">{$row.status}</span>
|
||||
{/switch}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-field-item">
|
||||
<div class="detail-field-label">创建时间</div>
|
||||
<div class="detail-field-value">
|
||||
{notempty name="row.create_time"}
|
||||
{$row.create_time|date="Y-m-d H:i:s"}
|
||||
{else/}
|
||||
<span class="layui-text-em">暂无数据</span>
|
||||
{/notempty}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
3
extend/base/admin/view/system/timer_log/read.js
Normal file
3
extend/base/admin/view/system/timer_log/read.js
Normal file
@@ -0,0 +1,3 @@
|
||||
$(function(){
|
||||
ua.listen();
|
||||
})
|
||||
Reference in New Issue
Block a user