代码
This commit is contained in:
commit
d49c9fde59
838
CHANGELOG.md
Normal file
838
CHANGELOG.md
Normal file
@ -0,0 +1,838 @@
|
||||
|
||||
## V5.1.34 LTS(2019-1-30)
|
||||
|
||||
本次更新为常规更新,修正了一些反馈的问题。
|
||||
|
||||
* 改进Request类的`has`方法,支持`patch`
|
||||
* 改进`unique`验证的多条件支持
|
||||
* 修复自定义上传验证,检测文件大小
|
||||
* 改进`in`查询支持表达式
|
||||
* 改进路由的`getBind`方法
|
||||
* 改进验证类的错误信息获取
|
||||
* 改进`response`助手函数默认值
|
||||
* 修正mysql的`regexp`查询
|
||||
* 改进模型类型强制转换写入对`Expression`对象的支持
|
||||
|
||||
## V5.1.33 LTS(2019-1-16)
|
||||
|
||||
* 修复路由中存在多个相同替换的正则BUG
|
||||
* 修正whereLike查询
|
||||
* join方法支持参数绑定
|
||||
* 改进union方法
|
||||
* 修正多对多关联的attach方法
|
||||
* 改进验证类的正则规则自定义
|
||||
* 改进Request类method方法
|
||||
* 改进File日志类型的CLI日志写入
|
||||
* 改进文件日志time_format配置对JSON格式的支持
|
||||
|
||||
## V5.1.32 LTS(2018-12-24)
|
||||
|
||||
本次主要为常规更新,修正了一些反馈的问题。
|
||||
|
||||
|
||||
* 改进多对多关联的`attach`方法
|
||||
* 改进聚合查询的`field`处理
|
||||
* 改进关联的`save`方法
|
||||
* 修正模型`exists`方法返回值
|
||||
* 改进时间字段写入和输出
|
||||
* 改进控制器中间件的调用
|
||||
* 改进路由变量替换的性能
|
||||
* 改进缓存标签的处理机制
|
||||
|
||||
## V5.1.31 LTS (2018-12-9)
|
||||
|
||||
本次版本包含一个安全更新,建议升级。
|
||||
|
||||
* 改进`field`方法
|
||||
* 改进`count`方法返回类型
|
||||
* `download`函数增加在浏览器中显示文件功能
|
||||
* 修正多对多模型的中间表数据写入
|
||||
* 改进`sqlsrv`驱动支持多个Schemas模式查询
|
||||
* 统一助手函数与\think\response\Download函数文件过期时间
|
||||
* 完善关联模型的`save`方法 增加`make`方法仅创建对象不保存
|
||||
* 修改条件表达式对静态变量的支持
|
||||
* 修正控制器名获取
|
||||
* 改进view方法的`field`解析
|
||||
|
||||
## V5.1.30 LTS(2018-11-30)
|
||||
|
||||
该版本为常规更新,修正了一些社区反馈的问题。
|
||||
|
||||
主要更新如下:
|
||||
|
||||
* 改进查询类的`execute`方法
|
||||
* 判断路由规则定义添加对请求类型的判断
|
||||
* 修复`orderRaw`异常
|
||||
* 修正 `optimize:autoload`指令
|
||||
* 改进软删除的`destroy`方法造成重复执行事件的问题
|
||||
* 改进验证类对扩展验证规则 始终验证 不管是否`require`
|
||||
* 修复自定义验证`remove`所有规则的异常
|
||||
* 改进时间字段的自动写入支持微秒数据
|
||||
* 改进`Connection`类的`getrealsql`方法
|
||||
* 修正`https`地址的URL生成
|
||||
* 修复 `array_walk_recursive` 在低于PHP7.1消耗内部指针问题
|
||||
* 改进手动参数绑定使用
|
||||
* 改进聚合查询方法的`field`参数支持`Expression`
|
||||
|
||||
## V5.1.29 LTS(2018-11-11)
|
||||
|
||||
该版本主要改进了参数绑定的解析问题和提升性能,并修正了一些反馈的问题。
|
||||
|
||||
* 改进手动参数绑定
|
||||
* 修正MISS路由的分组参数无效问题
|
||||
* 行为支持对象的方法
|
||||
* 修正全局查询范围
|
||||
* 改进`belongsto`关联的`has`方法
|
||||
* 改进`hasMany`关联
|
||||
* 改进模型观察者多次注册的问题
|
||||
* 改进`query`类的默认查询参数处理
|
||||
* 修正`parseBetween`解析方法
|
||||
* 改进路由地址生成的本地域名支持
|
||||
* 改进参数绑定的实际URL解析性能
|
||||
* 改进`Env`类的`getEnv`和`get`方法
|
||||
* 改进模板缓存的生成优化
|
||||
* 修复验证类的多语言支持
|
||||
* 修复自定义场景验证`remove`规则异常
|
||||
* File类添加是否自动补全扩展名的选项
|
||||
* 改进`strpos`对子串是否存在的判断
|
||||
* 修复`choice`无法用值选择第一个选项问题
|
||||
* 验证器支持多维数组取值验证
|
||||
* 改进解析`extend`和`block`标签的正则
|
||||
|
||||
## V5.1.28 LTS(2018-10-29)
|
||||
|
||||
该版本主要修正了上一个版本存在的一些问题,并改进了关联查询
|
||||
|
||||
* 改进聚合查询方法的字段支持DISTINCT
|
||||
* 改进定义路由后url函数的端口生成
|
||||
* 改进控制器中间件对`swoole`等的支持
|
||||
* 改进Log类`save`方法
|
||||
* 改进验证类的闭包验证参数
|
||||
* 多对多关联支持指定中间表数据的名称
|
||||
* 关联聚合查询支持闭包方式指定聚合字段
|
||||
* 改进Lang类`get`方法
|
||||
* 多对多关联增加判断关联数据是否存在的方法
|
||||
* 改进关联查询使用`fetchsql`的情况
|
||||
* 改进修改器的是否已经执行判断
|
||||
* 增加`afterWith`和`beforeWith`验证规则 用于比较日期字段
|
||||
|
||||
## V5.1.27 LTS(2018-10-22)
|
||||
|
||||
该版本主要修正了路由绑定的参数,改进了修改器的执行多次问题,并正式宣布为LTS版本!
|
||||
|
||||
|
||||
* 修正路由绑定的参数丢失问题
|
||||
* 修正路由别名的参数获取
|
||||
* 改进修改器会执行多次的问题
|
||||
|
||||
## V5.1.26(2018-10-12)
|
||||
|
||||
该版本主要修正了上一个版本的一些问题,并改进了全局查询范围的支持,同时包含了一个安全更新。
|
||||
|
||||
|
||||
* 修正单一模块下注解路由无效的问题
|
||||
* 改进数据库的聚合查询的字段处理
|
||||
* 模型类增加`globalScope`属性定义 用于指定全局的查询范围
|
||||
* 模型的`useGlobalScope`方法支持传入数组 用于指定当前查询需要使用的全局查询范围
|
||||
* 改进数据集的`order`方法对数字类型的支持
|
||||
* 修正上一个版本`order`方法解析的一处BUG
|
||||
* 排序字段不合法或者错误的时候抛出异常
|
||||
* 改进`Request`类的`file`方法对上传文件的错误判断
|
||||
|
||||
## V5.1.25(2018-9-21)
|
||||
|
||||
该版本主要改进了查询参数绑定的性能和对浮点型的支持,以及一些细节的完善。
|
||||
|
||||
* 修正一处命令行问题
|
||||
* 改进`Socketlog`日志驱动,支持自定义默认展开日志类别
|
||||
* 修正`MorphMany`一处bug
|
||||
* 跳转到上次记住的url,并支持默认值
|
||||
* 改进模型的异常提示
|
||||
* 改进参数绑定对浮点型的支持
|
||||
* 改进`order`方法解析
|
||||
* 改进`json`字段数据的自动编码
|
||||
* 改进日志`log_write`可能造成的日志写入死循环
|
||||
* Log类增加`log_level`行为标签位置,用于对某个类型的日志进行处理
|
||||
* Route类增加`clear`方法清空路由规则
|
||||
* 分布式数据库配置支持使用数组
|
||||
* 单日志文件也支持`max_files`参数
|
||||
* 改进查询参数绑定的性能
|
||||
* 改进别名路由的URL后缀参数检测
|
||||
* 控制器前置方法和控制器中间件的`only`和`except`定义不区分大小写
|
||||
|
||||
## V5.1.24(2018-9-5)
|
||||
|
||||
该版本主要增加了命令行的表格输出功能,并增加了查看路由定义的指令,以及修正了社区的一些反馈问题。
|
||||
|
||||
* 修正`Request`类的`file`方法
|
||||
* 修正路由的`cache`方法
|
||||
* 修正路由缓存的一处问题
|
||||
* 改进上传文件获取的异常处理
|
||||
* 改进`fetchCollection`方法支持传入数据集类名
|
||||
* 修正多级控制器的注解路由生成
|
||||
* 改进`Middleware`类`clear`方法
|
||||
* 增加`route:list`指令用于[查看定义的路由](752690) 并支持排序
|
||||
* 命令行增加`Table`输出类
|
||||
* `Command`类增加`table`方法用于输出表格
|
||||
* 改进搜索器查询方法支持别名定义
|
||||
* 命令行配置增加`auto_path`参数用于定义自动载入的命令类路径
|
||||
* 增加`make:command`指令用于[快速生成指令](354146)
|
||||
* 改进`make:controller`指令对操作方法后缀的支持
|
||||
* 改进命令行的定义文件支持索引数组 用于指令对象的惰性加载
|
||||
* 改进`value`和`column`方法对后续查询结果的影响
|
||||
* 改进`RuleName`类的`setRule`方法
|
||||
|
||||
## V5.1.23(2018-8-23)
|
||||
|
||||
该版本主要改进了数据集对象的处理,增加了`findOrEmpty`方法,并且修正了一些社区反馈的BUG。
|
||||
|
||||
* 数据集类增加`diff`/`intersect`方法用于获取差集和交集(默认根据主键值比较)
|
||||
* 数据集类增加`order`方法支持指定字段排序
|
||||
* 数据集类增加`map`方法使用回调函数处理数据并返回新的数据集对象
|
||||
* Db增加`allowEmpty`方法允许`find`方法在没有数据的时候返回空数组或者空模型对象而不是null
|
||||
* Db增加`findOrEmpty`方法
|
||||
* Db增加`fetchCollection`方法用于指定查询返回数据集对象
|
||||
* 改进`order`方法的数组方式解析,增强安全性
|
||||
* 改进`withSearch`方法,支持第三个参数传入字段前缀标识,用于多表查询字段搜索
|
||||
* 修正`optimize:route`指令开启类库后缀后的注解路由生成
|
||||
* 修正redis缓存及session驱动
|
||||
* 支持指定`Yaconf`的独立配置文件
|
||||
* 增加`yaconf`助手函数用于配置文件
|
||||
|
||||
|
||||
## V5.1.22(2018-8-9)
|
||||
|
||||
该版本主要增加了模型搜索器和`withJoin`方法,完善了模型输出和对`Yaconf`的支持,修正了一些社区反馈的BUG。
|
||||
|
||||
* 改进一对一关联的`table`识别问题
|
||||
* 改进内置`Facade`类
|
||||
* 增加`withJoin`方法支持`join`方式的[一对一关联](一对一关联.md)查询
|
||||
* 改进`join`预载入查询的空数据问题
|
||||
* 改进`Config`类的`load`方法支持快速加载配置文件
|
||||
* 改进`execute`方法和事务的断线重连
|
||||
* 改进`memcache`驱动的`has`方法
|
||||
* 模型类支持定义[搜索器](搜索器.md)方法
|
||||
* 完善`Config`类对`Yaconf`的支持
|
||||
* 改进模型的`hidden/visible/append/withAttr`方法,支持在[查询前后调用](数组访问.md),以及支持数据集对象
|
||||
* 数据集对象增加`where`方法根据字段或者关联数据[过滤数据](模型数据集.md)
|
||||
* 改进AJAX请求的`204`判断
|
||||
|
||||
|
||||
## V5.1.21(2018-8-2)
|
||||
|
||||
该版本主要增加了下载响应对象和数组查询对象的支持,并修正了一些社区反馈的问题。
|
||||
|
||||
* 改进核心对象的无用信息调试输出
|
||||
* 改进模型的`isRelationAttr`方法判断
|
||||
* 模型类的`get`和`all`方法并入Db类
|
||||
* 增加[下载响应对象](文件下载.md)和`download`助手函数
|
||||
* 修正别名路由配置定义读取
|
||||
* 改进`resultToModel`方法
|
||||
* 修正开启类库后缀后的注解路由生成
|
||||
* `Response`类增加`noCache`快捷方法
|
||||
* 改进路由对象在`Swoole`/`Workerman`下面参数多次合并问题
|
||||
* 修正路由`ajax`/`pjax`参数后路由变量无法正确获取的问题
|
||||
* 增加清除中间件的方法
|
||||
* 改进依赖注入的参数规范自动识别(便于对接前端小写+下划线规范)
|
||||
* 改进`hasWhere`的数组条件的字段判断
|
||||
* 增加[数组查询对象](高级查询.md)`Where`支持(喜欢数组查询的福音)
|
||||
* 改进多对多关联的闭包支持
|
||||
|
||||
## V5.1.20(2018-7-25)
|
||||
|
||||
该版本主要增加了Db和模型的动态获取器的支持,并修正了一些已知问题。
|
||||
|
||||
* Db类添加[获取器支持](703981)
|
||||
* 支持模型及关联模型字段[动态定义获取器](354046)
|
||||
* 动态获取器支持`JSON`字段
|
||||
* 改进路由的`before`行为执行(匹配后执行)
|
||||
* `Config`类支持`Yaconf`
|
||||
* 改进Url生成的端口问题
|
||||
* Request类增加`setUrl`和`setBaseUrl`方法
|
||||
* 改进页面trace的信息显示
|
||||
* 修正`MorphOne`关联
|
||||
* 命令行添加[查看版本指令](703994)
|
||||
|
||||
## V5.1.19 (2018-7-13)
|
||||
|
||||
该版本是一个小幅改进版本,针对`Swoole`和`Workerman`的`Cookie`支持做了一些改进,并修正了一些已知的问题。
|
||||
|
||||
|
||||
* 改进query类`delete`方法对软删除条件判断
|
||||
* 修正分表查询的软删除问题
|
||||
* 模型查询的时候同时传入`table`和`name`属性
|
||||
* 容器类增加`IteratorAggregate`和`Countable`接口支持
|
||||
* 路由分组支持对下面的资源路由统一设置`only/except/vars`参数
|
||||
* 改进Cookie类更好支持扩展
|
||||
* 改进Request类`post`方法
|
||||
* 改进模型自关联的自动识别
|
||||
* 改进Request类对`php://input`数据的处理
|
||||
|
||||
|
||||
## V5.1.18 (2018-6-30)
|
||||
|
||||
该版本主要完善了对`Swoole`和`Workerman`的`HttpServer`运行支持,改进`Request`类,并修正了一些已知的问题。
|
||||
|
||||
* 改进关联`append`方法的处理
|
||||
* 路由初始化和检测方法分离
|
||||
* 修正`destroy`方法强制删除
|
||||
* `app_init`钩子位置移入`run`方法
|
||||
* `think-swoole`扩展更新到2.0版本
|
||||
* `think-worker`扩展更新到2.0版本
|
||||
* 改进Url生成的域名自动识别
|
||||
* `Request`类增加`setPathinfo`方法和`setHost`方法
|
||||
* `Request`类增加`withGet`/`withPost`/`withHeader`/`withServer`/`withCookie`/`withEnv`方法进行赋值操作
|
||||
* Route类改进`host`属性的获取
|
||||
* 解决注解路由配置不生效的问题
|
||||
* 取消Test日志驱动,改为使用`close`设置关闭全局日志写入
|
||||
* 修正路由的`response`参数
|
||||
* 修正204响应输出的判断
|
||||
|
||||
## V5.1.17 (2018-6-18)
|
||||
|
||||
该版本主要增加了控制器中间件的支持,改进了路由功能,并且修正了社区反馈的一些问题。
|
||||
|
||||
* 修正软删除的`delete`方法
|
||||
* 修正Query类`Count`方法
|
||||
* 改进多对多`detach`方法
|
||||
* 改进Request类`Session`方法
|
||||
* 增加控制器中间件支持
|
||||
* 模型类增加`jsonAssoc`属性用于定义json数据是否返回数组
|
||||
* 修正Request类`method`方法的请求伪装
|
||||
* 改进静态路由的匹配
|
||||
* 分组首页路由自动完整匹配
|
||||
* 改进sqlsrv的`column`方法
|
||||
* 日志类的`apart_level`配置支持true自动生成对应类型的日志文件
|
||||
* 改进`204`输出判断
|
||||
* 修正cli下页面输出的BUG
|
||||
* 验证类使用更高效的`ctype`验证机制
|
||||
* 改进Request类`cookie`方法
|
||||
* 修正软删除的`withTrashed`方法
|
||||
* 改进多态一对多的预载入查询
|
||||
* 改进Query类`column`方法的缓存读取
|
||||
* Query类增加`whereBetweenTimeField`方法
|
||||
* 改进分组下多个相同路由规则的合并匹配问题
|
||||
* 路由类增加`getRule`/`getRuleList`方法获取定义的路由
|
||||
|
||||
## V5.1.16 (2018-6-7)
|
||||
|
||||
该版本主要修正了社区反馈的一些问题,并对Request类做了进一步规范和优化。
|
||||
|
||||
* 改进Session类的`boot`方法
|
||||
* App类的初始化方法可以单独执行
|
||||
* 改进Request类的`param`方法
|
||||
* 改进资源路由的变量替换
|
||||
* Request类增加`__isset`方法
|
||||
* 改进`useGlobalScope`方法对软删除的影响
|
||||
* 修正命令行调用
|
||||
* 改进Cookie类`init`方法
|
||||
* 改进多对多关联删除的返回值
|
||||
* 一对多关联写入支持`replace`
|
||||
* 路由增加`filter`检测方法,用于通过请求参数检测路由是否匹配
|
||||
* 取消Request类`session/env/server`方法的`filter`参数
|
||||
* 改进关联的指定属性输出
|
||||
* 模型删除操作删除后不清空对象数据仅作标记
|
||||
* 调整模型的`save`方法返回值为布尔值
|
||||
* 修正Request类`isAjax`方法
|
||||
* 修正中间件的模块配置读取
|
||||
* 取消Request类的请求变量的设置功能
|
||||
* 取消请求变量获取的默认修饰符
|
||||
* Request类增加`setAction/setModule/setController`方法
|
||||
* 关联模型的`delete`方法调用Query类
|
||||
* 改进URL生成的域名识别
|
||||
* 改进URL检测对已定义路由的域名判断
|
||||
* 模型类增加`isExists`和`isForce`方法
|
||||
* 软删除的`destroy`和`restore`方法返回值调整为布尔值
|
||||
|
||||
## V5.1.15 (2018-6-1)
|
||||
|
||||
该版本主要改进了路由缓存的性能和缓存方式设置,增加了JSON格式文件日志的支持,并修正了社区反馈的一些问题。
|
||||
|
||||
* 容器类增加`exists`方法 仅判断是否存在对象实例
|
||||
* 取消配置类的`autoload`方法
|
||||
* 改进路由缓存大小提高性能
|
||||
* 改进Dispatch类`init`方法
|
||||
* 增加`make:validate`指令生成验证器类
|
||||
* Config类`get`方法支持默认值参数
|
||||
* 修正字段缓存指令
|
||||
* 改进App类对`null`数据的返回
|
||||
* 改进模型类的`__isset`方法判断
|
||||
* 修正`Query`类的`withAggregate`方法
|
||||
* 改进`RuleItem`类的`setRuleName`方法
|
||||
* 修正依赖注入和参数的冲突问题
|
||||
* 修正Db类对第三方驱动的支持
|
||||
* 修正模型类查询对象问题
|
||||
* 修正File缓存驱动的`has`方法
|
||||
* 修正资源路由嵌套
|
||||
* 改进Request类对`$_SERVER`变量的读取
|
||||
* 改进请求缓存处理
|
||||
* 路由缓存支持指定单独的缓存方式和参数
|
||||
* 修正资源路由的中间件多次执行问题
|
||||
* 修正`optimize:config`指令
|
||||
* 文件日志支持`JSON`格式日志保存
|
||||
* 修正Db类`connect`方法
|
||||
* 改进Log类`write`方法不会自动写入之前日志
|
||||
* 模型的关联操作默认启用事务
|
||||
* 改进软删除的事件响应
|
||||
|
||||
## V5.1.14 (2018-5-18)
|
||||
|
||||
该版本主要对底层容器进行了一些优化改进,并增加了路由缓存功能,可以进一步提升路由性能。
|
||||
|
||||
* 依赖注入的对象参数传入改进
|
||||
* 改进核心类的容器实例化
|
||||
* 改进日期字段的读取
|
||||
* 改进验证类的`getScene`方法
|
||||
* 模型的`create`方法和`save`方法支持`replace`操作
|
||||
* 改进`Db`类的调用机制
|
||||
* App类调整为容器类
|
||||
* 改进容器默认绑定
|
||||
* `Loader`类增加工厂类的实例化方法
|
||||
* 增加路由变量默认规则配置参数
|
||||
* 增加路由缓存设计
|
||||
* 错误处理机制改进
|
||||
* 增加清空路由缓存指令
|
||||
|
||||
|
||||
## V5.1.13 (2018-5-11)
|
||||
|
||||
该版本主要增加了MySQL的XA事务支持,模型事件支持观察者,以及对Facade类的改进。
|
||||
|
||||
* 改进自动缓存
|
||||
* 改进Url生成
|
||||
* 修正数据缓存
|
||||
* 修正`value`方法的缓存
|
||||
* `join`方法和`view`方法的条件支持使用`Expression`对象
|
||||
* 改进驱动的`parseKey`方法
|
||||
* 改进Request类`host`方法和`domain`方法对端口的处理
|
||||
* 模型增加`withEvent`方法用于控制当前操作是否需要执行模型事件
|
||||
* 模型`setInc/setDec`方法支持更新事件
|
||||
* 模型添加`before_restore/after_restore`事件
|
||||
* 增加模型事件观察者
|
||||
* 路由增加`mobile`方法设置是否允许手机访问
|
||||
* 数据库XA事务支持
|
||||
* 改进索引数组查询对`IN`查询的支持
|
||||
* 修正`invokeMethod`方法
|
||||
* 修正空数据写入返回值的BUG
|
||||
* redis驱动支持`predis`
|
||||
* 改进`parseData`方法
|
||||
* 改进模块加载
|
||||
* App类初始化方法调整
|
||||
* 改进数组查询对表达式`Expression`对象支持
|
||||
* 改进闭包的依赖注入调用
|
||||
* 改进多对多关联的中间表模型更新
|
||||
* 增加容器中对象的自定义实例化
|
||||
|
||||
## V5.1.12 (2018-4-25)
|
||||
|
||||
该版本主要改进了主从查询的及时性,并支持动态设置请求数据。
|
||||
|
||||
* 支持动态设置请求数据
|
||||
* 改进`comment`方法解析
|
||||
* 修正App类`__unset`方法
|
||||
* 改进url生成的域名绑定
|
||||
* 改进主从查询的及时性
|
||||
* 修正`value`的数据缓存功能
|
||||
* 改进分页类的集合对象方法调用
|
||||
* 改进Db类的代码提示
|
||||
* SQL日志增加主从标记
|
||||
|
||||
## V5.1.11 (2018-4-19)
|
||||
|
||||
该版本为安全和修正版本,改进了JSON查询的参数绑定问题和容器类对象实例获取,并包含一处可能的安全隐患,建议更新。
|
||||
|
||||
* 支持指定JSON数据查询的字段类型
|
||||
* 修正`selectInsert`方法
|
||||
* `whereColumn`方法支持数组方式
|
||||
* 改进容器类`make`方法
|
||||
* 容器类`delete`方法支持数组
|
||||
* 改进`composer`自动加载
|
||||
* 改进模板引擎
|
||||
* 修正`like`查询的一处安全隐患
|
||||
|
||||
## V5.1.10 (2018-4-16)
|
||||
|
||||
该版本为修正版本,修正上一个版本的一些BUG,并增强了`think clear`指令。
|
||||
|
||||
* 改进`orderField`方法
|
||||
* 改进`exists`查询
|
||||
* 修改cli模式入口文件位置计算
|
||||
* 修正`null`查询
|
||||
* 改进`parseTime`方法
|
||||
* 修正关联预载入查询
|
||||
* 改进`mysql`驱动
|
||||
* 改进`think clear`指令 支持 `-c -l -r `选项
|
||||
* 改进路由规则对`/`结尾的支持
|
||||
|
||||
## V5.1.9 (2018-4-12)
|
||||
|
||||
该版本主要是一些改进和修正,并包含一个安全更新,是一个推荐更新版本。
|
||||
|
||||
* 默认模板渲染规则支持配置保持操作方法名
|
||||
* 改进`Request`类的`ip`方法
|
||||
* 支持模型软删除字段的默认值定义
|
||||
* 改进路由变量规则对中文的支持
|
||||
* 使用闭包查询的时候使用`cache(true)` 抛出异常提示
|
||||
* 改进`Loader`类`loadComposerAutoloadFiles`方法
|
||||
* 改进查询方法安全性
|
||||
* 修正路由地址中控制器名驼峰问题
|
||||
* 调整上一个版本的`module_init`和`app_begin`的钩子顺序问题
|
||||
* 改进CLI命令行执行的问题
|
||||
* 修正社区反馈的其它问题
|
||||
|
||||
## V5.1.8 (2018-4-5)
|
||||
|
||||
该版本主要改进了中间件的域名和模块支持,并同时修正了几个已知问题。
|
||||
|
||||
* 增加`template.auto_rule` 参数设置默认模板渲染的操作名自动转换规则
|
||||
* 默认模板渲染规则改由视图驱动实现
|
||||
* 修正路由标识定义
|
||||
* 修正控制器路由方法
|
||||
* 改进Request类`ip`方法支持自定义代理IP参数
|
||||
* 路由注册中间件支持数组方式别名
|
||||
* 改进命令行执行下的`composer`自动加载
|
||||
* 添加域名中间件注册支持
|
||||
* 全局中间件支持模块定义文件
|
||||
* Log日志配置支持`close`参数可以全局关闭日志写入
|
||||
* 中间件方法中捕获`HttpResponseException`异常
|
||||
* 改进中间件的闭包参数传入
|
||||
* 改进分组路由的延迟解析
|
||||
* 改进URL生成对域名绑定的支持
|
||||
* 改进文件缓存和文件日志驱动的并发支持
|
||||
|
||||
## V5.1.7 (2018-3-28)
|
||||
|
||||
该版本主要修正了路由的一些问题,并改进了查询的安全性。
|
||||
|
||||
* 支持`middleware`配置文件预先定义中间件别名方便路由调用
|
||||
* 修正资源路由
|
||||
* 改进`field`方法 自动识别`fieldRaw`
|
||||
* 增加`Expression`类
|
||||
* Query类增加`raw`方法
|
||||
* Query类的`field`/ `order` 和` where`方法都支持使用`raw`表达式查询
|
||||
* 改进`inc/dec`查询 支持批量更新
|
||||
* 改进路由分组
|
||||
* 改进Response类`create`方法
|
||||
* 改进composer自动加载
|
||||
* 修正域名路由的`append`方法
|
||||
* 修正操作方法的初始化方法获取不到问题
|
||||
|
||||
## V5.1.6 (2018-3-26)
|
||||
|
||||
该版本主要改进了路由规则的匹配算法,大幅提升了路由性能。并正式引入了中间件的支持,可以在路由中定义或者全局定义。另外包含了一个安全更新,是一个建议更新版本。
|
||||
|
||||
* 改进URL生成对路由`ext`方法的支持
|
||||
* 改进查询缓存对不同数据库相同表名的支持
|
||||
* 改进composer自动加载的性能
|
||||
* 改进空路由变量对默认参数的影响
|
||||
* mysql的`json`字段查询支持多级
|
||||
* Query类增加`option`方法
|
||||
* 优化路由匹配
|
||||
* 修复验证规则数字键名丢失问题
|
||||
* 改进路由Url生成
|
||||
* 改进一对一关联预载入查询
|
||||
* Request类增加`rootDomain`方法
|
||||
* 支持API资源控制器生成 `make:controller --api`
|
||||
* 优化Template类的标签解析
|
||||
* 容器类增加删除和清除对象实例的方法
|
||||
* 修正MorphMany关联的`eagerlyMorphToMany`方法一处错误
|
||||
* Container类的异常捕获改进
|
||||
* Domain对象支持`bind`方法
|
||||
* 修正分页参数
|
||||
* 默认模板的输出规则不受URL影响
|
||||
* 注解路由支持多级控制器
|
||||
* Query类增加`getNumRows`方法获取前次操作影响的记录数
|
||||
* 改进查询条件的性能
|
||||
* 改进模型类`readTransform`方法对序列化类型的处理
|
||||
* Log类增加`close`方法可以临时关闭当前请求的日志写入
|
||||
* 文件日志方式增加自动清理功能(设置`max_files`参数)
|
||||
* 修正Query类的`getPk`方法
|
||||
* 修正模板缓存的布局开关问题
|
||||
* 修正Query类`select`方法的缓存
|
||||
* 改进input助手函数
|
||||
* 改进断线重连的信息判断
|
||||
* 改进正则验证方法
|
||||
* 调整语言包的加载顺序 放到`app_init`之前
|
||||
* controller类`fetch`方法改为`final`
|
||||
* 路由地址中的变量支持使用`<var>`方式
|
||||
* 改进XMLResponse 支持传入编码过的xml内容
|
||||
* 修正Query类`view`方法的数组表名支持
|
||||
* 改进路由的模型闭包绑定
|
||||
* 改进分组变量规则的继承
|
||||
* 改进`cli-server`模式下的`composer`自动加载
|
||||
* 路由变量规则异常捕获
|
||||
* 引入中间件支持
|
||||
* 路由定义增加`middleware`方法
|
||||
* 增加生成中间件指令`make:middleware`
|
||||
* 增加全局中间件定义支持
|
||||
* 改进`optimize:config`指令对全局中间件的支持
|
||||
* 改进config类`has`方法
|
||||
* 改进时间查询的参数绑定
|
||||
* 改进`inc/dec/exp`查询的安全性
|
||||
|
||||
|
||||
## V5.1.5 (2018-1-31)
|
||||
|
||||
该版本主要增强了数据库的JSON查询,并支持JSON字段的聚合查询,改进了一些性能问题,修正了路由的一些BUG,主要更新如下:
|
||||
|
||||
* 改进数据集查询对`JSON`数据的支持
|
||||
* 改进聚合查询对`JSON`字段的支持
|
||||
* 模型类增加`getOrFail`方法
|
||||
* 改进数据库驱动的`parseKey`方法
|
||||
* 改进Query类`join`方法的自关联查询
|
||||
* 改进数据查询不存在不生成查询缓存
|
||||
* 增加`run`命令行指令启动内置服务器
|
||||
* `Request`类`pathinfo`方法改进对`cli-server`支持
|
||||
* `Session`类增加`use_lock`配置参数设置是否启用锁机制
|
||||
* 优化`File`缓存自动生成空目录的问题
|
||||
* 域名及分组路由支持`append`方法传递隐式参数
|
||||
* 改进日志的并发写入问题
|
||||
* 改进`Query`类的`where`方法支持传入`Query`对象
|
||||
* 支持设置单个日志文件的文件名
|
||||
* 修正路由规则的域名条件约束
|
||||
* `Request`类增加`subDomain`方法用于获取当前子域名
|
||||
* `Response`类增加`allowCache`方法控制是否允许请求缓存
|
||||
* `Request`类增加`sendData`方法便于扩展
|
||||
* 改进`Env`类不依赖`putenv`方法
|
||||
* 改进控制台`trace`显示错误
|
||||
* 改进`MorphTo`关联
|
||||
* 改进完整路由匹配后带斜线访问出错的情况
|
||||
* 改进路由的多级分组问题
|
||||
* 路由url地址生成支持多级分组
|
||||
* 改进路由Url生成的`url_convert`参数的影响
|
||||
* 改进`miss`和`auto`路由内部解析
|
||||
* 取消预载入关联查询缓存功能
|
||||
|
||||
## V5.1.4 (2018-1-19)
|
||||
|
||||
该版本主要增强了数据库和模型操作,主要更新如下:
|
||||
|
||||
* 支持设置 `deleteTime`属性为`false` 关闭软删除
|
||||
* 模型增加`getError`方法
|
||||
* 改进Query类的`getTableFields`/`getFieldsType`方法 支持表名自动获取
|
||||
* 模型类`toCollection`方法增加参数指定数据集类
|
||||
* 改进`union`查询
|
||||
* 关联预载入`with`方法增加缓存参数
|
||||
* 改进模型类的`get`和`all`方法的缓存 支持关联缓存
|
||||
* 支持`order by field`操作
|
||||
* 改进`insertAll`分批写入
|
||||
* 改进`json`字段数据支持
|
||||
* 增加JSON数据的模型对象化操作
|
||||
* 改进路由`ext`参数检测
|
||||
* 修正`rule`方法的`method`参数使用 `get|post` 方式注册路由的问题
|
||||
|
||||
## V5.1.3 (2018-1-12)
|
||||
|
||||
该版本主要改进了路由及调整函数加载顺序,主要更新如下:
|
||||
|
||||
* 增加`env`助手函数;
|
||||
* 增加`route`助手函数;
|
||||
* 增加视图路由方法;
|
||||
* 增加路由重定向方法;
|
||||
* 路由默认区分最后的目录斜杆(支持设置不区分);
|
||||
* 调整公共文件和配置文件的加载顺序(可以在配置文件中直接使用助手函数);
|
||||
* 视图类增加`filter`方法设置输出过滤;
|
||||
* `view`助手函数增加`filter`参数;
|
||||
* 改进缓存生成指令;
|
||||
* Session类的`get`方法支持获取多级;
|
||||
* Request类`only`方法支持指定默认值;
|
||||
* 改进路由分组;
|
||||
* 修正使用闭包查询的时候自动数据缓存出错的情况;
|
||||
* 废除`view_filter`钩子位置;
|
||||
* 修正分组下面的资源路由;
|
||||
* 改进session驱动;
|
||||
|
||||
## V5.1.2 (2018-1-8)
|
||||
|
||||
该版本改进了配置类及数据库类,主要更新如下:
|
||||
|
||||
* 修正嵌套路由分组;
|
||||
* 修正自定义模板标签界定符后表达式语法出错的情况;
|
||||
* 修正自关联的多次调用问题;
|
||||
* 修正数组查询的`null`条件查询;
|
||||
* 修正Query类的`order`及`field`的一处可能的BUG;
|
||||
* 配置参数设置支持三级;
|
||||
* 配置对象支持`ArrayAccess`;
|
||||
* App类增加`path`方法用于设置应用目录;
|
||||
* 关联定义增加`selfRelation`方法用于设置是否为自关联;
|
||||
|
||||
## V5.1.1 (2018-1-3)
|
||||
|
||||
修正一些反馈的BUG,包括:
|
||||
|
||||
* 修正Cookie类存取数组的问题
|
||||
* 修正Controller的`fetch`方法
|
||||
* 改进跨域请求
|
||||
* 修正`insertAll`方法
|
||||
* 修正`chunk`方法
|
||||
|
||||
## V5.1.0 (2018-1-1)
|
||||
|
||||
主要更新如下:
|
||||
|
||||
* 增加注解路由支持
|
||||
* 路由支持跨域请求设置
|
||||
* 增加`app_dispatch`钩子位置
|
||||
* 修正多对多关联的`detach`方法
|
||||
* 修正软删除的`destroy`方法
|
||||
* Cookie类`httponly`参数默认为false
|
||||
* 日志File驱动增加`single`参数配置记录同一个文件(不按日期生成)
|
||||
* 路由的`ext`和`denyExt`方法支持不传任何参数
|
||||
* 改进模型的`save`方法对`oracle`的支持
|
||||
* Query类的`insertall`方法支持配合`data`和`limit`方法
|
||||
* 增加`whereOr`动态查询支持
|
||||
* 日志的ip地址记录改进
|
||||
* 模型`saveAll`方法支持`isUpdate`方法
|
||||
* 改进`Pivot`模型的实例化操作
|
||||
* 改进Model类的`data`方法
|
||||
* 改进多对多中间表模型类
|
||||
* 模型增加`force`方法强制更新所有数据
|
||||
* Hook类支持设置入口方法名称
|
||||
* 改进验证类
|
||||
* 改进`hasWhere`查询的数据重复问题
|
||||
* 模型的`saveall`方法返回数据集对象
|
||||
* 改进File缓存的`clear`方法
|
||||
* 缓存添加统一的序列化机制
|
||||
* 改进泛三级域名的绑定
|
||||
* 改进泛域名的传值和取值
|
||||
* Request类增加`panDomain`方法
|
||||
* 改进废弃字段判断
|
||||
* App类增加`create`方法用于实例化应用类库
|
||||
* 容器类增加`has`方法
|
||||
* 改进多数据库切换连接
|
||||
* 改进断线重连的异常捕获
|
||||
* 改进模型类`buildQuery`方法
|
||||
* Query类增加`unionAll`方法
|
||||
* 关联统计功能增强(支持Sum/Max/Min/Avg)
|
||||
* 修正延迟写入
|
||||
* chunk方法支持复合主键
|
||||
* 改进JSON类型的写入
|
||||
* 改进Mysql的insertAll方法
|
||||
* Model类`save`方法改进复合主键包含自增的情况
|
||||
* 改进Query类`inc`和`dec`方法的关键字处理
|
||||
* File缓存inc和dec方法保持原来的有效期
|
||||
* 改进redis缓存的有效期判断
|
||||
* 增加checkRule方法用于单独数据的多个验证规则
|
||||
* 修正setDec方法的延迟写入
|
||||
* max和min方法增加force参数
|
||||
* 二级配置参数区分大小写
|
||||
* 改进join方法自关联的问题
|
||||
* 修正关联模型自定义表名的情况
|
||||
* Query类增加getFieldsType和getTableFields方法
|
||||
* 取消视图替换功能及view_replace_str配置参数
|
||||
* 改进域名绑定模块后的额外路由规则问题
|
||||
* 改进mysql的insertAll方法
|
||||
* 改进insertAll方法写入json字段数据的支持
|
||||
* 改进redis长连接多编号库的情况
|
||||
|
||||
## RC3版本(2017-11-6)
|
||||
|
||||
主要更新如下:
|
||||
|
||||
* 改进redis驱动的`get`方法
|
||||
* 修正Query类的`alias`方法
|
||||
* `File`类错误信息支持多语言
|
||||
* 修正路由的额外参数解析
|
||||
* 改进`whereTime`方法
|
||||
* 改进Model类`getAttr`方法
|
||||
* 改进App类的`controller`和`validate`方法支持多层
|
||||
* 改进`HasManyThrough`类
|
||||
* 修正软删除的`restore`方法
|
||||
* 改进`MorpthTo`关联
|
||||
* 改进数据库驱动类的`parseKey`方法
|
||||
* 增加`whereField`动态查询方法
|
||||
* 模型增加废弃字段功能
|
||||
* 改进路由的`after`行为检查和`before`行为机制
|
||||
* 改进路由分组的检查
|
||||
* 修正mysql的`json`字段查询
|
||||
* 取消Connection类的`quote`方法
|
||||
* 改进命令行的支持
|
||||
* 验证信息支持多语言
|
||||
* 修正路由模型绑定
|
||||
* 改进参数绑定类型对枚举类型的支持
|
||||
* 修正模板的`{$Think.version} `输出
|
||||
* 改进模板`date`函数解析
|
||||
* 改进`insertAll`方法支持分批执行
|
||||
* Request类`host`方法支持反向代理
|
||||
* 改进`JumpResponse`支持区分成功和错误模板
|
||||
* 改进开启类库后缀后的关联外键自动识别问题
|
||||
* 修正一对一关联的JOIN方式预载入查询问题
|
||||
* Query类增加`hidden`方法
|
||||
|
||||
## RC2版本(2017-10-17)
|
||||
|
||||
主要更新如下:
|
||||
|
||||
* 修正视图查询
|
||||
* 修正资源路由
|
||||
* 修正`HasMany`关联 修正`where`方法的闭包查询
|
||||
* 一对一关联绑定属性到父模型后 关联属性不再保留
|
||||
* 修正应用的命令行配置文件读取
|
||||
* 改进`Connection`类的`getCacheKey`方法
|
||||
* 改进文件上传的非法图像异常
|
||||
* 改进验证类的`unique`规则
|
||||
* Config类`get`方法支持获取一级配置
|
||||
* 修正count方法对`fetchSql`的支持
|
||||
* 修正mysql驱动对`socket`支持
|
||||
* 改进Connection类的`getRealSql`方法
|
||||
* 修正`view`助手函数
|
||||
* Query类增加`leftJoin` `rightJoin` 和 `fullJoin`方法
|
||||
* 改进app_namespace的获取
|
||||
* 改进`append`方法对一对一`bind`属性的支持
|
||||
* 改进关联的`saveall`方法的返回值
|
||||
* 路由标识设置异常修复
|
||||
* 改进Route类`rule`方法
|
||||
* 改进模型的`table`属性设置
|
||||
* 改进composer autofile的加载顺序
|
||||
* 改进`exception_handle`配置对闭包的支持
|
||||
* 改进app助手函数增加参数
|
||||
* 改进composer的加载路径判断
|
||||
* 修正路由组合变量的URL生成
|
||||
* 修正路由URL生成
|
||||
* 改进`whereTime`查询并支持扩展规则
|
||||
* File类的`move`方法第二个参数支持`false`
|
||||
* 改进Config类
|
||||
* 改进缓存类`remember`方法
|
||||
* 惯例配置文件调整 Url类当普通模式参数的时候不做`urlencode`处理
|
||||
* 取消`ROOT_PATH`和`APP_PATH`常量定义 如需更改应用目录 自己重新定义入口文件
|
||||
* 增加`app_debug`的`Env`获取
|
||||
* 修正泛域名绑定
|
||||
* 改进查询表达式的解析机制
|
||||
* mysql增加`regexp`查询表达式 支持正则查询
|
||||
* 改进查询表达式的异常判断
|
||||
* 改进model类的`destroy`方法
|
||||
* 改进Builder类 取消`parseValue`方法
|
||||
* 修正like查询的参数绑定问题
|
||||
* console和start文件移出核心纳入应用库
|
||||
* 改进Db类主键删除方法
|
||||
* 改进泛域名绑定模块
|
||||
* 取消`BIND_MODULE`常量 改为在入口文件使用`bind`方法设置
|
||||
* 改进数组查询
|
||||
* 改进模板渲染的异常处理
|
||||
* 改进控制器基类的架构方法参数
|
||||
* 改进Controller类的`success`和`error`方法
|
||||
* 改进对浏览器`JSON-Handle`插件的支持
|
||||
* 优化跳转模板的移动端显示
|
||||
* 修正模型查询的`chunk`方法对时间字段的支持
|
||||
* 改进trace驱动
|
||||
* Collection类增加`push`方法
|
||||
* 改进Redis Session驱动
|
||||
* 增加JumpResponse驱动
|
||||
|
||||
|
||||
## RC1(2017-9-8)
|
||||
|
||||
主要新特性为:
|
||||
|
||||
* 引入容器和Facade支持
|
||||
* 依赖注入完善和支持更多场景
|
||||
* 重构的(对象化)路由
|
||||
* 配置和路由目录独立
|
||||
* 取消系统常量
|
||||
* 助手函数增强
|
||||
* 类库别名机制
|
||||
* 模型和数据库增强
|
||||
* 验证类增强
|
||||
* 模板引擎改进
|
||||
* 支持PSR-3日志规范
|
||||
* RC1版本取消了5.0多个字段批量数组查询的方式
|
||||
32
README.md
Normal file
32
README.md
Normal file
@ -0,0 +1,32 @@
|
||||
DolphinPHP
|
||||
===============
|
||||
|
||||
DophinPHP(海豚PHP)是一个基于ThinkPHP5.1.34 LTS开发的一套开源PHP快速开发框架,DophinPHP秉承极简、极速、极致的开发理念,为开发集成了基于数据-角色的权限管理机制,集成多种灵活快速构建工具,可方便快速扩展的模块、插件、钩子、数据包。统一了模块、插件、钩子、数据包之间的版本和依赖关系,进一步降低了代码和数据的沉余,以方便开发者快速构建自己的应用。
|
||||
|
||||
## 功能特性
|
||||
### 万事俱备-ZBuilder构建神器
|
||||
使用DophinPHP(海豚PHP)自主开发的ZBuilder类,您可以轻松的应对复杂多变的表单、数据列表。数据列表集成类似EXCEL的快速筛选、排序、模糊搜索、AJAX编辑等功能,表单页集成常用的文本、下拉框、单选、多选、关键词、编辑器、文件上传、图片上传、图片裁切等控件,除此之外,您还可以灵活的扩展自己的控件,以便在自己的项目中重复使用。ZBuilder让您更加专注业务逻辑。
|
||||
|
||||
### 相得益彰-模块化组合
|
||||
千变万化的事物之间总是有着千丝万缕的关系,在应用开发的时候,不同的功能、模块甚至数据之间也存在各种依赖关系。在保证模块、插件独立的同时,为了降低代码、数据的沉余,我们将每个模块、插件、数据包加上了唯一的标示和版本号以及彼此之间的依赖关系,保证了应用程序的高内聚低耦合。
|
||||
|
||||
### 一举多得-夸平台支持
|
||||
DophinPHP(海豚PHP)集成BootStrap、JQuery、Xeditable、Select2等优秀的前端开源框架,基于ThinkPHP5出色的性能和REST支持、远程调试和更好的支持API开发。让您可以一次开发即可直接兼容PC、移动设备和微信界面,通过简单的配置即可快速构建属于自己的Android、IOS的APP。
|
||||
|
||||
## 鸣谢
|
||||
感谢[ThinkPHP](http://www.thinkphp.cn)、[JQuery](http://jquery.com/)、[Bootstrap](http://getbootstrap.com/)、[Xeditable](http://vitalets.github.io/x-editable)、[Select2](https://github.com/select2/select2)等优秀开源项目。
|
||||
|
||||
## 官方网站
|
||||
[www.dolphinphp.com](http://www.dolphinphp.com)
|
||||
|
||||
## 版权信息
|
||||
|
||||
DolphinPHP提供个人非商业用途免费使用,商业需授权。
|
||||
|
||||
本项目包含的第三方源码和二进制文件之版权信息另行标注。
|
||||
|
||||
版权所有Copyright © 2016-2019 广东卓锐软件有限公司 (http://www.zrthink.com)
|
||||
|
||||
All rights reserved。
|
||||
|
||||
更多细节参阅 [LICENSE.txt](LICENSE.txt)
|
||||
1
application/.htaccess
Normal file
1
application/.htaccess
Normal file
@ -0,0 +1 @@
|
||||
deny from all
|
||||
65
application/admin/controller/Action.php
Normal file
65
application/admin/controller/Action.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\admin\model\Action as ActionModel;
|
||||
use app\admin\model\Module as ModuleModel;
|
||||
|
||||
/**
|
||||
* 行为管理控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Action extends Admin
|
||||
{
|
||||
/**
|
||||
* 首页
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// 查询
|
||||
$map = $this->getMap();
|
||||
// 数据列表
|
||||
$data_list = ActionModel::where($map)->order('id desc')->paginate();
|
||||
// 所有模块的名称和标题
|
||||
$list_module = ModuleModel::getModule();
|
||||
|
||||
// 新增或编辑页面的字段
|
||||
$fields = [
|
||||
['hidden', 'id'],
|
||||
['select', 'module', '所属模块', '', $list_module],
|
||||
['text', 'name', '行为标识', '由英文字母和下划线组成'],
|
||||
['text', 'title', '行为名称', ''],
|
||||
['textarea', 'remark', '行为描述'],
|
||||
['textarea', 'rule', '行为规则', '不写则只记录日志'],
|
||||
['textarea', 'log', '日志规则', '记录日志备注时按此规则来生成,支持[变量|函数]。目前变量有:user,time,model,record,data,details'],
|
||||
['radio', 'status', '立即启用', '', ['否', '是'], 1]
|
||||
];
|
||||
|
||||
// 使用ZBuilder快速创建数据表格
|
||||
return ZBuilder::make('table')
|
||||
->setPageTitle('行为管理') // 设置页面标题
|
||||
->setSearch(['name' => '标识', 'title' => '名称']) // 设置搜索框
|
||||
->addColumns([ // 批量添加数据列
|
||||
['id', 'ID'],
|
||||
['name', '标识'],
|
||||
['title', '名称'],
|
||||
['remark', '描述'],
|
||||
['module', '所属模块', 'callback', function($module, $list_module){
|
||||
return isset($list_module[$module]) ? $list_module[$module] : '未知';
|
||||
}, $list_module],
|
||||
['status', '状态', 'switch'],
|
||||
['right_button', '操作', 'btn']
|
||||
])
|
||||
->autoAdd($fields, '', true, true) // 添加自动新增按钮
|
||||
->autoEdit($fields, '', true, true) // 添加自动编辑按钮
|
||||
->addTopButtons('enable,disable,delete') // 批量添加顶部按钮
|
||||
->addRightButtons('delete') // 批量添加右侧按钮
|
||||
->addFilter('module', $list_module)
|
||||
->setRowList($data_list) // 设置表格数据
|
||||
->fetch(); // 渲染模板
|
||||
}
|
||||
}
|
||||
499
application/admin/controller/Admin.php
Normal file
499
application/admin/controller/Admin.php
Normal file
@ -0,0 +1,499 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Common;
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\admin\model\Menu as MenuModel;
|
||||
use app\admin\model\Module as ModuleModel;
|
||||
use app\admin\model\Icon as IconModel;
|
||||
use app\user\model\Role as RoleModel;
|
||||
use app\user\model\Message as MessageModel;
|
||||
use think\facade\Cache;
|
||||
use think\Db;
|
||||
use think\facade\App;
|
||||
use think\helper\Hash;
|
||||
|
||||
/**
|
||||
* 后台公共控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Admin extends Common
|
||||
{
|
||||
/**
|
||||
* 初始化
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
// 是否拒绝ie浏览器访问
|
||||
if (config('system.deny_ie') && get_browser_type() == 'ie') {
|
||||
$this->redirect('admin/ie/index');
|
||||
}
|
||||
|
||||
// 判断是否登录,并定义用户ID常量
|
||||
defined('UID') or define('UID', $this->isLogin());
|
||||
|
||||
// 设置当前角色菜单节点权限
|
||||
role_auth();
|
||||
|
||||
// 检查权限
|
||||
if (!RoleModel::checkAuth()) $this->error('权限不足!');
|
||||
|
||||
// 设置分页参数
|
||||
$this->setPageParam();
|
||||
|
||||
// 如果不是ajax请求,则读取菜单
|
||||
if (!$this->request->isAjax()) {
|
||||
// 读取顶部菜单
|
||||
$this->assign('_top_menus', MenuModel::getTopMenu(config('top_menu_max'), '_top_menus'));
|
||||
// 读取全部顶级菜单
|
||||
$this->assign('_top_menus_all', MenuModel::getTopMenu('', '_top_menus_all'));
|
||||
// 获取侧边栏菜单
|
||||
$this->assign('_sidebar_menus', MenuModel::getSidebarMenu());
|
||||
// 获取面包屑导航
|
||||
$this->assign('_location', MenuModel::getLocation('', true));
|
||||
// 获取当前用户未读消息数量
|
||||
$this->assign('_message', MessageModel::getMessageCount());
|
||||
// 获取自定义图标
|
||||
$this->assign('_icons', IconModel::getUrls());
|
||||
// 构建侧栏
|
||||
$data = [
|
||||
'table' => 'admin_config', // 表名或模型名
|
||||
'prefix' => 1,
|
||||
'module' => 'admin',
|
||||
'controller' => 'system',
|
||||
'action' => 'quickedit',
|
||||
];
|
||||
$table_token = substr(sha1('_aside'), 0, 8);
|
||||
session($table_token, $data);
|
||||
$settings = [
|
||||
[
|
||||
'title' => '站点开关',
|
||||
'tips' => '站点关闭后将不能访问',
|
||||
'checked' => Db::name('admin_config')->where('id', 1)->value('value'),
|
||||
'table' => $table_token,
|
||||
'id' => 1,
|
||||
'field' => 'value'
|
||||
]
|
||||
];
|
||||
ZBuilder::make('aside')
|
||||
->addBlock('switch', '系统设置', $settings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前操作模型
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return object|\think\db\Query
|
||||
*/
|
||||
final protected function getCurrModel()
|
||||
{
|
||||
$table_token = input('param._t', '');
|
||||
$module = $this->request->module();
|
||||
$controller = parse_name($this->request->controller());
|
||||
|
||||
$table_token == '' && $this->error('缺少参数');
|
||||
!session('?'.$table_token) && $this->error('参数错误');
|
||||
|
||||
$table_data = session($table_token);
|
||||
$table = $table_data['table'];
|
||||
|
||||
$Model = null;
|
||||
if ($table_data['prefix'] == 2) {
|
||||
// 使用模型
|
||||
try {
|
||||
$Model = App::model($table);
|
||||
} catch (\Exception $e) {
|
||||
$this->error('找不到模型:'.$table);
|
||||
}
|
||||
} else {
|
||||
// 使用DB类
|
||||
$table == '' && $this->error('缺少表名');
|
||||
if ($table_data['module'] != $module || $table_data['controller'] != $controller) {
|
||||
$this->error('非法操作');
|
||||
}
|
||||
|
||||
$Model = $table_data['prefix'] == 0 ? Db::table($table) : Db::name($table);
|
||||
}
|
||||
|
||||
return $Model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置分页参数
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
final protected function setPageParam()
|
||||
{
|
||||
_system_check();
|
||||
$list_rows = input('?param.list_rows') ? input('param.list_rows') : config('list_rows');
|
||||
config('paginate.list_rows', $list_rows);
|
||||
config('paginate.query', input('get.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否登录,没有登录则跳转到登录页面
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return int
|
||||
*/
|
||||
final protected function isLogin()
|
||||
{
|
||||
// 判断是否登录
|
||||
if ($uid = is_signin()) {
|
||||
// 已登录
|
||||
return $uid;
|
||||
} else {
|
||||
// 未登录
|
||||
$this->redirect('user/publics/signin');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function disable($record = [])
|
||||
{
|
||||
return $this->setStatus('disable', $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function enable($record = [])
|
||||
{
|
||||
return $this->setStatus('enable', $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function delete($record = [])
|
||||
{
|
||||
return $this->setStatus('delete', $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速编辑
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function quickEdit($record = [])
|
||||
{
|
||||
$field = input('post.name', '');
|
||||
$value = input('post.value', '');
|
||||
$type = input('post.type', '');
|
||||
$id = input('post.pk', '');
|
||||
$validate = input('post.validate', '');
|
||||
$validate_fields = input('post.validate_fields', '');
|
||||
|
||||
$field == '' && $this->error('缺少字段名');
|
||||
$id == '' && $this->error('缺少主键值');
|
||||
|
||||
$Model = $this->getCurrModel();
|
||||
$protect_table = [
|
||||
'__ADMIN_USER__',
|
||||
'__ADMIN_ROLE__',
|
||||
config('database.prefix').'admin_user',
|
||||
config('database.prefix').'admin_role',
|
||||
];
|
||||
|
||||
// 验证是否操作管理员
|
||||
if (in_array($Model->getTable(), $protect_table) && $id == 1) {
|
||||
$this->error('禁止操作超级管理员');
|
||||
}
|
||||
|
||||
// 验证器
|
||||
if ($validate != '') {
|
||||
$validate_fields = array_flip(explode(',', $validate_fields));
|
||||
if (isset($validate_fields[$field])) {
|
||||
$result = $this->validate([$field => $value], $validate.'.'.$field);
|
||||
if (true !== $result) $this->error($result);
|
||||
}
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
// 日期时间需要转为时间戳
|
||||
case 'combodate':
|
||||
$value = strtotime($value);
|
||||
break;
|
||||
// 开关
|
||||
case 'switch':
|
||||
$value = $value == 'true' ? 1 : 0;
|
||||
break;
|
||||
// 开关
|
||||
case 'password':
|
||||
$value = Hash::make((string)$value);
|
||||
break;
|
||||
}
|
||||
|
||||
// 主键名
|
||||
$pk = $Model->getPk();
|
||||
$result = $Model->where($pk, $id)->setField($field, $value);
|
||||
|
||||
cache('hook_plugins', null);
|
||||
cache('system_config', null);
|
||||
cache('access_menus', null);
|
||||
if (false !== $result) {
|
||||
// 记录行为日志
|
||||
if (!empty($record)) {
|
||||
call_user_func_array('action_log', $record);
|
||||
}
|
||||
$this->success('操作成功');
|
||||
} else {
|
||||
$this->error('操作失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动创建添加页面
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
// 获取表单项
|
||||
$cache_name = $this->request->module().'/'.parse_name($this->request->controller()).'/add';
|
||||
$cache_name = strtolower($cache_name);
|
||||
$form = Cache::get($cache_name, []);
|
||||
if (!$form) {
|
||||
$this->error('自动新增数据不存在,请重新打开此页面');
|
||||
}
|
||||
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
// 表单数据
|
||||
$data = $this->request->post();
|
||||
$_pop = $this->request->get('_pop');
|
||||
|
||||
// 验证
|
||||
if ($form['validate'] != '') {
|
||||
$result = $this->validate($data, $form['validate']);
|
||||
if(true !== $result) $this->error($result);
|
||||
}
|
||||
|
||||
// 是否需要自动插入时间
|
||||
if ($form['auto_time'] != '') {
|
||||
$now_time = $this->request->time();
|
||||
foreach ($form['auto_time'] as $item) {
|
||||
if (strpos($item, '|')) {
|
||||
list($item, $format) = explode('|', $item);
|
||||
$data[$item] = date($format, $now_time);
|
||||
} else {
|
||||
$data[$item] = $form['format'] != '' ? date($form['format'], $now_time) : $now_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 插入数据
|
||||
if (Db::name($form['table'])->insert($data)) {
|
||||
if ($_pop == 1) {
|
||||
$this->success('新增成功', null, '_parent_reload');
|
||||
} else {
|
||||
$this->success('新增成功', $form['go_back']);
|
||||
}
|
||||
} else {
|
||||
$this->error('新增失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示添加页面
|
||||
return ZBuilder::make('form')
|
||||
->addFormItems($form['items'])
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动创建编辑页面
|
||||
* @param string $id 主键值
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function edit($id = '')
|
||||
{
|
||||
if ($id === '') $this->error('参数错误');
|
||||
|
||||
// 获取表单项
|
||||
$cache_name = $this->request->module().'/'.parse_name($this->request->controller()).'/edit';
|
||||
$cache_name = strtolower($cache_name);
|
||||
$form = Cache::get($cache_name, []);
|
||||
if (!$form) {
|
||||
$this->error('自动编辑数据不存在,请重新打开此页面');
|
||||
}
|
||||
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
// 表单数据
|
||||
$data = $this->request->post();
|
||||
$_pop = $this->request->get('_pop');
|
||||
|
||||
// 验证
|
||||
if ($form['validate'] != '') {
|
||||
$result = $this->validate($data, $form['validate']);
|
||||
if(true !== $result) $this->error($result);
|
||||
}
|
||||
|
||||
// 是否需要自动插入时间
|
||||
if ($form['auto_time'] != '') {
|
||||
$now_time = $this->request->time();
|
||||
foreach ($form['auto_time'] as $item) {
|
||||
if (strpos($item, '|')) {
|
||||
list($item, $format) = explode('|', $item);
|
||||
$data[$item] = date($format, $now_time);
|
||||
} else {
|
||||
$data[$item] = $form['format'] != '' ? date($form['format'], $now_time) : $now_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新数据
|
||||
if (false !== Db::name($form['table'])->update($data)) {
|
||||
if ($_pop == 1) {
|
||||
$this->success('编辑成功', null, '_parent_reload');
|
||||
} else {
|
||||
$this->success('编辑成功', $form['go_back']);
|
||||
}
|
||||
} else {
|
||||
$this->error('编辑失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
$info = Db::name($form['table'])->find($id);
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('编辑')
|
||||
->addFormItems($form['items'])
|
||||
->setFormData($info)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态
|
||||
* 禁用、启用、删除都是调用这个内部方法
|
||||
* @param string $type 操作类型:enable,disable,delete
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function setStatus($type = '', $record = [])
|
||||
{
|
||||
$ids = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
|
||||
$ids = (array)$ids;
|
||||
$field = input('param.field', 'status');
|
||||
|
||||
empty($ids) && $this->error('缺少主键');
|
||||
|
||||
$Model = $this->getCurrModel();
|
||||
$protect_table = [
|
||||
'__ADMIN_USER__',
|
||||
'__ADMIN_ROLE__',
|
||||
'__ADMIN_MODULE__',
|
||||
config('database.prefix').'admin_user',
|
||||
config('database.prefix').'admin_role',
|
||||
config('database.prefix').'admin_module',
|
||||
];
|
||||
|
||||
// 禁止操作核心表的主要数据
|
||||
if (in_array($Model->getTable(), $protect_table) && in_array('1', $ids)) {
|
||||
$this->error('禁止操作');
|
||||
}
|
||||
|
||||
// 主键名称
|
||||
$pk = $Model->getPk();
|
||||
$map = [
|
||||
[$pk, 'in', $ids]
|
||||
];
|
||||
|
||||
$result = false;
|
||||
switch ($type) {
|
||||
case 'disable': // 禁用
|
||||
$result = $Model->where($map)->setField($field, 0);
|
||||
break;
|
||||
case 'enable': // 启用
|
||||
$result = $Model->where($map)->setField($field, 1);
|
||||
break;
|
||||
case 'delete': // 删除
|
||||
$result = $Model->where($map)->delete();
|
||||
break;
|
||||
default:
|
||||
$this->error('非法操作');
|
||||
break;
|
||||
}
|
||||
|
||||
if (false !== $result) {
|
||||
Cache::clear();
|
||||
// 记录行为日志
|
||||
if (!empty($record)) {
|
||||
call_user_func_array('action_log', $record);
|
||||
}
|
||||
$this->success('操作成功');
|
||||
} else {
|
||||
$this->error('操作失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块设置
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function moduleConfig()
|
||||
{
|
||||
// 当前模块名
|
||||
$module = $this->request->module();
|
||||
|
||||
// 保存
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post();
|
||||
$data = json_encode($data);
|
||||
|
||||
if (false !== ModuleModel::where('name', $module)->update(['config' => $data])) {
|
||||
cache('module_config_'.$module, null);
|
||||
$this->success('更新成功');
|
||||
} else {
|
||||
$this->error('更新失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 模块配置信息
|
||||
$module_info = ModuleModel::getInfoFromFile($module);
|
||||
$config = $module_info['config'];
|
||||
$trigger = isset($module_info['trigger']) ? $module_info['trigger'] : [];
|
||||
|
||||
// 数据库内的模块信息
|
||||
$db_config = ModuleModel::where('name', $module)->value('config');
|
||||
$db_config = json_decode($db_config, true);
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('模块设置')
|
||||
->addFormItems($config)
|
||||
->setFormdata($db_config) // 设置表格数据
|
||||
->setTrigger($trigger) // 设置触发
|
||||
->fetch();
|
||||
}
|
||||
}
|
||||
235
application/admin/controller/Ajax.php
Normal file
235
application/admin/controller/Ajax.php
Normal file
@ -0,0 +1,235 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Common;
|
||||
use app\admin\model\Menu as MenuModel;
|
||||
use app\admin\model\Attachment as AttachmentModel;
|
||||
use think\facade\Cache;
|
||||
use think\Db;
|
||||
|
||||
/**
|
||||
* 用于处理ajax请求的控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Ajax extends Common
|
||||
{
|
||||
/**
|
||||
* 获取联动数据
|
||||
* @param string $token token
|
||||
* @param int $pid 父级ID
|
||||
* @param string $pidkey 父级id字段名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getLevelData($token = '', $pid = 0, $pidkey = 'pid')
|
||||
{
|
||||
if ($token == '') {
|
||||
return json(['code' => 0, 'msg' => '缺少Token']);
|
||||
}
|
||||
|
||||
$token_data = session($token);
|
||||
$table = $token_data['table'];
|
||||
$option = $token_data['option'];
|
||||
$key = $token_data['key'];
|
||||
|
||||
$data_list = Db::name($table)->where($pidkey, $pid)->column($option, $key);
|
||||
|
||||
if ($data_list === false) {
|
||||
return json(['code' => 0, 'msg' => '查询失败']);
|
||||
}
|
||||
|
||||
if ($data_list) {
|
||||
$result = [
|
||||
'code' => 1,
|
||||
'msg' => '请求成功',
|
||||
'list' => format_linkage($data_list)
|
||||
];
|
||||
return json($result);
|
||||
} else {
|
||||
return json(['code' => 0, 'msg' => '查询不到数据']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取筛选数据
|
||||
* @param string $token
|
||||
* @param array $map 查询条件
|
||||
* @param string $options 选项,用于显示转换
|
||||
* @param string $list 选项缓存列表名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getFilterList($token = '', $map = [], $options = '', $list = '')
|
||||
{
|
||||
if ($list != '') {
|
||||
$result = [
|
||||
'code' => 1,
|
||||
'msg' => '请求成功',
|
||||
'list' => Cache::get($list)
|
||||
];
|
||||
return json($result);
|
||||
}
|
||||
if ($token == '') {
|
||||
return json(['code' => 0, 'msg' => '缺少Token']);
|
||||
}
|
||||
|
||||
$token_data = session($token);
|
||||
$table = $token_data['table'];
|
||||
$field = $token_data['field'];
|
||||
|
||||
if ($field == '') {
|
||||
return json(['code' => 0, 'msg' => '缺少字段']);
|
||||
}
|
||||
if (!empty($map) && is_array($map)) {
|
||||
foreach ($map as &$item) {
|
||||
if (is_array($item)) {
|
||||
foreach ($item as &$value) {
|
||||
$value = trim($value);
|
||||
}
|
||||
} else {
|
||||
$item = trim($item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($table, '/')) {
|
||||
$data_list = model($table)->where($map)->group($field)->column($field);
|
||||
} else {
|
||||
$data_list = Db::name($table)->where($map)->group($field)->column($field);
|
||||
}
|
||||
|
||||
if ($data_list === false) {
|
||||
return json(['code' => 0, 'msg' => '查询失败']);
|
||||
}
|
||||
|
||||
if ($data_list) {
|
||||
if ($options != '') {
|
||||
// 从缓存获取选项数据
|
||||
$options = cache($options);
|
||||
if ($options) {
|
||||
$temp_data_list = [];
|
||||
foreach ($data_list as $item) {
|
||||
$temp_data_list[$item] = isset($options[$item]) ? $options[$item] : '';
|
||||
}
|
||||
$data_list = $temp_data_list;
|
||||
} else {
|
||||
$data_list = parse_array($data_list);
|
||||
}
|
||||
} else {
|
||||
$data_list = parse_array($data_list);
|
||||
}
|
||||
|
||||
$result = [
|
||||
'code' => 1,
|
||||
'msg' => '请求成功',
|
||||
'list' => $data_list
|
||||
];
|
||||
return json($result);
|
||||
} else {
|
||||
return json(['code' => 0, 'msg' => '查询不到数据']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定模块的菜单
|
||||
* @param string $module 模块名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function getModuleMenus($module = '')
|
||||
{
|
||||
if (!is_signin()) {
|
||||
$this->error('请先登录');
|
||||
}
|
||||
$menus = MenuModel::getMenuTree(0, '', $module);
|
||||
$result = [
|
||||
'code' => 1,
|
||||
'msg' => '请求成功',
|
||||
'list' => format_linkage($menus)
|
||||
];
|
||||
return json($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配色方案
|
||||
* @param string $theme 配色名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function setTheme($theme = '') {
|
||||
if (!is_signin()) {
|
||||
$this->error('请先登录');
|
||||
}
|
||||
$themes = ['default', 'amethyst', 'city', 'flat', 'modern', 'smooth'];
|
||||
if (!in_array($theme, $themes)) {
|
||||
$this->error('非法操作');
|
||||
}
|
||||
$map['name'] = 'system_color';
|
||||
$map['group'] = 'system';
|
||||
|
||||
if (Db::name('admin_config')->where($map)->setField('value', $theme)) {
|
||||
$this->success('设置成功');
|
||||
} else {
|
||||
$this->error('设置失败,请重试');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取侧栏菜单
|
||||
* @param string $module_id 模块id
|
||||
* @param string $module 模型名
|
||||
* @param string $controller 控制器名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return string
|
||||
*/
|
||||
public function getSidebarMenu($module_id = '', $module = '', $controller = '')
|
||||
{
|
||||
if (!is_signin()) {
|
||||
$this->error('登录已失效,请重新登录', 'user/publics/signin');
|
||||
}
|
||||
|
||||
role_auth();
|
||||
$menus = MenuModel::getSidebarMenu($module_id, $module, $controller);
|
||||
|
||||
$output = '';
|
||||
foreach ($menus as $key => $menu) {
|
||||
if (!empty($menu['url_value'])) {
|
||||
$output = $menu['url_value'];
|
||||
break;
|
||||
}
|
||||
if (!empty($menu['child'])) {
|
||||
$output = $menu['child'][0]['url_value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->success('获取成功', null, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查附件是否存在
|
||||
* @param string $md5 文件md5
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function check($md5 = '')
|
||||
{
|
||||
$md5 == '' && $this->error('参数错误');
|
||||
|
||||
// 判断附件是否已存在
|
||||
if ($file_exists = AttachmentModel::get(['md5' => $md5])) {
|
||||
if ($file_exists['driver'] == 'local') {
|
||||
$file_path = PUBLIC_PATH.$file_exists['path'];
|
||||
} else {
|
||||
$file_path = $file_exists['path'];
|
||||
}
|
||||
return json([
|
||||
'code' => 1,
|
||||
'info' => '上传成功',
|
||||
'class' => 'success',
|
||||
'id' => $file_exists['id'],
|
||||
'path' => $file_path
|
||||
]);
|
||||
} else {
|
||||
$this->error('文件不存在');
|
||||
}
|
||||
}
|
||||
}
|
||||
796
application/admin/controller/Attachment.php
Normal file
796
application/admin/controller/Attachment.php
Normal file
@ -0,0 +1,796 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\admin\model\Attachment as AttachmentModel;
|
||||
use think\Image;
|
||||
use think\File;
|
||||
use think\facade\Hook;
|
||||
use think\Db;
|
||||
use think\facade\Env;
|
||||
|
||||
/**
|
||||
* 附件控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Attachment extends Admin
|
||||
{
|
||||
/**
|
||||
* 附件列表
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// 查询
|
||||
$map = $this->getMap();
|
||||
|
||||
// 数据列表
|
||||
$data_list = AttachmentModel::where($map)->order('sort asc,id desc')->paginate();
|
||||
foreach ($data_list as $key => &$value) {
|
||||
if (in_array(strtolower($value['ext']), ['jpg', 'jpeg', 'png', 'gif', 'bmp'])) {
|
||||
if ($value['driver'] == 'local') {
|
||||
$thumb = $value['thumb'] != '' ? $value['thumb'] : $value['path'];
|
||||
$value['type'] = '<div class="js-gallery"><img class="image" title="点击查看大图" data-original="'. PUBLIC_PATH . $value['path'].'" src="'. PUBLIC_PATH . $thumb.'"></div>';
|
||||
} else {
|
||||
$value['type'] = '<div class="js-gallery"><img class="image" title="点击查看大图" data-original="'. $value['path'].'" src="'. $value['path'].'"></div>';
|
||||
}
|
||||
} else {
|
||||
if ($value['driver'] == 'local') {
|
||||
$path = PUBLIC_PATH. $value['path'];
|
||||
} else {
|
||||
$path = $value['path'];
|
||||
}
|
||||
if (is_file('.'.config('public_static_path').'admin/img/files/'.$value['ext'].'.png')) {
|
||||
$value['type'] = '<a href="'. $path.'"
|
||||
data-toggle="tooltip" title="点击下载">
|
||||
<img class="image" src="'.config('public_static_path').'admin/img/files/'.$value['ext'].'.png"></a>';
|
||||
} else {
|
||||
$value['type'] = '<a href="'. $path.'"
|
||||
data-toggle="tooltip" title="点击下载">
|
||||
<img class="image" src="'.config('public_static_path').'admin/img/files/file.png"></a>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用ZBuilder快速创建数据表格
|
||||
return ZBuilder::make('table')
|
||||
->setSearch(['name' => '名称']) // 设置搜索框
|
||||
->addColumns([ // 批量添加数据列
|
||||
['id', 'ID'],
|
||||
['type', '类型'],
|
||||
['name', '名称'],
|
||||
['size', '大小', 'byte'],
|
||||
['driver', '上传驱动', parse_attr(Db::name('admin_config')->where('name', 'upload_driver')->value('options'))],
|
||||
['create_time', '上传时间', 'datetime'],
|
||||
['status', '状态', 'switch'],
|
||||
['right_button', '操作', 'btn']
|
||||
])
|
||||
->addTopButtons('enable,disable,delete') // 批量添加顶部按钮
|
||||
->addRightButtons('delete') // 批量添加右侧按钮
|
||||
->setRowList($data_list) // 设置表格数据
|
||||
->fetch(); // 渲染模板
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传附件
|
||||
* @param string $dir 保存的目录:images,files,videos,voices
|
||||
* @param string $from 来源,wangeditor:wangEditor编辑器, ueditor:ueditor编辑器, editormd:editormd编辑器等
|
||||
* @param string $module 来自哪个模块
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function upload($dir = '', $from = '', $module = '')
|
||||
{
|
||||
// 临时取消执行时间限制
|
||||
set_time_limit(0);
|
||||
if ($dir == '') $this->error('没有指定上传目录');
|
||||
if ($from == 'ueditor') return $this->ueditor();
|
||||
if ($from == 'jcrop') return $this->jcrop();
|
||||
return $this->saveFile($dir, $from, $module);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存附件
|
||||
* @param string $dir 附件存放的目录
|
||||
* @param string $from 来源
|
||||
* @param string $module 来自哪个模块
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return string|\think\response\Json
|
||||
*/
|
||||
private function saveFile($dir = '', $from = '', $module = '')
|
||||
{
|
||||
// 附件大小限制
|
||||
$size_limit = $dir == 'images' ? config('upload_image_size') : config('upload_file_size');
|
||||
$size_limit = $size_limit * 1024;
|
||||
// 附件类型限制
|
||||
$ext_limit = $dir == 'images' ? config('upload_image_ext') : config('upload_file_ext');
|
||||
$ext_limit = $ext_limit != '' ? parse_attr($ext_limit) : '';
|
||||
// 缩略图参数
|
||||
$thumb = $this->request->post('thumb', '');
|
||||
// 水印参数
|
||||
$watermark = $this->request->post('watermark', '');
|
||||
|
||||
// 获取附件数据
|
||||
$callback = '';
|
||||
switch ($from) {
|
||||
case 'editormd':
|
||||
$file_input_name = 'editormd-image-file';
|
||||
break;
|
||||
case 'ckeditor':
|
||||
$file_input_name = 'upload';
|
||||
$callback = $this->request->get('CKEditorFuncNum');
|
||||
break;
|
||||
case 'ueditor_scrawl':
|
||||
return $this->saveScrawl();
|
||||
break;
|
||||
default:
|
||||
$file_input_name = 'file';
|
||||
}
|
||||
$file = $this->request->file($file_input_name);
|
||||
|
||||
// 判断附件是否已存在
|
||||
if ($file_exists = AttachmentModel::get(['md5' => $file->hash('md5')])) {
|
||||
if ($file_exists['driver'] == 'local') {
|
||||
$file_path = PUBLIC_PATH. $file_exists['path'];
|
||||
} else {
|
||||
$file_path = $file_exists['path'];
|
||||
}
|
||||
|
||||
// 附件已存在
|
||||
return $this->uploadSuccess($from, $file_path, $file_exists['name'], $file_exists['id'], $callback);
|
||||
}
|
||||
|
||||
// 判断附件大小是否超过限制
|
||||
if ($size_limit > 0 && ($file->getInfo('size') > $size_limit)) {
|
||||
return $this->uploadError($from, '附件过大', $callback);
|
||||
}
|
||||
|
||||
// 判断附件格式是否符合
|
||||
$file_name = $file->getInfo('name');
|
||||
$file_ext = strtolower(substr($file_name, strrpos($file_name, '.')+1));
|
||||
$error_msg = '';
|
||||
if ($ext_limit == '') {
|
||||
$error_msg = '获取文件信息失败!';
|
||||
}
|
||||
if ($file->getMime() == 'text/x-php' || $file->getMime() == 'text/html') {
|
||||
$error_msg = '禁止上传非法文件!';
|
||||
}
|
||||
if (preg_grep("/php/i", $ext_limit)) {
|
||||
$error_msg = '禁止上传非法文件!';
|
||||
}
|
||||
if (!preg_grep("/$file_ext/i", $ext_limit)) {
|
||||
$error_msg = '附件类型不正确!';
|
||||
}
|
||||
|
||||
if ($error_msg != '') {
|
||||
// 上传错误
|
||||
return $this->uploadError($from, $error_msg, $callback);
|
||||
}
|
||||
|
||||
// 附件上传钩子,用于第三方文件上传扩展
|
||||
if (config('upload_driver') != 'local') {
|
||||
$hook_result = Hook::listen('upload_attachment', ['file' => $file, 'from' => $from, 'module' => $module], true);
|
||||
if (false !== $hook_result) {
|
||||
return $hook_result;
|
||||
}
|
||||
}
|
||||
|
||||
// 移动到框架应用根目录/uploads/ 目录下
|
||||
$info = $file->move(config('upload_path') . DIRECTORY_SEPARATOR . $dir);
|
||||
if($info){
|
||||
// 缩略图路径
|
||||
$thumb_path_name = '';
|
||||
// 图片宽度
|
||||
$img_width = '';
|
||||
// 图片高度
|
||||
$img_height = '';
|
||||
if ($dir == 'images') {
|
||||
$img = Image::open($info);
|
||||
$img_width = $img->width();
|
||||
$img_height = $img->height();
|
||||
// 水印功能
|
||||
if ($watermark == '') {
|
||||
if (config('upload_thumb_water') == 1 && config('upload_thumb_water_pic') > 0) {
|
||||
$this->create_water($info->getRealPath(), config('upload_thumb_water_pic'));
|
||||
}
|
||||
} else {
|
||||
if (strtolower($watermark) != 'close') {
|
||||
list($watermark_img, $watermark_pos, $watermark_alpha) = explode('|', $watermark);
|
||||
$this->create_water($info->getRealPath(), $watermark_img, $watermark_pos, $watermark_alpha);
|
||||
}
|
||||
}
|
||||
|
||||
// 生成缩略图
|
||||
if ($thumb == '') {
|
||||
if (config('upload_image_thumb') != '') {
|
||||
$thumb_path_name = $this->create_thumb($info, $info->getPathInfo()->getfileName(), $info->getFilename());
|
||||
}
|
||||
} else {
|
||||
if (strtolower($thumb) != 'close') {
|
||||
list($thumb_size, $thumb_type) = explode('|', $thumb);
|
||||
$thumb_path_name = $this->create_thumb($info, $info->getPathInfo()->getfileName(), $info->getFilename(), $thumb_size, $thumb_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取附件信息
|
||||
$file_info = [
|
||||
'uid' => session('user_auth.uid'),
|
||||
'name' => $file->getInfo('name'),
|
||||
'mime' => $file->getInfo('type'),
|
||||
'path' => 'uploads/' . $dir . '/' . str_replace('\\', '/', $info->getSaveName()),
|
||||
'ext' => $info->getExtension(),
|
||||
'size' => $info->getSize(),
|
||||
'md5' => $info->hash('md5'),
|
||||
'sha1' => $info->hash('sha1'),
|
||||
'thumb' => $thumb_path_name,
|
||||
'module' => $module,
|
||||
'width' => $img_width,
|
||||
'height' => $img_height,
|
||||
];
|
||||
|
||||
// 写入数据库
|
||||
if ($file_add = AttachmentModel::create($file_info)) {
|
||||
$file_path = PUBLIC_PATH. $file_info['path'];
|
||||
return $this->uploadSuccess($from, $file_path, $file_info['name'], $file_add['id'], $callback);
|
||||
} else {
|
||||
return $this->uploadError($from, '上传失败', $callback);
|
||||
}
|
||||
}else{
|
||||
return $this->uploadError($from, $file->getError(), $callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理ueditor上传
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return string|\think\response\Json
|
||||
*/
|
||||
private function ueditor(){
|
||||
$action = $this->request->get('action');
|
||||
$config_file = './static/libs/ueditor/php/config.json';
|
||||
$config = json_decode(preg_replace("/\/\*[\s\S]+?\*\//", "", file_get_contents($config_file)), true);
|
||||
switch ($action) {
|
||||
/* 获取配置信息 */
|
||||
case 'config':
|
||||
$result = $config;
|
||||
break;
|
||||
|
||||
/* 上传图片 */
|
||||
case 'uploadimage':
|
||||
return $this->saveFile('images', 'ueditor');
|
||||
break;
|
||||
/* 上传涂鸦 */
|
||||
case 'uploadscrawl':
|
||||
return $this->saveFile('images', 'ueditor_scrawl');
|
||||
break;
|
||||
|
||||
/* 上传视频 */
|
||||
case 'uploadvideo':
|
||||
return $this->saveFile('videos', 'ueditor');
|
||||
break;
|
||||
|
||||
/* 上传附件 */
|
||||
case 'uploadfile':
|
||||
return $this->saveFile('files', 'ueditor');
|
||||
break;
|
||||
|
||||
/* 列出图片 */
|
||||
case 'listimage':
|
||||
return $this->showFile('listimage', $config);
|
||||
break;
|
||||
|
||||
/* 列出附件 */
|
||||
case 'listfile':
|
||||
return $this->showFile('listfile', $config);
|
||||
break;
|
||||
|
||||
/* 抓取远程附件 */
|
||||
// case 'catchimage':
|
||||
// $result = include("action_crawler.php");
|
||||
// break;
|
||||
|
||||
default:
|
||||
$result = ['state' => '请求地址出错'];
|
||||
break;
|
||||
}
|
||||
|
||||
/* 输出结果 */
|
||||
if (isset($_GET["callback"])) {
|
||||
if (preg_match("/^[\w_]+$/", $_GET["callback"])) {
|
||||
return htmlspecialchars($_GET["callback"]) . '(' . $result . ')';
|
||||
} else {
|
||||
return json(['state' => 'callback参数不合法']);
|
||||
}
|
||||
} else {
|
||||
return json($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存涂鸦(ueditor)
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
private function saveScrawl()
|
||||
{
|
||||
$file = $this->request->post('file');
|
||||
$file_content = base64_decode($file);
|
||||
$file_name = md5($file) . '.jpg';
|
||||
$dir = config('upload_path') . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . date('Ymd', $this->request->time());
|
||||
$file_path = $dir . DIRECTORY_SEPARATOR . $file_name;
|
||||
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
|
||||
if (false === file_put_contents($file_path, $file_content)) {
|
||||
return json(['state' => '涂鸦上传出错']);
|
||||
}
|
||||
|
||||
$file = new File($file_path);
|
||||
$img = Image::open($file);
|
||||
$file_info = [
|
||||
'uid' => session('user_auth.uid'),
|
||||
'name' => $file_name,
|
||||
'mime' => 'image/png',
|
||||
'path' => 'uploads/images/' . date('Ymd', $this->request->time()) . '/' . $file_name,
|
||||
'ext' => 'png',
|
||||
'size' => $file->getSize(),
|
||||
'md5' => $file->hash('md5'),
|
||||
'sha1' => $file->hash('sha1'),
|
||||
'module' => $this->request->module(),
|
||||
'width' => $img->width(),
|
||||
'height' => $img->height()
|
||||
];
|
||||
|
||||
if ($file_add = AttachmentModel::create($file_info)) {
|
||||
// 返回成功信息
|
||||
return json([
|
||||
"state" => "SUCCESS", // 上传状态,上传成功时必须返回"SUCCESS"
|
||||
"url" => PUBLIC_PATH. $file_info['path'], // 返回的地址
|
||||
"title" => $file_info['name'], // 附件名
|
||||
]);
|
||||
} else {
|
||||
return json(['state' => '涂鸦上传出错']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示附件列表(ueditor)
|
||||
* @param string $type 类型
|
||||
* @param $config
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
private function showFile($type, $config){
|
||||
/* 判断类型 */
|
||||
switch ($type) {
|
||||
/* 列出附件 */
|
||||
case 'listfile':
|
||||
$allowFiles = $config['fileManagerAllowFiles'];
|
||||
$listSize = $config['fileManagerListSize'];
|
||||
$path = realpath(config('upload_path') .'/files/');
|
||||
break;
|
||||
/* 列出图片 */
|
||||
case 'listimage':
|
||||
default:
|
||||
$allowFiles = $config['imageManagerAllowFiles'];
|
||||
$listSize = $config['imageManagerListSize'];
|
||||
$path = realpath(config('upload_path') .'/images/');
|
||||
}
|
||||
$allowFiles = substr(str_replace(".", "|", join("", $allowFiles)), 1);
|
||||
|
||||
/* 获取参数 */
|
||||
$size = isset($_GET['size']) ? htmlspecialchars($_GET['size']) : $listSize;
|
||||
$start = isset($_GET['start']) ? htmlspecialchars($_GET['start']) : 0;
|
||||
$end = $start + $size;
|
||||
|
||||
/* 获取附件列表 */
|
||||
$files = $this->getfiles($path, $allowFiles);
|
||||
if (!count($files)) {
|
||||
return json(array(
|
||||
"state" => "no match file",
|
||||
"list" => array(),
|
||||
"start" => $start,
|
||||
"total" => count($files)
|
||||
));
|
||||
}
|
||||
|
||||
/* 获取指定范围的列表 */
|
||||
$len = count($files);
|
||||
for ($i = min($end, $len) - 1, $list = array(); $i < $len && $i >= 0 && $i >= $start; $i--){
|
||||
$list[] = $files[$i];
|
||||
}
|
||||
//倒序
|
||||
//for ($i = $end, $list = array(); $i < $len && $i < $end; $i++){
|
||||
// $list[] = $files[$i];
|
||||
//}
|
||||
|
||||
/* 返回数据 */
|
||||
$result = array(
|
||||
"state" => "SUCCESS",
|
||||
"list" => $list,
|
||||
"start" => $start,
|
||||
"total" => count($files)
|
||||
);
|
||||
|
||||
return json($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Jcrop图片裁剪
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
private function jcrop()
|
||||
{
|
||||
$file_path = $this->request->post('path', '');
|
||||
$cut_info = $this->request->post('cut', '');
|
||||
$thumb = $this->request->post('thumb', '');
|
||||
$watermark = $this->request->post('watermark', '');
|
||||
$module = $this->request->param('module', '');
|
||||
|
||||
// 上传图片
|
||||
if ($file_path == '') {
|
||||
$file = $this->request->file('file');
|
||||
|
||||
// 附件类型限制
|
||||
$ext_limit = config('upload_image_ext');
|
||||
$ext_limit = $ext_limit != '' ? parse_attr($ext_limit) : '';
|
||||
|
||||
// 判断附件格式是否符合
|
||||
$file_name = $file->getInfo('name');
|
||||
$file_ext = strtolower(substr($file_name, strrpos($file_name, '.')+1));
|
||||
|
||||
if ($ext_limit == '') {
|
||||
$this->error('获取文件信息失败!');
|
||||
}
|
||||
if (strtolower($file_ext) == 'php') {
|
||||
$this->error('禁止上传非法文件!');
|
||||
}
|
||||
if ($file->getMime() == 'text/x-php' || $file->getMime() == 'text/html') {
|
||||
$this->error('禁止上传非法文件!');
|
||||
}
|
||||
if (preg_grep("/php/i", $ext_limit)) {
|
||||
$this->error('禁止上传非法文件!');
|
||||
}
|
||||
if (!preg_grep("/$file_ext/i", $ext_limit)) {
|
||||
$this->error('附件类型不正确!');
|
||||
}
|
||||
|
||||
if (!is_dir(config('upload_temp_path'))) {
|
||||
mkdir(config('upload_temp_path'), 0766, true);
|
||||
}
|
||||
$info = $file->move(config('upload_temp_path'), $file->hash('md5'));
|
||||
if ($info) {
|
||||
return json(['code' => 1, 'src' => PUBLIC_PATH. 'uploads/temp/'. $info->getFilename()]);
|
||||
} else {
|
||||
$this->error('上传失败');
|
||||
}
|
||||
}
|
||||
|
||||
$file_path = config('upload_temp_path') . str_replace(PUBLIC_PATH. 'uploads/temp/', '', $file_path);
|
||||
|
||||
if (is_file($file_path)) {
|
||||
// 获取裁剪信息
|
||||
$cut_info = explode(',', $cut_info);
|
||||
|
||||
// 读取图片
|
||||
$image = Image::open($file_path);
|
||||
|
||||
$dir_name = date('Ymd');
|
||||
$file_dir = config('upload_path') . DIRECTORY_SEPARATOR . 'images/' . $dir_name . '/';
|
||||
if (!is_dir($file_dir)) {
|
||||
mkdir($file_dir, 0766, true);
|
||||
}
|
||||
$file_name = md5(microtime(true)) . '.' . $image->type();
|
||||
$new_file_path = $file_dir . $file_name;
|
||||
|
||||
// 裁剪图片
|
||||
$image->crop($cut_info[0], $cut_info[1], $cut_info[2], $cut_info[3], $cut_info[4], $cut_info[5])->save($new_file_path);
|
||||
|
||||
// 水印功能
|
||||
if ($watermark == '') {
|
||||
if (config('upload_thumb_water') == 1 && config('upload_thumb_water_pic') > 0) {
|
||||
$this->create_water($new_file_path, config('upload_thumb_water_pic'));
|
||||
}
|
||||
} else {
|
||||
if (strtolower($watermark) != 'close') {
|
||||
list($watermark_img, $watermark_pos, $watermark_alpha) = explode('|', $watermark);
|
||||
$this->create_water($new_file_path, $watermark_img, $watermark_pos, $watermark_alpha);
|
||||
}
|
||||
}
|
||||
|
||||
// 是否创建缩略图
|
||||
$thumb_path_name = '';
|
||||
if ($thumb == '') {
|
||||
if (config('upload_image_thumb') != '') {
|
||||
$thumb_path_name = $this->create_thumb($new_file_path, $dir_name, $file_name);
|
||||
}
|
||||
} else {
|
||||
if (strtolower($thumb) != 'close') {
|
||||
list($thumb_size, $thumb_type) = explode('|', $thumb);
|
||||
$thumb_path_name = $this->create_thumb($new_file_path, $dir_name, $file_name, $thumb_size, $thumb_type);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存图片
|
||||
$file = new File($new_file_path);
|
||||
$file_info = [
|
||||
'uid' => session('user_auth.uid'),
|
||||
'name' => $file_name,
|
||||
'mime' => $image->mime(),
|
||||
'path' => 'uploads/images/' . $dir_name . '/' . $file_name,
|
||||
'ext' => $image->type(),
|
||||
'size' => $file->getSize(),
|
||||
'md5' => $file->hash('md5'),
|
||||
'sha1' => $file->hash('sha1'),
|
||||
'thumb' => $thumb_path_name,
|
||||
'module' => $module,
|
||||
'width' => $image->width(),
|
||||
'height' => $image->height()
|
||||
];
|
||||
|
||||
if ($file_add = AttachmentModel::create($file_info)) {
|
||||
// 删除临时图片
|
||||
unlink($file_path);
|
||||
// 返回成功信息
|
||||
return json([
|
||||
'code' => 1,
|
||||
'id' => $file_add['id'],
|
||||
'src' => PUBLIC_PATH . $file_info['path'],
|
||||
'thumb' => $thumb_path_name == '' ? '' : PUBLIC_PATH . $thumb_path_name,
|
||||
]);
|
||||
} else {
|
||||
$this->error('上传失败');
|
||||
}
|
||||
}
|
||||
$this->error('文件不存在');
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建缩略图
|
||||
* @param string $file 目标文件,可以是文件对象或文件路径
|
||||
* @param string $dir 保存目录,即目标文件所在的目录名
|
||||
* @param string $save_name 缩略图名
|
||||
* @param string $thumb_size 尺寸
|
||||
* @param string $thumb_type 裁剪类型
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return string 缩略图路径
|
||||
*/
|
||||
private function create_thumb($file = '', $dir = '', $save_name = '', $thumb_size = '', $thumb_type = '')
|
||||
{
|
||||
// 获取要生成的缩略图最大宽度和高度
|
||||
$thumb_size = $thumb_size == '' ? config('upload_image_thumb') : $thumb_size;
|
||||
list($thumb_max_width, $thumb_max_height) = explode(',', $thumb_size);
|
||||
// 读取图片
|
||||
$image = Image::open($file);
|
||||
// 生成缩略图
|
||||
$thumb_type = $thumb_type == '' ? config('upload_image_thumb_type') : $thumb_type;
|
||||
$image->thumb($thumb_max_width, $thumb_max_height, $thumb_type);
|
||||
// 保存缩略图
|
||||
$thumb_path = config('upload_path') . DIRECTORY_SEPARATOR . 'images/' . $dir . '/thumb/';
|
||||
if (!is_dir($thumb_path)) {
|
||||
mkdir($thumb_path, 0766, true);
|
||||
}
|
||||
$thumb_path_name = $thumb_path. $save_name;
|
||||
$image->save($thumb_path_name);
|
||||
$thumb_path_name = 'uploads/images/' . $dir . '/thumb/' . $save_name;
|
||||
return $thumb_path_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加水印
|
||||
* @param string $file 要添加水印的文件路径
|
||||
* @param string $watermark_img 水印图片id
|
||||
* @param string $watermark_pos 水印位置
|
||||
* @param string $watermark_alpha 水印透明度
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
private function create_water($file = '', $watermark_img = '', $watermark_pos = '', $watermark_alpha = '')
|
||||
{
|
||||
$path = model('admin/attachment')->getFilePath($watermark_img, 1);
|
||||
$thumb_water_pic = realpath(Env::get('root_path') . 'public/' . $path);
|
||||
if (is_file($thumb_water_pic)) {
|
||||
// 读取图片
|
||||
$image = Image::open($file);
|
||||
// 添加水印
|
||||
$watermark_pos = $watermark_pos == '' ? config('upload_thumb_water_position') : $watermark_pos;
|
||||
$watermark_alpha = $watermark_alpha == '' ? config('upload_thumb_water_alpha') : $watermark_alpha;
|
||||
$image->water($thumb_water_pic, $watermark_pos, $watermark_alpha);
|
||||
// 保存水印图片,覆盖原图
|
||||
$image->save($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传成功信息
|
||||
* @param $from
|
||||
* @param string $file_path
|
||||
* @param string $file_name
|
||||
* @param string $file_id
|
||||
* @param string $callback
|
||||
* @return string|\think\response\Json
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
private function uploadSuccess($from, $file_path = '', $file_name = '', $file_id = '', $callback = '')
|
||||
{
|
||||
switch ($from) {
|
||||
case 'wangeditor':
|
||||
return $file_path;
|
||||
break;
|
||||
case 'ueditor':
|
||||
return json([
|
||||
"state" => "SUCCESS", // 上传状态,上传成功时必须返回"SUCCESS"
|
||||
"url" => $file_path, // 返回的地址
|
||||
"title" => $file_name, // 附件名
|
||||
]);
|
||||
break;
|
||||
case 'editormd':
|
||||
return json([
|
||||
"success" => 1,
|
||||
"message" => '上传成功',
|
||||
"url" => $file_path,
|
||||
]);
|
||||
break;
|
||||
case 'ckeditor':
|
||||
return ck_js($callback, $file_path);
|
||||
break;
|
||||
default:
|
||||
return json([
|
||||
'code' => 1,
|
||||
'info' => '上传成功',
|
||||
'class' => 'success',
|
||||
'id' => $file_id,
|
||||
'path' => $file_path
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传错误信息
|
||||
* @param $from
|
||||
* @param string $msg
|
||||
* @param string $callback
|
||||
* @return string|\think\response\Json
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
private function uploadError($from, $msg = '', $callback = '')
|
||||
{
|
||||
switch ($from) {
|
||||
case 'wangeditor':
|
||||
return "error|".$msg;
|
||||
break;
|
||||
case 'ueditor':
|
||||
return json(['state' => $msg]);
|
||||
break;
|
||||
case 'editormd':
|
||||
return json(["success" => 0, "message" => $msg]);
|
||||
break;
|
||||
case 'ckeditor':
|
||||
return ck_js($callback, '', $msg);
|
||||
break;
|
||||
default:
|
||||
return json([
|
||||
'code' => 0,
|
||||
'class' => 'danger',
|
||||
'info' => $msg
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历获取目录下的指定类型的附件
|
||||
* @param string $path 路径
|
||||
* @param string $allowFiles 允许查看的类型
|
||||
* @param array $files 文件列表
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|null
|
||||
*/
|
||||
public function getfiles($path = '', $allowFiles = '', &$files = array())
|
||||
{
|
||||
if (!is_dir($path)) return null;
|
||||
if(substr($path, strlen($path) - 1) != '/') $path .= '/';
|
||||
$handle = opendir($path);
|
||||
while (false !== ($file = readdir($handle))) {
|
||||
if ($file != '.' && $file != '..') {
|
||||
$path2 = $path . $file;
|
||||
if (is_dir($path2)) {
|
||||
$this->getfiles($path2, $allowFiles, $files);
|
||||
} else {
|
||||
if (preg_match("/\.(".$allowFiles.")$/i", $file)) {
|
||||
$files[] = array(
|
||||
'url'=> str_replace("\\", "/", substr($path2, strlen($_SERVER['DOCUMENT_ROOT']))),
|
||||
'mtime'=> filemtime($path2)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用附件
|
||||
* @param array $record 行为日志
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function enable($record = [])
|
||||
{
|
||||
return $this->setStatus('enable');
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用附件
|
||||
* @param array $record 行为日志
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function disable($record = [])
|
||||
{
|
||||
return $this->setStatus('disable');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置附件状态:删除、禁用、启用
|
||||
* @param string $type 类型:delete/enable/disable
|
||||
* @param array $record
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function setStatus($type = '', $record = [])
|
||||
{
|
||||
$ids = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
|
||||
$ids = is_array($ids) ? implode(',', $ids) : $ids;
|
||||
return parent::setStatus($type, ['attachment_'.$type, 'admin_attachment', 0, UID, $ids]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除附件
|
||||
* @param string $ids 附件id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function delete($ids = '')
|
||||
{
|
||||
$ids = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
|
||||
if (empty($ids)) $this->error('缺少主键');
|
||||
|
||||
$files_path = AttachmentModel::where('id', 'in', $ids)->column('path,thumb', 'id');
|
||||
|
||||
foreach ($files_path as $value) {
|
||||
$real_path = realpath(config('upload_path').'/../'.$value['path']);
|
||||
$real_path_thumb = realpath(config('upload_path').'/../'.$value['thumb']);
|
||||
|
||||
if (is_file($real_path) && !unlink($real_path)) {
|
||||
$this->error('删除失败');
|
||||
}
|
||||
if (is_file($real_path_thumb) && !unlink($real_path_thumb)) {
|
||||
$this->error('删除缩略图失败');
|
||||
}
|
||||
}
|
||||
if (AttachmentModel::where('id', 'in', $ids)->delete()) {
|
||||
// 记录行为
|
||||
$ids = is_array($ids) ? implode(',', $ids) : $ids;
|
||||
action_log('attachment_delete', 'admin_attachment', 0, UID, $ids);
|
||||
$this->success('删除成功');
|
||||
} else {
|
||||
$this->error('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速编辑
|
||||
* @param array $record 行为日志
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function quickEdit($record = [])
|
||||
{
|
||||
$id = input('post.pk', '');
|
||||
return parent::quickEdit(['attachment_edit', 'admin_attachment', 0, UID, $id]);
|
||||
}
|
||||
}
|
||||
281
application/admin/controller/Config.php
Normal file
281
application/admin/controller/Config.php
Normal file
@ -0,0 +1,281 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\admin\model\Config as ConfigModel;
|
||||
|
||||
/**
|
||||
* 系统配置控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Config extends Admin
|
||||
{
|
||||
/**
|
||||
* 配置首页
|
||||
* @param string $group 分组
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function index($group = 'base')
|
||||
{
|
||||
cookie('__forward__', $_SERVER['REQUEST_URI']);
|
||||
|
||||
// 配置分组信息
|
||||
$list_group = config('config_group');
|
||||
$tab_list = [];
|
||||
foreach ($list_group as $key => $value) {
|
||||
$tab_list[$key]['title'] = $value;
|
||||
$tab_list[$key]['url'] = url('index', ['group' => $key]);
|
||||
}
|
||||
|
||||
// 查询
|
||||
$map = $this->getMap();
|
||||
$map[] = ['group', '=', $group];
|
||||
$map[] = ['status', 'egt', 0];
|
||||
|
||||
// 排序
|
||||
$order = $this->getOrder('sort asc,id asc');
|
||||
// 数据列表
|
||||
$data_list = ConfigModel::where($map)->order($order)->paginate();
|
||||
|
||||
// 使用ZBuilder快速创建数据表格
|
||||
return ZBuilder::make('table')
|
||||
->setPageTitle('配置管理') // 设置页面标题
|
||||
->setTabNav($tab_list, $group) // 设置tab分页
|
||||
->setSearch(['name' => '名称', 'title' => '标题']) // 设置搜索框
|
||||
->addColumns([ // 批量添加数据列
|
||||
['name', '名称', 'text.edit'],
|
||||
['title', '标题', 'text.edit'],
|
||||
['type', '类型', 'select', config('form_item_type')],
|
||||
['status', '状态', 'switch'],
|
||||
['sort', '排序', 'text.edit'],
|
||||
['right_button', '操作', 'btn']
|
||||
])
|
||||
->addValidate('Config', 'name,title') // 添加快捷编辑的验证器
|
||||
->addOrder('name,title,status') // 添加标题字段排序
|
||||
->addFilter('name,title') // 添加标题字段筛选
|
||||
->addFilter('type', config('form_item_type')) // 添加标题字段筛选
|
||||
->addFilterMap('name,title', ['group' => $group]) // 添加标题字段筛选条件
|
||||
->addTopButton('add', ['href' => url('add', ['group' => $group])], true) // 添加单个顶部按钮
|
||||
->addTopButtons('enable,disable,delete') // 批量添加顶部按钮
|
||||
->addRightButton('edit', [], true)
|
||||
->addRightButton('delete') // 批量添加右侧按钮
|
||||
->setRowList($data_list) // 设置表格数据
|
||||
->fetch(); // 渲染模板
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增配置项
|
||||
* @param string $group 分组
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function add($group = '')
|
||||
{
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
// 表单数据
|
||||
$data = $this->request->post();
|
||||
|
||||
// 验证
|
||||
$result = $this->validate($data, 'Config');
|
||||
if(true !== $result) $this->error($result);
|
||||
|
||||
// 如果是快速联动
|
||||
if ($data['type'] == 'linkages') {
|
||||
$data['key'] = $data['key'] == '' ? 'id' : $data['key'];
|
||||
$data['pid'] = $data['pid'] == '' ? 'pid' : $data['pid'];
|
||||
$data['level'] = $data['level'] == '' ? '2' : $data['level'];
|
||||
$data['option'] = $data['option'] == '' ? 'name' : $data['option'];
|
||||
}
|
||||
|
||||
if ($config = ConfigModel::create($data)) {
|
||||
cache('system_config', null);
|
||||
$forward = $this->request->param('_pop') == 1 ? null : cookie('__forward__');
|
||||
// 记录行为
|
||||
$details = '详情:分组('.$data['group'].')、类型('.$data['type'].')、标题('.$data['title'].')、名称('.$data['name'].')';
|
||||
action_log('config_add', 'admin_config', $config['id'], UID, $details);
|
||||
$this->success('新增成功', $forward);
|
||||
} else {
|
||||
$this->error('新增失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('新增')
|
||||
->addRadio('group', '配置分组', '', config('config_group'), $group)
|
||||
->addSelect('type', '配置类型', '', config('form_item_type'))
|
||||
->addText('title', '配置标题', '一般由中文组成,仅用于显示')
|
||||
->addText('name', '配置名称', '由英文字母和下划线组成,如 <code>web_site_title</code>,调用方法:<code>config(\'web_site_title\')</code>')
|
||||
->addTextarea('value', '配置值', '该配置的具体内容')
|
||||
->addTextarea('options', '配置项', '用于单选、多选、下拉、联动等类型')
|
||||
->addText('ajax_url', '异步请求地址', "如请求的地址是 <code>url('ajax/getCity')</code>,那么只需填写 <code>ajax/getCity</code>,或者直接填写以 <code>http</code>开头的url地址")
|
||||
->addText('next_items', '下一级联动下拉框的表单名', "与当前有关联的下级联动下拉框名,多个用逗号隔开,如:area,other")
|
||||
->addText('param', '请求参数名', "联动下拉框请求参数名,默认为配置名称")
|
||||
->addNumber('level', '级别', '需要显示的级别数量,默认为2', 2, 2, 4)
|
||||
->addText('table', '表名', '要查询的表,里面必须含有id、name、pid三个字段,其中id和name字段可在下面重新定义')
|
||||
->addText('pid', '父级id字段名', '即表中的父级ID字段名,如果表中的主键字段名为pid则可不填写')
|
||||
->addText('key', '键字段名', '即表中的主键字段名,如果表中的主键字段名为id则可不填写')
|
||||
->addText('option', '值字段名', '下拉菜单显示的字段名,如果表中的该字段名为name则可不填写')
|
||||
->addText('ak', 'APPKEY', '百度编辑器APPKEY')
|
||||
->addText('format', '格式')
|
||||
->addText('tips', '配置说明', '该配置的具体说明')
|
||||
->addText('sort', '排序', '', 100)
|
||||
->setTrigger('type', 'linkage', 'ajax_url,next_items,param')
|
||||
->setTrigger('type', 'linkages', 'table,pid,level,key,option')
|
||||
->setTrigger('type', 'bmap', 'ak')
|
||||
->setTrigger('type', 'masked,date,time,datetime', 'format')
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
* @param int $id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function edit($id = 0)
|
||||
{
|
||||
if ($id === 0) $this->error('参数错误');
|
||||
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
// 表单数据
|
||||
$data = $this->request->post();
|
||||
|
||||
// 验证
|
||||
$result = $this->validate($data, 'Config');
|
||||
if(true !== $result) $this->error($result);
|
||||
|
||||
// 如果是快速联动
|
||||
if ($data['type'] == 'linkages') {
|
||||
$data['key'] = $data['key'] == '' ? 'id' : $data['key'];
|
||||
$data['pid'] = $data['pid'] == '' ? 'pid' : $data['pid'];
|
||||
$data['level'] = $data['level'] == '' ? '2' : $data['level'];
|
||||
$data['option'] = $data['option'] == '' ? 'name' : $data['option'];
|
||||
}
|
||||
|
||||
// 原配置内容
|
||||
$config = ConfigModel::where('id', $id)->find();
|
||||
$details = '原数据:分组('.$config['group'].')、类型('.$config['type'].')、标题('.$config['title'].')、名称('.$config['name'].')';
|
||||
|
||||
if ($config = ConfigModel::update($data)) {
|
||||
cache('system_config', null);
|
||||
$forward = $this->request->param('_pop') == 1 ? null : cookie('__forward__');
|
||||
// 记录行为
|
||||
action_log('config_edit', 'admin_config', $config['id'], UID, $details);
|
||||
$this->success('编辑成功', $forward, '_parent_reload');
|
||||
} else {
|
||||
$this->error('编辑失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
$info = ConfigModel::get($id);
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('编辑')
|
||||
->addHidden('id')
|
||||
->addRadio('group', '配置分组', '', config('config_group'))
|
||||
->addSelect('type', '配置类型', '', config('form_item_type'))
|
||||
->addText('title', '配置标题', '一般由中文组成,仅用于显示')
|
||||
->addText('name', '配置名称', '由英文字母和下划线组成,如 <code>web_site_title</code>,调用方法:<code>config(\'web_site_title\')</code>')
|
||||
->addTextarea('value', '配置值', '该配置的具体内容')
|
||||
->addTextarea('options', '配置项', '用于单选、多选、下拉、联动等类型')
|
||||
->addText('ajax_url', '异步请求地址', "如请求的地址是 <code>url('ajax/getCity')</code>,那么只需填写 <code>ajax/getCity</code>,或者直接填写以 <code>http</code>开头的url地址")
|
||||
->addText('next_items', '下一级联动下拉框的表单名', "与当前有关联的下级联动下拉框名,多个用逗号隔开,如:area,other")
|
||||
->addText('param', '请求参数名', "联动下拉框请求参数名,默认为配置名称")
|
||||
->addNumber('level', '级别', '需要显示的级别数量,默认为2', 2, 2, 4)
|
||||
->addText('table', '表名', '要查询的表,里面必须含有id、name、pid三个字段,其中id和name字段可在下面重新定义')
|
||||
->addText('pid', '父级id字段名', '即表中的父级ID字段名,如果表中的主键字段名为pid则可不填写')
|
||||
->addText('key', '键字段名', '即表中的主键字段名,如果表中的主键字段名为id则可不填写')
|
||||
->addText('option', '值字段名', '下拉菜单显示的字段名,如果表中的该字段名为name则可不填写')
|
||||
->addText('ak', 'APPKEY', '百度编辑器APPKEY')
|
||||
->addText('format', '格式')
|
||||
->addText('tips', '配置说明', '该配置的具体说明')
|
||||
->addText('sort', '排序', '', 100)
|
||||
->setTrigger('type', 'linkage', 'ajax_url,next_items,param')
|
||||
->setTrigger('type', 'linkages', 'table,pid,level,key,option')
|
||||
->setTrigger('type', 'bmap', 'ak')
|
||||
->setTrigger('type', 'masked,date,time,datetime', 'format')
|
||||
->setFormData($info)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除配置
|
||||
* @param array $record 行为日志
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function delete($record = [])
|
||||
{
|
||||
return $this->setStatus('delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用配置
|
||||
* @param array $record 行为日志
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function enable($record = [])
|
||||
{
|
||||
return $this->setStatus('enable');
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用配置
|
||||
* @param array $record 行为日志
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function disable($record = [])
|
||||
{
|
||||
return $this->setStatus('disable');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置状态:删除、禁用、启用
|
||||
* @param string $type 类型:delete/enable/disable
|
||||
* @param array $record
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function setStatus($type = '', $record = [])
|
||||
{
|
||||
$ids = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
|
||||
$uid_delete = is_array($ids) ? '' : $ids;
|
||||
$ids = ConfigModel::where('id', 'in', $ids)->column('title');
|
||||
return parent::setStatus($type, ['config_'.$type, 'admin_config', $uid_delete, UID, implode('、', $ids)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速编辑
|
||||
* @param array $record 行为日志
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function quickEdit($record = [])
|
||||
{
|
||||
$id = input('post.pk', '');
|
||||
$field = input('post.name', '');
|
||||
$value = input('post.value', '');
|
||||
$config = ConfigModel::where('id', $id)->value($field);
|
||||
$details = '字段(' . $field . '),原值(' . $config . '),新值:(' . $value . ')';
|
||||
return parent::quickEdit(['config_edit', 'admin_config', $id, UID, $details]);
|
||||
}
|
||||
}
|
||||
366
application/admin/controller/Database.php
Normal file
366
application/admin/controller/Database.php
Normal file
@ -0,0 +1,366 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\builder\ZBuilder;
|
||||
use think\Db;
|
||||
use util\Database as DatabaseModel;
|
||||
|
||||
/**
|
||||
* 数据库管理
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Database extends Admin
|
||||
{
|
||||
/**
|
||||
* 数据库管理
|
||||
* @param string $group 分组
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function index($group = 'export')
|
||||
{
|
||||
// 配置分组信息
|
||||
$list_group = ['export' =>'备份数据库', 'import' => '还原数据库'];
|
||||
$tab_list = [];
|
||||
foreach ($list_group as $key => $value) {
|
||||
$tab_list[$key]['title'] = $value;
|
||||
$tab_list[$key]['url'] = url('index', ['group' => $key]);
|
||||
}
|
||||
|
||||
switch ($group) {
|
||||
case 'export':
|
||||
$data_list = Db::query("SHOW TABLE STATUS");
|
||||
$data_list = array_map('array_change_key_case', $data_list);
|
||||
|
||||
// 自定义按钮
|
||||
$btn_export = [
|
||||
'title' => '立即备份',
|
||||
'icon' => 'fa fa-fw fa-copy',
|
||||
'class' => 'btn btn-primary ajax-post confirm',
|
||||
'href' => url('export')
|
||||
];
|
||||
$btn_optimize_all = [
|
||||
'title' => '优化表',
|
||||
'icon' => 'fa fa-fw fa-cogs',
|
||||
'class' => 'btn btn-success ajax-post',
|
||||
'href' => url('optimize')
|
||||
];
|
||||
$btn_repair_all = [
|
||||
'title' => '修复表',
|
||||
'icon' => 'fa fa-fw fa-wrench',
|
||||
'class' => 'btn btn-success ajax-post',
|
||||
'href' => url('repair')
|
||||
];
|
||||
$btn_optimize = [
|
||||
'title' => '优化表',
|
||||
'icon' => 'fa fa-fw fa-cogs',
|
||||
'class' => 'btn btn-xs btn-default ajax-get',
|
||||
'href' => url('optimize', ['ids' => '__id__'])
|
||||
];
|
||||
$btn_repair = [
|
||||
'title' => '修复表',
|
||||
'icon' => 'fa fa-fw fa-wrench',
|
||||
'class' => 'btn btn-xs btn-default ajax-get',
|
||||
'href' => url('repair', ['ids' => '__id__'])
|
||||
];
|
||||
|
||||
// 使用ZBuilder快速创建数据表格
|
||||
return ZBuilder::make('table')
|
||||
->setPageTitle('数据库管理') // 设置页面标题
|
||||
->setPrimaryKey('name')
|
||||
->setTabNav($tab_list, $group) // 设置tab分页
|
||||
->addColumns([ // 批量添加数据列
|
||||
['name', '表名'],
|
||||
['rows', '行数'],
|
||||
['data_length', '大小', 'byte'],
|
||||
['data_free', '冗余', 'byte'],
|
||||
['comment', '备注'],
|
||||
['right_button', '操作', 'btn']
|
||||
])
|
||||
->addTopButton('custom', $btn_export) // 添加单个顶部按钮
|
||||
->addTopButton('custom', $btn_optimize_all) // 添加单个顶部按钮
|
||||
->addTopButton('custom', $btn_repair_all) // 添加单个顶部按钮
|
||||
->addRightButton('custom', $btn_optimize) // 添加右侧按钮
|
||||
->addRightButton('custom', $btn_repair) // 添加右侧按钮
|
||||
->setRowList($data_list) // 设置表格数据
|
||||
->fetch(); // 渲染模板
|
||||
break;
|
||||
case 'import':
|
||||
// 列出备份文件列表
|
||||
$path = config('data_backup_path');
|
||||
if(!is_dir($path)){
|
||||
mkdir($path, 0755, true);
|
||||
}
|
||||
$path = realpath($path);
|
||||
$flag = \FilesystemIterator::KEY_AS_FILENAME;
|
||||
$glob = new \FilesystemIterator($path, $flag);
|
||||
|
||||
$data_list = [];
|
||||
foreach ($glob as $name => $file) {
|
||||
if(preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql(?:\.gz)?$/', $name)){
|
||||
$name = sscanf($name, '%4s%2s%2s-%2s%2s%2s-%d');
|
||||
|
||||
$date = "{$name[0]}-{$name[1]}-{$name[2]}";
|
||||
$time = "{$name[3]}:{$name[4]}:{$name[5]}";
|
||||
$part = $name[6];
|
||||
|
||||
if(isset($data_list["{$date} {$time}"])){
|
||||
$info = $data_list["{$date} {$time}"];
|
||||
$info['part'] = max($info['part'], $part);
|
||||
$info['size'] = $info['size'] + $file->getSize();
|
||||
} else {
|
||||
$info['part'] = $part;
|
||||
$info['size'] = $file->getSize();
|
||||
}
|
||||
$extension = strtoupper(pathinfo($file->getFilename(), PATHINFO_EXTENSION));
|
||||
$info['compress'] = ($extension === 'SQL') ? '-' : $extension;
|
||||
$info['time'] = strtotime("{$date} {$time}");
|
||||
$info['name'] = $info['time'];
|
||||
|
||||
$data_list["{$date} {$time}"] = $info;
|
||||
}
|
||||
}
|
||||
|
||||
$data_list = !empty($data_list) ? array_values($data_list) : $data_list;
|
||||
|
||||
// 自定义按钮
|
||||
$btn_import = [
|
||||
'title' => '还原',
|
||||
'icon' => 'fa fa-fw fa-reply',
|
||||
'class' => 'btn btn-xs btn-default ajax-get confirm',
|
||||
'href' => url('import', ['time' => '__id__'])
|
||||
];
|
||||
|
||||
// 使用ZBuilder快速创建数据表格
|
||||
return ZBuilder::make('table')
|
||||
->setPageTitle('数据库管理') // 设置页面标题
|
||||
->setPrimaryKey('time')
|
||||
->hideCheckbox()
|
||||
->setTabNav($tab_list, $group) // 设置tab分页
|
||||
->addColumns([ // 批量添加数据列
|
||||
['name', '备份名称', 'datetime', '', 'Ymd-His'],
|
||||
['part', '卷数'],
|
||||
['compress', '压缩'],
|
||||
['size', '数据大小', 'byte'],
|
||||
['time', '备份时间', 'datetime', '', 'Y-m-d H:i:s'],
|
||||
['right_button', '操作', 'btn']
|
||||
])
|
||||
->addRightButton('custom', $btn_import) // 添加右侧按钮
|
||||
->addRightButton('delete') // 添加右侧按钮
|
||||
->setRowList($data_list) // 设置表格数据
|
||||
->fetch(); // 渲染模板
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份数据库(参考onthink 麦当苗儿 <zuojiazi@vip.qq.com>)
|
||||
* @param null|array $ids 表名
|
||||
* @param integer $start 起始行数
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function export($ids = null, $start = 0)
|
||||
{
|
||||
$tables = $ids;
|
||||
if ($this->request->isPost() && !empty($tables) && is_array($tables)) {
|
||||
// 初始化
|
||||
$path = config('data_backup_path');
|
||||
if(!is_dir($path)){
|
||||
mkdir($path, 0755, true);
|
||||
}
|
||||
|
||||
// 读取备份配置
|
||||
$config = array(
|
||||
'path' => realpath($path) . DIRECTORY_SEPARATOR,
|
||||
'part' => config('data_backup_part_size'),
|
||||
'compress' => config('data_backup_compress'),
|
||||
'level' => config('data_backup_compress_level'),
|
||||
);
|
||||
|
||||
// 检查是否有正在执行的任务
|
||||
$lock = "{$config['path']}backup.lock";
|
||||
if(is_file($lock)){
|
||||
$this->error('检测到有一个备份任务正在执行,请稍后再试!');
|
||||
} else {
|
||||
// 创建锁文件
|
||||
file_put_contents($lock, $this->request->time());
|
||||
}
|
||||
|
||||
// 检查备份目录是否可写
|
||||
is_writeable($config['path']) || $this->error('备份目录不存在或不可写,请检查后重试!');
|
||||
|
||||
// 生成备份文件信息
|
||||
$file = array(
|
||||
'name' => date('Ymd-His', $this->request->time()),
|
||||
'part' => 1,
|
||||
);
|
||||
|
||||
// 创建备份文件
|
||||
$Database = new DatabaseModel($file, $config);
|
||||
if(false !== $Database->create()){
|
||||
// 备份指定表
|
||||
foreach ($tables as $table) {
|
||||
$start = $Database->backup($table, $start);
|
||||
while (0 !== $start) {
|
||||
if (false === $start) { // 出错
|
||||
$this->error('备份出错!');
|
||||
}
|
||||
$start = $Database->backup($table, $start[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// 备份完成,删除锁定文件
|
||||
unlink($lock);
|
||||
// 记录行为
|
||||
action_log('database_export', 'database', 0, UID, implode(',', $tables));
|
||||
$this->success('备份完成!');
|
||||
} else {
|
||||
$this->error('初始化失败,备份文件创建失败!');
|
||||
}
|
||||
} else {
|
||||
$this->error('参数错误!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 还原数据库(参考onthink 麦当苗儿 <zuojiazi@vip.qq.com>)
|
||||
* @param int $time 文件时间戳
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function import($time = 0)
|
||||
{
|
||||
if ($time === 0) $this->error('参数错误!');
|
||||
|
||||
// 初始化
|
||||
$name = date('Ymd-His', $time) . '-*.sql*';
|
||||
$path = realpath(config('data_backup_path')) . DIRECTORY_SEPARATOR . $name;
|
||||
$files = glob($path);
|
||||
$list = array();
|
||||
foreach($files as $name){
|
||||
$basename = basename($name);
|
||||
$match = sscanf($basename, '%4s%2s%2s-%2s%2s%2s-%d');
|
||||
$gz = preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql.gz$/', $basename);
|
||||
$list[$match[6]] = array($match[6], $name, $gz);
|
||||
}
|
||||
ksort($list);
|
||||
|
||||
// 检测文件正确性
|
||||
$last = end($list);
|
||||
if(count($list) === $last[0]){
|
||||
foreach ($list as $item) {
|
||||
$config = [
|
||||
'path' => realpath(config('data_backup_path')) . DIRECTORY_SEPARATOR,
|
||||
'compress' => $item[2]
|
||||
];
|
||||
$Database = new DatabaseModel($item, $config);
|
||||
$start = $Database->import(0);
|
||||
|
||||
// 循环导入数据
|
||||
while (0 !== $start) {
|
||||
if (false === $start) { // 出错
|
||||
$this->error('还原数据出错!');
|
||||
}
|
||||
$start = $Database->import($start[0]);
|
||||
}
|
||||
}
|
||||
// 记录行为
|
||||
action_log('database_import', 'database', 0, UID, date('Ymd-His', $time));
|
||||
$this->success('还原完成!');
|
||||
} else {
|
||||
$this->error('备份文件可能已经损坏,请检查!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 优化表
|
||||
* @param null|string|array $ids 表名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function optimize($ids = null)
|
||||
{
|
||||
$tables = $ids;
|
||||
if($tables) {
|
||||
if(is_array($tables)){
|
||||
$tables = implode('`,`', $tables);
|
||||
$list = Db::query("OPTIMIZE TABLE `{$tables}`");
|
||||
|
||||
if($list){
|
||||
// 记录行为
|
||||
action_log('database_optimize', 'database', 0, UID, "`{$tables}`");
|
||||
$this->success("数据表优化完成!");
|
||||
} else {
|
||||
$this->error("数据表优化出错请重试!");
|
||||
}
|
||||
} else {
|
||||
$list = Db::query("OPTIMIZE TABLE `{$tables}`");
|
||||
if($list){
|
||||
// 记录行为
|
||||
action_log('database_optimize', 'database', 0, UID, $tables);
|
||||
$this->success("数据表'{$tables}'优化完成!");
|
||||
} else {
|
||||
$this->error("数据表'{$tables}'优化出错请重试!");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->error("请选择要优化的表!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修复表
|
||||
* @param null|string|array $ids 表名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function repair($ids = null)
|
||||
{
|
||||
$tables = $ids;
|
||||
if($tables) {
|
||||
if(is_array($tables)){
|
||||
$tables = implode('`,`', $tables);
|
||||
$list = Db::query("REPAIR TABLE `{$tables}`");
|
||||
|
||||
if($list){
|
||||
// 记录行为
|
||||
action_log('database_repair', 'database', 0, UID, "`{$tables}`");
|
||||
$this->success("数据表修复完成!");
|
||||
} else {
|
||||
$this->error("数据表修复出错请重试!");
|
||||
}
|
||||
} else {
|
||||
$list = Db::query("REPAIR TABLE `{$tables}`");
|
||||
if($list){
|
||||
// 记录行为
|
||||
action_log('database_repair', 'database', 0, UID, $tables);
|
||||
$this->success("数据表'{$tables}'修复完成!");
|
||||
} else {
|
||||
$this->error("数据表'{$tables}'修复出错请重试!");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->error("请指定要修复的表!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除备份文件
|
||||
* @param int $ids 备份时间
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete($ids = 0)
|
||||
{
|
||||
if ($ids == 0) $this->error('参数错误!');
|
||||
|
||||
$name = date('Ymd-His', $ids) . '-*.sql*';
|
||||
$path = realpath(config('data_backup_path')) . DIRECTORY_SEPARATOR . $name;
|
||||
array_map("unlink", glob($path));
|
||||
if(count(glob($path))){
|
||||
$this->error('备份文件删除失败,请检查权限!');
|
||||
} else {
|
||||
// 记录行为
|
||||
action_log('database_backup_delete', 'database', 0, UID, date('Ymd-His', $ids));
|
||||
$this->success('备份文件删除成功!');
|
||||
}
|
||||
}
|
||||
}
|
||||
234
application/admin/controller/Hook.php
Normal file
234
application/admin/controller/Hook.php
Normal file
@ -0,0 +1,234 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\model\HookPlugin;
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\admin\model\Hook as HookModel;
|
||||
use app\admin\model\HookPlugin as HookPluginModel;
|
||||
|
||||
/**
|
||||
* 钩子控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Hook extends Admin
|
||||
{
|
||||
/**
|
||||
* 钩子管理
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$map = $this->getMap();
|
||||
$order = $this->getOrder();
|
||||
|
||||
// 数据列表
|
||||
$data_list = HookModel::where($map)->order($order)->paginate();
|
||||
|
||||
// 分页数据
|
||||
$page = $data_list->render();
|
||||
|
||||
// 使用ZBuilder快速创建数据表格
|
||||
return ZBuilder::make('table')
|
||||
->setPageTitle('钩子管理') // 设置页面标题
|
||||
->setSearch(['name' => '钩子名称']) // 设置搜索框
|
||||
->addColumns([ // 批量添加数据列
|
||||
['name', '名称'],
|
||||
['description', '描述'],
|
||||
['plugin', '所属插件', 'callback', function($plugin){
|
||||
return $plugin == '' ? '系统' : $plugin;
|
||||
}],
|
||||
['system', '系统钩子', 'yesno'],
|
||||
['status', '状态', 'switch'],
|
||||
['right_button', '操作', 'btn']
|
||||
])
|
||||
->addOrder('name,status')
|
||||
->addTopButtons('add,enable,disable,delete') // 批量添加顶部按钮
|
||||
->addRightButtons('edit,delete') // 批量添加右侧按钮
|
||||
->setRowList($data_list) // 设置表格数据
|
||||
->setPages($page) // 设置分页数据
|
||||
->fetch(); // 渲染模板
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
// 表单数据
|
||||
$data = $this->request->post();
|
||||
$data['system'] = 1;
|
||||
|
||||
// 验证
|
||||
$result = $this->validate($data, 'Hook');
|
||||
if(true !== $result) $this->error($result);
|
||||
|
||||
if ($hook = HookModel::create($data)) {
|
||||
cache('hook_plugins', null);
|
||||
// 记录行为
|
||||
action_log('hook_add', 'admin_hook', $hook['id'], UID, $data['name']);
|
||||
$this->success('新增成功', 'index');
|
||||
} else {
|
||||
$this->error('新增失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('新增')
|
||||
->addText('name', '钩子名称', '由字母和下划线组成,如:<code>page_tips</code>')
|
||||
->addText('description', '钩子描述')
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
* @param int $id 钩子id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function edit($id = 0)
|
||||
{
|
||||
if ($id === 0) $this->error('参数错误');
|
||||
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post();
|
||||
// 验证
|
||||
$result = $this->validate($data, 'Hook');
|
||||
if(true !== $result) $this->error($result);
|
||||
|
||||
if ($hook = HookModel::update($data)) {
|
||||
// 调整插件顺序
|
||||
if ($data['sort'] != '') {
|
||||
HookPluginModel::sort($data['name'], $data['sort']);
|
||||
}
|
||||
cache('hook_plugins', null);
|
||||
// 记录行为
|
||||
action_log('hook_edit', 'admin_hook', $hook['id'], UID, $data['name']);
|
||||
$this->success('编辑成功', 'index');
|
||||
} else {
|
||||
$this->error('编辑失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
$info = HookModel::get($id);
|
||||
|
||||
// 该钩子的所有插件
|
||||
$hooks = HookPluginModel::where('hook', $info['name'])->order('sort')->column('plugin');
|
||||
$hooks = parse_array($hooks);
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('编辑')
|
||||
->addHidden('id')
|
||||
->addText('name', '钩子名称', '由字母和下划线组成,如:<code>page_tips</code>')
|
||||
->addText('description', '钩子描述')
|
||||
->addSort('sort', '插件排序', '', $hooks)
|
||||
->setFormData($info)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速编辑(启用/禁用)
|
||||
* @param string $status 状态
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function quickEdit($status = '')
|
||||
{
|
||||
$id = $this->request->post('pk');
|
||||
$status = $this->request->param('value');
|
||||
$hook_name = HookModel::where('id', $id)->value('name');
|
||||
|
||||
if (false === HookPluginModel::where('hook', $hook_name)->setField('status', $status == 'true' ? 1 : 0)) {
|
||||
$this->error('操作失败,请重试');
|
||||
}
|
||||
cache('hook_plugins', null);
|
||||
$details = $status == 'true' ? '启用钩子' : '禁用钩子';
|
||||
return parent::quickEdit(['hook_edit', 'admin_hook', $id, UID, $details]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function enable($record = [])
|
||||
{
|
||||
return $this->setStatus('enable');
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
/**
|
||||
* 禁用
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function disable($record = [])
|
||||
{
|
||||
return $this->setStatus('disable');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除钩子
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function delete($record = [])
|
||||
{
|
||||
$ids = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
|
||||
$map = [
|
||||
['id', 'in', $ids],
|
||||
['system', '=', 1],
|
||||
];
|
||||
if (HookModel::where($map)->find()) {
|
||||
$this->error('禁止删除系统钩子');
|
||||
}
|
||||
return $this->setStatus('delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态
|
||||
* @param string $type 类型
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function setStatus($type = '', $record = [])
|
||||
{
|
||||
$ids = $this->request->param('ids/a');
|
||||
foreach ($ids as $id) {
|
||||
$hook_name = HookModel::where('id', $id)->value('name');
|
||||
if (false === HookPluginModel::where('hook', $hook_name)->setField('status', $type == 'enable' ? 1 : 0)) {
|
||||
$this->error('操作失败,请重试');
|
||||
}
|
||||
}
|
||||
cache('hook_plugins', null);
|
||||
$hook_delete = is_array($ids) ? '' : $ids;
|
||||
$hook_names = HookModel::where('id', 'in', $ids)->column('name');
|
||||
return parent::setStatus($type, ['hook_'.$type, 'admin_hook', $hook_delete, UID, implode('、', $hook_names)]);
|
||||
}
|
||||
}
|
||||
223
application/admin/controller/Icon.php
Normal file
223
application/admin/controller/Icon.php
Normal file
@ -0,0 +1,223 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\admin\model\Icon as IconModel;
|
||||
use app\admin\model\IconList as IconListModel;
|
||||
|
||||
/**
|
||||
* 图标控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Icon extends Admin
|
||||
{
|
||||
/**
|
||||
* 图标列表
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data_list = IconModel::where($this->getMap())
|
||||
->order($this->getOrder('id DESC'))
|
||||
->paginate();
|
||||
|
||||
return ZBuilder::make('table')
|
||||
->addTopButtons('add,enable,disable,delete')
|
||||
->addRightButton('list', [
|
||||
'title' => '图标列表',
|
||||
'icon' => 'fa fa-list',
|
||||
'href' => url('items', ['id' => '__id__'])
|
||||
])
|
||||
->addRightButton('reload', [
|
||||
'title' => '更新图标',
|
||||
'icon' => 'fa fa-refresh',
|
||||
'class' => 'btn btn-xs btn-default ajax-get confirm',
|
||||
'href' => url('reload', ['id' => '__id__'])
|
||||
])
|
||||
->addRightButton('delete')
|
||||
->setSearch('name')
|
||||
->addColumns([
|
||||
['id', 'ID'],
|
||||
['name', '名称', 'text.edit'],
|
||||
['url', '链接', 'text.edit'],
|
||||
['status', '状态', 'switch'],
|
||||
['create_time', '创建时间', 'datetime'],
|
||||
['right_button', '操作', 'btn'],
|
||||
])
|
||||
->setRowList($data_list)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post('', null, 'trim');
|
||||
$data['name'] == '' && $this->error('请填写名称');
|
||||
$data['url'] == '' && $this->error('请填写链接');
|
||||
$data['create_time'] = $this->request->time();
|
||||
$data['update_time'] = $this->request->time();
|
||||
|
||||
// 获取图标信息
|
||||
$url = substr($data['url'], 0, 4) == 'http' ? $data['url'] : 'http:'.$data['url'];
|
||||
$content = file_get_contents($url);
|
||||
|
||||
// 获取字体名
|
||||
$font_family = '';
|
||||
$pattern = '/font-family: "(.*)";/';
|
||||
if (preg_match($pattern, $content, $match)) {
|
||||
$font_family = $match[1];
|
||||
} else {
|
||||
$this->error('无法获取字体名');
|
||||
}
|
||||
|
||||
$IconModel = new IconModel();
|
||||
if ($id = $IconModel->insertGetId($data)) {
|
||||
// 拉取图标列表
|
||||
$pattern = '/\.(.*):before/';
|
||||
if (preg_match_all($pattern, $content, $matches)) {
|
||||
$icon_list = [];
|
||||
foreach ($matches[1] as $match) {
|
||||
$icon_list[] = [
|
||||
'icon_id' => $id,
|
||||
'title' => $match,
|
||||
'class' => $font_family . ' ' . $match,
|
||||
'code' => $match,
|
||||
];
|
||||
}
|
||||
$IconListModel = new IconListModel();
|
||||
if ($IconListModel->saveAll($icon_list)) {
|
||||
$this->success('新增成功', 'index');
|
||||
} else {
|
||||
$IconModel->where('id', $id)->delete();
|
||||
$this->error('图标添加失败');
|
||||
}
|
||||
}
|
||||
$this->success('新增成功', 'index');
|
||||
} else {
|
||||
$this->error('新增失败');
|
||||
}
|
||||
}
|
||||
|
||||
return ZBuilder::make('form')
|
||||
->addFormItems([
|
||||
['text', 'name', '名称', '可填写中文'],
|
||||
['text', 'url', '链接', '如://at.alicdn.com/t/font_588968_z5hsg7xluoh41jor.css'],
|
||||
])
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 图标列表
|
||||
* @param string $id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function items($id = '')
|
||||
{
|
||||
$data_list = IconListModel::where($this->getMap())
|
||||
->order($this->getOrder('id DESC'))
|
||||
->where('icon_id', $id)
|
||||
->paginate();
|
||||
|
||||
return ZBuilder::make('table')
|
||||
->setTableName('admin_icon_list')
|
||||
->addTopButtons('back')
|
||||
->addTopButton('add', [
|
||||
'title' => '更新图标',
|
||||
'icon' => 'fa fa-refresh',
|
||||
'class' => 'btn btn-primary ajax-get confirm',
|
||||
'href' => url('reload', ['id' => $id])
|
||||
])
|
||||
->setSearch('title,code')
|
||||
->addColumns([
|
||||
['icon', '图标', 'callback', function($data){
|
||||
return '<i class="'.$data['class'].'"></i>';
|
||||
}, '__data__'],
|
||||
['title', '图标标题', 'text.edit'],
|
||||
['code', '图标关键词', 'text.edit'],
|
||||
['class', '图标类名'],
|
||||
])
|
||||
->setRowList($data_list)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新图标
|
||||
* @param string $id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function reload($id = '')
|
||||
{
|
||||
$icon = IconModel::get($id);
|
||||
// 获取图标信息
|
||||
$url = substr($icon['url'], 0, 4) == 'http' ? $icon['url'] : 'http:'.$icon['url'];
|
||||
$content = file_get_contents($url);
|
||||
|
||||
// 获取字体名
|
||||
$font_family = '';
|
||||
$pattern = '/font-family: "(.*)";/';
|
||||
if (preg_match($pattern, $content, $match)) {
|
||||
$font_family = $match[1];
|
||||
} else {
|
||||
$this->error('无法获取字体名');
|
||||
}
|
||||
|
||||
// 拉取图标列表
|
||||
$pattern = '/\.(.*):before/';
|
||||
if (preg_match_all($pattern, $content, $matches)) {
|
||||
$icon_list = [];
|
||||
foreach ($matches[1] as $match) {
|
||||
$icon_list[] = [
|
||||
'icon_id' => $id,
|
||||
'title' => $match,
|
||||
'class' => $font_family . ' ' . $match,
|
||||
'code' => $match,
|
||||
];
|
||||
}
|
||||
$IconListModel = new IconListModel();
|
||||
$IconListModel->where('icon_id', $id)->delete();
|
||||
if ($IconListModel->saveAll($icon_list)) {
|
||||
$this->success('更新成功');
|
||||
} else {
|
||||
$this->error('图标添加失败');
|
||||
}
|
||||
}
|
||||
$this->success('更新成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除图标库
|
||||
* @param string $ids
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function delete($ids = '')
|
||||
{
|
||||
$ids == '' && $this->error('请选择要删除的数据');
|
||||
$ids = (array)$ids;
|
||||
|
||||
// 删除图标列表
|
||||
if (false !== IconListModel::where('icon_id', 'in', $ids)->delete()) {
|
||||
// 删除图标库
|
||||
if (false !== IconModel::where('id', 'in', $ids)->delete()) {
|
||||
$this->success('删除成功');
|
||||
}
|
||||
}
|
||||
$this->error('删除失败');
|
||||
}
|
||||
}
|
||||
25
application/admin/controller/Ie.php
Normal file
25
application/admin/controller/Ie.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Common;
|
||||
|
||||
/**
|
||||
* ie提示页面控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Ie extends Common
|
||||
{
|
||||
/**
|
||||
* 显示ie提示
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function index(){
|
||||
// ie浏览器判断
|
||||
if (get_browser_type() == 'ie') {
|
||||
return $this->fetch();
|
||||
} else {
|
||||
$this->redirect('admin/index/index');
|
||||
}
|
||||
}
|
||||
}
|
||||
157
application/admin/controller/Index.php
Normal file
157
application/admin/controller/Index.php
Normal file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use think\facade\Cache;
|
||||
use think\facade\Env;
|
||||
use think\helper\Hash;
|
||||
use think\Db;
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\user\model\User as UserModel;
|
||||
|
||||
/**
|
||||
* 后台默认控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Index extends Admin
|
||||
{
|
||||
/**
|
||||
* 后台首页
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return string
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$admin_pass = Db::name('admin_user')->where('id', 1)->value('password');
|
||||
|
||||
if (UID == 1 && $admin_pass && Hash::check('admin', $admin_pass)) {
|
||||
$this->assign('default_pass', 1);
|
||||
}
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空系统缓存
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function wipeCache()
|
||||
{
|
||||
$wipe_cache_type = config('wipe_cache_type');
|
||||
if (!empty($wipe_cache_type)) {
|
||||
foreach ($wipe_cache_type as $item) {
|
||||
switch ($item) {
|
||||
case 'TEMP_PATH':
|
||||
array_map('unlink', glob(Env::get('runtime_path'). 'temp/*.*'));
|
||||
break;
|
||||
case 'LOG_PATH':
|
||||
$dirs = (array) glob(Env::get('runtime_path') . 'log/*');
|
||||
foreach ($dirs as $dir) {
|
||||
array_map('unlink', glob($dir . '/*.log'));
|
||||
}
|
||||
array_map('rmdir', $dirs);
|
||||
break;
|
||||
case 'CACHE_PATH':
|
||||
array_map('unlink', glob(Env::get('runtime_path'). 'cache/*.*'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
Cache::clear();
|
||||
$this->success('清空成功');
|
||||
} else {
|
||||
$this->error('请在系统设置中选择需要清除的缓存类型');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 个人设置
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function profile()
|
||||
{
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post();
|
||||
|
||||
$data['nickname'] == '' && $this->error('昵称不能为空');
|
||||
$data['id'] = UID;
|
||||
|
||||
// 如果没有填写密码,则不更新密码
|
||||
if ($data['password'] == '') {
|
||||
unset($data['password']);
|
||||
}
|
||||
|
||||
$UserModel = new UserModel();
|
||||
if ($user = $UserModel->allowField(['nickname', 'email', 'password', 'mobile', 'avatar'])->update($data)) {
|
||||
// 记录行为
|
||||
action_log('user_edit', 'admin_user', UID, UID, get_nickname(UID));
|
||||
$this->success('编辑成功');
|
||||
} else {
|
||||
$this->error('编辑失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
$info = UserModel::where('id', UID)->field('password', true)->find();
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->addFormItems([ // 批量添加表单项
|
||||
['static', 'username', '用户名', '不可更改'],
|
||||
['text', 'nickname', '昵称', '可以是中文'],
|
||||
['text', 'email', '邮箱', ''],
|
||||
['password', 'password', '密码', '必填,6-20位'],
|
||||
['text', 'mobile', '手机号'],
|
||||
['image', 'avatar', '头像']
|
||||
])
|
||||
->setFormData($info) // 设置表单数据
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查版本更新
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return \think\response\Json
|
||||
* @throws \think\db\exception\BindParamException
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function checkUpdate()
|
||||
{
|
||||
$params = config('dolphin.');
|
||||
$params['domain'] = request()->domain();
|
||||
$params['website'] = config('web_site_title');
|
||||
$params['ip'] = $_SERVER['SERVER_ADDR'];
|
||||
$params['php_os'] = PHP_OS;
|
||||
$params['php_version'] = PHP_VERSION;
|
||||
$params['mysql_version'] = db()->query('select version() as version')[0]['version'];
|
||||
$params['server_software'] = $_SERVER['SERVER_SOFTWARE'];
|
||||
$params = http_build_query($params);
|
||||
|
||||
$opts = [
|
||||
CURLOPT_TIMEOUT => 20,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_URL => config('dolphin.product_update'),
|
||||
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'],
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $params
|
||||
];
|
||||
|
||||
// 初始化并执行curl请求
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, $opts);
|
||||
$data = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
$result = json_decode($data, true);
|
||||
|
||||
if ($result['code'] == 1) {
|
||||
return json([
|
||||
'update' => '<a class="badge badge-primary" href="http://www.dolphinphp.com/download" target="_blank">有新版本:'.$result["version"].'</a>',
|
||||
'auth' => $result['auth']
|
||||
]);
|
||||
} else {
|
||||
return json([
|
||||
'update' => '',
|
||||
'auth' => $result['auth']
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
84
application/admin/controller/Log.php
Normal file
84
application/admin/controller/Log.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\admin\model\Log as LogModel;
|
||||
|
||||
/**
|
||||
* 系统日志控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Log extends Admin
|
||||
{
|
||||
/**
|
||||
* 日志列表
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// 查询
|
||||
$map = $this->getMap();
|
||||
// 排序
|
||||
$order = $this->getOrder('admin_log.id desc');
|
||||
// 数据列表
|
||||
$data_list = LogModel::getAll($map, $order);
|
||||
// 分页数据
|
||||
$page = $data_list->render();
|
||||
|
||||
// 使用ZBuilder快速创建数据表格
|
||||
return ZBuilder::make('table')
|
||||
->setPageTitle('系统日志') // 设置页面标题
|
||||
->setSearch(['admin_action.title' => '行为名称', 'admin_user.username' => '执行者', 'admin_module.title' => '所属模块']) // 设置搜索框
|
||||
->hideCheckbox()
|
||||
->addColumns([ // 批量添加数据列
|
||||
['id', '编号'],
|
||||
['title', '行为名称'],
|
||||
['username', '执行者'],
|
||||
['action_ip', '执行IP', 'callback', function($value){
|
||||
return long2ip(intval($value));
|
||||
}],
|
||||
['module_title', '所属模块'],
|
||||
['create_time', '执行时间', 'datetime', '', 'Y-m-d H:i:s'],
|
||||
['right_button', '操作', 'btn']
|
||||
])
|
||||
->addOrder(['title' => 'admin_action', 'username' => 'admin_user', 'module_title' => 'admin_module.title'])
|
||||
->addFilter(['admin_action.title', 'admin_user.username', 'module_title' => 'admin_module.title'])
|
||||
->addRightButton('edit', ['icon' => 'fa fa-eye', 'title' => '详情', 'href' => url('details', ['id' => '__id__'])])
|
||||
->setRowList($data_list) // 设置表格数据
|
||||
->setPages($page) // 设置分页数据
|
||||
->fetch(); // 渲染模板
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志详情
|
||||
* @param null $id 日志id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function details($id = null)
|
||||
{
|
||||
if ($id === null) $this->error('缺少参数');
|
||||
$info = LogModel::getAll(['admin_log.id' => $id]);
|
||||
$info = $info[0];
|
||||
$info['action_ip'] = long2ip(intval($info['action_ip']));
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('编辑') // 设置页面标题
|
||||
->addFormItems([ // 批量添加表单项
|
||||
['hidden', 'id'],
|
||||
['static', 'title', '行为名称'],
|
||||
['static', 'username', '执行者'],
|
||||
['static', 'record_id', '目标ID'],
|
||||
['static', 'action_ip', '执行IP'],
|
||||
['static', 'module_title', '所属模块'],
|
||||
['textarea', 'remark', '备注'],
|
||||
])
|
||||
->hideBtn('submit')
|
||||
->setFormData($info) // 设置表单数据
|
||||
->fetch();
|
||||
}
|
||||
}
|
||||
537
application/admin/controller/Menu.php
Normal file
537
application/admin/controller/Menu.php
Normal file
@ -0,0 +1,537 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\admin\model\Module as ModuleModel;
|
||||
use app\admin\model\Menu as MenuModel;
|
||||
use app\user\model\Role as RoleModel;
|
||||
use think\facade\Cache;
|
||||
|
||||
/**
|
||||
* 节点管理
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Menu extends Admin
|
||||
{
|
||||
/**
|
||||
* 节点首页
|
||||
* @param string $group 分组
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function index($group = 'admin')
|
||||
{
|
||||
// 保存模块排序
|
||||
if ($this->request->isPost()) {
|
||||
$modules = $this->request->post('sort/a');
|
||||
if ($modules) {
|
||||
$data = [];
|
||||
foreach ($modules as $key => $module) {
|
||||
$data[] = [
|
||||
'id' => $module,
|
||||
'sort' => $key + 1
|
||||
];
|
||||
}
|
||||
$MenuModel = new MenuModel();
|
||||
if (false !== $MenuModel->saveAll($data)) {
|
||||
$this->success('保存成功');
|
||||
} else {
|
||||
$this->error('保存失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cookie('__forward__', $_SERVER['REQUEST_URI']);
|
||||
// 配置分组信息
|
||||
$list_group = MenuModel::getGroup();
|
||||
foreach ($list_group as $key => $value) {
|
||||
$tab_list[$key]['title'] = $value;
|
||||
$tab_list[$key]['url'] = url('index', ['group' => $key]);
|
||||
}
|
||||
|
||||
// 模块排序
|
||||
if ($group == 'module-sort') {
|
||||
$map['status'] = 1;
|
||||
$map['pid'] = 0;
|
||||
$modules = MenuModel::where($map)->order('sort,id')->column('icon,title', 'id');
|
||||
$this->assign('modules', $modules);
|
||||
} else {
|
||||
// 获取节点数据
|
||||
$data_list = MenuModel::getMenusByGroup($group);
|
||||
|
||||
$max_level = $this->request->get('max', 0);
|
||||
|
||||
$this->assign('menus', $this->getNestMenu($data_list, $max_level));
|
||||
}
|
||||
|
||||
$this->assign('tab_nav', ['tab_list' => $tab_list, 'curr_tab' => $group]);
|
||||
$this->assign('page_title', '节点管理');
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增节点
|
||||
* @param string $module 所属模块
|
||||
* @param string $pid 所属节点id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function add($module = 'admin', $pid = '')
|
||||
{
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post('', null, 'trim');
|
||||
|
||||
// 验证
|
||||
$result = $this->validate($data, 'Menu');
|
||||
// 验证失败 输出错误信息
|
||||
if(true !== $result) $this->error($result);
|
||||
|
||||
// 顶部节点url检查
|
||||
if ($data['pid'] == 0 && $data['url_value'] == '' && ($data['url_type'] == 'module_admin' || $data['url_type'] == 'module_home')) {
|
||||
$this->error('顶级节点的节点链接不能为空');
|
||||
}
|
||||
|
||||
if ($menu = MenuModel::create($data)) {
|
||||
// 自动创建子节点
|
||||
if ($data['auto_create'] == 1 && !empty($data['child_node'])) {
|
||||
unset($data['icon']);
|
||||
unset($data['params']);
|
||||
$this->createChildNode($data, $menu['id']);
|
||||
}
|
||||
// 添加角色权限
|
||||
if (isset($data['role'])) {
|
||||
$this->setRoleMenu($menu['id'], $data['role']);
|
||||
}
|
||||
Cache::clear();
|
||||
// 记录行为
|
||||
$details = '所属模块('.$data['module'].'),所属节点ID('.$data['pid'].'),节点标题('.$data['title'].'),节点链接('.$data['url_value'].')';
|
||||
action_log('menu_add', 'admin_menu', $menu['id'], UID, $details);
|
||||
$this->success('新增成功', cookie('__forward__'));
|
||||
} else {
|
||||
$this->error('新增失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('新增节点')
|
||||
->addLinkage('module', '所属模块', '', ModuleModel::getModule(), $module, url('ajax/getModuleMenus'), 'pid')
|
||||
->addFormItems([
|
||||
['select', 'pid', '所属节点', '所属上级节点', MenuModel::getMenuTree(0, '', $module), $pid],
|
||||
['text', 'title', '节点标题'],
|
||||
['radio', 'url_type', '链接类型', '', ['module_admin' => '模块链接(后台)', 'module_home' => '模块链接(前台)', 'link' => '普通链接'], 'module_admin']
|
||||
])
|
||||
->addFormItem(
|
||||
'text',
|
||||
'url_value',
|
||||
'节点链接',
|
||||
"可留空,如果是模块链接,请填写<code>模块/控制器/操作</code>,如:<code>admin/menu/add</code>。如果是普通链接,则直接填写url地址,如:<code>http://www.dolphinphp.com</code>"
|
||||
)
|
||||
->addText('params', '参数', '如:a=1&b=2')
|
||||
->addSelect('role', '角色', '除超级管理员外,拥有该节点权限的角色', RoleModel::where('id', 'neq', 1)->column('id,name'), '', 'multiple')
|
||||
->addRadio('auto_create', '自动添加子节点', '选择【是】则自动添加指定的子节点', ['否', '是'], 0)
|
||||
->addCheckbox('child_node', '子节点', '仅上面选项为【是】时起作用', ['add' => '新增', 'edit' => '编辑', 'delete' => '删除', 'enable' => '启用', 'disable' => '禁用', 'quickedit' => '快速编辑'], 'add,edit,delete,enable,disable,quickedit')
|
||||
->addRadio('url_target', '打开方式', '', ['_self' => '当前窗口', '_blank' => '新窗口'], '_self')
|
||||
->addIcon('icon', '图标', '导航图标')
|
||||
->addRadio('online_hide', '网站上线后隐藏', '关闭开发模式后,则隐藏该菜单节点', ['否', '是'], 0)
|
||||
->addText('sort', '排序', '', 100)
|
||||
->setTrigger('auto_create', '1', 'child_node', false)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑节点
|
||||
* @param int $id 节点ID
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function edit($id = 0)
|
||||
{
|
||||
if ($id === 0) $this->error('缺少参数');
|
||||
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post('', null, 'trim');
|
||||
|
||||
// 验证
|
||||
$result = $this->validate($data, 'Menu');
|
||||
// 验证失败 输出错误信息
|
||||
if(true !== $result) $this->error($result);
|
||||
|
||||
// 顶部节点url检查
|
||||
if ($data['pid'] == 0 && $data['url_value'] == '' && ($data['url_type'] == 'module_admin' || $data['url_type'] == 'module_home')) {
|
||||
$this->error('顶级节点的节点链接不能为空');
|
||||
}
|
||||
|
||||
// 设置角色权限
|
||||
$this->setRoleMenu($data['id'], isset($data['role']) ? $data['role'] : []);
|
||||
|
||||
// 验证是否更改所属模块,如果是,则该节点的所有子孙节点的模块都要修改
|
||||
$map['id'] = $data['id'];
|
||||
$map['module'] = $data['module'];
|
||||
if (!MenuModel::where($map)->find()) {
|
||||
MenuModel::changeModule($data['id'], $data['module']);
|
||||
}
|
||||
|
||||
if (MenuModel::update($data)) {
|
||||
Cache::clear();
|
||||
// 记录行为
|
||||
$details = '节点ID('.$id.')';
|
||||
action_log('menu_edit', 'admin_menu', $id, UID, $details);
|
||||
$this->success('编辑成功', cookie('__forward__'));
|
||||
} else {
|
||||
$this->error('编辑失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
$info = MenuModel::get($id);
|
||||
// 拥有该节点权限的角色
|
||||
$info['role'] = RoleModel::getRoleWithMenu($id);
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('编辑节点')
|
||||
->addFormItem('hidden', 'id')
|
||||
->addLinkage('module', '所属模块', '', ModuleModel::getModule(), '', url('ajax/getModuleMenus'), 'pid')
|
||||
->addFormItem('select', 'pid', '所属节点', '所属上级节点', MenuModel::getMenuTree(0, '', $info['module']))
|
||||
->addFormItem('text', 'title', '节点标题')
|
||||
->addFormItem('radio', 'url_type', '链接类型', '', ['module_admin' => '模块链接(后台)', 'module_home' => '模块链接(前台)', 'link' => '普通链接'], 'module_admin')
|
||||
->addFormItem(
|
||||
'text',
|
||||
'url_value',
|
||||
'节点链接',
|
||||
"可留空,如果是模块链接,请填写<code>模块/控制器/操作</code>,如:<code>admin/menu/add</code>。如果是普通链接,则直接填写url地址,如:<code>http://www.dolphinphp.com</code>"
|
||||
)
|
||||
->addText('params', '参数', '如:a=1&b=2')
|
||||
->addSelect('role', '角色', '除超级管理员外,拥有该节点权限的角色', RoleModel::where('id', 'neq', 1)->column('id,name'), '', 'multiple')
|
||||
->addRadio('url_target', '打开方式', '', ['_self' => '当前窗口', '_blank' => '新窗口'], '_self')
|
||||
->addIcon('icon', '图标', '导航图标')
|
||||
->addRadio('online_hide', '网站上线后隐藏', '关闭开发模式后,则隐藏该菜单节点', ['否', '是'])
|
||||
->addText('sort', '排序', '', 100)
|
||||
->setFormData($info)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置角色权限
|
||||
* @param string $role_id 角色id
|
||||
* @param array $roles 角色id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function setRoleMenu($role_id = '', $roles = [])
|
||||
{
|
||||
$RoleModel = new RoleModel();
|
||||
|
||||
// 该节点的所有子节点,包括本身节点
|
||||
$menu_child = MenuModel::getChildsId($role_id);
|
||||
$menu_child[] = (int)$role_id;
|
||||
// 该节点的所有上下级节点
|
||||
$menu_all = MenuModel::getLinkIds($role_id);
|
||||
$menu_all = array_map('strval', $menu_all);
|
||||
|
||||
if (!empty($roles)) {
|
||||
// 拥有该节点的所有角色id及节点权限
|
||||
$role_menu_auth = RoleModel::getRoleWithMenu($role_id, true);
|
||||
// 已有该节点权限的角色id
|
||||
$role_exists = array_keys($role_menu_auth);
|
||||
// 新节点权限的角色
|
||||
$role_new = $roles;
|
||||
// 原有权限角色差集
|
||||
$role_diff = array_diff($role_exists, $role_new);
|
||||
// 新权限角色差集
|
||||
$role_diff_new = array_diff($role_new, $role_exists);
|
||||
// 新节点角色权限
|
||||
$role_new_auth = RoleModel::getAuthWithRole($roles);
|
||||
|
||||
// 删除原先角色的该节点权限
|
||||
if ($role_diff) {
|
||||
$role_del_auth = [];
|
||||
foreach ($role_diff as $role) {
|
||||
$auth = json_decode($role_menu_auth[$role], true);
|
||||
$auth_new = array_diff($auth, $menu_child);
|
||||
$role_del_auth[] = [
|
||||
'id' => $role,
|
||||
'menu_auth' => array_values($auth_new)
|
||||
];
|
||||
}
|
||||
if ($role_del_auth) {
|
||||
$RoleModel->saveAll($role_del_auth);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增权限角色
|
||||
if ($role_diff_new) {
|
||||
$role_update_auth = [];
|
||||
foreach ($role_new_auth as $role => $auth) {
|
||||
$auth = json_decode($auth, true);
|
||||
if (in_array($role, $role_diff_new)) {
|
||||
$auth = array_unique(array_merge($auth, $menu_all));
|
||||
}
|
||||
$role_update_auth[] = [
|
||||
'id' => $role,
|
||||
'menu_auth' => array_values($auth)
|
||||
];
|
||||
}
|
||||
if ($role_update_auth) {
|
||||
$RoleModel->saveAll($role_update_auth);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$role_menu_auth = RoleModel::getRoleWithMenu($role_id, true);
|
||||
$role_del_auth = [];
|
||||
foreach ($role_menu_auth as $role => $auth) {
|
||||
$auth = json_decode($auth, true);
|
||||
$auth_new = array_diff($auth, $menu_child);
|
||||
$role_del_auth[] = [
|
||||
'id' => $role,
|
||||
'menu_auth' => array_values($auth_new)
|
||||
];
|
||||
}
|
||||
if ($role_del_auth) {
|
||||
$RoleModel->saveAll($role_del_auth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除节点
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function delete($record = [])
|
||||
{
|
||||
$id = $this->request->param('id');
|
||||
$menu = MenuModel::where('id', $id)->find();
|
||||
|
||||
if ($menu['system_menu'] == '1') $this->error('系统节点,禁止删除');
|
||||
|
||||
// 获取该节点的所有后辈节点id
|
||||
$menu_childs = MenuModel::getChildsId($id);
|
||||
|
||||
// 要删除的所有节点id
|
||||
$all_ids = array_merge([(int)$id], $menu_childs);
|
||||
|
||||
// 删除节点
|
||||
if (MenuModel::destroy($all_ids)) {
|
||||
Cache::clear();
|
||||
// 记录行为
|
||||
$details = '节点ID('.$id.'),节点标题('.$menu['title'].'),节点链接('.$menu['url_value'].')';
|
||||
action_log('menu_delete', 'admin_menu', $id, UID, $details);
|
||||
$this->success('删除成功');
|
||||
} else {
|
||||
$this->error('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存节点排序
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post();
|
||||
if (!empty($data)) {
|
||||
$menus = $this->parseMenu($data['menus']);
|
||||
foreach ($menus as $menu) {
|
||||
if ($menu['pid'] == 0) {
|
||||
continue;
|
||||
}
|
||||
MenuModel::update($menu);
|
||||
}
|
||||
Cache::clear();
|
||||
$this->success('保存成功');
|
||||
} else {
|
||||
$this->error('没有需要保存的节点');
|
||||
}
|
||||
}
|
||||
$this->error('非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加子节点
|
||||
* @param array $data 节点数据
|
||||
* @param string $pid 父节点id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
private function createChildNode($data = [], $pid = '')
|
||||
{
|
||||
$url_value = substr($data['url_value'], 0, strrpos($data['url_value'], '/')).'/';
|
||||
$child_node = [];
|
||||
$data['pid'] = $pid;
|
||||
|
||||
foreach ($data['child_node'] as $item) {
|
||||
switch ($item) {
|
||||
case 'add':
|
||||
$data['title'] = '新增';
|
||||
break;
|
||||
case 'edit':
|
||||
$data['title'] = '编辑';
|
||||
break;
|
||||
case 'delete':
|
||||
$data['title'] = '删除';
|
||||
break;
|
||||
case 'enable':
|
||||
$data['title'] = '启用';
|
||||
break;
|
||||
case 'disable':
|
||||
$data['title'] = '禁用';
|
||||
break;
|
||||
case 'quickedit':
|
||||
$data['title'] = '快速编辑';
|
||||
break;
|
||||
}
|
||||
$data['url_value'] = $url_value.$item;
|
||||
$data['create_time'] = $this->request->time();
|
||||
$data['update_time'] = $this->request->time();
|
||||
$child_node[] = $data;
|
||||
}
|
||||
|
||||
if ($child_node) {
|
||||
$MenuModel = new MenuModel();
|
||||
$MenuModel->insertAll($child_node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归解析节点
|
||||
* @param array $menus 节点数据
|
||||
* @param int $pid 上级节点id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array 解析成可以写入数据库的格式
|
||||
*/
|
||||
private function parseMenu($menus = [], $pid = 0)
|
||||
{
|
||||
$sort = 1;
|
||||
$result = [];
|
||||
foreach ($menus as $menu) {
|
||||
$result[] = [
|
||||
'id' => (int)$menu['id'],
|
||||
'pid' => (int)$pid,
|
||||
'sort' => $sort,
|
||||
];
|
||||
if (isset($menu['children'])) {
|
||||
$result = array_merge($result, $this->parseMenu($menu['children'], $menu['id']));
|
||||
}
|
||||
$sort ++;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取嵌套式节点
|
||||
* @param array $lists 原始节点数组
|
||||
* @param int $pid 父级id
|
||||
* @param int $max_level 最多返回多少层,0为不限制
|
||||
* @param int $curr_level 当前层数
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return string
|
||||
*/
|
||||
private function getNestMenu($lists = [], $max_level = 0, $pid = 0, $curr_level = 1)
|
||||
{
|
||||
$result = '';
|
||||
foreach ($lists as $key => $value) {
|
||||
if ($value['pid'] == $pid) {
|
||||
$disable = $value['status'] == 0 ? 'dd-disable' : '';
|
||||
|
||||
// 组合节点
|
||||
$result .= '<li class="dd-item dd3-item '.$disable.'" data-id="'.$value['id'].'">';
|
||||
$result .= '<div class="dd-handle dd3-handle">拖拽</div><div class="dd3-content"><i class="'.$value['icon'].'"></i> '.$value['title'];
|
||||
if ($value['url_value'] != '') {
|
||||
$result .= '<span class="link"><i class="fa fa-link"></i> '.$value['url_value'].'</span>';
|
||||
}
|
||||
$result .= '<div class="action">';
|
||||
$result .= '<a href="'.url('add', ['module' => $value['module'], 'pid' => $value['id']]).'" data-toggle="tooltip" data-original-title="新增子节点"><i class="list-icon fa fa-plus fa-fw"></i></a><a href="'.url('edit', ['id' => $value['id']]).'" data-toggle="tooltip" data-original-title="编辑"><i class="list-icon fa fa-pencil fa-fw"></i></a>';
|
||||
if ($value['status'] == 0) {
|
||||
// 启用
|
||||
$result .= '<a href="javascript:void(0);" data-ids="'.$value['id'].'" class="enable" data-toggle="tooltip" data-original-title="启用"><i class="list-icon fa fa-check-circle-o fa-fw"></i></a>';
|
||||
} else {
|
||||
// 禁用
|
||||
$result .= '<a href="javascript:void(0);" data-ids="'.$value['id'].'" class="disable" data-toggle="tooltip" data-original-title="禁用"><i class="list-icon fa fa-ban fa-fw"></i></a>';
|
||||
}
|
||||
$result .= '<a href="'.url('delete', ['id' => $value['id'], 'table' => 'admin_menu']).'" data-toggle="tooltip" data-original-title="删除" class="ajax-get confirm"><i class="list-icon fa fa-times fa-fw"></i></a></div>';
|
||||
$result .= '</div>';
|
||||
|
||||
if ($max_level == 0 || $curr_level != $max_level) {
|
||||
unset($lists[$key]);
|
||||
// 下级节点
|
||||
$children = $this->getNestMenu($lists, $max_level, $value['id'], $curr_level + 1);
|
||||
if ($children != '') {
|
||||
$result .= '<ol class="dd-list">'.$children.'</ol>';
|
||||
}
|
||||
}
|
||||
|
||||
$result .= '</li>';
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用节点
|
||||
* @param array $record 行为日志
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function enable($record = [])
|
||||
{
|
||||
$id = input('param.ids');
|
||||
$menu = MenuModel::where('id', $id)->find();
|
||||
$details = '节点ID('.$id.'),节点标题('.$menu['title'].'),节点链接('.$menu['url_value'].')';
|
||||
$this->setStatus('enable', ['menu_enable', 'admin_menu', $id, UID, $details]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用节点
|
||||
* @param array $record 行为日志
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function disable($record = [])
|
||||
{
|
||||
$id = input('param.ids');
|
||||
$menu = MenuModel::where('id', $id)->find();
|
||||
$details = '节点ID('.$id.'),节点标题('.$menu['title'].'),节点链接('.$menu['url_value'].')';
|
||||
$this->setStatus('disable', ['menu_disable', 'admin_menu', $id, UID, $details]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态
|
||||
* @param string $type 类型
|
||||
* @param array $record 行为日志
|
||||
* @author 小乌 <82950492@qq.com>
|
||||
*/
|
||||
public function setStatus($type = '', $record = [])
|
||||
{
|
||||
$id = input('param.ids');
|
||||
|
||||
$status = $type == 'enable' ? 1 : 0;
|
||||
|
||||
if (false !== MenuModel::where('id', $id)->setField('status', $status)) {
|
||||
Cache::clear();
|
||||
// 记录行为日志
|
||||
if (!empty($record)) {
|
||||
call_user_func_array('action_log', $record);
|
||||
}
|
||||
$this->success('操作成功');
|
||||
} else {
|
||||
$this->error('操作失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
70
application/admin/controller/Message.php
Normal file
70
application/admin/controller/Message.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\user\model\Message as MessageModel;
|
||||
|
||||
/**
|
||||
* 消息控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Message extends Admin
|
||||
{
|
||||
/**
|
||||
* 消息中心
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data_list = MessageModel::where($this->getMap())
|
||||
->where('uid_receive', UID)
|
||||
->order($this->getOrder('id DESC'))
|
||||
->paginate();
|
||||
|
||||
return ZBuilder::make('table')
|
||||
->setTableName('admin_message')
|
||||
->addTopButton('enable', ['title' => '设置已阅读'])
|
||||
->addTopButton('delete')
|
||||
->addRightButton('enable', ['title' => '设置已阅读'])
|
||||
->addRightButton('delete')
|
||||
->addColumns([
|
||||
['uid_send', '发送者', 'callback', 'get_nickname'],
|
||||
['type', '分类'],
|
||||
['content', '内容'],
|
||||
['status', '状态', 'status', '', ['未读', '已读']],
|
||||
['create_time', '发送时间', 'datetime'],
|
||||
['read_time', '阅读时间', 'datetime'],
|
||||
['right_button', '操作', 'btn'],
|
||||
])
|
||||
->addFilter('type')
|
||||
->addFilter('status', ['未读', '已读'])
|
||||
->setRowList($data_list)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置已阅读
|
||||
* @param array $ids
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function enable($ids = [])
|
||||
{
|
||||
empty($ids) && $this->error('参数错误');
|
||||
$map = [
|
||||
['uid_receive', '=', UID],
|
||||
['id', 'in', $ids]
|
||||
];
|
||||
$result = MessageModel::where($map)
|
||||
->update(['status' => 1, 'read_time' => $this->request->time()]);
|
||||
if (false !== $result) {
|
||||
$this->success('设置成功');
|
||||
} else {
|
||||
$this->error('设置失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
607
application/admin/controller/Module.php
Normal file
607
application/admin/controller/Module.php
Normal file
@ -0,0 +1,607 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\model\Module as ModuleModel;
|
||||
use app\admin\model\Plugin as PluginModel;
|
||||
use app\admin\model\Menu as MenuModel;
|
||||
use app\admin\model\Action as ActionModel;
|
||||
use think\facade\Cache;
|
||||
use util\Database;
|
||||
use util\Sql;
|
||||
use util\File;
|
||||
use util\PHPZip;
|
||||
use util\Tree;
|
||||
use think\Db;
|
||||
use think\facade\Hook;
|
||||
use think\facade\Env;
|
||||
|
||||
/**
|
||||
* 模块管理控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Module extends Admin
|
||||
{
|
||||
/**
|
||||
* 模块首页
|
||||
* @param string $group 分组
|
||||
* @param string $type 显示类型
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function index($group = 'local', $type = '')
|
||||
{
|
||||
// 配置分组信息
|
||||
$list_group = ['local' => '本地模块'];
|
||||
$tab_list = [];
|
||||
foreach ($list_group as $key => $value) {
|
||||
$tab_list[$key]['title'] = $value;
|
||||
$tab_list[$key]['url'] = url('index', ['group' => $key]);
|
||||
}
|
||||
|
||||
// 监听tab钩子
|
||||
Hook::listen('module_index_tab_list', $tab_list);
|
||||
|
||||
switch ($group) {
|
||||
case 'local':
|
||||
// 查询条件
|
||||
$keyword = $this->request->get('keyword', '');
|
||||
|
||||
if (input('?param.status') && input('param.status') != '_all') {
|
||||
$status = input('param.status');
|
||||
} else {
|
||||
$status = '';
|
||||
}
|
||||
|
||||
$ModuleModel = new ModuleModel();
|
||||
$result = $ModuleModel->getAll($keyword, $status);
|
||||
|
||||
if ($result['modules'] === false) {
|
||||
$this->error($ModuleModel->getError());
|
||||
}
|
||||
|
||||
$type_show = Cache::get('module_type_show');
|
||||
$type_show = $type != '' ? $type : ($type_show == false ? 'block' : $type_show);
|
||||
Cache::set('module_type_show', $type_show);
|
||||
$type = $type_show == 'block' ? 'list' : 'block';
|
||||
|
||||
$this->assign('page_title', '模块管理');
|
||||
$this->assign('modules', $result['modules']);
|
||||
$this->assign('total', $result['total']);
|
||||
$this->assign('tab_nav', ['tab_list' => $tab_list, 'curr_tab' => $group]);
|
||||
$this->assign('type', $type);
|
||||
return $this->fetch();
|
||||
break;
|
||||
case 'online':
|
||||
return '<h2>正在建设中...</h2>';
|
||||
break;
|
||||
default:
|
||||
$this->error('非法操作');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装模块
|
||||
* @param string $name 模块标识
|
||||
* @param int $confirm 是否确认
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function install($name = '', $confirm = 0)
|
||||
{
|
||||
// 设置最大执行时间和内存大小
|
||||
ini_set('max_execution_time', '0');
|
||||
ini_set('memory_limit', '1024M');
|
||||
|
||||
if ($name == '') $this->error('模块不存在!');
|
||||
if ($name == 'admin' || $name == 'user') $this->error('禁止操作系统核心模块!');
|
||||
|
||||
// 模块配置信息
|
||||
$module_info = ModuleModel::getInfoFromFile($name);
|
||||
|
||||
if ($confirm == 0) {
|
||||
$need_module = [];
|
||||
$need_plugin = [];
|
||||
$table_check = [];
|
||||
// 检查模块依赖
|
||||
if (isset($module_info['need_module']) && !empty($module_info['need_module'])) {
|
||||
$need_module = $this->checkDependence('module', $module_info['need_module']);
|
||||
}
|
||||
|
||||
// 检查插件依赖
|
||||
if (isset($module_info['need_plugin']) && !empty($module_info['need_plugin'])) {
|
||||
$need_plugin = $this->checkDependence('plugin', $module_info['need_plugin']);
|
||||
}
|
||||
|
||||
// 检查数据表
|
||||
if (isset($module_info['tables']) && !empty($module_info['tables'])) {
|
||||
foreach ($module_info['tables'] as $table) {
|
||||
if (Db::query("SHOW TABLES LIKE '".config('database.prefix')."{$table}'")) {
|
||||
$table_check[] = [
|
||||
'table' => config('database.prefix')."{$table}",
|
||||
'result' => '<span class="text-danger">存在同名</span>'
|
||||
];
|
||||
} else {
|
||||
$table_check[] = [
|
||||
'table' => config('database.prefix')."{$table}",
|
||||
'result' => '<i class="fa fa-check text-success"></i>'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->assign('need_module', $need_module);
|
||||
$this->assign('need_plugin', $need_plugin);
|
||||
$this->assign('table_check', $table_check);
|
||||
$this->assign('name', $name);
|
||||
$this->assign('page_title', '安装模块:'. $name);
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
// 执行安装文件
|
||||
$install_file = realpath(Env::get('app_path').$name.'/install.php');
|
||||
if (file_exists($install_file)) {
|
||||
@include($install_file);
|
||||
}
|
||||
|
||||
// 执行安装模块sql文件
|
||||
$sql_file = realpath(Env::get('app_path').$name.'/sql/install.sql');
|
||||
if (file_exists($sql_file)) {
|
||||
if (isset($module_info['database_prefix']) && !empty($module_info['database_prefix'])) {
|
||||
$sql_statement = Sql::getSqlFromFile($sql_file, false, [$module_info['database_prefix'] => config('database.prefix')]);
|
||||
} else {
|
||||
$sql_statement = Sql::getSqlFromFile($sql_file);
|
||||
}
|
||||
if (!empty($sql_statement)) {
|
||||
foreach ($sql_statement as $value) {
|
||||
try{
|
||||
Db::execute($value);
|
||||
}catch(\Exception $e){
|
||||
$this->error('导入SQL失败,请检查install.sql的语句是否正确');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加菜单
|
||||
$menus = ModuleModel::getMenusFromFile($name);
|
||||
if (is_array($menus) && !empty($menus)) {
|
||||
if (false === $this->addMenus($menus, $name)) {
|
||||
$this->error('菜单添加失败,请重新安装');
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否有模块设置信息
|
||||
if (isset($module_info['config']) && !empty($module_info['config'])) {
|
||||
$module_info['config'] = json_encode(parse_config($module_info['config']));
|
||||
}
|
||||
|
||||
// 检查是否有模块授权配置
|
||||
if (isset($module_info['access']) && !empty($module_info['access'])) {
|
||||
$module_info['access'] = json_encode($module_info['access']);
|
||||
}
|
||||
|
||||
// 检查是否有行为规则
|
||||
if (isset($module_info['action']) && !empty($module_info['action'])) {
|
||||
$ActionModel = new ActionModel;
|
||||
if (!$ActionModel->saveAll($module_info['action'])) {
|
||||
MenuModel::where('module', $name)->delete();
|
||||
$this->error('行为添加失败,请重新安装');
|
||||
}
|
||||
}
|
||||
|
||||
// 将模块信息写入数据库
|
||||
$ModuleModel = new ModuleModel($module_info);
|
||||
$allowField = ['name','title','icon','description','author','author_url','config','access','version','identifier','status'];
|
||||
|
||||
if ($ModuleModel->allowField($allowField)->save()) {
|
||||
// 复制静态资源目录
|
||||
File::copy_dir(Env::get('app_path'). $name. '/public', Env::get('root_path'). 'public');
|
||||
// 删除静态资源目录
|
||||
File::del_dir(Env::get('app_path'). $name. '/public');
|
||||
cache('modules', null);
|
||||
cache('module_all', null);
|
||||
// 记录行为
|
||||
action_log('module_install', 'admin_module', 0, UID, $module_info['title']);
|
||||
$this->success('模块安装成功', 'index');
|
||||
} else {
|
||||
MenuModel::where('module', $name)->delete();
|
||||
$this->error('模块安装失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载模块
|
||||
* @param string $name 模块名
|
||||
* @param int $confirm 是否确认
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function uninstall($name = '', $confirm = 0)
|
||||
{
|
||||
if ($name == '') $this->error('模块不存在!');
|
||||
if ($name == 'admin') $this->error('禁止操作系统模块!');
|
||||
|
||||
// 模块配置信息
|
||||
$module_info = ModuleModel::getInfoFromFile($name);
|
||||
|
||||
if ($confirm == 0) {
|
||||
$this->assign('name', $name);
|
||||
$this->assign('page_title', '卸载模块:'. $name);
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
// 执行卸载文件
|
||||
$uninstall_file = realpath(Env::get('app_path').$name.'/uninstall.php');
|
||||
if (file_exists($uninstall_file)) {
|
||||
@include($uninstall_file);
|
||||
}
|
||||
|
||||
// 执行卸载模块sql文件
|
||||
$clear = $this->request->get('clear');
|
||||
if ($clear == 1) {
|
||||
$sql_file = realpath(Env::get('app_path').$name.'/sql/uninstall.sql');
|
||||
if (file_exists($sql_file)) {
|
||||
if (isset($module_info['database_prefix']) && !empty($module_info['database_prefix'])) {
|
||||
$sql_statement = Sql::getSqlFromFile($sql_file, false, [$module_info['database_prefix'] => config('database.prefix')]);
|
||||
} else {
|
||||
$sql_statement = Sql::getSqlFromFile($sql_file);
|
||||
}
|
||||
|
||||
if (!empty($sql_statement)) {
|
||||
foreach ($sql_statement as $sql) {
|
||||
try{
|
||||
Db::execute($sql);
|
||||
}catch(\Exception $e){
|
||||
$this->error('卸载失败,请检查uninstall.sql的语句是否正确');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除菜单
|
||||
if (false === MenuModel::where('module', $name)->delete()) {
|
||||
$this->error('菜单删除失败,请重新卸载');
|
||||
}
|
||||
|
||||
// 删除授权信息
|
||||
if (false === Db::name('admin_access')->where('module', $name)->delete()) {
|
||||
$this->error('删除授权信息失败,请重新卸载');
|
||||
}
|
||||
|
||||
// 删除行为规则
|
||||
if (false === Db::name('admin_action')->where('module', $name)->delete()) {
|
||||
$this->error('删除行为信息失败,请重新卸载');
|
||||
}
|
||||
|
||||
// 删除模块信息
|
||||
if (ModuleModel::where('name', $name)->delete()) {
|
||||
// 复制静态资源目录
|
||||
File::copy_dir(Env::get('root_path'). 'public/static/'. $name, Env::get('app_path').$name.'/public/static/'. $name);
|
||||
// 删除静态资源目录
|
||||
File::del_dir(Env::get('root_path'). 'public/static/'. $name);
|
||||
cache('modules', null);
|
||||
cache('module_all', null);
|
||||
// 记录行为
|
||||
action_log('module_uninstall', 'admin_module', 0, UID, $module_info['title']);
|
||||
$this->success('模块卸载成功', 'index');
|
||||
} else {
|
||||
$this->error('模块卸载失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新模块配置
|
||||
* @param string $name 模块名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function update($name = '')
|
||||
{
|
||||
$name == '' && $this->error('缺少模块名!');
|
||||
|
||||
$Module = ModuleModel::get(['name' => $name]);
|
||||
!$Module && $this->error('模块不存在,或未安装');
|
||||
|
||||
// 模块配置信息
|
||||
$module_info = ModuleModel::getInfoFromFile($name);
|
||||
unset($module_info['name']);
|
||||
|
||||
// 检查是否有模块设置信息
|
||||
if (isset($module_info['config']) && !empty($module_info['config'])) {
|
||||
$module_info['config'] = json_encode(parse_config($module_info['config']));
|
||||
} else {
|
||||
$module_info['config'] = '';
|
||||
}
|
||||
|
||||
// 检查是否有模块授权配置
|
||||
if (isset($module_info['access']) && !empty($module_info['access'])) {
|
||||
$module_info['access'] = json_encode($module_info['access']);
|
||||
} else {
|
||||
$module_info['access'] = '';
|
||||
}
|
||||
|
||||
// 更新模块信息
|
||||
if (false !== $Module->save($module_info)) {
|
||||
$this->success('模块配置更新成功');
|
||||
} else {
|
||||
$this->error('模块配置更新失败,请重试');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出模块
|
||||
* @param string $name 模块名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function export($name = '')
|
||||
{
|
||||
if ($name == '') $this->error('缺少模块名');
|
||||
|
||||
$export_data = $this->request->get('export_data', '');
|
||||
if ($export_data == '') {
|
||||
$this->assign('page_title', '导出模块:'. $name);
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
// 模块导出目录
|
||||
$module_dir = Env::get('root_path'). 'export/module/'. $name;
|
||||
|
||||
// 删除旧的导出数据
|
||||
if (is_dir($module_dir)) {
|
||||
File::del_dir($module_dir);
|
||||
}
|
||||
|
||||
// 复制模块目录到导出目录
|
||||
File::copy_dir(Env::get('app_path'). $name, $module_dir);
|
||||
// 复制静态资源目录
|
||||
File::copy_dir(Env::get('root_path'). 'public/static/'. $name, $module_dir.'/public/static/'. $name);
|
||||
|
||||
// 模块本地配置信息
|
||||
$module_info = ModuleModel::getInfoFromFile($name);
|
||||
|
||||
// 检查是否有模块设置信息
|
||||
if (isset($module_info['config'])) {
|
||||
$db_config = ModuleModel::where('name', $name)->value('config');
|
||||
$db_config = json_decode($db_config, true);
|
||||
// 获取最新的模块设置信息
|
||||
$module_info['config'] = set_config_value($module_info['config'], $db_config);
|
||||
}
|
||||
|
||||
// 检查是否有模块行为信息
|
||||
$action = Db::name('admin_action')->where('module', $name)->field('module,name,title,remark,rule,log,status')->select();
|
||||
if ($action) {
|
||||
$module_info['action'] = $action;
|
||||
}
|
||||
|
||||
// 表前缀
|
||||
$module_info['database_prefix'] = config('database.prefix');
|
||||
|
||||
// 生成配置文件
|
||||
if (false === $this->buildInfoFile($module_info, $name)) {
|
||||
$this->error('模块配置文件创建失败,请重新导出');
|
||||
}
|
||||
|
||||
// 获取模型菜单并导出
|
||||
$fields = 'id,pid,title,icon,url_type,url_value,url_target,online_hide,sort,status';
|
||||
$menus = MenuModel::getMenusByGroup($name, $fields);
|
||||
if (false === $this->buildMenuFile($menus, $name)) {
|
||||
$this->error('模型菜单文件创建失败,请重新导出');
|
||||
}
|
||||
|
||||
// 导出数据库表
|
||||
if (isset($module_info['tables']) && !empty($module_info['tables'])) {
|
||||
if (!is_dir($module_dir. '/sql')) {
|
||||
mkdir($module_dir. '/sql', 644, true);
|
||||
}
|
||||
if (!Database::export($module_info['tables'], $module_dir. '/sql/install.sql', config('database.prefix'), $export_data)) {
|
||||
$this->error('数据库文件创建失败,请重新导出');
|
||||
}
|
||||
if (!Database::exportUninstall($module_info['tables'], $module_dir. '/sql/uninstall.sql', config('database.prefix'))) {
|
||||
$this->error('数据库文件创建失败,请重新导出');
|
||||
}
|
||||
}
|
||||
|
||||
// 记录行为
|
||||
action_log('module_export', 'admin_module', 0, UID, $module_info['title']);
|
||||
|
||||
// 打包下载
|
||||
$archive = new PHPZip;
|
||||
return $archive->ZipAndDownload($module_dir, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模块菜单文件
|
||||
* @param array $menus 菜单
|
||||
* @param string $name 模块名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return int
|
||||
*/
|
||||
private function buildMenuFile($menus = [], $name = '')
|
||||
{
|
||||
$menus = Tree::toLayer($menus);
|
||||
|
||||
// 美化数组格式
|
||||
$menus = var_export($menus, true);
|
||||
$menus = preg_replace("/(\d+|'id'|'pid') =>(.*)/", '', $menus);
|
||||
$menus = preg_replace("/'child' => (.*)(\r\n|\r|\n)\s*array/", "'child' => $1array", $menus);
|
||||
$menus = str_replace(['array (', ')'], ['[', ']'], $menus);
|
||||
$menus = preg_replace("/(\s*?\r?\n\s*?)+/", "\n", $menus);
|
||||
|
||||
$content = <<<INFO
|
||||
<?php
|
||||
/**
|
||||
* 菜单信息
|
||||
*/
|
||||
return {$menus};
|
||||
|
||||
INFO;
|
||||
// 写入到文件
|
||||
return file_put_contents(Env::get('root_path'). 'export/module/'. $name. '/menus.php', $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模块配置文件
|
||||
* @param array $info 模块配置信息
|
||||
* @param string $name 模块名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return int
|
||||
*/
|
||||
private function buildInfoFile($info = [], $name = '')
|
||||
{
|
||||
// 美化数组格式
|
||||
$info = var_export($info, true);
|
||||
$info = preg_replace("/'(.*)' => (.*)(\r\n|\r|\n)\s*array/", "'$1' => array", $info);
|
||||
$info = preg_replace("/(\d+) => (\s*)(\r\n|\r|\n)\s*array/", "array", $info);
|
||||
$info = preg_replace("/(\d+ => )/", "", $info);
|
||||
$info = preg_replace("/array \((\r\n|\r|\n)\s*\)/", "[)", $info);
|
||||
$info = preg_replace("/array \(/", "[", $info);
|
||||
$info = preg_replace("/\)/", "]", $info);
|
||||
|
||||
$content = <<<INFO
|
||||
<?php
|
||||
/**
|
||||
* 模块信息
|
||||
*/
|
||||
return {$info};
|
||||
|
||||
INFO;
|
||||
// 写入到文件
|
||||
return file_put_contents(Env::get('root_path'). 'export/module/'. $name. '/info.php', $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态
|
||||
* @param string $type 类型:disable/enable
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function setStatus($type = '', $record = [])
|
||||
{
|
||||
$ids = input('param.ids');
|
||||
empty($ids) && $this->error('缺少主键');
|
||||
|
||||
$module = ModuleModel::where('id', $ids)->find();
|
||||
$module['system_module'] == 1 && $this->error('禁止操作系统内置模块');
|
||||
|
||||
$status = $type == 'enable' ? 1 : 0;
|
||||
|
||||
// 将模块对应的菜单禁用或启用
|
||||
$map = [
|
||||
'pid' => 0,
|
||||
'module' => $module['name']
|
||||
];
|
||||
MenuModel::where($map)->setField('status', $status);
|
||||
|
||||
if (false !== ModuleModel::where('id', $ids)->setField('status', $status)) {
|
||||
// 记录日志
|
||||
call_user_func_array('action_log', ['module_'.$type, 'admin_module', 0, UID, $module['title']]);
|
||||
$this->success('操作成功');
|
||||
} else {
|
||||
$this->error('操作失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用模块
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function disable($record = [])
|
||||
{
|
||||
$this->setStatus('disable');
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用模块
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function enable($record = [])
|
||||
{
|
||||
$this->setStatus('enable');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加模型菜单
|
||||
* @param array $menus 菜单
|
||||
* @param string $module 模型名称
|
||||
* @param int $pid 父级ID
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
*/
|
||||
private function addMenus($menus = [], $module = '', $pid = 0)
|
||||
{
|
||||
foreach ($menus as $menu) {
|
||||
$data = [
|
||||
'pid' => $pid,
|
||||
'module' => $module,
|
||||
'title' => $menu['title'],
|
||||
'icon' => isset($menu['icon']) ? $menu['icon'] : 'fa fa-fw fa-puzzle-piece',
|
||||
'url_type' => isset($menu['url_type']) ? $menu['url_type'] : 'module_admin',
|
||||
'url_value' => isset($menu['url_value']) ? $menu['url_value'] : '',
|
||||
'url_target' => isset($menu['url_target']) ? $menu['url_target'] : '_self',
|
||||
'online_hide' => isset($menu['online_hide']) ? $menu['online_hide'] : 0,
|
||||
'status' => isset($menu['status']) ? $menu['status'] : 1
|
||||
];
|
||||
|
||||
$result = MenuModel::create($data);
|
||||
if (!$result) return false;
|
||||
|
||||
if (isset($menu['child'])) {
|
||||
$this->addMenus($menu['child'], $module, $result['id']);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查依赖
|
||||
* @param string $type 类型:module/plugin
|
||||
* @param array $data 检查数据
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array
|
||||
*/
|
||||
private function checkDependence($type = '', $data = [])
|
||||
{
|
||||
$need = [];
|
||||
foreach ($data as $key => $value) {
|
||||
if (!isset($value[3])) {
|
||||
$value[3] = '=';
|
||||
}
|
||||
// 当前版本
|
||||
if ($type == 'module') {
|
||||
$curr_version = ModuleModel::where('identifier', $value[1])->value('version');
|
||||
} else {
|
||||
$curr_version = PluginModel::where('identifier', $value[1])->value('version');
|
||||
}
|
||||
|
||||
// 比对版本
|
||||
$result = version_compare($curr_version, $value[2], $value[3]);
|
||||
$need[$key] = [
|
||||
$type => $value[0],
|
||||
'identifier' => $value[1],
|
||||
'version' => $curr_version ? $curr_version : '未安装',
|
||||
'version_need' => $value[3].$value[2],
|
||||
'result' => $result ? '<i class="fa fa-check text-success"></i>' : '<i class="fa fa-times text-danger"></i>'
|
||||
];
|
||||
}
|
||||
|
||||
return $need;
|
||||
}
|
||||
}
|
||||
146
application/admin/controller/Packet.php
Normal file
146
application/admin/controller/Packet.php
Normal file
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\admin\model\Packet as PacketModel;
|
||||
|
||||
/**
|
||||
* 数据包控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Packet extends Admin
|
||||
{
|
||||
/**
|
||||
* 首页
|
||||
* @param string $group 分组
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed|string
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function index($group = 'local')
|
||||
{
|
||||
// 配置分组信息
|
||||
$list_group = ['local' => '本地数据包'];
|
||||
$tab_list = [];
|
||||
foreach ($list_group as $key => $value) {
|
||||
$tab_list[$key]['title'] = $value;
|
||||
$tab_list[$key]['url'] = url('index', ['group' => $key]);
|
||||
}
|
||||
|
||||
$PacketModel = new PacketModel;
|
||||
$data_list = $PacketModel->getAll();
|
||||
foreach ($data_list as &$value) {
|
||||
if (isset($value['author_url']) && !empty($value['author_url'])) {
|
||||
$value['author'] = '<a href="'. $value['author_url']. '" target="_blank">'. $value['author'] .'</a>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($data_list === false) {
|
||||
$this->error($PacketModel->getError());
|
||||
}
|
||||
|
||||
// 自定义按钮
|
||||
$btn_install = [
|
||||
'title' => '安装',
|
||||
'icon' => 'fa fa-fw fa-sign-in',
|
||||
'class' => 'btn btn-xs btn-default ajax-get confirm',
|
||||
'href' => url('install', ['name' => '__id__'])
|
||||
];
|
||||
$btn_uninstall = [
|
||||
'title' => '卸载',
|
||||
'icon' => 'fa fa-fw fa-sign-out',
|
||||
'class' => 'btn btn-xs btn-default ajax-get confirm',
|
||||
'href' => url('uninstall', ['name' => '__id__'])
|
||||
];
|
||||
$btn_install_all = [
|
||||
'title' => '安装',
|
||||
'icon' => 'fa fa-fw fa-sign-in',
|
||||
'class' => 'btn btn-primary ajax-post confirm',
|
||||
'href' => url('install')
|
||||
];
|
||||
$btn_uninstall_all = [
|
||||
'title' => '卸载',
|
||||
'icon' => 'fa fa-fw fa-sign-out',
|
||||
'class' => 'btn btn-danger ajax-post confirm',
|
||||
'href' => url('uninstall')
|
||||
];
|
||||
|
||||
switch ($group) {
|
||||
case 'local':
|
||||
// 使用ZBuilder快速创建数据表格
|
||||
return ZBuilder::make('table')
|
||||
->setPageTitle('数据包管理') // 设置页面标题
|
||||
->setPrimaryKey('name')
|
||||
->setTabNav($tab_list, $group) // 设置tab分页
|
||||
->addColumns([ // 批量添加数据列
|
||||
['name', '名称'],
|
||||
['title', '标题'],
|
||||
['author', '作者'],
|
||||
['version', '版本号'],
|
||||
['status', '是否安装', 'yesno'],
|
||||
['right_button', '操作', 'btn']
|
||||
])
|
||||
->addTopButton('custom', $btn_install_all)
|
||||
->addTopButton('custom', $btn_uninstall_all)
|
||||
->addRightButton('custom', $btn_install) // 添加右侧按钮
|
||||
->addRightButton('custom', $btn_uninstall) // 添加右侧按钮
|
||||
->setRowList($data_list) // 设置表格数据
|
||||
->fetch(); // 渲染模板
|
||||
break;
|
||||
case 'online':
|
||||
return '<h2>正在制作中...</h2>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装
|
||||
* @param string $name 数据包名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function install($name = '')
|
||||
{
|
||||
$names = $name != '' ? (array)$name : $this->request->param('ids/a');
|
||||
|
||||
foreach ($names as $name) {
|
||||
$result = PacketModel::install($name);
|
||||
if ($result === true) {
|
||||
if (!PacketModel::where('name', $name)->find()) {
|
||||
$data = PacketModel::getInfoFromFile($name);
|
||||
$data['status'] = 1;
|
||||
$data['tables'] = json_encode($data['tables']);
|
||||
PacketModel::create($data);
|
||||
}
|
||||
} else {
|
||||
$this->error('安装失败:'. $result);
|
||||
}
|
||||
}
|
||||
// 记录行为
|
||||
$packet_titles = PacketModel::where('name', 'in', $names)->column('title');
|
||||
action_log('packet_install', 'admin_packet', 0, UID, implode('、', $packet_titles));
|
||||
$this->success('安装成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载
|
||||
* @param string $name 数据包名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function uninstall($name = '')
|
||||
{
|
||||
$names = $name != '' ? (array)$name : $this->request->param('ids/a');
|
||||
|
||||
// 记录行为
|
||||
$packet_titles = PacketModel::where('name', 'in', $names)->column('title');
|
||||
action_log('packet_uninstall', 'admin_packet', 0, UID, implode('、', $packet_titles));
|
||||
|
||||
foreach ($names as $name) {
|
||||
PacketModel::uninstall($name);
|
||||
}
|
||||
|
||||
$this->success('卸载成功');
|
||||
}
|
||||
}
|
||||
612
application/admin/controller/Plugin.php
Normal file
612
application/admin/controller/Plugin.php
Normal file
@ -0,0 +1,612 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\admin\model\Plugin as PluginModel;
|
||||
use app\admin\model\HookPlugin as HookPluginModel;
|
||||
use think\facade\Cache;
|
||||
use util\Sql;
|
||||
use think\Db;
|
||||
use think\facade\Hook;
|
||||
|
||||
/**
|
||||
* 插件管理控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class Plugin extends Admin
|
||||
{
|
||||
/**
|
||||
* 首页
|
||||
* @param string $group 分组
|
||||
* @param string $type 显示类型
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function index($group = 'local', $type = '')
|
||||
{
|
||||
// 配置分组信息
|
||||
$list_group = ['local' => '本地插件'];
|
||||
$tab_list = [];
|
||||
foreach ($list_group as $key => $value) {
|
||||
$tab_list[$key]['title'] = $value;
|
||||
$tab_list[$key]['url'] = url('index', ['group' => $key]);
|
||||
}
|
||||
|
||||
// 监听tab钩子
|
||||
Hook::listen('plugin_index_tab_list', $tab_list);
|
||||
|
||||
switch ($group) {
|
||||
case 'local':
|
||||
// 查询条件
|
||||
$keyword = $this->request->get('keyword', '');
|
||||
|
||||
if (input('?param.status') && input('param.status') != '_all') {
|
||||
$status = input('param.status');
|
||||
} else {
|
||||
$status = '';
|
||||
}
|
||||
|
||||
$PluginModel = new PluginModel;
|
||||
$result = $PluginModel->getAll($keyword, $status);
|
||||
|
||||
if ($result['plugins'] === false) {
|
||||
$this->error($PluginModel->getError());
|
||||
}
|
||||
|
||||
$type_show = Cache::get('plugin_type_show');
|
||||
$type_show = $type != '' ? $type : ($type_show == false ? 'block' : $type_show);
|
||||
Cache::set('plugin_type_show', $type_show);
|
||||
$type = $type_show == 'block' ? 'list' : 'block';
|
||||
|
||||
$this->assign('page_title', '插件管理');
|
||||
$this->assign('plugins', $result['plugins']);
|
||||
$this->assign('total', $result['total']);
|
||||
$this->assign('tab_nav', ['tab_list' => $tab_list, 'curr_tab' => $group]);
|
||||
$this->assign('type', $type);
|
||||
return $this->fetch();
|
||||
break;
|
||||
case 'online':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装插件
|
||||
* @param string $name 插件标识
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
public function install($name = '')
|
||||
{
|
||||
// 设置最大执行时间和内存大小
|
||||
ini_set('max_execution_time', '0');
|
||||
ini_set('memory_limit', '1024M');
|
||||
|
||||
$plug_name = trim($name);
|
||||
if ($plug_name == '') $this->error('插件不存在!');
|
||||
|
||||
$plugin_class = get_plugin_class($plug_name);
|
||||
|
||||
if (!class_exists($plugin_class)) {
|
||||
$this->error('插件不存在!');
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
$plugin = new $plugin_class;
|
||||
// 插件预安装
|
||||
if(!$plugin->install()) {
|
||||
$this->error('插件预安装失败!原因:'. $plugin->getError());
|
||||
}
|
||||
|
||||
// 添加钩子
|
||||
if (isset($plugin->hooks) && !empty($plugin->hooks)) {
|
||||
if (!HookPluginModel::addHooks($plugin->hooks, $name)) {
|
||||
$this->error('安装插件钩子时出现错误,请重新安装');
|
||||
}
|
||||
cache('hook_plugins', null);
|
||||
}
|
||||
|
||||
// 执行安装插件sql文件
|
||||
$sql_file = realpath(config('plugin_path').$name.'/install.sql');
|
||||
if (file_exists($sql_file)) {
|
||||
if (isset($plugin->database_prefix) && $plugin->database_prefix != '') {
|
||||
$sql_statement = Sql::getSqlFromFile($sql_file, false, [$plugin->database_prefix => config('database.prefix')]);
|
||||
} else {
|
||||
$sql_statement = Sql::getSqlFromFile($sql_file);
|
||||
}
|
||||
|
||||
if (!empty($sql_statement)) {
|
||||
foreach ($sql_statement as $value) {
|
||||
Db::execute($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 插件配置信息
|
||||
$plugin_info = $plugin->info;
|
||||
|
||||
// 验证插件信息
|
||||
$result = $this->validate($plugin_info, 'Plugin');
|
||||
// 验证失败 输出错误信息
|
||||
if(true !== $result) $this->error($result);
|
||||
|
||||
// 并入插件配置值
|
||||
$plugin_info['config'] = $plugin->getConfigValue();
|
||||
|
||||
// 将插件信息写入数据库
|
||||
if (PluginModel::create($plugin_info)) {
|
||||
cache('plugin_all', null);
|
||||
$this->success('插件安装成功');
|
||||
} else {
|
||||
$this->error('插件安装失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载插件
|
||||
* @param string $name 插件标识
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function uninstall($name = '')
|
||||
{
|
||||
$plug_name = trim($name);
|
||||
if ($plug_name == '') $this->error('插件不存在!');
|
||||
|
||||
$class = get_plugin_class($plug_name);
|
||||
if (!class_exists($class)) {
|
||||
$this->error('插件不存在!');
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
$plugin = new $class;
|
||||
// 插件预卸
|
||||
if(!$plugin->uninstall()) {
|
||||
$this->error('插件预卸载失败!原因:'. $plugin->getError());
|
||||
}
|
||||
|
||||
// 卸载插件自带钩子
|
||||
if (isset($plugin->hooks) && !empty($plugin->hooks)) {
|
||||
if (false === HookPluginModel::deleteHooks($plug_name)) {
|
||||
$this->error('卸载插件钩子时出现错误,请重新卸载');
|
||||
}
|
||||
cache('hook_plugins', null);
|
||||
}
|
||||
|
||||
// 执行卸载插件sql文件
|
||||
$sql_file = realpath(config('plugin_path').$plug_name.'/uninstall.sql');
|
||||
if (file_exists($sql_file)) {
|
||||
if (isset($plugin->database_prefix) && $plugin->database_prefix != '') {
|
||||
$sql_statement = Sql::getSqlFromFile($sql_file, true, [$plugin->database_prefix => config('database.prefix')]);
|
||||
} else {
|
||||
$sql_statement = Sql::getSqlFromFile($sql_file, true);
|
||||
}
|
||||
|
||||
if (!empty($sql_statement)) {
|
||||
Db::execute($sql_statement);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除插件信息
|
||||
if (PluginModel::where('name', $plug_name)->delete()) {
|
||||
cache('plugin_all', null);
|
||||
$this->success('插件卸载成功');
|
||||
} else {
|
||||
$this->error('插件卸载失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件管理
|
||||
* @param string $name 插件名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function manage($name = '')
|
||||
{
|
||||
cookie('__forward__', $_SERVER['REQUEST_URI']);
|
||||
|
||||
// 加载自定义后台页面
|
||||
if (plugin_action_exists($name, 'Admin', 'index')) {
|
||||
return plugin_action($name, 'Admin', 'index');
|
||||
}
|
||||
|
||||
// 加载系统的后台页面
|
||||
$class = get_plugin_class($name);
|
||||
if (!class_exists($class)) {
|
||||
$this->error($name.'插件不存在!');
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
$plugin = new $class;
|
||||
|
||||
// 获取后台字段信息,并分析
|
||||
if (isset($plugin->admin)) {
|
||||
$admin = $this->parseAdmin($plugin->admin);
|
||||
} else {
|
||||
$admin = $this->parseAdmin();
|
||||
}
|
||||
|
||||
if (!plugin_model_exists($name)) {
|
||||
$this->error('插件: '.$name.' 缺少模型文件!');
|
||||
}
|
||||
|
||||
// 获取插件模型实例
|
||||
$PluginModel = get_plugin_model($name);
|
||||
$order = $this->getOrder();
|
||||
$map = $this->getMap();
|
||||
$data_list = $PluginModel->where($map)->order($order)->paginate();
|
||||
$page = $data_list->render();
|
||||
|
||||
// 使用ZBuilder快速创建数据表格
|
||||
$builder = ZBuilder::make('table')
|
||||
->setPageTitle($admin['title']) // 设置页面标题
|
||||
->setPluginName($name)
|
||||
->setTableName($admin['table_name'])
|
||||
->setSearch($admin['search_field'], $admin['search_title']) // 设置搜索框
|
||||
->addOrder($admin['order'])
|
||||
->addTopButton('back', [
|
||||
'title' => '返回插件列表',
|
||||
'icon' => 'fa fa-reply',
|
||||
'href' => url('index')
|
||||
])
|
||||
->addTopButtons($admin['top_buttons']) // 批量添加顶部按钮
|
||||
->addRightButtons($admin['right_buttons']); // 批量添加右侧按钮
|
||||
|
||||
// 自定义顶部按钮
|
||||
if (!empty($admin['custom_top_buttons'])) {
|
||||
foreach ($admin['custom_top_buttons'] as $custom) {
|
||||
$builder->addTopButton('custom', $custom);
|
||||
}
|
||||
}
|
||||
// 自定义右侧按钮
|
||||
if (!empty($admin['custom_right_buttons'])) {
|
||||
foreach ($admin['custom_right_buttons'] as $custom) {
|
||||
$builder->addRightButton('custom', $custom);
|
||||
}
|
||||
}
|
||||
|
||||
// 表头筛选
|
||||
if (is_array($admin['filter'])) {
|
||||
foreach ($admin['filter'] as $column => $params) {
|
||||
$options = isset($params[0]) ? $params[0] : [];
|
||||
$default = isset($params[1]) ? $params[1] : [];
|
||||
$type = isset($params[2]) ? $params[2] : 'checkbox';
|
||||
$builder->addFilter($column, $options, $default, $type);
|
||||
}
|
||||
} else {
|
||||
$builder->addFilter($admin['filter']);
|
||||
}
|
||||
|
||||
return $builder
|
||||
->addColumns($admin['columns']) // 批量添加数据列
|
||||
->setRowList($data_list) // 设置表格数据
|
||||
->setPages($page) // 设置分页数据
|
||||
->fetch(); // 渲染模板
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件新增方法
|
||||
* @param string $plugin_name 插件名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function add($plugin_name = '')
|
||||
{
|
||||
// 如果存在自定义的新增方法,则优先执行
|
||||
if (plugin_action_exists($plugin_name, 'Admin', 'add')) {
|
||||
$params = $this->request->param();
|
||||
return plugin_action($plugin_name, 'Admin', 'add', $params);
|
||||
}
|
||||
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post();
|
||||
|
||||
// 执行插件的验证器(如果存在的话)
|
||||
if (plugin_validate_exists($plugin_name)) {
|
||||
$plugin_validate = get_plugin_validate($plugin_name);
|
||||
if (!$plugin_validate->check($data)) {
|
||||
// 验证失败 输出错误信息
|
||||
$this->error($plugin_validate->getError());
|
||||
}
|
||||
}
|
||||
|
||||
// 实例化模型并添加数据
|
||||
$PluginModel = get_plugin_model($plugin_name);
|
||||
if ($PluginModel->data($data)->save()) {
|
||||
$this->success('新增成功', cookie('__forward__'));
|
||||
} else {
|
||||
$this->error('新增失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 获取插件模型
|
||||
$class = get_plugin_class($plugin_name);
|
||||
if (!class_exists($class)) {
|
||||
$this->error('插件不存在!');
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
$plugin = new $class;
|
||||
if (!isset($plugin->fields)) {
|
||||
$this->error('插件新增、编辑字段不存在!');
|
||||
}
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('新增')
|
||||
->addFormItems($plugin->fields)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑插件方法
|
||||
* @param string $id 数据id
|
||||
* @param string $plugin_name 插件名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function edit($id = '', $plugin_name = '')
|
||||
{
|
||||
// 如果存在自定义的编辑方法,则优先执行
|
||||
if (plugin_action_exists($plugin_name, 'Admin', 'edit')) {
|
||||
$params = $this->request->param();
|
||||
return plugin_action($plugin_name, 'Admin', 'edit', $params);
|
||||
}
|
||||
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post();
|
||||
|
||||
// 执行插件的验证器(如果存在的话)
|
||||
if (plugin_validate_exists($plugin_name)) {
|
||||
$plugin_validate = get_plugin_validate($plugin_name);
|
||||
if (!$plugin_validate->check($data)) {
|
||||
// 验证失败 输出错误信息
|
||||
$this->error($plugin_validate->getError());
|
||||
}
|
||||
}
|
||||
|
||||
// 实例化模型并添加数据
|
||||
$PluginModel = get_plugin_model($plugin_name);
|
||||
if (false !== $PluginModel->isUpdate(true)->save($data)) {
|
||||
$this->success('编辑成功', cookie('__forward__'));
|
||||
} else {
|
||||
$this->error('编辑失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 获取插件类名
|
||||
$class = get_plugin_class($plugin_name);
|
||||
if (!class_exists($class)) {
|
||||
$this->error('插件不存在!');
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
$plugin = new $class;
|
||||
if (!isset($plugin->fields)) {
|
||||
$this->error('插件新增、编辑字段不存在!');
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
$PluginModel = get_plugin_model($plugin_name);
|
||||
$info = $PluginModel->find($id);
|
||||
if (!$info) {
|
||||
$this->error('找不到数据!');
|
||||
}
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('编辑')
|
||||
->addHidden('id')
|
||||
->addFormItems($plugin->fields)
|
||||
->setFormData($info)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件参数设置
|
||||
* @param string $name 插件名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function config($name = '')
|
||||
{
|
||||
// 更新配置
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post();
|
||||
$data = json_encode($data);
|
||||
|
||||
if (false !== PluginModel::where('name', $name)->update(['config' => $data])) {
|
||||
$this->success('更新成功', 'index');
|
||||
} else {
|
||||
$this->error('更新失败');
|
||||
}
|
||||
}
|
||||
|
||||
$plugin_class = get_plugin_class($name);
|
||||
// 实例化插件
|
||||
$plugin = new $plugin_class;
|
||||
$trigger = isset($plugin->trigger) ? $plugin->trigger : [];
|
||||
|
||||
// 插件配置值
|
||||
$info = PluginModel::where('name', $name)->field('id,name,config')->find();
|
||||
$db_config = json_decode($info['config'], true);
|
||||
|
||||
// 插件配置项
|
||||
$config = include config('plugin_path'). $name. '/config.php';
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('插件设置')
|
||||
->addFormItems($config)
|
||||
->setFormData($db_config)
|
||||
->setTrigger($trigger)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态
|
||||
* @param string $type 状态类型:enable/disable
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function setStatus($type = '', $record = [])
|
||||
{
|
||||
$_t = input('param._t', '');
|
||||
$ids = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
|
||||
empty($ids) && $this->error('缺少主键');
|
||||
|
||||
$status = $type == 'enable' ? 1 : 0;
|
||||
|
||||
if ($_t != '') {
|
||||
parent::setStatus($type, $record);
|
||||
} else {
|
||||
$plugins = PluginModel::where('id', 'in', $ids)->value('name');
|
||||
if ($plugins) {
|
||||
HookPluginModel::$type($plugins);
|
||||
}
|
||||
|
||||
if (false !== PluginModel::where('id', 'in', $ids)->setField('status', $status)) {
|
||||
$this->success('操作成功');
|
||||
} else {
|
||||
$this->error('操作失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用插件/禁用插件数据
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function disable($record = [])
|
||||
{
|
||||
$this->setStatus('disable');
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用插件/启用插件数据
|
||||
* @param array $record 行为日志内容
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function enable($record = [])
|
||||
{
|
||||
$this->setStatus('enable');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除插件数据
|
||||
* @param array $record
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function delete($record = [])
|
||||
{
|
||||
$this->setStatus('delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行插件内部方法
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$plugin = input('param._plugin');
|
||||
$controller = input('param._controller');
|
||||
$action = input('param._action');
|
||||
$params = $this->request->except(['_plugin', '_controller', '_action'], 'param');
|
||||
|
||||
if (empty($plugin) || empty($controller) || empty($action)) {
|
||||
$this->error('没有指定插件名称、控制器名称或操作名称');
|
||||
}
|
||||
|
||||
if (!plugin_action_exists($plugin, $controller, $action)) {
|
||||
$this->error("找不到方法:{$plugin}/{$controller}/{$action}");
|
||||
}
|
||||
return plugin_action($plugin, $controller, $action, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析后台字段信息
|
||||
* @param array $data 字段信息
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array
|
||||
*/
|
||||
private function parseAdmin($data = [])
|
||||
{
|
||||
$admin = [
|
||||
'title' => '数据列表',
|
||||
'search_title' => '',
|
||||
'search_field' => [],
|
||||
'order' => '',
|
||||
'filter' => '',
|
||||
'table_name' => '',
|
||||
'columns' => [],
|
||||
'right_buttons' => [],
|
||||
'top_buttons' => [],
|
||||
'customs' => [],
|
||||
];
|
||||
|
||||
if (empty($data)) {
|
||||
return $admin;
|
||||
}
|
||||
|
||||
// 处理工具栏按钮链接
|
||||
if (isset($data['top_buttons']) && !empty($data['top_buttons'])) {
|
||||
$this->parseButton('top_buttons', $data);
|
||||
}
|
||||
|
||||
// 处理右侧按钮链接
|
||||
if (isset($data['right_buttons']) && !empty($data['right_buttons'])) {
|
||||
$this->parseButton('right_buttons', $data);
|
||||
}
|
||||
|
||||
return array_merge($admin, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析按钮链接
|
||||
* @param string $button 按钮名称
|
||||
* @param array $data 字段信息
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
private function parseButton($button, &$data)
|
||||
{
|
||||
foreach ($data[$button] as $key => &$value) {
|
||||
// 处理自定义按钮
|
||||
if ($key === 'customs') {
|
||||
if (!empty($value)) {
|
||||
foreach ($value as &$custom) {
|
||||
if (isset($custom['href']['url']) && $custom['href']['url'] != '') {
|
||||
$params = isset($custom['href']['params']) ? $custom['href']['params'] : [];
|
||||
$custom['href'] = plugin_url($custom['href']['url'], $params);
|
||||
$data['custom_'.$button][] = $custom;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($data[$button][$key]);
|
||||
}
|
||||
if (!is_numeric($key) && isset($value['href']['url']) && $value['href']['url'] != '') {
|
||||
$value['href'] = plugin_url($value['href']['url']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
178
application/admin/controller/System.php
Normal file
178
application/admin/controller/System.php
Normal file
@ -0,0 +1,178 @@
|
||||
<?php
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\builder\ZBuilder;
|
||||
use app\admin\model\Config as ConfigModel;
|
||||
use app\admin\model\Module as ModuleModel;
|
||||
|
||||
/**
|
||||
* 系统模块控制器
|
||||
* @package app\admin\controller
|
||||
*/
|
||||
class System extends Admin
|
||||
{
|
||||
/**
|
||||
* 系统设置
|
||||
* @param string $group 分组
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public function index($group = 'base')
|
||||
{
|
||||
// 保存数据
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post();
|
||||
|
||||
if (isset(config('config_group')[$group])) {
|
||||
// 查询该分组下所有的配置项名和类型
|
||||
$items = ConfigModel::where('group', $group)->where('status', 1)->column('name,type');
|
||||
|
||||
foreach ($items as $name => $type) {
|
||||
if (!isset($data[$name])) {
|
||||
switch ($type) {
|
||||
// 开关
|
||||
case 'switch':
|
||||
$data[$name] = 0;
|
||||
break;
|
||||
case 'checkbox':
|
||||
$data[$name] = '';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// 如果值是数组则转换成字符串,适用于复选框等类型
|
||||
if (is_array($data[$name])) {
|
||||
$data[$name] = implode(',', $data[$name]);
|
||||
}
|
||||
switch ($type) {
|
||||
// 开关
|
||||
case 'switch':
|
||||
$data[$name] = 1;
|
||||
break;
|
||||
// 日期时间
|
||||
case 'date':
|
||||
case 'time':
|
||||
case 'datetime':
|
||||
$data[$name] = strtotime($data[$name]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ConfigModel::where('name', $name)->update(['value' => $data[$name]]);
|
||||
}
|
||||
} else {
|
||||
// 保存模块配置
|
||||
if (false === ModuleModel::where('name', $group)->update(['config' => json_encode($data)])) {
|
||||
$this->error('更新失败');
|
||||
}
|
||||
// 非开发模式,缓存数据
|
||||
if (config('develop_mode') == 0) {
|
||||
cache('module_config_'.$group, $data);
|
||||
}
|
||||
}
|
||||
cache('system_config', null);
|
||||
// 记录行为
|
||||
action_log('system_config_update', 'admin_config', 0, UID, "分组($group)");
|
||||
$this->success('更新成功', url('index', ['group' => $group]));
|
||||
} else {
|
||||
// 配置分组信息
|
||||
$list_group = config('config_group');
|
||||
|
||||
// 读取模型配置
|
||||
$modules = ModuleModel::where('config', 'neq', '')
|
||||
->where('status', 1)
|
||||
->column('config,title,name', 'name');
|
||||
foreach ($modules as $name => $module) {
|
||||
$list_group[$name] = $module['title'];
|
||||
}
|
||||
|
||||
$tab_list = [];
|
||||
foreach ($list_group as $key => $value) {
|
||||
$tab_list[$key]['title'] = $value;
|
||||
$tab_list[$key]['url'] = url('index', ['group' => $key]);
|
||||
}
|
||||
|
||||
if (isset(config('config_group')[$group])) {
|
||||
// 查询条件
|
||||
$map['group'] = $group;
|
||||
$map['status'] = 1;
|
||||
|
||||
// 数据列表
|
||||
$data_list = ConfigModel::where($map)
|
||||
->order('sort asc,id asc')
|
||||
->field('group', true)
|
||||
->select();
|
||||
$data_list = $data_list->toArray();
|
||||
|
||||
foreach ($data_list as &$value) {
|
||||
// 解析options
|
||||
if ($value['options'] != '') {
|
||||
$value['options'] = parse_attr($value['options']);
|
||||
}
|
||||
// 默认模块列表
|
||||
if ($value['name'] == 'home_default_module') {
|
||||
$value['options'] = array_merge(['index' => '默认'], ModuleModel::getModule());
|
||||
}
|
||||
switch ($value['type']) {
|
||||
// 日期时间
|
||||
case 'date':
|
||||
$value['value'] = $value['value'] != '' ? date('Y-m-d', $value['value']) : '';
|
||||
break;
|
||||
case 'time':
|
||||
$value['value'] = $value['value'] != '' ? date('H:i:s', $value['value']) : '';
|
||||
break;
|
||||
case 'datetime':
|
||||
$value['value'] = $value['value'] != '' ? date('Y-m-d H:i:s', $value['value']) : '';
|
||||
break;
|
||||
case 'linkages':
|
||||
$value['token'] = $this->createLinkagesToken($value['table'], $value['option'], $value['key']);
|
||||
break;
|
||||
case 'colorpicker':
|
||||
$value['mode'] = 'rgba';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('系统设置')
|
||||
->setTabNav($tab_list, $group)
|
||||
->setFormItems($data_list)
|
||||
->fetch();
|
||||
} else {
|
||||
// 模块配置
|
||||
$module_info = ModuleModel::getInfoFromFile($group);
|
||||
$config = $module_info['config'];
|
||||
$trigger = isset($module_info['trigger']) ? $module_info['trigger'] : [];
|
||||
|
||||
// 数据库内的模块信息
|
||||
$db_config = ModuleModel::where('name', $group)->value('config');
|
||||
$db_config = json_decode($db_config, true);
|
||||
|
||||
// 使用ZBuilder快速创建表单
|
||||
return ZBuilder::make('form')
|
||||
->setPageTitle('模块设置')
|
||||
->setTabNav($tab_list, $group)
|
||||
->addFormItems($config)
|
||||
->setFormdata($db_config) // 设置表格数据
|
||||
->setTrigger($trigger) // 设置触发
|
||||
->fetch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建快速多级联动Token
|
||||
* @param string $table 表名
|
||||
* @param string $option
|
||||
* @param string $key
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool|string
|
||||
*/
|
||||
private function createLinkagesToken($table = '', $option = '', $key = '')
|
||||
{
|
||||
$table_token = substr(sha1($table.'-'.$option.'-'.$key.'-'.session('user_auth.last_login_ip').'-'.UID.'-'.session('user_auth.last_login_time')), 0, 8);
|
||||
session($table_token, ['table' => $table, 'option' => $option, 'key' => $key]);
|
||||
return $table_token;
|
||||
}
|
||||
}
|
||||
75
application/admin/model/Access.php
Normal file
75
application/admin/model/Access.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
use think\facade\Request;
|
||||
|
||||
/**
|
||||
* 统一授权模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class Access extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $name = 'admin_access';
|
||||
|
||||
/**
|
||||
* 获取用户授权节点
|
||||
* @param int $uid 用户id
|
||||
* @param string $group 权限分组,可以以点分开模型名称和分组名称,如user.group
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getAuthNode($uid = 0, $group = '')
|
||||
{
|
||||
if ($uid == 0 || $group == '') {
|
||||
$this->error = '缺少参数';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strpos($group, '.')) {
|
||||
list($module, $group) = explode('.', $group);
|
||||
} else {
|
||||
$module = Request::module();
|
||||
}
|
||||
|
||||
$map = [
|
||||
'module' => $module,
|
||||
'group' => $group,
|
||||
'uid' => $uid
|
||||
];
|
||||
|
||||
return $this->where($map)->column('nid');
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户的某个节点是否授权
|
||||
* @param int $uid 用户id
|
||||
* @param string $group $group 权限分组,可以以点分开模型名称和分组名称,如user.group
|
||||
* @param int $node 需要检查的节点id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
*/
|
||||
public function checkAuthNode($uid = 0, $group = '', $node = 0)
|
||||
{
|
||||
if ($uid == 0 || $group == '' || $node == 0) {
|
||||
$this->error = '缺少参数';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取该用户的所有授权节点
|
||||
$nodes = $this->getAuthNode($uid, $group);
|
||||
if (!$nodes) {
|
||||
$this->error = '该用户没有授权任何节点';
|
||||
return false;
|
||||
}
|
||||
|
||||
$nodes = array_flip($nodes);
|
||||
if (isset($nodes[$node])) {
|
||||
return true;
|
||||
} else {
|
||||
$this->error = '未授权';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
application/admin/model/Action.php
Normal file
17
application/admin/model/Action.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 日志模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class Action extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $name = 'admin_action';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
}
|
||||
87
application/admin/model/Attachment.php
Normal file
87
application/admin/model/Attachment.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 附件模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class Attachment extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $name = 'admin_attachment';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
/**
|
||||
* 根据附件id获取路径
|
||||
* @param string|array $id 附件id
|
||||
* @param int $type 类型:0-补全目录,1-直接返回数据库记录的地址
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|bool|mixed|string
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function getFilePath($id = '', $type = 0)
|
||||
{
|
||||
if (is_array($id)) {
|
||||
$data_list = $this->where('id', 'in', $id)->select();
|
||||
$paths = [];
|
||||
foreach ($data_list as $key => $value) {
|
||||
if ($value['driver'] == 'local') {
|
||||
$paths[$value['id']] = ($type == 0 ? PUBLIC_PATH : '').$value['path'];
|
||||
} else {
|
||||
$paths[$value['id']] = $value['path'];
|
||||
}
|
||||
}
|
||||
return $paths;
|
||||
} else {
|
||||
$data = $this->where('id', $id)->find();
|
||||
if ($data) {
|
||||
if ($data['driver'] == 'local') {
|
||||
return ($type == 0 ? PUBLIC_PATH : '').$data['path'];
|
||||
} else {
|
||||
return $data['path'];
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据图片id获取缩略图路径,如果缩略图不存在,则返回原图路径
|
||||
* @param string $id 图片id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|mixed|string|Model|null
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public function getThumbPath($id = '')
|
||||
{
|
||||
$result = $this->where('id', $id)->field('path,driver,thumb')->find();
|
||||
if ($result) {
|
||||
if ($result['driver'] == 'local') {
|
||||
return $result['thumb'] != '' ? PUBLIC_PATH.$result['thumb'] : PUBLIC_PATH.$result['path'];
|
||||
} else {
|
||||
return $result['thumb'] != '' ? $result['thumb'] : $result['path'];
|
||||
}
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据附件id获取名称
|
||||
* @param string $id 附件id
|
||||
* @return string 名称
|
||||
*/
|
||||
public function getFileName($id = '')
|
||||
{
|
||||
return $this->where('id', $id)->value('name');
|
||||
}
|
||||
}
|
||||
48
application/admin/model/Config.php
Normal file
48
application/admin/model/Config.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 后台配置模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class Config extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $name = 'admin_config';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
/**
|
||||
* 获取配置信息
|
||||
* @param string $name 配置名
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig($name = '')
|
||||
{
|
||||
$configs = self::column('value,type', 'name');
|
||||
|
||||
$result = [];
|
||||
foreach ($configs as $config) {
|
||||
switch ($config['type']) {
|
||||
case 'array':
|
||||
$result[$config['name']] = parse_attr($config['value']);
|
||||
break;
|
||||
case 'checkbox':
|
||||
if ($config['value'] != '') {
|
||||
$result[$config['name']] = explode(',', $config['value']);
|
||||
} else {
|
||||
$result[$config['name']] = [];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$result[$config['name']] = $config['value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $name != '' ? $result[$name] : $result;
|
||||
}
|
||||
}
|
||||
72
application/admin/model/Hook.php
Normal file
72
application/admin/model/Hook.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 钩子模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class Hook extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $name = 'admin_hook';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
/**
|
||||
* 添加钩子
|
||||
* @param array $hooks 钩子
|
||||
* @param string $plugin_name
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public static function addHooks($hooks = [], $plugin_name = '')
|
||||
{
|
||||
if (!empty($hooks) && is_array($hooks)) {
|
||||
$data = [];
|
||||
foreach ($hooks as $name => $description) {
|
||||
if (is_numeric($name)) {
|
||||
$name = $description;
|
||||
$description = '';
|
||||
}
|
||||
if (self::where('name', $name)->find()) {
|
||||
continue;
|
||||
}
|
||||
$data[] = [
|
||||
'name' => $name,
|
||||
'plugin' => $plugin_name,
|
||||
'description' => $description,
|
||||
'create_time' => request()->time(),
|
||||
'update_time' => request()->time(),
|
||||
];
|
||||
}
|
||||
if (!empty($data) && false === self::insertAll($data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除钩子
|
||||
* @param string $plugin_name 钩子名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public static function deleteHooks($plugin_name = '')
|
||||
{
|
||||
if (!empty($plugin_name)) {
|
||||
if (false === self::where('plugin', $plugin_name)->delete()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
122
application/admin/model/HookPlugin.php
Normal file
122
application/admin/model/HookPlugin.php
Normal file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
use app\admin\model\Hook as HookModel;
|
||||
|
||||
/**
|
||||
* 钩子-插件模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class HookPlugin extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $name = 'admin_hook_plugin';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
/**
|
||||
* 启用插件钩子
|
||||
* @param string $plugin 插件名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
*/
|
||||
public static function enable($plugin = '')
|
||||
{
|
||||
return self::where('plugin', $plugin)->setField('status', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用插件钩子
|
||||
* @param string $plugin 插件名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return int
|
||||
*/
|
||||
public static function disable($plugin = '')
|
||||
{
|
||||
return self::where('plugin', $plugin)->setField('status', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加钩子-插件对照
|
||||
* @param array $hooks 钩子
|
||||
* @param string $plugin_name 插件名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool|int|string
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public static function addHooks($hooks = [], $plugin_name = '')
|
||||
{
|
||||
if (!empty($hooks) && is_array($hooks)) {
|
||||
// 添加钩子
|
||||
if (!HookModel::addHooks($hooks, $plugin_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = [];
|
||||
foreach ($hooks as $name => $description) {
|
||||
if (is_numeric($name)) {
|
||||
$name = $description;
|
||||
}
|
||||
$data[] = [
|
||||
'hook' => $name,
|
||||
'plugin' => $plugin_name,
|
||||
'create_time' => request()->time(),
|
||||
'update_time' => request()->time(),
|
||||
];
|
||||
}
|
||||
|
||||
return self::insertAll($data);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除钩子
|
||||
* @param string $plugin_name 钩子名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public static function deleteHooks($plugin_name = '')
|
||||
{
|
||||
if (!empty($plugin_name)) {
|
||||
// 删除钩子
|
||||
if (!HookModel::deleteHooks($plugin_name)) {
|
||||
return false;
|
||||
}
|
||||
if (false === self::where('plugin', $plugin_name)->delete()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 钩子插件排序
|
||||
* @param string $hook 钩子
|
||||
* @param string $plugins 插件名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
*/
|
||||
public static function sort($hook = '', $plugins = '')
|
||||
{
|
||||
if ($hook != '' && $plugins != '') {
|
||||
$plugins = is_array($plugins) ? $plugins : explode(',', $plugins);
|
||||
|
||||
foreach ($plugins as $key => $plugin) {
|
||||
$map = [
|
||||
'hook' => $hook,
|
||||
'plugin' => $plugin
|
||||
];
|
||||
self::where($map)->setField('sort', $key + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
55
application/admin/model/Icon.php
Normal file
55
application/admin/model/Icon.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 图标模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class Icon extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $name = 'admin_icon';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
/**
|
||||
* 图标列表
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return \think\model\relation\HasMany
|
||||
*/
|
||||
public function icons()
|
||||
{
|
||||
return $this->hasMany('IconList', 'icon_id')->field('title,class,code');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图标css链接
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|string|\think\Collection
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public static function getUrls()
|
||||
{
|
||||
$list = self::where('status', 1)->select();
|
||||
if ($list) {
|
||||
foreach ($list as $key => $item) {
|
||||
if ($item['icons']) {
|
||||
$html = '<ul class="js-icon-list items-push-2x text-center">';
|
||||
foreach ($item['icons'] as $icon) {
|
||||
$html .= '<li title="'.$icon['title'].'"><i class="'.$icon['class'].'"></i> <code>'.$icon['code'].'</code></li>';
|
||||
}
|
||||
$html .= '</ul>';
|
||||
} else {
|
||||
$html = '<p class="text-center text-muted">暂无图标</p>';
|
||||
}
|
||||
$list[$key]['html'] = $html;
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
24
application/admin/model/IconList.php
Normal file
24
application/admin/model/IconList.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 海豚PHP框架 [ DolphinPHP ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: http://dolphinphp.com
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 图标列表模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class IconList extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $name = 'admin_icon_list';
|
||||
}
|
||||
37
application/admin/model/Log.php
Normal file
37
application/admin/model/Log.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 日志记录模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class Log extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $name = 'admin_log';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
/**
|
||||
* 获取所有日志
|
||||
* @param array $map 条件
|
||||
* @param string $order 排序
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return \think\Paginator
|
||||
* @throws \think\exception\DbException
|
||||
*/
|
||||
public static function getAll($map = [], $order = '')
|
||||
{
|
||||
$data_list = self::view('admin_log', true)
|
||||
->view('admin_action', 'title,module', 'admin_action.id=admin_log.action_id', 'left')
|
||||
->view('admin_user', 'username', 'admin_user.id=admin_log.user_id', 'left')
|
||||
->view('admin_module', ['title' => 'module_title'], 'admin_module.name=admin_action.module')
|
||||
->where($map)
|
||||
->order($order)
|
||||
->paginate();
|
||||
return $data_list;
|
||||
}
|
||||
}
|
||||
322
application/admin/model/Menu.php
Normal file
322
application/admin/model/Menu.php
Normal file
@ -0,0 +1,322 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 海豚PHP框架 [ DolphinPHP ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: http://dolphinphp.com
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\admin\model;
|
||||
|
||||
use app\user\model\Role as RoleModel;
|
||||
use think\Model;
|
||||
use think\Exception;
|
||||
use util\Tree;
|
||||
|
||||
/**
|
||||
* 节点模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class Menu extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $table = '__ADMIN_MENU__';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 将节点url转为小写
|
||||
public function setUrlValueAttr($value)
|
||||
{
|
||||
return strtolower(trim($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归修改所属模型
|
||||
* @param int $id 父级节点id
|
||||
* @param string $module 模型名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
*/
|
||||
public static function changeModule($id = 0, $module = '')
|
||||
{
|
||||
if ($id > 0) {
|
||||
$ids = self::where('pid', $id)->column('id');
|
||||
if ($ids) {
|
||||
foreach ($ids as $id) {
|
||||
self::where('id', $id)->setField('module', $module);
|
||||
self::changeModule($id, $module);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取树形节点
|
||||
* @param int $id 需要隐藏的节点id
|
||||
* @param string $default 默认第一个节点项,默认为“顶级节点”,如果为false则不显示,也可传入其他名称
|
||||
* @param string $module 模型名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getMenuTree($id = 0, $default = '', $module = '')
|
||||
{
|
||||
$result[0] = '顶级节点';
|
||||
$where = [
|
||||
['status', 'egt', 0]
|
||||
];
|
||||
if ($module != '') {
|
||||
$where[] = ['module', '=', $module];
|
||||
}
|
||||
|
||||
// 排除指定节点及其子节点
|
||||
if ($id !== 0) {
|
||||
$hide_ids = array_merge([$id], self::getChildsId($id));
|
||||
$where[] = ['id', 'not in', $hide_ids];
|
||||
}
|
||||
|
||||
// 获取节点
|
||||
$menus = Tree::toList(self::where($where)->order('pid,id')->column('id,pid,title'));
|
||||
foreach ($menus as $menu) {
|
||||
$result[$menu['id']] = $menu['title_display'];
|
||||
}
|
||||
|
||||
// 设置默认节点项标题
|
||||
if ($default != '') {
|
||||
$result[0] = $default;
|
||||
}
|
||||
|
||||
// 隐藏默认节点项
|
||||
if ($default === false) {
|
||||
unset($result[0]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取顶部节点
|
||||
* @param string $max 最多返回多少个
|
||||
* @param string $cache_tag 缓存标签
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array
|
||||
*/
|
||||
public static function getTopMenu($max = '', $cache_tag = '')
|
||||
{
|
||||
$cache_tag .= '_role_'.session('user_auth.role');
|
||||
$menus = cache($cache_tag);
|
||||
if (!$menus) {
|
||||
// 非开发模式,只显示可以显示的菜单
|
||||
if (config('develop_mode') == 0) {
|
||||
$map['online_hide'] = 0;
|
||||
}
|
||||
$map['status'] = 1;
|
||||
$map['pid'] = 0;
|
||||
$list_menu = self::where($map)->order('sort,id')->column('id,pid,module,title,url_value,url_type,url_target,icon,params');
|
||||
$i = 0;
|
||||
$menus = [];
|
||||
foreach ($list_menu as $key => &$menu) {
|
||||
if ($max != '' && $i >= $max) {
|
||||
break;
|
||||
}
|
||||
// 没有访问权限的节点不显示
|
||||
if (!RoleModel::checkAuth($menu['id'])) {
|
||||
continue;
|
||||
}
|
||||
if ($menu['url_value'] != '' && ($menu['url_type'] == 'module_admin' || $menu['url_type'] == 'module_home')) {
|
||||
$url = explode('/', $menu['url_value']);
|
||||
$menu['controller'] = $url[1];
|
||||
$menu['action'] = $url[2];
|
||||
$menu['url_value'] = $menu['url_type'] == 'module_admin' ? admin_url($menu['url_value'], $menu['params']) : home_url($menu['url_value'], $menu['params']);
|
||||
}
|
||||
$menus[$key] = $menu;
|
||||
$i++;
|
||||
}
|
||||
// 非开发模式,缓存菜单
|
||||
if (config('develop_mode') == 0) {
|
||||
cache($cache_tag, $menus);
|
||||
}
|
||||
}
|
||||
return $menus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取侧栏节点
|
||||
* @param string $id 模块id
|
||||
* @param string $module 模块名
|
||||
* @param string $controller 控制器名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function getSidebarMenu($id = '', $module = '', $controller = '')
|
||||
{
|
||||
$module = $module == '' ? request()->module() : $module;
|
||||
$controller = $controller == '' ? request()->controller() : $controller;
|
||||
$cache_tag = strtolower('_sidebar_menus_' . $module . '_' . $controller).'_role_'.session('user_auth.role');
|
||||
$menus = cache($cache_tag);
|
||||
|
||||
if (!$menus) {
|
||||
// 获取当前节点地址
|
||||
$location = self::getLocation($id);
|
||||
// 当前顶级节点id
|
||||
$top_id = $location[0]['id'];
|
||||
// 获取顶级节点下的所有节点
|
||||
$map = [
|
||||
'status' => 1
|
||||
];
|
||||
// 非开发模式,只显示可以显示的菜单
|
||||
if (config('develop_mode') == 0) {
|
||||
$map['online_hide'] = 0;
|
||||
}
|
||||
$menus = self::where($map)->order('sort,id')->column('id,pid,module,title,url_value,url_type,url_target,icon,params');
|
||||
|
||||
// 解析模块链接
|
||||
foreach ($menus as $key => &$menu) {
|
||||
// 没有访问权限的节点不显示
|
||||
if (!RoleModel::checkAuth($menu['id'])) {
|
||||
unset($menus[$key]);
|
||||
continue;
|
||||
}
|
||||
if ($menu['url_value'] != '' && ($menu['url_type'] == 'module_admin' || $menu['url_type'] == 'module_home')) {
|
||||
$menu['url_value'] = $menu['url_type'] == 'module_admin' ? admin_url($menu['url_value'], $menu['params']) : home_url($menu['url_value'], $menu['params']);
|
||||
}
|
||||
}
|
||||
$menus = Tree::toLayer($menus, $top_id, 2);
|
||||
|
||||
// 非开发模式,缓存菜单
|
||||
if (config('develop_mode') == 0) {
|
||||
cache($cache_tag, $menus);
|
||||
}
|
||||
}
|
||||
return $menus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定节点ID的位置
|
||||
* @param string $id 节点id,如果没有指定,则取当前节点id
|
||||
* @param bool $del_last_url 是否删除最后一个节点的url地址
|
||||
* @param bool $check 检查节点是否存在,不存在则抛出错误
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public static function getLocation($id = '', $del_last_url = false, $check = true)
|
||||
{
|
||||
$model = request()->module();
|
||||
$controller = request()->controller();
|
||||
$action = request()->action();
|
||||
|
||||
if ($id != '') {
|
||||
$cache_name = 'location_menu_'.$id;
|
||||
} else {
|
||||
$cache_name = 'location_'.$model.'_'.$controller.'_'.$action;
|
||||
}
|
||||
|
||||
$location = cache($cache_name);
|
||||
|
||||
if (!$location) {
|
||||
$map = [
|
||||
['pid', '<>', 0],
|
||||
['url_value', '=', strtolower($model.'/'.trim(preg_replace("/[A-Z]/", "_\\0", $controller), "_").'/'.$action)]
|
||||
];
|
||||
|
||||
// 当前操作对应的节点ID
|
||||
$curr_id = $id == '' ? self::where($map)->value('id') : $id;
|
||||
|
||||
// 获取节点ID是所有父级节点
|
||||
$location = Tree::getParents(self::column('id,pid,title,url_value,params'), $curr_id);
|
||||
|
||||
if ($check && empty($location)) {
|
||||
throw new Exception('获取不到当前节点地址,可能未添加节点', 9001);
|
||||
}
|
||||
|
||||
// 剔除最后一个节点url
|
||||
if ($del_last_url) {
|
||||
$location[count($location) - 1]['url_value'] = '';
|
||||
}
|
||||
|
||||
// 非开发模式,缓存菜单
|
||||
if (config('develop_mode') == 0) {
|
||||
cache($cache_name, $location);
|
||||
}
|
||||
}
|
||||
|
||||
return $location;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据分组获取节点
|
||||
* @param string $group 分组名称
|
||||
* @param bool|string $fields 要返回的字段
|
||||
* @param array $map 查找条件
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array
|
||||
*/
|
||||
public static function getMenusByGroup($group = '', $fields = true, $map = [])
|
||||
{
|
||||
$map['module'] = $group;
|
||||
return self::where($map)->order('sort,id')->column($fields, 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点分组
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array
|
||||
*/
|
||||
public static function getGroup()
|
||||
{
|
||||
$map['status'] = 1;
|
||||
$map['pid'] = 0;
|
||||
$menus = self::where($map)->order('id,sort')->column('module,title');
|
||||
return $menus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有子节点id
|
||||
* @param int $pid 父级id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array
|
||||
*/
|
||||
public static function getChildsId($pid = 0)
|
||||
{
|
||||
$ids = self::where('pid', $pid)->column('id');
|
||||
foreach ($ids as $value) {
|
||||
$ids = array_merge($ids, self::getChildsId($value));
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有父节点id
|
||||
* @param int $id 节点id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array
|
||||
*/
|
||||
public static function getParentsId($id = 0)
|
||||
{
|
||||
$pid = self::where('id', $id)->value('pid');
|
||||
$pids = [];
|
||||
if ($pid != 0) {
|
||||
$pids[] = $pid;
|
||||
$pids = array_merge($pids, self::getParentsId($pid));
|
||||
}
|
||||
return $pids;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点id获取上下级的所有id
|
||||
* @param int $id 节点id
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array
|
||||
*/
|
||||
public static function getLinkIds($id = 0)
|
||||
{
|
||||
$childs = self::getChildsId($id);
|
||||
$parents = self::getParentsId($id);
|
||||
return array_merge((array)(int)$id, $childs, $parents);
|
||||
}
|
||||
}
|
||||
343
application/admin/model/Module.php
Normal file
343
application/admin/model/Module.php
Normal file
@ -0,0 +1,343 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 海豚PHP框架 [ DolphinPHP ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: http://dolphinphp.com
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
use think\facade\Env;
|
||||
|
||||
/**
|
||||
* 模块模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class Module extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $table = '__ADMIN_MODULE__';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
/**
|
||||
* 获取所有模块的名称和标题
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getModule()
|
||||
{
|
||||
$modules = cache('modules');
|
||||
if (!$modules) {
|
||||
$modules = self::where('status', '>=', 0)->order('id')->column('name,title');
|
||||
// 非开发模式,缓存数据
|
||||
if (config('develop_mode') == 0) {
|
||||
cache('modules', $modules);
|
||||
}
|
||||
}
|
||||
return $modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有模块信息
|
||||
* @param string $keyword 查找关键词
|
||||
* @param string $status 查找状态
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getAll($keyword = '', $status = '')
|
||||
{
|
||||
$result = cache('module_all');
|
||||
if (!$result) {
|
||||
$dirs = array_map('basename', glob(Env::get('app_path').'*', GLOB_ONLYDIR));
|
||||
if ($dirs === false || !file_exists(Env::get('app_path'))) {
|
||||
$this->error = '模块目录不可读或者不存在';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 不读取模块信息的目录
|
||||
$except_module = config('system.except_module');
|
||||
// 正常模块(包括已安装和未安装)
|
||||
$dirs = array_diff($dirs, $except_module);
|
||||
|
||||
// 读取数据库模块表
|
||||
$modules = $this->order('sort asc,id desc')->column(true, 'name');
|
||||
|
||||
// 读取未安装的模块
|
||||
foreach ($dirs as $module) {
|
||||
if (!isset($modules[$module])) {
|
||||
// 获取模块信息
|
||||
$info = self::getInfoFromFile($module);
|
||||
|
||||
$modules[$module]['name'] = $module;
|
||||
|
||||
// 模块模块信息缺失
|
||||
if (empty($info)) {
|
||||
$modules[$module]['status'] = '-2';
|
||||
continue;
|
||||
}
|
||||
|
||||
// 模块模块信息不完整
|
||||
if (!$this->checkInfo($info)) {
|
||||
$modules[$module]['status'] = '-3';
|
||||
continue;
|
||||
}
|
||||
|
||||
// 模块未安装
|
||||
$modules[$module] = $info;
|
||||
$modules[$module]['status'] = '-1'; // 模块未安装
|
||||
}
|
||||
}
|
||||
|
||||
// 数量统计
|
||||
$total = [
|
||||
'all' => count($modules), // 所有模块数量
|
||||
'-2' => 0, // 已损坏数量
|
||||
'-1' => 0, // 未安装数量
|
||||
'0' => 0, // 已禁用数量
|
||||
'1' => 0, // 已启用数量
|
||||
];
|
||||
|
||||
// 过滤查询结果和统计数量
|
||||
foreach ($modules as $key => $value) {
|
||||
// 统计数量
|
||||
if (in_array($value['status'], ['-2', '-3'])) {
|
||||
// 已损坏数量
|
||||
$total['-2']++;
|
||||
} else {
|
||||
$total[(string)$value['status']]++;
|
||||
}
|
||||
|
||||
// 过滤查询
|
||||
if ($status != '') {
|
||||
if ($status == '-2') {
|
||||
// 过滤掉非已损坏的模块
|
||||
if (!in_array($value['status'], ['-2', '-3'])) {
|
||||
unset($modules[$key]);
|
||||
continue;
|
||||
}
|
||||
} else if ($value['status'] != $status) {
|
||||
unset($modules[$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($keyword != '') {
|
||||
if (stristr($value['name'], $keyword) === false && (!isset($value['title']) || stristr($value['title'], $keyword) === false) && (!isset($value['author']) || stristr($value['author'], $keyword) === false)) {
|
||||
unset($modules[$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理状态及模块按钮
|
||||
foreach ($modules as &$module) {
|
||||
// 系统核心模块
|
||||
if (isset($module['system_module']) && $module['system_module'] == '1') {
|
||||
$module['actions'] = '<button class="btn btn-sm btn-noborder btn-danger" type="button" disabled>不可操作</button>';
|
||||
$module['status_class'] = 'text-success';
|
||||
$module['status_info'] = '<i class="fa fa-check"></i> 已启用';
|
||||
$module['bg_color'] = 'success';
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($module['status']) {
|
||||
case '-3': // 模块信息不完整
|
||||
$module['title'] = '模块信息不完整';
|
||||
$module['bg_color'] = 'danger';
|
||||
$module['status_class'] = 'text-danger';
|
||||
$module['status_info'] = '<i class="fa fa-times"></i> 已损坏';
|
||||
$module['actions'] = '<button class="btn btn-sm btn-noborder btn-danger" type="button" disabled>不可操作</button>';
|
||||
break;
|
||||
case '-2': // 模块信息缺失
|
||||
$module['title'] = '模块信息缺失';
|
||||
$module['bg_color'] = 'danger';
|
||||
$module['status_class'] = 'text-danger';
|
||||
$module['status_info'] = '<i class="fa fa-times"></i> 已损坏';
|
||||
$module['actions'] = '<button class="btn btn-sm btn-noborder btn-danger" type="button" disabled>不可操作</button>';
|
||||
break;
|
||||
case '-1': // 未安装
|
||||
$module['bg_color'] = 'info';
|
||||
$module['actions'] = '<a class="btn btn-sm btn-noborder btn-success" href="'.url('install', ['name' => $module['name']]).'">安装</a>';
|
||||
$module['status_class'] = 'text-info';
|
||||
$module['status_info'] = '<i class="fa fa-fw fa-th-large"></i> 未安装';
|
||||
break;
|
||||
case '0': // 禁用
|
||||
$module['bg_color'] = 'warning';
|
||||
$module['actions'] = '<a class="btn btn-sm btn-noborder btn-success ajax-get confirm" href="'.url('enable', ['ids' => $module['id']]).'">启用</a> ';
|
||||
$module['actions'] .= '<a class="btn btn-sm btn-noborder btn-primary" href="'.url('export', ['name' => $module['name']]).'">导出</a> ';
|
||||
$module['actions'] .= '<a class="btn btn-sm btn-noborder btn-danger" href="'.url('uninstall', ['name' => $module['name']]).'">卸载</a> ';
|
||||
$module['status_class'] = 'text-warning';
|
||||
$module['status_info'] = '<i class="fa fa-ban"></i> 已禁用';
|
||||
break;
|
||||
case '1': // 启用
|
||||
$module['bg_color'] = 'success';
|
||||
$module['actions'] = '<a class="btn btn-sm btn-noborder btn-info ajax-get confirm" href="'.url('update', ['name' => $module['name']]).'">更新</a> ';
|
||||
$module['actions'] .= '<a class="btn btn-sm btn-noborder btn-warning ajax-get confirm" href="'.url('disable', ['ids' => $module['id']]).'">禁用</a> ';
|
||||
$module['actions'] .= '<a class="btn btn-sm btn-noborder btn-primary" href="'.url('export', ['name' => $module['name']]).'">导出</a> ';
|
||||
$module['actions'] .= '<a class="btn btn-sm btn-noborder btn-danger" href="'.url('uninstall', ['name' => $module['name']]).'">卸载</a> ';
|
||||
$module['status_class'] = 'text-success';
|
||||
$module['status_info'] = '<i class="fa fa-check"></i> 已启用';
|
||||
break;
|
||||
default: // 未知
|
||||
$module['title'] = '未知';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$result = ['total' => $total, 'modules' => $modules];
|
||||
// 非开发模式,缓存数据
|
||||
if (config('develop_mode') == 0) {
|
||||
cache('module_all', $result);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件获取模块信息
|
||||
* @param string $name 模块名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function getInfoFromFile($name = '')
|
||||
{
|
||||
$info = [];
|
||||
if ($name != '') {
|
||||
// 从配置文件获取
|
||||
if (is_file(Env::get('app_path'). $name . '/info.php')) {
|
||||
$info = include Env::get('app_path'). $name . '/info.php';
|
||||
}
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查模块模块信息是否完整
|
||||
* @param string $info 模块模块信息
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
*/
|
||||
private function checkInfo($info = '')
|
||||
{
|
||||
$default_item = ['name','title','author','version'];
|
||||
foreach ($default_item as $item) {
|
||||
if (!isset($info[$item]) || $info[$item] == '') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型配置信息
|
||||
* @param string $name 模型名
|
||||
* @param string $item 指定返回的模块配置项
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getConfig($name = '', $item = '')
|
||||
{
|
||||
$name = $name == '' ? request()->module() : $name;
|
||||
|
||||
$config = cache('module_config_'.$name);
|
||||
if (!$config) {
|
||||
$config = self::where('name', $name)->value('config');
|
||||
if (!$config) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$config = json_decode($config, true);
|
||||
// 非开发模式,缓存数据
|
||||
if (config('develop_mode') == 0) {
|
||||
cache('module_config_'.$name, $config);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($item)) {
|
||||
$items = explode(',', $item);
|
||||
if (count($items) == 1) {
|
||||
return isset($config[$item]) ? $config[$item] : '';
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($items as $item) {
|
||||
$result[$item] = isset($config[$item]) ? $config[$item] : '';
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型配置信息
|
||||
* @param string $name 插件名.配置名
|
||||
* @param string $value 配置值
|
||||
* @author caiweiming <314013107@qq.com>
|
||||
* @return bool
|
||||
*/
|
||||
public static function setConfig($name = '', $value = '')
|
||||
{
|
||||
$item = '';
|
||||
if (strpos($name, '.')) {
|
||||
list($name, $item) = explode('.', $name);
|
||||
}
|
||||
|
||||
// 获取缓存
|
||||
$config = cache('module_config_'.$name);
|
||||
|
||||
if (!$config) {
|
||||
$config = self::where('name', $name)->value('config');
|
||||
if (!$config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$config = json_decode($config, true);
|
||||
}
|
||||
|
||||
if ($item === '') {
|
||||
// 批量更新
|
||||
if (!is_array($value) || empty($value)) {
|
||||
// 值的格式错误,必须为数组
|
||||
return false;
|
||||
}
|
||||
|
||||
$config = array_merge($config, $value);
|
||||
} else {
|
||||
// 更新单个值
|
||||
$config[$item] = $value;
|
||||
}
|
||||
|
||||
if (false === self::where('name', $name)->setField('config', json_encode($config))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 非开发模式,缓存数据
|
||||
if (config('develop_mode') == 0) {
|
||||
cache('module_config_'.$name, $config);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件获取模块菜单
|
||||
* @param string $name 模块名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function getMenusFromFile($name = '')
|
||||
{
|
||||
$menus = [];
|
||||
if ($name != '' && is_file(Env::get('app_path'). $name . '/menus.php')) {
|
||||
// 从菜单文件获取
|
||||
$menus = include Env::get('app_path'). $name . '/menus.php';
|
||||
}
|
||||
return $menus;
|
||||
}
|
||||
}
|
||||
117
application/admin/model/Packet.php
Normal file
117
application/admin/model/Packet.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
use think\Db;
|
||||
use util\Sql;
|
||||
|
||||
/**
|
||||
* 数据包模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class Packet extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $name = 'admin_packet';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
/**
|
||||
* 获取所有数据包列表
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
// 获取数据包目录下的所有插件目录
|
||||
$dirs = array_map('basename', glob(config('packet_path').'*', GLOB_ONLYDIR));
|
||||
if ($dirs === false || !file_exists(config('packet_path'))) {
|
||||
$this->error = '插件目录不可读或者不存在';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 读取数据库数据包表
|
||||
$packets = $this->column(true, 'name');
|
||||
|
||||
// 读取未安装的数据包
|
||||
foreach ($dirs as $packet) {
|
||||
if (!isset($packets[$packet])) {
|
||||
$info = $this->getInfoFromFile($packet);
|
||||
$info['status'] = 0;
|
||||
$packets[] = $info;
|
||||
}
|
||||
}
|
||||
|
||||
return $packets;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件获取数据包信息
|
||||
* @param string $name 数据包名称
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function getInfoFromFile($name = '')
|
||||
{
|
||||
$info = [];
|
||||
if ($name != '') {
|
||||
// 从配置文件获取
|
||||
if (is_file(config('packet_path'). $name . '/info.php')) {
|
||||
$info = include config('packet_path'). $name . '/info.php';
|
||||
}
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装数据包
|
||||
* @param string $name 数据包名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
*/
|
||||
public static function install($name = '')
|
||||
{
|
||||
$info = self::getInfoFromFile($name);
|
||||
|
||||
foreach ($info['tables'] as $table) {
|
||||
$sql_file = realpath(config('packet_path').$name."/{$table}.sql");
|
||||
if (file_exists($sql_file)) {
|
||||
if (isset($info['database_prefix']) && $info['database_prefix'] != '') {
|
||||
$sql_statement = Sql::getSqlFromFile($sql_file, false, [$info['database_prefix'] => config('database.prefix')]);
|
||||
} else {
|
||||
$sql_statement = Sql::getSqlFromFile($sql_file);
|
||||
}
|
||||
|
||||
if (!empty($sql_statement)) {
|
||||
foreach ($sql_statement as $value) {
|
||||
Db::execute($value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "【{$table}.sql】文件不存在";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载数据包
|
||||
* @param string $name 数据包名
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
* @throws \think\Exception
|
||||
* @throws \think\exception\PDOException
|
||||
*/
|
||||
public static function uninstall($name = '')
|
||||
{
|
||||
$info = self::getInfoFromFile($name);
|
||||
foreach ($info['tables'] as $table) {
|
||||
$sql = "DROP TABLE IF EXISTS `". config('database.prefix') ."{$table}`;";
|
||||
Db::execute($sql);
|
||||
self::where('name', $name)->delete();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
299
application/admin/model/Plugin.php
Normal file
299
application/admin/model/Plugin.php
Normal file
@ -0,0 +1,299 @@
|
||||
<?php
|
||||
namespace app\admin\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 插件模型
|
||||
* @package app\admin\model
|
||||
*/
|
||||
class Plugin extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $name = 'admin_plugin';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 写入时处理config
|
||||
public function setConfigAttr($value)
|
||||
{
|
||||
return !empty($value) ? json_encode($value) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有插件信息
|
||||
* @param string $keyword 查找关键词
|
||||
* @param string $status 查找状态
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getAll($keyword = '', $status = '')
|
||||
{
|
||||
$result = cache('plugin_all');
|
||||
if (!$result) {
|
||||
// 获取插件目录下的所有插件目录
|
||||
$dirs = array_map('basename', glob(config('plugin_path').'*', GLOB_ONLYDIR));
|
||||
if ($dirs === false || !file_exists(config('plugin_path'))) {
|
||||
$this->error = '插件目录不可读或者不存在';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 读取数据库插件表
|
||||
$plugins = $this->order('sort asc,id desc')->column(true, 'name');
|
||||
|
||||
// 读取未安装的插件
|
||||
foreach ($dirs as $plugin) {
|
||||
if (!isset($plugins[$plugin])) {
|
||||
$plugins[$plugin]['name'] = $plugin;
|
||||
|
||||
// 获取插件类名
|
||||
$class = get_plugin_class($plugin);
|
||||
|
||||
// 插件类不存在则跳过实例化
|
||||
if (!class_exists($class)) {
|
||||
// 插件的入口文件不存在!
|
||||
$plugins[$plugin]['status'] = '-2';
|
||||
continue;
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
$obj = new $class;
|
||||
|
||||
// 插件插件信息缺失
|
||||
if (!isset($obj->info) || empty($obj->info)) {
|
||||
// 插件信息缺失!
|
||||
$plugins[$plugin]['status'] = '-3';
|
||||
continue;
|
||||
}
|
||||
|
||||
// 插件插件信息不完整
|
||||
if (!$this->checkInfo($obj->info)) {
|
||||
$plugins[$plugin]['status'] = '-4';
|
||||
continue;
|
||||
}
|
||||
|
||||
// 插件未安装
|
||||
$plugins[$plugin] = $obj->info;
|
||||
$plugins[$plugin]['status'] = '-1';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 数量统计
|
||||
$total = [
|
||||
'all' => count($plugins), // 所有插件数量
|
||||
'-2' => 0, // 错误插件数量
|
||||
'-1' => 0, // 未安装数量
|
||||
'0' => 0, // 未启用数量
|
||||
'1' => 0, // 已启用数量
|
||||
];
|
||||
|
||||
// 过滤查询结果和统计数量
|
||||
foreach ($plugins as $key => $value) {
|
||||
// 统计数量
|
||||
if (in_array($value['status'], ['-2', '-3', '-4'])) {
|
||||
// 已损坏数量
|
||||
$total['-2']++;
|
||||
} else {
|
||||
$total[(string)$value['status']]++;
|
||||
}
|
||||
|
||||
// 过滤查询
|
||||
if ($status != '') {
|
||||
if ($status == '-2') {
|
||||
// 过滤掉非已损坏的插件
|
||||
if (!in_array($value['status'], ['-2', '-3', '-4'])) {
|
||||
unset($plugins[$key]);
|
||||
continue;
|
||||
}
|
||||
} else if ($value['status'] != $status) {
|
||||
unset($plugins[$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($keyword != '') {
|
||||
if (stristr($value['name'], $keyword) === false && (!isset($value['title']) || stristr($value['title'], $keyword) === false) && (!isset($value['author']) || stristr($value['author'], $keyword) === false)) {
|
||||
unset($plugins[$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理状态及插件按钮
|
||||
foreach ($plugins as &$plugin) {
|
||||
switch ($plugin['status']) {
|
||||
case '-4': // 插件信息不完整
|
||||
$plugin['title'] = '插件信息不完整';
|
||||
$plugin['bg_color'] = 'danger';
|
||||
$plugin['status_class'] = 'text-danger';
|
||||
$plugin['status_info'] = '<i class="fa fa-times"></i> 已损坏';
|
||||
$plugin['actions'] = '<button class="btn btn-sm btn-noborder btn-danger" type="button" disabled>不可操作</button>';
|
||||
break;
|
||||
case '-3': // 插件信息缺失
|
||||
$plugin['title'] = '插件信息缺失';
|
||||
$plugin['bg_color'] = 'danger';
|
||||
$plugin['status_class'] = 'text-danger';
|
||||
$plugin['status_info'] = '<i class="fa fa-times"></i> 已损坏';
|
||||
$plugin['actions'] = '<button class="btn btn-sm btn-noborder btn-danger" type="button" disabled>不可操作</button>';
|
||||
break;
|
||||
case '-2': // 入口文件不存在
|
||||
$plugin['title'] = '入口文件不存在';
|
||||
$plugin['bg_color'] = 'danger';
|
||||
$plugin['status_class'] = 'text-danger';
|
||||
$plugin['status_info'] = '<i class="fa fa-times"></i> 已损坏';
|
||||
$plugin['actions'] = '<button class="btn btn-sm btn-noborder btn-danger" type="button" disabled>不可操作</button>';
|
||||
break;
|
||||
case '-1': // 未安装
|
||||
$plugin['bg_color'] = 'info';
|
||||
$plugin['actions'] = '<a class="btn btn-sm btn-noborder btn-success ajax-get confirm" href="'.url('install', ['name' => $plugin['name']]).'">安装</a>';
|
||||
$plugin['status_class'] = 'text-info';
|
||||
$plugin['status_info'] = '<i class="fa fa-fw fa-th-large"></i> 未安装';
|
||||
break;
|
||||
case '0': // 禁用
|
||||
$plugin['bg_color'] = 'warning';
|
||||
$plugin['actions'] = '<a class="btn btn-sm btn-noborder btn-success ajax-get confirm" href="'.url('enable', ['ids' => $plugin['id']]).'">启用</a> ';
|
||||
$plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-danger ajax-get confirm" data-tips="如果包括数据库,将同时删除数据库!" href="'.url('uninstall', ['name' => $plugin['name']]).'">卸载</a> ';
|
||||
if (isset($plugin['config']) && $plugin['config'] != '') {
|
||||
$plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-info" href="'.url('config', ['name' => $plugin['name']]).'">设置</a> ';
|
||||
}
|
||||
if ($plugin['admin'] != '0') {
|
||||
$plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-primary" href="'.url('manage', ['name' => $plugin['name']]).'">管理</a> ';
|
||||
}
|
||||
$plugin['status_class'] = 'text-warning';
|
||||
$plugin['status_info'] = '<i class="fa fa-ban"></i> 已禁用';
|
||||
break;
|
||||
case '1': // 启用
|
||||
$plugin['bg_color'] = 'success';
|
||||
$plugin['actions'] = '<a class="btn btn-sm btn-noborder btn-warning ajax-get confirm" href="'.url('disable', ['ids' => $plugin['id']]).'">禁用</a> ';
|
||||
$plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-danger ajax-get confirm" data-tips="如果包括数据库,将同时删除数据库!" href="'.url('uninstall', ['name' => $plugin['name']]).'">卸载</a> ';
|
||||
if (isset($plugin['config']) && $plugin['config'] != '') {
|
||||
$plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-info" href="'.url('config', ['name' => $plugin['name']]).'">设置</a> ';
|
||||
}
|
||||
if ($plugin['admin'] != '0') {
|
||||
$plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-primary" href="'.url('manage', ['name' => $plugin['name']]).'">管理</a> ';
|
||||
}
|
||||
$plugin['status_class'] = 'text-success';
|
||||
$plugin['status_info'] = '<i class="fa fa-check"></i> 已启用';
|
||||
break;
|
||||
default: // 未知
|
||||
$plugin['title'] = '未知';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$result = ['total' => $total, 'plugins' => $plugins];
|
||||
// 非开发模式,缓存数据
|
||||
if (config('develop_mode') == 0) {
|
||||
cache('plugin_all', $result);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查插件插件信息是否完整
|
||||
* @param string $info 插件插件信息
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return bool
|
||||
*/
|
||||
private function checkInfo($info = '')
|
||||
{
|
||||
$default_item = ['name','title','author','version'];
|
||||
foreach ($default_item as $item) {
|
||||
if (!isset($info[$item]) || $info[$item] == '') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件配置
|
||||
* @param string $name 插件名称
|
||||
* @param string $item 指定返回的插件配置项
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function getConfig($name = '', $item = '')
|
||||
{
|
||||
$config = cache('plugin_config_'.$name);
|
||||
if (!$config) {
|
||||
$config = $this->where('name', $name)->value('config');
|
||||
if (!$config) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$config = json_decode($config, true);
|
||||
// 非开发模式,缓存数据
|
||||
if (config('develop_mode') == 0) {
|
||||
cache('plugin_config_'.$name, $config);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($item)) {
|
||||
$items = explode(',', $item);
|
||||
if (count($items) == 1) {
|
||||
return isset($config[$item]) ? $config[$item] : '';
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($items as $item) {
|
||||
$result[$item] = isset($config[$item]) ? $config[$item] : '';
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置插件配置
|
||||
* @param string $name 插件名.配置名
|
||||
* @param string $value 配置值
|
||||
* @author caiweiming <314013107@qq.com>
|
||||
* @return bool
|
||||
*/
|
||||
public function setConfig($name = '', $value = '')
|
||||
{
|
||||
$item = '';
|
||||
if (strpos($name, '.')) {
|
||||
list($name, $item) = explode('.', $name);
|
||||
}
|
||||
|
||||
// 获取缓存
|
||||
$config = cache('plugin_config_'.$name);
|
||||
|
||||
if (!$config) {
|
||||
$config = $this->where('name', $name)->value('config');
|
||||
if (!$config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$config = json_decode($config, true);
|
||||
}
|
||||
|
||||
if ($item === '') {
|
||||
// 批量更新
|
||||
if (!is_array($value) || empty($value)) {
|
||||
// 值的格式错误,必须为数组
|
||||
return false;
|
||||
}
|
||||
|
||||
$config = array_merge($config, $value);
|
||||
} else {
|
||||
// 更新单个值
|
||||
$config[$item] = $value;
|
||||
}
|
||||
|
||||
if (false === $this->where('name', $name)->setField('config', json_encode($config))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 非开发模式,缓存数据
|
||||
if (config('develop_mode') == 0) {
|
||||
cache('plugin_config_'.$name, $config);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
25
application/admin/validate/Action.php
Normal file
25
application/admin/validate/Action.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace app\admin\validate;
|
||||
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* 行为验证器
|
||||
* @package app\admin\validate
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
class Action extends Validate
|
||||
{
|
||||
//定义验证规则
|
||||
protected $rule = [
|
||||
'module|所属模块' => 'require',
|
||||
'name|行为标识' => 'require|regex:^[a-zA-Z]\w{0,39}$|unique:admin_action,name^module',
|
||||
'title|行为名称' => 'require|length:1,80',
|
||||
'remark|行为描述' => 'require|length:1,128'
|
||||
];
|
||||
|
||||
//定义验证提示
|
||||
protected $message = [
|
||||
'name.regex' => '行为标识由字母和下划线组成',
|
||||
];
|
||||
}
|
||||
31
application/admin/validate/Config.php
Normal file
31
application/admin/validate/Config.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace app\admin\validate;
|
||||
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* 配置验证器
|
||||
* @package app\admin\validate
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
class Config extends Validate
|
||||
{
|
||||
// 定义验证规则
|
||||
protected $rule = [
|
||||
'group|配置分组' => 'require',
|
||||
'type|配置类型' => 'require',
|
||||
'name|配置名称' => 'require|regex:^[a-zA-Z]\w{0,39}$|unique:admin_config',
|
||||
'title|配置标题' => 'require',
|
||||
];
|
||||
|
||||
// 定义验证提示
|
||||
protected $message = [
|
||||
'name.regex' => '配置名称由字母和下划线组成',
|
||||
];
|
||||
|
||||
// 定义场景,供快捷编辑时验证
|
||||
protected $scene = [
|
||||
'name' => ['name'],
|
||||
'title' => ['title'],
|
||||
];
|
||||
}
|
||||
22
application/admin/validate/Hook.php
Normal file
22
application/admin/validate/Hook.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace app\admin\validate;
|
||||
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* 钩子验证器
|
||||
* @package app\admin\validate
|
||||
* @author 蔡伟明 <314013107@qq.com>
|
||||
*/
|
||||
class Hook extends Validate
|
||||
{
|
||||
//定义验证规则
|
||||
protected $rule = [
|
||||
'name|钩子名称' => 'require|regex:^[a-zA-Z]\w{0,39}$|unique:admin_hook'
|
||||
];
|
||||