41 Commits

Author SHA1 Message Date
wolfcode
7a4fd01c4c Update composer.json 2025-04-01 11:28:50 +08:00
wolfcode
de07c44890 Update Admin.php 2025-03-27 20:14:06 +08:00
wolfcode
bc03616e43 refactor(admin): refactor model generation and improve array string handling
- Update CommonTool to fix array string formatting issue- Refactor model generation template to use getOptions method
2025-03-27 14:58:32 +08:00
wolfcode
8aba56c8c2 🚀 Layui v2.10.1 2025-03-27 11:22:43 +08:00
wolfcode
150e0ecd23 Update composer.json 2025-03-26 16:09:01 +08:00
wolfcode
e9ed0cd8f6 Merge branch 'main' of https://github.com/easyadmin8/EasyAdmin8 2025-03-26 14:03:22 +08:00
wolfcode
187d4343b3 refactor(admin): remove unused 'where' option in system module
- Remove unnecessary 'where' option from various models in system module
- Simplify query methods by removing redundant 'where' calls
- Affected models: Config, Node, SystemAdmin, SystemMenu, SystemNode
2025-03-26 14:00:58 +08:00
wolfcode
9bc0185b6b refactor(admin): improve error handling in Admin controller
- Enhance error messages by appending exception details
- Remove unnecessary password field handling in update scenario
2025-03-26 11:20:06 +08:00
wolfcode
652b17d6a6 refactor(admin): move auth_ids explode logic to model
- Remove auth_ids explode logic from Admin controller
- Add getAuthIdsAttr method to SystemAdmin model for auth_ids parsing
- This change improves code organization and reusability
2025-03-26 10:03:59 +08:00
wolfcode
ed8c3d545b refactor(admin):适配新版 think-orm remove console.log and refactor MallGoods model
- Remove unnecessary console.log statement from log.js
- Refactor MallGoods model to use getOptions method for deleteTime
2025-03-25 18:38:07 +08:00
wolfcode
12b38c7bf5 refactor(admin): optimize log data processing and display
- Update log data serialization and deserialization method
- Improve log data display format in the admin interface- Refactor log model initialization and table suffix handling
- Optimize time model configuration for better timestamp management
2025-03-25 18:08:35 +08:00
wolfcode
fc202be987 build(deps): update topthink/think-orm to 4.0.3
- Specify version 4.0.3 for topthink/think-orm in composer.json
- This change pins the ORM package to a specific version for stability and compatibility
2025-03-25 15:02:33 +08:00
wolfcode
71e069712a style(easy-admin): adjust image height in admin table
- Change image height from 40 to 30 in the admin table
2025-03-19 18:32:26 +08:00
wolfcode
ca4080d5e6 feat(config-admin): integrate lazyload for image optimization
- Add lazyload plugin to config-admin.js
- Implement lazyload functionality in easy-admin.js
- Add lazyload.min.js file to project
2025-03-18 17:10:46 +08:00
wolfcode
4bbe287626 refactor(system): upgrade tabs functionality and improve error handling
- Replace element.on with tabs.on for better tab management Add success and error handling for form submission
- Update HTML structure to use layui-tabs for improved UI
- Remove unnecessary Vue import
2025-03-17 18:02:06 +08:00
wolfcode
e316cd40e0 🚀 Layui v2.10.0 2025-03-17 18:01:02 +08:00
wolfcode
a7a3ddef8b refactor(easy-admin): improve AI optimization feature and enhance user experience
- Remove line-height style from AI optimization content div
- Add smooth scrolling to the end of the content when streaming output Move input validation to the beginning of the aiOptimization function
2025-03-10 11:54:41 +08:00
wolfcode
51f2cbc0f4 feat(mall): 新增AI对话支持 add AI optimization function for product titles
- Integrate AI functionality to optimize product titles
- Add AI optimization button in product add and edit pages
- Implement AI chat service for generating optimized titles
- Update backend to support AI optimization requests
- Add error handling and loading state for AI optimization process
2025-03-07 17:58:11 +08:00
wolfcode
8acf9f3f6c Update .example.env 2025-03-06 11:02:49 +08:00
wolfcode
d7b23f305d Update RateLimiting.php 2025-03-05 18:31:45 +08:00
wolfcode
fdfe2a542a Update RateLimiting.php 2025-03-05 15:25:04 +08:00
wolfcode
3d412d9ec6 Update index.html 2025-03-05 09:18:41 +08:00
wolfcode
f75ebffa5d feat(admin): 新增限流器 add rate limiting middleware for backend
- Add rate limiting functionality to the admin panel
- Implement rate limiting middleware to control API request frequency
- Update login controller to use rate limiting Add helper functions for getting IP and admin UID
- Update route configuration to include rate limiting middleware
- Add Redis configuration to .env file
- Update composer.json to include rate limiting package dependency
2025-03-04 17:26:24 +08:00
wolfcode
77881a27ed fix(curd): resolve select option value binding issue
- Update buildOptionView method to use correct field parameter
- Add conditional check for templateValue before appending to indexCols
2025-03-03 17:58:35 +08:00
wolfcode
07e11a1c45 fix(curd): improve CURD generation logic
- Update relation handling in BuildCurd.php:
  - Modify how foreign key relationships are constructed
  - Remove unnecessary template generation for non-editable fields

- Enhance command processing in CurdGenerate.php:
  - Filter and re-index command arguments to improve flexibility
2025-03-03 17:42:52 +08:00
wolfcode
8531ec89ad fix(admin): handle exceptions when reflecting on controller methods
- Add try-catch block around ReflectionMethod usage
- Catch and ignore any Throwable that occurs during reflection
- Improve error handling and prevent potential fatal errors
2025-02-25 10:50:18 +08:00
wolfcode
ff2e842c24 🚀 Layui v2.9.23 2025-02-21 11:11:24 +08:00
wolfcode
3d767643c8 Update BuildCurd.php 2025-02-20 15:40:47 +08:00
wolfcode
08fb6ef4d0 feat(log): 新增支持删除部分日志 add function to delete logs older than specified months
- Add deleteMonthLog functionality to log system
- Create new route and controller method for handling log deletion
- Implement frontend UI and logic for selecting and confirming log deletion-Add necessary database queries to safely delete logs
2025-02-19 11:23:46 +08:00
wolfcode
32d94bb3e0 feat(log): 新增支持删除部分日志 add function to delete logs older than specified months
- Add deleteMonthLog functionality to log system
- Create new route and controller method for handling log deletion
- Implement frontend UI and logic for selecting and confirming log deletion-Add necessary database queries to safely delete logs
2025-02-19 11:07:06 +08:00
wolfcode
d93483d9bf Update README.md 2025-02-18 13:44:24 +08:00
wolfcode
973e9cb24c feat(orm): upgrade think-orm to version 4.0 and add BaseEntity class
- Upgrade topthink/think-orm from ^3.0 to ^4.0 in composer.json
- Add new BaseEntity class in app/common/entity to handle common entity options
2025-02-14 16:12:12 +08:00
wolfcode
b510042323 Update Goods.php 2025-02-14 11:42:40 +08:00
wolfcode
b55dd8f67a feat(auth): add support for ignoring node authentication via annotation
- Add checkNodeAnnotationAttrAuth method to AuthService for annotation-based auth control
- Update checkAuth method to use the new annotation check-Modify Goods controller to use NodeAnnotation for specifying auth requirements
- Remove unused library imports in config-admin.js
2025-02-14 11:36:07 +08:00
wolfcode
8e488fb46c 🚀 Layui v2.9.21 2025-02-10 10:40:14 +08:00
wolfcode
d99e168583 Update Install.php 2025-01-15 09:54:11 +08:00
wolfcode
d6bb1456fa build(dependencies): update composer dependencies and adjust view rendering
- Update topthink/think-view dependency to version ^2.0
- Modify view rendering in Install controller to use empty template path
2025-01-15 09:45:07 +08:00
wolfcode
91eac36371 refactor(view): remove redundant template path generation
- Remove unnecessary code that generates a template path based on the request pathinfo
- Simplify the render function by removing the conditional block that sets the template path
2025-01-15 09:28:15 +08:00
wolfcode
1e4486989a refactor(admin): improve login check middleware
- Rename $ignoreAuth to $ignoreLogin for better clarity
- Update comments for better code readability
- Modify Login controller to use $ignoreLogin instead of $ignoreAuth
2025-01-14 10:09:58 +08:00
wolfcode
f9f25b76dd Merge branch 'main' of https://github.com/easyadmin8/EasyAdmin8 2025-01-12 18:13:17 +08:00
wolfcode
7603cdfa7e feat(admin): add middleware annotation for login exemption
- Add MiddlewareAnnotation to CheckLogin middleware for login exemption
- Implement IGNORE_LOGIN constant in MiddlewareAnnotation
- Use MiddlewareAnnotation in Goods controller for login exemption

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2025-01-12 18:12:28 +08:00
60 changed files with 760 additions and 208 deletions

View File

@@ -14,6 +14,16 @@ DB_PORT=3306
DB_CHARSET=utf8mb4
DB_PREFIX=ea8_
# 限流器开关 若启动需要配置 Redis 服务
RATE_LIMITING_STATUS=false
# Redis配置
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_PREFIX=
REDIS_DATABASE=0
# 后台配置项组
[EASYADMIN]

View File

