特性(全局):一波更新
14
CHANGELOG.md
@@ -3,10 +3,18 @@
|
||||
所有重要的更改都将在此文件中记录。
|
||||
|
||||
|
||||
## [20221210] - 常规更新
|
||||
## [20221222] - 常规更新
|
||||
|
||||
- 增强面板安全性
|
||||
- 修复网站管理个别小Bug
|
||||
- 优化计划任务日志格式
|
||||
- 网站管理支持分页
|
||||
- 文件管理的初步实现
|
||||
- 样式微调
|
||||
- 部分代码优化
|
||||
- 框架版本更新
|
||||
|
||||
## [20221212] - 常规更新
|
||||
|
||||
- 修复创建网站可能重复添加listen端口的Bug
|
||||
|
||||
## [20221210] - 安全更新
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class Kernel extends ConsoleKernel
|
||||
// 检查文件是否存在,及所有者是否为root
|
||||
if (!file_exists($file) || fileowner($file) != 0) {
|
||||
file_put_contents('/www/server/cron/logs/'.$cron->id.'.log',
|
||||
'耗子Linux面板:检测到脚本文件异常,为确保安全已终止运行,如果你不知道发生了什么,这通常意味着服务器已被入侵。',
|
||||
PHP_EOL.'耗子Linux面板:检测到脚本文件异常,为确保安全已终止运行,如果你不知道发生了什么,这通常意味着服务器已被入侵。'.PHP_EOL,
|
||||
FILE_APPEND);
|
||||
continue;
|
||||
}
|
||||
@@ -37,10 +37,10 @@ class Kernel extends ConsoleKernel
|
||||
$cron->save();
|
||||
})->onSuccess(function () use ($cron) {
|
||||
file_put_contents('/www/server/cron/logs/'.$cron->id.'.log',
|
||||
Carbon::now()->toDateTimeString().' 任务执行成功', FILE_APPEND);
|
||||
PHP_EOL.Carbon::now()->toDateTimeString().' 任务执行成功'.PHP_EOL, FILE_APPEND);
|
||||
})->onFailure(function () use ($cron) {
|
||||
file_put_contents('/www/server/cron/logs/'.$cron->id.'.log',
|
||||
Carbon::now()->toDateTimeString().' 任务执行失败', FILE_APPEND);
|
||||
PHP_EOL.Carbon::now()->toDateTimeString().' 任务执行失败'.PHP_EOL, FILE_APPEND);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,21 +11,494 @@ use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
class FilesController extends Controller
|
||||
{
|
||||
/**
|
||||
* 获取某个目录的文件列表
|
||||
* 获取某个目录下的文件(夹)列表
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getDirList(Request $request): JsonResponse
|
||||
public function getList(Request $request): JsonResponse
|
||||
{
|
||||
$limit = $request->input('limit', 10);
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
'limit' => 'required|integer',
|
||||
'page' => 'required|integer',
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
$limit = $credentials['limit'];
|
||||
$page = $credentials['page'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '目录不存在']);
|
||||
}
|
||||
|
||||
$files = scandir($path);
|
||||
$fileData = [];
|
||||
foreach ($files as $k => $v) {
|
||||
if ($v == '.' || $v == '..') {
|
||||
continue;
|
||||
}
|
||||
$fileData[$k]['name'] = $v;
|
||||
$fileData[$k]['path'] = $path.'/'.$v;
|
||||
$fileData[$k]['type'] = filetype($path.'/'.$v);
|
||||
$fileData[$k]['size'] = filesize($path.'/'.$v);
|
||||
$fileData[$k]['mtime'] = Carbon::createFromTimestamp(filemtime($path.'/'.$v))->toDateTimeString();
|
||||
}
|
||||
|
||||
// 分页
|
||||
|
||||
$total = count($fileData);
|
||||
$filesArr = array_slice($fileData, ($page - 1) * $limit, $limit);
|
||||
$data['code'] = 0;
|
||||
$data['msg'] = 'success';
|
||||
$data['count'] = '';
|
||||
$data['data'] = '';
|
||||
$data['count'] = $total;
|
||||
$data['data'] = $filesArr;
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件内容
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getFileContent(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'file' => ['required', 'regex:/^\/.*$/'],
|
||||
]);
|
||||
$file = $credentials['file'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($file)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件不存在']);
|
||||
}
|
||||
|
||||
$content = @file_get_contents($file);
|
||||
$data['code'] = 0;
|
||||
$data['msg'] = 'success';
|
||||
$data['data'] = $content;
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存文件内容
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function saveFileContent(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'file' => ['required', 'regex:/^\/.*$/'],
|
||||
'content' => 'required',
|
||||
]);
|
||||
$file = $credentials['file'];
|
||||
$content = $credentials['content'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($file)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件不存在']);
|
||||
}
|
||||
|
||||
$res = @file_put_contents($file, $content);
|
||||
if ($res === false) {
|
||||
return response()->json(['code' => 1, 'msg' => '保存失败']);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function createDir(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
'name' => 'required',
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
$name = $credentials['name'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '目录不存在']);
|
||||
}
|
||||
|
||||
$res = @mkdir($path.'/'.$name);
|
||||
if ($res === false) {
|
||||
return response()->json(['code' => 1, 'msg' => '创建失败']);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名文件或目录
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function rename(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
'name' => 'required',
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
$name = $credentials['name'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($path) && !is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件或目录不存在']);
|
||||
}
|
||||
|
||||
$res = @rename($path, dirname($path).'/'.$name);
|
||||
if ($res === false) {
|
||||
return response()->json(['code' => 1, 'msg' => '重命名失败']);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function uploadFile(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
'file' => 'required|file',
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
$file = $credentials['file'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '目录不存在']);
|
||||
}
|
||||
|
||||
$res = @move_uploaded_file($file->getRealPath(), $path.'/'.$file->getClientOriginalName());
|
||||
if ($res === false) {
|
||||
return response()->json(['code' => 1, 'msg' => '上传失败']);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param Request $request
|
||||
* @return JsonResponse|BinaryFileResponse
|
||||
*/
|
||||
public function downloadFile(Request $request): BinaryFileResponse|JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件不存在']);
|
||||
}
|
||||
|
||||
return response()->download($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件
|
||||
*/
|
||||
public function createFile(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
'name' => 'required',
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
$name = $credentials['name'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '目录不存在']);
|
||||
}
|
||||
|
||||
$res = @file_put_contents($path.'/'.$name, '');
|
||||
if ($res === false) {
|
||||
return response()->json(['code' => 1, 'msg' => '创建失败']);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置权限
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function chmod(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
'mode' => 'required',
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
$mode = $credentials['mode'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($path) && !is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件或目录不存在']);
|
||||
}
|
||||
|
||||
$res = @chmod($path, $mode);
|
||||
if ($res === false) {
|
||||
return response()->json(['code' => 1, 'msg' => '设置失败']);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置所有者
|
||||
*/
|
||||
public function chown(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
'user' => 'required',
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
$user = $credentials['user'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($path) && !is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件或目录不存在']);
|
||||
}
|
||||
|
||||
$res = @chown($path, $user);
|
||||
if ($res === false) {
|
||||
return response()->json(['code' => 1, 'msg' => '设置失败']);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置所有者组
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function chgrp(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
'group' => 'required',
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
$group = $credentials['group'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($path) && !is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件或目录不存在']);
|
||||
}
|
||||
|
||||
$res = @chgrp($path, $group);
|
||||
if ($res === false) {
|
||||
return response()->json(['code' => 1, 'msg' => '设置失败']);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制文件/目录
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function copy(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
'dest' => ['required', 'regex:/^\/.*$/'],
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
$dest = $credentials['dest'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($path) && !is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件或目录不存在']);
|
||||
}
|
||||
|
||||
if (!is_dir($dest)) {
|
||||
return response()->json(['code' => 1, 'msg' => '目标目录不存在']);
|
||||
}
|
||||
|
||||
$res = @copy($path, $dest.'/'.basename($path));
|
||||
if ($res === false) {
|
||||
return response()->json(['code' => 1, 'msg' => '复制失败']);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动文件/目录
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function move(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
'dest' => ['required', 'regex:/^\/.*$/'],
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
$dest = $credentials['dest'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($path) && !is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件或目录不存在']);
|
||||
}
|
||||
|
||||
if (!is_dir($dest)) {
|
||||
return response()->json(['code' => 1, 'msg' => '目标目录不存在']);
|
||||
}
|
||||
|
||||
$res = @rename($path, $dest.'/'.basename($path));
|
||||
if ($res === false) {
|
||||
return response()->json(['code' => 1, 'msg' => '移动失败']);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件/目录
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function delete(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($path) && !is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件或目录不存在']);
|
||||
}
|
||||
|
||||
$res = @shell_exec('rm -rf '.escapeshellarg($path).' 2>&1');
|
||||
if (!empty($res)) {
|
||||
return response()->json(['code' => 1, 'msg' => '删除失败:'.$res]);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩文件/目录
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function zip(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($path) && !is_dir($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件或目录不存在']);
|
||||
}
|
||||
|
||||
$zipPath = dirname($path).'/'.basename($path).'.zip';
|
||||
$res = @shell_exec('zip -r '.escapeshellarg($zipPath).' '.escapeshellarg($path).' 2>&1');
|
||||
if (!empty($res)) {
|
||||
return response()->json(['code' => 1, 'msg' => '压缩失败:'.$res]);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压文件/目录
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function unzip(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$credentials = $this->validate($request, [
|
||||
'path' => ['required', 'regex:/^\/.*$/'],
|
||||
]);
|
||||
$path = $credentials['path'];
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if (!is_file($path)) {
|
||||
return response()->json(['code' => 1, 'msg' => '文件不存在']);
|
||||
}
|
||||
|
||||
$res = @shell_exec('unzip -o '.escapeshellarg($path).' -d '.escapeshellarg(dirname($path)).' 2>&1');
|
||||
if (!empty($res)) {
|
||||
return response()->json(['code' => 1, 'msg' => '解压失败:'.$res]);
|
||||
}
|
||||
|
||||
return response()->json(['code' => 0, 'msg' => 'success']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,14 @@ class WebsitesController extends Controller
|
||||
{
|
||||
/**
|
||||
* 获取面板网站
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getList(): JsonResponse
|
||||
public function getList(Request $request): JsonResponse
|
||||
{
|
||||
$websiteList = Website::query()->get();
|
||||
$limit = $request->input('limit', 10);
|
||||
|
||||
$websiteList = Website::query()->orderBy('id')->paginate($limit);
|
||||
// 判空
|
||||
if ($websiteList->isEmpty()) {
|
||||
return response()->json([
|
||||
@@ -42,7 +45,8 @@ class WebsitesController extends Controller
|
||||
return response()->json([
|
||||
'code' => 0,
|
||||
'msg' => '获取成功',
|
||||
'data' => $websiteList
|
||||
'count' => $websiteList->total(),
|
||||
'data' => $websiteList->items()
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ class AddAccessToken
|
||||
// 获取请求头中的token
|
||||
$token = $request->header('access_token') ?? $request->input('access_token');
|
||||
// 将token放入请求中
|
||||
$request->headers->set('Authorization', 'Bearer ' . $token);
|
||||
$request->headers->set('Authorization', 'Bearer '.$token);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
647
composer.lock
generated
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
return [
|
||||
'name' => '耗子Linux面板',
|
||||
'version' => '20221212',
|
||||
'version' => '20221222',
|
||||
'plugin_dir' => '/www/panel/plugins',
|
||||
];
|
||||
@@ -1,30 +1,35 @@
|
||||
layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模块也可以依赖其它模块,如:layui.define('layer', callback);
|
||||
var $ = layui.jquery,
|
||||
layer = layui.layer,
|
||||
laypage = layui.laypage;
|
||||
/**
|
||||
* Name: Layui文件管理插件(LayuiAdmin定制版)
|
||||
* Author: 李春林
|
||||
* Author: 耗子
|
||||
* Date: 2022-12-22
|
||||
*/
|
||||
layui.define(['jquery', 'layer', 'laypage', 'admin'], function (exports) { //提示:模块也可以依赖其它模块,如:layui.define('layer', callback);
|
||||
var $ = layui.jquery, layer = layui.layer, laypage = layui.laypage, admin = layui.admin;
|
||||
//外部接口
|
||||
var fm = {
|
||||
config:{'test':'test','thumb':{'nopic':'',width:100,height:100},icon_url:'ico/',btn_upload:true,btn_create:true}
|
||||
,cache: {} //数据缓存
|
||||
,index: layui.fm ? (layui.fm.index + 10000) : 0
|
||||
config: {
|
||||
'test': 'test',
|
||||
'thumb': {'nopic': '', width: 100, height: 100},
|
||||
icon_url: 'ico/',
|
||||
btn_upload: true,
|
||||
btn_create: true
|
||||
}, cache: {} //数据缓存
|
||||
, index: layui.fm ? (layui.fm.index + 10000) : 0
|
||||
//设置全局项
|
||||
,set: function(options){
|
||||
, set: function (options) {
|
||||
var that = this;
|
||||
that.config = $.extend({}, that.config, options);
|
||||
return that;
|
||||
}
|
||||
//事件监听
|
||||
,on: function(events, callback){
|
||||
, on: function (events, callback) {
|
||||
return layui.onevent.call(this, 'file', events, callback);
|
||||
}
|
||||
,dirRoot:[{'path':'','name': '根目录'}]
|
||||
,v:'1.0.1.2019.12.26'
|
||||
}, dirRoot: [{'path': '/www/wwwroot', 'name': '网站目录'}], v: '20221222'
|
||||
}
|
||||
//操作当前实例
|
||||
, thisFm = function() {
|
||||
var that = this,
|
||||
options = that.config,
|
||||
id = options.id || options.index;
|
||||
, thisFm = function () {
|
||||
var that = this, options = that.config, id = options.id || options.index;
|
||||
|
||||
// console.log(id)
|
||||
if (id) {
|
||||
@@ -32,20 +37,19 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
thisFm.config[id] = options; //记录当前实例配置项
|
||||
}
|
||||
return {
|
||||
config: options,
|
||||
reload: function(options) {
|
||||
config: options, reload: function (options) {
|
||||
that.reload.call(that, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
//获取当前实例配置项
|
||||
,getThisFmConfig = function(id){
|
||||
, getThisFmConfig = function (id) {
|
||||
var config = thisFm.config[id];
|
||||
if(!config) hint.error('The ID option was not found in the fm instance');
|
||||
if (!config) hint.error('The ID option was not found in the fm instance');
|
||||
return config || null;
|
||||
}
|
||||
//构造器
|
||||
,Class = function(options){
|
||||
, Class = function (options) {
|
||||
var that = this;
|
||||
that.config = $.extend({}, that.config, fm.config, options);
|
||||
//记录所有实例
|
||||
@@ -55,9 +59,8 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
that.render();
|
||||
};
|
||||
//渲染
|
||||
Class.prototype.render = function(){
|
||||
var that = this
|
||||
,options = that.config;
|
||||
Class.prototype.render = function () {
|
||||
var that = this, options = that.config;
|
||||
|
||||
options.elem = $(options.elem);
|
||||
options.where = options.where || {};
|
||||
@@ -65,21 +68,16 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
|
||||
//请求参数的自定义格式
|
||||
options.request = $.extend({
|
||||
pageName: 'page'
|
||||
,limitName: 'limit'
|
||||
pageName: 'page', limitName: 'limit'
|
||||
}, options.request)
|
||||
|
||||
//响应数据的自定义格式
|
||||
options.response = $.extend({
|
||||
statusName: 'code'
|
||||
,statusCode: 0
|
||||
,msgName: 'msg'
|
||||
,dataName: 'data'
|
||||
,countName: 'count'
|
||||
statusName: 'code', statusCode: 0, msgName: 'msg', dataName: 'data', countName: 'count'
|
||||
}, options.response);
|
||||
|
||||
//如果 page 传入 laypage 对象
|
||||
if(typeof options.page === 'object'){
|
||||
if (typeof options.page === 'object') {
|
||||
options.limit = options.page.limit || options.limit;
|
||||
options.limits = options.page.limits || options.limits;
|
||||
that.page = options.page.curr = options.page.curr || 1;
|
||||
@@ -87,37 +85,23 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
delete options.page.jump;
|
||||
}
|
||||
|
||||
if(!options.elem[0]) return that;
|
||||
if (!options.elem[0]) return that;
|
||||
//渲染主体
|
||||
var _btn = ''
|
||||
if(options.btn_create){
|
||||
_btn +='<button type="button" class="layui-btn layui-btn-primary layui-btn-sm" id="new_dir">建文件夹</button>';
|
||||
if (options.btn_create) {
|
||||
_btn += '<button type="button" class="layui-btn layui-btn-primary layui-btn-sm" id="new_dir">建文件夹</button>';
|
||||
}
|
||||
if(options.btn_upload){
|
||||
_btn +='<button type="button" class="layui-btn layui-btn-primary layui-btn-sm" id="uploadfile">上传文件</button>';
|
||||
if (options.btn_upload) {
|
||||
_btn += '<button type="button" class="layui-btn layui-btn-primary layui-btn-sm" id="uploadfile">上传文件</button>';
|
||||
}
|
||||
var _html = '<div class="layui-card" >' +
|
||||
'<div class="layui-card-body">' +
|
||||
'<div class="layui-btn-group tool_bar">' +
|
||||
_btn+
|
||||
'<button type="button" class="layui-btn layui-btn-primary layui-btn-sm" id="back"><i class="layui-icon layui-icon-left line"></i></button>' +
|
||||
'</div>' +
|
||||
'<div class="layui-inline path_bar" id="">' +
|
||||
'<a ><i class="layui-icon layui-icon-more-vertical line" ></i>根目录</a>' +
|
||||
'</div>' +
|
||||
'</div><hr><div class="layui-card-body">' +
|
||||
'<div class="file-body layui-form" style="">' +
|
||||
'<ul class="file layui-row fm_body layui-col-space10" >' +
|
||||
'</ul>' +
|
||||
'</div>' +
|
||||
'<hr><div ><div class="layui_page_'+options.id+'" id="layui_page_'+options.id+'"></div></div></div>';
|
||||
var _html = '<div class="layui-card" >' + '<div class="layui-card-body">' + '<div class="layui-btn-group tool_bar">' + _btn + '<button type="button" class="layui-btn layui-btn-primary layui-btn-sm" id="back"><i class="layui-icon layui-icon-left line"></i></button>' + '</div>' + '<div class="layui-inline path_bar" id="">' + '<a ><i class="layui-icon layui-icon-more-vertical line" ></i>根目录</a>' + '</div>' + '</div><hr><div class="layui-card-body">' + '<div class="file-body layui-form" style="">' + '<ul class="file layui-row fm_body layui-col-space10" >' + '</ul>' + '</div>' + '<hr><div ><div class="layui_page_' + options.id + '" id="layui_page_' + options.id + '"></div></div></div>';
|
||||
|
||||
options.elem.html(_html);
|
||||
|
||||
options.index = that.index;
|
||||
that.key = options.id || options.index;
|
||||
//各级容器
|
||||
that.layPage = options.elem.find('.layui_page_'+options.id);
|
||||
that.layPage = options.elem.find('.layui_page_' + options.id);
|
||||
that.layBody = options.elem.find('.fm_body');
|
||||
that.layPathBar = options.elem.find('.path_bar');
|
||||
that.layToolBar = options.elem.find('.tool_bar');
|
||||
@@ -129,12 +113,8 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
Class.prototype.page = 1;
|
||||
|
||||
//获得数据
|
||||
Class.prototype.pullData = function(curr) {
|
||||
var that = this,
|
||||
options = that.config,
|
||||
request = options.request,
|
||||
response = options.response,
|
||||
_status = false;
|
||||
Class.prototype.pullData = function (curr) {
|
||||
var that = this, options = that.config, request = options.request, response = options.response, _status = false;
|
||||
|
||||
that.startTime = new Date().getTime(); //渲染开始时间
|
||||
if (options.url) { //Ajax请求
|
||||
@@ -150,7 +130,7 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
|
||||
that.loading();
|
||||
|
||||
$.ajax({
|
||||
admin.req({
|
||||
type: options.method || 'get',
|
||||
url: options.url,
|
||||
contentType: options.contentType,
|
||||
@@ -158,7 +138,7 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
async: false,
|
||||
dataType: 'json',
|
||||
headers: options.headers || {},
|
||||
success: function(res) {
|
||||
success: function (res) {
|
||||
//如果有数据解析的回调,则获得其返回的数据
|
||||
if (typeof options.parseData === 'function') {
|
||||
res = options.parseData(res) || res;
|
||||
@@ -166,12 +146,9 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
//检查数据格式是否符合规范
|
||||
if (res[response.statusName] != response.statusCode) {
|
||||
|
||||
that.errorView(
|
||||
res[response.msgName] ||
|
||||
('返回的数据不符合规范,正确的成功状态码应为:"' + response.statusName + '": ' + response.statusCode)
|
||||
);
|
||||
that.errorView(res[response.msgName] || ('返回的数据不符合规范,正确的成功状态码应为:"' + response.statusName + '": ' + response.statusCode));
|
||||
} else {
|
||||
// console.log(res, curr, res[response.countName]);
|
||||
console.log(res);
|
||||
that.renderData(res, curr, res[response.countName]);
|
||||
|
||||
options.time = (new Date().getTime() - that.startTime) + ' ms'; //耗时(接口请求+视图渲染)
|
||||
@@ -179,7 +156,7 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
typeof options.done === 'function' && options.done(res, curr, res[response.countName]);
|
||||
_status = true;
|
||||
},
|
||||
error: function(e, m) {
|
||||
error: function (e, m) {
|
||||
that.errorView('数据接口请求异常:' + m);
|
||||
|
||||
}
|
||||
@@ -188,61 +165,54 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
return _status;
|
||||
};
|
||||
//数据渲染
|
||||
Class.prototype.renderData = function(res, curr, count){
|
||||
var that = this
|
||||
,options = that.config
|
||||
,data = res[options.response.dataName] || []
|
||||
Class.prototype.renderData = function (res, curr, count) {
|
||||
var that = this, options = that.config, data = res[options.response.dataName] || []
|
||||
|
||||
//渲染数据
|
||||
var _content = ''
|
||||
layui.each(data,function(i,v){
|
||||
let _img,_type;
|
||||
layui.each(data, function (i, v) {
|
||||
let _img, _type;
|
||||
_type = v.type;
|
||||
switch (v.type) {
|
||||
case 'directory':
|
||||
_img = '<div style="width:'+options.thumb['width']+'px;height:'+options.thumb['height']+'px;line-height:'+options.thumb['height']+'px"><img src="ico/dir.png" style="vertical-align:middle;"></div>';
|
||||
case 'dir':
|
||||
_img = '<div style="width:' + options.thumb['width'] + 'px;height:' + options.thumb['height'] + 'px;line-height:' + options.thumb['height'] + 'px"><img src="' + options.icon_url + 'dir.png" style="vertical-align:middle;"></div>';
|
||||
_type = 'DIR';
|
||||
break;
|
||||
default:
|
||||
|
||||
if (v.type == 'png' || v.type == 'gif' || v.type == 'jpg' || v.type == 'image') {
|
||||
_img = '<img src="' + v.thumb + '" width="'+options.thumb['width']+'" height="'+options.thumb['height']+'" onerror=\'this.src="'+options.thumb['nopic']+'"\' />';
|
||||
_img = '<img src="' + v.thumb + '" width="' + options.thumb['width'] + '" height="' + options.thumb['height'] + '" onerror=\'this.src="' + options.thumb['nopic'] + '"\' />';
|
||||
} else {
|
||||
_img = '<div style="width:'+options.thumb['width']+'px;height:'+options.thumb['height']+'px;line-height:'+options.thumb['height']+'px"><img src="' + options.icon_url + v.type + '.png" onerror=\'this.src="'+options.thumb['nopic']+'"\' /></div>';
|
||||
_img = '<div style="width:' + options.thumb['width'] + 'px;height:' + options.thumb['height'] + 'px;line-height:' + options.thumb['height'] + 'px"><img src="' + options.icon_url + v.type + '.png" onerror=\'this.src="' + options.thumb['nopic'] + '"\' /></div>';
|
||||
}
|
||||
break;
|
||||
}
|
||||
_content+='<li style="display:inline-block" data-type="'+_type+'" data-index="'+i+'">' +
|
||||
'<div class="content" align="center">'+
|
||||
_img +
|
||||
'<p class="layui-elip" title="' + v.name + '">' + v.name + ' </p>' +
|
||||
'</div>' +
|
||||
'</li>';
|
||||
_content += '<li style="display:inline-block" data-type="' + _type + '" data-index="' + i + '">' + '<div class="content" align="center">' + _img + '<p class="layui-elip" title="' + v.name + '">' + v.name + ' </p>' + '</div>' + '</li>';
|
||||
});
|
||||
options.elem.find('.file').html(_content);
|
||||
fm.cache[options.id] = data; //记录数据
|
||||
//显示隐藏分页栏
|
||||
// console.log(that.layPage)
|
||||
that.layPage[(count == 0 || (data.length === 0 && curr == 1)) ? 'addClass' : 'removeClass']('layui-hide');
|
||||
if(data.length === 0){
|
||||
if (data.length === 0) {
|
||||
return that.errorView('空目录');
|
||||
} else {
|
||||
//that.layFixed.removeClass('layui-hide');
|
||||
}
|
||||
//同步分页状态
|
||||
if(options.page){
|
||||
if (options.page) {
|
||||
// console.log(options,'layui_page_' + options.id)
|
||||
options.page = $.extend({
|
||||
elem: 'layui_page_' + options.id
|
||||
,count: count
|
||||
,limit: options.limit
|
||||
,limits: options.limits || [10,20,30,40,50,60,70,80,90]
|
||||
,groups: 3
|
||||
,layout: ['prev', 'page', 'next', 'skip', 'count', 'limit']
|
||||
,prev: '<i class="layui-icon"></i>'
|
||||
,next: '<i class="layui-icon"></i>'
|
||||
,jump: function(obj, first){
|
||||
if(!first){
|
||||
elem: 'layui_page_' + options.id,
|
||||
count: count,
|
||||
limit: options.limit,
|
||||
limits: options.limits || [10, 20, 30, 40, 50, 60, 70, 80, 90],
|
||||
groups: 3,
|
||||
layout: ['prev', 'page', 'next', 'skip', 'count', 'limit'],
|
||||
prev: '<i class="layui-icon"></i>',
|
||||
next: '<i class="layui-icon"></i>',
|
||||
jump: function (obj, first) {
|
||||
if (!first) {
|
||||
//分页本身并非需要做以下更新,下面参数的同步,主要是因为其它处理统一用到了它们
|
||||
//而并非用的是 options.page 中的参数(以确保分页未开启的情况仍能正常使用)
|
||||
that.page = obj.curr; //更新页码
|
||||
@@ -257,135 +227,124 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
}
|
||||
};
|
||||
//更新路径工具条
|
||||
Class.prototype.updatePathBar = function(){
|
||||
Class.prototype.updatePathBar = function () {
|
||||
// console.log('updatePathBar',fm.dirRoot);
|
||||
var that = this
|
||||
,options = that.config;
|
||||
var that = this, options = that.config;
|
||||
//请求数据
|
||||
let dir_cur = fm.dirRoot[fm.dirRoot.length -1];
|
||||
options.where = {'path':dir_cur['path']}
|
||||
let dir_cur = fm.dirRoot[fm.dirRoot.length - 1];
|
||||
options.where = {'path': dir_cur['path']}
|
||||
let _rs = that.pullData(1);
|
||||
// console.log(_rs)
|
||||
if(false == _rs) return;
|
||||
if (false == _rs) return;
|
||||
that.layPathBar.html('');
|
||||
|
||||
fm.dirRoot.map(function(item,index,arr){
|
||||
let icon = index==0 ?'layui-icon-more-vertical':'layui-icon-right';
|
||||
let html = '<i class="layui-icon '+icon+'"></i>'+
|
||||
'<a data-path="' + item.path + '" data-name="' + item.name + '" >' + item.name + '</a>'
|
||||
fm.dirRoot.map(function (item, index, arr) {
|
||||
let icon = index == 0 ? 'layui-icon-more-vertical' : 'layui-icon-right';
|
||||
let html = '<i class="layui-icon ' + icon + '"></i>' + '<a data-path="' + item.path + '" data-name="' + item.name + '" >' + item.name + '</a>'
|
||||
that.layPathBar.append(html);
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
//事件处理
|
||||
Class.prototype.events = function(){
|
||||
var that = this
|
||||
,options = that.config
|
||||
,_BODY = $('body')
|
||||
,dict = {}
|
||||
,filter = options.elem.attr('lay-filter');
|
||||
Class.prototype.events = function () {
|
||||
var that = this, options = that.config, _BODY = $('body'), dict = {}, filter = options.elem.attr('lay-filter');
|
||||
//文件事件
|
||||
that.layBody.on('click', 'li', function(){ //单击行
|
||||
that.layBody.on('click', 'li', function () { //单击行
|
||||
setPicEvent.call(this, 'pic');
|
||||
});
|
||||
//文件夹事件
|
||||
that.layBody.on('click', 'li[data-type=DIR]', function(){ //单击行
|
||||
that.layBody.on('click', 'li[data-type=DIR]', function () { //单击行
|
||||
var othis = $(this);
|
||||
var data = fm.cache[options.id];
|
||||
var data = fm.cache[options.id];
|
||||
var index = othis.data('index');
|
||||
data = data[index] || {};
|
||||
|
||||
//导航图标
|
||||
fm.dirRoot.push({'path':data.path,'name': data.name});
|
||||
fm.dirRoot.push({'path': data.path, 'name': data.name});
|
||||
that.updatePathBar();
|
||||
});
|
||||
//返回上一级目录
|
||||
that.layToolBar.on('click', '#back', function(){
|
||||
that.layToolBar.on('click', '#back', function () {
|
||||
var othis = $(this);
|
||||
if(fm.dirRoot.length == 1) return layer.msg('已经是根目录');
|
||||
if (fm.dirRoot.length == 1) return layer.msg('已经是根目录');
|
||||
|
||||
fm.dirRoot.length >1 && fm.dirRoot.pop()
|
||||
fm.dirRoot.length > 1 && fm.dirRoot.pop()
|
||||
that.updatePathBar();
|
||||
|
||||
// console.log('back');
|
||||
});
|
||||
//上传文件
|
||||
that.layToolBar.on('click', '#uploadfile', function(){
|
||||
that.layToolBar.on('click', '#uploadfile', function () {
|
||||
var othis = $(this);
|
||||
let eventType = 'uploadfile';
|
||||
layui.event.call(this,
|
||||
'file', eventType + '('+ filter +')'
|
||||
,{obj:othis,path:fm.dirRoot[fm.dirRoot.length -1]['path']}
|
||||
);
|
||||
layui.event.call(this, 'file', eventType + '(' + filter + ')', {
|
||||
obj: othis,
|
||||
path: fm.dirRoot[fm.dirRoot.length - 1]['path']
|
||||
});
|
||||
// console.log('uploadfile');
|
||||
});
|
||||
//新建文件夹
|
||||
that.layToolBar.on('click', '#new_dir', function(){
|
||||
that.layToolBar.on('click', '#new_dir', function () {
|
||||
var othis = $(this);
|
||||
let eventType = 'new_dir';
|
||||
layer.prompt({ title: '请输入新文件夹名字', formType: 0 }, function(name, index) {
|
||||
layer.prompt({title: '请输入新文件夹名字', formType: 0}, function (name, index) {
|
||||
layer.close(index);
|
||||
//新建文件夹
|
||||
layui.event.call(this,
|
||||
'file', eventType + '('+ filter +')'
|
||||
,{obj:othis,folder:name,path:fm.dirRoot[fm.dirRoot.length -1]['path']}
|
||||
);
|
||||
layui.event.call(this, 'file', eventType + '(' + filter + ')', {
|
||||
obj: othis,
|
||||
folder: name,
|
||||
path: fm.dirRoot[fm.dirRoot.length - 1]['path']
|
||||
});
|
||||
});
|
||||
});
|
||||
//创建点击文件事件监听
|
||||
var setPicEvent = function(eventType) {
|
||||
var setPicEvent = function (eventType) {
|
||||
var othis = $(this);
|
||||
var data = fm.cache[options.id];
|
||||
var data = fm.cache[options.id];
|
||||
var index = othis.data('index');
|
||||
if (othis.data('type')=='DIR') return; //不触发事件
|
||||
if (othis.data('type') == 'DIR') return; //不触发事件
|
||||
data = data[index] || {};
|
||||
layui.event.call(this,
|
||||
'file', eventType + '('+ filter +')'
|
||||
,{obj:othis,data:data}
|
||||
);
|
||||
layui.event.call(this, 'file', eventType + '(' + filter + ')', {obj: othis, data: data});
|
||||
};
|
||||
};
|
||||
|
||||
//请求loading
|
||||
Class.prototype.loading = function(hide){
|
||||
var that = this
|
||||
,options = that.config;
|
||||
if(options.loading){
|
||||
if(hide){
|
||||
Class.prototype.loading = function (hide) {
|
||||
var that = this, options = that.config;
|
||||
if (options.loading) {
|
||||
if (hide) {
|
||||
that.layInit && that.layInit.remove();
|
||||
delete that.layInit;
|
||||
that.layBox.find(ELEM_INIT).remove();
|
||||
} else {
|
||||
that.layInit = $(['<div class="layui-table-init">'
|
||||
,'<i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i>'
|
||||
,'</div>'].join(''));
|
||||
that.layInit = $(['<div class="layui-table-init">', '<i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i>', '</div>'].join(''));
|
||||
that.layBox.append(that.layInit);
|
||||
}
|
||||
}
|
||||
};
|
||||
//异常提示
|
||||
Class.prototype.errorView = function(html){
|
||||
Class.prototype.errorView = function (html) {
|
||||
var that = this
|
||||
layer.msg(html);
|
||||
|
||||
};
|
||||
//重载
|
||||
Class.prototype.reload = function(options){
|
||||
Class.prototype.reload = function (options) {
|
||||
var that = this;
|
||||
|
||||
options = options || {};
|
||||
delete that.haveInit;
|
||||
|
||||
if(options.data && options.data.constructor === Array) delete that.config.data;
|
||||
if (options.data && options.data.constructor === Array) delete that.config.data;
|
||||
that.config = $.extend(true, {}, that.config, options);
|
||||
|
||||
that.render();
|
||||
};
|
||||
//重载
|
||||
fm.reload = function(id, options){
|
||||
fm.reload = function (id, options) {
|
||||
var config = getThisFmConfig(id); //获取当前实例配置项
|
||||
if(!config) return;
|
||||
if (!config) return;
|
||||
|
||||
var that = thisFm.that[id];
|
||||
that.reload(options);
|
||||
@@ -393,7 +352,7 @@ layui.define(['jquery', 'layer', 'laypage'], function(exports) { //提示:模
|
||||
return thisFm.call(that);
|
||||
};
|
||||
//核心入口
|
||||
fm.render = function(options){
|
||||
fm.render = function (options) {
|
||||
var inst = new Class(options);
|
||||
return thisFm.call(inst);
|
||||
};
|
||||
|
||||
BIN
public/panel/style/ico/ai.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/apk.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/bt.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/cad.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/code.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/dir.png
Normal file
|
After Width: | Height: | Size: 358 B |
BIN
public/panel/style/ico/doc.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/eps.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/exe.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/fla.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/fonts.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/ipa.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/keynote.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/links.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/misc.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/mm.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/mmap.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/mp3.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/mp4.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/null.jpg
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/panel/style/ico/number.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/pages.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/pdf.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/ppt.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/ps.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/rar.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/txt.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/panel/style/ico/visio.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/web.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/panel/style/ico/xls.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/xmind.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/panel/style/ico/zip.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -95,7 +95,7 @@
|
||||
, {field: 'updated_at', title: '上次运行时间'}
|
||||
, {
|
||||
field: 'edit',
|
||||
width: 170,
|
||||
width: 180,
|
||||
title: '操作',
|
||||
templet: '#cron-table-edit',
|
||||
fixed: 'right',
|
||||
|
||||
@@ -1,2 +1,48 @@
|
||||
<!--
|
||||
Name: 文件管理
|
||||
Author: 耗子
|
||||
Date: 2022-12-22
|
||||
-->
|
||||
<title>文件管理</title>
|
||||
<h1 style="text-align: center; padding-top: 20px;">文件管理正在开发中!</h1>
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">该功能尚未完成开发,请等待!(推荐使用sftp临时代替)</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-fluid">
|
||||
<div id="files" lay-filter="files"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
layui.use(['layer', 'admin', 'view', 'form', 'file'], function () {
|
||||
let $ = layui.jquery;
|
||||
let admin = layui.admin;
|
||||
let view = layui.view;
|
||||
let form = layui.form;
|
||||
let file = layui.file;
|
||||
|
||||
let path = '/www/wwwroot';// 当前路径
|
||||
|
||||
let router = layui.router();
|
||||
if (router.search.path) {
|
||||
path = window.atob(router.search.path);
|
||||
}
|
||||
|
||||
file.render({
|
||||
elem: '#files'
|
||||
, method: 'get'
|
||||
, id: 'files-list'
|
||||
, btn_upload: true
|
||||
, btn_create: true
|
||||
, url: '/api/panel/file/getList'
|
||||
, thumb: {'nopic': '/panel/style/ico/null.jpg', 'width': 100, 'height': 100}
|
||||
, icon_url: '/panel/style/ico/'
|
||||
, done: function (res, curr, count) {
|
||||
// console.log(res,curr,count)
|
||||
}
|
||||
, page: {limit: 10}
|
||||
, where: {path: '/www/wwwroot'}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -85,12 +85,9 @@ Date: 2022-11-28
|
||||
, {field: 'php', title: 'PHP', width: 60}
|
||||
, {field: 'ssl', title: 'SSL', width: 110, templet: '#website-ssl'}
|
||||
, {field: 'note', title: '备注', edit: 'textarea'}
|
||||
, {fixed: 'right', title: '操作', unresize: true, toolbar: '#website-control', width: 160}
|
||||
, {fixed: 'right', title: '操作', unresize: true, toolbar: '#website-control', width: 180}
|
||||
]]
|
||||
/**
|
||||
* TODO: 分页
|
||||
*/
|
||||
//, page: true
|
||||
, page: true
|
||||
});
|
||||
|
||||
// 头工具栏事件
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Api\CronsController;
|
||||
use App\Http\Controllers\Api\FilesController;
|
||||
use App\Http\Controllers\Api\MonitorsController;
|
||||
use App\Http\Controllers\Api\PluginsController;
|
||||
use App\Http\Controllers\Api\SafesController;
|
||||
@@ -8,11 +9,10 @@ use App\Http\Controllers\Api\SettingsController;
|
||||
use App\Http\Controllers\Api\TasksController;
|
||||
use App\Http\Controllers\Api\UsersController;
|
||||
use App\Http\Controllers\Api\WebsitesController;
|
||||
use App\Http\Controllers\Api\InfosController;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
use App\Http\Controllers\Api\InfosController;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| API Routes
|
||||
@@ -63,31 +63,44 @@ Route::prefix('panel')->group(function () {
|
||||
Route::get('getInstalledDbAndPhp', [InfosController::class, 'getInstalledDbAndPhp']);
|
||||
|
||||
});
|
||||
// 网站
|
||||
// 网站管理
|
||||
Route::middleware('auth:sanctum')->prefix('website')->group(function () {
|
||||
// 获取网站列表
|
||||
// 获取默认设置
|
||||
Route::get('getDefaultSettings', [WebsitesController::class, 'getDefaultSettings']);
|
||||
// 保存默认设置
|
||||
Route::post('saveDefaultSettings', [WebsitesController::class, 'saveDefaultSettings']);
|
||||
// 获取网站列表
|
||||
Route::get('getList', [WebsitesController::class, 'getList']);
|
||||
// 添加网站
|
||||
Route::post('add', [WebsitesController::class, 'add']);
|
||||
// 删除网站
|
||||
Route::post('delete', [WebsitesController::class, 'delete']);
|
||||
// 获取网站设置
|
||||
Route::get('getSiteSettings', [WebsitesController::class, 'getSiteSettings']);
|
||||
// 保存网站设置
|
||||
Route::post('saveSiteSettings', [WebsitesController::class, 'saveSiteSettings']);
|
||||
// 清空网站日志
|
||||
Route::post('clearSiteLog', [WebsitesController::class, 'clearSiteLog']);
|
||||
// 更新网站备注
|
||||
Route::post('updateSiteNote', [WebsitesController::class, 'updateSiteNote']);
|
||||
// 设置网站状态
|
||||
Route::post('setSiteStatus', [WebsitesController::class, 'setSiteStatus']);
|
||||
// 获取备份列表
|
||||
Route::get('getBackupList', [WebsitesController::class, 'getBackupList']);
|
||||
// 创建备份
|
||||
Route::post('createBackup', [WebsitesController::class, 'createBackup']);
|
||||
// 上传备份
|
||||
Route::post('uploadBackup', [WebsitesController::class, 'uploadBackup']);
|
||||
// 恢复备份
|
||||
Route::post('restoreBackup', [WebsitesController::class, 'restoreBackup']);
|
||||
// 删除备份
|
||||
Route::post('deleteBackup', [WebsitesController::class, 'deleteBackup']);
|
||||
// 重置网站配置
|
||||
Route::post('resetSiteConfig', [WebsitesController::class, 'resetSiteConfig']);
|
||||
// 签发SSL证书
|
||||
Route::post('issueSsl', [WebsitesController::class, 'issueSsl']);
|
||||
});
|
||||
// 监控
|
||||
// 资源监控
|
||||
Route::middleware('auth:sanctum')->prefix('monitor')->group(function () {
|
||||
// 获取监控数据
|
||||
Route::get('getMonitorData', [MonitorsController::class, 'getMonitorData']);
|
||||
@@ -100,7 +113,7 @@ Route::prefix('panel')->group(function () {
|
||||
// 清空监控数据
|
||||
Route::post('clearMonitorData', [MonitorsController::class, 'clearMonitorData']);
|
||||
});
|
||||
// 安全
|
||||
// 系统安全
|
||||
Route::middleware('auth:sanctum')->prefix('safe')->group(function () {
|
||||
// 获取防火墙状态
|
||||
Route::get('getFirewallStatus', [SafesController::class, 'getFirewallStatus']);
|
||||
@@ -125,26 +138,40 @@ Route::prefix('panel')->group(function () {
|
||||
// 删除防火墙规则
|
||||
Route::post('deleteFirewallRule', [SafesController::class, 'deleteFirewallRule']);
|
||||
});
|
||||
// 插件
|
||||
Route::middleware('auth:sanctum')->prefix('plugin')->group(function () {
|
||||
// 获取插件列表
|
||||
Route::get('getList', [PluginsController::class, 'getList']);
|
||||
Route::post('install', [PluginsController::class, 'install']);
|
||||
Route::post('uninstall', [PluginsController::class, 'uninstall']);
|
||||
Route::post('update', [PluginsController::class, 'update']);
|
||||
Route::post('setShowHome', [PluginsController::class, 'setShowHome']);
|
||||
// 文件管理
|
||||
Route::middleware('auth:sanctum')->prefix('file')->group(function () {
|
||||
// 获取文件(夹)列表
|
||||
Route::get('getList', [FilesController::class, 'getList']);
|
||||
});
|
||||
// 计划任务
|
||||
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('plugin')->group(function () {
|
||||
// 获取插件列表
|
||||
Route::get('getList', [PluginsController::class, 'getList']);
|
||||
// 安装插件
|
||||
Route::post('install', [PluginsController::class, 'install']);
|
||||
// 卸载插件
|
||||
Route::post('uninstall', [PluginsController::class, 'uninstall']);
|
||||
// 更新插件
|
||||
Route::post('update', [PluginsController::class, 'update']);
|
||||
// 设置插件首页显示
|
||||
Route::post('setShowHome', [PluginsController::class, 'setShowHome']);
|
||||
});
|
||||
// 面板设置
|
||||
Route::middleware('auth:sanctum')->prefix('setting')->group(function () {
|
||||
// 获取设置
|
||||
Route::get('get', [SettingsController::class, 'get']);
|
||||
@@ -152,4 +179,3 @@ Route::prefix('panel')->group(function () {
|
||||
Route::post('save', [SettingsController::class, 'save']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||