2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-05 20:47:18 +08:00

特性(计划任务):新增计划任务模块

This commit is contained in:
耗子
2022-12-02 03:56:43 +08:00
parent 53baea1422
commit 939867d100
16 changed files with 1753 additions and 35 deletions

View File

@@ -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');
});
}
}
/**

View 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);
}
}

View File

@@ -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
View 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',
];
}

View 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');
}
};

View 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;
}

View File

@@ -0,0 +1,961 @@
/**
@ Namelayui.cron Cron表达式解析器
@ Author贝哥哥
@ LicenseMIT
*/
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);
});

View 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: '任务名&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;执行周期&nbsp;&nbsp;&nbsp;&nbsp;<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>

View File

@@ -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>

View File

@@ -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&amp;k=I1oJKSTH" class="layadmin-user-jump-change layadmin-link"
style="margin-top: 7px;">忘记密码?</a>
</div>
<div class="layui-form-item">

View File

@@ -1,4 +1,4 @@
<title>监控</title>
<title>资源监控</title>
<div class="layui-fluid">
<div class="layui-card">

View File

@@ -1,4 +1,4 @@
<title>插件</title>
<title>插件中心</title>
<div class="layui-fluid">
<div class="layui-card">

View File

@@ -1,4 +1,4 @@
<title>安全</title>
<title>系统安全</title>
<div class="layui-fluid">
<div class="layui-card">

View File

@@ -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">

View File

@@ -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 () {
// 获取设置

View File

@@ -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');
});