44 Commits
v8.0.2 ... v8.0

Author SHA1 Message Date
wolfcode
cb624aa51a Update README.md 2025-03-12 14:26:49 +08:00
wolfcode
187f97a511 Update AdminController.php 2025-01-15 09:30:42 +08:00
wolfcode
f05c7ded0e Update composer.json 2025-01-09 11:50:47 +08:00
wolfcode
386b11cfc9 Update AdminController.php 2025-01-08 16:05:13 +08:00
wolfcode
c22be8ac76 Merge pull request #17 from 1rmb/main
隐藏搜索框提示
2024-12-31 11:06:00 +08:00
淘青松
ebce7f3b0e 隐藏搜索框提示
将注释属性添加到table标签上即可
2024-12-31 10:56:13 +08:00
wolfcode
f8b6d847d5 fix(admin): 阿里云对象存储签名版本 V4, upgrade OSS SDK and implement signature version 4
- Upgrade aliyuncs/oss-sdk-php to v2.7.2 or above
- Implement signature version 4 for OSS client
- Add region and environment variable credentials provider
- Update error handling and code formatting
2024-12-27 11:25:02 +08:00
wolfcode
55a9b13e69 refactor(curd): optimize relation binding and code generation
- Move relation binding logic from model constructor to controller
- Generate controller code for binding select fields-Remove unnecessary code and templates
- Improve code readability and maintainability
2024-12-20 17:09:04 +08:00
wolfcode
5d58248cf4 feat(curd): add relation data binding and optimize model construction
- Add relation data binding in model construction
- Update model template to include new relation construction logic
- Remove unused index method template
2024-12-20 16:26:56 +08:00
wolfcode
bbd0320a35 Update README.md 2024-12-19 10:40:01 +08:00
wolfcode
d62a67aafe refactor(admin): use param instead of post to get request parameters
- In both Admin and Menu controllers, change $request->post('id') to $request->param('id')
- This change improves code consistency and allows for more flexible parameter retrieval
2024-12-17 11:22:01 +08:00
wolfcode
c6cc9d4164 feat(common): add support for absolute URLs in __url function
- Implement a check for absolute URLs using filter_var with FILTER_VALIDATE_URL
- If the provided URL is an absolute URL, it is returned as is
- This enhancement allows the __url function to handle both relative and absolute URLs
2024-12-16 13:51:27 +08:00
wolfcode
add8c5144c fix(easymde): update font-awesome css link
- Replace external font-awesome link with local static resource
- Update font-awesome CSS path to local version
2024-12-13 10:44:24 +08:00
wolfcode
62f591045e refactor(admin): enhance delete method to support custom primary keys
- Update delete method in Curd trait to accept Request parameter
- Add support for deleting multiple records using 'id' parameter
- Improve error handling for non-existent data
2024-12-11 17:00:34 +08:00
wolfcode
7470790657 refactor(admin): optimize system log middleware
- Adjust class name construction logic in SystemLog middleware
- Simplify condition for checking controller and action existence
2024-12-09 13:55:24 +08:00
wolfcode
11fa69afaf Merge pull request #13 from Rodots/main
路由配置新增操作方法的参数绑定方式
2024-12-02 16:04:23 +08:00
Rodots
8983705ce4 feat: Parameter Binding Method of New Operation Method for Routing Configuration 2024-12-02 16:01:49 +08:00
wolfcode
95ccd4071b feat(easy-admin): add support for custom template functions in operat column-Implement the ability to use custom template functions in the operat column of easy-admin tables
- Add a new button in the goods list page to demonstrate the usage of custom templates
2024-12-02 11:03:26 +08:00
wolfcode
09f3ea7e54 feat(search): add date type search support
- Implement search functionality for date type fields
- Add new case for 'date' in the search HTML generation logic
- Include date-specific input field with appropriate attributes
2024-11-29 18:50:02 +08:00
wolfcode
9cadb27c9e 🚀Layui v2.9.20 2024-11-29 16:38:46 +08:00
wolfcode
2772034a93 fix(admin): add type attribute to login button
- Add 'type="button"' attribute to the login button in admin login page
- This change prevents the button from being submitted as a form Reset button
2024-11-28 18:19:12 +08:00
wolfcode
08ea79033c feat(admin): add ignoreNode property to skip unnecessary node generation
- Add $ignoreNode property to Goods controller to specify methods to ignore
- Update Node service to check and skip ignored methods during node generation
- This change helps to filter out unnecessary nodes, improving system performance and readability
2024-11-28 10:54:15 +08:00
wolfcode
e7f09d9c68 refactor(install):使用PDO初始化安装 use PDO instead of mysqli for database operations
- Replace mysqli with PDO for database connection and queries
- Create a separate method for generating PDO DSN
- Update error handling to use PDOException
- Remove unnecessary mysqli_set_charset and mysqli_select_db calls
2024-11-27 12:33:04 +08:00
wolfcode
e2effb762c build(deps): update dependency wolfcode/authenticator to v0.0.6
- Updated wolfcode/authenticator from version 0.0.5 to 0.0.6 in composer.json
2024-11-22 16:59:07 +08:00
wolfcode
0e18825808 fix(authenticator): update dependency and adjust QR code generation
- Update wolfcode/authenticator from 0.0.3 to 0.0.5
- Modify QR code generation method in Index controller
2024-11-22 16:51:36 +08:00
wolfcode
c031b09422 build(deps): update topthink/think-multi-app to ^1.1.0
- Replace dev-master with stable version ^1.1.0 for topthink/think-multi-app- This change ensures compatibility and stability in dependency management
2024-11-22 15:16:44 +08:00
wolfcode
231fd48e2f build(deps): update topthink/think-multi-app to dev-master
- Change the version of topthink/think-multi-app from ^1.0 to dev-master in composer.json
2024-11-22 09:32:06 +08:00
wolfcode
53772badd4 Merge pull request #12 from Rodots/main
fix: 修正命名空间,移除无用类的引用
2024-11-14 16:45:09 +08:00
wolfcode
3e329a4ea3 fix(install): update ea_system_admin insert statement
- Add missing column for 'status' in ea_system_admin insert statement
- Set 'status' column to 1 for the admin user
2024-11-13 16:30:15 +08:00
wolfcode
f015a90b89 style(layuimini): remove redundant CSS rules for nav more icons- Removed specific margin-top rules for .layuimini-menu-left and .layuimini-menu-left-zoom classes
- Kept the general rule for .layuimini-menu-left .layui-nav .layui-nav-mored and .layuimini-menu-left .layui-nav-itemed > a .layui-nav-more
2024-11-12 17:21:04 +08:00
wolfcode
75c668b966 feat(auth): 新增谷歌验证码登录 implement Google Authenticator support for two-factor authentication
- Add Google Authenticator integration for enhanced login security-Update admin edit page to include login type selection
- Modify login process to support two-factor authentication
- Add new database fields for login type and GA secret
- Update client-side JavaScript to handle GA code input and validation
2024-11-12 10:51:48 +08:00
wolfcode
f3e5a041d1 feat(ueditor): add custom upload service and other enhancements
- Add custom upload service support
- Implement server response preparation function
- Enable loading config from server
- Add content import button to toolbar
- Update image alignment buttons order
- Add tip error function for better error handling
- Remove unused comments and simplify configuration options
2024-11-11 11:22:47 +08:00
Rodots
34354837d5 fix: 修正命名空间,移除无用类的引用 2024-11-06 14:51:15 +08:00
wolfcode
3b219cfe7f feat(install): add official website tutorial and common questions links
- Add links to the official website tutorial and common questions in the installation page
- Update the logo image style for better visual presentation
2024-11-04 11:53:39 +08:00
wolfcode
ed99fe79fd feat(admin): 快捷操作支持翻页滚动显示 and optimize UI for better user experience
- Add swiper plugin support for carousel functionality
- Optimize UI for better user experience by using swiper
- Update index.js to support swiper initialization
- Modify welcome.html to include swiper HTML structure
- Adjust welcome.css for better styling of swiper elements
2024-10-29 17:24:54 +08:00
wolfcode
846b8ffafc feat(easy-admin): update window open URL for new tab button
- Change the URL for the "New tab open" button to use the iframe's content URL
- Add fallback to current window URL if iframe content URL cannot be accessed
- Improve user experience by opening the correct URL in a new tab

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2024-10-28 18:26:26 +08:00
wolfcode
47ef5d9f10 feat(auth): 改用 zTree 替换 layui 的 tree 组件 use zTree for node authorization
- Replace layui tree with zTree for better performance and flexibility
- Update auth.js to use zTree for node rendering and checking
- Add zTree CSS and JS files to project

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2024-10-28 16:35:06 +08:00
wolfcode
70ec12f406 feat(admin): 新增搜索表单多选功能 add xmSelect plugin support for multiple selection
- Add xmSelect case handling in AdminController for 'in' operation
- Include xmSelect.js in admin layout
- Update easy-admin.js to support xmSelect initialization and data binding
- Modify goods.js to demonstrate xmSelect usage with simulated data
- Adjust public.css for better styling of xmSelect elements

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2024-10-25 11:44:19 +08:00
wolfcode
e15b56ecc9 Update composer.json 2024-10-24 09:37:09 +08:00
wolfcode
af275e1c6a fix(install): check for .env file existence before installation
- Add a check for the presence of the .env file in the root directory
- Update the index method to redirect to the admin page with a default value

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2024-10-23 10:27:06 +08:00
wolfcode
20d4038159 Update public.css 2024-10-22 16:08:23 +08:00
wolfcode
475a1ef84d refactor(admin): update login.js to include jQuery dependency
- Add jQuery as a dependency in the define function
- Update the function parameters to include jQuery ($)

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2024-10-22 14:43:50 +08:00
wolfcode
9307e724a9 refactor(SystemLogService): optimize detectTable method and improve code quality
- Remove unnecessary return type annotation in instance method
- Add type hint for SystemLogService instance
- Use variable for cache key to improve code readability
- Update cache key name for consistency

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2024-10-22 11:54:59 +08:00
wolfcode
6616e96724 perf(middleware): optimize system log middleware for performance
- Remove redundant code for checking ignoreLog property
- Consolidate logic to check ignoreLog property in a single location
- Improve readability and maintainability of the code

Signed-off-by: wolfcode <37436228+wolf-leo@users.noreply.github.com>
2024-10-22 11:31:06 +08:00
75 changed files with 57695 additions and 32080 deletions

View File