@@ -1,5 +1,5 @@
<div align="center" dir="auto">
<img alt="log" src="/public/static/common/images/logo-8.png" />
<img alt="log" src="public/static/common/images/logo-8.png" />
<p>
<img src="https://img.shields.io/badge/php-%3E=8.1.0-brightgreen.svg?style=for-the-badge&logo=php&colorB=ff69b4" alt="php">
@@ -21,11 +21,11 @@
## 项目介绍
> `EasyAdmin8` 在 [`EasyAdmin`](https://gitee.com/zhongshaofa/easyadmin) 的基础上更新 ThinkPHP 框架到 8.0+ PHP 最低版本要求不低于 8.1
> `EasyAdmin8` 在 [`EasyAdmin`](https://gitee.com/zhongshaofa/easyadmin) 的基础上更新 ThinkPHP 框架到 8.1+ PHP 最低版本要求不低于 8.1
>
> 2025年起 `PHP` 版本要求提升到 `8.1+`, 如果需要 `8.0` 到分支 `v8.0` 中下载
>
> ThinkPHP v8.0+ 和 Layui v2.9.x 的快速开发的后台管理系统。
> ThinkPHP v8.1+ 和 Layui v2.9.x 的快速开发的后台管理系统。
>
> 项目地址:[http://easyadmin8.top](http://easyadmin8.top)
>

View File

@@ -4,6 +4,7 @@ use app\admin\middleware\CheckInstall;
use app\admin\middleware\CheckLogin;
use app\admin\middleware\CheckAuth;
use app\admin\middleware\SystemLog;
use app\admin\middleware\RateLimiting;
// 你可以在这里继续写你需要的路由
@@ -16,6 +17,8 @@ use app\admin\middleware\SystemLog;
return [
'middleware' => [
// 限流中间件
RateLimiting::class,
// 判断是否已经安装后台系统
CheckInstall::class,
// 检测是否登录

View File

@@ -213,4 +213,23 @@ class Ajax extends AdminController
return json($config);
}
}
public function composerInfo(): Json
{
$lockFilePath = root_path() . '/composer.lock';
$list = [];
if (file_exists($lockFilePath)) {
$lockFileContent = file_get_contents($lockFilePath);
if ($lockFileContent !== false) {
$lockData = json_decode($lockFileContent, true);
if (!empty($lockData['packages'])) {
foreach ($lockData['packages'] as $package) {
$list[] = ['name' => $package['name'], 'version' => $package['version']];
}
}
}
}
$this->success('success', $list);
}
}

View File

@@ -4,17 +4,19 @@ namespace app\admin\controller;
use app\admin\model\SystemAdmin;
use app\common\controller\AdminController;
use app\common\utils\Helper;
use think\captcha\facade\Captcha;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use app\Request;
use think\Response;
use Wolfcode\RateLimiting\Attributes\RateLimitingMiddleware;
class Login extends AdminController
{
protected bool $ignoreAuth = true;
protected bool $ignoreLogin = true;
public function initialize(): void
{
@@ -34,6 +36,7 @@ class Login extends AdminController
* @throws DbException
* @throws ModelNotFoundException
*/
#[RateLimitingMiddleware(key: [Helper::class, 'getIp'], seconds: 1, limit: 1, message: '请求过于频繁')]
public function index(Request $request): string
{
$captcha = env('EASYADMIN.CAPTCHA', 1);

View File

@@ -4,12 +4,15 @@ namespace app\admin\controller\mall;
use app\admin\model\MallCate;
use app\admin\model\MallGoods;
use app\admin\service\annotation\MiddlewareAnnotation;
use app\common\controller\AdminController;
use app\admin\service\annotation\ControllerAnnotation;
use app\admin\service\annotation\NodeAnnotation;
use app\Request;
use think\App;
use think\response\Json;
use Wolfcode\Ai\Enum\AiType;
use Wolfcode\Ai\Service\AiChatService;
#[ControllerAnnotation(title: '商城商品管理')]
class Goods extends AdminController
@@ -66,4 +69,67 @@ class Goods extends AdminController
return $this->fetch();
}
#[MiddlewareAnnotation(ignore: MiddlewareAnnotation::IGNORE_LOGIN)]
public function no_check_login(Request $request): string
{
return '这里演示方法不需要经过登录验证';
}
#[NodeAnnotation(title: 'AI优化', auth: true)]
public function aiOptimization(Request $request): void
{
$message = $request->post('message');
if (empty($message)) $this->error('请输入内容');
// 演示环境下 默认返回的内容
if ($this->isDemo) {
$content = <<<EOF
演示环境中 默认返回的内容
我来帮你优化这个标题,让它更有吸引力且更符合电商平台的搜索逻辑:
"商务男士高端定制马克杯 | 办公室精英必备 | 优质陶瓷防烫手柄"
这个优化后的标题:
1. 突出了目标用户群体(商务男士)
2. 强调了产品定位(高端定制)
3. 点明了使用场景(办公室)
4. 添加了材质和功能特点(优质陶瓷、防烫手柄)
5. 使用了吸引人的关键词(精英必备)
这样的标题不仅更具体,也更容易被搜索引擎识别,同时能精准触达目标客户群。您觉得这个版本如何?
EOF;
$choices = [['message' => [
'role' => 'assistant',
'content' => $content,
]]];
$this->success('success', compact('choices'));
}
try {
$result = AiChatService::instance()
// 当使用推理模型时,可能存在超时的情况,所以需要设置超时时间为 0
// ->setTimeLimit(0)
// 请替换为您需要的模型类型
->setAiType(AiType::QWEN)
// 如果需要指定模型的 API 地址,可自行设置
// ->setAiUrl('https://xxx.com')
// 请替换为您的模型
->setAiModel('qwen-plus')
// 请替换为您的 API KEY
->setAiKey('sk-1234567890')
// 此内容会作为系统提示,会影响到回答的内容 当前仅作为测试使用
->setSystemContent('你现在是一位资深的海外电商产品经理')
->chat($message);
$choices = $result['choices'];
}catch (\Throwable $exception) {
$choices = [['message' => [
'role' => 'assistant',
'content' => $exception->getMessage(),
]]];
}
$this->success('success', compact('choices'));
}
}

View File

@@ -70,7 +70,7 @@ class Admin extends AdminController
try {
$save = $this->model->save($post);
}catch (\Exception $e) {
$this->error('保存失败');
$this->error('保存失败' . $e->getMessage());
}
$save ? $this->success('保存成功') : $this->error('保存失败');
}
@@ -88,18 +88,14 @@ class Admin extends AdminController
$post['auth_ids'] = implode(',', array_keys($authIds));
$rule = [];
$this->validate($post, $rule);
if (isset($row['password'])) {
unset($row['password']);
}
try {
$save = $row->save($post);
TriggerService::updateMenu($id);
}catch (\Exception $e) {
$this->error('保存失败');
$this->error('保存失败' . $e->getMessage());
}
$save ? $this->success('保存成功') : $this->error('保存失败');
}
$row->auth_ids = explode(',', $row->auth_ids ?: '');
$this->assign('row', $row);
return $this->fetch();
}
@@ -128,7 +124,7 @@ class Admin extends AdminController
}
$save ? $this->success('保存成功') : $this->error('保存失败');
}
$row->auth_ids = explode(',', $row->auth_ids ?: '');
// $row->auth_ids = explode(',', $row->auth_ids ?: '');
$this->assign('row', $row);
return $this->fetch();
}

View File

@@ -41,12 +41,12 @@ class Config extends AdminController
if ($group == 'upload') {
$upload_types = config('admin.upload_types');
// 兼容旧版本
$this->model->where('name', 'upload_allow_type')->update(['value' => implode(',', array_keys($upload_types))]);
$this->model->removeOption()->where('name', 'upload_allow_type')->update(['value' => implode(',', array_keys($upload_types))]);
}
foreach ($post as $key => $val) {
if (in_array($key, $notAddFields)) continue;
if ($this->model->where('name', $key)->count()) {
$this->model->where('name', $key)->update(['value' => $val,]);
if ($this->model->removeOption()->where('name', $key)->count()) {
$this->model->removeOption()->where('name', $key)->update(['value' => $val,]);
}else {
$this->model->create(
[
@@ -59,7 +59,7 @@ class Config extends AdminController
TriggerService::updateMenu();
TriggerService::updateSysConfig();
}catch (\Exception $e) {
$this->error('保存失败');
$this->error('保存失败' . $e->getMessage());
}
$this->success('保存成功');
}

View File

@@ -137,6 +137,7 @@ class CurdGenerate extends AdminController
$command = $request->post('command', '');
if (empty($command)) $this->error('请输入命令');
$commandExp = explode(' ', $command);
$commandExp = array_values(array_filter($commandExp));
try {
$output = Console::call('curd', [...$commandExp]);

View File

@@ -34,7 +34,7 @@ class Log extends AdminController
}
[$page, $limit, $where, $excludeFields] = $this->buildTableParams(['month']);
$month = !empty($excludeFields['month']) ? date('Ym', strtotime($excludeFields['month'])) : date('Ym');
$model = $this->model->setMonth($month)->with('admin')->where($where);
$model = $this->model->setSuffix("_$month")->with('admin')->where($where);
try {
$count = $model->count();
$list = $model->page($page, $limit)->order($this->sort)->select();
@@ -60,7 +60,8 @@ class Log extends AdminController
$this->error('演示环境下不允许操作');
}
[$page, $limit, $where, $excludeFields] = $this->buildTableParams(['month']);
$tableName = $this->model->getName();
$month = !empty($excludeFields['month']) ? date('Ym', strtotime($excludeFields['month'])) : date('Ym');
$tableName = $this->model->setSuffix("_$month")->getName();
$tableName = CommonTool::humpToLine(lcfirst($tableName));
$prefix = config('database.connections.mysql.prefix');
$dbList = Db::query("show full columns from {$prefix}{$tableName}");
@@ -71,15 +72,18 @@ class Log extends AdminController
$header[] = [$comment, $vo['Field']];
}
}
$month = !empty($excludeFields['month']) ? date('Ym', strtotime($excludeFields['month'])) : date('Ym');
$model = $this->model->setMonth($month)->with('admin')->where($where);
$model = $this->model->setSuffix("_$month")->with('admin')->where($where);
try {
$list = $model
->where($where)
->limit(100000)
->limit(10000)
->order('id', 'desc')
->select()
->toArray();
foreach ($list as &$vo) {
$vo['content'] = json_encode($vo['content'], JSON_UNESCAPED_UNICODE);
$vo['response'] = json_encode($vo['response'], JSON_UNESCAPED_UNICODE);
}
}catch (PDOException|DbException $exception) {
$this->error($exception->getMessage());
}
@@ -87,6 +91,45 @@ class Log extends AdminController
return Excel::exportData($list, $header, $fileName, 'xlsx');
}
#[NodeAnnotation(title: '删除指定日志', auth: true)]
public function deleteMonthLog(Request $request)
{
if (!$request->isAjax()) {
return $this->fetch();
}
if ($this->isDemo) $this->error('演示环境下不允许操作');
$monthsAgo = $request->param('month/d', 0);
if ($monthsAgo < 1) $this->error('月份错误');
$currentDate = new \DateTime();
$currentDate->modify("-$monthsAgo months");
$dbPrefix = env('DB_PREFIX');
$dbLike = "{$dbPrefix}system_log_";
$tables = Db::query("SHOW TABLES LIKE '$dbLike%'");
$threshold = date('Ym', strtotime("-$monthsAgo month"));
$tableNames = [];
try {
foreach ($tables as $table) {
$tableName = current($table);
if (!preg_match("/^$dbLike\d{6}$/", $tableName)) continue;
$datePart = substr($tableName, -6);
$issetTable = Db::query("SHOW TABLES LIKE '$tableName'");
if (!$issetTable) continue;
if ($datePart - $threshold <= 0) {
Db::execute("DROP TABLE `$tableName`");
$tableNames[] = $tableName;
}
}
}catch (PDOException) {
}
if (empty($tableNames)) $this->error('没有需要删除的表');
$this->success('操作成功 - 共删除 ' . count($tableNames) . ' 张表<br/>' . implode('<br>', $tableNames));
}
#[MiddlewareAnnotation(ignore: MiddlewareAnnotation::IGNORE_LOG)]
#[NodeAnnotation(title: '框架日志', auth: true, ignore: NodeAnnotation::IGNORE_NODE)]
public function record(): Json|string

View File

