mirror of
https://github.com/acepanel/panel.git
synced 2026-02-05 20:47:18 +08:00
特性(计划任务):新增计划任务模块
This commit is contained in:
@@ -4,6 +4,8 @@ namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
use App\Models\Cron;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
@@ -16,6 +18,21 @@ class Kernel extends ConsoleKernel
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
$schedule->command('monitor')->everyMinute();
|
||||
// 查询所有计划任务
|
||||
$crons = Cron::all();
|
||||
foreach ($crons as $cron) {
|
||||
$schedule->exec('bash /www/server/cron/'.$cron->shell)->withoutOverlapping()->cron($cron->time)->appendOutputTo('/www/server/cron/logs/'.$cron->id.'.log')->when(function (
|
||||
) use ($cron) {
|
||||
return (boolean) $cron->status;
|
||||
})->after(function () use ($cron) {
|
||||
$cron->updated_at = now();
|
||||
$cron->save();
|
||||
})->onSuccess(function () use ($cron) {
|
||||
shell_exec('echo "'.Carbon::now()->toDateTimeString().' 任务执行成功" >> /www/server/cron/logs/'.$cron->id.'.log');
|
||||
})->onFailure(function () use ($cron) {
|
||||
shell_exec('echo "'.Carbon::now()->toDateTimeString().' 任务执行失败" >> /www/server/cron/logs/'.$cron->id.'.log');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
205
app/Http/Controllers/Api/CronsController.php
Normal file
205
app/Http/Controllers/Api/CronsController.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
/**
|
||||
* 耗子Linux面板 - 计划任务控制器
|
||||
* @author 耗子
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Cron;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class CronsController extends Controller
|
||||
{
|
||||
/**
|
||||
* 面板计划任务列表
|
||||
*/
|
||||
public function getList(Request $request): JsonResponse
|
||||
{
|
||||
$limit = $request->input('limit', 10);
|
||||
|
||||
$crons = Cron::query()->orderBy('id', 'desc')->paginate($limit);
|
||||
$cronData = [];
|
||||
|
||||
foreach ($crons as $k => $v) {
|
||||
// 格式化时间
|
||||
$cronData[$k]['id'] = $v['id'];
|
||||
$cronData[$k]['name'] = $v['name'];
|
||||
$cronData[$k]['status'] = $v['status'];
|
||||
$cronData[$k]['type'] = $v['type'];
|
||||
$cronData[$k]['time'] = $v['time'];
|
||||
$cronData[$k]['shell'] = $v['shell'];
|
||||
$cronData[$k]['script'] = @file_get_contents('/www/server/cron/'.$v['shell']);
|
||||
$cronData[$k]['created_at'] = Carbon::create($v['created_at'])->toDateTimeString();
|
||||
$cronData[$k]['updated_at'] = Carbon::create($v['updated_at'])->toDateTimeString();
|
||||
}
|
||||
|
||||
$data['code'] = 0;
|
||||
$data['msg'] = 'success';
|
||||
$data['count'] = $crons->total();
|
||||
$data['data'] = $cronData;
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加计划任务
|
||||
*/
|
||||
public function add(Request $request): JsonResponse
|
||||
{
|
||||
// 消毒
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'name' => 'required|max:255',
|
||||
'time' => ['required', 'regex:/^((\*|\d+|\d+-\d+|\d+\/\d+|\d+-\d+\/\d+|\*\/\d+)(\,(\*|\d+|\d+-\d+|\d+\/\d+|\d+-\d+\/\d+|\*\/\d+))*\s?){5}$/'],
|
||||
'script' => 'required',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
// 将script写入shell文件
|
||||
$shellDir = '/www/server/cron/';
|
||||
$shellLogDir = '/www/server/cron/logs/';
|
||||
if (!is_dir($shellDir)) {
|
||||
mkdir($shellDir, 0755, true);
|
||||
}
|
||||
if (!is_dir($shellLogDir)) {
|
||||
mkdir($shellLogDir, 0755, true);
|
||||
}
|
||||
$shellFile = uniqid().'.sh';
|
||||
file_put_contents($shellDir.$shellFile, $credentials['script']);
|
||||
|
||||
$cron = new Cron();
|
||||
$cron->name = $credentials['name'];
|
||||
$cron->status = 1;
|
||||
$cron->type = '脚本';
|
||||
$cron->time = $credentials['time'];
|
||||
$cron->shell = $shellFile;
|
||||
$cron->save();
|
||||
|
||||
$data['code'] = 0;
|
||||
$data['msg'] = 'success';
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改计划任务
|
||||
*/
|
||||
public function edit(Request $request): JsonResponse
|
||||
{
|
||||
// 消毒
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'id' => 'required|integer',
|
||||
'name' => 'required|max:255',
|
||||
'time' => ['required', 'regex:/^((\*|\d+|\d+-\d+|\d+\/\d+)(\,(\*|\d+|\d+-\d+|\d+\/\d+))*\s?){5}$/'],
|
||||
'script' => 'required',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
$cron = Cron::query()->find($credentials['id']);
|
||||
$cron->name = $credentials['name'];
|
||||
$cron->time = $credentials['time'];
|
||||
// 将script写入shell文件
|
||||
$shellDir = '/www/server/cron/';
|
||||
$shellLogDir = '/www/server/cron/logs/';
|
||||
if (!is_dir($shellDir)) {
|
||||
mkdir($shellDir, 0755, true);
|
||||
}
|
||||
if (!is_dir($shellLogDir)) {
|
||||
mkdir($shellLogDir, 0755, true);
|
||||
}
|
||||
$shellFile = $cron->shell;
|
||||
file_put_contents($shellDir.$shellFile, $credentials['script']);
|
||||
$cron->save();
|
||||
|
||||
$data['code'] = 0;
|
||||
$data['msg'] = 'success';
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除计划任务
|
||||
*/
|
||||
public function delete(Request $request): JsonResponse
|
||||
{
|
||||
// 消毒
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'id' => 'required|integer',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
$cron = Cron::query()->find($credentials['id']);
|
||||
// 删除shell文件
|
||||
$shellDir = '/www/server/cron/';
|
||||
$shellFile = $cron->shell;
|
||||
@unlink($shellDir.$shellFile);
|
||||
// 删除日志文件
|
||||
$shellLogDir = '/www/server/cron/logs/';
|
||||
$shellLogFile = $shellFile.'.log';
|
||||
@unlink($shellLogDir.$shellLogFile);
|
||||
$cron->delete();
|
||||
|
||||
$data['code'] = 0;
|
||||
$data['msg'] = 'success';
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改计划任务状态
|
||||
*/
|
||||
public function setStatus(Request $request): JsonResponse
|
||||
{
|
||||
// 消毒
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'id' => 'required|integer',
|
||||
'status' => 'required|integer',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
$cron = Cron::query()->find($credentials['id']);
|
||||
$cron->status = $credentials['status'];
|
||||
$cron->save();
|
||||
|
||||
$data['code'] = 0;
|
||||
$data['msg'] = 'success';
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取计划任务日志
|
||||
*/
|
||||
public function getLog(Request $request): JsonResponse
|
||||
{
|
||||
// 消毒
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'id' => 'required|integer',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
$log = @file_get_contents('/www/server/cron/logs/'.$credentials['id'].'.log');
|
||||
if ($log === false) {
|
||||
$log = '暂无日志';
|
||||
}
|
||||
|
||||
$data['code'] = 0;
|
||||
$data['msg'] = 'success';
|
||||
$data['data'] = $log;
|
||||
return response()->json($data);
|
||||
}
|
||||
}
|
||||
@@ -27,45 +27,45 @@ class InfosController extends Controller
|
||||
),
|
||||
array(
|
||||
"name" => "website",
|
||||
"title" => "网站",
|
||||
"title" => "网站管理",
|
||||
"icon" => "layui-icon-website",
|
||||
"jump" => "website/list"
|
||||
),
|
||||
array(
|
||||
"name" => "monitor",
|
||||
"title" => "监控",
|
||||
"title" => "资源监控",
|
||||
"icon" => "layui-icon-chart-screen",
|
||||
"jump" => "monitor"
|
||||
),
|
||||
array(
|
||||
"name" => "safe",
|
||||
"title" => "安全",
|
||||
"title" => "系统安全",
|
||||
"icon" => "layui-icon-auz",
|
||||
"jump" => "safe"
|
||||
),
|
||||
array(
|
||||
"name" => "file",
|
||||
"title" => "文件",
|
||||
"title" => "文件管理",
|
||||
"icon" => "layui-icon-file",
|
||||
"jump" => "file"
|
||||
),
|
||||
array(
|
||||
"name" => "cron",
|
||||
"title" => "计划任务",
|
||||
"icon" => "layui-icon-date",
|
||||
"jump" => "cron"
|
||||
),
|
||||
array(
|
||||
"name" => "plugin",
|
||||
"title" => "插件",
|
||||
"title" => "插件中心",
|
||||
"icon" => "layui-icon-app",
|
||||
"jump" => "plugin"
|
||||
),
|
||||
array(
|
||||
"name" => "setting",
|
||||
"title" => "设置",
|
||||
"title" => "面板设置",
|
||||
"icon" => "layui-icon-set",
|
||||
"jump" => "setting"
|
||||
),
|
||||
array(
|
||||
"name" => "logout",
|
||||
"title" => "退出",
|
||||
"icon" => "layui-icon-logout",
|
||||
"jump" => "logout"
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
22
app/Models/Cron.php
Normal file
22
app/Models/Cron.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Cron extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
// 白名单
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'status',
|
||||
'type',
|
||||
'time',
|
||||
'shell',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
}
|
||||
36
database/migrations/2022_12_01_212925_create_crons_table.php
Normal file
36
database/migrations/2022_12_01_212925_create_crons_table.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('crons', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name')->nullable()->comment('任务名称');
|
||||
$table->boolean('status')->comment('任务状态');
|
||||
$table->string('type')->comment('任务类型');
|
||||
$table->string('time')->comment('任务周期');
|
||||
$table->text('shell')->comment('任务脚本文件');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('crons');
|
||||
}
|
||||
};
|
||||
194
public/panel/modules/cron.css
Normal file
194
public/panel/modules/cron.css
Normal file
@@ -0,0 +1,194 @@
|
||||
|
||||
/* 样式加载完毕的标识 */
|
||||
html #layuicss-cron {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 1989px;
|
||||
}
|
||||
|
||||
|
||||
/* 主体结构 */
|
||||
.layui-cron {
|
||||
width: 700px;
|
||||
position: absolute;
|
||||
z-index: 99999999;
|
||||
margin: 5px 0;
|
||||
border-radius: 2px;
|
||||
font-size: 14px;
|
||||
-webkit-animation-duration: 0.3s;
|
||||
animation-duration: 0.3s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0px 2px 5px 0px;
|
||||
-webkit-animation-name: cron-upbit;
|
||||
animation-name: cron-upbit;
|
||||
border: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.layui-cron-main ul {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
@-webkit-keyframes cron-upbit {
|
||||
|
||||
/* 微微往上滑入 */
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 20px, 0);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cron-upbit {
|
||||
from {
|
||||
transform: translate3d(0, 20px, 0);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.layui-cron>.layui-tab {
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* 行 */
|
||||
.cron-row {
|
||||
padding-left: 13px;
|
||||
}
|
||||
/* 格 */
|
||||
.cron-grid {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
/* 表达式 */
|
||||
.cron-title {
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
margin: 10px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.cron-box {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.cron-box+.cron-box {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* 按钮 */
|
||||
.cron-footer-btns {
|
||||
text-align: right;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.cron-footer-btns span {
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
margin: 0 0 0 -1px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #C9C9C9;
|
||||
background-color: #fff;
|
||||
white-space: nowrap;
|
||||
vertical-align: top;
|
||||
border-radius: 2px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
box-sizing: border-box;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.cron-footer-btns span:hover {
|
||||
color: #5FB878;
|
||||
}
|
||||
|
||||
|
||||
/* 表单 */
|
||||
.layui-cron .layui-form-radio {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.cron-form {
|
||||
line-height: 28px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.cron-input-mid {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-top: 6px;
|
||||
background-color: #e5e5e5;
|
||||
padding: 0 12px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
border: 1px solid #ccc;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cron-input {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-top: 6px;
|
||||
padding: 0 8px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
box-sizing: border-box;
|
||||
width: 80px;
|
||||
-webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
|
||||
-o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
|
||||
}
|
||||
|
||||
.cron-input:focus {
|
||||
outline: 0;
|
||||
border: 1px solid #01AAED;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 4px 0px #01AAED;
|
||||
translate: 1s;
|
||||
}
|
||||
|
||||
.layui-cron .layui-form-checkbox[lay-skin="primary"] span {
|
||||
padding-right: 10px;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
.layui-cron .layui-form-checkbox[lay-skin="primary"] {
|
||||
padding-left: 22px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.layui-cron input[type=number] {
|
||||
-moz-appearance:textfield;
|
||||
}
|
||||
.layui-cron input[type=number]::-webkit-inner-spin-button,
|
||||
.layui-cron input[type=number]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
.cron-tips{
|
||||
color: grey;
|
||||
line-height: 28px;
|
||||
height: 28px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-top: 8px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
961
public/panel/modules/cron.js
Normal file
961
public/panel/modules/cron.js
Normal file
@@ -0,0 +1,961 @@
|
||||
/**
|
||||
@ Name:layui.cron Cron表达式解析器
|
||||
@ Author:贝哥哥
|
||||
@ License:MIT
|
||||
*/
|
||||
|
||||
layui.define(['lay', 'element', 'form'], function (exports) { //假如该组件依赖 layui.form
|
||||
var $ = layui.$, layer = layui.layer, lay = layui.lay, element = layui.element, form = layui.form
|
||||
|
||||
|
||||
//字符常量
|
||||
, MOD_NAME = 'cron', ELEM = '.layui-cron', THIS = 'layui-this', SHOW = 'layui-show', HIDE = 'layui-hide'
|
||||
|
||||
, ELEM_STATIC = 'layui-cron-static', ELEM_FOOTER = 'layui-cron-footer', ELEM_CONFIRM = '.cron-btns-confirm',
|
||||
ELEM_HINT = 'layui-cron-hint'
|
||||
|
||||
, ELEM_RUN_HINT = 'layui-cron-run-hint'
|
||||
|
||||
//外部接口
|
||||
, cron = {
|
||||
v: '2.0.0' // cron 组件当前版本
|
||||
, index: layui.cron ? (layui.cron.index + 10000) : 0 // corn 实例标识
|
||||
|
||||
//设置全局项
|
||||
, set: function (options) {
|
||||
var that = this;
|
||||
that.config = $.extend({}, that.config, options);
|
||||
return that;
|
||||
}
|
||||
|
||||
//事件监听
|
||||
, on: function (events, callback) {
|
||||
return layui.onevent.call(this, MOD_NAME, events, callback);
|
||||
}
|
||||
|
||||
//主体CSS等待事件
|
||||
, ready: function (fn) {
|
||||
var cssPath = layui.cache.base + "cron.css?v=" + cron.v;
|
||||
layui.link(cssPath, fn, "cron"); //此处的“cron”要对应 cron.css 中的样式: html #layuicss-cron{}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
//操作当前实例
|
||||
, thisIns = function () {
|
||||
var that = this, options = that.config, id = options.id || options.index;
|
||||
|
||||
return {
|
||||
//提示框
|
||||
hint: function (content) {
|
||||
that.hint.call(that, content);
|
||||
}, config: options
|
||||
}
|
||||
}
|
||||
|
||||
//构造器,创建实例
|
||||
, Class = function (options) {
|
||||
var that = this;
|
||||
that.index = ++cron.index;
|
||||
that.config = $.extend({}, that.config, cron.config, options);
|
||||
cron.ready(function () {
|
||||
that.init();
|
||||
});
|
||||
};
|
||||
|
||||
//默认配置
|
||||
Class.prototype.config = {
|
||||
value: null // 当前表达式值,每秒执行一次
|
||||
,
|
||||
isInitValue: true //用于控制是否自动向元素填充初始值(需配合 value 参数使用)
|
||||
,
|
||||
lang: "cn" //语言,只支持cn/en,即中文和英文
|
||||
,
|
||||
tabs: [{key: 'minutes', range: '0-59'}, {
|
||||
key: 'hours', range: '0-23'
|
||||
}, {key: 'days', range: '1-31'}, {key: 'months', range: '1-12'}, {key: 'weeks', range: '1-7'}],
|
||||
defaultCron: {minutes: "*", hours: "*", days: "*", months: "*", weeks: "*"},
|
||||
trigger: "click" //呼出控件的事件
|
||||
,
|
||||
btns: ['run', 'confirm'] //右下角显示的按钮,会按照数组顺序排列
|
||||
,
|
||||
position: null //控件定位方式定位, 默认absolute,支持:fixed/absolute/static
|
||||
,
|
||||
zIndex: null //控件层叠顺序
|
||||
,
|
||||
show: false //是否直接显示,如果设置 true,则默认直接显示控件
|
||||
,
|
||||
showBottom: true //是否显示底部栏
|
||||
,
|
||||
done: null //控件选择完毕后的回调,点击运行/确定也均会触发
|
||||
,
|
||||
run: null // 最近运行时间接口
|
||||
};
|
||||
|
||||
//多语言
|
||||
Class.prototype.lang = function () {
|
||||
var that = this, options = that.config, text = {
|
||||
cn: {
|
||||
tabs: [{title: "分"}, {title: "时"}, {title: "日"}, {title: "月"}, {
|
||||
title: "周",
|
||||
rateBegin: "第",
|
||||
rateMid: "周的星期",
|
||||
rateEnd: ""
|
||||
}],
|
||||
every: "每",
|
||||
unspecified: "不指定",
|
||||
period: "周期",
|
||||
periodFrom: "从",
|
||||
rate: "按照",
|
||||
rateBegin: "从",
|
||||
rateMid: "开始,每",
|
||||
rateEnd: "执行一次",
|
||||
weekday: "工作日",
|
||||
weekdayPrefix: "每月",
|
||||
weekdaySuffix: "号最近的那个工作日",
|
||||
lastday: "本月最后一日",
|
||||
lastweek: "本月最后一个星期",
|
||||
custom: "指定",
|
||||
tools: {
|
||||
confirm: '确定', run: '运行'
|
||||
},
|
||||
formatError: ['Cron格式不合法', '<br>已为你重置']
|
||||
}, en: {
|
||||
tabs: [{title: "Minutes"}, {title: "Hours"}, {title: "Days"}, {title: "Months"}, {title: "Weeks"}],
|
||||
every: "Every ",
|
||||
unspecified: "Unspecified",
|
||||
period: "Period",
|
||||
periodFrom: "From",
|
||||
rate: "According to",
|
||||
rateBegin: "begin at",
|
||||
rateMid: ", every",
|
||||
rateEnd: " execute once",
|
||||
weekday: "Weekday",
|
||||
weekdayPrefix: "Every month at ",
|
||||
weekdaySuffix: "号最近的那个工作日",
|
||||
lastday: "Last day of the month",
|
||||
lastweek: "本月最后一个星期",
|
||||
custom: "Custom",
|
||||
tools: {
|
||||
confirm: 'Confirm', run: 'Run'
|
||||
},
|
||||
formatError: ['The cron format error', '<br>It has been reset']
|
||||
}
|
||||
};
|
||||
return text[options.lang] || text['cn'];
|
||||
};
|
||||
|
||||
//初始准备
|
||||
Class.prototype.init = function () {
|
||||
var that = this, options = that.config, isStatic = options.position === 'static';
|
||||
|
||||
options.elem = lay(options.elem);
|
||||
|
||||
options.eventElem = lay(options.eventElem);
|
||||
|
||||
if (!options.elem[0]) return;
|
||||
|
||||
//如果不是input|textarea元素,则默认采用click事件
|
||||
if (!that.isInput(options.elem[0])) {
|
||||
if (options.trigger === 'focus') {
|
||||
options.trigger = 'click';
|
||||
}
|
||||
}
|
||||
|
||||
// 设置渲染所绑定元素的唯一KEY
|
||||
if (!options.elem.attr('lay-key')) {
|
||||
options.elem.attr('lay-key', that.index);
|
||||
options.eventElem.attr('lay-key', that.index);
|
||||
}
|
||||
|
||||
// 当前实例主面板ID
|
||||
that.elemID = 'layui-icon' + options.elem.attr('lay-key');
|
||||
|
||||
//默认赋值
|
||||
if (options.value && options.isInitValue) {
|
||||
that.setValue(options.value);
|
||||
}
|
||||
if (!options.value) {
|
||||
options.value = options.elem[0].value || '';
|
||||
}
|
||||
var cronArr = options.value.split(' ');
|
||||
if (cronArr.length >= 6) {
|
||||
options.cron = {
|
||||
minutes: cronArr[0],
|
||||
hours: cronArr[1],
|
||||
days: cronArr[2],
|
||||
months: cronArr[3],
|
||||
weeks: cronArr[4],
|
||||
};
|
||||
} else {
|
||||
options.cron = lay.extend({}, options.defaultCron);
|
||||
}
|
||||
|
||||
|
||||
if (options.show || isStatic) that.render();
|
||||
isStatic || that.events();
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
// 控件主体渲染
|
||||
Class.prototype.render = function () {
|
||||
var that = this, options = that.config, lang = that.lang(), isStatic = options.position === 'static',
|
||||
tabFilter = 'cron-tab' + options.elem.attr('lay-key')
|
||||
//主面板
|
||||
, elem = that.elem = lay.elem('div', {
|
||||
id: that.elemID, 'class': ['layui-cron', isStatic ? (' ' + ELEM_STATIC) : ''].join('')
|
||||
})
|
||||
|
||||
// tab 内容区域
|
||||
, elemTab = that.elemTab = lay.elem('div', {
|
||||
'class': 'layui-tab layui-tab-card', 'lay-filter': tabFilter
|
||||
}), tabHead = lay.elem('ul', {
|
||||
'class': 'layui-tab-title'
|
||||
}), tabContent = lay.elem('div', {
|
||||
'class': 'layui-tab-content'
|
||||
})
|
||||
|
||||
//底部区域
|
||||
, divFooter = that.footer = lay.elem('div', {
|
||||
'class': ELEM_FOOTER
|
||||
});
|
||||
|
||||
if (options.zIndex) elem.style.zIndex = options.zIndex;
|
||||
|
||||
// 生成tab 内容区域
|
||||
elemTab.appendChild(tabHead);
|
||||
elemTab.appendChild(tabContent);
|
||||
lay.each(lang.tabs, function (i, item) {
|
||||
// 表头
|
||||
var li = lay.elem('li', {
|
||||
'class': i === 0 ? THIS : "", 'lay-id': i
|
||||
});
|
||||
li.innerHTML = item.title;
|
||||
tabHead.appendChild(li);
|
||||
|
||||
// 表体
|
||||
tabContent.appendChild(that.getTabContentChildElem(i));
|
||||
});
|
||||
|
||||
// 主区域
|
||||
elemMain = that.elemMain = lay.elem('div', {
|
||||
'class': 'layui-cron-main'
|
||||
});
|
||||
elemMain.appendChild(elemTab);
|
||||
|
||||
//生成底部栏
|
||||
lay(divFooter).html(function () {
|
||||
var html = [], btns = [];
|
||||
lay.each(options.btns, function (i, item) {
|
||||
var title = lang.tools[item] || 'btn';
|
||||
btns.push('<span lay-type="' + item + '" class="cron-btns-' + item + '">' + title + '</span>');
|
||||
});
|
||||
html.push('<div class="cron-footer-btns">' + btns.join('') + '</div>');
|
||||
return html.join('');
|
||||
}());
|
||||
|
||||
//插入到主区域
|
||||
elem.appendChild(elemMain);
|
||||
|
||||
options.showBottom && elem.appendChild(divFooter);
|
||||
|
||||
|
||||
//移除上一个控件
|
||||
that.remove(Class.thisElemCron);
|
||||
|
||||
//如果是静态定位,则插入到指定的容器中,否则,插入到body
|
||||
isStatic ? options.elem.append(elem) : (document.body.appendChild(elem)
|
||||
, that.position());
|
||||
|
||||
|
||||
that.checkCron();
|
||||
|
||||
that.elemEvent(); // 主面板事件
|
||||
|
||||
Class.thisElemCron = that.elemID;
|
||||
|
||||
form.render();
|
||||
|
||||
}
|
||||
|
||||
// 渲染 tab 子控件
|
||||
Class.prototype.getTabContentChildElem = function (index) {
|
||||
var that = this, options = that.config, tabItem = options.tabs[index], tabItemKey = tabItem.key,
|
||||
lang = that.lang(), tabItemLang = lang.tabs[index], cron = options.cron,
|
||||
formFilter = 'cronForm' + tabItemKey + options.elem.attr('lay-key'), data = function () {
|
||||
if (cron[tabItemKey].indexOf('-') != -1) {
|
||||
// 周期数据
|
||||
var arr = cron[tabItemKey].split('-');
|
||||
return {
|
||||
type: 'range', start: arr[0], end: arr[1]
|
||||
};
|
||||
}
|
||||
if (cron[tabItemKey].indexOf('/') != -1) {
|
||||
// 频率数据
|
||||
var arr = cron[tabItemKey].split('/');
|
||||
return {
|
||||
type: 'rate', begin: arr[0], rate: arr[1]
|
||||
};
|
||||
}
|
||||
if (cron[tabItemKey].indexOf(',') != -1 || /^\+?[0-9][0-9]*$/.test(cron[tabItemKey])) {
|
||||
// 按照指定执行
|
||||
var arr = cron[tabItemKey].split(',').map(Number);
|
||||
return {
|
||||
type: 'custom', values: arr
|
||||
};
|
||||
}
|
||||
if (cron[tabItemKey].indexOf('W') != -1) {
|
||||
// 最近的工作日
|
||||
var value = cron[tabItemKey].replace('W', '');
|
||||
return {
|
||||
type: 'weekday', value: value
|
||||
};
|
||||
}
|
||||
if (index === 2 && cron[tabItemKey] === 'L') {
|
||||
// 本月最后一日
|
||||
return {
|
||||
type: 'lastday', value: 'L'
|
||||
};
|
||||
}
|
||||
if (index === 4 && cron[tabItemKey].indexOf('L') != -1) {
|
||||
// 本月最后一个周 value
|
||||
var value = cron[tabItemKey].replace('L', '');
|
||||
return {
|
||||
type: 'lastweek', value: value
|
||||
};
|
||||
}
|
||||
if (cron[tabItemKey] === '*') {
|
||||
// 每次
|
||||
return {
|
||||
type: 'every', value: '*'
|
||||
};
|
||||
}
|
||||
if (cron[tabItemKey] === '?' || cron[tabItemKey] === undefined || cron[tabItemKey] === '') {
|
||||
// 不指定
|
||||
return {
|
||||
//type: 'unspecified', value: cron[tabItemKey]
|
||||
type: 'every', value: '*'
|
||||
};
|
||||
}
|
||||
}(), rangeData = function () {
|
||||
if (tabItem.range) {
|
||||
var arr = tabItem.range.split('-');
|
||||
return {
|
||||
min: parseInt(arr[0]), max: parseInt(arr[1])
|
||||
};
|
||||
}
|
||||
}();
|
||||
var elem = lay.elem('div', {
|
||||
'class': 'layui-tab-item layui-form ' + (index === 0 ? SHOW : ""), 'lay-filter': formFilter
|
||||
});
|
||||
|
||||
// 每次
|
||||
elem.appendChild(function () {
|
||||
var everyRadio = lay.elem('input', {
|
||||
'name': tabItemKey + '[type]',
|
||||
'type': 'radio',
|
||||
'value': 'every',
|
||||
'title': lang.every + tabItemLang.title
|
||||
});
|
||||
if (data.type === 'every') {
|
||||
lay(everyRadio).attr('checked', true);
|
||||
}
|
||||
var everyDiv = lay.elem('div', {
|
||||
'class': 'cron-row'
|
||||
});
|
||||
everyDiv.appendChild(everyRadio);
|
||||
return everyDiv;
|
||||
}());
|
||||
|
||||
// 不指定,从日开始
|
||||
/*if (index >= 2) {
|
||||
elem.appendChild(function () {
|
||||
var unspecifiedRadio = lay.elem('input', {
|
||||
'name': tabItemKey + '[type]', 'type': 'radio', 'value': 'unspecified', 'title': lang.unspecified
|
||||
});
|
||||
if (data.type === 'unspecified') {
|
||||
lay(unspecifiedRadio).attr('checked', true);
|
||||
}
|
||||
var unspecifiedDiv = lay.elem('div', {
|
||||
'class': 'cron-row'
|
||||
});
|
||||
unspecifiedDiv.appendChild(unspecifiedRadio);
|
||||
return unspecifiedDiv;
|
||||
}());
|
||||
}*/
|
||||
|
||||
// 周期
|
||||
var rangeChild = [function () {
|
||||
var rangeRadio = lay.elem('input', {
|
||||
'name': tabItemKey + '[type]', 'type': 'radio', 'value': 'range', 'title': lang.period
|
||||
});
|
||||
if (data.type === 'range') {
|
||||
lay(rangeRadio).attr('checked', true);
|
||||
}
|
||||
return rangeRadio;
|
||||
}(), function () {
|
||||
var elem = lay.elem('div', {
|
||||
'class': 'cron-input-mid'
|
||||
});
|
||||
elem.innerHTML = lang.periodFrom;
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('input', {
|
||||
'class': 'cron-input', 'type': 'number', 'name': 'rangeStart', 'value': data.start || ''
|
||||
});
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('div', {
|
||||
'class': 'cron-input-mid'
|
||||
});
|
||||
elem.innerHTML = '-';
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('input', {
|
||||
'class': 'cron-input', 'type': 'number', 'name': 'rangeEnd', 'value': data.end || ''
|
||||
});
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('div', {
|
||||
'class': 'cron-input-mid'
|
||||
});
|
||||
elem.innerHTML = tabItemLang.title;
|
||||
return elem;
|
||||
}()]
|
||||
|
||||
, rangeDiv = lay.elem('div', {
|
||||
'class': 'cron-row'
|
||||
});
|
||||
lay.each(rangeChild, function (i, item) {
|
||||
rangeDiv.appendChild(item);
|
||||
});
|
||||
if (tabItem.range) {
|
||||
var rangeTip = lay.elem('div', {
|
||||
'class': 'cron-tips'
|
||||
});
|
||||
rangeTip.innerHTML = ['(', tabItem.range, ')'].join('');
|
||||
rangeDiv.appendChild(rangeTip);
|
||||
}
|
||||
elem.appendChild(rangeDiv);
|
||||
|
||||
// 频率,年没有
|
||||
if (index < 6) {
|
||||
var rateChild = [function () {
|
||||
var rateRadio = lay.elem('input', {
|
||||
'name': tabItemKey + '[type]', 'type': 'radio', 'value': 'rate', 'title': lang.rate
|
||||
});
|
||||
if (data.type === 'rate') {
|
||||
lay(rateRadio).attr('checked', true);
|
||||
}
|
||||
return rateRadio;
|
||||
}(), function () {
|
||||
var elem = lay.elem('div', {
|
||||
'class': 'cron-input-mid'
|
||||
});
|
||||
elem.innerHTML = tabItemLang.rateBegin || lang.rateBegin;
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('input', {
|
||||
'class': 'cron-input', 'type': 'number', 'name': 'begin', 'value': data.begin || ''
|
||||
});
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('div', {
|
||||
'class': 'cron-input-mid'
|
||||
});
|
||||
elem.innerHTML = tabItemLang.rateMid || (tabItemLang.title + lang.rateMid);
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('input', {
|
||||
'class': 'cron-input', 'type': 'number', 'name': 'rate', 'value': data.rate || ''
|
||||
});
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('div', {
|
||||
'class': 'cron-input-mid'
|
||||
});
|
||||
elem.innerHTML = undefined != tabItemLang.rateEnd ? tabItemLang.rateEnd : (tabItemLang.title + lang.rateEnd);
|
||||
if (undefined != tabItemLang.rateEnd && tabItemLang.rateEnd === '') {
|
||||
lay(elem).addClass(HIDE);
|
||||
}
|
||||
return elem;
|
||||
}()]
|
||||
|
||||
, rateDiv = lay.elem('div', {
|
||||
'class': 'cron-row'
|
||||
});
|
||||
lay.each(rateChild, function (i, item) {
|
||||
rateDiv.appendChild(item);
|
||||
});
|
||||
if (tabItem.range) {
|
||||
var rateTip = lay.elem('div', {
|
||||
'class': 'cron-tips'
|
||||
});
|
||||
if (index === 4) {
|
||||
// 周
|
||||
rateTip.innerHTML = '(1-4/1-7)';
|
||||
} else {
|
||||
rateTip.innerHTML = ['(', rangeData.min, '/', (rangeData.max + (index <= 1 ? 1 : 0)), ')'].join('');
|
||||
}
|
||||
rateDiv.appendChild(rateTip);
|
||||
}
|
||||
elem.appendChild(rateDiv);
|
||||
}
|
||||
|
||||
// 特殊:日(最近的工作日、最后一日),周(最后一周)
|
||||
/*if (index === 2) {
|
||||
// 日
|
||||
// 最近的工作日
|
||||
var weekChild = [function () {
|
||||
var weekRadio = lay.elem('input', {
|
||||
'name': tabItemKey + '[type]', 'type': 'radio', 'value': 'weekday', 'title': lang.weekday
|
||||
});
|
||||
if (data.type === 'weekday') {
|
||||
lay(weekRadio).attr('checked', true);
|
||||
}
|
||||
return weekRadio;
|
||||
}(), function () {
|
||||
var elem = lay.elem('div', {
|
||||
'class': 'cron-input-mid'
|
||||
});
|
||||
elem.innerHTML = lang.weekdayPrefix;
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('input', {
|
||||
'class': 'cron-input', 'type': 'number', 'name': 'weekday', 'value': data.value || ''
|
||||
});
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('div', {
|
||||
'class': 'cron-input-mid'
|
||||
});
|
||||
elem.innerHTML = lang.weekdaySuffix;
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('div', {
|
||||
'class': 'cron-tips'
|
||||
});
|
||||
elem.innerHTML = ['(', tabItem.range, ')'].join('');
|
||||
return elem;
|
||||
}()]
|
||||
|
||||
, weekDiv = lay.elem('div', {
|
||||
'class': 'cron-row'
|
||||
});
|
||||
lay.each(weekChild, function (i, item) {
|
||||
weekDiv.appendChild(item);
|
||||
});
|
||||
elem.appendChild(weekDiv);
|
||||
|
||||
// 本月最后一日
|
||||
elem.appendChild(function () {
|
||||
var lastRadio = lay.elem('input', {
|
||||
'name': tabItemKey + '[type]', 'type': 'radio', 'value': 'lastday', 'title': lang.lastday
|
||||
});
|
||||
if (data.type === 'lastday') {
|
||||
lay(lastRadio).attr('checked', true);
|
||||
}
|
||||
var lastDiv = lay.elem('div', {
|
||||
'class': 'cron-row'
|
||||
});
|
||||
lastDiv.appendChild(lastRadio);
|
||||
return lastDiv;
|
||||
}());
|
||||
|
||||
}
|
||||
|
||||
if (index === 4) {
|
||||
// 本月最后一个周几
|
||||
var lastWeekChild = [function () {
|
||||
var lastWeekRadio = lay.elem('input', {
|
||||
'name': tabItemKey + '[type]', 'type': 'radio', 'value': 'lastweek', 'title': lang.lastweek
|
||||
});
|
||||
if (data.type === 'lastweek') {
|
||||
lay(lastWeekRadio).attr('checked', true);
|
||||
}
|
||||
return lastWeekRadio;
|
||||
}(), function () {
|
||||
var elem = lay.elem('input', {
|
||||
'class': 'cron-input', 'type': 'number', 'name': 'lastweek', 'value': data.value || ''
|
||||
});
|
||||
return elem;
|
||||
}(), function () {
|
||||
var elem = lay.elem('div', {
|
||||
'class': 'cron-tips'
|
||||
});
|
||||
elem.innerHTML = ['(', tabItem.range, ')'].join('');
|
||||
return elem;
|
||||
}()]
|
||||
|
||||
, lastWeekDiv = lay.elem('div', {
|
||||
'class': 'cron-row'
|
||||
});
|
||||
lay.each(lastWeekChild, function (i, item) {
|
||||
lastWeekDiv.appendChild(item);
|
||||
});
|
||||
elem.appendChild(lastWeekDiv);
|
||||
|
||||
}*/
|
||||
|
||||
// 指定
|
||||
if (index <= 4) {
|
||||
elem.appendChild(function () {
|
||||
var customRadio = lay.elem('input', {
|
||||
'name': tabItemKey + '[type]', 'type': 'radio', 'value': 'custom', 'title': lang.custom
|
||||
});
|
||||
if (data.type === 'custom') {
|
||||
lay(customRadio).attr('checked', true);
|
||||
}
|
||||
var customDiv = lay.elem('div', {
|
||||
'class': 'cron-row'
|
||||
});
|
||||
customDiv.appendChild(customRadio);
|
||||
return customDiv;
|
||||
}());
|
||||
|
||||
// 指定数值,时分秒显示两位数,自动补零
|
||||
elem.appendChild(function () {
|
||||
var customGrid = lay.elem('div', {
|
||||
'class': 'cron-grid'
|
||||
});
|
||||
var i = rangeData.min;
|
||||
while (i <= rangeData.max) {
|
||||
// 时分秒显示两位数,自动补零
|
||||
var gridItemValue = index <= 1 ? lay.digit(i, 2) : i;
|
||||
var gridItem = lay.elem('input', {
|
||||
'type': 'checkbox',
|
||||
'title': gridItemValue,
|
||||
'lay-skin': 'primary',
|
||||
'name': tabItemKey + '[custom]',
|
||||
'value': i
|
||||
});
|
||||
if (data.values && data.values.includes(i)) {
|
||||
lay(gridItem).attr('checked', true);
|
||||
}
|
||||
customGrid.appendChild(gridItem);
|
||||
i++;
|
||||
}
|
||||
return customGrid;
|
||||
}());
|
||||
}
|
||||
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
//是否输入框
|
||||
Class.prototype.isInput = function (elem) {
|
||||
return /input|textarea/.test(elem.tagName.toLocaleLowerCase());
|
||||
};
|
||||
|
||||
// 绑定的元素事件处理
|
||||
Class.prototype.events = function () {
|
||||
var that = this, options = that.config
|
||||
|
||||
//绑定呼出控件事件
|
||||
, showEvent = function (elem, bind) {
|
||||
elem.on(options.trigger, function () {
|
||||
bind && (that.bindElem = this);
|
||||
that.render();
|
||||
});
|
||||
};
|
||||
|
||||
if (!options.elem[0] || options.elem[0].eventHandler) return;
|
||||
|
||||
showEvent(options.elem, 'bind');
|
||||
showEvent(options.eventElem);
|
||||
|
||||
//绑定关闭控件事件
|
||||
lay(document).on('click', function (e) {
|
||||
if (e.target === options.elem[0] || e.target === options.eventElem[0] || e.target === lay(options.closeStop)[0]) {
|
||||
return;
|
||||
}
|
||||
that.remove();
|
||||
}).on('keydown', function (e) {
|
||||
if (e.keyCode === 13) {
|
||||
if (lay('#' + that.elemID)[0] && that.elemID === Class.thisElemDate) {
|
||||
e.preventDefault();
|
||||
lay(that.footer).find(ELEM_CONFIRM)[0].click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//自适应定位
|
||||
lay(window).on('resize', function () {
|
||||
if (!that.elem || !lay(ELEM)[0]) {
|
||||
return false;
|
||||
}
|
||||
that.position();
|
||||
});
|
||||
|
||||
options.elem[0].eventHandler = true;
|
||||
};
|
||||
|
||||
// 主面板事件
|
||||
Class.prototype.elemEvent = function () {
|
||||
var that = this, options = that.config, tabFilter = 'cron-tab' + options.elem.attr('lay-key');
|
||||
|
||||
// 阻止主面板点击冒泡,避免因触发文档事件而关闭主面
|
||||
lay(that.elem).on('click', function (e) {
|
||||
lay.stope(e);
|
||||
});
|
||||
|
||||
// tab选项卡切换
|
||||
var lis = lay(that.elemTab).find('li');
|
||||
lis.on('click', function () {
|
||||
var layid = lay(this).attr('lay-id');
|
||||
if (undefined === layid) {
|
||||
return;
|
||||
}
|
||||
element.tabChange(tabFilter, layid);
|
||||
});
|
||||
|
||||
// cron选项点击
|
||||
form.on('radio', function (data) {
|
||||
var $parent = data.othis.parent();
|
||||
var formFilter = $parent.parent().attr('lay-filter');
|
||||
var formData = form.val(formFilter);
|
||||
var radioType = data.value;
|
||||
if ('range' === radioType) {
|
||||
// 范围
|
||||
form.val(formFilter, {
|
||||
rangeStart: formData.rangeStart || 0, rangeEnd: formData.rangeEnd || 2
|
||||
});
|
||||
}
|
||||
if ('rate' === radioType) {
|
||||
// 频率
|
||||
form.val(formFilter, {
|
||||
begin: formData.begin || 0, rate: formData.rate || 2
|
||||
});
|
||||
}
|
||||
if ('custom' === radioType) {
|
||||
// custom
|
||||
var $grid = $parent.next();
|
||||
if ($grid.find(':checkbox:checked').length <= 0) {
|
||||
$grid.children(':checkbox:first').next().click()
|
||||
}
|
||||
}
|
||||
if ('weekday' === radioType) {
|
||||
// weekday
|
||||
form.val(formFilter, {
|
||||
weekday: formData.weekday || 1
|
||||
});
|
||||
}
|
||||
if ('lastweek' === radioType) {
|
||||
// lastweek
|
||||
form.val(formFilter, {
|
||||
lastweek: formData.lastweek || 1
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//点击底部按钮
|
||||
lay(that.footer).find('span').on('click', function () {
|
||||
var type = lay(this).attr('lay-type');
|
||||
that.tool(this, type);
|
||||
});
|
||||
};
|
||||
|
||||
//底部按钮点击事件
|
||||
Class.prototype.tool = function (btn, type) {
|
||||
var that = this, options = that.config, lang = that.lang(), isStatic = options.position === 'static', active = {
|
||||
//运行
|
||||
run: function () {
|
||||
var value = that.parse();
|
||||
var loading = layer.load();
|
||||
$.get(options.run, {cron: value}, function (res) {
|
||||
layer.close(loading);
|
||||
if (res.code !== 0) {
|
||||
return that.hint(res.msg);
|
||||
}
|
||||
that.runHint(res.data);
|
||||
}, 'json').fail(function () {
|
||||
layer.close(loading);
|
||||
that.hint('服务器异常!');
|
||||
});
|
||||
}
|
||||
|
||||
//确定
|
||||
, confirm: function () {
|
||||
var value = that.parse();
|
||||
that.done([value]);
|
||||
that.setValue(value).remove()
|
||||
}
|
||||
};
|
||||
active[type] && active[type]();
|
||||
};
|
||||
|
||||
//执行 done/change 回调
|
||||
Class.prototype.done = function (param, type) {
|
||||
var that = this, options = that.config;
|
||||
|
||||
param = param || [that.parse()];
|
||||
typeof options[type || 'done'] === 'function' && options[type || 'done'].apply(options, param);
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
// 解析cron表达式
|
||||
Class.prototype.parse = function () {
|
||||
var that = this, options = that.config, valueArr = [];
|
||||
|
||||
lay.each(options.tabs, function (index, item) {
|
||||
var key = item.key;
|
||||
var formFilter = 'cronForm' + key + options.elem.attr('lay-key');
|
||||
var formData = form.val(formFilter);
|
||||
var radioType = (key + '[type]');
|
||||
var current = "";
|
||||
if (formData[radioType] === 'every') {
|
||||
// 每次
|
||||
current = "*";
|
||||
}
|
||||
if (formData[radioType] === 'range') {
|
||||
// 范围
|
||||
current = formData.rangeStart + "-" + formData.rangeEnd;
|
||||
}
|
||||
if (formData[radioType] === 'rate') {
|
||||
// 频率
|
||||
current = formData.begin + "/" + formData.rate;
|
||||
}
|
||||
if (formData[radioType] === 'custom') {
|
||||
// 指定
|
||||
var checkboxName = (item.key + '[custom]');
|
||||
var customArr = [];
|
||||
$('input[name="' + checkboxName + '"]:checked').each(function () {
|
||||
customArr.push($(this).val());
|
||||
});
|
||||
current = customArr.join(',');
|
||||
}
|
||||
if (formData[radioType] === 'weekday') {
|
||||
// 每月 formData.weekday 号最近的那个工作日
|
||||
current = formData.weekday + "W";
|
||||
}
|
||||
if (formData[radioType] === 'lastday') {
|
||||
// 本月最后一日
|
||||
current = "L";
|
||||
}
|
||||
if (formData[radioType] === 'lastweek') {
|
||||
// 本月最后星期
|
||||
current = formData.lastweek + "L";
|
||||
}
|
||||
|
||||
if (formData[radioType] === 'unspecified' && index != 6) {
|
||||
// 不指定
|
||||
current = "?";
|
||||
}
|
||||
if (current !== "") {
|
||||
valueArr.push(current);
|
||||
options.cron[key] = current;
|
||||
}
|
||||
});
|
||||
return valueArr.join(' ');
|
||||
};
|
||||
|
||||
//控件移除
|
||||
Class.prototype.remove = function (prev) {
|
||||
var that = this, options = that.config, elem = lay('#' + (prev || that.elemID));
|
||||
if (!elem[0]) return that;
|
||||
|
||||
if (!elem.hasClass(ELEM_STATIC)) {
|
||||
that.checkCron(function () {
|
||||
elem.remove();
|
||||
});
|
||||
}
|
||||
return that;
|
||||
};
|
||||
|
||||
//定位算法
|
||||
Class.prototype.position = function () {
|
||||
var that = this, options = that.config;
|
||||
lay.position(that.bindElem || options.elem[0], that.elem, {
|
||||
position: options.position
|
||||
});
|
||||
return that;
|
||||
};
|
||||
|
||||
//提示
|
||||
Class.prototype.hint = function (content) {
|
||||
var that = this, options = that.config, div = lay.elem('div', {
|
||||
'class': ELEM_HINT
|
||||
});
|
||||
|
||||
if (!that.elem) return;
|
||||
|
||||
div.innerHTML = content || '';
|
||||
lay(that.elem).find('.' + ELEM_HINT).remove();
|
||||
that.elem.appendChild(div);
|
||||
|
||||
clearTimeout(that.hinTimer);
|
||||
that.hinTimer = setTimeout(function () {
|
||||
lay(that.elem).find('.' + ELEM_HINT).remove();
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
//运行提示
|
||||
Class.prototype.runHint = function (runList) {
|
||||
var that = this, options = that.config, div = lay.elem('div', {
|
||||
'class': ELEM_RUN_HINT
|
||||
});
|
||||
// debugger;
|
||||
if (!that.elem || !runList || !runList.length) return;
|
||||
|
||||
|
||||
lay(div).html(function () {
|
||||
var html = [];
|
||||
lay.each(runList, function (i, item) {
|
||||
html.push('<div class="cron-run-list">' + item + '</div>');
|
||||
});
|
||||
return html.join('');
|
||||
}());
|
||||
|
||||
lay(that.elem).find('.' + ELEM_RUN_HINT).remove();
|
||||
that.elem.appendChild(div);
|
||||
};
|
||||
|
||||
//赋值
|
||||
Class.prototype.setValue = function (value = '') {
|
||||
var that = this, options = that.config, elem = that.bindElem || options.elem[0],
|
||||
valType = that.isInput(elem) ? 'val' : 'html'
|
||||
|
||||
options.position === 'static' || lay(elem)[valType](value || '');
|
||||
elem.textContent = '生成';
|
||||
return this;
|
||||
};
|
||||
|
||||
//cron校验
|
||||
Class.prototype.checkCron = function (fn) {
|
||||
var that = this, options = that.config, lang = that.lang(), elem = that.bindElem || options.elem[0],
|
||||
value = that.isInput(elem) ? elem.value : (options.position === 'static' ? '' : elem.innerHTML)
|
||||
|
||||
, checkValid = function (value = "") {
|
||||
|
||||
};
|
||||
|
||||
// cron 值,多个空格替换为一个空格,去掉首尾空格
|
||||
value = value || options.value;
|
||||
if (typeof value === 'string') {
|
||||
value = value.replace(/\s+/g, ' ').replace(/^\s|\s$/g, '');
|
||||
}
|
||||
|
||||
if (fn === 'init') return checkValid(value), that;
|
||||
|
||||
value = that.parse();
|
||||
if (value) {
|
||||
that.setValue(value);
|
||||
}
|
||||
fn && fn();
|
||||
return that;
|
||||
};
|
||||
|
||||
//核心入口
|
||||
cron.render = function (options) {
|
||||
var ins = new Class(options);
|
||||
return thisIns.call(ins);
|
||||
};
|
||||
|
||||
exports('cron', cron);
|
||||
});
|
||||
|
||||
281
resources/views/cron.blade.php
Normal file
281
resources/views/cron.blade.php
Normal file
@@ -0,0 +1,281 @@
|
||||
<title>计划任务</title>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">添加计划任务</div>
|
||||
<div class="layui-card-body">
|
||||
<form class="layui-form" action="" lay-filter="cron-add-form">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">任务名</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="name" lay-verify="required" placeholder="请输入任务名称"
|
||||
autocomplete="off" class="layui-input"/>
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">请填写任务名称</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">执行周期</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="time" id="cron-add-time"
|
||||
lay-verify="required" placeholder="请选择或输入cron表达式" class="layui-input"/>
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">请务必正确填写执行周期</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">脚本内容</label>
|
||||
<div class="layui-input-block">
|
||||
<div id="cron-add-script-editor"
|
||||
style="height: 250px;"># 在此输入你要执行的脚本内容</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="cron-add-submit">立即提交</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">计划任务列表</div>
|
||||
<div class="layui-card-body">
|
||||
<table id="panel-cron" lay-filter="panel-cron"></table>
|
||||
<!-- 操作按钮模板 -->
|
||||
<script type="text/html" id="cron-table-edit">
|
||||
<a class="layui-btn layui-btn-xs" lay-event="log">日志</a>
|
||||
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
|
||||
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
<!-- 运行开关 -->
|
||||
<script type="text/html" id="cron-table-status">
|
||||
<input type="checkbox" name="cron-status" lay-skin="switch" lay-text="ON|OFF"
|
||||
lay-filter="cron-status"
|
||||
value="@{{ d.status }}" data-id="@{{ d.id }}"
|
||||
@{{ d.status==
|
||||
1 ? 'checked' : '' }} />
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var cronAddScriptEditor = ace.edit("cron-add-script-editor", {
|
||||
mode: "ace/mode/sh",
|
||||
selectionStyle: "text"
|
||||
});
|
||||
var cronEditScriptEditor;
|
||||
layui.use(['admin', 'table', 'jquery', 'cron'], function () {
|
||||
var $ = layui.$
|
||||
, form = layui.form
|
||||
, table = layui.table
|
||||
, admin = layui.admin
|
||||
, cron = layui.cron;
|
||||
|
||||
cron.render({
|
||||
elem: "#cron-add-time",
|
||||
btns: ['confirm'],
|
||||
show: false,
|
||||
done: function (value) {
|
||||
$('#cron-add-time').val(value);
|
||||
}
|
||||
});
|
||||
|
||||
form.render();
|
||||
|
||||
table.render({
|
||||
elem: '#panel-cron'
|
||||
, url: '/api/panel/cron/getList'
|
||||
, cols: [[
|
||||
{field: 'id', hide: true, title: 'ID', sort: true}
|
||||
, {field: 'name', width: '15%', title: '任务名'}
|
||||
, {field: 'type', width: '10%', title: '任务类型'}
|
||||
, {field: 'status', title: '启用', width: 100, templet: '#cron-table-status', unresize: true}
|
||||
, {field: 'time', width: '25%', title: '任务周期(cron表达式)'}
|
||||
, {field: 'updated_at', title: '上次运行时间'}
|
||||
, {
|
||||
field: 'edit',
|
||||
width: '15%',
|
||||
title: '操作',
|
||||
templet: '#cron-table-edit',
|
||||
fixed: 'right',
|
||||
align: 'left'
|
||||
}
|
||||
]]
|
||||
, page: true
|
||||
, limit: 10
|
||||
, limits: [10, 100, 200, 500, 1000]
|
||||
, text: {
|
||||
none: '暂无数据'
|
||||
}
|
||||
, done: function () {
|
||||
//element.render('progress');
|
||||
}
|
||||
});
|
||||
|
||||
// 工具条
|
||||
table.on('tool(panel-cron)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'log') {
|
||||
// 打开日志弹窗
|
||||
admin.popup({
|
||||
title: '日志'
|
||||
,
|
||||
area: ['80%', '80%']
|
||||
,
|
||||
id: 'cron-log'
|
||||
,
|
||||
content: '<pre id="cron-log-view" style="overflow: auto; height: 95%;border: 0 none; line-height:23px; padding: 15px; margin: 0; white-space: pre-wrap; background-color: rgb(51,51,51); color:#f1f1f1; border-radius:0;"></pre>'
|
||||
,
|
||||
success: function (layero, index) {
|
||||
admin.req({
|
||||
url: '/api/panel/cron/getLog?id=' + data.id
|
||||
, type: 'GET'
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
$('#cron-log-view').html(res.data);
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2, time: 1000});
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
} else if (obj.event === 'edit') {
|
||||
// 打开编辑弹窗
|
||||
admin.popup({
|
||||
title: '编辑'
|
||||
,
|
||||
area: ['80%', '80%']
|
||||
,
|
||||
id: 'cron-log'
|
||||
,
|
||||
content: '任务名 <div class="layui-input-inline" style="width: 190px;"><input type="text" name="cron-edit-name" placeholder="请输入任务名称" autocomplete="off" class="layui-input" value="' + data.name + '"/></div> 执行周期 <div class="layui-input-inline" style="width: 190px;"><input id="cron-edit-time-' + data.id + '" type="text" name="cron-edit-time" placeholder="请输入执行周期" autocomplete="off" class="layui-input" value="' + data.time + '"/></div><hr><div id="cron-edit-script-editor" style="height: 80%;">' + data.script + '</div><br><button id="cron-edit-' + data.id + '" class="layui-btn">保存</button>'
|
||||
,
|
||||
success: function (layero, index) {
|
||||
cronEditScriptEditor = ace.edit("cron-edit-script-editor", {
|
||||
mode: "ace/mode/sh",
|
||||
selectionStyle: "text"
|
||||
});
|
||||
cron.render({
|
||||
elem: "#cron-edit-time-" + data.id,
|
||||
btns: ['confirm'],
|
||||
show: false,
|
||||
done: function (value) {
|
||||
$('#cron-add-time').val(value);
|
||||
}
|
||||
});
|
||||
$('#cron-edit-' + data.id).click(function () {
|
||||
admin.req({
|
||||
url: '/api/panel/cron/edit'
|
||||
, type: 'POST'
|
||||
, data: {
|
||||
id: data.id,
|
||||
name: $('input[name="cron-edit-name"]').val(),
|
||||
time: $('input[name="cron-edit-time"]').val(),
|
||||
script: cronEditScriptEditor.getValue()
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('保存成功', {icon: 1, time: 1000});
|
||||
table.reload('panel-cron');
|
||||
layer.close(index);
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2, time: 1000});
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (obj.event === 'del') {
|
||||
layer.confirm('确定删除计划任务' + data.name + '吗?', function (index) {
|
||||
layer.close(index);
|
||||
admin.req({
|
||||
url: '/api/panel/cron/delete',
|
||||
type: 'POST',
|
||||
data: {
|
||||
id: data.id
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
table.reload('panel-cron');
|
||||
layer.msg('计划任务:' + data.name + ' 已删除', {
|
||||
icon: 1,
|
||||
time: 1000
|
||||
});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2, time: 1000});
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
form.on('switch(cron-status)', function (obj) {
|
||||
let $ = layui.$;
|
||||
let id = $(this).data('id');
|
||||
let status = obj.elem.checked ? 1 : 0;
|
||||
|
||||
admin.req({
|
||||
url: '/api/panel/cron/setStatus',
|
||||
type: 'POST',
|
||||
data: {
|
||||
id: id,
|
||||
status: status
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('设置成功', {icon: 1, time: 1000});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2, time: 1000});
|
||||
}
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
form.on('submit(cron-add-submit)', function (data) {
|
||||
data.field.script = cronAddScriptEditor.getValue();
|
||||
admin.req({
|
||||
url: "/api/panel/cron/add"
|
||||
, method: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:计划任务添加失败,接口返回' + result);
|
||||
layer.msg('计划任务添加失败!')
|
||||
return false;
|
||||
}
|
||||
table.reload('panel-cron');
|
||||
layer.alert('计划任务添加成功!', {
|
||||
icon: 1
|
||||
, title: '提示'
|
||||
, btn: ['确定']
|
||||
, yes: function (index) {
|
||||
layer.closeAll();
|
||||
//location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -1,2 +1,2 @@
|
||||
<title>文件</title>
|
||||
<h1 style="text-align: center; padding-top: 20px;">管理正在开发中!</h1>
|
||||
<title>文件管理</title>
|
||||
<h1 style="text-align: center; padding-top: 20px;">文件管理正在开发中!</h1>
|
||||
@@ -24,8 +24,8 @@
|
||||
placeholder="密码" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-item" style="margin-bottom: 20px;">
|
||||
<input type="checkbox" name="remember" id="remember" lay-skin="primary" title="记住我">
|
||||
<a href="https://hzbk.net/" class="layadmin-user-jump-change layadmin-link"
|
||||
{{--<input type="checkbox" name="remember" id="remember" lay-skin="primary" title="记住我">--}}
|
||||
<a target="_blank" href="https://jq.qq.com/?_wv=1027&k=I1oJKSTH" class="layadmin-user-jump-change layadmin-link"
|
||||
style="margin-top: 7px;">忘记密码?</a>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<title>监控</title>
|
||||
<title>资源监控</title>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<title>插件</title>
|
||||
<title>插件中心</title>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<title>安全</title>
|
||||
<title>系统安全</title>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!--
|
||||
Name: 设置模版
|
||||
Name: 面板设置模版
|
||||
Author: 耗子
|
||||
Date: 2022-12-01
|
||||
-->
|
||||
<title>设置</title>
|
||||
<title>面板设置</title>
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-row layui-col-space15">
|
||||
<div class="layui-col-md12">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Api\CronsController;
|
||||
use App\Http\Controllers\Api\MonitorsController;
|
||||
use App\Http\Controllers\Api\PluginsController;
|
||||
use App\Http\Controllers\Api\SafesController;
|
||||
@@ -62,12 +63,6 @@ Route::prefix('panel')->group(function () {
|
||||
Route::get('getInstalledDbAndPhp', [InfosController::class, 'getInstalledDbAndPhp']);
|
||||
|
||||
});
|
||||
Route::middleware('auth:sanctum')->prefix('settings')->group(function () {
|
||||
// 获取面板设置
|
||||
Route::get('get', [TasksController::class, 'get']);
|
||||
// 保存面板设置
|
||||
Route::post('save', [TasksController::class, 'save']);
|
||||
});
|
||||
// 网站
|
||||
Route::middleware('auth:sanctum')->prefix('website')->group(function () {
|
||||
// 获取网站列表
|
||||
@@ -128,6 +123,16 @@ Route::prefix('panel')->group(function () {
|
||||
Route::post('update', [PluginsController::class, 'update']);
|
||||
Route::post('setShowHome', [PluginsController::class, 'setShowHome']);
|
||||
});
|
||||
// 计划任务
|
||||
Route::middleware('auth:sanctum')->prefix('cron')->group(function () {
|
||||
// 获取计划任务列表
|
||||
Route::get('getList', [CronsController::class, 'getList']);
|
||||
Route::post('add', [CronsController::class, 'add']);
|
||||
Route::post('edit', [CronsController::class, 'edit']);
|
||||
Route::post('delete', [CronsController::class, 'delete']);
|
||||
Route::post('setStatus', [CronsController::class, 'setStatus']);
|
||||
Route::get('getLog', [CronsController::class, 'getLog']);
|
||||
});
|
||||
// 设置
|
||||
Route::middleware('auth:sanctum')->prefix('setting')->group(function () {
|
||||
// 获取设置
|
||||
|
||||
@@ -26,7 +26,6 @@ Route::prefix('panel/views')->group(function () {
|
||||
|
||||
// 主页
|
||||
Route::view('index', 'home');
|
||||
Route::view('setting', 'setting');
|
||||
// 网站
|
||||
Route::prefix('website')->group(function () {
|
||||
//全局设置
|
||||
@@ -38,12 +37,6 @@ Route::prefix('panel/views')->group(function () {
|
||||
// 编辑
|
||||
Route::view('edit', 'website.edit');
|
||||
});
|
||||
|
||||
// 数据库-MySQL
|
||||
Route::prefix('database')->group(function () {
|
||||
Route::view('mysql', 'database.mysql');
|
||||
Route::view('postgresql', 'database.postgresql');
|
||||
});
|
||||
// 监控
|
||||
Route::view('monitor', 'monitor');
|
||||
// 安全
|
||||
@@ -52,6 +45,10 @@ Route::prefix('panel/views')->group(function () {
|
||||
Route::view('file', 'file');
|
||||
// 插件
|
||||
Route::view('plugin', 'plugin');
|
||||
// 插件
|
||||
Route::view('cron', 'cron');
|
||||
// 设置
|
||||
Route::view('setting', 'setting');
|
||||
|
||||
// 其他独立页面
|
||||
// 登录
|
||||
@@ -60,6 +57,6 @@ Route::prefix('panel/views')->group(function () {
|
||||
Route::view('logout', 'logout');
|
||||
// 主题设置
|
||||
Route::view('theme', 'theme');
|
||||
// 任务
|
||||
// 任务中心
|
||||
Route::view('task', 'task');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user