@@ -1,21 +1,23 @@
<div align="center" dir="auto">
<img alt="log" src="/public/static/common/images/logo-8.png" />
<span><img src="https://img.shields.io/badge/php-%3E=8.0.0-brightgreen.svg?style=for-the-badge&logo=php&colorB=ff69b4" alt="php"></span>
<span><img src="https://img.shields.io/badge/mysql-%3E=5.7-brightgreen.svg?style=for-the-badge&logo=mysql&colorB=blue" alt="MySQL"></span>
<span><img src="https://img.shields.io/badge/thinkphp-%3E=8.0.0-brightgreen.svg?style=for-the-badge&logo=thinkphp" alt="ThinkPHP"></span>
<span><img src="https://img.shields.io/badge/layui-%3E=2.9.0-brightgreen.svg?style=for-the-badge&logo=layui&colorB=orange" alt="layui"></span>
<span><img src="https://img.shields.io/badge/license-MIT-green?style=for-the-badge&logo=license&colorB=purple" alt="License"></span>
<p>
<img src="https://img.shields.io/badge/php-%3E=8.0.0-brightgreen.svg?style=for-the-badge&logo=php&colorB=ff69b4" alt="php">
<img src="https://img.shields.io/badge/mysql-%3E=5.7-brightgreen.svg?style=for-the-badge&logo=mysql&colorB=blue" alt="MySQL">
<img src="https://img.shields.io/badge/thinkphp-%3E=8.0.0-brightgreen.svg?style=for-the-badge&logo=thinkphp" alt="ThinkPHP">
<img src="https://img.shields.io/badge/layui-%3E=2.9.0-brightgreen.svg?style=for-the-badge&logo=layui&colorB=orange" alt="layui">
<img src="https://img.shields.io/badge/license-MIT-green?style=for-the-badge&logo=license&colorB=purple" alt="License">
</p>
</div>
## `EasyAdmin8`所有版本 (当前项目为`ThinkPHP`版本)
| | Github | Gitee |
|----------|:--------------------------------------------------------------------:|:-----------------------------------------------------------------:|
| ThinkPHP | [EasyAdmin8](https://github.com/wolf-leo/EasyAdmin8) | [EasyAdmin8](https://gitee.com/wolf18/EasyAdmin8) |
| Laravel | [EasyAdmin8-Laravel](https://github.com/wolf-leo/EasyAdmin8-Laravel) | [EasyAdmin8-Laravel](https://gitee.com/wolf18/EasyAdmin8-Laravel) |
| webman | [EasyAdmin8-webman](https://github.com/wolf-leo/EasyAdmin8-webman) | [EasyAdmin8-webman](https://gitee.com/wolf18/EasyAdmin8-webman) |
| | Github | Gitee |
|----------|:----------------------------------------------------------------------:|:---------------------------------------------------------------------:|
| ThinkPHP | [EasyAdmin8](https://github.com/EasyAdmin8/EasyAdmin8) | [EasyAdmin8](https://gitee.com/EasyAdmin8/EasyAdmin8) |
| Laravel | [EasyAdmin8-Laravel](https://github.com/EasyAdmin8/EasyAdmin8-Laravel) | [EasyAdmin8-Laravel](https://gitee.com/EasyAdmin8/EasyAdmin8-Laravel) |
| webman | [EasyAdmin8-webman](https://github.com/EasyAdmin8/EasyAdmin8-webman) | [EasyAdmin8-webman](https://gitee.com/EasyAdmin8/EasyAdmin8-webman) |
## 项目介绍
@@ -39,22 +41,16 @@
> EasyAdmin8 使用 Composer 来管理项目依赖。因此,在使用 EasyAdmin8 之前,请确保你的机器已经安装了 Composer。
### 通过一键安装命令
```
if [ -f /usr/bin/curl ];then curl -sSO https://easyadmin8.top/auto-install-EasyAdmin8.sh;else wget -O auto-install-EasyAdmin8.sh https://easyadmin8.top/auto-install-EasyAdmin8.sh;fi;bash auto-install-EasyAdmin8.sh
```
### 通过`git`下载安装包,`composer`安装依赖包
```
1.下载安装包
git clone https://github.com/wolf-leo/EasyAdmin8
git clone -b v8.0 https://github.com/EasyAdmin8/EasyAdmin8
或者
git clone https://gitee.com/wolf18/EasyAdmin8
git clone -b v8.0 https://gitee.com/EasyAdmin8/EasyAdmin8
2.安装依赖包(确保 PHP 版本 >= 8.0
@@ -75,7 +71,7 @@ if [ -f /usr/bin/curl ];then curl -sSO https://easyadmin8.top/auto-install-EasyA
## CURD命令大全
> 参考 [CURD命令大全](CURD.md)
> 参考 [CURD命令大全](https://edocs.easyadmin8.top/curd/command.html)
## 常见问题
@@ -97,7 +93,7 @@ if [ -f /usr/bin/curl ];then curl -sSO https://easyadmin8.top/auto-install-EasyA
## 相关文档
* [ThinkPHP 8.0](https://doc.thinkphp.cn/v8_0)
* [ThinkPHP 8.1](https://doc.thinkphp.cn)
* [EasyAdmin](http://easyadmin.99php.cn/docs)
@@ -137,4 +133,4 @@ if [ -f /usr/bin/curl ];then curl -sSO https://easyadmin8.top/auto-install-EasyA
* 如果本声明的任何部分被认为无效或不可执行,其余部分仍具有完全效力。不可执行的部分声明,并不构成我们放弃执行该声明的权利。
* 本开源项目有权随时对本声明条款及附件内容进行单方面的变更,并以消息推送、网页公告等方式予以公布,公布后立即自动生效,无需另行单独通知;若您在本声明内容公告变更后继续使用的,表示您已充分阅读、理解并接受修改后的声明内容。
* 本开源项目有权随时对本声明条款及附件内容进行单方面的变更,并以消息推送、网页公告等方式予以公布,公布后立即自动生效,无需另行单独通知;若您在本声明内容公告变更后继续使用的,表示您已充分阅读、理解并接受修改后的声明内容。

View File

@@ -36,11 +36,9 @@ class Index extends AdminController
$mysqlVersion = Db::query("select version() as version")[0]['version'] ?? '未知';
$phpVersion = phpversion();
$versions = compact('tpVersion', 'mysqlVersion', 'phpVersion');
$quicks = SystemQuick::field('id,title,icon,href')
->where(['status' => 1])
->order('sort', 'desc')
->limit(8)
->select();
$quick_list = SystemQuick::field('id,title,icon,href')
->where(['status' => 1])->order('sort', 'desc')->limit(50)->select()->toArray();
$quicks = array_chunk($quick_list, 8);
$this->assign(compact('quicks', 'versions'));
return $this->fetch();
}
@@ -55,7 +53,7 @@ class Index extends AdminController
*/
public function editAdmin(Request $request): string
{
$id = $this->adminUid;
$id = $this->adminUid;
$row = (new SystemAdmin())
->withoutField('password')
->find($id);
@@ -66,15 +64,20 @@ class Index extends AdminController
$rule = [];
$this->validate($post, $rule);
try {
$save = $row
->allowField(['head_img', 'phone', 'remark', 'update_time'])
->save($post);
}catch (Exception $e) {
$login_type = $post['login_type'] ?? 1;
if ($login_type == 2) {
$ga_secret = (new SystemAdmin())->where('id', $id)->value('ga_secret');
if (empty($ga_secret)) $this->error('请先绑定谷歌验证器');
}
$save = $row->allowField(['head_img', 'phone', 'remark', 'update_time', 'login_type'])->save($post);
}catch (\PDOException $e) {
$this->error('保存失败');
}
$save ? $this->success('保存成功') : $this->error('保存失败');
}
$this->assign('row', $row);
$notes = (new SystemAdmin())->notes;
$this->assign('notes', $notes);
return $this->fetch();
}
@@ -82,13 +85,10 @@ class Index extends AdminController
* 修改密码
* @param Request $request
* @return string
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function editPassword(Request $request): string
{
$id = $this->adminUid;
$id = $this->adminUid;
$row = (new SystemAdmin())
->withoutField('password')
->find($id);
@@ -124,4 +124,37 @@ class Index extends AdminController
return $this->fetch();
}
/**
* 设置谷歌验证码
* @param Request $request
* @return string
* @throws Exception
*/
public function set2fa(Request $request): string
{
$id = $this->adminUid;
$row = (new SystemAdmin())->withoutField('password')->find($id);
if (!$row) $this->error('用户信息不存在');
// You can see: https://gitee.com/wolf-code/authenticator
$ga = new \Wolfcode\Authenticator\google\PHPGangstaGoogleAuthenticator();
if (!$request->isAjax()) {
$old_secret = $row->ga_secret;
$secret = $ga->createSecret(32);
$ga_title = $this->isDemo ? 'EasyAdmin8演示环境' : '可自定义修改显示标题';
$dataUri = $ga->getQRCode($ga_title, $secret);
$this->assign(compact('row', 'dataUri', 'old_secret', 'secret'));
return $this->fetch();
}
$this->isDemo && $this->error('演示环境下不允许修改');
$post = $request->post();
$ga_secret = $post['ga_secret'] ?? '';
$ga_code = $post['ga_code'] ?? '';
if (empty($ga_code)) $this->error('请输入验证码');
if (!$ga->verifyCode($ga_secret, $ga_code)) $this->error('验证码错误');
$row->ga_secret = $ga_secret;
$row->login_type = 2;
$row->save();
$this->success('操作成功');
}
}

View File

@@ -56,6 +56,11 @@ class Login extends AdminController
if ($admin->status == 0) {
$this->error('账号已被禁用');
}
if ($admin->login_type == 2) {
if (empty($post['ga_code'])) $this->error('请输入谷歌验证码', ['is_ga_code' => true]);
$ga = new \Wolfcode\Authenticator\google\PHPGangstaGoogleAuthenticator();
if (!$ga->verifyCode($admin->ga_secret, $post['ga_code'])) $this->error('谷歌验证码错误');;
}
$admin->login_num += 1;
$admin->save();
$admin = $admin->toArray();

View File

@@ -20,6 +20,12 @@ use think\response\Json;
class Goods extends AdminController
{
/**
* 过滤不需要生成的权限节点 默认 CURD 中会自动生成部分节点 可以在此处过滤
* @var array[]
*/
protected array $ignoreNode = ['export'];
public function __construct(App $app)
{
parent::__construct($app);

View File

@@ -150,9 +150,10 @@ class Admin extends AdminController
/**
* @NodeAnnotation(title="删除")
*/
public function delete($id): void
public function delete(Request $request): void
{
$this->checkPostRequest();
$id = $request->param('id');
$row = $this->model->whereIn('id', $id)->select();
$row->isEmpty() && $this->error('数据不存在');
$id == AdminConstant::SUPER_ADMIN_ID && $this->error('超级管理员不允许修改');

View File

@@ -132,9 +132,10 @@ class Menu extends AdminController
/**
* @NodeAnnotation(title="删除")
*/
public function delete($id): void
public function delete(Request $request): void
{
$this->checkPostRequest();
$id = $request->param('id');
$row = $this->model->whereIn('id', $id)->select();
empty($row) && $this->error('数据不存在');
try {

View File

@@ -48,15 +48,6 @@ class SystemLog
if ($request->isAjax()) {
if (in_array($method, ['post', 'put', 'delete'])) {
$controller = $request->controller();
if (str_contains($controller, '.')) $controller = str_replace('.', '\\', $controller);
$action = $request->action();
$controllerClass = 'app\\admin\\controller\\' . $controller;
$classObj = new ReflectionClass($controllerClass);
$properties = $classObj->getDefaultProperties();
$ignoreLog = $properties['ignoreLog'] ?? [];
if (in_array($action, $ignoreLog)) return $response;
$title = '';
try {
$pathInfo = $request->pathinfo();
@@ -65,10 +56,13 @@ class SystemLog
$pathInfoExp = explode('.', $pathInfoExp[0] ?? '');
$_name = $pathInfoExp[0] ?? '';
$_controller = ucfirst($pathInfoExp[1] ?? '');
if ($_name && $_controller && $_action) {
$className = "app\admin\controller\\{$_name}\\{$_controller}";
$className = $_controller ? "app\admin\controller\\{$_name}\\{$_controller}" : "app\admin\controller\\{$_name}";
if ($_name && $_action) {
$reflectionClass = new \ReflectionClass($className);
$parser = new DocParser();
$properties = $reflectionClass->getDefaultProperties();
$ignoreLog = $properties['ignoreLog'] ?? [];
if (in_array($_action, $ignoreLog)) return $response;
$parser = new DocParser();
$parser->setIgnoreNotImportedAnnotations(true);
$reader = new AnnotationReader($parser);
$controllerAnnotation = $reader->getClassAnnotation($reflectionClass, ControllerAnnotation::class);

View File

@@ -10,6 +10,13 @@ class SystemAdmin extends TimeModel
protected $deleteTime = 'delete_time';
public array $notes = [
'login_type' => [
1 => '密码登录',
2 => '密码 + 谷歌验证码登录'
],
];
public function getAuthList()
{
$list = (new SystemAuth())

View File

@@ -44,14 +44,13 @@ class SystemLogService
$this->tablePrefix = Config::get('database.connections.mysql.prefix');
$this->tableSuffix = date('Ym', time());
$this->tableName = "{$this->tablePrefix}system_log_{$this->tableSuffix}";
return $this;
}
/**
* 获取实例对象
* @return SystemLogService|object
* @return SystemLogService
*/
public static function instance()
public static function instance(): SystemLogService
{
if (is_null(self::$instance)) {
self::$instance = new static();
@@ -85,15 +84,16 @@ class SystemLogService
*/
public function detectTable(): bool
{
$_key = "system_log_{$this->tableName}_table";
// 手动删除日志表时候 记得清除缓存
$isset = Cache::get("systemLog{$this->tableName}Table");
$isset = Cache::get($_key);
if ($isset) return true;
$check = Db::query("show tables like '{$this->tableName}'");
if (empty($check)) {
$sql = $this->getCreateSql();
Db::execute($sql);
}
Cache::set("system_log_{$this->tableName}_table", !empty($check));
Cache::set($_key, !empty($check));
return true;
}

View File

@@ -4,6 +4,7 @@ namespace app\admin\service;
use app\admin\model\SystemUploadfile;
use OSS\Core\OssException;
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use think\facade\Env;
use think\file\UploadedFile;
@@ -112,10 +113,21 @@ class UploadService
$accessKeySecret = $config['oss_access_key_secret'];
$endpoint = $config['oss_endpoint'];
$bucket = $config['oss_bucket'];
// 升级 aliyuncs/oss-sdk-php 到 v2.7.2 以上, 使用签名 v4 版本
putenv('OSS_ACCESS_KEY_ID=' . $accessKeyId);
putenv('OSS_ACCESS_KEY_SECRET=' . $accessKeySecret);
$region = str_replace(['http://oss-', 'https://oss-', 'oss-'], '', explode('.aliyuncs.com', $endpoint)[0] ?? '');
$provider = new EnvironmentVariableCredentialsProvider();
$args = [
"provider" => $provider,
"endpoint" => $endpoint,
"signatureVersion" => OssClient::OSS_SIGNATURE_VERSION_V4,
"region" => $region
];
if ($file->isValid()) {
$object = $this->setFilePath($file, Env::get('EASYADMIN.OSS_STATIC_PREFIX', 'easyadmin8') . '/');
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
$ossClient = new OssClient($args);
$_rs = $ossClient->putObject($bucket, $object, file_get_contents($file->getRealPath()));
$oss_request_url = $_rs['oss-request-url'] ?? '';
if (empty($oss_request_url)) return ['code' => 0, 'data' => '上传至OSS失败'];
@@ -164,7 +176,7 @@ class UploadService
if (empty($location)) return ['code' => 0, 'data' => '上传至COS失败'];
$location = 'https://' . $location;
$this->setSaveData($file);
} catch (Exception $e) {
}catch (Exception $e) {
return ['code' => 0, 'data' => $e->getMessage()];
}
$data = ['url' => $location];

View File

@@ -67,6 +67,12 @@ class Node
// 遍历读取所有方法的注释的参数信息
foreach ($methods as $method) {
// 忽略的节点
$properties = $reflectionClass->getDefaultProperties();
$ignoreNode = $properties['ignoreNode'] ?? [];
if (!empty($ignoreNode)) if (in_array($method->name, $ignoreNode)) continue;
// 读取NodeAnnotation的注解
$nodeAnnotation = $reader->getMethodAnnotation($method, NodeAnnotation::class);
if (!empty($nodeAnnotation)) {

View File

@@ -392,7 +392,7 @@ class BuildCurd
if (!empty($bindSelectField)) {
$relationArray = explode('\\', $modelFilename);
$this->tableColumns[$foreignKey]['bindSelectField'] = $bindSelectField;
$this->tableColumns[$foreignKey]['bindRelation'] = end($relationArray);
$this->tableColumns[$foreignKey]['bindRelation'] = lcfirst(end($relationArray)) . ucfirst($bindSelectField);
}
$this->relationArray[$relationTable] = $relation;
$this->selectFields[] = $foreignKey;
@@ -1027,7 +1027,8 @@ class BuildCurd
*/
protected function renderController(): static
{
$controllerFile = "{$this->rootDir}app{$this->DS}admin{$this->DS}controller{$this->DS}{$this->controllerFilename}.php";
$controllerFile = "{$this->rootDir}app{$this->DS}admin{$this->DS}controller{$this->DS}{$this->controllerFilename}.php";
$constructRelation = '';
if (empty($this->relationArray)) {
$controllerIndexMethod = '';
}else {
@@ -1035,6 +1036,9 @@ class BuildCurd
foreach ($this->relationArray as $key => $val) {
$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'] . '");';
}
}
$controllerIndexMethod = CommonTool::replaceTemplate(
$this->getTemplate("controller{$this->DS}indexMethod"),
@@ -1066,6 +1070,7 @@ class BuildCurd
'modelFilename' => "\app\admin\model\\{$modelFilenameExtend}",
'indexMethod' => $controllerIndexMethod,
'selectList' => $selectList,
'constructRelation' => $constructRelation,
]);
$this->fileList[$controllerFile] = $controllerValue;
return $this;
@@ -1082,16 +1087,16 @@ class BuildCurd
$relationList = '';
if (!empty($this->relationArray)) {
foreach ($this->relationArray as $key => $val) {
$relation = CommonTool::lineToHump($key);
$relationCode = CommonTool::replaceTemplate(
$this->getTemplate("model{$this->DS}relation"),
[
'relationMethod' => $relation,
'relationModel' => "\app\admin\model\\{$val['modelFilename']}",
'foreignKey' => $val['foreignKey'],
'primaryKey' => $val['primaryKey'],
]);
$relationList .= $relationCode;
$relation = CommonTool::lineToHump($key);
// $relationCode = CommonTool::replaceTemplate(
// $this->getTemplate("model{$this->DS}relation"),
// [
// 'relationMethod' => $relation,
// 'relationModel' => "\app\admin\model\\{$val['modelFilename']}",
// 'foreignKey' => $val['foreignKey'],
// 'primaryKey' => $val['primaryKey'],
// ]);
// $relationList .= $relationCode;
}
}
@@ -1126,6 +1131,7 @@ class BuildCurd
'selectArrays' => CommonTool::replaceArrayString(var_export($selectArrays, true)),
]);
$this->fileList[$modelFile] = $modelValue;
// 关联模型
@@ -1153,10 +1159,12 @@ class BuildCurd
[
'modelName' => $val['modelName'],
'modelNamespace' => "app\admin\model{$extendNamespace}",
'prefix_table' => $this->tablePrefix == config('database.connections.mysql.prefix') ? "" : $this->tablePrefix . $this->table,
'table' => $key,
'deleteTime' => $val['delete'] ? '"delete_time"' : 'false',
'relationList' => '',
'selectList' => '',
'selectArrays' => "[]",
]);
$this->fileList[$relationModelFile] = $relationModelValue;
}
@@ -1302,7 +1310,6 @@ class BuildCurd
$templateFile = "view{$this->DS}module{$this->DS}textarea";
$value = '{$row.' . $field . '|raw|default=\'\'}';
}
$editFormList .= CommonTool::replaceTemplate(
$this->getTemplate($templateFile),
[

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types = 1);
namespace EasyAdmin\curd\exceptions;
namespace app\admin\service\curd\exceptions;
class CurdException extends \Exception
{
}

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types = 1);
namespace EasyAdmin\curd\exceptions;
namespace app\admin\service\curd\exceptions;
class FileException extends \Exception
{
}

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types = 1);
namespace app\admin\service\curd\exceptions;
class TableException extends \Exception
{
}

View File

@@ -20,6 +20,7 @@ class {{controllerName}} extends AdminController
parent::__construct($app);
$this->model = new {{modelFilename}}();
$this->notes = $notes = $this->model->notes;
{{constructRelation}}
$this->assign(compact('notes'));
}

View File

@@ -1,23 +0,0 @@
/**
* @NodeAnnotation(title="列表")
*/
public function index()
{
if ($this->request->isAjax()) {
if (input('selectFields')) {
return $this->selectList();
}
list($page, $limit, $where) = $this->buildTableParams();
$count = $this->model{{relationIndexMethod}}->where($where)->count();
$list = $this->model{{relationIndexMethod}}->where($where)->page($page, $limit)->order($this->sort)->select()->toArray();
$data = [
'code' => 0,
'msg' => '',
'count' => $count,
'data' => $list,
];
return json($data);
}
return $this->fetch();
}

View File

@@ -5,6 +5,7 @@
data-auth-edit="{:auth('{{controllerUrl}}/edit')}"
data-auth-delete="{:auth('{{controllerUrl}}/delete')}"
lay-filter="currentTable">
<!-- searchTableShow="false" 隐藏搜索框 -->
</table>
</div>
</div>

View File

@@ -88,8 +88,10 @@ trait Curd
/**
* @NodeAnnotation(title="删除")
*/
public function delete($id): void
public function delete(Request $request): void
{
// 如果不是id作为主键 请在对应的控制器中覆盖重写
$id = $request->param('id', []);
$this->checkPostRequest();
$row = $this->model->whereIn('id', $id)->select();
$row->isEmpty() && $this->error('数据不存在');

View File

@@ -30,6 +30,15 @@
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">登录方式</label>
<div class="layui-input-block">
{foreach notes.login_type as $key=>$val}
<input type="radio" name="login_type" lay-skin="primary" title="{$val}" value="{$key}" lay-filter="loginType-filter" {if $key==$row.login_type}checked=""{/if}>
{/foreach}
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">备注信息</label>
<div class="layui-input-block">

View File

@@ -0,0 +1,45 @@
<div class="layuimini-container">
<form id="app-form" class="layui-form layuimini-form" autocomplete="off">
{if $old_secret}
<div class="layui-card">
<div class="layui-card-header">提示</div>
<div class="layui-card-body">
当前账号已经绑定过了 谷歌验证码 ,如果重新保存将替换
</div>
</div>
{/if}
<div class="layui-form-item">
<label class="layui-form-label required">验证秘钥</label>
<div class="layui-input-block">
<input type="text" name="ga_secret" class="layui-input" value="{$secret}" readonly disabled>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">二维码</label>
<div class="layui-input-block">
<img src="{$dataUri}" alt="二维码" style="width: 200px;height: 200px">
<div class="layui-text layui-font-cyan layui-font-12">
使用&nbsp;
<a href="https://2fas.com" target="_blank"><span class="layui-text layui-font-blue">2FAS</span></a>
&nbsp;或者&nbsp;
<a href="https://cn.bing.com/search?q=Google+Authenticator" target="_blank"><span class="layui-text layui-font-blue">Google Authenticator</span></a>
&nbsp;APP 扫描二维码 输入验证码 进行绑定
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">谷歌验证码</label>
<div class="layui-input-block">
<input type="text" name="ga_code" class="layui-input" maxlength="6" lay-verify="required" placeholder="扫描二维码,输入验证码" value="">
</div>
</div>
<div class=" hr-line">
</div>
<div class="layui-form-item text-center">
<button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit>确认</button>
<button type="reset" class="layui-btn layui-btn-primary layui-btn-sm">重置</button>
</div>
</form>
</div>

View File

@@ -69,16 +69,27 @@
<div class="layui-card-body">
<div class="welcome-module">
<div class="layui-row layui-col-space10">
<div class="swiper mySwiper">
<div class="swiper-wrapper">
{foreach $quicks as $value}
{foreach $quicks as $vo}
<div class="layui-col-xs3 layuimini-qiuck-module">
<a layuimini-content-href="{:url($vo['href'])}" data-title="{$vo['title']}">
<i class="{$vo['icon']|raw}"></i>
<cite>{$vo['title']}</cite>
</a>
<div class="swiper-slide">
{foreach $value as $vo}
<div class="layui-col-xs3 layuimini-qiuck-module">
<a layuimini-content-href="{:url($vo['href'])}" data-title="{$vo['title']}">
<i class="{$vo['icon']|raw}"></i>
<cite>{$vo['title']}</cite>
</a>
</div>
{/foreach}
</div>
{/foreach}
</div>
</div>
{/foreach}
<div class="swiper-pagination"></div>
</div>
</div>
</div>

View File

@@ -26,6 +26,7 @@
EDITOR_TYPE: "{$adminEditor|default='wangEditor'}",
};
</script>
<script src="/static/plugs/xmSelect/xm-select.js" charset="utf-8"></script>
<script src="/static/plugs/layui-v2.x/layui.js" charset="utf-8"></script>
<script src="/static/plugs/require-2.3.6/require.js" charset="utf-8"></script>
<script src="/static/config-admin.js?v={$version}" charset="utf-8"></script>

View File

@@ -22,6 +22,11 @@
<span class="bind-password icon icon-4"></span>
</div>
<div class="item layui-hide" id="gaCode">
<span class="icon icon-3"></span>
<input type="text" name="ga_code" placeholder="谷歌验证码" maxlength="6">
</div>
{if $captcha == 1}
<div id="validatePanel" class="item" style="width: 137px;">
<input type="text" name="captcha" placeholder="请输入验证码" maxlength="4">
@@ -36,7 +41,7 @@
<a href="javascript:" class="forget-password">忘记密码?</a>
</div>
<div class="layui-form-item" style="text-align:center; width:100%;height:100%;margin:0px;">
<button class="login-btn" lay-submit>立即登录</button>
<button type="button" class="login-btn" lay-submit>立即登录</button>
</div>
</form>
</div>

View File

@@ -1,4 +1,11 @@
<link rel="stylesheet" href="/static/plugs/zTree/fontawesome.css">
<link rel="stylesheet" href="/static/plugs/zTree/zTreeStyle.css?v={$version}">
<script src='/static/plugs/jquery-3.4.1/jquery-3.4.1.min.js'></script>
<script src='/static/plugs/zTree/jquery.ztree.core.js'></script>
<script src='/static/plugs/zTree/jquery.ztree.excheck.js'></script>
<div class="layuimini-container">
<form id="app-form" class="layui-form layuimini-form">
<div class="layui-form-item">
@@ -10,8 +17,8 @@
<div class="layui-form-item">
<label class="layui-form-label required">分配节点</label>
<div class="layui-input-block">
<div id="node_ids" class="demo-tree-more"></div>
<div class="layui-input-block" id="zTree">
<ul id="tree" class="ztree"></ul>
</div>
</div>
@@ -24,4 +31,4 @@
</div>
</form>
</div>
</div>

View File

@@ -19,6 +19,7 @@ if (!function_exists('__url')) {
*/
function __url(string $url = '', array $vars = [], bool $suffix = true, bool $domain = false): string
{
if (filter_var($url, FILTER_VALIDATE_URL)) return $url;
return url($url, $vars, $suffix, $domain)->build();
}
}

View File

@@ -168,6 +168,12 @@ class Curd extends Command
}
}
}
if (PHP_SAPI == 'cli') {
$output->info(">>>>>>>>>>>>>>>");
$output->info('执行成功');
}else {
$output->writeln('执行成功');
}
}catch (\Exception $e) {
if (PHP_SAPI == 'cli')
CliEcho::error($e->getMessage());

View File

@@ -1,47 +0,0 @@
<?php
namespace app\common\command;
use EasyAdmin\console\CliEcho;
use EasyAdmin\tool\CommonTool;
use EasyAdmin\upload\driver\alioss\Oss;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
class OssStatic extends Command
{
protected function configure()
{
$this->setName('OssStatic')
->setDescription('将静态资源上传到oss上');
}
protected function execute(Input $input, Output $output)
{
$output->writeln("========正在上传静态资源到OSS上========" . date('Y-m-d H:i:s'));
$dir = root_path() . 'public' . DIRECTORY_SEPARATOR . 'static';
$list = CommonTool::readDirAllFiles($dir);
$uploadConfig = sysConfig('upload');
$uploadPrefix = config('app.oss_static_prefix', 'oss_static_prefix');
foreach ($list as $key => $val) {
list($objectName, $filePath) = [$uploadPrefix . DIRECTORY_SEPARATOR . $key, $val];
try {
$upload = Oss::instance($uploadConfig)
->save($objectName, $filePath);
} catch (\Exception $e) {
CliEcho::error('文件上传失败:' . $filePath . '。错误信息:' . $e->getMessage());
continue;
}
if ($upload['save'] == true) {
CliEcho::success('文件上传成功:' . $filePath . '。上传地址:' . $upload['url']);
} else {
CliEcho::error('文件上传失败:' . $filePath . '。错误信息:' . $upload['msg']);
}
}
$output->writeln("========已完成静态资源上传到OSS上========" . date('Y-m-d H:i:s'));
}
}

View File

@@ -190,6 +190,9 @@ class AdminController extends BaseController
case '%*':
$where[] = [$key, 'LIKE', "%{$val}"];
break;
case 'in':
$where[] = [$key, 'IN', $val];
break;
case 'range':
[$beginTime, $endTime] = explode(' - ', $val);
$where[] = [$key, '>=', strtotime($beginTime)];
@@ -256,4 +259,4 @@ class AdminController extends BaseController
}
}
}
}

View File

@@ -3,7 +3,6 @@
namespace app\common\service;
use app\common\constants\AdminConstant;
use EasyAdmin\tool\CommonTool;
use think\facade\Db;
/**

View File

@@ -14,6 +14,6 @@ class Index extends BaseController
public function index(): Redirect
{
// 这是项目首页 系统默认跳转后台页面
return redirect('/' . Env::get('EASYADMIN.ADMIN', false));
return redirect('/' . Env::get('EASYADMIN.ADMIN', 'admin'));
}
}

View File

@@ -27,6 +27,9 @@ class Install extends BaseController
}elseif (!extension_loaded("PDO")) {
$errorInfo = '当前未开启PDO无法进行安装';
}
if (!is_file(root_path() . '.env')) {
$errorInfo = '.env 文件不存在,请先配置 .env 文件';
}
if (!$request->isAjax()) {
$currentHost = '://';
$result = compact('errorInfo', 'currentHost', 'isInstall');
@@ -88,12 +91,11 @@ class Install extends BaseController
$installPath = config_path() . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR;
$sqlPath = file_get_contents($installPath . 'sql' . DIRECTORY_SEPARATOR . 'install.sql');
$sqlArray = $this->parseSql($sqlPath, $config['prefix'], 'ea_');
$conn = mysqli_connect($config['host'], $config['username'], $config['password'], null, $config['port']);
$dsn = $this->pdoDsn($config, true);
try {
mysqli_set_charset($conn, $config['charset']);
mysqli_select_db($conn, $config['database']);
$pdo = new \PDO($dsn, $config['username'] ?? 'root', $config['password'] ?? '');
foreach ($sqlArray as $sql) {
mysqli_query($conn, $sql);
$pdo->query($sql);
}
$_password = password($password);
$tableName = 'system_admin';
@@ -105,9 +107,8 @@ class Install extends BaseController
'update_time' => time()
];
foreach ($update as $_k => $_up) {
mysqli_query($conn, "UPDATE {$config['prefix']}{$tableName} SET {$_k} = '{$_up}' WHERE id = 1");
$pdo->query("UPDATE {$config['prefix']}{$tableName} SET {$_k} = '{$_up}' WHERE id = 1");
}
mysqli_close($conn);
// 处理安装文件
!is_dir($installPath) && @mkdir($installPath);
!is_dir($installPath . 'lock' . DIRECTORY_SEPARATOR) && @mkdir($installPath . 'lock' . DIRECTORY_SEPARATOR);
@@ -158,11 +159,11 @@ class Install extends BaseController
protected function createDatabase($database, $config): bool
{
$dsn = $this->pdoDsn($config);
try {
$con = mysqli_connect($config['host'] ?? '127.0.0.1', $config['username'] ?? 'root', $config['password'] ?? '', null, $config['port'] ?? '');
mysqli_query($con, "CREATE DATABASE IF NOT EXISTS `{$database}` DEFAULT CHARACTER SET {$config['charset']} COLLATE=utf8mb4_general_ci");
mysqli_close($con);
}catch (\Throwable $e) {
$pdo = new \PDO($dsn, $config['username'] ?? 'root', $config['password'] ?? '');
$pdo->query("CREATE DATABASE IF NOT EXISTS `{$database}` DEFAULT CHARACTER SET {$config['charset']} COLLATE=utf8mb4_general_ci");
}catch (\PDOException $e) {
return false;
}
return true;
@@ -184,19 +185,32 @@ class Install extends BaseController
protected function checkConnect(array $config): ?bool
{
$dsn = $this->pdoDsn($config);
try {
$con = mysqli_connect($config['host'] ?? '127.0.0.1', $config['username'] ?? 'root', $config['password'] ?? '', null, $config['port'] ?? '');
$res = mysqli_query($con, 'select VERSION()');
$mysqlVersion = mysqli_fetch_row($res);
mysqli_close($con);
$_version = $mysqlVersion[0] ?? 0;
$pdo = new \PDO($dsn, $config['username'] ?? 'root', $config['password'] ?? '');
$res = $pdo->query('select VERSION()');
$_version = $res->fetch()[0] ?? 0;
if (version_compare($_version, '5.7.0', '<')) {
$this->error('mysql版本最低要求 5.7.x');
}
}catch (\mysqli_sql_exception $e) {
}catch (\PDOException $e) {
$this->error($e->getMessage());
}
return true;
}
/**
* @param array $config
* @param bool $needDatabase
* @return string
*/
protected function pdoDsn(array $config, bool $needDatabase = false): string
{
$host = $config['host'] ?? '127.0.0.1';
$database = $config['database'] ?? '';
$port = $config['port'] ?? '3306';
$charset = $config['charset'] ?? 'utf8mb4';
if ($needDatabase) return "mysql:host=$host;port=$port;dbname=$database;charset=$charset";
return "mysql:host=$host;port=$port;charset=$charset";
}
}

View File

@@ -23,8 +23,8 @@
"php": ">=8.0.0",
"topthink/framework": "^8.0",
"topthink/think-orm": "^3.0",
"topthink/think-multi-app": "^1.0",
"topthink/think-view": "^2.0",
"topthink/think-multi-app": "^1.1.0",
"topthink/think-view": "2.0.0",
"topthink/think-captcha": "^3.0",
"topthink/think-filesystem": "^2.0",
"aliyuncs/oss-sdk-php": "^2.6",
@@ -37,7 +37,8 @@
"qiniu/php-sdk": "v7.11.0",
"ext-mysqli": "*",
"ext-pdo": "*",
"wolf-leo/phplogviewer": "^0.10.0"
"wolf-leo/phplogviewer": "^0.11.3",
"wolfcode/authenticator": "^0.0.6"
},
"require-dev": {
"symfony/var-dumper": ">=4.2",

View File

@@ -97,6 +97,8 @@ CREATE TABLE `ea_system_admin`
`create_time` int(11) DEFAULT NULL COMMENT '创建时间',
`update_time` int(11) DEFAULT NULL COMMENT '更新时间',
`delete_time` int(11) DEFAULT NULL COMMENT '删除时间',
`login_type` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '登录方式',
`ga_secret` varchar(32) NOT NULL DEFAULT '' COMMENT '谷歌验证码秘钥',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`) USING BTREE,
KEY `phone` (`phone`)
@@ -106,7 +108,7 @@ CREATE TABLE `ea_system_admin`
-- Records of ea_system_admin
-- ----------------------------
INSERT INTO `ea_system_admin`
VALUES ('1', null, '/static/admin/images/head.jpg', 'admin', 'a33b679d5581a8692988ec9f92ad2d6a2259eaa7', 'admin', 'admin', '0', '0', '1', '1589454169', '1589476815', null);
VALUES ('1', null, '/static/admin/images/head.jpg', 'admin', 'a33b679d5581a8692988ec9f92ad2d6a2259eaa7', 'admin', 'admin', '0', '0', '1', '1589454169', '1589476815', null,1,'');
-- ----------------------------
-- Table structure for ea_system_auth

View File

@@ -42,4 +42,6 @@ return [
'default_jsonp_handler' => 'jsonpReturn',
// 默认JSONP处理方法
'var_jsonp_handler' => 'callback',
// 操作方法的参数绑定方式 route get param
'action_bind_param' => 'param',
];

View File

@@ -134,6 +134,12 @@ html.dark, body {
color: #a29c9c;
}
.layui-form-item xm-select {
min-height: 30px !important;
line-height: 30px !important;
margin: 0 !important;
}
/** 按钮背景色 */
.layuimini-container .layuimini-btn-primary {
color: #fff;
@@ -475,6 +481,18 @@ table样式
.form-search .layui-input-inline input, .form-search .layui-input-inline select {
width: 100% !important;
}
.tableSelect {
margin: 5px !important;
min-width: auto !important;
left: 1px !important;
right: 1px !important;
}
.tableSelect img {
object-fit: cover;
height: 100%;
}
}
/**

View File

@@ -32,7 +32,7 @@
.layuimini-qiuck-module a i {
display: inline-block;
width: 100%;
width: 80%;
height: 60px;
line-height: 60px;
text-align: center;

View File

@@ -1,4 +1,4 @@
define(["jquery", "easy-admin", "echarts", "echarts-theme", "miniAdmin", "miniTab"], function ($, ea, echarts, undefined, miniAdmin, miniTab) {
define(["jquery", "easy-admin", "echarts", "echarts-theme", "miniAdmin", "miniTab", "swiper"], function ($, ea, echarts, undefined, miniAdmin, miniTab) {
return {
index: function () {
@@ -50,6 +50,14 @@ define(["jquery", "easy-admin", "echarts", "echarts-theme", "miniAdmin", "miniTa
},
welcome: function () {
miniTab.listen();
new Swiper('.mySwiper', {
pagination: {
el: '.swiper-pagination',
clickable: true,
},
})
/**
* 查看公告信息
**/
@@ -156,10 +164,22 @@ define(["jquery", "easy-admin", "echarts", "echarts-theme", "miniAdmin", "miniTa
})
},
editAdmin: function () {
let form = layui.form
form.on('radio(loginType-filter)', function (data) {
let elem = data.elem
let value = elem.value
if (value === '2') {
let width = screen.width < 768 ? '85%' : '60%'
ea.open('绑定谷歌验证码', ea.url('index/set2fa'), width, '75%')
}
});
ea.listen();
},
editPassword: function () {
ea.listen();
}
},
set2fa: function () {
ea.listen();
},
};
});