@@ -10,6 +10,9 @@ use app\admin\service\annotation\NodeAnnotation;
use app\admin\service\NodeService;
use app\Request;
use think\App;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\response\Json;
#[ControllerAnnotation(title: '系统节点管理')]
@@ -55,11 +58,11 @@ class Node extends AdminController
try {
if ($force == 1) {
$updateNodeList = $model->whereIn('node', array_column($nodeList, 'node'))->select();
$updateNodeList = $model->removeOption()->whereIn('node', array_column($nodeList, 'node'))->select();
$formatNodeList = array_format_key($nodeList, 'node');
foreach ($updateNodeList as $vo) {
isset($formatNodeList[$vo['node']])
&& $model->where('id', $vo['id'])->update(
&& $model->removeOption()->where('id', $vo['id'])->update(
[
'title' => $formatNodeList[$vo['node']]['title'],
'is_auth' => $formatNodeList[$vo['node']]['is_auth'],
@@ -67,7 +70,7 @@ class Node extends AdminController
);
}
}
$existNodeList = $model->field('node,title,type,is_auth')->select();
$existNodeList = $model->removeOption()->field('node,title,type,is_auth')->select();
foreach ($nodeList as $key => $vo) {
foreach ($existNodeList as $v) {
if ($vo['node'] == $v->node) {
@@ -76,8 +79,10 @@ class Node extends AdminController
}
}
}
$model->saveAll($nodeList);
TriggerService::updateNode();
if (!empty($nodeList)) {
$model->saveAll($nodeList);
TriggerService::updateNode();
}
}catch (\Exception $e) {
$this->error('节点更新失败');
}

0
app/admin/entity/.keep Normal file
View File

12
app/admin/entity/Test.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
namespace app\admin\entity;
use app\common\entity\BaseEntity;
/**
* ThinkORM 4.0 实体模型案例
* 可与 Model 并存 或者 单独使用
* @package app\admin\entity
*/
class Test extends BaseEntity {}

View File

@@ -35,7 +35,7 @@ class CheckAuth
!$check && $this->error('无权限访问');
// 判断是否为演示环境
if (env('EASYADMIN.IS_DEMO', false) && $request->isPost()) {
if (!in_array($currentNode, ['system.log/record', ''])) $this->error('演示环境下不允许修改');
if (!in_array($currentNode, ['system.log/record', 'mall.goods/aiOptimization'])) $this->error('演示环境下不允许修改');
}
}
return $next($request);

View File

@@ -7,6 +7,7 @@ use app\Request;
use Closure;
use ReflectionClass;
use ReflectionException;
use app\admin\service\annotation\MiddlewareAnnotation;
class CheckLogin
{
@@ -24,13 +25,25 @@ class CheckLogin
$controllerClass = 'app\\admin\\controller\\' . $controller;
$classObj = new ReflectionClass($controllerClass);
$properties = $classObj->getDefaultProperties();
$ignoreAuth = $properties['ignoreAuth'] ?? false;
$adminUserInfo = session('admin');
if (!$ignoreAuth) {
// 整个控制器是否忽略登录
$ignoreLogin = $properties['ignoreLogin'] ?? false;
$adminUserInfo = session('admin');
if (!$ignoreLogin) {
$noNeedCheck = $properties['noNeedCheck'] ?? [];
if (in_array($action, $noNeedCheck)) {
return $next($request);
}
try {
$reflectionMethod = new \ReflectionMethod($controllerClass, $action);
$attributes = $reflectionMethod->getAttributes(MiddlewareAnnotation::class);
foreach ($attributes as $attribute) {
$annotation = $attribute->newInstance();
$_ignore = (array)$annotation->ignore;
// 控制器中的某个方法忽略登录
if (in_array('LOGIN', $_ignore)) return $next($request);
}
}catch (\Throwable) {
}
if (empty($adminUserInfo)) {
return redirect(__url('login/index'));
}

View File

@@ -0,0 +1,45 @@
<?php
namespace app\admin\middleware;
use app\common\traits\JumpTrait;
use app\Request;
use Closure;
use Wolfcode\RateLimiting\Bootstrap;
class RateLimiting
{
use JumpTrait;
/**
* 启用限流器需要开启Redis
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next): mixed
{
// 是否启用限流器
if (!env('RATE_LIMITING_STATUS', false)) return $next($request);
if ($request->method() == 'GET') return $next($request);
$controller = $request->controller();
$module = app('http')->getName();
$appNamespace = config('app.app_namespace');
$controllerClass = "app\\{$module}\\controller\\{$controller}{$appNamespace}";
$controllerClass = str_replace('.', '\\', $controllerClass);
$action = $request->action();
try {
Bootstrap::init($controllerClass, $action, [
# Redis 相关配置
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => (int)env('REDIS_PORT', 6379),
'password' => env('REDIS_PASSWORD', ''),
'prefix' => env('REDIS_PREFIX', ''),
'database' => (int)env('REDIS_DATABASE', 0),
]);
}catch (\Throwable $exception) {
$this->error($exception->getMessage());
}
return $next($request);
}
}

View File

@@ -8,6 +8,11 @@ use app\common\model\TimeModel;
class MallCate extends TimeModel
{
protected $deleteTime = 'delete_time';
protected function getOptions(): array
{
return [
'deleteTime' => 'delete_time',
];
}
}

View File

@@ -8,10 +8,12 @@ use think\model\relation\HasOne;
class MallGoods extends TimeModel
{
protected $table = "";
protected $deleteTime = 'delete_time';
protected function getOptions(): array
{
return [
'deleteTime' => 'delete_time',
];
}
// * +++++++++++++++++++++++++++
// | 以下两种写法适用于 with 关联

View File

@@ -8,7 +8,12 @@ use app\common\model\TimeModel;
class SystemAdmin extends TimeModel
{
protected $deleteTime = 'delete_time';
protected function getOptions(): array
{
return [
'deleteTime' => 'delete_time',
];
}
public array $notes = [
'login_type' => [
@@ -17,12 +22,15 @@ class SystemAdmin extends TimeModel
],
];
public function getAuthList()
public function getAuthIdsAttr($value): array
{
$list = (new SystemAuth())
->where('status', 1)
->column('title', 'id');
return $list;
if (!$value) return [];
return explode(',', $value);
}
public function getAuthList(): array
{
return (new SystemAuth())->removeOption()->where('status', 1)->column('title', 'id');
}
}

View File

@@ -3,44 +3,52 @@
namespace app\admin\model;
use app\common\model\TimeModel;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
class SystemAuth extends TimeModel
{
protected $deleteTime = 'delete_time';
protected function getOptions(): array
{
return [
'deleteTime' => 'delete_time',
];
}
/**
* 根据角色ID获取授权节点
* @param $authId
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function getAuthorizeNodeListByAdminId($authId)
public function getAuthorizeNodeListByAdminId($authId): array
{
$checkNodeList = (new SystemAuthNode())
->where('auth_id', $authId)
->column('node_id');
$systemNode = new SystemNode();
$nodelList = $systemNode
$systemNode = new SystemNode();
$nodeList = $systemNode
->where('is_auth', 1)
->field('id,node,title,type,is_auth')
->select()
->toArray();
$newNodeList = [];
foreach ($nodelList as $vo) {
$newNodeList = [];
foreach ($nodeList as $vo) {
if ($vo['type'] == 1) {
$vo = array_merge($vo, ['field' => 'node', 'spread' => true]);
$vo = array_merge($vo, ['field' => 'node', 'spread' => true]);
$vo['checked'] = false;
$vo['title'] = "{$vo['title']}{$vo['node']}";
$children = [];
foreach ($nodelList as $v) {
$vo['title'] = "{$vo['title']}{$vo['node']}";
$children = [];
foreach ($nodeList as $v) {
if ($v['type'] == 2 && strpos($v['node'], $vo['node'] . '/') !== false) {
$v = array_merge($v, ['field' => 'node', 'spread' => true]);
$v = array_merge($v, ['field' => 'node', 'spread' => true]);
$v['checked'] = in_array($v['id'], $checkNodeList) ? true : false;
$v['title'] = "{$v['title']}{$v['node']}";
$children[] = $v;
$v['title'] = "{$v['title']}{$v['node']}";
$children[] = $v;
}
}
!empty($children) && $vo['children'] = $children;

View File

@@ -4,24 +4,23 @@ namespace app\admin\model;
use app\admin\service\SystemLogService;
use app\common\model\TimeModel;
use think\model\relation\BelongsTo;
class SystemLog extends TimeModel
{
public function __construct(array $data = [])
{
parent::__construct($data);
$this->name = 'system_log_' . date('Ym');
}
protected array $type = [
'content' => 'json',
'response' => 'json',
];
public function setMonth($month)
protected function init(): void
{
SystemLogService::instance()->detectTable();
$this->name = 'system_log_' . $month;
return $this;
}
public function admin()
public function admin(): BelongsTo
{
return $this->belongsTo('app\admin\model\SystemAdmin', 'admin_id', 'id');
}

View File

@@ -4,31 +4,41 @@ namespace app\admin\model;
use app\common\constants\MenuConstant;
use app\common\model\TimeModel;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
class SystemMenu extends TimeModel
{
protected $deleteTime = 'delete_time';
public function getPidMenuList()
protected function getOptions(): array
{
$list = $this->field('id,pid,title')
->where([
['pid', '<>', MenuConstant::HOME_PID],
['status', '=', 1],
])
->select()
->toArray();
return [
'deleteTime' => 'delete_time',
];
}
/**
* @throws ModelNotFoundException
* @throws DbException
* @throws DataNotFoundException
*/
public function getPidMenuList(): array
{
$list = $this->removeOption()->field('id,pid,title')->where([
['pid', '<>', MenuConstant::HOME_PID],
['status', '=', 1],
])->select()->toArray();
$pidMenuList = $this->buildPidMenu(0, $list);
$pidMenuList = array_merge([[
return array_merge([[
'id' => 0,
'pid' => 0,
'title' => '顶级菜单',
]], $pidMenuList);
return $pidMenuList;
}
protected function buildPidMenu($pid, $list, $level = 0)
protected function buildPidMenu($pid, $list, $level = 0): array
{
$newList = [];
foreach ($list as $vo) {

View File

@@ -7,22 +7,21 @@ use app\common\model\TimeModel;
class SystemNode extends TimeModel
{
public function getNodeTreeList()
public function getNodeTreeList(): array
{
$list = $this->select()->toArray();
$list = $this->buildNodeTree($list);
return $list;
$list = $this->removeOption()->select()->toArray();
return $this->buildNodeTree($list);
}
protected function buildNodeTree($list)
protected function buildNodeTree($list): array
{
$newList = [];
$newList = [];
$repeatString = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
foreach ($list as $vo) {
if ($vo['type'] == 1) {
$newList[] = $vo;
foreach ($list as $v) {
if ($v['type'] == 2 && strpos($v['node'], $vo['node'] . '/') !== false) {
if ($v['type'] == 2 && str_contains($v['node'], $vo['node'] . '/')) {
$v['node'] = "{$repeatString}{$repeatString}" . $v['node'];
$newList[] = $v;
}

View File

@@ -7,6 +7,11 @@ use app\common\model\TimeModel;
class SystemQuick extends TimeModel
{
protected $deleteTime = 'delete_time';
protected function getOptions(): array
{
return [
'deleteTime' => 'delete_time',
];
}
}

View File

@@ -10,6 +10,9 @@ final class MiddlewareAnnotation
/** 过滤日志 */
const IGNORE_LOG = 'LOG';
/** 免登录 */
const IGNORE_LOGIN = 'LOGIN';
public function __construct(public string $type = '', public string|array $ignore = '')
{
}

View File

@@ -1040,7 +1040,7 @@ class BuildCurd
$relation = CommonTool::lineToHump($key);
$relationCode = "->withJoin('{$relation}', 'LEFT')\r";
if (!empty($val['bindSelectField']) && !empty($val['primaryKey'])) {
$constructRelation = '$notes["' . lcfirst($val['modelFilename']) . ucfirst($val['bindSelectField']) . '"] = \app\admin\model\\' . $val['modelFilename'] . '::column("' . $val['bindSelectField'] . '", "' . $val['primaryKey'] . '");';
$constructRelation = '$notes["' . lcfirst($val['foreignKey']) . '"] = \app\admin\model\\' . $val['modelFilename'] . '::column("' . $val['bindSelectField'] . '", "' . $val['primaryKey'] . '");';
}
}
$controllerIndexMethod = CommonTool::replaceTemplate(
@@ -1240,7 +1240,7 @@ class BuildCurd
} elseif ($val['formType'] == 'select') {
$templateFile = "view{$this->DS}module{$this->DS}select";
if (isset($val['bindRelation'])) {
$define = $this->buildOptionView($val['bindRelation']);
$define = $this->buildOptionView($field);
} elseif (!empty($val['define'])) {
$define = $this->buildOptionView($field);
}
@@ -1314,7 +1314,7 @@ class BuildCurd
} elseif ($val['formType'] == 'select') {
$templateFile = "view{$this->DS}module{$this->DS}select";
if (isset($val['bindRelation'])) {
$define = $this->buildOptionView($val['bindRelation'], '{in name="k" value="$row.' . $field . '"}selected=""{/in}');
$define = $this->buildOptionView($field, '{in name="k" value="$row.' . $field . '"}selected=""{/in}');
} elseif (!empty($val['define'])) {
$define = $this->buildOptionView($field, '{in name="k" value="$row.' . $field . '"}selected=""{/in}');
}
@@ -1361,7 +1361,9 @@ class BuildCurd
if ($val['formType'] == 'image') {
$templateValue = "{field: '{$field}', title: '{$val['comment']}', templet: ea.table.image}";
} elseif ($val['formType'] == 'images') {
} elseif ($val['formType'] == 'datetime') {
$templateValue = "{field: '{$field}', search: 'range', title: '{$val['comment']}'}";
} elseif ($val['formType'] == 'images') {
continue;
} elseif ($val['formType'] == 'file') {
$templateValue = "{field: '{$field}', title: '{$val['comment']}', templet: ea.table.url}";
@@ -1415,10 +1417,10 @@ class BuildCurd
} elseif (in_array($field, $this->sortFields)) {
$templateValue = "{field: '{$table}.{$field}', title: '{$val['comment']}', edit: 'text'}";
} else {
$templateValue = "{field: '{$table}.{$field}', title: '{$val['comment']}'}";
$templateValue = "";
}
$indexCols .= $this->formatColsRow("{$templateValue},\r");
if ($templateValue) $indexCols .= $this->formatColsRow("{$templateValue},\r");
}
}

View File

@@ -7,11 +7,14 @@ use app\common\model\TimeModel;
class {{modelName}} extends TimeModel
{
protected $name = "{{table}}";
protected $table = "{{prefix_table}}";
protected $deleteTime = {{deleteTime}};
protected function getOptions(): array
{
return [
'name' => "{{table}}",
'table' => "{{prefix_table}}",
'deleteTime' => {{deleteTime}},
];
}
public array $notes = {{selectArrays}};

View File

@@ -100,6 +100,9 @@ class CommonTool
{
$arrayString = str_replace('array (', '[', $arrayString);
$arrayString = str_replace(')', ']', $arrayString);
$arrayString = str_replace('=>
[', '=> [', $arrayString);
return $arrayString;
}
}

View File

@@ -151,12 +151,18 @@
<tr>
<td>DEBUG模式</td>
<td>
<button type="button" class="layui-btn layui-btn-xs {:env('APP_DEBUG')?'layui-btn-warm':'layui-bg-gray'}">
<button type="button" class="layui-btn layui-btn-xs {:env('APP_DEBUG')?'layui-bg-cyan':'layui-bg-gray'}">
{:env('APP_DEBUG')?'开启中':'已关闭'}
</button>
<span class="layui-badge layui-bg-gray">建议线上环境关闭 APP_DEBUG</span>
</td>
</tr>
<tr>
<td>composer信息</td>
<td>
<button type="button" class="layui-btn layui-btn-xs layui-bg-cyan" lay-on="showComposerInfo">点击查看</button>
</td>
</tr>
<tr>
<td>主要特色</td>
<td>

View File

@@ -26,9 +26,18 @@
<!-- </div>-->
<div class="layui-form-item">
<label class="layui-form-label">商品标题</label>
<div class="layui-input-block">
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入商品标题" value="">
<div class="layui-row">
<label class="layui-form-label required">商品标题</label>
<div class="layui-input-block layui-col-space5">
<div class="layui-col-xs10">
<div class="layui-input-wrap">
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入商品标题" value="">
</div>
</div>
<div class="layui-col-xs2">
<button class="layui-btn layui-bg-purple layui-btn-fluid" type="button" lay-on="AiOptimization">AI优化</button>
</div>
</div>
</div>
</div>

View File

@@ -26,12 +26,22 @@
<!-- </div>-->
<div class="layui-form-item">
<label class="layui-form-label">商品标题</label>
<div class="layui-input-block">
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入商品标题" value="{$row.title|default=''}">
<div class="layui-row">
<label class="layui-form-label required">商品标题</label>
<div class="layui-input-block layui-col-space5">
<div class="layui-col-xs10">
<div class="layui-input-wrap">
<input type="text" name="title" class="layui-input" lay-verify="required" placeholder="请输入商品标题" value="{$row.title|default=''}">
</div>
</div>
<div class="layui-col-xs2">
<button class="layui-btn layui-bg-purple layui-btn-fluid" type="button" lay-on="AiOptimization">AI优化</button>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">商品LOGO</label>
<div class="layui-input-block layuimini-upload">

View File

@@ -1,22 +1,16 @@
<div class="layuimini-container">
<div class="layuimini-main" id="app">
<div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief">
<ul class="layui-tab-title">
<div class="layui-tabs layui-tabs-card layui-panel " id="docDemoTabBrief">
<ul class="layui-tabs-header layui-bg-tint">
<li class="layui-this" data-group="site">网站设置</li>
<li data-group="logo">LOGO配置</li>
<li data-group="upload">上传配置</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
{include file="system/config/site" /}
</div>
<div class="layui-tab-item">
{include file="system/config/logo" /}
</div>
<div class="layui-tab-item">
{include file="system/config/upload" /}
</div>
<div class="layui-tabs-body">
<div class="layui-tabs-item layui-show"> {include file="system/config/site" /}</div>
<div class="layui-tabs-item"> {include file="system/config/logo" /}</div>
<div class="layui-tabs-item">{include file="system/config/upload" /}</div>
</div>
</div>

View File

@@ -0,0 +1,23 @@
<div class="layuimini-container">
<form id="app-form" class="layui-form layuimini-form">
<div class="layui-form-item">
<div class="layui-input-group">
<div class="layui-input-prefix layui-input-split">删除</div>
<label>
<input type="number" name="month" lay-affix="number" placeholder="" min="1" class="layui-input" value="3">
</label>
<div class="layui-input-suffix layui-input-split">个月前的日志</div>
</div>
</div>
<div class="hr-line"></div>
<div class="layui-form-item text-center">
<button type="button" class="layui-btn" lay-submit lay-filter="submit">提交</button>
</div>
</form>
</div>

View File

@@ -2,6 +2,7 @@
<div class="layuimini-main">
<table id="currentTable" class="layui-table layui-hide"
data-auth-record="{:auth('system.log/record')}"
data-auth-deleteMonthLog="{:auth('system.log/deleteMonthLog')}"
lay-filter="currentTable">
</table>
</div>

View File

@@ -100,21 +100,21 @@ if (!function_exists('auth')) {
$authService = new AuthService(session('admin.id'));
return $authService->checkNode($node);
}
}
/**
* @param string|null $detail
* @param string $name
* @param string $placeholder
* @return string
*/
function editor_textarea(?string $detail, string $name = 'desc', string $placeholder = '请输入'): string
{
$editor_type = sysConfig('site', 'editor_type');
return match ($editor_type) {
'ckeditor' => "<textarea name='{$name}' rows='20' class='layui-textarea editor' placeholder='{$placeholder}'>{$detail}</textarea>",
'ueditor' => "<script type='text/plain' id='{$name}' name='{$name}' class='editor' data-content='{$detail}'></script>",
'EasyMDE' => "<textarea id='{$name}' class='editor' name='{$name}'>{$detail}</textarea>",
default => "<div class='wangEditor_div'><textarea name='{$name}' rows='20' class='layui-textarea editor layui-hide'>{$detail}</textarea><div id='editor_toolbar_{$name}'></div><div id='editor_{$name}' style='height: 300px'></div></div>",
};
}
}
/**
* @param string|null $detail
* @param string $name
* @param string $placeholder
* @return string
*/
function editor_textarea(?string $detail, string $name = 'desc', string $placeholder = '请输入'): string
{
$editor_type = sysConfig('site', 'editor_type');
return match ($editor_type) {
'ckeditor' => "<textarea name='{$name}' rows='20' class='layui-textarea editor' placeholder='{$placeholder}'>{$detail}</textarea>",
'ueditor' => "<script type='text/plain' id='{$name}' name='{$name}' class='editor' data-content='{$detail}'></script>",
'EasyMDE' => "<textarea id='{$name}' class='editor' name='{$name}'>{$detail}</textarea>",
default => "<div class='wangEditor_div'><textarea name='{$name}' rows='20' class='layui-textarea editor layui-hide'>{$detail}</textarea><div id='editor_toolbar_{$name}'></div><div id='editor_{$name}' style='height: 300px'></div></div>",
};
}

View File

@@ -133,9 +133,6 @@ class AdminController extends BaseController
{
if ($layout) View::instance()->engine()->layout('/layout/default');
View::assign($vars);
if (empty($template)) {
$template = strtolower(str_replace('.', '/', $this->request->pathinfo()));
}
return View::fetch($template);
}

View File

@@ -0,0 +1,22 @@
<?php
namespace app\common\entity;
use think\Entity;
use think\model\type\DateTime;
class BaseEntity extends Entity
{
protected function getOptions(): array
{
return [
'type' => [
'create_time' => DateTime::class,
'update_time' => DateTime::class,
'delete_time' => DateTime::class,
],
];
}
}

View File

@@ -13,28 +13,20 @@ use think\model\concern\SoftDelete;
class TimeModel extends Model
{
/**
* 自动时间戳类型
* @var string
*/
protected $autoWriteTimestamp = true;
/**
* 添加时间
* @var string
*/
protected $createTime = 'create_time';
/**
* 更新时间
* @var string
*/
protected $updateTime = 'update_time';
/**
* 软删除
*/
use SoftDelete;
protected $deleteTime = false;
protected function getOptions(): array
{
return [
'autoWriteTimestamp' => true,
'createTime' => 'create_time',
'updateTime' => 'update_time',
'deleteTime' => false,
];
}
}

View File

@@ -2,6 +2,7 @@
namespace app\common\service;
use app\admin\service\annotation\NodeAnnotation;
use app\common\constants\AdminConstant;
use think\facade\Db;
@@ -52,7 +53,7 @@ class AuthService
/***
* 构造方法
* AuthService constructor.
* @param null $adminId
* @param null $adminId
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
@@ -68,7 +69,7 @@ class AuthService
/**
* 检测检测权限
* @param null $node
* @param null $node
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
@@ -87,7 +88,7 @@ class AuthService
// 判断是否需要获取当前节点
if (empty($node)) {
$node = $this->getCurrentNode();
} else {
}else {
$node = $this->parseNodeStr($node);
}
// 判断是否加入节点控制,优先获取缓存信息
@@ -106,9 +107,30 @@ class AuthService
if (in_array($node, $this->adminNode)) {
return true;
}
if ($this->checkNodeAnnotationAttrAuth($node)) return true;
return false;
}
protected function checkNodeAnnotationAttrAuth(string $node): bool
{
$bool = false;
$controller = request()->controller();
try {
$controllerExplode = explode('.', $controller);
[$_name, $_controller] = $controllerExplode;
$nodeExplode = explode('/', $node);
$action = end($nodeExplode);
$reflectionClass = new \ReflectionClass("app\admin\controller\\{$_name}\\{$_controller}");
$attributes = $reflectionClass->getMethod($action)->getAttributes(NodeAnnotation::class);
foreach ($attributes as $attribute) {
$annotation = $attribute->newInstance();
$bool = $annotation->auth === false;
}
}catch (\Throwable) {
}
return $bool;
}
/**
* 获取当前节点
* @return string
@@ -130,25 +152,25 @@ class AuthService
{
$nodeList = [];
$adminInfo = Db::name($this->config['system_admin'])
->where([
'id' => $this->adminId,
'status' => 1,
])->find();
->where([
'id' => $this->adminId,
'status' => 1,
])->find();
if (!empty($adminInfo) && !empty($adminInfo['auth_ids'])) {
$buildAuthSql = Db::name($this->config['system_auth'])
->distinct(true)
->whereIn('id', $adminInfo['auth_ids'])
->field('id')
->buildSql(true);
->distinct(true)
->whereIn('id', $adminInfo['auth_ids'])
->field('id')
->buildSql(true);
$buildAuthNodeSql = Db::name($this->config['system_auth_node'])
->distinct(true)
->where("auth_id IN {$buildAuthSql}")
->field('node_id')
->buildSql(true);
->distinct(true)
->where("auth_id IN {$buildAuthSql}")
->field('node_id')
->buildSql(true);
$nodeList = Db::name($this->config['system_node'])
->distinct(true)
->where("id IN {$buildAuthNodeSql}")
->column('node');
->distinct(true)
->where("id IN {$buildAuthNodeSql}")
->column('node');
}
return $nodeList;
}
@@ -162,7 +184,7 @@ class AuthService
public function getNodeList()
{
return Db::name($this->config['system_node'])
->column('id,node,title,type,is_auth', 'node');
->column('id,node,title,type,is_auth', 'node');
}
/**
@@ -177,13 +199,13 @@ class AuthService
public function getAdminInfo()
{
return Db::name($this->config['system_admin'])
->where('id', $this->adminId)
->find();
->where('id', $this->adminId)
->find();
}
/**
* 驼峰转下划线规则
* @param string $node
* @param string $node
* @return string
*/
public function parseNodeStr($node)

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types = 1);
namespace app\common\utils;
class Helper
{
/**
* 获取当前IP地址
* @return string
*/
public static function getIp(): string
{
return request()->ip();
}
/**
* 获取当前登录用户ID
* @return int|string
*/
public static function getAdminUid(): int|string
{
return session('admin.id') ?: 0;
}
}

View File

@@ -41,7 +41,7 @@ class Install extends BaseController
];
$currentHost = '://';
$result = compact('errorInfo', 'currentHost', 'isInstall', 'envInfo');
return view('index/install/index', $result);
return view('index@install/index', $result);
}
if ($errorInfo) $this->error($errorInfo);
$charset = 'utf8mb4';

View File

@@ -22,12 +22,12 @@
"require": {
"php": ">=8.1.0",
"topthink/framework": "^8.0",
"topthink/think-orm": "^3.0",
"topthink/think-orm": "4.0.3",
"topthink/think-multi-app": "^1.1.0",
"topthink/think-view": "2.0.0",
"topthink/think-view": "^2.0",
"topthink/think-captcha": "^3.0",
"topthink/think-filesystem": "^2.0",
"aliyuncs/oss-sdk-php": "^2.6",
"aliyuncs/oss-sdk-php": "^2.7.2",
"qcloud/cos-sdk-v5": "^2.6",
"jianyan74/php-excel": "^1.0.2",
"doctrine/annotations": "^2.0.0",
@@ -36,6 +36,8 @@
"qiniu/php-sdk": "^7.11.0",
"wolf-leo/phplogviewer": "^0.11.3",
"wolfcode/authenticator": "^0.0.6",
"wolfcode/rate-limiting": "^0.1.0",
"wolfcode/php-ai": "^0.1.2",
"ext-json": "*",
"ext-mysqli": "*",
"ext-pdo": "*"

View File

@@ -571,3 +571,22 @@ INSERT INTO `ea_system_uploadfile`
VALUES ('290', 'oss', 'image/jpeg', 'https://lxn-99php.oss-cn-shenzhen.aliyuncs.com/upload/20191111/2c412adf1b30c8be3a913e603c7b6e4a.jpg', '', '', '', '0', 'image/jpeg', '0', 'jpg', '', 1573612437, null, null);
INSERT INTO `ea_system_uploadfile`
VALUES ('296', 'cos', 'image/jpeg', 'https://easyadmin-1251997243.cos.ap-guangzhou.myqcloud.com/upload/20191114/2381eaf81208ac188fa994b6f2579953.jpg', '', '', '', '0', 'image/jpeg', '0', 'jpg', '', 1573612437, null, null);
-- ----------------------------
-- Table structure for ea_system_log
-- ----------------------------
DROP TABLE IF EXISTS `ea_system_log`;
CREATE TABLE `ea_system_log`
(
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`admin_id` int unsigned DEFAULT '0' COMMENT '管理员ID',
`url` varchar(1500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '操作页面',
`method` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求方法',
`title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '日志标题',
`content` json NOT NULL COMMENT '请求数据',
`response` json DEFAULT NULL COMMENT '回调数据',
`ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'IP',
`useragent` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'User-Agent',
`create_time` int DEFAULT NULL COMMENT '操作时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='后台操作日志表 - 202412';

View File

@@ -140,6 +140,38 @@ define(["jquery", "easy-admin", "echarts", "echarts-theme", "miniAdmin", "miniTh
echartsRecords.resize();
});
})
let util = layui.util;
util.on({
showComposerInfo: function () {
// <div style="padding: 25px;">12313</div>
let html = ``
ea.request.post({
url: ea.url('ajax/composerInfo'),
}, function (success) {
let data = success.data
data.forEach(function (item) {
html += `${item.name} ${item.version}\r\n`
})
html = `<pre class="layui-code code-demo">${html}</pre>`
layer.open({
type: 1,
title: 'composer 信息',
area: ['50%', '90%'],
shade: 0.8,
shadeClose: true,
content: html,
success: function () {
layui.code({elem: '.code-demo', theme: 'dark', lang: 'php'});
}
})
}, function (error) {
console.error(error)
return false;
})
}
})
},
editAdmin: function () {
let form = layui.form

View File

@@ -40,7 +40,7 @@ define(["jquery", "easy-admin"], function ($, ea) {
})
}, function (res) {
let data = res.data
if (data.is_ga_code) {
if (data?.is_ga_code || false) {
let elem = $('#gaCode')
elem.removeClass('layui-hide');
elem.find('input').focus()

View File

@@ -85,13 +85,55 @@ define(["jquery", "easy-admin"], function ($, ea) {
ea.listen();
},
add: function () {
layui.util.on({
AiOptimization: function (data) {
let layOn = $(data).attr('lay-on')
$(data).attr('lay-on', layOn + 'Loading')
aiOptimization(data)
},
})
ea.listen();
},
edit: function () {
layui.util.on({
AiOptimization: function (data) {
let layOn = $(data).attr('lay-on')
$(data).attr('lay-on', layOn + 'Loading')
aiOptimization(data)
},
})
ea.listen();
},
stock: function () {
ea.listen();
},
};
function aiOptimization(data) {
let layOn = $(data).attr('lay-on')
let title = $('input[name="title"]').val()
// 告诉AI 你需要做什么
let message = `优化这个标题 ${title}`
if ($.trim(title) === '') {
ea.msg.error('标题不能为空', function () {
$(data).attr('lay-on', layOn.split('Loading')[0])
})
return false
}
let url = ea.url('mall.goods/aiOptimization')
ea.request.post({url: url, data: {message: message}}, function (res) {
let content = res.data?.choices[0]?.message?.content
// stream 为true 时AI 内容会逐字输出
let stream = true
ea.ai.chat(content, {stream: stream}, function () {
$(data).attr('lay-on', layOn.split('Loading')[0])
})
}, function (error) {
ea.msg.error(error.msg, function () {
$(data).attr('lay-on', layOn.split('Loading')[0])
})
})
}
});

View File

@@ -1,15 +1,15 @@
define(["jquery", "easy-admin", "vue"], function ($, ea, Vue) {
define(["jquery", "easy-admin"], function ($, ea) {
var form = layui.form;
return {
index: function () {
var _group = 'site'
var element = layui.element;
element.on('tab(docDemoTabBrief)', function (data) {
let tabs = layui.tabs
var TABS_ID = 'docDemoTabBrief';
tabs.on(`afterChange(${TABS_ID})`, function (data) {
_group = $(this).data('group')
});
})
let _upload_type = upload_type || 'local'
$('.upload_type').addClass('layui-hide')
$('.' + _upload_type).removeClass('layui-hide')
@@ -20,12 +20,15 @@ define(["jquery", "easy-admin", "vue"], function ($, ea, Vue) {
$('.' + _upload_type).removeClass('layui-hide')
});
form.on("submit", function (data) {
data.field['group'] = _group
});
ea.listen();
ea.listen('', function (res) {
ea.msg.success(res.msg);
}, function (err) {
ea.msg.error(err.msg);
});
}
};
});

View File

@@ -6,6 +6,7 @@ define(["jquery", "easy-admin"], function ($, ea) {
table_render_id: 'currentTableRenderId',
index_url: 'system.log/index',
export_url: 'system.log/export',
deleteMonthLog_url: 'system.log/deleteMonthLog',
};
return {
@@ -23,8 +24,15 @@ define(["jquery", "easy-admin"], function ($, ea) {
class: 'layui-btn layui-btn-sm',
icon: 'fa fa-book',
extend: 'data-width="95%" data-height="95%"'
},
]
}, {
text: '删除部分日志',
url: 'system.log/deleteMonthLog',
method: 'open',
auth: 'deleteMonthLog',
class: 'layui-btn layui-btn-sm layui-btn-danger',
icon: 'fa fa-remove',
extend: 'data-width="35%" data-height="42%"'
},]
],
cols: [[
{field: 'id', width: 80, title: 'ID', search: false},
@@ -43,7 +51,7 @@ define(["jquery", "easy-admin"], function ($, ea) {
field: 'content', minWidth: 200, title: '请求数据', align: "left", templet: function (res) {
let html = '<div class="layui-colla-item">' +
'<div class="layui-colla-title">点击预览</div>' +
'<div class="layui-colla-content">' + prettyFormat(res.content) + '</div>' +
'<div class="layui-colla-content">' + prettyFormat(JSON.stringify(res.content)) + '</div>' +
'</div>'
return '<div class="layui-collapse" lay-accordion>' + html + '</div>'
}
@@ -52,7 +60,7 @@ define(["jquery", "easy-admin"], function ($, ea) {
field: 'response', minWidth: 200, title: '回调数据', align: "left", templet: function (res) {
let html = '<div class="layui-colla-item">' +
'<div class="layui-colla-title">点击预览</div>' +
'<div class="layui-colla-content">' + prettyFormat(res.response) + '</div>' +
'<div class="layui-colla-content">' + prettyFormat(JSON.stringify(res.response)) + '</div>' +
'</div>'
return '<div class="layui-collapse" lay-accordion>' + html + '</div>'
}
@@ -65,5 +73,20 @@ define(["jquery", "easy-admin"], function ($, ea) {
});
ea.listen();
},
deleteMonthLog: function () {
layui.form.on('submit(submit)', function (data) {
let field = data.field
let options = {
url: ea.url(init.deleteMonthLog_url),
data: field,
}
ea.msg.confirm('确认执行该操作?重要数据请先做好相关备份!', function () {
ea.request.post(options, function (rs) {
let msg = rs.msg || '未知~'
layer.msg(msg.replace(/\n/g, '<br>'), {shade: 0.3, shadeClose: true, time: 2000})
})
})
})
}
};
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -5,11 +5,9 @@ require.config({
baseUrl: BASE_URL,
paths: {
"jquery": ["plugs/jquery-3.4.1/jquery-3.4.1.min"],
"jquery-particleground": ["plugs/jq-module/jquery.particleground.min"],
"echarts": ["plugs/echarts/echarts.min"],
"echarts-theme": ["plugs/echarts/echarts-theme"],
"easy-admin": ["plugs/easy-admin/easy-admin"],
"layuiall": ["plugs/layui-v2.x/layui.all"],
"layui": ["plugs/layui-v2.x/layui"],
"miniAdmin": ["plugs/lay-module/layuimini/miniAdmin"],
"miniMenu": ["plugs/lay-module/layuimini/miniMenu"],
@@ -24,6 +22,7 @@ require.config({
"vue": ["plugs/vue-2.6.10/vue.min"],
"swiper": ["plugs/swiper/swiper-bundle.min"],
"colorMode": ["plugs/colorMode/colorMode"],
"lazyload": ["plugs/lazyload/lazyload.min"],
}
});

View File

@@ -1,4 +1,4 @@
define(["jquery", "tableSelect", "miniTheme", "xmSelect"], function ($, tableSelect, miniTheme, xmSelect) {
define(["jquery", "tableSelect", "miniTheme", "xmSelect", "lazyload"], function ($, tableSelect, miniTheme, xmSelect, lazyload) {
//切换日夜模式
window.onInitElemStyle = function () {
@@ -748,8 +748,11 @@ define(["jquery", "tableSelect", "miniTheme", "xmSelect"], function ($, tableSel
var values = value.split(option.imageSplit),
valuesHtml = [];
values.forEach((value, index) => {
valuesHtml.push('<img style="max-width: ' + option.imageWidth + 'px; max-height: ' + option.imageHeight + 'px;" src="' + value + '" data-image="' + title + '">');
valuesHtml.push('<img style="max-width: ' + option.imageWidth + 'px; max-height: ' + option.imageHeight + 'px;" class="lazyload" src="/static/common/images/loading.gif" data-src="' + value + '" data-image="' + title + '">');
});
$(function () {
$("img.lazyload").lazyload({threshold: 1});
})
return valuesHtml.join(option.imageJoin);
}
},
@@ -1020,8 +1023,9 @@ define(["jquery", "tableSelect", "miniTheme", "xmSelect"], function ($, tableSel
maxmin: true,
anim: 0,
moveOut: true,
move: false,
shade: 0.3,
shadeClose: shadeClose,
scrollbar: false,
before: function () {
},
success: function (layero, index) {
@@ -1593,7 +1597,7 @@ define(["jquery", "tableSelect", "miniTheme", "xmSelect"], function ($, tableSel
cols: [[
{type: selectCheck},
{field: 'id', title: 'ID'},
{field: 'url', minWidth: 80, search: false, title: '图片信息', imageHeight: 40, align: "center", templet: admin.table.image},
{field: 'url', minWidth: 80, search: false, title: '图片信息', imageHeight: 30, align: "center", templet: admin.table.image},
{field: 'original_name', width: 150, title: '文件原名', align: "center"},
{field: 'mime_type', width: 120, title: 'mime类型', align: "center"},
{field: 'create_time', width: 200, title: '创建时间', align: "center", search: 'range'},
@@ -1761,6 +1765,61 @@ define(["jquery", "tableSelect", "miniTheme", "xmSelect"], function ($, tableSel
}
},
},
ai: {
chat: function (content, options, cancel) {
let id = 'chat_' + (new Date()).getTime()
layer.open({
'title': options?.title || 'AI建议',
type: 1,
area: options?.area || ['42%', '60%'],
shade: options?.shade || 0,
shadeClose: options?.shadeClose || false,
scrollbar: options?.scrollbar || false,
maxmin: options?.maxmin || true,
anim: options?.anim || 0,
content: `<div style="padding: 20px;white-space: pre-wrap;" id="${id}"></div>`,
success: function (layero, index) {
let elem = document.getElementById(id)
if (options?.stream) {
clearTimeout(aiStreamTimeout)
aiStreamCurrentIndex = 0
setTimeout(() => {
admin.ai.streamOutput(elem, content)
}, 300)
} else {
content = content.replace(/\r\n/g, '<br>').replace(/\n/g, '<br>')
setTimeout(() => {
elem.innerHTML = content
}, 100)
}
},
cancel: function (index, layero) {
cancel()
}
})
},
streamOutput: function (dom, htmlContent) {
const chunkSize = 1;
let length = htmlContent.length;
if (aiStreamCurrentIndex < length) {
const endIndex = Math.min(aiStreamCurrentIndex + chunkSize, length);
const chunk = htmlContent.slice(aiStreamCurrentIndex, endIndex);
const tempDiv = document.createElement('div');
tempDiv.innerHTML = chunk;
while (tempDiv.firstChild) {
dom.appendChild(tempDiv.firstChild);
}
aiStreamCurrentIndex = endIndex;
aiStreamTimeout = setTimeout(() => {
admin.ai.streamOutput(dom, htmlContent);
dom.scrollIntoView({behavior: "smooth", block: "end"});
}, 60);
}
}
},
};
var aiStreamCurrentIndex = 0;
var aiStreamTimeout = null;
return admin;
});

File diff suppressed because one or more lines are too long

View File

@@ -14,15 +14,19 @@
/>
<missing-glyph />
<glyph glyph-name="edge" unicode="&#59019;" d="M240.185509 821.062741C322.180562 871.479699 415.37494 897.48813 509.969233 895.934224 845.948962 895.934224 1023.938224 648.353161 1023.938224 456.964708c-0.199988-65.396055-25.998431-127.79229-71.795669-174.389479-45.797237-46.397201-107.993485-72.995596-173.389539-73.995536-150.390927 0-182.98896 46.197213-182.98896 63.996139 0 7.599542 2.399855 12.399252 9.599421 18.798866l1.99988 2.399855 0.799951 3.199807c20.998733 22.998612 31.798082 52.396839 31.798082 83.194981 0 157.390504-164.390082 285.382782-367.977799 285.382782-75.075471 0.599964-149.071006-17.798926-215.027027-53.796754 53.996742 115.03306 165.430019 195.188224 182.628981 207.627473 1.599903 1.099934 0.599964 1.679899 0.599964 1.679899z m31.198118-636.081624c-2.799831-59.99638 9.199445-119.992761 32.798021-174.389479 27.198359-52.796815 65.396055-101.993847 112.993183-138.591638-118.992821 22.998612-222.966548 87.794703-298.781974 178.589225C42.237452 143.383627 0 259.176641 0 380.169341c0 102.393822 124.792471 188.78861 271.983591 188.78861 73.195584 1.199928 144.791264-21.798685 203.587717-65.396054l-7.199566-2.399856c-102.993786-35.197876-196.988115-181.389056-196.988115-316.180924zM939.543315 95.986486l-1.399915-0.199987c-23.598576-37.597732-51.796875-70.195765-84.394908-98.994028-61.596284-55.996622-136.191783-90.99451-217.586873-99.793979-37.197756-0.599964-73.59556 6.399614-107.593509 22.798624-51.196911 20.598757-94.194317 59.99638-123.192567 105.993605-28.798263 47.797116-42.197454 103.393762-37.997708 159.190396-1.199928 40.197575 10.799348 80.595138 29.99819 116.392978 27.798323-66.196006 74.995475-122.592604 135.191844-161.590251 60.196368-38.997647 130.992097-58.996441 202.787766-57.196549 61.99626-0.599964 124.192507 13.399192 180.389116 40.997526l3.799771 1.799892c7.799529 4.599722 15.399071 7.799529 23.1986 0 8.999457-9.799409 3.599783-18.39889-2.399855-27.998311-0.399976-0.399976-0.599964-0.99994-0.799952-1.399916z" horiz-adv-x="1024" />
<glyph glyph-name="sound" unicode="&#59037;" d="M631.4-29.7c-5.1 0-10.1 1.3-14.5 3.7L277.1 162.2H116.8c-16 0-28.9 13-28.9 28.9v384c0 16 13 28.9 28.9 28.9h160.3l339.7 189.9c4.5 2.5 9.5 3.8 14.6 3.8 5.3 0 10.6-1.4 15.1-4.1 9.1-5.3 14.8-15.2 14.8-25.8v-767.6c0-10.5-5.6-20.4-14.7-25.8-4.6-2.7-9.9-4.1-15.2-4.1zM305.8 551.5v-336.7L601.4 51V716.8L305.8 551.5zM145.7 220.1h102.2v326H145.7v-326zM829.4 93c-7.1 0-13.9 2.2-19.8 6.2-7.5 5.3-12.6 13.2-14.2 22.3-1.6 9.1 0.4 18.2 5.7 25.8 48.7 69.5 74.5 151.3 74.5 236.6 0 84.1-25.2 165.1-72.7 234.1-5.2 7.6-7.2 16.7-5.5 25.8 1.7 9.1 6.8 16.9 14.3 22.2 5.8 4 12.5 6.1 19.5 6.1 11.4 0 22-5.6 28.4-14.9 55.6-80.5 84.9-175 84.9-273.2 0-99.5-30.1-195-87-276.2-6.3-9.3-16.8-14.8-28.1-14.8zM755 221.4c-4.8 0-9.5 1-13.9 2.9-8.4 3.7-14.9 10.5-18.2 19.1-3.3 8.6-3.1 18 0.6 26.4 16 36.3 24.1 75 24.1 115 0 38.6-7.5 75.9-22.4 111.1-3.6 8.5-3.7 17.9-0.2 26.4s10 15.2 18.5 18.8c4.3 1.8 8.8 2.7 13.4 2.7 13.9 0 26.4-8.3 31.8-21.1 18.5-43.7 27.9-90.2 27.9-138 0-49.6-10.1-97.7-29.9-142.8-5.6-12.4-18-20.5-31.7-20.5z" horiz-adv-x="1024" />
<glyph glyph-name="leaf" unicode="&#59137;" d="M1017.948269 886.876437c-4.863707 5.785251-12.031275 9.113051-19.557222 9.113051l-26.110427 0c-258.032454 0.102394-461.847374 0.153591-611.905533-35.735447-80.635142-19.301237-142.992985-48.432282-190.606116-89.031436-51.401703-43.82456-86.420393-101.216302-107.155144-175.554223-13.77197-49.353826-20.222782-138.487656 6.96278-227.160714 10.034595-32.766026 25.700852-63.688963 46.589193-92.103251-62.255449-97.530124-116.063407-225.983185-116.063407-378.805977 0-14.130349 11.468109-25.598458 25.598458-25.598458s25.598458 11.468109 25.598458 25.598458c0 235.761795 139.665185 410.650458 222.91137 493.845446 59.7468 59.7468 127.275532 110.175762 195.367429 145.808815 63.381781 33.175601 123.947732 51.4529 170.536925 51.4529 14.130349 0 25.598458 11.468109 25.598458 25.598458s-11.468109 25.598458-25.598458 25.598458c-55.497456 0-122.667809-19.813206-194.241097-57.340545-72.597226-38.039308-144.477695-91.591282-207.80828-154.973063-26.72479-26.72479-58.876453-62.357843-90.823328-105.977615-12.389654 19.506025-22.014674 40.189579-28.619076 61.794677-25.598458 83.553366-16.178225 164.034917-6.604402 198.388047 73.211589 262.384191 351.313233 263.049751 855.858835 262.896161-60.156376-321.926204-172.328817-530.29765-333.599101-619.533873-149.597387-82.785412-297.966048-37.629733-354.845821-14.335136-11.980078 4.914904-24.06255 10.95614-35.786644 17.91892-12.133669 7.218765-27.851122 3.225406-35.069887-8.908263s-3.225406-27.851122 8.908263-35.069887c13.925561-8.2939 28.260697-15.461468 42.595834-21.349114 31.844481-13.004017 83.143791-29.694211 146.679163-35.172281 14.027955-1.228726 27.902319-1.791892 41.674289-1.791892 75.208269 0 145.860012 18.072511 210.675307 53.910352 82.375837 45.565255 153.641943 119.749585 211.904033 220.351524 68.296685 118.00889 119.698388 274.51786 152.720399 465.175173 1.279923 7.423553-0.767954 15.051893-5.631661 20.837145z" horiz-adv-x="1025" />
<glyph glyph-name="bot" unicode="&#59350;" d="M511.453867 861.866667c35.498667 0 63.8976-28.398933 63.8976-63.8976 0-22.9376-12.014933-42.5984-30.037334-54.0672V657.066667h306.926934c18.568533 0 33.860267-15.291733 33.860266-33.860267v-613.853867c0-18.568533-15.291733-33.860267-33.860266-33.860266H170.666667c-18.568533 0-33.860267 15.291733-33.860267 33.860266V623.2064c0 18.568533 15.291733 33.860267 33.860267 33.860267h306.926933V743.901867c-17.476267 10.922667-29.4912 30.583467-30.037333 52.974933v1.092267c0 35.498667 28.398933 63.8976 63.8976 63.8976zM809.642667 580.608H213.265067v-528.657067h596.923733l-0.546133 528.657067z m-170.3936-367.547733c4.9152 0 8.738133-3.822933 8.738133-8.738134v-51.336533c0-4.9152-3.822933-8.738133-8.738133-8.738133H383.658667c-4.9152 0-8.738133 3.822933-8.738134 8.738133v51.336533c0 4.9152 3.822933 8.738133 8.738134 8.738134h255.5904zM93.661867 504.149333c4.9152 0 8.738133-3.822933 8.738133-8.738133v-323.857067c0-4.9152-3.822933-8.738133-8.738133-8.738133H42.325333c-4.9152 0-8.738133 3.822933-8.738133 8.738133V495.4112c0 4.9152 3.822933 8.738133 8.738133 8.738133h51.336534z m886.920533 0c4.9152 0 8.738133-3.822933 8.738133-8.738133v-323.857067c0-4.9152-3.822933-8.738133-8.738133-8.738133h-51.336533c-4.9152 0-8.738133 3.822933-8.738134 8.738133V495.4112c0 4.9152 3.822933 8.738133 8.738134 8.738133h51.336533zM332.322133 435.882667c32.768 0 59.528533-26.760533 59.528534-59.528534s-26.760533-59.528533-59.528534-59.528533-59.528533 26.760533-59.528533 59.528533 26.760533 59.528533 59.528533 59.528534z m358.263467 0c32.768 0 59.528533-26.760533 59.528533-59.528534s-26.760533-59.528533-59.528533-59.528533-59.528533 26.760533-59.528533 59.528533 26.2144 59.528533 59.528533 59.528534z" horiz-adv-x="1024" />
<glyph glyph-name="folder" unicode="&#60094;" d="M970.666667 682.666667H542.173333L429.793333 795.046667A52.986667 52.986667 0 0 1 392.08 810.666667H96a53.393333 53.393333 0 0 1-53.333333-53.333334v-704a53.393333 53.393333 0 0 1 53.333333-53.333333h874.666667a53.393333 53.393333 0 0 1 53.333333 53.333333V629.333333a53.393333 53.393333 0 0 1-53.333333 53.333334zM96 768h296.08a10.573333 10.573333 0 0 0 7.54-3.126667L481.826667 682.666667H96a53.546667 53.546667 0 0 1-10.666667-1.073334V757.333333a10.666667 10.666667 0 0 0 10.666667 10.666667z m885.333333-714.666667a10.666667 10.666667 0 0 0-10.666666-10.666666H96a10.666667 10.666667 0 0 0-10.666667 10.666666V629.333333a10.666667 10.666667 0 0 0 10.666667 10.666667h874.666667a10.666667 10.666667 0 0 0 10.666666-10.666667z" horiz-adv-x="1024" />
<glyph glyph-name="edge" unicode="&#59019;" d="M257.173915 793.74632C334.044277 841.012218 421.414006 865.395122 510.096156 863.938335 825.077152 863.938335 991.942085 631.831088 991.942085 452.404414c-0.187489-61.308802-24.373529-119.805272-67.30844-163.490137-42.93491-43.497376-101.243892-68.433371-162.552692-69.370815-140.991494 0-171.55215 43.309887-171.55215 59.996381 0 7.124571 2.249864 11.624299 8.999457 17.623936l1.874887 2.249864 0.749954 2.99982c19.686312 21.561199 29.810702 49.122037 29.810702 77.995294 0 147.553597-154.115702 267.546358-344.979186 267.546358-70.383254 0.562466-139.754068-16.686493-201.587838-50.434457 50.621946 107.843494 155.090643 182.98896 171.214669 194.650756 1.499909 1.031188 0.562466 1.574905 0.562467 1.574906z m29.248235-596.326523c-2.624842-56.246606 8.62448-112.493213 30.748145-163.490136 25.498462-49.497014 61.308802-95.619232 105.931109-129.929661-111.55577 21.561199-209.031139 82.307534-280.108101 167.427398C71.597611 158.42215 32 266.978101 32 380.408757c0 95.994208 116.992942 176.989322 254.984617 176.989322 68.62086 1.124932 135.74181-20.436267 190.863484-61.308801l-6.749593-2.249865c-96.556674-32.998009-184.676358-170.05224-184.676358-296.419616zM912.821858 113.987331l-1.312421-0.187488c-22.123665-35.247874-48.55957-65.80853-79.120226-92.806901-57.746516-52.496833-127.679797-85.307353-203.987693-93.556856-34.872896-0.562466-68.995837 5.999638-100.868915 21.37371-47.997104 19.311335-88.307172 56.246606-115.493031 99.369005-26.998372 44.809796-39.560113 96.931652-35.622852 149.240996-1.124932 37.685227 10.124389 75.557942 28.123303 109.118417 26.060928-62.058756 70.308258-114.930566 126.742354-151.49086 56.434095-36.560294 122.805091-55.309163 190.113531-53.621765 58.121494-0.562466 116.430475 12.561742 169.114796 38.435181l3.562285 1.687398c7.312058 4.312239 14.436629 7.312058 21.748688 0 8.436991-9.186946 3.374797-17.248959-2.249864-26.248416-0.374978-0.374978-0.562466-0.937444-0.749955-1.312421z" horiz-adv-x="1024" />
<glyph glyph-name="folder-open" unicode="&#60097;" d="M1003.153333 491.04a52.933333 52.933333 0 0 1-42.38 20.96H896V629.333333a53.393333 53.393333 0 0 1-53.333333 53.333334H461.253333a10.573333 10.573333 0 0 0-7.54 3.126666L344.46 795.046667A52.986667 52.986667 0 0 1 306.746667 810.666667H53.333333a53.393333 53.393333 0 0 1-53.333333-53.333334v-704a53.393333 53.393333 0 0 1 53.333333-53.333333h796.893334a53.453333 53.453333 0 0 1 51.453333 39.333333l110.546667 405.333334a52.953333 52.953333 0 0 1-9.073334 46.373333zM53.333333 768h253.413334a10.573333 10.573333 0 0 0 7.54-3.126667l109.253333-109.253333A52.986667 52.986667 0 0 1 461.253333 640H842.666667a10.666667 10.666667 0 0 0 10.666666-10.666667v-117.333333H173.773333a53.453333 53.453333 0 0 1-51.453333-39.333333L42.666667 180.633333V757.333333a10.666667 10.666667 0 0 0 10.666666 10.666667z m917.726667-312.14l-110.546667-405.333333a10.666667 10.666667 0 0 0-10.286666-7.86H63.226667a10.666667 10.666667 0 0 0-10.286667 13.473333l110.546667 405.333333A10.666667 10.666667 0 0 0 173.773333 469.333333h787a10.666667 10.666667 0 0 0 10.286667-13.473333z" horiz-adv-x="1024" />
<glyph glyph-name="leaf" unicode="&#59137;" d="M956.681096 804.64791c-4.274742 5.084693-10.574363 8.009517-17.188965 8.009518l-22.948617 0c-226.786337 0.089995-405.920544 0.134992-537.807598-31.408108-70.87073-16.963978-125.677428-42.567435-167.524906-78.250286-45.177278-38.51768-75.955424-88.95964-94.179326-154.295703-12.104271-43.377386-17.773929-121.717666 6.119631-199.652972 8.819468-28.798265 22.588639-55.976628 40.947533-80.950123-54.716703-85.719836-102.008854-198.618034-102.008854-332.93494 0-12.419252 10.079393-22.498645 22.498645-22.498645s22.498645 10.079393 22.498644 22.498645c0 207.212515 122.752604 360.923254 195.918197 434.043849 52.511836 52.511836 111.863261 96.834166 171.709654 128.152278 55.706643 29.158243 108.938436 45.222275 149.885969 45.222276 12.419252 0 22.498645 10.079393 22.498645 22.498645s-10.079393 22.498645-22.498645 22.498644c-48.777061 0-107.813504-17.413951-170.719714-50.396963-63.806156-33.432986-126.982349-80.50015-182.643996-136.206794-23.488585-23.488585-51.746883-54.806698-79.825191-93.144388-10.889344 17.143967-19.348835 35.322872-25.153484 54.311728-22.498645 73.435576-14.219143 144.171314-5.804651 174.364494 64.346123 230.611105 308.771396 231.19607 752.21968 231.061079-52.871815-282.942953-151.460874-466.081919-293.202335-544.512193-131.482078-72.760616-261.884222-33.073008-311.87621-12.59924-10.529365 4.31974-21.148726 9.62942-31.453105 15.74905-10.664358 6.344618-24.478525 2.83483-30.823143-7.829528s-2.83483-24.478525 7.829528-30.823142c12.239263-7.289561 24.838503-13.589181 37.437745-18.76387 27.988313-11.429312 73.075598-26.098428 128.917233-30.913138 12.329257-1.079935 24.523523-1.574905 36.627793-1.574905 66.101018 0 128.197276 15.884043 185.163844 47.382145 72.400638 40.047587 135.036864 105.248659 186.243779 193.668332 60.026383 103.718751 105.203661 241.275463 134.226913 408.845367 1.124932 6.524607-0.67496 13.229203-4.949702 18.313897z" horiz-adv-x="1025" />
<glyph glyph-name="gitee" unicode="&#59035;" d="M512-128C229.222-128 0 101.222 0 384S229.222 896 512 896s512-229.222 512-512-229.222-512-512-512z m259.149 568.883h-290.74a25.293 25.293 0 0 1-25.292-25.293l-0.026-63.206c0-13.952 11.315-25.293 25.267-25.293h177.024c13.978 0 25.293-11.315 25.293-25.267v-12.646a75.853 75.853 0 0 0-75.853-75.853h-240.23a25.293 25.293 0 0 0-25.267 25.293V478.797a75.853 75.853 0 0 0 75.827 75.853h353.946a25.293 25.293 0 0 1 25.267 25.292l0.077 63.207a25.293 25.293 0 0 1-25.268 25.293H417.152a189.62 189.62 0 0 1-189.62-189.645V124.85c0-13.977 11.316-25.293 25.294-25.293h372.94a170.65 170.65 0 0 1 170.65 170.65V415.616a25.293 25.293 0 0 1-25.293 25.267z" horiz-adv-x="1024" />
<glyph glyph-name="folder" unicode="&#60094;" d="M947.911111 679.727408H519.417777L407.037777 792.107408A52.986667 52.986667 0 0 1 369.324444 807.727408H73.244444a53.393333 53.393333 0 0 1-53.333333-53.333334v-704a53.393333 53.393333 0 0 1 53.333333-53.333333h874.666667a53.393333 53.393333 0 0 1 53.333333 53.333333V626.394074a53.393333 53.393333 0 0 1-53.333333 53.333334zM73.244444 765.060741h296.08a10.573333 10.573333 0 0 0 7.54-3.126667L459.071111 679.727408H73.244444a53.546667 53.546667 0 0 1-10.666667-1.073334V754.394074a10.666667 10.666667 0 0 0 10.666667 10.666667z m885.333333-714.666667a10.666667 10.666667 0 0 0-10.666666-10.666666H73.244444a10.666667 10.666667 0 0 0-10.666667 10.666666V626.394074a10.666667 10.666667 0 0 0 10.666667 10.666667h874.666667a10.666667 10.666667 0 0 0 10.666666-10.666667z" horiz-adv-x="1024" />
<glyph glyph-name="folder-open" unicode="&#60097;" d="M1003.153333 488.100741a52.933333 52.933333 0 0 1-42.38 20.96H896V626.394074a53.393333 53.393333 0 0 1-53.333333 53.333334H461.253333a10.573333 10.573333 0 0 0-7.54 3.126666L344.46 792.107408A52.986667 52.986667 0 0 1 306.746667 807.727408H53.333333a53.393333 53.393333 0 0 1-53.333333-53.333334v-704a53.393333 53.393333 0 0 1 53.333333-53.333333h796.893334a53.453333 53.453333 0 0 1 51.453333 39.333333l110.546667 405.333334a52.953333 52.953333 0 0 1-9.073334 46.373333zM53.333333 765.060741h253.413334a10.573333 10.573333 0 0 0 7.54-3.126667l109.253333-109.253333A52.986667 52.986667 0 0 1 461.253333 637.060741H842.666667a10.666667 10.666667 0 0 0 10.666666-10.666667v-117.333333H173.773333a53.453333 53.453333 0 0 1-51.453333-39.333333L42.666667 177.694074V754.394074a10.666667 10.666667 0 0 0 10.666666 10.666667z m917.726667-312.14l-110.546667-405.333333a10.666667 10.666667 0 0 0-10.286666-7.86H63.226667a10.666667 10.666667 0 0 0-10.286667 13.473333l110.546667 405.333333A10.666667 10.666667 0 0 0 173.773333 466.394074h787a10.666667 10.666667 0 0 0 10.286667-13.473333z" horiz-adv-x="1024" />
<glyph glyph-name="gitee" unicode="&#59035;" d="M512-96C246.895625-96 32 118.895625 32 384S246.895625 864 512 864s480-214.895625 480-480-214.895625-480-480-480z m242.952188 533.327813h-272.568751a23.712187 23.712187 0 0 1-23.71125-23.712188l-0.024375-59.255625c0-13.08 10.607812-23.712187 23.687813-23.712187h165.96c13.104375 0 23.712187-10.607812 23.712187-23.687813v-11.855625a71.112188 71.112188 0 0 0-71.112187-71.112187h-225.215625a23.712187 23.712187 0 0 0-23.687812 23.712187V472.872187a71.112188 71.112188 0 0 0 71.087812 71.112188h331.824375a23.712187 23.712187 0 0 1 23.687812 23.71125l0.072188 59.256563a23.712187 23.712187 0 0 1-23.68875 23.712187H423.08a177.76875 177.76875 0 0 1-177.76875-177.792188V141.046875c0-13.103438 10.60875-23.712187 23.713125-23.712187h349.63125a159.984375 159.984375 0 0 1 159.984375 159.984375V413.64a23.712187 23.712187 0 0 1-23.712187 23.687812z" horiz-adv-x="1024" />
<glyph glyph-name="github" unicode="&#59047;" d="M512 883.32190493c275.66730126 0 499.32190493-223.65460366 499.32190493-499.32190493 0-220.40901063-143.01411555-407.65472541-341.32813256-473.98131826-25.34058667-4.53550763-34.45321159 11.06830222-34.45321159 24.05067207 0 16.26957255 0.6657627 70.19633778 0.66576271 137.18869334 0 46.81142841-15.60380985 76.72913237-33.7874489 92.33294222 111.18234397 12.35821682 228.19011015 54.63413874 228.1901113 246.41536 0 54.63413874-19.51516445 98.82412715-51.34693604 133.9431003 5.20127033 13.02397952 22.09499477 63.70515285-5.20127033 132.61157604-41.61015922 13.02397952-137.18869333-51.34693603-137.18869333-51.34693604a469.36259015 469.36259015 0 0 1-249.6609519 0S291.63259904 689.58500523 250.02244096 676.56102571c-27.29626397-68.90642318-10.40253952-119.62920619-5.20127033-132.61157604-31.87338126-35.11897429-51.34693603-79.3089627-51.34693604-133.9431003 0-191.15706937 116.38361429-234.05714318 227.56595826-246.41536-14.31389411-13.02397952-27.29626397-35.11897429-31.87338126-66.95074588-28.62778937-13.02397952-101.44556715-35.11897429-144.96979285 41.61015921-27.29626397 47.47719111-76.72913237 51.34693603-76.72913351 51.34693604-48.76710571 0.6657627-3.24559189-30.54185699-3.2455919-30.541857 32.49753429-14.93804715 55.25829063-72.81777778 55.25829064-72.81777777 29.25194126-89.08735033 168.39631189-59.16964523 168.39631302-59.16964523 0-41.61015922 0.6657627-80.5988773 0.66576157-92.95709525 0-13.02397952-9.11262493-28.62778937-34.45321045-24.05067094C155.77543111-23.61311459000001 12.76131555 163.63259903999995 12.76131555 384.04160967c0 275.66730126 223.65460366 499.32190493 499.32190493 499.32190492zM201.87948715 166.21242937c1.28991459 2.62144-0.6657627 5.86703189-4.53550763 7.78109952-3.91135459 1.28991459-7.15694763 0.6657627-8.44686222-1.2899146-1.28991459-2.62144 0.6657627-5.86703189 4.53550763-7.78109952 3.24559189-1.9556773 7.15694763-1.28991459 8.44686222 1.2899146z m20.13931634-22.13660444c2.62144 1.9556773 1.9556773 6.49118493-1.2899146 10.40253952-3.24559189 3.24559189-7.78109952 4.53550763-10.40253952 1.95567729-2.62144-1.9556773-1.9556773-6.49118493 1.2899146-10.40253952 3.24559189-3.24559189 7.78109952-4.53550763 10.40253952-1.95567729z m19.51516444-29.25194127c3.24559189 2.62144 3.24559189 7.78109952 0 12.35821682-2.62144 4.53550763-7.78109952 6.49118493-11.06830222 3.91135459-3.24559189-1.9556773-3.24559189-7.15694763 0-11.69245411s8.44686222-6.49118493 11.06830222-4.53550763z m27.29626396-27.2962651c2.62144 2.62144 1.28991459 8.44686222-2.62144 12.35821795-4.53550763 4.53550763-10.40253952 5.20127033-13.02397952 1.9556773-3.24559189-2.62144-1.9556773-8.44686222 2.62144-12.35821682 4.53550763-4.53550763 10.40253952-5.20127033 13.02397952-1.95567843z m37.0746516-16.26957141c1.28991459 3.91135459-2.62144 8.44686222-8.44686223 10.40253952-5.20127033 1.28991459-11.06830222-0.6657627-12.35821681-4.53550763s2.62144-8.44686222 8.44686222-9.73677682c5.20127033-1.9556773 11.06830222 0 12.35821682 3.9113546z m40.94439651-3.24559304c0 4.53550763-5.20127033 7.78109952-11.06830222 7.15694763-5.86703189 0-10.40253952-3.24559189-10.40253952-7.15694763 0-4.53550763 4.53550763-7.78109952 11.06830222-7.15694648 5.86703189 0 10.40253952 3.24559189 10.40253952 7.15694648z m37.69880349 6.49118493c-0.6657627 3.91135459-5.86703189 6.49118493-11.69245412 5.86703303-5.86703189-1.28991459-9.73677682-5.20127033-9.11262492-9.73677796 0.6657627-3.91135459 5.86703189-6.49118493 11.69245411-5.20126918s9.73677682 5.20127033 9.11262493 9.11262492z" horiz-adv-x="1024" />

Before

Width:  |  Height:  |  Size: 322 KiB

After

Width:  |  Height:  |  Size: 326 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
/*! Lazy Load 2.0.0-rc.2 - MIT license - Copyright 2007-2019 Mika Tuupola */
!function(t,e){"object"==typeof exports?module.exports=e(t):"function"==typeof define&&define.amd?define([],e):t.LazyLoad=e(t)}("undefined"!=typeof global?global:this.window||this.global,function(t){"use strict";function e(t,e){this.settings=s(r,e||{}),this.images=t||document.querySelectorAll(this.settings.selector),this.observer=null,this.init()}"function"==typeof define&&define.amd&&(t=window);const r={src:"data-src",srcset:"data-srcset",selector:".lazyload",root:null,rootMargin:"0px",threshold:0},s=function(){let t={},e=!1,r=0,o=arguments.length;"[object Boolean]"===Object.prototype.toString.call(arguments[0])&&(e=arguments[0],r++);for(;r<o;r++)!function(r){for(let o in r)Object.prototype.hasOwnProperty.call(r,o)&&(e&&"[object Object]"===Object.prototype.toString.call(r[o])?t[o]=s(!0,t[o],r[o]):t[o]=r[o])}(arguments[r]);return t};if(e.prototype={init:function(){if(!t.IntersectionObserver)return void this.loadImages();let e=this,r={root:this.settings.root,rootMargin:this.settings.rootMargin,threshold:[this.settings.threshold]};this.observer=new IntersectionObserver(function(t){Array.prototype.forEach.call(t,function(t){if(t.isIntersecting){e.observer.unobserve(t.target);let r=t.target.getAttribute(e.settings.src),s=t.target.getAttribute(e.settings.srcset);"img"===t.target.tagName.toLowerCase()?(r&&(t.target.src=r),s&&(t.target.srcset=s)):t.target.style.backgroundImage="url("+r+")"}})},r),Array.prototype.forEach.call(this.images,function(t){e.observer.observe(t)})},loadAndDestroy:function(){this.settings&&(this.loadImages(),this.destroy())},loadImages:function(){if(!this.settings)return;let t=this;Array.prototype.forEach.call(this.images,function(e){let r=e.getAttribute(t.settings.src),s=e.getAttribute(t.settings.srcset);"img"===e.tagName.toLowerCase()?(r&&(e.src=r),s&&(e.srcset=s)):e.style.backgroundImage="url('"+r+"')"})},destroy:function(){this.settings&&(this.observer.disconnect(),this.settings=null)}},t.lazyload=function(t,r){return new e(t,r)},t.jQuery){const r=t.jQuery;r.fn.lazyload=function(t){return t=t||{},t.attribute=t.attribute||"data-src",new e(r.makeArray(this),t),this}}return e});

View File

@@ -75,7 +75,7 @@
<div class="layui-form-item">
<label class="layui-form-label">数据库密码</label>
<div class="layui-input-block">
<input type="text" class="layui-input" name="db_password" autocomplete="off" lay-verify="required" lay-reqtext="请输入数据库密码" placeholder="请输入数据库密码" value="{$envInfo.DB_PASS}">
<input type="password" class="layui-input" name="db_password" autocomplete="off" lay-verify="required" lay-reqtext="请输入数据库密码" placeholder="请输入数据库密码" value="{$envInfo.DB_PASS}">
</div>
</div>