View File

@@ -1,4 +1,4 @@
define(["easy-admin"], function (ea) {
define(["jquery", "easy-admin"], function ($, ea) {
return {
index: function () {
@@ -39,6 +39,12 @@ define(["easy-admin"], function (ea) {
window.location = ea.url('index');
})
}, function (res) {
let data = res.data
if (data.is_ga_code) {
let elem = $('#gaCode')
elem.removeClass('layui-hide');
elem.find('input').focus()
}
ea.msg.error(res.msg, function () {
$('#refreshCaptcha').trigger("click");
});

View File

@@ -42,6 +42,14 @@ define(["jquery", "easy-admin"], function ($, ea) {
{field: 'virtual_sales', width: 100, title: '虚拟销量'},
{field: 'sales', width: 80, title: '销量'},
{field: 'status', title: '状态', width: 85, selectList: {0: '禁用', 1: '启用'}, templet: ea.table.switch},
// 演示多选,实际数据库并无 status2 字段,搜索后会报错
{
field: 'status2', title: '演示多选', width: 105, search: 'xmSelect', selectList: {1: '模拟选项1', 2: '模拟选项2', 3: '模拟选项3', 4: '模拟选项4', 5: '模拟选项5'},
searchOp: 'in', templet: function (res) {
// 根据自己实际项目进行输出
return res?.status2 || '模拟数据'
}
},
{field: 'create_time', minWidth: 80, title: '创建时间', search: 'range'},
{
width: 250,
@@ -49,6 +57,10 @@ define(["jquery", "easy-admin"], function ($, ea) {
templet: ea.table.tool,
operat: [
[{
templet: function (d) {
return `<button type="button" class="layui-btn layui-btn-xs">自定义 ${d.id}</button>`
}
}, {
text: '编辑',
url: init.edit_url,
method: 'open',

View File

@@ -1,89 +1,107 @@
define(["jquery", "easy-admin"], function ($, ea) {
var init = {
table_elem: '#currentTable',
table_render_id: 'currentTableRenderId',
index_url: 'system.auth/index',
add_url: 'system.auth/add',
edit_url: 'system.auth/edit',
delete_url: 'system.auth/delete',
export_url: 'system.auth/export',
modify_url: 'system.auth/modify',
authorize_url: 'system.auth/authorize',
};
var init = {
table_elem: '#currentTable',
table_render_id: 'currentTableRenderId',
index_url: 'system.auth/index',
add_url: 'system.auth/add',
edit_url: 'system.auth/edit',
delete_url: 'system.auth/delete',
export_url: 'system.auth/export',
modify_url: 'system.auth/modify',
authorize_url: 'system.auth/authorize',
};
return {
return {
index: function () {
ea.table.render({
init: init,
cols: [[
{type: "checkbox"},
{field: 'id', width: 80, title: 'ID', searchOp: '='},
{field: 'sort', width: 80, title: '排序', edit: 'text'},
{field: 'title', minWidth: 80, title: '权限名称'},
{field: 'remark', minWidth: 80, title: '备注信息'},
{field: 'status', title: '状态', width: 85, search: 'select', selectList: {0: '禁用', 1: '启用'}, templet: ea.table.switch},
{field: 'create_time', minWidth: 80, title: '创建时间', search: 'range'},
{
width: 250,
title: '操作',
templet: ea.table.tool,
operat: [
'edit',
[{
text: '授权',
url: init.authorize_url,
method: 'open',
auth: 'authorize',
class: 'layui-btn layui-btn-normal layui-btn-xs',
}],
'delete'
]
}
]],
});
ea.listen();
},
add: function () {
ea.listen();
},
edit: function () {
ea.listen();
},
authorize: function () {
var tree = layui.tree;
ea.request.get(
{
url: window.location.href,
}, function (res) {
res.data = res.data || [];
tree.render({
elem: '#node_ids',
data: res.data,
showCheckbox: true,
id: 'nodeDataId',
});
}
);
ea.listen(function (data) {
var checkedData = tree.getChecked('nodeDataId');
var ids = [];
$.each(checkedData, function (i, v) {
ids.push(v.id);
if (v.children !== undefined && v.children.length > 0) {
$.each(v.children, function (ii, vv) {
ids.push(vv.id);
});
}
index: function () {
ea.table.render({
init: init,
cols: [[
{type: "checkbox"},
{field: 'id', width: 80, title: 'ID', searchOp: '='},
{field: 'sort', width: 80, title: '排序', edit: 'text'},
{field: 'title', minWidth: 80, title: '权限名称'},
{field: 'remark', minWidth: 80, title: '备注信息'},
{field: 'status', title: '状态', width: 85, search: 'select', selectList: {0: '禁用', 1: '启用'}, templet: ea.table.switch},
{field: 'create_time', minWidth: 80, title: '创建时间', search: 'range'},
{
width: 250,
title: '操作',
templet: ea.table.tool,
operat: [
'edit',
[{
text: '授权',
url: init.authorize_url,
method: 'open',
auth: 'authorize',
class: 'layui-btn layui-btn-normal layui-btn-xs',
}],
'delete'
]
}
]],
});
data.node = JSON.stringify(ids);
return data;
});
ea.listen();
},
add: function () {
ea.listen();
},
edit: function () {
ea.listen();
},
authorize: function () {
let setting = {
check: {
enable: true,
chkStyle: "checkbox",
},
view: {
showIcon: true,
showLine: true,
selectedMulti: false,
dblClickExpand: false
}, callback: {
onClick: function (e, treeId, treeNode, clickFlag) {
treeObj.checkNode(treeNode, !treeNode.checked, true);
}
}
};
let treeObj
let treeData = []
ea.request.get({url: window.location.href}, function (res) {
res.data = res.data || [];
$.each(res.data, function (index, value) {
treeData[index] = []
treeData[index].id = value.id
treeData[index].name = value.title
treeData[index].pId = 0
treeData[index].open = true
let children = value.children
treeData[index]['children'] = []
$.each(children, function (idx, val) {
treeData[index]['children'].push({id: val.id, name: val.title, checked: val.checked, pId: value.id})
})
})
$.fn.zTree.init($("#tree"), setting, treeData);
treeObj = $.fn.zTree.getZTreeObj("tree");
}
);
ea.listen(function (data) {
let checkedData = treeObj.getCheckedNodes();
let ids = []
for (var i = 0; i < checkedData.length; i++) {
ids.push(checkedData[i].id)
}
data.node = JSON.stringify(ids);
return data;
});
}
}
};
});
}
)

View File

@@ -21,6 +21,7 @@ require.config({
"iconPickerFa": ["plugs/lay-module/iconPicker/iconPickerFa"],
"autocomplete": ["plugs/lay-module/autocomplete/autocomplete"],
"vue": ["plugs/vue-2.6.10/vue.min"],
"swiper": ["plugs/swiper/swiper-bundle.min"],
}
});

View File

@@ -20,7 +20,9 @@ define(["jquery", "tableSelect"], function ($, tableSelect) {
upload_url: 'ajax/upload',
upload_exts: 'doc|gif|ico|icon|jpg|mp3|mp4|p12|pem|png|rar',
csrf_token: window.CONFIG.CSRF_TOKEN,
wait_submit: false
wait_submit: false,
xmSelectList: {},
xmSelectModel: {},
};
@@ -370,6 +372,15 @@ define(["jquery", "tableSelect"], function ($, tableSelect) {
'</div>\n' +
'</div>';
break;
case 'xmSelect':
formHtml += '\t<div class="layui-form-item layui-inline">\n' +
'<label class="layui-form-label">' + d.title + '</label>\n' +
'<div class="layui-input-inline">\n' +
'<div id="c-' + d.fieldAlias + '" class="tableSearch-xmSelect xmSelect-' + d.fieldAlias + '" name="' + d.fieldAlias + '" data-search-op="' + d.searchOp + '"></div>\n' +
'</div>\n' +
'</div>';
init.xmSelectList[d.fieldAlias] = d.selectList
break;
case 'range':
d.searchOp = 'range';
formHtml += '\t<div class="layui-form-item layui-inline">\n' +
@@ -388,6 +399,15 @@ define(["jquery", "tableSelect"], function ($, tableSelect) {
'</div>\n' +
'</div>';
break;
case 'date':
d.searchOp = '=';
formHtml += `<div class="layui-form-item layui-inline">
<label class="layui-form-label">${d.title}</label>
<div class="layui-input-inline">
<input data-date data-date-type="date" id="c-${d.fieldAlias}" name="${d.fieldAlias}" data-search-op="${d.searchOp}" value="${d.searchValue}" placeholder="${d.searchTip}" class="layui-input">
</div>
</div>`
break;
}
newCols.push(d);
}
@@ -652,6 +672,12 @@ define(["jquery", "tableSelect"], function ($, tableSelect) {
$.each(item, function (i, operat) {
if (typeof operat !== 'object') return
if ('function' === typeof operat.templet) {
html += operat.templet(data);
return true;
}
operat.class = operat.class || '';
operat.icon = operat.icon || '';
operat.auth = operat.auth || '';
@@ -823,6 +849,17 @@ define(["jquery", "tableSelect"], function ($, tableSelect) {
return value;
},
listenTableSearch: function (tableId) {
if (Object.keys(init.xmSelectList).length > 0) {
$.each(init.xmSelectList, function (index, value) {
const keysArray = Object.keys(value).map((key) => {
return {name: value[key], value: key}
})
init.xmSelectModel[index] = xmSelect.render({
el: '.xmSelect-' + index, language: 'zn', data: keysArray, name: index,
filterable: true, paging: true, pageSize: 10, theme: {color: '#16b777'}, toolbar: {show: true},
})
})
}
form.on('submit(' + tableId + '_filter)', function (data) {
var dataField = data.field;
var formatFilter = {},
@@ -978,9 +1015,17 @@ define(["jquery", "tableSelect"], function ($, tableSelect) {
},
success: function (layero, index) {
if (window.CONFIG.IFRAME_OPEN_TOP == '1') {
let iframeUrl = ``
try {
let iframeId = $('iframe:eq(0)').attr('id')
let iframe = document.getElementById(iframeId)
iframeUrl = iframe.contentWindow.location.href
} catch (e) {
iframeUrl = location.href
}
let _winTopBtn = `
<span class="_winTopBtn layui-btn layui-btn-primary layui-btn-xs"
style="position: absolute;top: 14px;right: 120px;color: #fff;border-color: #fff;" onclick="window.open(location.href)">
style="position: absolute;top: 14px;right: 120px;color: #fff;border-color: #fff;" onclick="window.open('${iframeUrl}')">
新标签页打开
</span>`
$('.layui-layer-iframe').append(_winTopBtn)
@@ -1155,6 +1200,11 @@ define(["jquery", "tableSelect"], function ($, tableSelect) {
if (tableId === undefined || tableId === '' || tableId == null) {
tableId = init.table_render_id;
}
if (Object.keys(init.xmSelectModel).length > 0) {
$.each(init.xmSelectModel, function (index, value) {
init.xmSelectModel[index].setValue([])
})
}
table.reload(tableId, {
page: {
curr: 1

View File

@@ -8184,7 +8184,7 @@
if (!1 === e.autoDownloadFontAwesome && (t = !1), !0 !== e.autoDownloadFontAwesome) for (var n = document.styleSheets, i = 0; i < n.length; i++) n[i].href && n[i].href.indexOf("//maxcdn.bootstrapcdn.com/font-awesome/") > -1 && (t = !1);
if (t) {
var r = document.createElement("link");
r.rel = "stylesheet", r.href = "https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css", document.getElementsByTagName("head")[0].appendChild(r)
r.rel = "stylesheet", r.href = "/static/plugs/font-awesome-4.7.0/css/font-awesome.min.css", document.getElementsByTagName("head")[0].appendChild(r)
}
if (e.element) this.element = e.element; else if (null === e.element) return void console.log("EasyMDE: Error. No element was found.");
if (void 0 === e.toolbar) for (var o in e.toolbar = [], te) Object.prototype.hasOwnProperty.call(te, o) && (-1 != o.indexOf("separator-") && e.toolbar.push("|"), (!0 === te[o].default || e.showIcons && e.showIcons.constructor === Array && -1 != e.showIcons.indexOf(o)) && e.toolbar.push(o));

View File

@@ -700,14 +700,6 @@
margin-top: -6px !important;
}
.layuimini-menu-left .layui-nav .layui-nav-mored,.layuimini-menu-left .layui-nav-itemed>a .layui-nav-more{
margin-top: -9px!important;
}
.layuimini-menu-left-zoom.layui-nav .layui-nav-mored,.layuimini-menu-left-zoom.layui-nav-itemed>a .layui-nav-more{
margin-top: -9px!important;
}
.layuimini-menu-left .layui-nav-more:before,.layuimini-menu-left-zoom .layui-nav-more:before {
content: "\e61a";
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -10,14 +10,8 @@
**************************提示********************************/
(function () {
/**
* 编辑器资源文件根路径。它所表示的含义是以编辑器实例化页面为当前路径指向编辑器资源文件即dialog等文件夹的路径。
* 鉴于很多同学在使用编辑器的时候出现的种种路径问题,此处强烈建议大家使用"相对于网站根目录的相对路径"进行配置。
* "相对于网站根目录的相对路径"也就是以斜杠开头的形如"/myProject/ueditor/"这样的路径。
* 如果站点中有多个不在同一层级的页面需要实例化编辑器且引用了同一UEditor的时候此处的URL可能不适用于每个页面的编辑器。
* 因此UEditor提供了针对不同页面的编辑器可单独配置的根路径具体来说在需要实例化编辑器的页面最顶部写上如下代码即可。当然需要令此处的URL等于对应的配置。
* window.UEDITOR_HOME_URL = "/xxxx/xxxx/";
*/
// 资源文件根路径,如果你的页面不是放在根目录下,请注意修改这个路径
// 通常情况下这个可以配置成静态资源CDN的地址
window.UEDITOR_HOME_URL = "/static/plugs/ueditor/";
var URL, CORS_URL;
if (window.UEDITOR_HOME_URL) {
@@ -29,6 +23,8 @@
} else {
URL = getUEBasePath();
}
// 需要能跨域的静态资源请求,主要用户弹窗页面等静态资源
// 通常情况下这个可以配置成静态资源CDN的地址
if (window.UEDITOR_CORS_URL) {
CORS_URL = window.UEDITOR_CORS_URL;
} else if (window.__msRoot) {
@@ -38,6 +34,7 @@
} else {
CORS_URL = getUEBasePath();
}
/**
* 配置项主体。注意此处所有涉及到路径的配置别遗漏URL变量。
*/
@@ -53,10 +50,19 @@
// 服务器统一请求接口路径
serverUrl: "/" + (window.CONFIG.ADMIN || 'admin') + "/ajax/uploadUEditor",
// 从服务器获取配置
loadConfigFromServer: true,
// 服务器统一请求头信息,会在所有请求中带上该信息
serverHeaders: {
// 'Authorization': 'Bearer xxx'
},
// 服务器返回参数统一转换方法,可以在这里统一处理返回参数
serverResponsePrepare: function (res) {
// console.log('serverResponsePrepare', res);
return res;
},
//工具栏上的所有的功能按钮和下拉框可以在new编辑器的实例时选择自己需要的重新定义
toolbars: [
@@ -114,8 +120,8 @@
"|",
"imagenone", // 图片默认
"imageleft", // 图片左浮动
"imageright", // 图片右浮动
"imagecenter", // 图片居中
"imageright", // 图片右浮动
"|",
"simpleupload", // 单图上传
"insertimage", // 多图上传
@@ -154,12 +160,15 @@
"print", // 打印
"preview", // 预览
"searchreplace", // 查询替换
"|",
"contentimport",
"help", // 帮助
]
]
// 自定义工具栏按钮点击,返回 true 表示已经处理点击,会阻止默认事件
, toolbarCallback: function (cmd, editor) {
// console.log('toolbarCallback',cmd, editor);
// switch(cmd){
// case 'insertimage':
// editor.execCommand('insertHtml', '<p><img src="xxxxx" /></p>');
@@ -176,6 +185,33 @@
// }
}
// 自定义上传功能
, uploadServiceEnable: false
// 自定义上传函数,需要在这个函数中实现自定义上传逻辑
// type 上传类型image 图片video 视频audio 音频attachment 附件
// file 文件对象
// callback 回调函数,需要在上传完成后调用 callback.success、callback.error、callback.progress
// option 上传配置,其他一些未来扩展配置
, uploadServiceUpload: function(type, file, callback, option ) {
console.log('uploadServiceUpload', type, file, callback, option);
// var i = 0;
// var call = function(){
// i++;
// if(i > 3){
// callback.success({
// "state": "SUCCESS",
// "url": "https://ms-assets.modstart.com/demo/modstart.jpg",
// })
// return;
// }
// setTimeout(function(){
// callback.progress(0.3 * i);
// call();
// },500);
// }
// call();
}
// 插入图片自定义配置
, imageConfig: {
// 禁止本地上传
@@ -316,7 +352,8 @@
// 是否开启初始化时即全屏,默认关闭
, fullscreen: false
//,imagePopup:true //图片操作的浮层开关,默认打开
// 图片操作的浮层开关,默认打开
//,imagePopup:true
// 自动同步编辑器要提交的数据
//,autoSyncData:true
@@ -359,17 +396,8 @@
// 提交到后台的数据是否包含整个html字符串
, allHtmlEnabled: false
//insertorderedlist
//有序列表的下拉配置,值留空时支持多语言自动识别,若配置值,则以此值为准
//,'insertorderedlist':{
// //自定的样式
// 'num':'1,2,3...',
// 'num1':'1),2),3)...',
// 'num2':'(1),(2),(3)...',
// 'cn':'一,二,三....',
// 'cn1':'一),二),三)....',
// 'cn2':'(一),(二),(三)....',
// //系统自带
// 'decimal' : '' , //'1,2,3...'
// 'lower-alpha' : '' , // 'a,b,c...'
// 'lower-roman' : '' , //'i,ii,iii...'
@@ -380,8 +408,6 @@
//insertunorderedlist
//无序列表的下拉配置,值留空时支持多语言自动识别,若配置值,则以此值为准
//,insertunorderedlist : { //自定的样式
// 'dash' :'— 破折号', //-破折号
// 'dot':' 。 小圆圈', //系统自带
// 'circle' : '', // '○ 小圆圈'
// 'disc' : '', // '● 小圆点'
// 'square' : '' //'■ 小方块'
@@ -465,6 +491,12 @@
"forecolor", // 字体颜色
// "shadowcolor", // 字体阴影
"backcolor", // 背景色
"imagenone",
"imageleft",
"imagecenter",
"imageright",
"insertimage",
"formula",
// "justifyleft", // 居左对齐
// "justifycenter", // 居中对齐
// "justifyright", // 居右对齐
@@ -513,19 +545,19 @@
// 是否自动长高,默认true
, autoHeightEnabled: true
//scaleEnabled
//是否可以拉伸长高,默认true(当开启时,自动长高失效)
// 是否可以拉伸长高默认true(当开启时,自动长高失效)
//,scaleEnabled:false
//,minFrameWidth:800 //编辑器拖动时最小宽度,默认800
//,minFrameHeight:220 //编辑器拖动时最小高度,默认220
//autoFloatEnabled
//是否保持toolbar的位置不动,默认true
//,autoFloatEnabled:true
//浮动时工具栏距离浏览器顶部的高度,用于某些具有固定头部的页面
//,topOffset:30
//编辑器底部距离工具栏高度(如果参数大于等于编辑器高度,则设置无效)
//,toolbarTopOffset:400
// 编辑器最小高度,默认220
, minFrameHeight: 220
// 是否保持toolbar的位置不动,默认true
, autoFloatEnabled: true
// 浮动时工具栏距离浏览器顶部的高度,用于某些具有固定头部的页面
, topOffset: 0
// 编辑器底部距离工具栏高度(如果参数大于等于编辑器高度,则设置无效)
, toolbarTopOffset: 0
//设置远程图片是否抓取到本地保存
, catchRemoteImageEnable: true //设置是否抓取远程图片
@@ -595,7 +627,15 @@
// 允许进入编辑器的 div 标签自动变成 p 标签
, allowDivTransToP: true
// 默认产出的数据中的color自动从rgb格式变成16进制格式
, rgb2Hex: true
, rgb2Hex: true,
tipError: function (msg, param) {
if (window && window.MS && window.MS.dialog) {
window.MS.dialog.tipError(msg);
} else {
alert(msg);
}
}
};
function getUEBasePath(docUrl, confUrl) {
@@ -651,4 +691,4 @@
window.UE = {
getUEBasePath: getUEBasePath
};
})();
})();

File diff suppressed because one or more lines are too long

6110
public/static/plugs/zTree/fontawesome.css vendored Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,652 @@
/*
* JQuery zTree excheck
* v3.5.48
* http://treejs.cn/
*
* Copyright (c) 2010 Hunter.z
*
* Licensed same as jquery - MIT License
* http://www.opensource.org/licenses/mit-license.php
*
* Date: 2020-11-21
*/
(function ($) {
//default consts of excheck
var _consts = {
event: {
CHECK: "ztree_check"
},
id: {
CHECK: "_check"
},
checkbox: {
STYLE: "checkbox",
DEFAULT: "chk",
DISABLED: "disable",
FALSE: "false",
TRUE: "true",
FULL: "full",
PART: "part",
FOCUS: "focus"
},
radio: {
STYLE: "radio",
TYPE_ALL: "all",
TYPE_LEVEL: "level"
}
},
//default setting of excheck
_setting = {
check: {
enable: false,
autoCheckTrigger: false,
chkStyle: _consts.checkbox.STYLE,
nocheckInherit: false,
chkDisabledInherit: false,
radioType: _consts.radio.TYPE_LEVEL,
chkboxType: {
"Y": "ps",
"N": "ps"
}
},
data: {
key: {
checked: "checked"
}
},
callback: {
beforeCheck: null,
onCheck: null
}
},
//default root of excheck
_initRoot = function (setting) {
var r = data.getRoot(setting);
r.radioCheckedList = [];
},
//default cache of excheck
_initCache = function (treeId) {
},
//default bind event of excheck
_bindEvent = function (setting) {
var o = setting.treeObj,
c = consts.event;
o.bind(c.CHECK, function (event, srcEvent, treeId, node) {
event.srcEvent = srcEvent;
tools.apply(setting.callback.onCheck, [event, treeId, node]);
});
},
_unbindEvent = function (setting) {
var o = setting.treeObj,
c = consts.event;
o.unbind(c.CHECK);
},
//default event proxy of excheck
_eventProxy = function (e) {
var target = e.target,
setting = data.getSetting(e.data.treeId),
tId = "", node = null,
nodeEventType = "", treeEventType = "",
nodeEventCallback = null, treeEventCallback = null;
if (tools.eqs(e.type, "mouseover")) {
if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) {
tId = tools.getNodeMainDom(target).id;
nodeEventType = "mouseoverCheck";
}
} else if (tools.eqs(e.type, "mouseout")) {
if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) {
tId = tools.getNodeMainDom(target).id;
nodeEventType = "mouseoutCheck";
}
} else if (tools.eqs(e.type, "click")) {
if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) {
tId = tools.getNodeMainDom(target).id;
nodeEventType = "checkNode";
}
}
if (tId.length > 0) {
node = data.getNodeCache(setting, tId);
switch (nodeEventType) {
case "checkNode" :
nodeEventCallback = _handler.onCheckNode;
break;
case "mouseoverCheck" :
nodeEventCallback = _handler.onMouseoverCheck;
break;
case "mouseoutCheck" :
nodeEventCallback = _handler.onMouseoutCheck;
break;
}
}
var proxyResult = {
stop: nodeEventType === "checkNode",
node: node,
nodeEventType: nodeEventType,
nodeEventCallback: nodeEventCallback,
treeEventType: treeEventType,
treeEventCallback: treeEventCallback
};
return proxyResult
},
//default init node of excheck
_initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) {
if (!n) return;
var checked = data.nodeChecked(setting, n);
n.checkedOld = checked;
if (typeof n.nocheck == "string") n.nocheck = tools.eqs(n.nocheck, "true");
n.nocheck = !!n.nocheck || (setting.check.nocheckInherit && parentNode && !!parentNode.nocheck);
if (typeof n.chkDisabled == "string") n.chkDisabled = tools.eqs(n.chkDisabled, "true");
n.chkDisabled = !!n.chkDisabled || (setting.check.chkDisabledInherit && parentNode && !!parentNode.chkDisabled);
if (typeof n.halfCheck == "string") n.halfCheck = tools.eqs(n.halfCheck, "true");
n.halfCheck = !!n.halfCheck;
n.check_Child_State = -1;
n.check_Focus = false;
n.getCheckStatus = function () {
return data.getCheckStatus(setting, n);
};
if (setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL && checked) {
var r = data.getRoot(setting);
r.radioCheckedList.push(n);
}
},
//add dom for check
_beforeA = function (setting, node, html) {
if (setting.check.enable) {
data.makeChkFlag(setting, node);
html.push("<span ID='", node.tId, consts.id.CHECK, "' class='", view.makeChkClass(setting, node), "' treeNode", consts.id.CHECK, (node.nocheck === true ? " style='display:none;'" : ""), "></span>");
}
},
//update zTreeObj, add method of check
_zTreeTools = function (setting, zTreeTools) {
zTreeTools.checkNode = function (node, checked, checkTypeFlag, callbackFlag) {
var nodeChecked = data.nodeChecked(setting, node);
if (node.chkDisabled === true) return;
if (checked !== true && checked !== false) {
checked = !nodeChecked;
}
callbackFlag = !!callbackFlag;
if (nodeChecked === checked && !checkTypeFlag) {
return;
} else if (callbackFlag && tools.apply(this.setting.callback.beforeCheck, [this.setting.treeId, node], true) == false) {
return;
}
if (tools.uCanDo(this.setting) && this.setting.check.enable && node.nocheck !== true) {
data.nodeChecked(setting, node, checked);
var checkObj = $$(node, consts.id.CHECK, this.setting);
if (checkTypeFlag || this.setting.check.chkStyle === consts.radio.STYLE) view.checkNodeRelation(this.setting, node);
view.setChkClass(this.setting, checkObj, node);
view.repairParentChkClassWithSelf(this.setting, node);
if (callbackFlag) {
this.setting.treeObj.trigger(consts.event.CHECK, [null, this.setting.treeId, node]);
}
}
}
zTreeTools.checkAllNodes = function (checked) {
view.repairAllChk(this.setting, !!checked);
}
zTreeTools.getCheckedNodes = function (checked) {
checked = (checked !== false);
var children = data.nodeChildren(setting, data.getRoot(this.setting));
return data.getTreeCheckedNodes(this.setting, children, checked);
}
zTreeTools.getChangeCheckedNodes = function () {
var children = data.nodeChildren(setting, data.getRoot(this.setting));
return data.getTreeChangeCheckedNodes(this.setting, children);
}
zTreeTools.setChkDisabled = function (node, disabled, inheritParent, inheritChildren) {
disabled = !!disabled;
inheritParent = !!inheritParent;
inheritChildren = !!inheritChildren;
view.repairSonChkDisabled(this.setting, node, disabled, inheritChildren);
view.repairParentChkDisabled(this.setting, node.getParentNode(), disabled, inheritParent);
}
var _updateNode = zTreeTools.updateNode;
zTreeTools.updateNode = function (node, checkTypeFlag) {
if (_updateNode) _updateNode.apply(zTreeTools, arguments);
if (!node || !this.setting.check.enable) return;
var nObj = $$(node, this.setting);
if (nObj.get(0) && tools.uCanDo(this.setting)) {
var checkObj = $$(node, consts.id.CHECK, this.setting);
if (checkTypeFlag == true || this.setting.check.chkStyle === consts.radio.STYLE) view.checkNodeRelation(this.setting, node);
view.setChkClass(this.setting, checkObj, node);
view.repairParentChkClassWithSelf(this.setting, node);
}
}
},
//method of operate data
_data = {
getRadioCheckedList: function (setting) {
var checkedList = data.getRoot(setting).radioCheckedList;
for (var i = 0, j = checkedList.length; i < j; i++) {
if (!data.getNodeCache(setting, checkedList[i].tId)) {
checkedList.splice(i, 1);
i--;
j--;
}
}
return checkedList;
},
getCheckStatus: function (setting, node) {
if (!setting.check.enable || node.nocheck || node.chkDisabled) return null;
var checked = data.nodeChecked(setting, node),
r = {
checked: checked,
half: node.halfCheck ? node.halfCheck : (setting.check.chkStyle == consts.radio.STYLE ? (node.check_Child_State === 2) : (checked ? (node.check_Child_State > -1 && node.check_Child_State < 2) : (node.check_Child_State > 0)))
};
return r;
},
getTreeCheckedNodes: function (setting, nodes, checked, results) {
if (!nodes) return [];
var onlyOne = (checked && setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL);
results = !results ? [] : results;
for (var i = 0, l = nodes.length; i < l; i++) {
var node = nodes[i];
var children = data.nodeChildren(setting, node);
var nodeChecked = data.nodeChecked(setting, node);
if (node.nocheck !== true && node.chkDisabled !== true && nodeChecked == checked) {
results.push(node);
if (onlyOne) {
break;
}
}
data.getTreeCheckedNodes(setting, children, checked, results);
if (onlyOne && results.length > 0) {
break;
}
}
return results;
},
getTreeChangeCheckedNodes: function (setting, nodes, results) {
if (!nodes) return [];
results = !results ? [] : results;
for (var i = 0, l = nodes.length; i < l; i++) {
var node = nodes[i];
var children = data.nodeChildren(setting, node);
var nodeChecked = data.nodeChecked(setting, node);
if (node.nocheck !== true && node.chkDisabled !== true && nodeChecked != node.checkedOld) {
results.push(node);
}
data.getTreeChangeCheckedNodes(setting, children, results);
}
return results;
},
makeChkFlag: function (setting, node) {
if (!node) return;
var chkFlag = -1;
var children = data.nodeChildren(setting, node);
if (children) {
for (var i = 0, l = children.length; i < l; i++) {
var cNode = children[i];
var nodeChecked = data.nodeChecked(setting, cNode);
var tmp = -1;
if (setting.check.chkStyle == consts.radio.STYLE) {
if (cNode.nocheck === true || cNode.chkDisabled === true) {
tmp = cNode.check_Child_State;
} else if (cNode.halfCheck === true) {
tmp = 2;
} else if (nodeChecked) {
tmp = 2;
} else {
tmp = cNode.check_Child_State > 0 ? 2 : 0;
}
if (tmp == 2) {
chkFlag = 2;
break;
} else if (tmp == 0) {
chkFlag = 0;
}
} else if (setting.check.chkStyle == consts.checkbox.STYLE) {
if (cNode.nocheck === true || cNode.chkDisabled === true) {
tmp = cNode.check_Child_State;
} else if (cNode.halfCheck === true) {
tmp = 1;
} else if (nodeChecked) {
tmp = (cNode.check_Child_State === -1 || cNode.check_Child_State === 2) ? 2 : 1;
} else {
tmp = (cNode.check_Child_State > 0) ? 1 : 0;
}
if (tmp === 1) {
chkFlag = 1;
break;
} else if (tmp === 2 && chkFlag > -1 && i > 0 && tmp !== chkFlag) {
chkFlag = 1;
break;
} else if (chkFlag === 2 && tmp > -1 && tmp < 2) {
chkFlag = 1;
break;
} else if (tmp > -1) {
chkFlag = tmp;
}
}
}
}
node.check_Child_State = chkFlag;
}
},
//method of event proxy
_event = {},
//method of event handler
_handler = {
onCheckNode: function (event, node) {
if (node.chkDisabled === true) return false;
var setting = data.getSetting(event.data.treeId);
if (tools.apply(setting.callback.beforeCheck, [setting.treeId, node], true) == false) return true;
var nodeChecked = data.nodeChecked(setting, node);
data.nodeChecked(setting, node, !nodeChecked);
view.checkNodeRelation(setting, node);
var checkObj = $$(node, consts.id.CHECK, setting);
view.setChkClass(setting, checkObj, node);
view.repairParentChkClassWithSelf(setting, node);
setting.treeObj.trigger(consts.event.CHECK, [event, setting.treeId, node]);
return true;
},
onMouseoverCheck: function (event, node) {
if (node.chkDisabled === true) return false;
var setting = data.getSetting(event.data.treeId),
checkObj = $$(node, consts.id.CHECK, setting);
node.check_Focus = true;
view.setChkClass(setting, checkObj, node);
return true;
},
onMouseoutCheck: function (event, node) {
if (node.chkDisabled === true) return false;
var setting = data.getSetting(event.data.treeId),
checkObj = $$(node, consts.id.CHECK, setting);
node.check_Focus = false;
view.setChkClass(setting, checkObj, node);
return true;
}
},
//method of tools for zTree
_tools = {},
//method of operate ztree dom
_view = {
checkNodeRelation: function (setting, node) {
var pNode, i, l,
r = consts.radio;
var nodeChecked = data.nodeChecked(setting, node);
if (setting.check.chkStyle == r.STYLE) {
var checkedList = data.getRadioCheckedList(setting);
if (nodeChecked) {
if (setting.check.radioType == r.TYPE_ALL) {
for (i = checkedList.length - 1; i >= 0; i--) {
pNode = checkedList[i];
var pNodeChecked = data.nodeChecked(setting, pNode);
if (pNodeChecked && pNode != node) {
data.nodeChecked(setting, pNode, false);
checkedList.splice(i, 1);
view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode);
if (pNode.parentTId != node.parentTId) {
view.repairParentChkClassWithSelf(setting, pNode);
}
}
}
checkedList.push(node);
} else {
var parentNode = (node.parentTId) ? node.getParentNode() : data.getRoot(setting);
var children = data.nodeChildren(setting, parentNode);
for (i = 0, l = children.length; i < l; i++) {
pNode = children[i];
var pNodeChecked = data.nodeChecked(setting, pNode);
if (pNodeChecked && pNode != node) {
data.nodeChecked(setting, pNode, false);
view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode);
}
}
}
} else if (setting.check.radioType == r.TYPE_ALL) {
for (i = 0, l = checkedList.length; i < l; i++) {
if (node == checkedList[i]) {
checkedList.splice(i, 1);
break;
}
}
}
} else {
var children = data.nodeChildren(setting, node);
if (nodeChecked && (!children || children.length == 0 || setting.check.chkboxType.Y.indexOf("s") > -1)) {
view.setSonNodeCheckBox(setting, node, true);
}
if (!nodeChecked && (!children || children.length == 0 || setting.check.chkboxType.N.indexOf("s") > -1)) {
view.setSonNodeCheckBox(setting, node, false);
}
if (nodeChecked && setting.check.chkboxType.Y.indexOf("p") > -1) {
view.setParentNodeCheckBox(setting, node, true);
}
if (!nodeChecked && setting.check.chkboxType.N.indexOf("p") > -1) {
view.setParentNodeCheckBox(setting, node, false);
}
}
},
makeChkClass: function (setting, node) {
var c = consts.checkbox, r = consts.radio,
fullStyle = "";
var nodeChecked = data.nodeChecked(setting, node);
if (node.chkDisabled === true) {
fullStyle = c.DISABLED;
} else if (node.halfCheck) {
fullStyle = c.PART;
} else if (setting.check.chkStyle == r.STYLE) {
fullStyle = (node.check_Child_State < 1) ? c.FULL : c.PART;
} else {
fullStyle = nodeChecked ? ((node.check_Child_State === 2 || node.check_Child_State === -1) ? c.FULL : c.PART) : ((node.check_Child_State < 1) ? c.FULL : c.PART);
}
var chkName = setting.check.chkStyle + "_" + (nodeChecked ? c.TRUE : c.FALSE) + "_" + fullStyle;
chkName = (node.check_Focus && node.chkDisabled !== true) ? chkName + "_" + c.FOCUS : chkName;
return consts.className.BUTTON + " " + c.DEFAULT + " " + chkName;
},
repairAllChk: function (setting, checked) {
if (setting.check.enable && setting.check.chkStyle === consts.checkbox.STYLE) {
var root = data.getRoot(setting);
var children = data.nodeChildren(setting, root);
for (var i = 0, l = children.length; i < l; i++) {
var node = children[i];
if (node.nocheck !== true && node.chkDisabled !== true) {
data.nodeChecked(setting, node, checked);
}
view.setSonNodeCheckBox(setting, node, checked);
}
}
},
repairChkClass: function (setting, node) {
if (!node) return;
data.makeChkFlag(setting, node);
if (node.nocheck !== true) {
var checkObj = $$(node, consts.id.CHECK, setting);
view.setChkClass(setting, checkObj, node);
}
},
repairParentChkClass: function (setting, node) {
if (!node || !node.parentTId) return;
var pNode = node.getParentNode();
view.repairChkClass(setting, pNode);
view.repairParentChkClass(setting, pNode);
},
repairParentChkClassWithSelf: function (setting, node) {
if (!node) return;
var children = data.nodeChildren(setting, node);
if (children && children.length > 0) {
view.repairParentChkClass(setting, children[0]);
} else {
view.repairParentChkClass(setting, node);
}
},
repairSonChkDisabled: function (setting, node, chkDisabled, inherit) {
if (!node) return;
if (node.chkDisabled != chkDisabled) {
node.chkDisabled = chkDisabled;
}
view.repairChkClass(setting, node);
var children = data.nodeChildren(setting, node);
if (children && inherit) {
for (var i = 0, l = children.length; i < l; i++) {
var sNode = children[i];
view.repairSonChkDisabled(setting, sNode, chkDisabled, inherit);
}
}
},
repairParentChkDisabled: function (setting, node, chkDisabled, inherit) {
if (!node) return;
if (node.chkDisabled != chkDisabled && inherit) {
node.chkDisabled = chkDisabled;
}
view.repairChkClass(setting, node);
view.repairParentChkDisabled(setting, node.getParentNode(), chkDisabled, inherit);
},
setChkClass: function (setting, obj, node) {
if (!obj) return;
if (node.nocheck === true) {
obj.hide();
} else {
obj.show();
}
obj.attr('class', view.makeChkClass(setting, node));
},
setParentNodeCheckBox: function (setting, node, value, srcNode) {
var checkObj = $$(node, consts.id.CHECK, setting);
if (!srcNode) srcNode = node;
data.makeChkFlag(setting, node);
if (node.nocheck !== true && node.chkDisabled !== true) {
data.nodeChecked(setting, node, value);
view.setChkClass(setting, checkObj, node);
if (setting.check.autoCheckTrigger && node != srcNode) {
setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]);
}
}
if (node.parentTId) {
var pSign = true;
if (!value) {
var pNodes = data.nodeChildren(setting, node.getParentNode());
for (var i = 0, l = pNodes.length; i < l; i++) {
var pNode = pNodes[i];
var nodeChecked = data.nodeChecked(setting, pNode);
if ((pNode.nocheck !== true && pNode.chkDisabled !== true && nodeChecked)
|| ((pNode.nocheck === true || pNode.chkDisabled === true) && pNode.check_Child_State > 0)) {
pSign = false;
break;
}
}
}
if (pSign) {
view.setParentNodeCheckBox(setting, node.getParentNode(), value, srcNode);
}
}
},
setSonNodeCheckBox: function (setting, node, value, srcNode) {
if (!node) return;
var checkObj = $$(node, consts.id.CHECK, setting);
if (!srcNode) srcNode = node;
var hasDisable = false;
var children = data.nodeChildren(setting, node);
if (children) {
for (var i = 0, l = children.length; i < l; i++) {
var sNode = children[i];
view.setSonNodeCheckBox(setting, sNode, value, srcNode);
if (sNode.chkDisabled === true) hasDisable = true;
}
}
if (node != data.getRoot(setting) && node.chkDisabled !== true) {
if (hasDisable && node.nocheck !== true) {
data.makeChkFlag(setting, node);
}
if (node.nocheck !== true && node.chkDisabled !== true) {
data.nodeChecked(setting, node, value);
if (!hasDisable) node.check_Child_State = (children && children.length > 0) ? (value ? 2 : 0) : -1;
} else {
node.check_Child_State = -1;
}
view.setChkClass(setting, checkObj, node);
if (setting.check.autoCheckTrigger && node != srcNode && node.nocheck !== true && node.chkDisabled !== true) {
setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]);
}
}
}
},
_z = {
tools: _tools,
view: _view,
event: _event,
data: _data
};
$.extend(true, $.fn.zTree.consts, _consts);
$.extend(true, $.fn.zTree._z, _z);
var zt = $.fn.zTree,
tools = zt._z.tools,
consts = zt.consts,
view = zt._z.view,
data = zt._z.data,
event = zt._z.event,
$$ = tools.$;
data.nodeChecked = function (setting, node, newChecked) {
if (!node) {
return false;
}
var key = setting.data.key.checked;
if (typeof newChecked !== 'undefined') {
if (typeof newChecked === "string") {
newChecked = tools.eqs(newChecked, "true");
}
newChecked = !!newChecked;
node[key] = newChecked;
} else if (typeof node[key] == "string"){
node[key] = tools.eqs(node[key], "true");
} else {
node[key] = !!node[key];
}
return node[key];
};
data.exSetting(_setting);
data.addInitBind(_bindEvent);
data.addInitUnBind(_unbindEvent);
data.addInitCache(_initCache);
data.addInitNode(_initNode);
data.addInitProxy(_eventProxy, true);
data.addInitRoot(_initRoot);
data.addBeforeA(_beforeA);
data.addZTreeTools(_zTreeTools);
var _createNodes = view.createNodes;
view.createNodes = function (setting, level, nodes, parentNode, index) {
if (_createNodes) _createNodes.apply(view, arguments);
if (!nodes) return;
view.repairParentChkClassWithSelf(setting, parentNode);
}
var _removeNode = view.removeNode;
view.removeNode = function (setting, node) {
var parentNode = node.getParentNode();
if (_removeNode) _removeNode.apply(view, arguments);
if (!node || !parentNode) return;
view.repairChkClass(setting, parentNode);
view.repairParentChkClass(setting, parentNode);
}
var _appendNodes = view.appendNodes;
view.appendNodes = function (setting, level, nodes, parentNode, index, initFlag, openFlag) {
var html = "";
if (_appendNodes) {
html = _appendNodes.apply(view, arguments);
}
if (parentNode) {
data.makeChkFlag(setting, parentNode);
}
return html;
}
})(jQuery);

Binary file not shown.

View File

@@ -0,0 +1,250 @@
/**
* @author zhixin wen <wenzhixin2010@gmail.com>
* version: 1.2.0
* https://github.com/wenzhixin/font-awesome-zTree
*/
.ztree * {
font-size: 14px;
font-family: inherit;
}
.ztree .fa, .ztree .fab, .ztree .fad, .ztree .fal, .ztree .far, .ztree .fas {
font-family: "Font Awesome 5 Free";
}
.ztree .ztree-search {
margin: 5px;
}
.ztree .ztree-search input {
padding: 5px 10px;
}
.ztree .ztree-search .search-clear {
padding: 5px 10px;
font-size: 12px;
line-height: 1;
}
.ztree li ul {
position: relative;
background: none;
margin-left: 12px;
padding-left: 0;
}
.ztree li ul.line {
background: none;
}
.ztree li ul::before {
content: "";
display: block;
width: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
border-left: 1px dotted #ccc;
}
.ztree li li {
position: relative;
padding-left: 9px;
line-height: inherit;
}
.ztree li li::before {
content: "";
display: block;
width: 10px;
height: 0;
border-top: 1px dotted #ccc;
position: absolute;
top: 13px;
left: 0;
}
.ztree li .noline_open + a + ul li::before {
border-top: none;
}
.ztree li .noline_open + a + ul::before {
border-left: none;
}
.ztree li span.node_name {
margin-right: 6px;
}
.ztree li span.button[class$=Page] {
width: 15px;
}
.ztree li span.button::before {
display: block;
line-height: 26px;
text-align: center;
}
.ztree li span.button.roots_docu, .ztree li span.button.center_docu, .ztree li span.button.bottom_docu {
position: relative;
height: 26px;
padding: 0;
}
.ztree li span.button.roots_docu::before, .ztree li span.button.center_docu::before, .ztree li span.button.bottom_docu::before {
content: "";
display: block;
width: 18px;
height: 0;
border-top: 1px dotted #ccc;
position: absolute;
top: 13px;
left: 0;
}
.ztree li span.button.root_open::before, .ztree li span.button.roots_open::before, .ztree li span.button.center_open::before, .ztree li span.button.bottom_open::before, .ztree li span.button.noline_open::before {
content: "\f146";
color: #3c8dbc;
}
.ztree li span.button.root_close::before, .ztree li span.button.roots_close::before, .ztree li span.button.center_close::before, .ztree li span.button.bottom_close::before, .ztree li span.button.noline_close::before {
content: "\f0fe";
color: #3c8dbc;
}
.ztree li span.button.chk {
color: #6b6b6b;
}
.ztree li span.button.chk[class$=focus]::before {
font-weight: bold;
}
.ztree li span.button.chk[class$=disable] {
color: #ccc;
}
.ztree li span.button.chk.checkbox_true_full::before, .ztree li span.button.chk.checkbox_true_disable::before, .ztree li span.button.chk.checkbox_true_full_focus::before {
content: "\f14a";
font-weight: normal !important;
color: #16b777;
}
.ztree li span.button.chk.checkbox_false_full::before, .ztree li span.button.chk.checkbox_false_disable::before, .ztree li span.button.chk.checkbox_false_full_focus::before {
content: "\f0c8";
font-weight: normal !important;
}
.ztree li span.button.chk.checkbox_true_part::before, .ztree li span.button.chk.checkbox_true_part_focus::before, .ztree li span.button.chk.checkbox_false_part::before, .ztree li span.button.chk.checkbox_false_part_focus::before {
content: "\f146";
font-weight: bold !important;
color: #16B777;
}
.ztree li span.button.chk.radio_true_full::before, .ztree li span.button.chk.radio_true_disable::before, .ztree li span.button.chk.radio_true_full_focus::before {
content: "\f058";
font-weight: normal !important;
}
.ztree li span.button.chk.radio_false_full::before, .ztree li span.button.chk.radio_false_disable::before, .ztree li span.button.chk.radio_false_full_focus::before {
content: "\f111";
font-weight: normal !important;
}
.ztree li span.button.chk.radio_true_part::before, .ztree li span.button.chk.radio_true_part_focus::before, .ztree li span.button.chk.radio_false_part::before, .ztree li span.button.chk.radio_false_part_focus::before {
content: "\f192";
font-weight: normal !important;
}
.ztree li span.button.ico_open::before {
content: "\f07c";
}
.ztree li span.button.ico_close::before {
content: "\f07b";
}
.ztree li span.button.ico_docu::before {
content: "\f15b";
}
.ztree li span.button.add::before {
content: "\f067";
}
.ztree li span.button.edit::before {
content: "\f044";
}
.ztree li span.button.remove::before {
content: "\f2ed";
}
.ztree li span.button.fa_history_ico_open::before, .ztree li span.button.fa_history_ico_close::before, .ztree li span.button.fa_history_ico_docu::before {
font-weight: bold !important;
content: "\f1da";
}
.ztree li span.button.fa_tasks_ico_open::before {
font-weight: bold !important;
content: "\f233";
}
.ztree li span.button.firstPage::before {
content: "\f100";
}
.ztree li span.button.prevPage::before {
content: "\f053";
}
.ztree li span.button.nextPage::before {
content: "\f054";
}
.ztree li span.button.lastPage::before {
content: "\f101";
}
.ztree li a span.button[style="width:0px;height:0px;"]::before, .ztree li a span.button[style^=background]::before {
content: "";
}
.ztree li a.curSelectedNode, .ztree li a.tmpTargetNode_inner {
height: 26px;
}
.ztree li > a {
height: 26px;
line-height: 26px;
}
.ztree li > span {
height: 26px;
}
.ztree li:last-child::before {
height: auto;
top: 13px;
bottom: 0;
}
.ztree li span.button.switch,
.ztree li span.button.chk,
.ztree li a span.button,
.ztree li span.button[class$=Page] {
position: relative;
width: 21px;
height: 26px;
background: none;
background-position: center center !important;
padding: 2px 0;
vertical-align: middle;
display: inline-block;
font: normal normal normal 15px/1 "Font Awesome 5 Free";
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@@ -10,10 +10,22 @@
<link rel="stylesheet" href="/static/common/css/insatll.css?v={:time()}" media="all">
</head>
<body>
<h1><img src="/static/common/images/logo-1.png"></h1>
<h1><img src="/static/common/images/logo-1.png" alt="" style="width: 100px;height: 100px;"></h1>
<h2>安装 EasyAdmin8 后台系统</h2>
<div class="content">
<form class="layui-form layui-form-pane" action="">
<div class="layui-card">
<blockquote class="layui-elem-quote layui-font-green" style="text-align: left;padding: 5px 10px;">
<div class="layui-row">
<div class="layui-col-lg6 layui-col-xl6 layui-col-xs6 layui-col-sm6 layui-col-xs6">
官网教程:<a href="https://EasyAdmin8.top?spm=001.3e3c9d.29f459" target="_blank" class="layui-font-blue">EasyAdmin8.top</a>
</div>
<div class="layui-col-lg6 layui-col-xl6 layui-col-xs6 layui-col-sm6 layui-col-xs6">
常见问题:<a href="https://EasyAdmin8.top/guide/question.html?spm=001.3e3c9d.29f460" target="_blank" class="layui-font-blue">Question</a>
</div>
</div>
</blockquote>
</div>
{if $errorInfo}
<div class="error">
{$errorInfo}