This commit is contained in:
Wenbin.Wang 2021-12-24 16:40:05 +08:00
commit d49c9fde59
2828 changed files with 621149 additions and 0 deletions

838
CHANGELOG.md Normal file
View File

@ -0,0 +1,838 @@
## V5.1.34 LTS2019-1-30
本次更新为常规更新,修正了一些反馈的问题。
* 改进Request类的`has`方法,支持`patch`
* 改进`unique`验证的多条件支持
* 修复自定义上传验证,检测文件大小
* 改进`in`查询支持表达式
* 改进路由的`getBind`方法
* 改进验证类的错误信息获取
* 改进`response`助手函数默认值
* 修正mysql的`regexp`查询
* 改进模型类型强制转换写入对`Expression`对象的支持
## V5.1.33 LTS2019-1-16
* 修复路由中存在多个相同替换的正则BUG
* 修正whereLike查询
* join方法支持参数绑定
* 改进union方法
* 修正多对多关联的attach方法
* 改进验证类的正则规则自定义
* 改进Request类method方法
* 改进File日志类型的CLI日志写入
* 改进文件日志time_format配置对JSON格式的支持
## V5.1.32 LTS2018-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 LTS2018-11-30
该版本为常规更新,修正了一些社区反馈的问题。
主要更新如下:
* 改进查询类的`execute`方法
* 判断路由规则定义添加对请求类型的判断
* 修复`orderRaw`异常
* 修正 `optimize:autoload`指令
* 改进软删除的`destroy`方法造成重复执行事件的问题
* 改进验证类对扩展验证规则 始终验证 不管是否`require`
* 修复自定义验证`remove`所有规则的异常
* 改进时间字段的自动写入支持微秒数据
* 改进`Connection`类的`getrealsql`方法
* 修正`https`地址的URL生成
* 修复 `array_walk_recursive` 在低于PHP7.1消耗内部指针问题
* 改进手动参数绑定使用
* 改进聚合查询方法的`field`参数支持`Expression`
## V5.1.29 LTS2018-11-11
该版本主要改进了参数绑定的解析问题和提升性能,并修正了一些反馈的问题。
* 改进手动参数绑定
* 修正MISS路由的分组参数无效问题
* 行为支持对象的方法
* 修正全局查询范围
* 改进`belongsto`关联的`has`方法
* 改进`hasMany`关联
* 改进模型观察者多次注册的问题
* 改进`query`类的默认查询参数处理
* 修正`parseBetween`解析方法
* 改进路由地址生成的本地域名支持
* 改进参数绑定的实际URL解析性能
* 改进`Env`类的`getEnv`和`get`方法
* 改进模板缓存的生成优化
* 修复验证类的多语言支持
* 修复自定义场景验证`remove`规则异常
* File类添加是否自动补全扩展名的选项
* 改进`strpos`对子串是否存在的判断
* 修复`choice`无法用值选择第一个选项问题
* 验证器支持多维数组取值验证
* 改进解析`extend`和`block`标签的正则
## V5.1.28 LTS2018-10-29
该版本主要修正了上一个版本存在的一些问题,并改进了关联查询
* 改进聚合查询方法的字段支持DISTINCT
* 改进定义路由后url函数的端口生成
* 改进控制器中间件对`swoole`等的支持
* 改进Log类`save`方法
* 改进验证类的闭包验证参数
* 多对多关联支持指定中间表数据的名称
* 关联聚合查询支持闭包方式指定聚合字段
* 改进Lang类`get`方法
* 多对多关联增加判断关联数据是否存在的方法
* 改进关联查询使用`fetchsql`的情况
* 改进修改器的是否已经执行判断
* 增加`afterWith`和`beforeWith`验证规则 用于比较日期字段
## V5.1.27 LTS2018-10-22
该版本主要修正了路由绑定的参数改进了修改器的执行多次问题并正式宣布为LTS版本
* 修正路由绑定的参数丢失问题
* 修正路由别名的参数获取
* 改进修改器会执行多次的问题
## V5.1.262018-10-12
该版本主要修正了上一个版本的一些问题,并改进了全局查询范围的支持,同时包含了一个安全更新。
* 修正单一模块下注解路由无效的问题
* 改进数据库的聚合查询的字段处理
* 模型类增加`globalScope`属性定义 用于指定全局的查询范围
* 模型的`useGlobalScope`方法支持传入数组 用于指定当前查询需要使用的全局查询范围
* 改进数据集的`order`方法对数字类型的支持
* 修正上一个版本`order`方法解析的一处BUG
* 排序字段不合法或者错误的时候抛出异常
* 改进`Request`类的`file`方法对上传文件的错误判断
## V5.1.252018-9-21
该版本主要改进了查询参数绑定的性能和对浮点型的支持,以及一些细节的完善。
* 修正一处命令行问题
* 改进`Socketlog`日志驱动,支持自定义默认展开日志类别
* 修正`MorphMany`一处bug
* 跳转到上次记住的url并支持默认值
* 改进模型的异常提示
* 改进参数绑定对浮点型的支持
* 改进`order`方法解析
* 改进`json`字段数据的自动编码
* 改进日志`log_write`可能造成的日志写入死循环
* Log类增加`log_level`行为标签位置,用于对某个类型的日志进行处理
* Route类增加`clear`方法清空路由规则
* 分布式数据库配置支持使用数组
* 单日志文件也支持`max_files`参数
* 改进查询参数绑定的性能
* 改进别名路由的URL后缀参数检测
* 控制器前置方法和控制器中间件的`only`和`except`定义不区分大小写
## V5.1.242018-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.232018-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.222018-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.212018-8-2
该版本主要增加了下载响应对象和数组查询对象的支持,并修正了一些社区反馈的问题。
* 改进核心对象的无用信息调试输出
* 改进模型的`isRelationAttr`方法判断
* 模型类的`get`和`all`方法并入Db类
* 增加[下载响应对象](文件下载.md)和`download`助手函数
* 修正别名路由配置定义读取
* 改进`resultToModel`方法
* 修正开启类库后缀后的注解路由生成
* `Response`类增加`noCache`快捷方法
* 改进路由对象在`Swoole`/`Workerman`下面参数多次合并问题
* 修正路由`ajax`/`pjax`参数后路由变量无法正确获取的问题
* 增加清除中间件的方法
* 改进依赖注入的参数规范自动识别(便于对接前端小写+下划线规范)
* 改进`hasWhere`的数组条件的字段判断
* 增加[数组查询对象](高级查询.md)`Where`支持(喜欢数组查询的福音)
* 改进多对多关联的闭包支持
## V5.1.202018-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驱动
## RC12017-9-8
主要新特性为:
* 引入容器和Facade支持
* 依赖注入完善和支持更多场景
* 重构的(对象化)路由
* 配置和路由目录独立
* 取消系统常量
* 助手函数增强
* 类库别名机制
* 模型和数据库增强
* 验证类增强
* 模板引擎改进
* 支持PSR-3日志规范
* RC1版本取消了5.0多个字段批量数组查询的方式

32
README.md Normal file
View 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
View File

@ -0,0 +1 @@
deny from all

View 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(); // 渲染模板
}
}

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

View 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('文件不存在');
}
}
}

View 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 来源wangeditorwangEditor编辑器, 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]);
}
}

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

View 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('备份文件删除成功!');
}
}
}

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

View 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('删除失败');
}
}

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

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

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

View 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('操作失败');
}
}
}

View 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('设置失败');
}
}
}

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

View 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('卸载成功');
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View 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' => '行为标识由字母和下划线组成',
];
}

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

View 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'
];
//定义验证提示
protected $message = [
'name.regex' => '钩子名称由字母和下划线组成',
];
}

View File

@ -0,0 +1,25 @@
<?php
namespace app\admin\validate;
use think\Validate;
/**
* 节点验证器
* @package app\admin\validate
* @author 蔡伟明 <314013107@qq.com>
*/
class Menu extends Validate
{
//定义验证规则
protected $rule = [
'module|所属模块' => 'require',
'pid|所属节点' => 'require',
'title|节点标题' => 'require',
];
//定义验证提示
protected $message = [
'module.require' => '请选择所属模块',
'pid.require' => '请选择所属节点',
];
}

View File

@ -0,0 +1,18 @@
<?php
namespace app\admin\validate;
use think\Validate;
/**
* 插件验证器
* @package app\admin\validate
* @author 蔡伟明 <314013107@qq.com>
*/
class Plugin extends Validate
{
//定义验证规则
protected $rule = [
'name|插件名称' => 'require|unique:admin_plugin',
'title|插件标题' => 'require',
];
}

View File

@ -0,0 +1,78 @@
<!DOCTYPE html>
<!--[if IE 9]> <html class="ie9 no-focus" lang="zh"> <![endif]-->
<!--[if gt IE 9]><!--> <html class="no-focus" lang="zh"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<title>跳转提示 | {:config('web_site_title')} - DolphinPHP</title>
<meta name="robots" content="noindex, nofollow">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1.0">
<!-- Icons -->
<!-- The following icons can be replaced with your own, they are used by desktop and mobile browsers -->
<link rel="shortcut icon" href="__STATIC__/img/favicons/favicon.png">
<!-- END Icons -->
<!-- Stylesheets -->
<!-- Bootstrap and OneUI CSS framework -->
<link rel="stylesheet" href="__ADMIN_CSS__/bootstrap.min.css">
<link rel="stylesheet" href="__ADMIN_CSS__/oneui.css">
<link rel="stylesheet" href="__ADMIN_CSS__/dolphin.css">
<!-- END Stylesheets -->
</head>
<body>
<!-- Error Content -->
<div class="content bg-white text-center pulldown overflow-hidden">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<!-- Error Titles -->
<h1 class="font-w300 {$code? 'text-success' : 'text-city'} push-10 animated flipInX"><i class="fa fa-{$code? 'check' : 'times'}-circle"></i> <?php echo(strip_tags($msg));?></h1>
<p class="font-w300 push-20 animated fadeInUp">页面自动 <a id="href" href="<?php echo($url);?>">跳转</a> 等待时间: <b id="wait"><?php echo($wait);?></b>秒</p>
<div class="push-50">
<a class="btn btn-minw btn-rounded btn-success" href="<?php echo($url);?>"><i class="fa fa-external-link-square"></i> 立即跳转</a>
<button class="btn btn-minw btn-rounded btn-warning" type="button" onclick="stop()"><i class="fa fa-ban"></i> 禁止跳转</button>
<a class="btn btn-minw btn-rounded btn-default" href="{$Request.baseFile}"><i class="fa fa-home"></i> 返回首页</a>
</div>
<!-- END Error Titles -->
</div>
</div>
</div>
<!-- END Error Content -->
<!-- Error Footer -->
<div class="content pulldown text-muted text-center">
极简 · 极速 · 极致<br>
海豚PHP让开发更简单<br>
由 <a class="link-effect" href="http://www.dolphinphp.com">DolphinPHP</a> 强力驱动 <a class="link-effect" href="http://www.zrthink.com">卓锐软件</a> 倾情奉献
</div>
<!-- END Error Footer -->
<script type="text/javascript">
(function(){
let wait = document.getElementById('wait'),
href = document.getElementById('href').href,
pop = '{$Request.param._pop}'; //获取窗口索引
let interval = setInterval(function(){
let time = --wait.innerHTML;
if(time <= 0) {
if (pop === '1' && parent.layer !== undefined) {
let index = parent.layer.getFrameIndex(window.name);
parent.layer.close(index);
} else {
location.href = href;
}
clearInterval(interval);
}
}, 1000);
// 禁止跳转
window.stop = function (){
clearInterval(interval);
}
})();
</script>
</body>
</html>

View File

@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>页面提示 - {:config('web_site_title')}</title>
<link rel=stylesheet type=text/css href="https://wx.gtimg.com/pay/css/common/common.css?v=20170117">
<link rel=stylesheet type=text/css href="https://wx.gtimg.com/pay/css/error/error.css?v=20151125">
<script src="https://wx.gtimg.com/third/jquery/jquery-1.7.min.js"></script>
</head>
<body class="browser-tips no-nav no-topbar">
<div class="container wrap">
<div class="container-bd">
<div class="page-msg mini">
<div class="inner">
<div class="msg-ico"><i class="ico-msg-s warn"></i></div>
<div class="msg-cnt">
<p>温馨提示在IE浏览器环境下浏览本系统体验不够友好建议您使用下方的浏览器访问本系统。</p>
</div>
</div>
</div>
<div class="browser-list">
<a class="browser-item chrome" href="http://xiazai.sogou.com/detail/34/0/6262355089742005676.html?uID=04583CB73B70900A0000000057318502&amp;w=2295" target="_blank">
<i class="icon-browser"></i>
<p class="browser-name">Chrome浏览器</p>
</a>
<a class="browser-item qq" href="http://browser.qq.com/" target="_blank">
<i class="icon-browser"></i>
<p class="browser-name">QQ浏览器</p>
</a>
<a class="browser-item sogou" href="http://ie.sogou.com/" target="_blank">
<i class="icon-browser"></i>
<p class="browser-name">搜狗浏览器</p>
</a>
<a class="browser-item firefox" href="http://www.firefox.com.cn/?igfox" target="_blank">
<i class="icon-browser"></i>
<p class="browser-name">Firefox浏览器</p>
</a>
</div>
<div class="browser-more">
<div class="browser-text">如果你正在使用的是双核浏览器比如QQ浏览器、搜狗浏览器、猎豹浏览器、世界之窗浏览器、傲游浏览器、360浏览器等可以使用浏览器的极速模式来继续访问本系统。<a id="access" href="javascript:void(0);">查看详情</a>
</div>
<div id="tips" class="browser-step" style="display: block;">
<ul>
<li>方法一,点击浏览器顶部地址栏右侧的浏览器兼容模式图标,<img alt="" src="https://wx.gtimg.com/pay/img/error/ie_s.png"><img alt="" src="https://wx.gtimg.com/pay/img/error/ie_mode.png">,切换到极速模式
<p class="demo"><img alt="" src="https://wx.gtimg.com/pay/img/error/pic_ie01.png"></p>
</li>
<li>方法二,在当前页面中,点击鼠标右键,选择“切换到极速模式”
<p class="demo"><img alt="" src="https://wx.gtimg.com/pay/img/error/pic_ie02.png"></p>
</li>
<li>方法三,在浏览器菜单栏中选择工具选项,打开“兼容性视图设置”,把设置框底部的“在兼容性视图显示”三个勾选框去掉
<p class="demo"><img alt="" src="https://wx.gtimg.com/pay/img/error/pic_ie03.png"></p></li>
</ul>
</div>
</div>
</div>
</div>
<script>
(function(){
var tips = document.getElementById("tips");
var access = document.getElementById("access");
var isShow = true;
access.onclick = function(){
if(!isShow){
tips.style.display = "block";
isShow = true;
}else{
tips.style.display = "none";
isShow = false;
}
}
})();
</script>
</body>
</html>

View File

@ -0,0 +1,31 @@
{extend name="layout" /}
{block name="page-header"}{/block}
{block name="content"}
{notempty name="default_pass"}
<div class="alert alert-danger alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h3 class="font-w300 push-15">安全提示</h3>
<p>超级管理员默认密码未修改,建议马上 <a class="alert-link link-effect" href="{:url('user/index/edit', ['id' => 1])}">修改</a></p>
</div>
{/notempty}
{// 后台首页钩子}
<div class="row">
{:hook('admin_index')}
</div>
{/block}
{block name="script"}
<script>
$(document).ready(function () {
$.ajax({
url: '{:url("checkUpdate")}',
type: 'GET'
}).done(function(data) {
$('#product-auth').html(data.auth);
$('#product-update').html($('#product-update').text() + ' ' + data.update);
});
});
</script>
{/block}

View File

@ -0,0 +1,493 @@
<!DOCTYPE html>
<!--[if IE 9]> <html class="ie9 no-focus" lang="zh"> <![endif]-->
<!--[if gt IE 9]><!--> <html class="no-focus" lang="zh"> <!--<![endif]-->
<head>
<meta charset="utf-8">
{block name="page-title"}<title>{$page_title|default='后台'} | {:config('web_site_title')} - DolphinPHP</title>{/block}
<meta name="description" content="{:config('web_site_description')}">
<meta name="author" content="caiweiming">
<meta name="robots" content="noindex, nofollow">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1.0,user-scalable=0">
<!-- Icons -->
<!-- The following icons can be replaced with your own, they are used by desktop and mobile browsers -->
<link rel="shortcut icon" href="__ADMIN_IMG__/favicons/favicon.ico">
<link rel="icon" type="image/png" href="__ADMIN_IMG__/favicons/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="__ADMIN_IMG__/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="__ADMIN_IMG__/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="__ADMIN_IMG__/favicons/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="__ADMIN_IMG__/favicons/favicon-192x192.png" sizes="192x192">
<link rel="apple-touch-icon" sizes="57x57" href="__ADMIN_IMG__/favicons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="__ADMIN_IMG__/favicons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="__ADMIN_IMG__/favicons/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="__ADMIN_IMG__/favicons/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="__ADMIN_IMG__/favicons/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="__ADMIN_IMG__/favicons/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="__ADMIN_IMG__/favicons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="__ADMIN_IMG__/favicons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="__ADMIN_IMG__/favicons/apple-touch-icon-180x180.png">
<!-- END Icons -->
<!-- Stylesheets -->
<!-- Page JS Plugins CSS -->
{notempty name="_css_files"}
{eq name="Think.config.minify_status" value="1"}
<link rel="stylesheet" href="{:minify('group', $_css_files)}">
{else/}
{volist name="_css_files" id="css"}
{:load_assets($css)}
{/volist}
{/eq}
{/notempty}
{notempty name="_icons"}
{volist name="_icons" id="icon"}
<link rel="stylesheet" href="{$icon.url}">
{/volist}
{/notempty}
{block name="plugins-css"}{/block}
<!-- Bootstrap and OneUI CSS framework -->
{eq name="Think.config.minify_status" value="1"}
<link rel="stylesheet" id="css-main" href="{:minify('group', 'libs_css,core_css')}">
{else/}
<link rel="stylesheet" href="__LIBS__/sweetalert/sweetalert.min.css?v={:config('asset_version')}">
<link rel="stylesheet" href="__LIBS__/magnific-popup/magnific-popup.min.css?v={:config('asset_version')}">
<link rel="stylesheet" href="__ADMIN_CSS__/bootstrap.min.css?v={:config('asset_version')}">
<link rel="stylesheet" href="__ADMIN_CSS__/oneui.css?v={:config('asset_version')}">
<link rel="stylesheet" href="__ADMIN_CSS__/dolphin.css?v={:config('asset_version')}" id="css-main">
<link rel="stylesheet" href="__LIBS__/viewer/viewer.min.css?v={:config('asset_version')}">
{/eq}
<link rel="stylesheet" id="css-theme" href="__ADMIN_CSS__/themes/{:config('system_color')}.min.css?v={:config('asset_version')}">
<!--页面css-->
{block name="style"}{/block}
{notempty name="_pop"}
<style>
#page-container.sidebar-l.sidebar-o {
padding-left: 0;
}
.header-navbar-fixed #main-container {
padding-top: 0;
}
</style>
{/notempty}
<!-- END Stylesheets -->
<!--插件css钩子-->
{:hook('page_plugin_css')}
<!--自定义css-->
<link rel="stylesheet" href="__ADMIN_CSS__/custom.css?v={:config('asset_version')}">
<script>
// url
var dolphin = {
'top_menu_url': '{:url("admin/ajax/getSidebarMenu")}',
'theme_url': '{:url("admin/ajax/setTheme")}',
'jcrop_upload_url': '{$jcrop_upload_url ? $jcrop_upload_url : url("admin/attachment/upload", ["dir" => "images", "from" => "jcrop", "module" => request()->module()])}',
'editormd_upload_url': '{$editormd_upload_url ? $editormd_upload_url : url("admin/attachment/upload", ["dir" => "images", "from" => "editormd", "module" => request()->module()])}',
'editormd_mudule_path': '__LIBS__/editormd/lib/',
'ueditor_upload_url': '{$ueditor_upload_url ? $ueditor_upload_url : url("admin/attachment/upload", ["dir" => "images", "from" => "ueditor", "module" => request()->module()])}',
'wangeditor_upload_url': '{$wangeditor_upload_url ? $wangeditor_upload_url : url("admin/attachment/upload", ["dir" => "images", "from" => "wangeditor", "module" => request()->module()])}',
'wangeditor_emotions': "__LIBS__/wang-editor/emotions.data",
'ckeditor_img_upload_url': '{$ckeditor_img_upload_url ? $ckeditor_img_upload_url : url("admin/attachment/upload", ["dir" => "images", "from" => "ckeditor", "module" => request()->module()])}',
'WebUploader_swf': '__LIBS__/webuploader/Uploader.swf',
'file_upload_url': '{$file_upload_url ? $file_upload_url : url("admin/attachment/upload", ["dir" => "files", "module" => request()->module()])}',
'image_upload_url': '{$image_upload_url ? $image_upload_url : url("admin/attachment/upload", ["dir" => "images", "module" => request()->module()])}',
'upload_check_url': '{$upload_check_url ? $upload_check_url : url("admin/ajax/check")}',
'get_level_data': '{:url("admin/ajax/getLevelData")}',
'quick_edit_url': '{$quick_edit_url ? $quick_edit_url : url("quickEdit")}',
'aside_edit_url': '{$aside_edit_url ? $aside_edit_url : url("admin/system/quickEdit")}',
'triggers': {:json_encode(isset($field_triggers) ? $field_triggers : [])}, // 触发器集合
'field_hide': '{$field_hide|default=""}', // 需要隐藏的字段
'field_values': '{$field_values|default=""}',
'validate': '{$validate|default=""}', // 验证器
'validate_fields': '{$validate_fields|default=""}', // 验证字段
'search_field': '{:input("param.search_field", "")}', // 搜索字段
// 字段过滤
'_filter': '{$Request.param._filter ? $Request.param._filter : (isset($_filter) ? $_filter : "")}',
'_filter_content': '{$Request.param._filter_content == '' ? (isset($_filter_content) ? $_filter_content : "") : $Request.param._filter_content}',
'_field_display': '{$Request.param._field_display ? $Request.param._field_display : (isset($_field_display) ? $_field_display : "")}',
'_field_clear': {:json_encode(isset($field_clear) ? $field_clear : [])},
'get_filter_list': '{:url("admin/ajax/getFilterList")}',
'curr_url': '{:url("", $Request.route)}',
'curr_params': {:json_encode($Request.param)},
'layer': {:json_encode(config("zbuilder.pop"))}
};
</script>
</head>
<body>
<!-- Page Container -->
<div id="page-container" class="sidebar-l sidebar-o side-scroll header-navbar-fixed {empty name="_pop"}{$_COOKIE['sidebarMini'] ?= 'sidebar-mini'}{/empty}">
<!-- Side Overlay-->
{empty name="_pop"}
{block name="aside"}
<aside id="side-overlay">
<!-- Side Overlay Scroll Container -->
<div id="side-overlay-scroll">
<!-- Side Header -->
<div class="side-header side-content">
<!-- Layout API, functionality initialized in App() -> uiLayoutApi() -->
<button class="btn btn-default pull-right" type="button" data-toggle="layout" data-action="side_overlay_close">
<i class="fa fa-times"></i>
</button>
<span>
<img class="img-avatar img-avatar32" src="{$Think.session.user_auth.uid|get_avatar}" alt="">
<span class="font-w600 push-10-l">{:session('user_auth.username')}</span>
</span>
</div>
<!-- END Side Header -->
<!--侧栏-->
{include file="../application/common/builder/aside/layout.html" /}
</div>
<!-- END Side Overlay Scroll Container -->
</aside>
{/block}
{/empty}
<!-- END Side Overlay -->
<!-- Sidebar -->
{empty name="_pop"}
{block name="sidebar"}
<nav id="sidebar">
<!-- Sidebar Scroll Container -->
<div id="sidebar-scroll">
<!-- Sidebar Content -->
<!-- Adding .sidebar-mini-hide to an element will hide it when the sidebar is in mini mode -->
<div class="sidebar-content">
<!-- Side Header -->
<div class="side-header side-content bg-white-op dolphin-header">
<!-- Layout API, functionality initialized in App() -> uiLayoutApi() -->
<button class="btn btn-link text-gray pull-right hidden-md hidden-lg" type="button" data-toggle="layout" data-action="sidebar_close">
<i class="fa fa-times"></i>
</button>
<!-- Themes functionality initialized in App() -> uiHandleTheme() -->
<div class="btn-group pull-right">
<button class="btn btn-link text-gray dropdown-toggle" data-toggle="dropdown" type="button">
<i class="si si-drop"></i>
</button>
<ul class="dropdown-menu dropdown-menu-right font-s13 sidebar-mini-hide">
<li {$system_color == 'modern' ?= 'class="active"'}>
<a data-toggle="theme" data-theme="modern" data-css="__ADMIN_CSS__/themes/modern.min.css" tabindex="-1" href="javascript:void(0)">
<i class="fa fa-circle text-modern pull-right"></i> <span class="font-w600">Modern</span>
</a>
</li>
<li {$system_color == 'amethyst' ?= 'class="active"'}>
<a data-toggle="theme" data-theme="amethyst" data-css="__ADMIN_CSS__/themes/amethyst.min.css" tabindex="-1" href="javascript:void(0)">
<i class="fa fa-circle text-amethyst pull-right"></i> <span class="font-w600">Amethyst</span>
</a>
</li>
<li {$system_color == 'city' ?= 'class="active"'}>
<a data-toggle="theme" data-theme="city" data-css="__ADMIN_CSS__/themes/city.min.css" tabindex="-1" href="javascript:void(0)">
<i class="fa fa-circle text-city pull-right"></i> <span class="font-w600">City</span>
</a>
</li>
<li {$system_color == 'flat' ?= 'class="active"'}>
<a data-toggle="theme" data-theme="flat" data-css="__ADMIN_CSS__/themes/flat.min.css" tabindex="-1" href="javascript:void(0)">
<i class="fa fa-circle text-flat pull-right"></i> <span class="font-w600">Flat</span>
</a>
</li>
<li {$system_color == 'smooth' ?= 'class="active"'}>
<a data-toggle="theme" data-theme="smooth" data-css="__ADMIN_CSS__/themes/smooth.min.css" tabindex="-1" href="javascript:void(0)">
<i class="fa fa-circle text-smooth pull-right"></i> <span class="font-w600">Smooth</span>
</a>
</li>
<li {$system_color == 'default' ?= 'class="active"'}>
<a data-toggle="theme" data-theme="default" tabindex="-1" href="javascript:void(0)">
<i class="fa fa-circle text-default pull-right"></i> <span class="font-w600">Default</span>
</a>
</li>
</ul>
</div>
<a class="h5 text-white" href="{:url('admin/index/index')}">
{notempty name="Think.config.web_site_logo"}
<img src="{$Think.config.web_site_logo|get_file_path}" class="logo" alt="{$Think.config.web_site_title|default='Dolphin PHP'}">
{else/}
<img src="{$Think.config.public_static_path}admin/img/logo.png" class="logo" alt="Dolphin PHP">
{/notempty}
{notempty name="Think.config.web_site_logo_text"}
<img src="{$Think.config.web_site_logo_text|get_file_path}" class="logo-text sidebar-mini-hide" alt="{$Think.config.web_site_title|default='Dolphin PHP'}">
{else/}
<img src="{$Think.config.public_static_path}admin/img/logo-text.png" class="logo-text sidebar-mini-hide" alt="Dolphin PHP">
{/notempty}
</a>
</div>
<!-- END Side Header -->
<!-- Side Content -->
<div class="side-content" id="sidebar-menu">
{notempty name="_sidebar_menus"}
<ul class="nav-main" id="nav-{$_location[0]['id']}">
{volist name="_sidebar_menus" id="menu"}
<li {$menu['id'] == $_location[1]["id"]?='class="open"'}>
{notempty name="menu.url_value"}
<a {if($menu['id'] == $_location[1]["id"])}class="active"{/if} href="{$menu.url_value}" target="{$menu.url_target}"><i class="{$menu.icon}"></i><span class="sidebar-mini-hide">{$menu.title}</span></a>
{else/}
<a class="nav-submenu" data-toggle="nav-submenu" href="javascript:void(0);"><i class="{$menu.icon}"></i><span class="sidebar-mini-hide">{$menu.title}</span></a>
{notempty name="menu['child']"}
<ul>
{volist name="menu['child']" id="submenu"}
<li>
<a {if(isset($_location[2]) && $submenu['id'] == $_location[2]["id"])}class="active"{/if} href="{$submenu.url_value}" target="{$submenu.url_target}"><i class="{$submenu.icon}"></i>{$submenu.title}</a>
</li>
{/volist}
</ul>
{/notempty}
{/notempty}
</li>
{/volist}
</ul>
{/notempty}
</div>
<!-- END Side Content -->
</div>
<!-- Sidebar Content -->
</div>
<!-- END Sidebar Scroll Container -->
</nav>
{/block}
{/empty}
<!-- END Sidebar -->
<!-- Header -->
{empty name="_pop"}
{block name="header"}
<header id="header-navbar" class="content-mini content-mini-full">
<!-- Header Navigation Right -->
<ul class="nav-header pull-right">
<li>
<div class="btn-group">
<button class="btn btn-default btn-image dropdown-toggle" data-toggle="dropdown" type="button">
<img src="{$Think.session.user_auth.uid|get_avatar}" alt="{:session('user_auth.username')}">
<span class="caret"></span>
{notempty name="_message"}
{gt name="_message" value="0"}
<i class="fa fa-circle text-danger notice-circle"></i>
{/gt}
{/notempty}
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li class="dropdown-header">{:session('user_auth.username')} ({:session('user_auth.role_name')})</li>
<li>
<a tabindex="-1" href="{:url('admin/index/profile')}">
<i class="si si-settings pull-right"></i>个人设置
</a>
</li>
<li>
<a tabindex="-1" href="{:url('admin/message/index')}">
<i class="si si-envelope-open pull-right"></i><span class="badge badge-primary pull-right">{$_message|default=0}</span>消息中心
</a>
</li>
<li class="divider"></li>
<li>
<a tabindex="-1" href="{:url('user/publics/signout')}">
<i class="si si-logout pull-right"></i>退出帐号
</a>
</li>
</ul>
</div>
</li>
<li>
<a class="btn btn-default ajax-get" href="{:url('admin/index/wipeCache')}" data-toggle="tooltip" data-placement="bottom" data-original-title="清空缓存">
<i class="fa fa-trash"></i>
</a>
</li>
<li>
<a class="btn btn-default" href="{:rtrim(home_url('/'), '/')}" target="_blank" data-toggle="tooltip" data-placement="bottom" data-original-title="打开前台">
<i class="fa fa-external-link-square"></i>
</a>
</li>
<li>
<!-- Layout API, functionality initialized in App() -> uiLayoutApi() -->
<button class="btn btn-default" data-toggle="layout" data-action="side_overlay_toggle" title="侧边栏" type="button">
<i class="fa fa-tasks"></i>
</button>
</li>
</ul>
<!-- END Header Navigation Right -->
<!-- Header Navigation Left -->
<ul class="nav nav-pills pull-left">
<li class="hidden-md hidden-lg">
<!-- Layout API, functionality initialized in App() -> uiLayoutApi() -->
<a href="javascript:void(0)" data-toggle="layout" data-action="sidebar_toggle"><i class="fa fa-navicon"></i></a>
</li>
<li class="hidden-xs hidden-sm">
<!-- Layout API, functionality initialized in App() -> uiLayoutApi() -->
<a href="javascript:void(0)" title="打开/关闭左侧导航" data-toggle="layout" data-action="sidebar_mini_toggle"><i class="fa fa-bars"></i></a>
</li>
{notempty name="_top_menus"}
{volist name="_top_menus" id="menu"}
<li class="hidden-xs hidden-sm {$menu['id'] == $_location[0]['id'] ?= 'active'}">
{in name="menu.url_type" value="module_admin,module_home"}
<a href="javascript:void(0);" data-module-id="{$menu['id']}" data-module="{$menu['module']}" data-controller="{$menu['controller']}" target="{$menu['url_target']}" class="top-menu"><i class="{$menu.icon}"></i> {$menu.title}</a>
{else/}
<a href="{$menu['url_value']}" target="{$menu['url_target']}"><i class="{$menu.icon}"></i> {$menu.title}</a>
{/in}
</li>
{/volist}
{/notempty}
<li>
<!-- Opens the Apps modal found at the bottom of the page, before including JS code -->
<a href="#" data-toggle="modal" data-target="#apps-modal"><i class="si si-grid"></i></a>
</li>
</ul>
<!-- END Header Navigation Left -->
</header>
{/block}
{/empty}
<!-- END Header -->
<!-- Main Container -->
<main id="main-container">
<!-- Page Header -->
{block name="page-header"}
{empty name="_pop"}
<div class="bg-gray-lighter">
<ol class="breadcrumb">
<li><i class="fa fa-map-marker"></i></li>
{notempty name="_location"}
{volist name="_location" id="v"}
<li><a class="link-effect" href="{notempty name='$v["url_value"]'}{:url($v.url_value, $v.params)}{else/}javascript:void(0);{/notempty}">{$v.title}</a></li>
{/volist}
{/notempty}
</ol>
</div>
{/empty}
{/block}
<!-- END Page Header -->
<!-- Page Content -->
<div class="content">
{// 页面提示钩子}
{:hook('page_tips')}
{// 主体内容}
{block name="content"}{/block}
</div>
<!-- END Page Content -->
</main>
<!-- END Main Container -->
<!-- Footer -->
{empty name="_pop"}
<footer id="page-footer" class="content-mini content-mini-full font-s12 bg-gray-lighter clearfix">
<div class="pull-right">
Crafted with <i class="fa fa-heart text-city"></i> by <a class="font-w600" href="{:config('dolphin.company_website')}" target="_blank">{:config('dolphin.company_name')}</a>
</div>
<div class="pull-left">
<a class="font-w600" href="{:config('dolphin.product_website')}" target="_blank">{:config('dolphin.product_name')} {:config('dolphin.product_version')}</a> &copy; <span class="js-year-copy"></span>
</div>
</footer>
{/empty}
<!-- END Footer -->
</div>
<!-- END Page Container -->
<!-- Apps Modal -->
<!-- Opens from the button in the header -->
<div class="modal fade" id="apps-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-top">
<div class="modal-content">
<!-- Apps Block -->
<div class="block block-themed block-transparent">
<div class="block-header bg-primary-dark">
<ul class="block-options">
<li>
<button data-dismiss="modal" type="button"><i class="si si-close"></i></button>
</li>
</ul>
<h3 class="block-title">所有模块</h3>
</div>
<div class="block-content">
<div class="row text-center">
{notempty name="_top_menus_all"}
{volist name="_top_menus_all" id="menu"}
<div class="col-xs-6 col-sm-3">
{in name="menu.url_type" value="module_admin,module_home"}
<a class="block block-rounded top-menu" href="javascript:void(0);" data-module-id="{$menu['id']}" data-module="{$menu['module']}" data-controller="{$menu['controller']}" target="{$menu['url_target']}">
<div class="block-content text-white {$menu['id'] == $_location[0]['id'] ? 'bg-primary' : 'bg-primary-dark'}">
<i class="{$menu.icon} fa-2x"></i>
<div class="font-w600 push-15-t push-15">{$menu.title}</div>
</div>
</a>
{else/}
<a class="block block-rounded" href="{$menu['url_value']}" target="{$menu['url_target']}">
<div class="block-content text-white {$menu['id'] == $_location[0]['id'] ? 'bg-primary' : 'bg-primary-dark'}">
<i class="{$menu.icon} fa-2x"></i>
<div class="font-w600 push-15-t push-15">{$menu.title}</div>
</div>
</a>
{/in}
</div>
{/volist}
{/notempty}
</div>
</div>
</div>
<!-- END Apps Block -->
</div>
</div>
</div>
<!-- END Apps Modal -->
<!-- OneUI Core JS: jQuery, Bootstrap, slimScroll, scrollLock, Appear, CountTo, Placeholder, Cookie and App.js -->
{eq name="Think.config.minify_status" value="1"}
<script src="{:minify('group', 'core_js,libs_js')}"></script>
{else/}
<script src="__ADMIN_JS__/core/jquery.min.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/core/bootstrap.min.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/core/jquery.slimscroll.min.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/core/jquery.scrollLock.min.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/core/jquery.appear.min.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/core/jquery.countTo.min.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/core/jquery.placeholder.min.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/core/js.cookie.min.js?v={:config('asset_version')}"></script>
<script src="__LIBS__/magnific-popup/magnific-popup.min.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/app.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/dolphin.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/builder/form.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/builder/aside.js?v={:config('asset_version')}"></script>
<script src="__ADMIN_JS__/builder/table.js?v={:config('asset_version')}"></script>
<script src="__LIBS__/bootstrap-notify/bootstrap-notify.min.js?v={:config('asset_version')}"></script>
<script src="__LIBS__/sweetalert/sweetalert.min.js?v={:config('asset_version')}"></script>
<script src="__LIBS__/js-xss/xss.min.js?v={:config('asset_version')}"></script>
<script src="__LIBS__/viewer/viewer.min.js?v={:config('asset_version')}"></script>
{/eq}
<!-- Page JS Plugins -->
<script src="__LIBS__/layer/layer.js?v={:config('asset_version')}"></script>
{notempty name="_js_files"}
{eq name="Think.config.minify_status" value="1"}
<script src="{:minify('group', $_js_files)}"></script>
{else/}
{volist name="_js_files" id="js"}
{:load_assets($js, 'js')}
{/volist}
{/eq}
{/notempty}
<script>
jQuery(function () {
App.initHelpers(['appear', 'slimscroll', 'magnific-popup', 'table-tools']);
{notempty name="_js_init"}
App.initHelpers({$_js_init|raw});
{/notempty}
});
</script>
<!--页面js-->
{block name="script"}{/block}
<!--插件js钩子-->
{:hook('page_plugin_js')}
{// 额外HTML代码 }
{$extra_html|raw|default=''}
</body>
</html>

View File

@ -0,0 +1,203 @@
{extend name="layout" /}
{block name="plugins-css"}
<link href="__LIBS__/jquery-nestable/jquery.nestable.css" rel="stylesheet" type="text/css" />
{/block}
{block name="content"}
<div class="alert alert-warning alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<p><strong><i class="fa fa-fw fa-info-circle"></i> 提示:</strong>按住表头可拖动节点,调整后点击【保存节点】。</p>
</div>
<div class="row">
<div class="col-md-12">
<div class="block">
{notempty name="tab_nav"}
<ul class="nav nav-tabs">
{volist name="tab_nav['tab_list']" id="tab"}
<li {eq name="tab_nav.curr_tab" value="$key"}class="active"{/eq}>
<a href="{$tab.url}">{$tab.title}</a>
</li>
{/volist}
<li {eq name="tab_nav.curr_tab" value="module-sort"}class="active"{/eq}>
<a href="{:url('', ['group' => 'module-sort'])}">模块排序</a>
</li>
<li class="pull-right">
<ul class="block-options push-10-t push-10-r">
<li>
<button type="button" data-toggle="block-option" data-action="fullscreen_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="refresh_toggle" data-action-mode="demo"><i class="si si-refresh"></i></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="content_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="close"><i class="si si-close"></i></button>
</li>
</ul>
</li>
</ul>
{else/}
<div class="block-header bg-gray-lighter">
<ul class="block-options">
<li>
<button type="button" data-toggle="block-option" data-action="fullscreen_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="refresh_toggle" data-action-mode="demo"><i class="si si-refresh"></i></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="content_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="close"><i class="si si-close"></i></button>
</li>
</ul>
<h3 class="block-title">{$page_title|raw}</h3>
</div>
{/notempty}
<div class="block-content tab-content">
<div class="tab-pane active">
{notempty name="menus"}
<div class="row data-table-toolbar">
<div class="col-sm-12">
<form action="{$Request.url}" method="get">
<div class="toolbar-btn-action">
<a title="新增" class="btn btn-primary" href="{:url('add', ['module' => $Request.param.group])}"><i class="fa fa-plus-circle"></i> 新增</a>
<button title="保存" type="button" class="btn btn-default disabled" id="save" disabled><i class="fa fa-check-circle-o"></i> 保存节点</button>
<button title="隐藏禁用节点" type="button" class="btn btn-danger" id="hide_disable"><i class="fa fa-eye-slash"></i> 隐藏禁用节点</button>
<button title="显示禁用节点" type="button" class="btn btn-info" id="show_disable"><i class="fa fa-eye"></i> 显示禁用节点</button>
<button title="展开所有节点" type="button" class="btn btn-success" id="expand-all"><i class="fa fa-plus"></i> 展开所有节点</button>
<button title="收起所有节点" type="button" class="btn btn-warning" id="collapse-all"><i class="fa fa-minus"></i> 收起所有节点</button>
<span class="form-inline">
<input class="form-control" type="text" name="max" value="{$Request.get.max|default=''}" placeholder="显示层数">
</span>
</div>
</form>
</div>
</div>
<div class="dd" id="menu_list">
<ol class="dd-list">{$menus|raw}</ol>
</div>
{/notempty}
{notempty name="modules"}
<form action="{:url('')}" method="post" name="sort-form" class="sort-form">
<button title="保存" type="submit" class="btn btn-success push-10 ajax-post" target-form="sort-form">保存排序</button>
<div class="row">
<div class="col-md-12">
<div id="sortable" class="connectedSortable push-20">
{volist name="modules" id="module"}
<div class="sortable-item pull-left">
<input type="hidden" name="sort[]" value="{$key}">
<i class="{$module.icon}"></i> {$module.title}
</div>
{/volist}
</div>
</div>
</div>
</form>
{/notempty}
</div>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="__LIBS__/jquery-nestable/jquery.nestable.js"></script>
<script src="__LIBS__/jquery-ui/jquery-ui.min.js"></script>
<script>
$(document).ready(function(){
// 模块拖拽
$( "#sortable" ).sortable({
connectWith: ".connectedSortable"
}).disableSelection();
// 保存节点
$('#save').click(function(){
Dolphin.loading();
$.post("{:url('save')}", {menus: $('#menu_list').nestable('serialize')}, function(data) {
Dolphin.loading('hide');
if (data.code) {
$('#save').removeClass('btn-success').addClass('btn-default disabled');
Dolphin.notify(data.msg, 'success');
} else {
Dolphin.notify(data.msg, 'danger');
}
});
});
// 初始化节点拖拽
$('#menu_list').nestable({maxDepth:4}).on('change', function(){
$('#save').removeAttr("disabled").removeClass('btn-default disabled').addClass('btn-success');
});
// 隐藏禁用节点
$('#hide_disable').click(function(){
$('.dd-disable').hide();
});
// 显示禁用节点
$('#show_disable').click(function(){
$('.dd-disable').show();
});
// 展开所有节点
$('#expand-all').click(function(){
$('#menu_list').nestable('expandAll');
});
// 收起所有节点
$('#collapse-all').click(function(){
$('#menu_list').nestable('collapseAll');
});
// 禁用节点
$('.dd3-content').delegate('.disable', 'click', function(){
var self = $(this);
var ids = self.data('ids');
var ajax_url = '{:url("disable", ["table" => "admin_menu"])}';
Dolphin.loading();
$.post(ajax_url, {ids:ids}, function(data) {
Dolphin.loading('hide');
if (data.code) {
self.attr('data-original-title', '启用').removeClass('disable').addClass('enable')
.children().removeClass('fa-ban').addClass('fa-check-circle-o')
.closest('.dd-item')
.addClass('dd-disable');
} else {
Dolphin.notify(data.msg, 'danger');
}
});
return false;
});
// 启用节点
$('.dd3-content').delegate('.enable', 'click', function(){
var self = $(this);
var ids = self.data('ids');
var ajax_url = '{:url("enable", ["table" => "admin_menu"])}';
Dolphin.loading();
$.post(ajax_url, {ids:ids}, function(data) {
Dolphin.loading('hide');
if (data.code) {
self.attr('data-original-title', '禁用').removeClass('enable').addClass('disable')
.children().removeClass('fa-check-circle-o').addClass('fa-ban')
.closest('.dd-item')
.removeClass('dd-disable');
} else {
Dolphin.notify(data.msg, 'danger');
}
});
return false;
});
});
</script>
{/block}

View File

@ -0,0 +1,63 @@
{extend name="layout" /}
{block name="content"}
<div class="row">
<div class="col-md-12">
<div class="block">
<div class="block-header bg-gray-lighter">
<ul class="block-options">
<li>
<button type="button" data-toggle="block-option" data-action="fullscreen_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="refresh_toggle" data-action-mode="demo"><i class="si si-refresh"></i></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="content_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="close"><i class="si si-close"></i></button>
</li>
</ul>
<h3 class="block-title">{$page_title|default=""}</h3>
</div>
<div class="block-content tab-content">
<div class="tab-pane active">
<div class="block-content">
<form class="form-horizontal form-builder" action="{:url('export')}" method="get">
<input type="hidden" name="name" value="{$Request.param.name|default=''}">
<div class="form-group " id="form_group_group">
<h4 class="col-xs-12 push-10">是否导出数据</h4>
<div class="col-xs-9">
<label class="css-input css-radio css-radio-primary css-radio-sm push-10-r">
<input type="radio" name="export_data" id="clear1" value="0">
<span></span>
</label>
<label class="css-input css-radio css-radio-primary css-radio-sm push-10-r">
<input type="radio" name="export_data" id="clear2" value="1" checked="">
<span></span>
</label>
<div class="help-block">
选择“否”,只导出数据表结构。选择“是”,导出该模块的所有数据
</div>
</div>
</div>
<div class="form-group">
<div class="col-xs-12">
<button class="btn btn-minw btn-primary" type="submit" id="uninstall">
导出
</button>
<button class="btn btn-default" type="button" onclick="javascript:history.back(-1);return false;">
返回
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{/block}

View File

@ -0,0 +1,163 @@
{extend name="layout" /}
{block name="content"}
<div class="row">
<div class="col-md-12">
<div class="block">
{notempty name="tab_nav"}
<ul class="nav nav-tabs">
{volist name="tab_nav['tab_list']" id="tab"}
<li {eq name="tab_nav.curr_tab" value="$key"}class="active"{/eq}>
<a href="{$tab.url}">{$tab.title}</a>
</li>
{/volist}
<li class="pull-right">
<ul class="block-options push-10-t push-10-r">
<li>
<a href="{:url('index', ['type' => $type])}"><i class="si si-{$type=='list' ? 'list' : 'grid'}"></i></a>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="fullscreen_toggle"></button>
</li>
</ul>
</li>
</ul>
{else/}
<div class="block-header bg-gray-lighter">
<ul class="block-options">
<li>
<button type="button" data-toggle="block-option" data-action="fullscreen_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="refresh_toggle" data-action-mode="demo"><i class="si si-refresh"></i></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="content_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="close"><i class="si si-close"></i></button>
</li>
</ul>
<h3 class="block-title">{$page_title}</h3>
</div>
{/notempty}
<div class="block-content tab-content">
<div class="tab-pane active">
<div class="row push-20">
<div class="col-md-12">
<div class="pull-right search-bar">
<form class="input-group">
<span class="input-group-addon"><i class="fa fa-search"></i></span>
<input type="text" class="form-control" value="{:input('get.keyword')}" name="keyword" placeholder="请输入标识/名称/作者">
</form>
</div>
<div class="toolbar-btn-action">
<a title="全部" class="btn btn-default" href="{:url('index')}">全部({$total.all|default='0'})</a>
<a title="已启用" class="btn btn-success" href="{:url('index', ['status' => '1'])}">已启用({$total['1']|default='0'})</a>
<a title="已禁用" class="btn btn-warning" href="{:url('index', ['status' => '0'])}">已禁用({$total['0']|default='0'})</a>
<a title="未安装" class="btn btn-info" href="{:url('index', ['status' => '-1'])}">未安装({$total['-1']|default='0'})</a>
<a title="已损坏" class="btn btn-danger" href="{:url('index', ['status' => '-2'])}" id="hide_disable">已损坏({$total['-2']|default='0'})</a>
</div>
</div>
</div>
<div class="row module-list push-20">
{empty name="modules"}
<div class="table-empty">
<div class="text-center empty-info">
<i class="fa fa-database"></i> 暂无数据<br>
</div>
</div>
{/empty}
{eq name="type" value="block"}
{volist name="modules" id="module"}
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="block block-rounded">
<div class="block-content block-content-full text-center bg-{$module.bg_color|default='danger'} ribbon ribbon-bookmark ribbon-crystal">
<div class="ribbon-box font-w600">{$module.version|default='无版本号'}</div>
<div class="item item-2x item-circle bg-crystal-op push-20-t push-20 visibility-hidden" data-toggle="appear" data-offset="50" data-class="animated fadeIn">
<i class="{$module.icon|default='fa fa-puzzle-piece'} text-white-op"></i>
</div>
<h3 class="h4 text-white">{$module.title|default='无模块标题'}</h3>
<div class="text-white-op">
<em>{$module.name|default=''}</em>
</div>
</div>
<div class="block-content bg-gray-lighter">
<div class="{$module.status_class} pull-right push-10-l">{$module.status_info|raw}</div>
<div class="push-10">
<a class="h5" href="{$module.author_url|default=''}" target="_blank"><i class="fa fa-user"></i> {$module.author|default=''}</a>
</div>
<div class="push-10 mheight-100">
<div data-toggle="slimscroll" data-height="110px">
<p class="text-gray-dark">{$module.description|raw|default='暂无简介'}</p>
</div>
</div>
<div class="text-center push">
{$module.actions|raw}
</div>
</div>
</div>
</div>
{/volist}
{else/}
{notempty name="modules"}
<div class="col-md-12">
<div class="builder-table-wrapper" id="builder-table-wrapper">
<div class="builder-table" id="builder-table">
<table class="table table-builder table-hover table-bordered table-striped">
<thead>
<tr>
<th>名称</th>
<th>图标</th>
<th>版本</th>
<th>作者</th>
<th>简介</th>
<th class="column-right_button">操作</th>
</tr>
</thead>
<tbody>
{volist name="modules" id="module"}
<tr>
<td>{$module.title|default='无模块标题'}</td>
<td><i class="{$module.icon|default='fa fa-puzzle-piece'}"></i></td>
<td>{$module.version|default='无版本号'}</td>
<td><a href="{$module.author_url|default=''}" target="_blank">{$module.author|default=''}</a></td>
<td>{$module.description|raw|default='暂无简介'}</td>
<td>{$module.actions|raw}</td>
</tr>
{/volist}
</tbody>
</table>
</div>
</div>
</div>
{/notempty}
{/eq}
</div>
<div class="data-table-toolbar">
<div class="row">
<div class="col-sm-12">
{// 分页 }
{notempty name="pages"}
{$pages}
{/notempty}
{notempty name="row_list"}
<div class="pagination-info pull-right">
<form action="" method="get">
<input type="text" class="form-control input-sm go-page" name="page" value="{:input('param.page', '1')}">
<input type="text" class="form-control input-sm nums" name="list_rows" value="{php}echo input('param.list_rows', '') == '' ? config('list_rows') : input('param.list_rows');{/php}">
<input type="submit" class="hidden">
/ <strong>{$row_list->lastPage()}</strong> 页,共 <strong>{$row_list->total()}</strong> 条数据,每页显示数量
</form>
</div>
{/notempty}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/block}

View File

@ -0,0 +1,182 @@
{extend name="layout" /}
{block name="content"}
<div class="row">
<div class="col-md-12">
<div class="block">
<div class="block-header bg-gray-lighter">
<ul class="block-options">
<li>
<button type="button" data-toggle="block-option" data-action="fullscreen_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="refresh_toggle" data-action-mode="demo"><i class="si si-refresh"></i></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="content_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="close"><i class="si si-close"></i></button>
</li>
</ul>
<h3 class="block-title">{$page_title|default=""}</h3>
</div>
<div class="block-content tab-content">
<div class="tab-pane active">
<div class="block-content">
<form class="form-horizontal form-builder" action="{:url('install')}" method="get">
<input type="hidden" name="name" value="{$name|default=''}">
<input type="hidden" name="confirm" value="1">
<div class="form-group">
<h4 class="col-xs-12 push-10">模块依赖检查</h4>
<div class="col-sm-5">
{empty name="need_module"}
<div class="form-control-static">无需依赖其他模块</div>
{else/}
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>模块</th>
<th>唯一标识</th>
<th style="width: 100px;">当前版本</th>
<th style="width: 100px;">所需版本</th>
<th class="text-center" style="width: 100px;">检查结果</th>
</tr>
</thead>
<tbody>
{volist name="need_module" id="vo"}
<tr>
<td>{$vo.module}</td>
<td><a href="http://www.dolphinphp.com" target="_blank" data-toggle="tooltip" title="到商城查看该模块">{$vo.identifier}</a></td>
<td>{$vo.version}</td>
<td>{$vo.version_need}</td>
<td class="text-center">
{$vo.result|raw}
</td>
</tr>
{/volist}
</tbody>
</table>
{/empty}
</div>
</div>
<div class="form-group">
<h4 class="col-xs-12 push-10">插件依赖检查</h4>
<div class="col-sm-5">
{empty name="need_plugin"}
<div class="form-control-static">无需依赖其他插件</div>
{else/}
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>插件</th>
<th>唯一标识</th>
<th style="width: 100px;">当前版本</th>
<th style="width: 100px;">所需版本</th>
<th class="text-center" style="width: 100px;">检查结果</th>
</tr>
</thead>
<tbody>
{volist name="need_plugin" id="vo"}
<tr>
<td>{$vo.plugin}</td>
<td><a href="http://www.dolphinphp.com" target="_blank" data-toggle="tooltip" title="到商城查看该插件">{$vo.identifier}</a></td>
<td>{$vo.version}</td>
<td>{$vo.version_need}</td>
<td class="text-center">
{$vo.result|raw}
</td>
</tr>
{/volist}
</tbody>
</table>
{/empty}
</div>
</div>
<div class="form-group">
<h4 class="col-xs-12 push-10">数据表检查</h4>
<div class="col-sm-5">
{empty name="table_check"}
<div class="form-control-static">该模块不需要数据表</div>
{else/}
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>数据表</th>
<th class="text-center" style="width: 100px;">检查结果</th>
</tr>
</thead>
<tbody>
{volist name="table_check" id="vo"}
<tr>
<td>{$vo.table}</td>
<td class="text-center">
{$vo.result|raw}
</td>
</tr>
{/volist}
</tbody>
</table>
{/empty}
</div>
</div>
<div class="form-group " id="form_group_group">
<h4 class="col-xs-12 push-10">是否清除旧数据</h4>
<div class="col-xs-9">
<label class="css-input css-radio css-radio-primary css-radio-sm push-10-r">
<input type="radio" name="clear" id="clear1" value="0" checked="">
<span></span>
</label>
<label class="css-input css-radio css-radio-primary css-radio-sm push-10-r">
<input type="radio" name="clear" id="clear2" value="1">
<span></span>
</label>
<div class="help-block">
选择“是”,将删除数据库中已存在的相同数据表
</div>
</div>
</div>
<div class="form-group">
<div class="col-xs-12">
<button class="btn btn-minw btn-primary" type="button" id="install">
安装
</button>
<button class="btn btn-default" type="button" onclick="javascript:history.back(-1);return false;">
返回
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script>
$(document).ready(function(){
$('#install').click(function () {
var table_exists = false;
if ($('.form-builder i.text-danger').length) {
Dolphin.notify('缺少依赖模块或插件', 'danger');
return false;
}
if ($('.form-builder span.text-danger').length) {
table_exists = true;
}
if (table_exists && $('.form-builder input[name=clear]:checked').val() == 0) {
Dolphin.notify('数据库表存在冲突,如果需要覆盖原有数据,请选择“清除旧数据”', 'danger');
return false;
}
$('.form-builder').submit();
});
});
</script>
{/block}

View File

@ -0,0 +1,64 @@
{extend name="layout" /}
{block name="content"}
<div class="row">
<div class="col-md-12">
<div class="block">
<div class="block-header bg-gray-lighter">
<ul class="block-options">
<li>
<button type="button" data-toggle="block-option" data-action="fullscreen_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="refresh_toggle" data-action-mode="demo"><i class="si si-refresh"></i></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="content_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="close"><i class="si si-close"></i></button>
</li>
</ul>
<h3 class="block-title">{$page_title|default=""}</h3>
</div>
<div class="block-content tab-content">
<div class="tab-pane active">
<div class="block-content">
<form class="form-horizontal form-builder" action="{:url('uninstall')}" method="get">
<input type="hidden" name="name" value="{$name|default=''}">
<input type="hidden" name="confirm" value="1">
<div class="form-group " id="form_group_group">
<h4 class="col-xs-12 push-10">是否清除数据</h4>
<div class="col-xs-9">
<label class="css-input css-radio css-radio-primary css-radio-sm push-10-r">
<input type="radio" name="clear" id="clear1" value="0" checked="">
<span></span>
</label>
<label class="css-input css-radio css-radio-primary css-radio-sm push-10-r">
<input type="radio" name="clear" id="clear2" value="1">
<span></span>
</label>
<div class="help-block">
选择“是”,将删除数据库中的数据表
</div>
</div>
</div>
<div class="form-group">
<div class="col-xs-12">
<button class="btn btn-minw btn-danger" type="submit" id="uninstall">
卸载
</button>
<button class="btn btn-default" type="button" onclick="javascript:history.back(-1);return false;">
返回
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{/block}

View File

@ -0,0 +1,164 @@
{extend name="layout" /}
{block name="content"}
<div class="row">
<div class="col-md-12">
<div class="block">
{notempty name="tab_nav"}
<ul class="nav nav-tabs">
{volist name="tab_nav['tab_list']" id="tab"}
<li {eq name="tab_nav.curr_tab" value="$key"}class="active"{/eq}>
<a href="{$tab.url}">{$tab.title}</a>
</li>
{/volist}
<li class="pull-right">
<ul class="block-options push-10-t push-10-r">
<li>
<a href="{:url('index', ['type' => $type])}"><i class="si si-{$type=='list' ? 'list' : 'grid'}"></i></a>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="fullscreen_toggle"></button>
</li>
</ul>
</li>
</ul>
{else/}
<div class="block-header bg-gray-lighter">
<ul class="block-options">
<li>
<button type="button" data-toggle="block-option" data-action="fullscreen_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="refresh_toggle" data-action-mode="demo"><i class="si si-refresh"></i></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="content_toggle"></button>
</li>
<li>
<button type="button" data-toggle="block-option" data-action="close"><i class="si si-close"></i></button>
</li>
</ul>
<h3 class="block-title">{$page_title}</h3>
</div>
{/notempty}
<div class="block-content tab-content">
<div class="tab-pane active">
<div class="row push-20">
<div class="col-md-12">
<div class="pull-right search-bar">
<form class="input-group">
<span class="input-group-addon"><i class="fa fa-search"></i></span>
<input type="text" class="form-control" value="{:input('get.keyword')}" name="keyword" placeholder="请输入标识/名称/作者">
</form>
</div>
<div class="toolbar-btn-action">
<a title="全部" class="btn btn-default" href="{:url('index')}">全部({$total.all|default='0'})</a>
<a title="已启用" class="btn btn-success" href="{:url('index', ['status' => '1'])}">已启用({$total['1']|default='0'})</a>
<a title="已禁用" class="btn btn-warning" href="{:url('index', ['status' => '0'])}">已禁用({$total['0']|default='0'})</a>
<a title="未安装" class="btn btn-info" href="{:url('index', ['status' => '-1'])}">未安装({$total['-1']|default='0'})</a>
<a title="已损坏" class="btn btn-danger" href="{:url('index', ['status' => '-2'])}" id="hide_disable">已损坏({$total['-2']|default='0'})</a>
</div>
</div>
</div>
<div class="row module-list push-20">
{empty name="plugins"}
<div class="table-empty">
<div class="text-center empty-info">
<i class="fa fa-database"></i> 暂无数据<br>
</div>
</div>
{/empty}
{eq name="type" value="block"}
{volist name="plugins" id="plugin"}
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="block block-rounded">
<div class="block-content block-content-full text-center bg-{$plugin.bg_color|default='danger'} ribbon ribbon-bookmark ribbon-crystal">
<div class="ribbon-box font-w600">{$plugin.version|default='无版本号'}</div>
<div class="item item-2x item-circle bg-crystal-op push-20-t push-20 visibility-hidden" data-toggle="appear" data-offset="50" data-class="animated fadeIn">
<i class="{$plugin.icon|default='fa fa-puzzle-piece'} text-white-op"></i>
</div>
<h3 class="h4 text-white">{$plugin.title|default='无插件标题'}</h3>
<div class="text-white-op">
<em>{$plugin.name|default=''}</em>
</div>
</div>
<div class="block-content bg-gray-lighter">
<div class="{$plugin.status_class} pull-right push-10-l">{$plugin.status_info|raw}</div>
<div class="push-10">
<a class="h5" href="{$plugin.author_url|default=''}" target="_blank"><i class="fa fa-user"></i> {$plugin.author|default=''}</a>
</div>
<div class="push-10 mheight-100">
<div data-toggle="slimscroll" data-height="110px">
<p class="text-gray-dark">{$plugin.description|raw|default='暂无简介'}</p>
</div>
</div>
<div class="text-center push">
{$plugin.actions|raw}
</div>
</div>
</div>
</div>
{/volist}
{else/}
{notempty name="plugins"}
<div class="col-md-12">
<div class="builder-table-wrapper" id="builder-table-wrapper">
<div class="builder-table" id="builder-table">
<table class="table table-builder table-hover table-bordered table-striped">
<thead>
<tr>
<th>名称</th>
<th>图标</th>
<th>版本</th>
<th>作者</th>
<th>简介</th>
<th class="column-right_button" style="min-width:180px">操作</th>
</tr>
</thead>
<tbody>
{volist name="plugins" id="plugin"}
<tr>
<td>{$plugin.title|default='无插件标题'}</td>
<td><i class="{$plugin.icon|default='fa fa-puzzle-piece'}"></i></td>
<td>{$plugin.version|default='无版本号'}</td>
<td><a href="{$plugin.author_url|default=''}" target="_blank">{$plugin.author|default=''}</a></td>
<td>{$plugin.description|raw|default='暂无简介'}</td>
<td>{$plugin.actions|raw}</td>
</tr>
{/volist}
</tbody>
</table>
</div>
</div>
</div>
{/notempty}
{/eq}
</div>
<div class="data-table-toolbar">
<div class="row">
<div class="col-sm-12">
{// 分页 }
{notempty name="pages"}
{$pages}
{/notempty}
{notempty name="row_list"}
<div class="pagination-info pull-right">
<form action="" method="get">
<input type="text" class="form-control input-sm go-page" name="page" value="{:input('param.page', '1')}">
<input type="text" class="form-control input-sm nums" name="list_rows" value="{php}echo input('param.list_rows', '') == '' ? config('list_rows') : input('param.list_rows');{/php}">
<input type="submit" class="hidden">
/ <strong>{$row_list->lastPage()}</strong> 页,共 <strong>{$row_list->total()}</strong> 条数据,每页显示数量
</form>
</div>
{/notempty}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/block}

View File

@ -0,0 +1,298 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Advert as AdvertModel;
use app\cms\model\AdvertType as AdvertTypeModel;
use think\facade\Validate;
/**
* 广告控制器
* @package app\cms\admin
*/
class Advert extends Admin
{
/**
* 广告列表
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
* @throws \think\exception\DbException
*/
public function index()
{
// 查询
$map = $this->getMap();
// 排序
$order = $this->getOrder('update_time desc');
// 数据列表
$data_list = AdvertModel::where($map)->order($order)->paginate();
$btnType = [
'class' => 'btn btn-info',
'title' => '广告分类',
'icon' => 'fa fa-fw fa-sitemap',
'href' => url('advert_type/index')
];
$list_type = AdvertTypeModel::where('status', 1)->column('id,name');
array_unshift($list_type, '默认分类');
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['title' => '标题']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['name', '广告名称', 'text.edit'],
['typeid', '分类', 'select', $list_type],
['ad_type', '类型', 'text', '', ['代码', '文字', '图片', 'flash']],
['timeset', '时间限制', 'text', '', ['永不过期', '限时']],
['create_time', '创建时间', 'datetime'],
['update_time', '更新时间', 'datetime'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->addTopButtons('add,enable,disable,delete') // 批量添加顶部按钮
->addTopButton('custom', $btnType) // 添加顶部按钮
->addRightButtons(['edit', 'delete' => ['data-tips' => '删除后无法恢复。']]) // 批量添加右侧按钮
->addOrder('id,name,typeid,timeset,ad_type,create_time,update_time')
->setRowList($data_list) // 设置表格数据
->addValidate('Advert', 'name')
->fetch(); // 渲染模板
}
/**
* 新增
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function add()
{
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Advert');
if (true !== $result) $this->error($result);
if ($data['ad_type'] != 0) {
$data['link'] == '' && $this->error('链接不能为空');
Validate::is($data['link'], 'url') === false && $this->error('链接不是有效的url地址'); // true
}
// 广告类型
switch ($data['ad_type']) {
case 0: // 代码
$data['content'] = $data['code'];
break;
case 1: // 文字
$data['content'] = '<a href="'.$data['link'].'" target="_blank" style="';
if ($data['size'] != '') {
$data['content'] .= 'font-size:'.$data['size'].'px;';
}
if ($data['color'] != '') {
$data['content'] .= 'color:'.$data['color'];
}
$data['content'] .= '">'.$data['title'].'</a>';
break;
case 2: // 图片
$data['content'] = '<a href="'.$data['link'].'" target="_blank"><img src="'.get_file_path($data['src']).'" style="';
if ($data['width'] != '') {
$data['content'] .= 'width:'.$data['width'].'px;';
}
if ($data['height'] != '') {
$data['content'] .= 'height:'.$data['height'].'px;';
}
if ($data['alt'] != '') {
$data['content'] .= '" alt="'.$data['alt'];
}
$data['content'] .= '" /></a>';
break;
case 3: // flash
$data['content'] = '';
$data['content'] = '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"';
if ($data['width'] != '') {
$data['content'] .= ' width="'.$data['width'].'"';
}
if ($data['height'] != '') {
$data['content'] .= ' height="'.$data['height'].'"';
}
$data['content'] .= '><param name="quality" value="high" /><param name="movie" value="'.$data['link'].'" /><embed allowfullscreen="true"';
if ($data['height'] != '') {
$data['content'] .= ' height="'.$data['height'].'"';
}
$data['content'] .= ' pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="'.$data['link'].'" type="application/x-shockwave-flash"';
if ($data['width'] != '') {
$data['content'] .= ' width="'.$data['width'].'"';
}
$data['content'] .= '></embed></object>';
break;
}
if ($advert = AdvertModel::create($data)) {
// 记录行为
action_log('advert_add', 'cms_advert', $advert['id'], UID, $data['name']);
$this->success('新增成功', 'index');
} else {
$this->error('新增失败');
}
}
$list_type = AdvertTypeModel::where('status', 1)->column('id,name');
array_unshift($list_type, '默认分类');
// 显示添加页面
return ZBuilder::make('form')
->setPageTips('如果出现无法添加的情况,可能由于浏览器将本页面当成了广告,请尝试关闭浏览器的广告过滤功能再试。', 'warning')
->addFormItems([
['select', 'typeid', '广告分类', '', $list_type, 0],
['text', 'tagname', '广告位标识', '由小写字母、数字或下划线组成,不能以数字开头'],
['text', 'name', '广告位名称'],
['radio', 'timeset', '时间限制', '', ['永不过期', '在设内时间内有效'], 0],
['daterange', 'start_time,end_time', '开始时间-结束时间'],
['radio', 'ad_type', '广告类型', '', ['代码', '文字', '图片', 'flash'], 0],
['textarea', 'code', '代码', '<code>必填</code>支持html代码'],
['image', 'src', '图片', '<code>必须</code>'],
['text', 'title', '文字内容', '<code>必填</code>'],
['text', 'link', '链接', '<code>必填</code>'],
['colorpicker', 'color', '文字颜色', '', '', 'rgb'],
['text', 'size', '文字大小', '只需填写数字,例如:12表示12px', '', ['', 'px']],
['text', 'width', '宽度', '不用填写单位,只需填写具体数字'],
['text', 'height', '高度', '不用填写单位,只需填写具体数字'],
['text', 'alt', '图片描述', '即图片alt的值'],
['radio', 'status', '立即启用', '', ['否', '是'], 1]
])
->setTrigger('ad_type', '0', 'code')
->setTrigger('ad_type', '1', 'title,color,size')
->setTrigger('ad_type', '2', 'src,alt')
->setTrigger('ad_type', '2,3', 'width,height')
->setTrigger('ad_type', '1,2,3', 'link')
->setTrigger('timeset', '1', 'start_time')
->fetch();
}
/**
* 编辑
* @param null $id 广告id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function edit($id = null)
{
if ($id === null) $this->error('缺少参数');
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Advert');
if (true !== $result) $this->error($result);
if (AdvertModel::update($data)) {
// 记录行为
action_log('advert_edit', 'cms_advert', $id, UID, $data['name']);
$this->success('编辑成功', 'index');
} else {
$this->error('编辑失败');
}
}
$list_type = AdvertTypeModel::where('status', 1)->column('id,name');
array_unshift($list_type, '默认分类');
$info = AdvertModel::get($id);
$info['ad_type'] = ['代码', '文字', '图片', 'flash'][$info['ad_type']];
// 显示编辑页面
return ZBuilder::make('form')
->setPageTips('如果出现无法添加的情况,可能由于浏览器将本页面当成了广告,请尝试关闭浏览器的广告过滤功能再试。', 'warning')
->addFormItems([
['hidden', 'id'],
['hidden', 'tagname'],
['static', 'tagname', '广告位标识'],
['static', 'ad_type', '广告类型'],
['text', 'name', '广告位名称'],
['select', 'typeid', '广告分类', '', $list_type],
['radio', 'timeset', '时间限制', '', ['永不过期', '在设内时间内有效']],
['daterange', 'start_time,end_time', '开始时间-结束时间'],
['textarea', 'content', '广告内容'],
['radio', 'status', '立即启用', '', ['否', '是']]
])
->setTrigger('timeset', '1', 'start_time')
->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');
$advert_name = AdvertModel::where('id', 'in', $ids)->column('name');
return parent::setStatus($type, ['advert_'.$type, 'cms_advert', 0, UID, implode('、', $advert_name)]);
}
/**
* 快速编辑
* @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', '');
$advert = AdvertModel::where('id', $id)->value($field);
$details = '字段(' . $field . '),原值(' . $advert . '),新值:(' . $value . ')';
return parent::quickEdit(['advert_edit', 'cms_advert', $id, UID, $details]);
}
}

View File

@ -0,0 +1,196 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\AdvertType as AdvertTypeModel;
/**
* 广告分类控制器
* @package app\cms\admin
*/
class AdvertType extends Admin
{
/**
* 广告列表
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
* @throws \think\exception\DbException
*/
public function index()
{
// 查询
$map = $this->getMap();
// 排序
$order = $this->getOrder('update_time desc');
// 数据列表
$data_list = AdvertTypeModel::where($map)->order($order)->paginate();
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['name' => '分类名称']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['name', '分类名称', 'text.edit'],
['create_time', '创建时间', 'datetime'],
['update_time', '更新时间', 'datetime'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->setTableName('cms_advert_type')
->addTopButton('back', ['href' => url('advert/index')]) // 批量添加顶部按钮
->addTopButtons('add,enable,disable,delete') // 批量添加顶部按钮
->addRightButtons(['edit', 'delete' => ['data-tips' => '删除后无法恢复。']]) // 批量添加右侧按钮
->addOrder('id,name,create_time,update_time')
->setRowList($data_list) // 设置表格数据
->addValidate('AdvertType', 'name')
->fetch(); // 渲染模板
}
/**
* 新增
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function add()
{
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'AdvertType');
if(true !== $result) $this->error($result);
if ($type = AdvertTypeModel::create($data)) {
// 记录行为
action_log('advert_type_add', 'cms_advert_type', $type['id'], UID, $data['name']);
$this->success('新增成功', 'index');
} else {
$this->error('新增失败');
}
}
// 显示添加页面
return ZBuilder::make('form')
->setPageTips('如果出现无法添加的情况,可能由于浏览器将本页面当成了广告,请尝试关闭浏览器的广告过滤功能再试。', 'warning')
->addFormItems([
['text', 'name', '分类名称'],
['radio', 'status', '立即启用', '', ['否', '是'], 1]
])
->fetch();
}
/**
* 编辑
* @param null $id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function edit($id = null)
{
if ($id === null) $this->error('缺少参数');
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'AdvertType');
if(true !== $result) $this->error($result);
if (AdvertTypeModel::update($data)) {
// 记录行为
action_log('advert_type_edit', 'cms_advert_type', $id, UID, $data['name']);
$this->success('编辑成功', 'index');
} else {
$this->error('编辑失败');
}
}
$info = AdvertTypeModel::get($id);
// 显示编辑页面
return ZBuilder::make('form')
->setPageTips('如果出现无法编辑的情况,可能由于浏览器将本页面当成了广告,请尝试关闭浏览器的广告过滤功能再试。', 'warning')
->addFormItems([
['hidden', 'id'],
['text', 'name', '分类名称'],
['radio', 'status', '立即启用', '', ['否', '是']]
])
->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');
$type_name = AdvertTypeModel::where('id', 'in', $ids)->column('name');
return parent::setStatus($type, ['advert_type_'.$type, 'cms_advert_type', 0, UID, implode('、', $type_name)]);
}
/**
* 快速编辑
* @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', '');
$type = AdvertTypeModel::where('id', $id)->value($field);
$details = '字段(' . $field . '),原值(' . $type . '),新值:(' . $value . ')';
return parent::quickEdit(['advert_type_edit', 'cms_advert_type', $id, UID, $details]);
}
}

View File

@ -0,0 +1,279 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Model as DocumentModel;
use app\cms\model\Column as ColumnModel;
use app\cms\model\Document;
use app\user\model\Role as RoleModel;
use util\Tree;
use util\File;
use think\facade\Env;
/**
* 栏目控制器
* @package app\cms\admin
*/
class Column extends Admin
{
/**
* 栏目列表
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function index()
{
// 查询
$map = $this->getMap();
// 数据列表
$data_list = ColumnModel::where($map)->column(true);
if (empty($map)) {
$data_list = Tree::config(['title' => 'name'])->toList($data_list);
}
// 自定义按钮
$btnMove = [
'class' => 'btn btn-xs btn-default js-move-column',
'icon' => 'fa fa-fw fa-arrow-circle-right',
'title' => '移动栏目'
];
$btnAdd = [
'class' => 'btn btn-xs btn-default',
'icon' => 'fa fa-fw fa-plus',
'title' => '新增子栏目',
'href' => url('add', ['pid' => '__id__'])
];
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['name' => '栏目名称']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['icon', '图标', 'icon'],
['name', '栏目名称', 'callback', function($value, $data){
return isset($data['title_prefix']) ? $data['title_display'] : $value;
}, '__data__'],
['model', '内容模型', 'select', DocumentModel::getTitleList()],
['rank_auth', '浏览权限', 'select', RoleModel::getTree(null, '开放浏览')],
['hide', '是否隐藏', 'yesno'],
['post_auth', '支持投稿', 'yesno'],
['create_time', '创建时间', 'datetime'],
['sort', '排序', 'text.edit'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->addTopButtons('add,enable,disable') // 批量添加顶部按钮
->addRightButton('custom', $btnAdd)
->addRightButton('edit') // 添加右侧按钮
// ->addRightButton('custom', $btnMove)
->addRightButton('delete', ['data-tips' => '删除栏目前,请确保无子栏目和文档!']) // 添加右侧按钮
->setRowList($data_list) // 设置表格数据
->fetch(); // 渲染模板
}
/**
* 新增栏目
* @param int $pid 父级id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function add($pid = 0)
{
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Column');
if(true !== $result) $this->error($result);
if ($column = ColumnModel::create($data)) {
cache('cms_column_list', null);
// 记录行为
action_log('column_add', 'cms_column', $column['id'], UID, $data['name']);
$this->success('新增成功', 'index');
} else {
$this->error('新增失败');
}
}
$template_list = File::get_dirs(Env::get('app_path').'cms/view/column/')['file'];
$template_detail = File::get_dirs(Env::get('app_path').'cms/view/document/')['file'];
// 显示添加页面
return ZBuilder::make('form')
->addFormItems([
['select', 'pid', '所属栏目', '<span class="text-danger">必选</span>', ColumnModel::getTreeList(), $pid],
['text', 'name', '栏目名称', '<span class="text-danger">必填</span>'],
['radio', 'model', '内容模型', '<span class="text-danger">必选</span>', DocumentModel::getTitleList()],
['radio', 'type', '栏目属性', '', ['最终列表栏目', '外部链接'], 0],
['text', 'url', '链接', '可以填写完整的url<code>http://www.dolphinphp.com</code>,也可以填写 <code>模块/控制器/操作</code>,如:<code>cms/index/index</code>'],
['radio', 'target', '打开方式', '', ['_self' => '当前窗口', '_blank' => '新窗口'], '_self'],
// ['select', 'index_template', '封面页模板', '可选'],
['select', 'list_template', '列表页模板', '可选,模板目录: <code>cms/view/column</code>', parse_array($template_list)],
['select', 'detail_template', '详情页模板', '可选,模板目录: <code>cms/view/document</code>', parse_array($template_detail)],
['ckeditor', 'content', '栏目内容', '可作为单页使用'],
['icon', 'icon', '图标'],
['radio', 'post_auth', '是否支持投稿', '是否允许前台用户投稿', ['禁止投稿', '允许投稿'], 1],
['radio', 'hide', '是否隐藏栏目', '隐藏后前台不可见', ['显示', '隐藏'], 0],
['select', 'rank_auth', '浏览权限', '', RoleModel::getTree(null, '开放浏览'), 0],
['radio', 'status', '立即启用', '', ['否', '是'], 1],
['text', 'sort', '排序', '', 100],
])
->setTrigger('type', '0,2', 'index_template,list_template,detail_template')
->setTrigger('type', '1', 'url,target')
->fetch();
}
/**
* 编辑栏目
* @param string $id 栏目id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function edit($id = '')
{
if ($id === 0) $this->error('参数错误');
// 保存数据
if ($this->request->isPost()) {
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Column');
// 验证失败 输出错误信息
if(true !== $result) $this->error($result);
if (ColumnModel::update($data)) {
// 记录行为
action_log('column_edit', 'cms_column', $id, UID, $data['name']);
$this->success('编辑成功', 'index');
} else {
$this->error('编辑失败');
}
}
// 获取数据
$info = ColumnModel::get($id);
$template_list = File::get_dirs(Env::get('app_path').'cms/view/column/')['file'];
$template_detail = File::get_dirs(Env::get('app_path').'cms/view/document/')['file'];
// 显示编辑页面
return ZBuilder::make('form')
->addFormItems([
['hidden', 'id'],
['select', 'pid', '所属栏目', '<span class="text-danger">必选</span>', ColumnModel::getTreeList($id)],
['text', 'name', '栏目名称', '<span class="text-danger">必填</span>'],
['radio', 'model', '内容模型', '<span class="text-danger">必选</span>', DocumentModel::getTitleList()],
['radio', 'type', '栏目属性', '', ['最终列表栏目', '外部链接'], 0],
['text', 'url', '链接', '可以填写完整的url<code>http://www.dolphinphp.com</code>,也可以填写 <code>模块/控制器/操作</code>,如:<code>cms/index/index</code>'],
['radio', 'target', '打开方式', '', ['_self' => '当前窗口', '_blank' => '新窗口'], '_self'],
// ['select', 'index_template', '封面页模板', '可选'],
['select', 'list_template', '列表页模板', '可选,模板目录: <code>cms/view/column</code>', parse_array($template_list)],
['select', 'detail_template', '详情页模板', '可选,模板目录: <code>cms/view/document</code>', parse_array($template_detail)],
['ckeditor', 'content', '栏目内容', '可作为单页使用'],
['icon', 'icon', '图标'],
['radio', 'post_auth', '是否支持投稿', '是否允许前台用户投稿', ['禁止投稿', '允许投稿']],
['radio', 'hide', '是否隐藏栏目', '隐藏后前台不可见', ['显示', '隐藏'], 0],
['select', 'rank_auth', '浏览权限', '', RoleModel::getTree(null, '开放浏览')],
['radio', 'status', '立即启用', '', ['否', '是']],
['text', 'sort', '排序'],
])
->setTrigger('type', '0,2', 'index_template,list_template,detail_template')
->setTrigger('type', '1', 'url,target')
->setFormData($info)
->fetch();
}
/**
* 删除栏目
* @param null $ids 栏目id
* @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($ids = null)
{
if ($ids === null) $this->error('参数错误');
// 检查是否有子栏目
if (ColumnModel::where('pid', $ids)->find()) {
$this->error('请先删除或移动该栏目下的子栏目');
}
// 检查是否有文档
if (Document::where('cid', $ids)->find()) {
$this->error('请先删除或移动该栏目下的所有文档');
}
// 删除并记录日志
$column_name = get_column_name($ids);
return parent::delete(['column_delete', 'cms_column', 0, UID, $column_name]);
}
/**
* 启用栏目
* @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 类型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');
$column_delete = is_array($ids) ? '' : $ids;
$column_names = ColumnModel::where('id', 'in', $ids)->column('name');
return parent::setStatus($type, ['column_'.$type, 'cms_column', $column_delete, UID, implode('、', $column_names)]);
}
/**
* 快速编辑
* @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', '');
$column = ColumnModel::where('id', $id)->value($field);
$details = '字段(' . $field . '),原值(' . $column . '),新值:(' . $value . ')';
return parent::quickEdit(['column_edit', 'cms_column', $id, UID, $details]);
}
}

View File

@ -0,0 +1,129 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Document;
use think\Db;
/**
* 内容控制器
* @package app\cms\admin
*/
class Content extends Admin
{
/**
* 空操作,用于显示各个模型的文档列表
* @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 _empty()
{
cookie('__forward__', $_SERVER['REQUEST_URI']);
$model_name = $this->request->action();
$model = Db::name('cms_model')->where('name', $model_name)->find();
if (!$model) $this->error('找不到该内容');
// 独立模型
if ($model['type'] == 2) {
$table_name = substr($model['table'], strlen(config('database.prefix')));
// 查询
$map = $this->getMap();
$map[] = ['trash', '=', 0];
// 排序
$order = $this->getOrder('update_time desc');
// 数据列表
$data_list = Db::view($table_name, true)
->view("cms_column", ['name' => 'column_name'], 'cms_column.id='.$table_name.'.cid', 'left')
->view("admin_user", 'username', 'admin_user.id='.$table_name.'.uid', 'left')
->where($map)
->order($order)
->paginate();
$trash_count = Db::table($model['table'])->where('trash', 1)->count();
// 自定义按钮
$btnRecycle = [
'title' => '回收站('.$trash_count.')',
'icon' => 'fa fa-trash',
'class' => 'btn btn-info',
'href' => url('recycle/index', ['model' => $model['id']])
];
$columns = Db::name('cms_column')->where(['model' => $model['id']])->column('id,name');
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['title' => '标题', 'cms_column.name' => '栏目名称']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['title', '标题'],
['cid', '栏目名称', 'select', $columns],
['view', '点击量'],
['username', '发布人'],
['update_time', '更新时间', 'datetime'],
['sort', '排序', 'text.edit'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->setTableName($table_name)
->addTopButton('add', ['href' => url('document/add', ['model' => $model['id']])]) // 添加顶部按钮
->addTopButton('enable', ['href' => url('document/enable', ['table' => $table_name])]) // 添加顶部按钮
->addTopButton('disable', ['href' => url('document/disable', ['table' => $table_name])]) // 添加顶部按钮
->addTopButton('delete', ['href' => url('document/delete', ['table' => $table_name])]) // 添加顶部按钮
->addTopButton('custom', $btnRecycle) // 添加顶部按钮
->addRightButton('edit', ['href' => url('document/edit', ['model' => $model['id'], 'id' => '__id__'])]) // 添加右侧按钮
->addRightButton('delete', ['href' => url('document/delete', ['ids' => '__id__', 'table' => $table_name])]) // 添加右侧按钮
->addOrder('id,title,cid,view,username,update_time')
->addFilter('cid', $columns)
->addFilter(['username' => 'admin_user'])
->addFilterMap(['cid' => ['model' => $model['id']]])
->setRowList($data_list) // 设置表格数据
->fetch(); // 渲染模板
} else {
// 查询
$map = $this->getMap();
$map[] = ['cms_document.trash', '=', 0];
$map[] = ['cms_document.model', '=', $model['id']];
// 排序
$order = $this->getOrder('update_time desc');
// 数据列表
$data_list = Document::getList($map, $order);
$columns = Db::name('cms_column')->where(['model' => $model['id']])->column('id,name');
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['title' => '标题', 'cms_column.name' => '栏目名称']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['title', '标题'],
['cid', '栏目名称', 'select', $columns],
['view', '点击量'],
['username', '发布人'],
['update_time', '更新时间', 'datetime'],
['sort', '排序', 'text.edit'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->setTableName('cms_document')
->addTopButton('add', ['href' => url('document/add', ['model' => $model['id']])]) // 添加顶部按钮
->addTopButton('enable', ['href' => url('document/enable', ['table' => 'cms_document'])]) // 添加顶部按钮
->addTopButton('disable', ['href' => url('document/disable', ['table' => 'cms_document'])]) // 添加顶部按钮
->addTopButton('delete', ['href' => url('document/delete', ['table' => 'cms_document'])]) // 添加顶部按钮
->addRightButton('edit', ['href' => url('document/edit', ['id' => '__id__'])]) // 添加右侧按钮
->addRightButton('delete', ['href' => url('document/delete', ['ids' => '__id__', 'table' => 'cms_document'])]) // 添加右侧按钮
->addOrder('id,title,cid,view,username,update_time')
->addFilter('cid', $columns)
->addFilter(['username' => 'admin_user'])
->addFilterMap(['cid' => ['model' => $model['id']]])
->setRowList($data_list) // 设置表格数据
->fetch(); // 渲染模板
}
}
}

View File

@ -0,0 +1,337 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Column as ColumnModel;
use app\cms\model\Document as DocumentModel;
use app\cms\model\Field as FieldModel;
use think\Db;
use util\Tree;
/**
* 文档控制器
* @package app\cms\admin
*/
class Document extends Admin
{
/**
* 文档列表
* @author 蔡伟明 <314013107@qq.com>
*/
public function index()
{
cookie('__forward__', $_SERVER['REQUEST_URI']);
// 查询
$map = $this->getMap();
$map[] = ['cms_document.trash', '=', 0];
// 排序
$order = $this->getOrder('update_time desc');
// 数据列表
$data_list = DocumentModel::getList($map, $order);
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['title' => '标题', 'cms_column.name' => '栏目名称']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['title', '标题'],
['column_name', '栏目名称'],
['view', '点击量'],
['username', '发布人'],
['update_time', '更新时间', 'datetime'],
['sort', '排序', 'text.edit'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->addTopButtons('add,enable,disable,delete') // 批量添加顶部按钮
->addRightButtons(['edit', 'delete']) // 批量添加右侧按钮
->addOrder(['column_name' => 'cms_document.cid'])
->addOrder('id,title,view,username,update_time')
->addFilter(['column_name' => 'cms_column.name', 'username' => 'admin_user'])
->setRowList($data_list) // 设置表格数据
->fetch(); // 渲染模板
}
/**
* 添加文档
* @param int $cid 栏目id
* @param string $model 模型id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function add($cid = 0, $model = '')
{
// 保存文档数据
if ($this->request->isAjax()) {
$DocumentModel = new DocumentModel();
if (false === $DocumentModel->saveData()) {
$this->error($DocumentModel->getError());
}
$this->success('新增成功', cookie('__forward__'));
}
// 第二步,填写文档信息
if ($cid > 0) {
cookie('__forward__', url('add', ['cid' => $cid]));
// 获取栏目数据
$column = ColumnModel::getInfo($cid);
// 独立模型只取该模型的字段,不包含系统字段
$where = [];
if (get_model_type($column['model']) == 2) {
$where[] = ['model', '=', $column['model']];
} else {
$where[] = ['model', 'in', [0, $column['model']]];
}
// 获取文档模型字段
$where[] = ['status', '=', 1];
$where[] = ['show', '=', 1];
$fields = FieldModel::where($where)->order('sort asc,id asc')->column(true);
foreach ($fields as &$value) {
// 解析options
if ($value['options'] != '') {
$value['options'] = parse_attr($value['options']);
}
switch ($value['type']) {
case 'linkage':// 解析联动下拉框异步请求地址
if (!empty($value['ajax_url']) && substr($value['ajax_url'], 0, 4) != 'http') {
$value['ajax_url'] = url($value['ajax_url']);
}
break;
case 'date':
case 'time':
case 'datetime':
$value['value'] = '';
break;
case 'bmap':
$value['level'] = $value['level'] == 0 ? 12 : $value['level'];
break;
case 'colorpicker':
$value['mode'] = 'rgba';
break;
}
}
// 添加额外表单项信息
$extra_field = [
['name' => 'cid', 'title' => '所属栏目', 'type' => 'static', 'value' => $column['name']],
['name' => 'cid', 'type' => 'hidden', 'value' => $cid],
['name' => 'model', 'type' => 'hidden', 'value' => $column['model']]
];
$fields = array_merge($extra_field, $fields);
// 使用ZBuilder快速创建表单
return ZBuilder::make('form')
->setFormItems($fields)
->hideBtn('back')
->fetch();
}
// 第一步,选择栏目
if ($model == '') {
$columns = ColumnModel::getTreeList(0, false);
} else {
// 获取相同内容模型的栏目
$columns = Db::name('cms_column')->where('model', $model)->order('pid,id')->column('id,name,pid');
$columns = Tree::config(['title' => 'name'])->toList($columns, current($columns)['pid']);
$result = [];
foreach ($columns as $column) {
$result[$column['id']] = $column['title_display'];
}
$columns = $result;
}
return ZBuilder::make('form')
->addFormItem('select', 'cid', '选择栏目', '请选择栏目', $columns)
->setBtnTitle('submit', '下一步')
->hideBtn('back')
->isAjax(false)
->fetch();
}
/**
* 编辑文档
* @param null $id 文档id
* @param string $model 模型id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function edit($id = null, $model = '')
{
if ($id === null) $this->error('参数错误');
// 保存文档数据
if ($this->request->isPost()) {
$DocumentModel = new DocumentModel();
$result = $DocumentModel->saveData();
if (false === $result) {
$this->error($DocumentModel->getError());
}
$this->success('编辑成功', cookie('__forward__'));
}
// 获取数据
$info = DocumentModel::getOne($id, $model);
// 独立模型只取该模型的字段,不包含系统字段
$where = [];
if ($model != '') {
$where[] = ['model', '=', $model];
} else {
$where[] = ['model', 'in', [0, $info['model']]];
}
// 用于查询内容模型栏目
$map = $where;
// 获取文档模型字段
$where[] = ['status', '=', 1];
$where[] = ['show', '=', 1];
$fields = FieldModel::where($where)->order('sort asc,id asc')->column(true);
foreach ($fields as $id => &$value) {
// 解析options
if ($value['options'] != '') {
$value['options'] = parse_attr($value['options']);
}
// 日期时间
switch ($value['type']) {
case 'date':
$info[$value['name']] = format_time($info[$value['name']], 'Y-m-d');
break;
case 'time':
$info[$value['name']] = format_time($info[$value['name']], 'H:i:s');
break;
case 'datetime':
$info[$value['name']] = empty($info[$value['name']]) ? '' : format_time($info[$value['name']]);
break;
case 'bmap':
$value['level'] = $value['level'] == 0 ? 12 : $value['level'];
break;
case 'colorpicker':
$value['mode'] = 'rgba';
break;
}
}
// 获取相同内容模型的栏目
$columns = Db::name('cms_column')->where($map)->whereOr('model', $info['model'])->order('pid,id')->column('id,name,pid');
$columns = Tree::config(['title' => 'name'])->toList($columns, current($columns)['pid']);
$result = [];
foreach ($columns as $column) {
$result[$column['id']] = $column['title_display'];
}
$columns = $result;
// 添加额外表单项信息
$extra_field = [
['name' => 'id', 'type' => 'hidden'],
['name' => 'cid', 'title' => '所属栏目', 'type' => 'select', 'options' => $columns],
['name' => 'model', 'type' => 'hidden']
];
$fields = array_merge($extra_field, $fields);
// 使用ZBuilder快速创建表单
return ZBuilder::make('form')
->setFormItems($fields)
->setFormData($info)
->fetch();
}
/**
* 删除文档(不是彻底删除,而是移动到回收站)
* @param null $ids 文档id
* @param string $table 数据表
* @author 蔡伟明 <314013107@qq.com>
*/
public function delete($ids = null, $table = '')
{
if ($ids === null) $this->error('参数错误');
$document_id = is_array($ids) ? '' : $ids;
$document_title = Db::name($table)->where('id', 'in', $ids)->column('title');
// 移动文档到回收站
if (false === Db::name($table)->where('id', 'in', $ids)->setField('trash', 1)) {
$this->error('删除失败');
}
// 删除并记录日志
action_log('document_trash', $table, $document_id, UID, implode('、', $document_title));
$this->success('删除成功');
}
/**
* 启用文档
* @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 类型enable/disable
* @param array $record
* @author 蔡伟明 <314013107@qq.com>
* @throws \think\Exception
* @throws \think\exception\PDOException
*/
public function setStatus($type = '', $record = [])
{
$table_token = input('param._t', '');
$table_token == '' && $this->error('缺少参数');
!session('?'.$table_token) && $this->error('参数错误');
$table_data = session($table_token);
$table_name = $table_data['table'];
$ids = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
$document_id = is_array($ids) ? '' : $ids;
$document_title = Db::name($table_name)->where('id', 'in', $ids)->column('title');
return parent::setStatus($type, ['document_'.$type, 'cms_document', $document_id, UID, implode('、', $document_title)]);
}
/**
* 快速编辑
* @param array $record 行为日志
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function quickEdit($record = [])
{
$table_token = input('param._t', '');
$table_token == '' && $this->error('缺少参数');
!session('?'.$table_token) && $this->error('参数错误');
$table_data = session($table_token);
$table = $table_data['table'];
$id = input('post.pk', '');
$field = input('post.name', '');
$value = input('post.value', '');
$document = Db::name($table)->where('id', $id)->value($field);
$details = '表名(' . $table . '),字段(' . $field . '),原值(' . $document . '),新值:(' . $value . ')';
return parent::quickEdit(['document_edit', 'cms_document', $id, UID, $details]);
}
}

View File

@ -0,0 +1,330 @@
<?php
// +----------------------------------------------------------------------
// | 海豚PHP框架 [ DolphinPHP ]
// +----------------------------------------------------------------------
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://dolphinphp.com
// +----------------------------------------------------------------------
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Field as FieldModel;
use think\Db;
/**
* 字段管理控制器
* @package app\cms\admin
*/
class Field extends Admin
{
/**
* 字段列表
* @param null $id 文档模型id
* @author 蔡伟明 <314013107@qq.com>
*/
public function index($id = null)
{
$id === null && $this->error('参数错误');
cookie('__forward__', $_SERVER['REQUEST_URI']);
// 查询
$map = $this->getMap();
$map['model'] = $id;
// 数据列表
$data_list = FieldModel::where($map)->order('id desc')->paginate();
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['name' => '名称', 'title' => '标题']) // 设置搜索框
->setPageTips('【显示】表示新增或编辑文档时是否显示该字段<br>【启用】表示前台是否显示')
->addColumns([ // 批量添加数据列
['id', 'ID'],
['name', '名称'],
['title', '标题'],
['type', '类型', 'text', '', config('form_item_type')],
['create_time', '创建时间', 'datetime'],
['sort', '排序', 'text.edit'],
['show', '显示', 'switch'],
['status', '启用', 'switch'],
['right_button', '操作', 'btn']
])
->addTopButton('back', ['href' => url('model/index')]) // 批量添加顶部按钮
->addTopButton('add', ['href' => url('add', ['model' => $id])]) // 添加顶部按钮
->addTopButtons('enable,disable') // 批量添加顶部按钮
->addRightButtons('edit,delete') // 批量添加右侧按钮
->replaceRightButton(['fixed' => 1], '<button class="btn btn-danger btn-xs" type="button" disabled>固定字段禁止操作</button>')
->setRowList($data_list) // 设置表格数据
->fetch(); // 渲染模板
}
/**
* 新增字段
* @param string $model 文档模型id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function add($model = '')
{
// 内容模型类别[0-系统1-普通2-独立]
$model_type = get_model_type($model);
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 非独立模型需验证字段名称是否为aid
if ($model_type != 2) {
// 非独立模型需验证新增的字段是否被系统占用
if ($data['name'] == 'aid' || is_default_field($data['name'])) {
$this->error('字段名称已存在');
}
}
$result = $this->validate($data, 'Field');
if(true !== $result) $this->error($result);
// 如果是快速联动
switch ($data['type']) {
case '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'];
break;
case 'number':
$data['type'] = 'text';
break;
case 'bmap':
$data['level'] = !$data['level'] ? 12 : $data['level'];
break;
}
if ($field = FieldModel::create($data)) {
$FieldModel = new FieldModel();
// 添加字段
if ($FieldModel->newField($data)) {
// 记录行为
$details = '详情:文档模型('.get_model_title($data['model']).')、字段名称('.$data['name'].')、字段标题('.$data['title'].')、字段类型('.$data['type'].')';
action_log('field_add', 'cms_field', $field['id'], UID, $details);
// 清除缓存
cache('cms_system_fields', null);
$this->success('新增成功', cookie('__forward__'));
} else {
// 添加失败,删除新增的数据
FieldModel::destroy($field['id']);
$this->error($FieldModel->getError());
}
} else {
$this->error('新增失败');
}
}
if ($model_type != 2) {
$field_exist = Db::name('cms_field')->where('model', 'in', [0, $model])->column('name');
$field_exist[] = 'aid';
} else {
$field_exist = ['id','cid','uid','title','model','create_time','update_time','sort','status','view','trash'];
}
// 显示添加页面
return ZBuilder::make('form')
->setPageTips('以下字段名称已存在,请不要建立同名的字段:<br>'. implode('、', $field_exist))
->addFormItems([
['hidden', 'model', $model],
['text', 'name', '字段名称', '由小写英文字母和下划线组成'],
['text', 'title', '字段标题', '可填写中文'],
['select', 'type', '字段类型', '', config('form_item_type')],
['text', 'define', '字段定义', '可根据实际需求自行填写或修改但必须是正确的sql语法'],
['text', 'value', '字段默认值'],
['textarea', 'options', '额外选项', '用于单选、多选、下拉、联动等类型'],
['text', 'ajax_url', '异步请求地址', "如请求的地址是 <code>url('ajax/getCity')</code>,那么只需填写 <code>ajax/getCity</code>,或者直接填写以 <code>http</code>开头的url地址"],
['text', 'next_items', '下一级联动下拉框的表单名', "与当前有关联的下级联动下拉框名多个用逗号隔开area,other"],
['text', 'param', '请求参数名', "联动下拉框请求参数名,默认为配置名称"],
['text', 'level', '级别', '如果类型为【快速联动下拉框】则表示需要显示的级别数量默认为2。如果类型为【百度地图】则表示地图默认缩放级别建议设置为12', 2],
['text', 'table', '表名', '要查询的表里面必须含有id、name、pid三个字段其中id和name字段可在下面重新定义'],
['text', 'pid', '父级id字段名', '即表中的父级ID字段名如果表中的主键字段名为pid则可不填写'],
['text', 'key', '键字段名', '即表中的主键字段名如果表中的主键字段名为id则可不填写'],
['text', 'option', '值字段名', '下拉菜单显示的字段名如果表中的该字段名为name则可不填写'],
['text', 'ak', 'APPKEY', '百度编辑器APPKEY'],
['text', 'format', '格式'],
['textarea', 'tips', '字段说明', '字段补充说明'],
['radio', 'fixed', '是否为固定字段', '如果为 <code>固定字段</code> 则添加后不可修改', ['否', '是'], 0],
['radio', 'show', '是否显示', '新增或编辑时是否显示该字段', ['否', '是'], 1],
['radio', 'status', '立即启用', '', ['否', '是'], 1],
['text', 'sort', '排序', '', 100],
])
->setTrigger('type', 'linkage', 'ajax_url,next_items,param')
->setTrigger('type', 'linkages', 'table,pid,key,option')
->setTrigger('type', 'bmap', 'ak')
->setTrigger('type', 'linkages,bmap', 'level')
->setTrigger('type', 'masked,date,time,datetime', 'format')
->setTrigger('type', 'checkbox,radio,array,select,linkage,linkages', 'options')
->js('field')
->fetch();
}
/**
* 编辑字段
* @param null $id 字段id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function edit($id = null)
{
if ($id === null) $this->error('参数错误');
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Field');
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 ($data['type'] == 'bmap') {
$data['level'] = !$data['level'] ? 12 : $data['level'];
}
// 更新字段信息
$FieldModel = new FieldModel();
if ($FieldModel->updateField($data)) {
if ($FieldModel->isUpdate(true)->save($data)) {
// 记录行为
action_log('field_edit', 'cms_field', $id, UID, $data['name']);
$this->success('字段更新成功', cookie('__forward__'));
}
}
$this->error('字段更新失败');
}
// 获取数据
$info = FieldModel::get($id);
// 显示编辑页面
return ZBuilder::make('form')
->addFormItems([
['hidden', 'id'],
['hidden', 'model'],
['text', 'name', '字段名称', '由小写英文字母和下划线组成'],
['text', 'title', '字段标题', '可填写中文'],
['select', 'type', '字段类型', '', config('form_item_type')],
['text', 'define', '字段定义', '可根据实际需求自行填写或修改但必须是正确的sql语法'],
['text', 'value', '字段默认值'],
['textarea', 'options', '额外选项', '用于单选、多选、下拉、联动等类型'],
['text', 'ajax_url', '异步请求地址', "如请求的地址是 <code>url('ajax/getCity')</code>,那么只需填写 <code>ajax/getCity</code>,或者直接填写以 <code>http</code>开头的url地址"],
['text', 'next_items', '下一级联动下拉框的表单名', "与当前有关联的下级联动下拉框名多个用逗号隔开area,other"],
['text', 'param', '请求参数名', "联动下拉框请求参数名,默认为配置名称"],
['text', 'level', '级别', '如果类型为【快速联动下拉框】则表示需要显示的级别数量默认为2。如果类型为【百度地图】则表示地图默认缩放级别建议设置为12'],
['text', 'table', '表名', '要查询的表里面必须含有id、name、pid三个字段其中id和name字段可在下面重新定义'],
['text', 'pid', '父级id字段名', '即表中的父级ID字段名如果表中的主键字段名为pid则可不填写'],
['text', 'key', '键字段名', '即表中的主键字段名如果表中的主键字段名为id则可不填写'],
['text', 'option', '值字段名', '下拉菜单显示的字段名如果表中的该字段名为name则可不填写'],
['text', 'ak', 'APPKEY', '百度编辑器APPKEY'],
['text', 'format', '格式'],
['textarea', 'tips', '字段说明', '字段补充说明'],
['radio', 'show', '是否显示', '新增或编辑时是否显示该字段', ['否', '是']],
['radio', 'status', '立即启用', '', ['否', '是']],
['text', 'sort', '排序'],
])
->setTrigger('type', 'linkage', 'ajax_url,next_items,param')
->setTrigger('type', 'linkages', 'table,pid,key,option')
->setTrigger('type', 'bmap', 'ak')
->setTrigger('type', 'linkages,bmap', 'level')
->setTrigger('type', 'masked,date,time,datetime', 'format')
->setTrigger('type', 'checkbox,radio,array,select,linkage,linkages', 'options')
->js('field')
->setFormData($info)
->fetch();
}
/**
* 删除字段
* @param null $ids 字段id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function delete($ids = null)
{
if ($ids === null) $this->error('参数错误');
$FieldModel = new FieldModel();
$field = $FieldModel->where('id', $ids)->find();
if ($FieldModel->deleteField($field)) {
if ($FieldModel->where('id', $ids)->delete()) {
// 记录行为
$details = '详情:文档模型('.get_model_title($field['model']).')、字段名称('.$field['name'].')、字段标题('.$field['title'].')、字段类型('.$field['type'].')';
action_log('field_delete', 'cms_field', $ids, UID, $details);
$this->success('删除成功', cookie('__forward__'));
}
}
return $this->error('删除失败');
}
/**
* 启用字段
* @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 类型enable/disable
* @param array $record
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function setStatus($type = '', $record = [])
{
$ids = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
$field_delete = is_array($ids) ? '' : $ids;
$field_names = FieldModel::where('id', 'in', $ids)->column('name');
return parent::setStatus($type, ['field_'.$type, 'cms_field', $field_delete, UID, implode('、', $field_names)]);
}
/**
* 快速编辑
* @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 = FieldModel::where('id', $id)->value($field);
$details = '字段(' . $field . '),原值(' . $config . '),新值:(' . $value . ')';
return parent::quickEdit(['field_edit', 'cms_field', $id, UID, $details]);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use think\Db;
/**
* 仪表盘控制器
* @package app\cms\admin
*/
class Index extends Admin
{
/**
* 首页
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function index()
{
$this->assign('document', Db::name('cms_document')->where('trash', 0)->count());
$this->assign('column', Db::name('cms_column')->count());
$this->assign('page', Db::name('cms_page')->count());
$this->assign('model', Db::name('cms_model')->count());
$this->assign('page_title', '仪表盘');
return $this->fetch(); // 渲染模板
}
}

View File

@ -0,0 +1,208 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Link as LinkModel;
/**
* 友情链接控制器
* @package app\cms\admin
*/
class Link extends Admin
{
/**
* 友情链接列表
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
* @throws \think\exception\DbException
*/
public function index()
{
// 查询
$map = $this->getMap();
// 排序
$order = $this->getOrder('update_time desc');
// 数据列表
$data_list = LinkModel::where($map)->order($order)->paginate();
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['title' => '标题']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['title', '标题', 'text.edit'],
['url', '链接', 'text.edit'],
['type', '类型', 'text', '', [1 => '文字链接', 2 => '图片链接']],
['create_time', '创建时间', 'datetime'],
['update_time', '更新时间', 'datetime'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->addTopButtons('add,enable,disable,delete') // 批量添加顶部按钮
->addRightButtons(['edit', 'delete' => ['data-tips' => '删除后无法恢复。']]) // 批量添加右侧按钮
->addOrder('id,title,type,create_time,update_time')
->setRowList($data_list) // 设置表格数据
->addValidate('Link', 'title,url')
->fetch(); // 渲染模板
}
/**
* 新增
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function add()
{
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Link');
if(true !== $result) $this->error($result);
if ($link = LinkModel::create($data)) {
// 记录行为
action_log('link_add', 'cms_link', $link['id'], UID, $data['title']);
$this->success('新增成功', 'index');
} else {
$this->error('新增失败');
}
}
// 显示添加页面
return ZBuilder::make('form')
->addFormItems([
['radio', 'type', '链接类型', '', [1 => '文字链接', 2 => '图片链接'], 1],
['text', 'title', '链接标题'],
['text', 'url', '链接地址', '请以 <code>http</code> 或 <code>https</code>开头'],
['image', 'logo', '链接LOGO'],
['tags', 'keywords', '关键词'],
['textarea', 'contact', '联系方式'],
['text', 'sort', '排序', '', 100],
['radio', 'status', '立即启用', '', ['否', '是'], 1]
])
->setTrigger('type', 2, 'logo')
->fetch();
}
/**
* 编辑
* @param null $id 链接id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function edit($id = null)
{
if ($id === null) $this->error('缺少参数');
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Link');
if(true !== $result) $this->error($result);
if (LinkModel::update($data)) {
// 记录行为
action_log('link_edit', 'cms_link', $id, UID, $data['title']);
$this->success('编辑成功', 'index');
} else {
$this->error('编辑失败');
}
}
$info = LinkModel::get($id);
// 显示编辑页面
return ZBuilder::make('form')
->addFormItems([
['hidden', 'id'],
['radio', 'type', '链接类型', '', [1 => '文字链接', 2 => '图片链接']],
['text', 'title', '链接标题'],
['text', 'url', '链接地址', '请以 <code>http</code> 或 <code>https</code>开头'],
['image', 'logo', '链接LOGO'],
['tags', 'keywords', '关键词'],
['textarea', 'contact', '联系方式'],
['text', 'sort', '排序'],
['radio', 'status', '立即启用', '', ['否', '是']]
])
->setTrigger('type', 2, 'logo')
->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');
$link_title = LinkModel::where('id', 'in', $ids)->column('title');
return parent::setStatus($type, ['link_'.$type, 'cms_link', 0, UID, implode('、', $link_title)]);
}
/**
* 快速编辑
* @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', '');
$link = LinkModel::where('id', $id)->value($field);
$details = '字段(' . $field . '),原值(' . $link . '),新值:(' . $value . ')';
return parent::quickEdit(['link_edit', 'cms_link', $id, UID, $details]);
}
}

View File

@ -0,0 +1,255 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Menu as MenuModel;
use app\cms\model\Column as ColumnModel;
use app\cms\model\Page as PageModel;
use util\Tree;
use think\Db;
/**
* 菜单控制器
* @package app\cms\admin
*/
class Menu extends Admin
{
/**
* 菜单列表
* @param null $id 导航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 index($id = null)
{
if ($id === null) $this->error('缺少参数');
// 查询
$map = $this->getMap();
// 数据列表
$data_list = Db::view('cms_menu', true)
->view('cms_column', ['name' => 'column_name'], 'cms_menu.column=cms_column.id', 'left')
->view('cms_page', ['title' => 'page_title'], 'cms_menu.page=cms_page.id', 'left')
->where('cms_menu.nid', $id)
->order('cms_menu.sort,cms_menu.pid,cms_menu.id')
->select();
foreach ($data_list as &$item) {
if ($item['type'] == 0) {
$item['title'] = $item['column_name'];
} elseif ($item['type'] == 1) {
$item['title'] = $item['page_title'];
}
}
if (empty($map)) {
$data_list = Tree::toList($data_list);
}
$btnAdd = ['icon' => 'fa fa-plus', 'title' => '新增子菜单', 'href' => url('add', ['nid' => $id, 'pid' => '__id__'])];
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['cms_menu.title|cms_column.name|cms_page.title' => '标题'])// 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['title', '标题', 'callback', function($value, $data){
return isset($data['title_prefix']) ? $data['title_display'] : $value;
}, '__data__'],
['type', '类型', 'text', '', ['栏目链接', '单页链接', '自定义链接']],
['target', '打开方式', 'select', ['_self' => '当前窗口', '_blank' => '新窗口']],
['create_time', '创建时间', 'datetime'],
['update_time', '更新时间', 'datetime'],
['sort', '排序', 'text.edit'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->addTopButton('back', ['href' => url('nav/index')])
->addTopButton('add', ['href' => url('add', ['nid' => $id])])
->addTopButtons('enable,disable')// 批量添加顶部按钮
->addRightButton('custom', $btnAdd)
->addRightButton('edit')
->addRightButton('delete', ['data-tips' => '删除后无法恢复。'])// 批量添加右侧按钮
->setRowList($data_list)// 设置表格数据
->addValidate('Nav', 'title')
->fetch(); // 渲染模板
}
/**
* 新增
* @param null $nid 导航id
* @param int $pid 菜单父级id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function add($nid = null, $pid = 0)
{
if ($nid === null) $this->error('缺少参数');
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Menu');
if(true !== $result) $this->error($result);
if ($menu = MenuModel::create($data)) {
// 记录行为
action_log('menu_add', 'cms_menu', $menu['id'], UID, $data['title']);
$this->success('新增成功', url('index', ['id' => $nid]));
} else {
$this->error('新增失败');
}
}
// 显示添加页面
return ZBuilder::make('form')
->addFormItems([
['hidden', 'nid', $nid],
['hidden', 'pid', $pid],
['radio', 'type', '类型', '', ['栏目链接', '单页链接', '自定义链接'], 0],
['select', 'column', '栏目', '<code>必选</code>', ColumnModel::getTreeList(0, false)],
['select', 'page', '单页', '<code>必选</code>', PageModel::getTitleList()],
['text', 'title', '菜单标题', '<code>必填</code>,只用于区分'],
['text', 'url', 'URL', "<code>必填</code>。如果是模块链接,请填写<code>模块/控制器/操作</code>,如:<code>admin/menu/add</code>。如果是普通链接则直接填写url地址<code>http://www.dolphinphp.com</code>"],
['text', 'css', 'CSS类', '可选'],
['text', 'rel', '链接关系网XFN', '可选即链接的rel值'],
['radio', 'target', '打开方式', '', ['_self' => '当前窗口', '_blank' => '新窗口'], '_self'],
['text', 'sort', '排序', '', 100],
['radio', 'status', '立即启用', '', ['否', '是'], 1]
])
->setTrigger('type', '0', 'column')
->setTrigger('type', '1', 'page')
->setTrigger('type', '2', 'title,url')
->fetch();
}
/**
* 编辑
* @param null $id 菜单id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function edit($id = null)
{
if ($id === null) $this->error('缺少参数');
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Menu');
if(true !== $result) $this->error($result);
if (MenuModel::update($data)) {
// 记录行为
action_log('menu_edit', 'cms_menu', $id, UID, $data['title']);
$this->success('编辑成功', url('index', ['id' => $data['nid']]));
} else {
$this->error('编辑失败');
}
}
// 显示添加页面
return ZBuilder::make('form')
->addFormItems([
['hidden', 'id'],
['hidden', 'nid'],
['radio', 'type', '类型', '', ['栏目链接', '单页链接', '自定义链接']],
['select', 'column', '栏目', '<code>必选</code>', ColumnModel::getTreeList(0, false)],
['select', 'page', '单页', '<code>必选</code>', PageModel::getTitleList()],
['text', 'title', '菜单标题', '<code>必填</code>,只用于区分'],
['text', 'url', 'URL', "<code>必填</code>。如果是模块链接,请填写<code>模块/控制器/操作</code>,如:<code>admin/menu/add</code>。如果是普通链接则直接填写url地址<code>http://www.dolphinphp.com</code>"],
['text', 'css', 'CSS类', '可选'],
['text', 'rel', '链接关系网XFN', '可选即链接的rel值'],
['radio', 'target', '打开方式', '', ['_self' => '当前窗口', '_blank' => '新窗口'], '_self'],
['text', 'sort', '排序', '', 100],
['radio', 'status', '立即启用', '', ['否', '是'], 1]
])
->setFormData(MenuModel::get($id))
->setTrigger('type', '0', 'column')
->setTrigger('type', '1', 'page')
->setTrigger('type', '2', 'title,url')
->fetch();
}
/**
* 删除菜单
* @param null $ids 菜单id
* @author 蔡伟明 <314013107@qq.com>
* @throws \think\Exception
* @throws \think\exception\PDOException
*/
public function delete($ids = null)
{
// 检查是否有子菜单
if (MenuModel::where('pid', $ids)->find()) {
$this->error('请先删除或移动该菜单下的子菜单');
}
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');
$menu_title = MenuModel::where('id', 'in', $ids)->column('title');
return parent::setStatus($type, ['menu_'.$type, 'cms_menu', 0, UID, implode('、', $menu_title)]);
}
/**
* 快速编辑
* @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', '');
$menu = MenuModel::where('id', $id)->value($field);
$details = '字段(' . $field . '),原值(' . $menu . '),新值:(' . $value . ')';
return parent::quickEdit(['menu_edit', 'cms_menu', $id, UID, $details]);
}
}

View File

@ -0,0 +1,228 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Model as DocumentModel;
use app\admin\model\Menu as MenuModel;
use think\Db;
use think\facade\Cache;
/**
* 内容模型控制器
* @package app\cms\admin
*/
class Model extends Admin
{
/**
* 内容模型列表
* @author 蔡伟明 <314013107@qq.com>
*/
public function index()
{
// 查询
$map = $this->getMap();
// 数据列表
$data_list = DocumentModel::where($map)->order('sort,id desc')->paginate();
// 字段管理按钮
$btnField = [
'title' => '字段管理',
'icon' => 'fa fa-fw fa-navicon',
'href' => url('field/index', ['id' => '__id__'])
];
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['name' => '标识', 'title' => '标题']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['icon', '图标', 'icon'],
['title', '标题'],
['name', '标识'],
['table', '附加表'],
['type', '模型', 'text', '', ['系统', '普通', '独立']],
['create_time', '创建时间', 'datetime'],
['sort', '排序', 'text.edit'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->addFilter('type', ['系统', '普通', '独立'])
->addTopButtons('add,enable,disable') // 批量添加顶部按钮
->addRightButtons(['edit', 'custom' => $btnField, 'delete' => ['data-tips' => '删除模型将同时删除该模型下的所有字段,且无法恢复。']]) // 批量添加右侧按钮
->setRowList($data_list) // 设置表格数据
->fetch(); // 渲染模板
}
/**
* 新增内容模型
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function add()
{
// 保存数据
if ($this->request->isPost()) {
$data = $this->request->post();
if ($data['table'] == '') {
$data['table'] = config('database.prefix') . 'cms_document_' . $data['name'];
} else {
$data['table'] = str_replace('#@__', config('database.prefix'), $data['table']);
}
// 验证
$result = $this->validate($data, 'Model');
if(true !== $result) $this->error($result);
// 严格验证附加表是否存在
if (table_exist($data['table'])) {
$this->error('附加表已存在');
}
if ($model = DocumentModel::create($data)) {
// 创建附加表
if (false === DocumentModel::createTable($model)) {
$this->error('创建附加表失败');
}
// 创建菜单节点
$map = [
'module' => 'cms',
'title' => '内容管理'
];
$menu_data = [
"module" => "cms",
"pid" => Db::name('admin_menu')->where($map)->value('id'),
"title" => $data['title'],
"url_type" => "module_admin",
"url_value" => "cms/content/{$data['name']}",
"url_target" => "_self",
"icon" => "fa fa-fw fa-list",
"online_hide" => "0",
"sort" => "100",
];
MenuModel::create($menu_data);
// 记录行为
action_log('model_add', 'cms_model', $model['id'], UID, $data['title']);
Cache::clear();
$this->success('新增成功', 'index');
} else {
$this->error('新增失败');
}
}
$type_tips = '此选项添加后不可更改。如果为 <code>系统模型</code> 将禁止删除,对于 <code>独立模型</code>将强制创建字段id,cid,uid,model,title,create_time,update_time,sort,status,trash,view';
// 显示添加页面
return ZBuilder::make('form')
->addFormItems([
['text', 'name', '模型标识', '由小写字母、数字或下划线组成,不能以数字开头'],
['text', 'title', '模型标题', '可填写中文'],
['text', 'table', '附加表', '创建后不可更改。由小写字母、数字或下划线组成,如果不填写默认为 <code>'. config('database.prefix') . 'cms_document_模型标识</code>,如果需要自定义,请务必填写系统表前缀,<code>#@__</code>表示当前系统表前缀'],
['radio', 'type', '模型类别', $type_tips, ['系统模型', '普通模型', '独立模型(不使用主表)'], 1],
['icon', 'icon', '图标'],
['radio', 'status', '立即启用', '', ['否', '是'], 1],
['text', 'sort', '排序', '', 100],
])
->fetch();
}
/**
* 编辑内容模型
* @param null $id 模型id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function edit($id = null) {
if ($id === null) $this->error('参数错误');
// 保存数据
if ($this->request->isPost()) {
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Model.edit');
if(true !== $result) $this->error($result);
if (DocumentModel::update($data)) {
cache('cms_model_list', null);
cache('cms_model_title_list', null);
// 记录行为
action_log('model_edit', 'cms_model', $id, UID, "ID({$id}),标题({$data['title']})");
$this->success('编辑成功', 'index');
} else {
$this->error('编辑失败');
}
}
$list_model_type = ['系统模型', '普通模型', '独立模型(不使用主表)'];
// 模型信息
$info = DocumentModel::get($id);
$info['type'] = $list_model_type[$info['type']];
// 显示编辑页面
return ZBuilder::make('form')
->addFormItems([
['hidden', 'id'],
['hidden', 'name'],
['static', 'name', '模型标识'],
['static', 'type', '模型类别'],
['static', 'table', '附加表'],
['text', 'title', '模型标题', '可填写中文'],
['icon', 'icon', '图标'],
['radio', 'status', '立即启用', '', ['否', '是']],
['text', 'sort', '排序'],
])
->setFormData($info)
->fetch();
}
/**
* 删除内容模型
* @param null $ids 内容模型id
* @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($ids = null)
{
if ($ids === null) $this->error('参数错误');
$model = DocumentModel::where('id', $ids)->find();
if ($model['type'] == 0) {
$this->error('禁止删除系统模型');
}
// 删除表和字段信息
if (DocumentModel::deleteTable($ids)) {
// 删除主表中的文档
if (false === Db::name('cms_document')->where('model', $ids)->delete()) {
$this->error('删除主表文档失败');
}
// 删除菜单节点
$map = [
'module' => 'cms',
'url_value' => "cms/content/{$model['name']}"
];
if (false === Db::name('admin_menu')->where($map)->delete()) {
$this->error('删除菜单节点失败');
}
// 删除字段数据
if (false !== Db::name('cms_field')->where('model', $ids)->delete()) {
cache('cms_model_list', null);
cache('cms_model_title_list', null);
return parent::delete();
} else {
$this->error('删除内容模型字段失败');
}
} else {
$this->error('删除内容模型表失败');
}
}
}

View File

@ -0,0 +1,167 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Nav as NavModel;
use app\cms\model\Menu as MenuModel;
/**
* 导航控制器
* @package app\cms\admin
*/
class Nav extends Admin
{
/**
* 导航列表
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
* @throws \think\exception\DbException
*/
public function index()
{
// 查询
$map = $this->getMap();
// 排序
$order = $this->getOrder('update_time desc');
// 数据列表
$data_list = NavModel::where($map)->order($order)->paginate();
// 自定义按钮
$btnMenuList = [
'title' => '菜单列表',
'icon' => 'fa fa-list',
'href' => url('menu/index', ['id' => '__id__'])
];
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['title' => '标题'])// 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['tag', '标识', 'text.edit'],
['title', '标题', 'text.edit'],
['create_time', '创建时间', 'datetime'],
['update_time', '更新时间', 'datetime'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->addTopButtons('add,enable,disable,delete')// 批量添加顶部按钮
->addRightButton('custom', $btnMenuList)
->addRightButton('delete', ['data-tips' => '删除后无法恢复。'])// 批量添加右侧按钮
->addOrder('id,title,create_time,update_time')
->setRowList($data_list)// 设置表格数据
->addValidate('Nav', 'tag,title')
->fetch(); // 渲染模板
}
/**
* 新增
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function add()
{
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Nav');
if(true !== $result) $this->error($result);
if ($nav = NavModel::create($data)) {
// 记录行为
action_log('nav_add', 'cms_nav', $nav['id'], UID, $data['title']);
$this->success('新增成功', 'index');
} else {
$this->error('新增失败');
}
}
// 显示添加页面
return ZBuilder::make('form')
->addFormItems([
['text', 'tag', '菜单标识', '由字母和下划线组成main_nav'],
['text', 'title', '菜单标题', '必填'],
['radio', 'status', '立即启用', '', ['否', '是'], 1]
])
->fetch();
}
/**
* 删除导航
* @param null $ids 菜单id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
* @throws \think\exception\PDOException
*/
public function delete($ids = null)
{
if ($ids === null) $this->error('参数错误');
// 删除该导航的所有子菜单
if (false === MenuModel::where('nid', 'in', $ids)->delete()) {
$this->error('删除失败');
}
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');
$nav_title = NavModel::where('id', 'in', $ids)->column('title');
return parent::setStatus($type, ['nav_'.$type, 'cms_nav', 0, UID, implode('、', $nav_title)]);
}
/**
* 快速编辑
* @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', '');
$nav = NavModel::where('id', $id)->value($field);
$details = '字段(' . $field . '),原值(' . $nav . '),新值:(' . $value . ')';
return parent::quickEdit(['nav_edit', 'cms_nav', $id, UID, $details]);
}
}

View File

@ -0,0 +1,204 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Page as PageModel;
/**
* 单页控制器
* @package app\cms\admin
*/
class Page 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 = PageModel::where($map)->order($order)->paginate();
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['title' => '标题']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['title', '标题', 'text.edit'],
['create_time', '创建时间', 'datetime'],
['update_time', '更新时间', 'datetime'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->addTopButtons('add,enable,disable,delete') // 批量添加顶部按钮
->addRightButtons(['edit', 'delete' => ['data-tips' => '删除后无法恢复。']]) // 批量添加右侧按钮
->addOrder('id,title,create_time,update_time')
->setRowList($data_list) // 设置表格数据
->addValidate('Page', 'title')
->fetch(); // 渲染模板
}
/**
* 新增
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function add()
{
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Page');
if(true !== $result) $this->error($result);
if ($page = PageModel::create($data)) {
// 记录行为
action_log('page_add', 'cms_page', $page['id'], UID, $data['title']);
$this->success('新增成功', 'index');
} else {
$this->error('新增失败');
}
}
// 显示添加页面
return ZBuilder::make('form')
->addFormItems([
['text', 'title', '页面标题'],
['tags', 'keywords', '页面关键词', '关键字之间用英文逗号隔开'],
['textarea', 'description', '页面描述', '100字左右'],
['text', 'template', '模板文件名'],
['ckeditor', 'content', '页面内容'],
['image', 'cover', '单页封面'],
['text', 'view', '阅读量', '', 0],
['radio', 'status', '立即启用', '', ['否', '是'], 1]
])
->fetch();
}
/**
* 编辑
* @param null $id 单页id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function edit($id = null)
{
if ($id === null) $this->error('缺少参数');
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Page');
if(true !== $result) $this->error($result);
if (PageModel::update($data)) {
// 记录行为
action_log('page_edit', 'cms_page', $id, UID, $data['title']);
$this->success('编辑成功', 'index');
} else {
$this->error('编辑失败');
}
}
$info = PageModel::get($id);
// 显示编辑页面
return ZBuilder::make('form')
->addFormItems([
['hidden', 'id'],
['text', 'title', '页面标题'],
['tags', 'keywords', '页面关键词', '关键字之间用英文逗号隔开'],
['textarea', 'description', '页面描述', '100字左右'],
['text', 'template', '模板文件名'],
['ckeditor', 'content', '页面内容'],
['image', 'cover', '单页封面'],
['text', 'view', '阅读量', '', 0],
['radio', 'status', '立即启用', '', ['否', '是']]
])
->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');
$page_title = PageModel::where('id', 'in', $ids)->column('title');
return parent::setStatus($type, ['page_'.$type, 'cms_page', 0, UID, implode('、', $page_title)]);
}
/**
* 快速编辑
* @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', '');
$page = PageModel::where('id', $id)->value($field);
$details = '字段(' . $field . '),原值(' . $page . '),新值:(' . $value . ')';
return parent::quickEdit(['page_edit', 'cms_page', $id, UID, $details]);
}
}

View File

@ -0,0 +1,194 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Document as DocumentModel;
use think\Db;
/**
* 回收站控制器
* @package app\cms\admin
*/
class Recycle extends Admin
{
/**
* 文档列表
* @param string $model 内容模型id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
* @throws \think\exception\DbException
*/
public function index($model = '')
{
if ($model == '') {
// 查询
$map = $this->getMap();
$map[] = ['cms_document.trash', '=', 1];
// 排序
$order = $this->getOrder('update_time desc');
// 数据列表
$data_list = DocumentModel::getList($map, $order);
// 自定义按钮
$btnRestore = [
'class' => 'btn btn-xs btn-default ajax-get confirm',
'icon' => 'fa fa-fw fa-reply',
'title' => '还原',
'href' => url('restore', ['ids' => '__id__'])
];
$btnRestoreAll = [
'class' => 'btn btn-success ajax-post confirm',
'icon' => 'fa fa-fw fa-reply-all',
'title' => '批量还原',
'href' => url('restore')
];
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['title' => '标题', 'cms_column.name' => '栏目名称']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['title', '标题'],
['column_name', '栏目名称'],
['view', '点击量'],
['username', '发布人'],
['update_time', '更新时间', 'datetime'],
['right_button', '操作', 'btn']
])
->addTopButton('enable', $btnRestoreAll) // 批量添加顶部按钮
->addTopButton('delete', ['title' => '批量删除', 'href' => url('delete'), 'data-tips' => '删除后不可回复!']) // 批量添加顶部按钮
->addRightButton('custom', $btnRestore) // 添加右侧按钮
->addRightButton('delete', ['href' => url('delete', ['ids' => '__id__']), 'data-tips' => '删除后不可回复!']) // 添加右侧按钮
->addOrder('id,title,column_name,view,username,update_time')
->addFilter(['column_name' => 'cms_column.name', 'username' => 'admin_user'])
->setRowList($data_list) // 设置表格数据
->fetch(); // 渲染模板
} else {
$table_name = get_model_table($model);
// 查询
$map = $this->getMap();
$map[] = ['trash', '=', 1];
// 排序
$order = $this->getOrder('update_time desc');
// 数据列表
$data_list = Db::view($table_name, true)
->view("cms_column", ['name' => 'column_name'], 'cms_column.id='.$table_name.'.cid', 'left')
->view("admin_user", 'username', 'admin_user.id='.$table_name.'.uid', 'left')
->where($map)
->order($order)
->paginate();
// 自定义按钮
$btnRestore = [
'class' => 'btn btn-xs btn-default ajax-get confirm',
'icon' => 'fa fa-fw fa-reply',
'title' => '还原',
'href' => url('restore', ['table' => $table_name, 'ids' => '__id__'])
];
$btnRestoreAll = [
'class' => 'btn btn-success ajax-post confirm',
'icon' => 'fa fa-fw fa-reply-all',
'title' => '批量还原',
'href' => url('restore', ['table' => $table_name])
];
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['title' => '标题', 'cms_column.name' => '栏目名称']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['title', '标题'],
['column_name', '栏目名称'],
['view', '点击量'],
['username', '发布人'],
['update_time', '更新时间', 'datetime'],
['right_button', '操作', 'btn']
])
->addTopButton('enable', $btnRestoreAll) // 添加顶部按钮
->addTopButton('delete', ['title' => '批量删除', 'href' => url('delete', ['table' => $table_name]), 'data-tips' => '删除后不可回复!']) // 添加顶部按钮
->addRightButton('custom', $btnRestore) // 添加右侧按钮
->addRightButton('delete', ['href' => url('delete', ['ids' => '__id__', 'table' => $table_name]), 'data-tips' => '删除后不可回复!']) // 添加右侧按钮
->addOrder('id,title,column_name,view,username,update_time')
->addFilter(['column_name' => 'cms_column.name', 'username' => 'admin_user'])
->setRowList($data_list) // 设置表格数据
->fetch(); // 渲染模板
}
}
/**
* 还原文档
* @param null $ids 文档id
* @param string $table 表名
* @author 蔡伟明 <314013107@qq.com>
*/
public function restore($ids = null, $table = '')
{
if ($ids === null) $this->error('请选择要操作的数据');
$table = $table != '' ? substr($table, strlen(config('database.prefix'))) : 'cms_document';
$document_id = is_array($ids) ? '' : $ids;
$document_title = Db::name($table)->where('id', 'in', $ids)->column('title');
// 还原文档
if (false === Db::name($table)->where('id', 'in', $ids)->setField('trash', 0)) {
$this->error('还原失败');
}
// 删除并记录日志
action_log('document_restore', $table, $document_id, UID, implode('、', $document_title));
$this->success('还原成功');
}
/**
* 彻底删除文档
* @param null $ids 文档id
* @param string $table 表名
* @author 蔡伟明 <314013107@qq.com>
* @throws \think\Exception
* @throws \think\exception\PDOException
*/
public function delete($ids = null, $table = '')
{
if ($ids === null) $this->error('请选择要操作的数据');
$ids = is_array($ids) ? $ids : (array)$ids;
if ($table == '') {
// 获取文档标题和模型id
$data_list = Db::name('cms_document')->where('id', 'in', $ids)->column('id,model,title');
foreach ($data_list as $document) {
// 附加表名
$extra_table = get_model_table($document['model']);
// 删除附加表文档
if (false === Db::table($extra_table)->where('aid', $document['id'])->delete()) {
$this->error('删除文档:'. $document['title']. ' 失败');
}
// 删除主表文档
if (false === Db::name('cms_document')->where('id', $document['id'])->delete()) {
$this->error('删除失败');
}
// 记录行为
action_log('document_delete', 'cms_document', $document['id'], UID, $document['title']);
}
} else {
// 文档标题
$document_title = Db::table($table)->where('id', 'in', $ids)->column('title');
// 删除独立文档
if (false === Db::table($table)->where('id', 'in', $ids)->delete()) {
$this->error('删除失败');
}
// 记录行为
action_log('document_delete', $table, 0, UID, '表('.$table.'),文档('.implode('、', $document_title).')');
}
$this->success('删除成功');
}
}

View File

@ -0,0 +1,200 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Slider as SliderModel;
/**
* 滚动图片控制器
* @package app\cms\admin
*/
class Slider 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 = SliderModel::where($map)->order($order)->paginate();
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['title' => '标题']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['cover', '图片', 'picture'],
['title', '标题', 'text.edit'],
['url', '链接', 'text.edit'],
['create_time', '创建时间', 'datetime'],
['sort', '排序', 'text.edit'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->addTopButtons('add,enable,disable,delete') // 批量添加顶部按钮
->addRightButtons(['edit', 'delete' => ['data-tips' => '删除后无法恢复。']]) // 批量添加右侧按钮
->addOrder('id,title,create_time')
->setRowList($data_list) // 设置表格数据
->addValidate('Slider', 'title,url')
->fetch(); // 渲染模板
}
/**
* 新增
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function add()
{
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Slider');
if(true !== $result) $this->error($result);
if ($slider = SliderModel::create($data)) {
// 记录行为
action_log('slider_add', 'cms_slider', $slider['id'], UID, $data['title']);
$this->success('新增成功', 'index');
} else {
$this->error('新增失败');
}
}
// 显示添加页面
return ZBuilder::make('form')
->addFormItems([
['text', 'title', '标题'],
['image', 'cover', '图片'],
['text', 'url', '链接'],
['text', 'sort', '排序', '', 100],
['radio', 'status', '立即启用', '', ['否', '是'], 1]
])
->fetch();
}
/**
* 编辑
* @param null $id 滚动图片id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function edit($id = null)
{
if ($id === null) $this->error('缺少参数');
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Slider');
if(true !== $result) $this->error($result);
if (SliderModel::update($data)) {
// 记录行为
action_log('slider_add', 'cms_slider', $id, UID, $data['title']);
$this->success('编辑成功', 'index');
} else {
$this->error('编辑失败');
}
}
$info = SliderModel::get($id);
// 显示编辑页面
return ZBuilder::make('form')
->addFormItems([
['hidden', 'id'],
['text', 'title', '标题'],
['image', 'cover', '图片'],
['text', 'url', '链接'],
['text', 'sort', '排序'],
['radio', 'status', '立即启用', '', ['否', '是']]
])
->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');
$slider_title = SliderModel::where('id', 'in', $ids)->column('title');
return parent::setStatus($type, ['slider_'.$type, 'cms_slider', 0, UID, implode('、', $slider_title)]);
}
/**
* 快速编辑
* @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', '');
$slider = SliderModel::where('id', $id)->value($field);
$details = '字段(' . $field . '),原值(' . $slider . '),新值:(' . $value . ')';
return parent::quickEdit(['slider_edit', 'cms_slider', $id, UID, $details]);
}
}

View File

@ -0,0 +1,212 @@
<?php
namespace app\cms\admin;
use app\admin\controller\Admin;
use app\common\builder\ZBuilder;
use app\cms\model\Support as SupportModel;
/**
* 客服控制器
* @package app\cms\admin
*/
class Support extends Admin
{
public function index()
{
// 查询
$map = $this->getMap();
// 排序
$order = $this->getOrder();
// 数据列表
$data_list = SupportModel::where($map)->order($order)->paginate();
$search = [
'name' => '客服名称',
'qq' => 'QQ',
'msn' => 'MSN',
'taobao' => '淘宝旺旺',
'alibaba' => '阿里旺旺',
'skype' => 'SKYPE'
];
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setPageTips('添加的QQ需要到【shang.qq.com】登录后在【商家沟通组建—设置】开启QQ的在线状态否则将显示“未启用”<br>开启和关闭在线客服功能,以及更多设置,请在 <a class="alert-link link-effect" href="'.url('admin/system/index', ['group' => 'cms']).'">系统设置</a> 中操作。')
->setSearch($search) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['name', '客服名称', 'text.edit'],
['qq', 'QQ'],
['msn', 'MSN'],
['taobao', '淘宝旺旺'],
['alibaba', '阿里旺旺'],
['skype', 'SKYPE'],
['create_time', '创建时间', 'datetime'],
['sort', '排序', 'text.edit'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->addTopButtons('add,enable,disable,delete') // 批量添加顶部按钮
->addRightButtons(['edit', 'delete' => ['data-tips' => '删除后无法恢复。']]) // 批量添加右侧按钮
->addOrder('id,name,create_time,update_time')
->addValidate('Support', 'name')
->setRowList($data_list) // 设置表格数据
->fetch(); // 渲染模板
}
/**
* 新增
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function add()
{
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Support');
if(true !== $result) $this->error($result);
if ($support = SupportModel::create($data)) {
// 记录行为
action_log('support_add', 'cms_support', $support['id'], UID, $data['name']);
$this->success('新增成功', 'index');
} else {
$this->error('新增失败');
}
}
// 显示添加页面
return ZBuilder::make('form')
->addFormItems([
['text', 'name', '客服名称'],
['text', 'qq', 'QQ号码'],
['text', 'msn', 'MSN号码'],
['text', 'taobao', '淘宝旺旺'],
['text', 'alibaba', '阿里旺旺'],
['text', 'skype', 'SKYPE'],
['text', 'sort', '排序', '', 100],
['radio', 'status', '立即启用', '', ['否', '是'], 1]
])
->fetch();
}
/**
* 编辑
* @param null $id 客服id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\Exception
*/
public function edit($id = null)
{
if ($id === null) $this->error('缺少参数');
// 保存数据
if ($this->request->isPost()) {
// 表单数据
$data = $this->request->post();
// 验证
$result = $this->validate($data, 'Support');
if(true !== $result) $this->error($result);
if (SupportModel::update($data)) {
// 记录行为
action_log('support_edit', 'cms_support', $id, UID, $data['name']);
$this->success('编辑成功', 'index');
} else {
$this->error('编辑失败');
}
}
$info = SupportModel::get($id);
// 显示编辑页面
return ZBuilder::make('form')
->addFormItems([
['hidden', 'id'],
['text', 'name', '客服名称'],
['text', 'qq', 'QQ号码'],
['text', 'msn', 'MSN号码'],
['text', 'taobao', '淘宝旺旺'],
['text', 'alibaba', '阿里旺旺'],
['text', 'skype', 'SKYPE'],
['text', 'sort', '排序'],
['radio', 'status', '立即启用', '', ['否', '是']]
])
->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');
$support_title = SupportModel::where('id', 'in', $ids)->column('name');
return parent::setStatus($type, ['support_'.$type, 'cms_support', 0, UID, implode('、', $support_title)]);
}
/**
* 快速编辑
* @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', '');
$support = SupportModel::where('id', $id)->value($field);
$details = '字段(' . $field . '),原值(' . $support . '),新值:(' . $value . ')';
return parent::quickEdit(['support_edit', 'cms_support', $id, UID, $details]);
}
}

158
application/cms/common.php Normal file
View File

@ -0,0 +1,158 @@
<?php
// +----------------------------------------------------------------------
// | 海豚PHP框架 [ DolphinPHP ]
// +----------------------------------------------------------------------
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://dolphinphp.com
// +----------------------------------------------------------------------
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// 门户模块公共函数库
use think\Db;
if (!function_exists('get_column_name')) {
/**
* 获取栏目名称
* @param int $cid 栏目id
* @author 蔡伟明 <314013107@qq.com>
* @return string
*/
function get_column_name($cid = 0)
{
$column_list = model('cms/column')->getList();
return isset($column_list[$cid]) ? $column_list[$cid]['name'] : '';
}
}
if (!function_exists('get_model_name')) {
/**
* 获取内容模型名称
* @param string $id 内容模型id
* @author 蔡伟明 <314013107@qq.com>
* @return string
*/
function get_model_name($id = '')
{
$model_list = model('cms/model')->getList();
return isset($model_list[$id]) ? $model_list[$id]['name'] : '';
}
}
if (!function_exists('get_model_title')) {
/**
* 获取内容模型标题
* @param string $id 内容模型标题
* @author 蔡伟明 <314013107@qq.com>
* @return string
*/
function get_model_title($id = '')
{
$model_list = model('cms/model')->getList();
return isset($model_list[$id]) ? $model_list[$id]['title'] : '';
}
}
if (!function_exists('get_model_type')) {
/**
* 获取内容模型类别0-系统1-普通2-独立
* @param int $id 模型id
* @author 蔡伟明 <314013107@qq.com>
* @return string
*/
function get_model_type($id = 0)
{
$model_list = model('cms/model')->getList();
return isset($model_list[$id]) ? $model_list[$id]['type'] : '';
}
}
if (!function_exists('get_model_table')) {
/**
* 获取内容模型附加表名
* @param int $id 模型id
* @author 蔡伟明 <314013107@qq.com>
* @return string
*/
function get_model_table($id = 0)
{
$model_list = model('cms/model')->getList();
return isset($model_list[$id]) ? $model_list[$id]['table'] : '';
}
}
if (!function_exists('is_default_field')) {
/**
* 检查是否为系统默认字段
* @param string $field 字段名称
* @author 蔡伟明 <314013107@qq.com>
* @return bool
*/
function is_default_field($field = '')
{
$system_fields = cache('cms_system_fields');
if (!$system_fields) {
$system_fields = Db::name('cms_field')->where('model', 0)->column('name');
cache('cms_system_fields', $system_fields);
}
return in_array($field, $system_fields, true);
}
}
if (!function_exists('table_exist')) {
/**
* 检查附加表是否存在
* @param string $table_name 附加表名
* @author 蔡伟明 <314013107@qq.com>
* @return string
*/
function table_exist($table_name = '')
{
return true == Db::query("SHOW TABLES LIKE '{$table_name}'");
}
}
if (!function_exists('time_tran')) {
/**
* 转换时间
* @param int $timer 时间戳
* @author 蔡伟明 <314013107@qq.com>
* @return string
*/
function time_tran($timer)
{
$diff = $_SERVER['REQUEST_TIME'] - $timer;
$day = floor($diff / 86400);
$free = $diff % 86400;
if ($day > 0) {
return $day . " 天前";
} else {
if ($free > 0) {
$hour = floor($free / 3600);
$free = $free % 3600;
if ($hour > 0) {
return $hour . " 小时前";
} else {
if ($free > 0) {
$min = floor($free / 60);
$free = $free % 60;
if ($min > 0) {
return $min . " 分钟前";
} else {
if ($free > 0) {
return $free . " 秒前";
} else {
return '刚刚';
}
}
} else {
return '刚刚';
}
}
} else {
return '刚刚';
}
}
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace app\cms\home;
use app\cms\model\Column as ColumnModel;
use think\Db;
use util\Tree;
/**
* 前台栏目文档列表控制器
* @package app\cms\admin
*/
class Column extends Common
{
/**
* 栏目文章列表
* @param null $id 栏目id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function index($id = null)
{
if ($id === null) $this->error('缺少参数');
$map = [
'status' => 1,
'id' => $id
];
$column = Db::name('cms_column')->where($map)->find();
if (!$column) $this->error('该栏目不存在');
$model = Db::name('cms_model')->where('id', $column['model'])->find();
if ($model['type'] == 2) {
$cid_all = ColumnModel::getChildsId($id);
$cid_all[] = (int)$id;
$map = [
[$model['table'].'.trash', '=', 0],
[$model['table'].'.status', '=', 1],
[$model['table'].'.cid', 'in', $cid_all],
];
$data_list = Db::view($model['table'], true)
->view('admin_user', 'username', $model['table'].'.uid=admin_user.id', 'left')
->where($map)
->order('create_time desc')
->paginate(config('list_rows'));
$this->assign('model', $column['model']);
} else {
$cid_all = ColumnModel::getChildsId($id);
$cid_all[] = (int)$id;
$map = [
['cms_document.trash', '=', 0],
['cms_document.status', '=', 1],
['cms_document.cid', 'in', $cid_all],
];
$data_list = Db::view('cms_document', true)
->view('admin_user', 'username', 'cms_document.uid=admin_user.id', 'left')
->view($model['table'], '*', 'cms_document.id='. $model['table'] . '.aid', 'left')
->where($map)
->order('create_time desc')
->paginate(config('list_rows'));
$this->assign('model', '');
}
$this->assign('lists', $data_list);
$this->assign('pages', $data_list->render());
$this->assign('breadcrumb', $this->getBreadcrumb($id));
$this->assign('column_info', $column);
$template = $column['list_template'] == '' ? 'list' : substr($column['list_template'], 0, strpos($column['list_template'], '.'));
return $this->fetch($template);
}
/**
* 获取栏目面包屑导航
* @param $id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function getBreadcrumb($id)
{
$columns = ColumnModel::where('status', 1)->column('id,pid,name,url,target,type');
foreach ($columns as &$column) {
if ($column['type'] == 0) {
$column['url'] = url('cms/column/index', ['id' => $column['id']]);
}
}
return Tree::config(['title' => 'name'])->getParents($columns, $id);
}
}

View File

@ -0,0 +1,82 @@
<?php
namespace app\cms\home;
use app\index\controller\Home;
use think\Db;
use util\Tree;
/**
* 前台公共控制器
* @package app\cms\admin
*/
class Common extends Home
{
/**
* 初始化方法
* @author 蔡伟明 <314013107@qq.com>
*/
protected function initialize()
{
parent::initialize();
// 获取菜单
$this->getNav();
// 获取滚动图片
$this->assign('slider', $this->getSlider());
// 获取客服
$this->assign('support', $this->getSupport());
}
/**
* 获取导航
* @author 蔡伟明 <314013107@qq.com>
*/
private function getNav()
{
$list_nav = Db::name('cms_nav')->where('status', 1)->column('id,tag');
foreach ($list_nav as $id => $tag) {
$data_list = Db::view('cms_menu', true)
->view('cms_column', ['name' => 'column_name'], 'cms_menu.column=cms_column.id', 'left')
->view('cms_page', ['title' => 'page_title'], 'cms_menu.page=cms_page.id', 'left')
->where('cms_menu.nid', $id)
->where('cms_menu.status', 1)
->order('cms_menu.sort,cms_menu.pid,cms_menu.id')
->select();
foreach ($data_list as &$item) {
if ($item['type'] == 0) { // 栏目链接
$item['title'] = $item['column_name'];
$item['url'] = url('cms/column/index', ['id' => $item['column']]);
} elseif ($item['type'] == 1) { // 单页链接
$item['title'] = $item['page_title'];
$item['url'] = url('cms/page/detail', ['id' => $item['page']]);
} else {
if ($item['url'] != '#' && substr($item['url'], 0, 4) != 'http') {
$item['url'] = url($item['url']);
}
}
}
$this->assign($tag, Tree::toLayer($data_list));
}
}
/**
* 获取滚动图片
* @author 蔡伟明 <314013107@qq.com>
*/
private function getSlider()
{
return Db::name('cms_slider')->where('status', 1)->select();
}
/**
* 获取在线客服
* @author 蔡伟明 <314013107@qq.com>
*/
private function getSupport()
{
return Db::name('cms_support')->where('status', 1)->order('sort')->select();
}
}

View File

@ -0,0 +1,144 @@
<?php
namespace app\cms\home;
use app\cms\model\Column as ColumnModel;
use app\cms\model\Document as DocumentModel;
use util\Tree;
use think\Db;
/**
* 文档控制器
* @package app\cms\home
*/
class Document extends Common
{
/**
* 文档详情页
* @param null $id 文档id
* @param string $model 独立模型id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function detail($id = null, $model = '')
{
if ($id === null) $this->error('缺少参数');
if ($model != '') {
$table = get_model_table($model);
$map = [
$table.'.status' => 1,
$table.'.trash' => 0
];
} else {
$map = [
'cms_document.status' => 1,
'cms_document.trash' => 0
];
}
$info = DocumentModel::getOne($id, $model, $map);
if (isset($info['tags'])) {
$info['tags'] = explode(',', $info['tags']);
}
$this->assign('document', $info);
$this->assign('breadcrumb', $this->getBreadcrumb($info['cid']));
$this->assign('prev', $this->getPrev($id, $model));
$this->assign('next', $this->getNext($id, $model));
$template = $info['detail_template'] == '' ? 'detail' : substr($info['detail_template'], 0, strpos($info['detail_template'], '.'));
return $this->fetch($template);
}
/**
* 获取栏目面包屑导航
* @param int $id 栏目id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
private function getBreadcrumb($id)
{
$columns = ColumnModel::where('status', 1)->column('id,pid,name,url,target,type');
foreach ($columns as &$column) {
if ($column['type'] == 0) {
$column['url'] = url('cms/column/index', ['id' => $column['id']]);
}
}
return Tree::config(['title' => 'name'])->getParents($columns, $id);
}
/**
* 获取上一篇文档
* @param int $id 当前文档id
* @param string $model 独立模型id
* @author 蔡伟明 <314013107@qq.com>
* @return array|string|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
private function getPrev($id, $model = '')
{
if ($model == '') {
$cid = Db::name('cms_document')->where('id', $id)->value('cid');
$document = Db::name('cms_document')->where([
['status', '=', 1],
['trash', '=', 0],
['cid', '=', $cid],
['id', 'lt', $id]
])->order('id desc')->find();
} else {
$table = get_model_table($model);
$cid = Db::table($table)->where('id', $id)->value('cid');
$document = Db::table($table)->where([
['status', '=', 1],
['trash', '=', 0],
['cid', '=', $cid],
['id', 'lt', $id]
])->order('id desc')->find();
}
if ($document) {
$document['url'] = url('cms/document/detail', ['id' => $document['id'], 'model' => $model]);
}
return $document;
}
/**
* 获取下一篇文档
* @param int $id 当前文档id
* @param string $model 独立模型id
* @author 蔡伟明 <314013107@qq.com>
* @return array|string|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
private function getNext($id, $model = '')
{
if ($model == '') {
$cid = Db::name('cms_document')->where('id', $id)->value('cid');
$document = Db::name('cms_document')->where([
['status', '=', 1],
['trash', '=', 0],
['cid', '=', $cid],
['id', 'gt', $id]
])->find();
} else {
$table = get_model_table($model);
$cid = Db::table($table)->where('id', $id)->value('cid');
$document = Db::table($table)->where([
['status', '=', 1],
['trash', '=', 0],
['cid', '=', $cid],
['id', 'gt', $id]
])->find();
}
if ($document) {
$document['url'] = url('cms/document/detail', ['id' => $document['id'], 'model' => $model]);
}
return $document;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace app\cms\home;
/**
* 前台首页控制器
* @package app\cms\admin
*/
class Index extends Common
{
/**
* 首页
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function index()
{
return $this->fetch(); // 渲染模板
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace app\cms\home;
use app\cms\model\Page as PageModel;
/**
* 前台单页控制器
* @package app\cms\admin
*/
class Page extends Common
{
/**
* 单页详情
* @param null $id 单页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 detail($id = null)
{
$info = PageModel::where('status', 1)->find($id);
$info['url'] = url('cms/page/detail', ['id' => $info['id']]);
$info['tags'] = explode(',', $info['keywords']);
// 更新阅读量
PageModel::where('id', $id)->setInc('view');
$this->assign('page_info', $info);
return $this->fetch(); // 渲染模板
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace app\cms\home;
use think\Db;
/**
* 前台搜索控制器
* @package app\cms\admin
*/
class Search extends Common
{
/**
* 搜索列表
* @param string $keyword 关键词
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
* @throws \think\exception\DbException
*/
public function index($keyword = '')
{
if ($keyword == '') $this->error('请输入关键字');
$map = [
['cms_document.trash', '=', 0],
['cms_document.status', '=', 1],
['cms_document.title', 'like', "%$keyword%"]
];
$data_list = Db::view('cms_document', true)
->view('admin_user', 'username', 'cms_document.uid=admin_user.id', 'left')
->where($map)
->order('create_time desc')
->paginate(config('list_rows'));
$this->assign('keyword', $keyword);
$this->assign('lists', $data_list);
$this->assign('pages', $data_list->render());
return $this->fetch(); // 渲染模板
}
}

604
application/cms/info.php Normal file
View File

@ -0,0 +1,604 @@
<?php
// +----------------------------------------------------------------------
// | 海豚PHP框架 [ DolphinPHP ]
// +----------------------------------------------------------------------
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://dolphinphp.com
// +----------------------------------------------------------------------
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
/**
* 模块信息
*/
return [
// 模块名[必填]
'name' => 'cms',
// 模块标题[必填]
'title' => '门户',
// 模块唯一标识[必填],格式:模块名.开发者标识.module
'identifier' => 'cms.ming.module',
// 模块图标[选填]
'icon' => 'fa fa-fw fa-newspaper-o',
// 模块描述[选填]
'description' => '门户模块',
// 开发者[必填]
'author' => 'CaiWeiMing',
// 开发者网址[选填]
'author_url' => 'http://www.dolphinphp.com',
// 版本[必填],格式采用三段式:主版本号.次版本号.修订版本号
'version' => '1.0.0',
// 模块依赖[可选],格式[[模块名, 模块唯一标识, 依赖版本, 对比方式]]
'need_module' => [
['admin', 'admin.dolphinphp.module', '1.0.0']
],
// 插件依赖[可选],格式[[插件名, 插件唯一标识, 依赖版本, 对比方式]]
'need_plugin' => [],
// 数据表[有数据库表时必填]
'tables' => [
'cms_advert',
'cms_advert_type',
'cms_column',
'cms_document',
'cms_document_article',
'cms_field',
'cms_link',
'cms_menu',
'cms_model',
'cms_nav',
'cms_page',
'cms_slider',
'cms_support',
],
// 原始数据库表前缀
// 用于在导入模块sql时将原有的表前缀转换成系统的表前缀
// 一般模块自带sql文件时才需要配置
'database_prefix' => 'dp_',
// 模块参数配置
'config' => [
['text', 'summary', '默认摘要字数', '发布文章时如果没有填写摘要则自动获取文档内容为摘要。如果此处不填写或填写0则不提取摘要。', 0],
['ckeditor', 'contact', '联系方式', '', '<div class="font-s13 push"><strong>河源市卓锐科技有限公司</strong><br />
地址河源市江东新区东环路汇通苑D3-H232<br />
电话0762-8910006<br />
邮箱admin@zrthink.com</div>'],
['textarea', 'meta_head', '顶部代码', '代码会放在 <code>&lt;/head&gt;</code> 标签以上'],
['textarea', 'meta_foot', '底部代码', '代码会放在 <code>&lt;/body&gt;</code> 标签以上'],
['radio', 'support_status', '在线客服', '', ['禁用', '启用'], 1],
['colorpicker', 'support_color', '在线客服配色', '', 'rgba(0,158,232,1)'],
['image', 'support_wx', '在线客服微信二维码', '在线客服微信二维码'],
['ckeditor', 'support_extra', '在线客服额外内容', '在线客服额外内容,可填写电话或其他说明'],
],
// 行为配置
'action' => [
[
'module' => 'cms',
'name' => 'slider_delete',
'title' => '删除滚动图片',
'remark' => '删除滚动图片',
'rule' => '',
'log' => '[user|get_nickname] 删除了滚动图片:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'slider_edit',
'title' => '编辑滚动图片',
'remark' => '编辑滚动图片',
'rule' => '',
'log' => '[user|get_nickname] 编辑了滚动图片:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'slider_add',
'title' => '添加滚动图片',
'remark' => '添加滚动图片',
'rule' => '',
'log' => '[user|get_nickname] 添加了滚动图片:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'document_delete',
'title' => '删除文档',
'remark' => '删除文档',
'rule' => '',
'log' => '[user|get_nickname] 删除了文档:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'document_restore',
'title' => '还原文档',
'remark' => '还原文档',
'rule' => '',
'log' => '[user|get_nickname] 还原了文档:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'nav_disable',
'title' => '禁用导航',
'remark' => '禁用导航',
'rule' => '',
'log' => '[user|get_nickname] 禁用了导航:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'nav_enable',
'title' => '启用导航',
'remark' => '启用导航',
'rule' => '',
'log' => '[user|get_nickname] 启用了导航:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'nav_delete',
'title' => '删除导航',
'remark' => '删除导航',
'rule' => '',
'log' => '[user|get_nickname] 删除了导航:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'nav_edit',
'title' => '编辑导航',
'remark' => '编辑导航',
'rule' => '',
'log' => '[user|get_nickname] 编辑了导航:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'nav_add',
'title' => '添加导航',
'remark' => '添加导航',
'rule' => '',
'log' => '[user|get_nickname] 添加了导航:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'model_disable',
'title' => '禁用内容模型',
'remark' => '禁用内容模型',
'rule' => '',
'log' => '[user|get_nickname] 禁用了内容模型:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'model_enable',
'title' => '启用内容模型',
'remark' => '启用内容模型',
'rule' => '',
'log' => '[user|get_nickname] 启用了内容模型:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'model_delete',
'title' => '删除内容模型',
'remark' => '删除内容模型',
'rule' => '',
'log' => '[user|get_nickname] 删除了内容模型:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'model_edit',
'title' => '编辑内容模型',
'remark' => '编辑内容模型',
'rule' => '',
'log' => '[user|get_nickname] 编辑了内容模型:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'model_add',
'title' => '添加内容模型',
'remark' => '添加内容模型',
'rule' => '',
'log' => '[user|get_nickname] 添加了内容模型:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'menu_disable',
'title' => '禁用导航菜单',
'remark' => '禁用导航菜单',
'rule' => '',
'log' => '[user|get_nickname] 禁用了导航菜单:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'menu_enable',
'title' => '启用导航菜单',
'remark' => '启用导航菜单',
'rule' => '',
'log' => '[user|get_nickname] 启用了导航菜单:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'menu_delete',
'title' => '删除导航菜单',
'remark' => '删除导航菜单',
'rule' => '',
'log' => '[user|get_nickname] 删除了导航菜单:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'menu_edit',
'title' => '编辑导航菜单',
'remark' => '编辑导航菜单',
'rule' => '',
'log' => '[user|get_nickname] 编辑了导航菜单:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'menu_add',
'title' => '添加导航菜单',
'remark' => '添加导航菜单',
'rule' => '',
'log' => '[user|get_nickname] 添加了导航菜单:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'link_disable',
'title' => '禁用友情链接',
'remark' => '禁用友情链接',
'rule' => '',
'log' => '[user|get_nickname] 禁用了友情链接:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'link_enable',
'title' => '启用友情链接',
'remark' => '启用友情链接',
'rule' => '',
'log' => '[user|get_nickname] 启用了友情链接:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'link_delete',
'title' => '删除友情链接',
'remark' => '删除友情链接',
'rule' => '',
'log' => '[user|get_nickname] 删除了友情链接:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'link_edit',
'title' => '编辑友情链接',
'remark' => '编辑友情链接',
'rule' => '',
'log' => '[user|get_nickname] 编辑了友情链接:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'link_add',
'title' => '添加友情链接',
'remark' => '添加友情链接',
'rule' => '',
'log' => '[user|get_nickname] 添加了友情链接:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'field_disable',
'title' => '禁用模型字段',
'remark' => '禁用模型字段',
'rule' => '',
'log' => '[user|get_nickname] 禁用了模型字段:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'field_enable',
'title' => '启用模型字段',
'remark' => '启用模型字段',
'rule' => '',
'log' => '[user|get_nickname] 启用了模型字段:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'field_delete',
'title' => '删除模型字段',
'remark' => '删除模型字段',
'rule' => '',
'log' => '[user|get_nickname] 删除了模型字段:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'field_edit',
'title' => '编辑模型字段',
'remark' => '编辑模型字段',
'rule' => '',
'log' => '[user|get_nickname] 编辑了模型字段:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'field_add',
'title' => '添加模型字段',
'remark' => '添加模型字段',
'rule' => '',
'log' => '[user|get_nickname] 添加了模型字段:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'column_disable',
'title' => '禁用栏目',
'remark' => '禁用栏目',
'rule' => '',
'log' => '[user|get_nickname] 禁用了栏目:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'column_enable',
'title' => '启用栏目',
'remark' => '启用栏目',
'rule' => '',
'log' => '[user|get_nickname] 启用了栏目:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'column_delete',
'title' => '删除栏目',
'remark' => '删除栏目',
'rule' => '',
'log' => '[user|get_nickname] 删除了栏目:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'column_edit',
'title' => '编辑栏目',
'remark' => '编辑栏目',
'rule' => '',
'log' => '[user|get_nickname] 编辑了栏目:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'column_add',
'title' => '添加栏目',
'remark' => '添加栏目',
'rule' => '',
'log' => '[user|get_nickname] 添加了栏目:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'advert_type_disable',
'title' => '禁用广告分类',
'remark' => '禁用广告分类',
'rule' => '',
'log' => '[user|get_nickname] 禁用了广告分类:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'advert_type_enable',
'title' => '启用广告分类',
'remark' => '启用广告分类',
'rule' => '',
'log' => '[user|get_nickname] 启用了广告分类:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'advert_type_delete',
'title' => '删除广告分类',
'remark' => '删除广告分类',
'rule' => '',
'log' => '[user|get_nickname] 删除了广告分类:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'advert_type_edit',
'title' => '编辑广告分类',
'remark' => '编辑广告分类',
'rule' => '',
'log' => '[user|get_nickname] 编辑了广告分类:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'advert_type_add',
'title' => '添加广告分类',
'remark' => '添加广告分类',
'rule' => '',
'log' => '[user|get_nickname] 添加了广告分类:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'advert_disable',
'title' => '禁用广告',
'remark' => '禁用广告',
'rule' => '',
'log' => '[user|get_nickname] 禁用了广告:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'advert_enable',
'title' => '启用广告',
'remark' => '启用广告',
'rule' => '',
'log' => '[user|get_nickname] 启用了广告:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'advert_delete',
'title' => '删除广告',
'remark' => '删除广告',
'rule' => '',
'log' => '[user|get_nickname] 删除了广告:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'advert_edit',
'title' => '编辑广告',
'remark' => '编辑广告',
'rule' => '',
'log' => '[user|get_nickname] 编辑了广告:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'advert_add',
'title' => '添加广告',
'remark' => '添加广告',
'rule' => '',
'log' => '[user|get_nickname] 添加了广告:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'document_disable',
'title' => '禁用文档',
'remark' => '禁用文档',
'rule' => '',
'log' => '[user|get_nickname] 禁用了文档:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'document_enable',
'title' => '启用文档',
'remark' => '启用文档',
'rule' => '',
'log' => '[user|get_nickname] 启用了文档:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'document_trash',
'title' => '回收文档',
'remark' => '回收文档',
'rule' => '',
'log' => '[user|get_nickname] 回收了文档:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'document_edit',
'title' => '编辑文档',
'remark' => '编辑文档',
'rule' => '',
'log' => '[user|get_nickname] 编辑了文档:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'document_add',
'title' => '添加文档',
'remark' => '添加文档',
'rule' => '',
'log' => '[user|get_nickname] 添加了文档:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'slider_enable',
'title' => '启用滚动图片',
'remark' => '启用滚动图片',
'rule' => '',
'log' => '[user|get_nickname] 启用了滚动图片:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'slider_disable',
'title' => '禁用滚动图片',
'remark' => '禁用滚动图片',
'rule' => '',
'log' => '[user|get_nickname] 禁用了滚动图片:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'support_add',
'title' => '添加客服',
'remark' => '添加客服',
'rule' => '',
'log' => '[user|get_nickname] 添加了客服:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'support_edit',
'title' => '编辑客服',
'remark' => '编辑客服',
'rule' => '',
'log' => '[user|get_nickname] 编辑了客服:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'support_delete',
'title' => '删除客服',
'remark' => '删除客服',
'rule' => '',
'log' => '[user|get_nickname] 删除了客服:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'support_enable',
'title' => '启用客服',
'remark' => '启用客服',
'rule' => '',
'log' => '[user|get_nickname] 启用了客服:[details]',
'status' => 1,
],
[
'module' => 'cms',
'name' => 'support_disable',
'title' => '禁用客服',
'remark' => '禁用客服',
'rule' => '',
'log' => '[user|get_nickname] 禁用了客服:[details]',
'status' => 1,
]
],
// 授权配置
'access' => [
'column' => [
'title' => '栏目授权',
'nodes' => [
'group' => 'column',
'table_name' => 'cms_column',
'primary_key' => 'id',
'parent_id' => 'pid',
'node_name' => 'name',
]
],
],
];

888
application/cms/menus.php Normal file
View File

@ -0,0 +1,888 @@
<?php
// +----------------------------------------------------------------------
// | 海豚PHP框架 [ DolphinPHP ]
// +----------------------------------------------------------------------
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://dolphinphp.com
// +----------------------------------------------------------------------
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
/**
* 菜单信息
*/
return [
[
'title' => '门户',
'icon' => 'fa fa-fw fa-newspaper-o',
'url_type' => 'module_admin',
'url_value' => 'cms/index/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '常用操作',
'icon' => 'fa fa-fw fa-folder-open-o',
'url_type' => 'module_admin',
'url_value' => '',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '仪表盘',
'icon' => 'fa fa-fw fa-tachometer',
'url_type' => 'module_admin',
'url_value' => 'cms/index/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '发布文档',
'icon' => 'fa fa-fw fa-plus',
'url_type' => 'module_admin',
'url_value' => 'cms/document/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '文档列表',
'icon' => 'fa fa-fw fa-list',
'url_type' => 'module_admin',
'url_value' => 'cms/document/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/document/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/document/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/document/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/document/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/document/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
],
],
[
'title' => '单页管理',
'icon' => 'fa fa-fw fa-file-word-o',
'url_type' => 'module_admin',
'url_value' => 'cms/page/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '新增',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/page/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/page/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/page/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/page/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/page/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/page/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
],
],
[
'title' => '回收站',
'icon' => 'fa fa-fw fa-recycle',
'url_type' => 'module_admin',
'url_value' => 'cms/recycle/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/recycle/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '还原',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/recycle/restore',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
],
],
],
],
[
'title' => '内容管理',
'icon' => 'fa fa-fw fa-th-list',
'url_type' => 'module_admin',
'url_value' => '',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [],
],
[
'title' => '营销管理',
'icon' => 'fa fa-fw fa-money',
'url_type' => 'module_admin',
'url_value' => '',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '广告管理',
'icon' => 'fa fa-fw fa-handshake-o',
'url_type' => 'module_admin',
'url_value' => 'cms/advert/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '新增',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '广告分类',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert_type/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '新增',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert_type/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert_type/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert_type/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert_type/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert_type/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/advert_type/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
],
],
],
],
[
'title' => '滚动图片',
'icon' => 'fa fa-fw fa-photo',
'url_type' => 'module_admin',
'url_value' => 'cms/slider/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '新增',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/slider/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/slider/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/slider/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/slider/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/slider/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/slider/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
],
],
[
'title' => '友情链接',
'icon' => 'fa fa-fw fa-link',
'url_type' => 'module_admin',
'url_value' => 'cms/link/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '新增',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/link/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/link/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/link/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/link/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/link/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/link/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
],
],
[
'title' => '客服管理',
'icon' => 'fa fa-fw fa-commenting',
'url_type' => 'module_admin',
'url_value' => 'cms/support/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '新增',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/support/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/support/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/support/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/support/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/support/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/support/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
],
],
],
],
[
'title' => '门户设置',
'icon' => 'fa fa-fw fa-sliders',
'url_type' => 'module_admin',
'url_value' => '',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '栏目分类',
'icon' => 'fa fa-fw fa-sitemap',
'url_type' => 'module_admin',
'url_value' => 'cms/column/index',
'url_target' => '_self',
'online_hide' => 1,
'sort' => 100,
'child' => [
[
'title' => '新增',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/column/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/column/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/column/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/column/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/column/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/column/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
],
],
[
'title' => '内容模型',
'icon' => 'fa fa-fw fa-th-large',
'url_type' => 'module_admin',
'url_value' => 'cms/model/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '新增',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/model/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/model/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/model/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/model/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/model/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/model/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '字段管理',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/field/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '新增',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/field/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/field/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/field/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/field/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/field/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/field/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
],
],
],
],
[
'title' => '导航管理',
'icon' => 'fa fa-fw fa-map-signs',
'url_type' => 'module_admin',
'url_value' => 'cms/nav/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '新增',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/nav/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/nav/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/nav/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/nav/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/nav/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/nav/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '菜单管理',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/menu/index',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
'child' => [
[
'title' => '新增',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/menu/add',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/menu/edit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '删除',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/menu/delete',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '启用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/menu/enable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '禁用',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/menu/disable',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
[
'title' => '快速编辑',
'icon' => '',
'url_type' => 'module_admin',
'url_value' => 'cms/menu/quickedit',
'url_target' => '_self',
'online_hide' => 0,
'sort' => 100,
],
],
],
],
],
],
],
],
],
];

View File

@ -0,0 +1,35 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
/**
* 广告模型
* @package app\cms\model
*/
class Advert extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_advert';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
// 定义修改器
public function setStartTimeAttr($value)
{
return $value != '' ? strtotime($value) : 0;
}
public function setEndTimeAttr($value)
{
return $value != '' ? strtotime($value) : 0;
}
public function getStartTimeAttr($value)
{
return $value != 0 ? date('Y-m-d', $value) : '';
}
public function getEndTimeAttr($value)
{
return $value != 0 ? date('Y-m-d', $value) : '';
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
/**
* 广告分类模型
* @package app\cms\model
*/
class AdvertType extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_advert_type';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
}

View File

@ -0,0 +1,123 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
use util\Tree;
/**
* 栏目模型
* @package app\cms\model
*/
class Column extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_column';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
/**
* 标题获取器
* @param $value
* @param $data
* @author 蔡伟明 <314013107@qq.com>
*/
protected function getTitleAttr($value, $data) {
switch ($data['type']) {
case 0: // 栏目
break;
case 1: // 单页
break;
}
}
/**
* 获取栏目列表
* @author 蔡伟明 <314013107@qq.com>
* @return array|mixed
*/
public static function getList()
{
$data_list = cache('cms_column_list');
if (!$data_list) {
$data_list = self::where('status', 1)->column(true, 'id');
// 非开发模式,缓存数据
if (config('develop_mode') == 0) {
cache('cms_column_list', $data_list);
}
}
return $data_list;
}
/**
* 获取树状栏目
* @param int $id 需要隐藏的栏目id
* @param string $default 默认第一个节点项默认为“顶级栏目”如果为false则不显示也可传入其他名称
* @author 蔡伟明 <314013107@qq.com>
* @return array|mixed
*/
public static function getTreeList($id = 0, $default = '')
{
$result[0] = '顶级栏目';
// 排除指定节点及其子节点
$where = [
['status', '=', 1]
];
if ($id !== 0) {
$hide_ids = array_merge([$id], self::getChildsId($id));
$where[] = ['id', 'not in', $hide_ids];
}
$data_list = Tree::config(['title' => 'name'])->toList(self::where($where)->order('pid,id')->column('id,pid,name'));
foreach ($data_list as $item) {
$result[$item['id']] = $item['title_display'];
}
// 设置默认节点项标题
if ($default != '') {
$result[0] = $default;
}
// 隐藏默认节点项
if ($default === false) {
unset($result[0]);
}
return $result;
}
/**
* 获取所有子栏目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;
}
/**
* 获取指定栏目数据
* @param int $cid 栏目id
* @author 蔡伟明 <314013107@qq.com>
* @return mixed|static
*/
public static function getInfo($cid = 0)
{
$result = cache('cms_column_info_'. $cid);
if (!$result) {
$result = self::get($cid);
// 非开发模式,缓存数据
if (config('develop_mode') == 0) {
cache('cms_column_info_'. $cid, $result);
}
}
return $result;
}
}

View File

@ -0,0 +1,213 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
use think\Db;
use app\cms\model\Field as FieldModel;
/**
* 文档模型
* @package app\cms\model
*/
class Document extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_document';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
/**
* 获取文档列表
* @param array $map 筛选条件
* @param array $order 排序
* @author 蔡伟明 <314013107@qq.com>
* @return \think\Paginator
* @throws \think\exception\DbException
*/
public static function getList($map = [], $order = [])
{
$data_list = self::view('cms_document', true)
->view("cms_column", ['name' => 'column_name'], 'cms_column.id=cms_document.cid', 'left')
->view("admin_user", 'username', 'admin_user.id=cms_document.uid', 'left')
->where($map)
->order($order)
->paginate();
return $data_list;
}
/**
* 获取单篇文档
* @param string $id 文档id
* @param string $model 独立模型id
* @param array $map 查询条件
* @author 蔡伟明 <314013107@qq.com>
* @return array|string|ThinkModel|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function getOne($id = '', $model = '', $map = [])
{
if ($model == '') {
$document = self::get($id);
$extra_table = get_model_table($document['model']);
$data = self::view('cms_document', true);
if ($extra_table != '') {
$data = $data->view($extra_table, true, 'cms_document.id='.$extra_table.'.aid', 'left');
}
return $data->view("cms_column", ['name' => 'column_name', 'list_template', 'detail_template'], 'cms_column.id=cms_document.cid', 'left')
->view("admin_user", 'username', 'admin_user.id=cms_document.uid', 'left')
->where('cms_document.id', $id)
->where($map)
->find();
} else {
$table = get_model_table($model);
return Db::view($table, true)
->view("cms_column", ['name' => 'column_name', 'list_template', 'detail_template'], 'cms_column.id='.$table.'.cid', 'left')
->where($table.'.id', $id)
->where($map)
->find();
}
}
/**
* 新增或更新文档
* @author 蔡伟明 <314013107@qq.com>
* @return bool
* @throws \think\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
* @throws \think\exception\PDOException
*/
public function saveData()
{
$data = request()->post();
$data['uid'] = UID;
// 文档模型
$model = Db::name('cms_model')->where('id', $data['model'])->find();
if ($model['type'] != 2 && empty($data['summary']) && config('cms_config.summary') > 0) {
$data['summary'] = mb_substr(strip_tags($data['content']), 0, config('cms_config.summary'), 'utf-8');
}
// 处理自定义属性
if (isset($data['flag'])) {
$data['flag'] = implode(',', $data['flag']);
} else {
$data['flag'] = '';
}
// 验证基础内容
if ($data['title'] == '') {
$this->error = '标题不能为空';
return false;
}
// 处理特殊字段类型
$fields = FieldModel::where('model', $data['model'])->where('status', 1)->column('name,type');
foreach ($fields 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;
}
}
}
if (empty($data['id'])) {
if ($model['type'] == 2) {
// 新增独立模型文档
$data['create_time'] = request()->time();
$data['update_time'] = request()->time();
$insert_id = Db::table($model['table'])->insertGetId($data);
if (false === $insert_id) {
$this->error = '新增失败';
return false;
} else {
// 记录行为
action_log('document_add', $model['table'], $insert_id, UID, $data['title']);
return true;
}
} else {
// 新增文档基础内容
if ($document = self::create($data)) {
// 新增文档扩展内容
if ($model['table'] != '') {
$data['aid'] = $document['id'];
if (false === Db::table($model['table'])->insert($data)) {
// 删除已添加的基础内容
self::destroy($document['id']);
$this->error = '新增扩展内容出错';
return false;
}
}
// 记录行为
action_log('document_add', 'cms_document', $document['id'], UID, $document['title']);
return true;
} else {
$this->error = '新增基础内容出错';
return false;
}
}
} else {
// 更新独立模型文档
if ($model['type'] == 2) {
// 新增独立模型文档
$data['update_time'] = request()->time();
if (false === Db::table($model['table'])->update($data)) {
$this->error = '编辑失败';
return false;
} else {
// 记录行为
action_log('document_edit', $model['table'], $data['id'], UID, $data['title']);
return true;
}
} else {
// 更新文档基础内容
if (self::update($data)) {
// 更新文档扩展内容
$data['aid'] = $data['id'];
if (false !== Db::table($model['table'])->update($data)) {
// 记录行为
action_log('document_edit', 'cms_document', $data['id'], UID, $data['title']);
return true;
} else {
$this->error = '更新扩展内容出错';
return false;
}
} else {
$this->error = '更新基础内容出错';
return false;
}
}
}
}
}

View File

@ -0,0 +1,140 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
use think\Db;
/**
* 字段模型
* @package app\cms\model
*/
class Field extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_field';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
// 当前表名
protected $_table_name = '';
/**
* 创建字段
* @param null $field 字段数据
* @author 蔡伟明 <314013107@qq.com>
* @return bool
*/
public function newField($field = null)
{
if ($field === null) {
$this->error = '缺少参数';
return false;
}
if ($this->tableExist($field['model'])) {
$sql = <<<EOF
ALTER TABLE `{$this->_table_name}`
ADD COLUMN `{$field['name']}` {$field['define']} COMMENT '{$field['title']}';
EOF;
} else {
$mdoel_title = get_model_title($field['model']);
// 新建普通扩展表
$sql = <<<EOF
CREATE TABLE IF NOT EXISTS `{$this->_table_name}` (
`aid` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '文档id' ,
`{$field['name']}` {$field['define']} COMMENT '{$field['title']}' ,
PRIMARY KEY (`aid`)
)
ENGINE=MyISAM
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
CHECKSUM=0
ROW_FORMAT=DYNAMIC
DELAY_KEY_WRITE=0
COMMENT='{$mdoel_title}模型扩展表'
;
EOF;
}
try {
Db::execute($sql);
} catch(\Exception $e) {
$this->error = '字段添加失败';
return false;
}
return true;
}
/**
* 更新字段
* @param null $field 字段数据
* @author 蔡伟明 <314013107@qq.com>
* @return bool
*/
public function updateField($field = null)
{
if ($field === null) {
return false;
}
// 获取原字段名
$field_name = $this->where('id', $field['id'])->value('name');
if ($this->tableExist($field['model'])) {
$sql = <<<EOF
ALTER TABLE `{$this->_table_name}`
CHANGE COLUMN `{$field_name}` `{$field['name']}` {$field['define']} COMMENT '{$field['title']}';
EOF;
try {
Db::execute($sql);
} catch(\Exception $e) {
return false;
}
return true;
} else {
return false;
}
}
/**
* 删除字段
* @param null $field 字段数据
* @author 蔡伟明 <314013107@qq.com>
* @return bool
*/
public function deleteField($field = null)
{
if ($field === null) {
return false;
}
if ($this->tableExist($field['model'])) {
$sql = <<<EOF
ALTER TABLE `{$this->_table_name}`
DROP COLUMN `{$field['name']}`;
EOF;
try {
Db::execute($sql);
} catch(\Exception $e) {
return false;
}
return true;
} else {
return false;
}
}
/**
* 检查表是否存在
* @param string $model 文档模型id
* @author 蔡伟明 <314013107@qq.com>
* @return bool
*/
private function tableExist($model = '')
{
$this->_table_name = strtolower(get_model_table($model));
return true == Db::query("SHOW TABLES LIKE '{$this->_table_name}'");
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
/**
* 友情链接模型
* @package app\cms\model
*/
class Link extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_link';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
}

View File

@ -0,0 +1,17 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
/**
* 菜单模型
* @package app\cms\model
*/
class Menu extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_menu';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
}

View File

@ -0,0 +1,227 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
use think\Db;
/**
* 内容模型
* @package app\cms\model
*/
class Model extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_model';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
/**
* 获取内容模型列表
* @author 蔡伟明 <314013107@qq.com>
* @return array|mixed
*/
public static function getList()
{
$data_list = cache('cms_model_list');
if (!$data_list) {
$data_list = self::where('status', 1)->column(true, 'id');
// 非开发模式,缓存数据
if (config('develop_mode') == 0) {
cache('cms_model_list', $data_list);
}
}
return $data_list;
}
/**
* 获取内容模型标题列表只含id和title
* @param array $map 筛选条件
* @author 蔡伟明 <314013107@qq.com>
* @return array|mixed
*/
public static function getTitleList($map = [])
{
return self::where('status', 1)->where($map)->column('id,title');
}
/**
* 删除附加表
* @param null $model 内容模型id
* @author 蔡伟明 <314013107@qq.com>
* @return bool
*/
public static function deleteTable($model = null)
{
if ($model === null) {
return false;
}
$table_name = self::where('id', $model)->value('table');
return false !== Db::execute("DROP TABLE IF EXISTS `{$table_name}`");
}
/**
* 创建独立模型表
* @param mixed $data 模型数据
* @author 蔡伟明 <314013107@qq.com>
* @return bool
*/
public static function createTable($data)
{
if ($data['type'] == 2) {
// 新建独立扩展表
$sql = <<<EOF
CREATE TABLE IF NOT EXISTS `{$data['table']}` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '文档id' ,
`cid` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '栏目id' ,
`uid` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户id' ,
`model` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '模型id' ,
`title` varchar(256) NOT NULL DEFAULT '' COMMENT '标题' ,
`create_time` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间' ,
`update_time` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '更新时间' ,
`sort` int(11) NOT NULL DEFAULT 100 COMMENT '排序' ,
`status` tinyint(2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '状态' ,
`view` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '点击量' ,
`trash` tinyint(2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '回收站' ,
PRIMARY KEY (`id`)
)
ENGINE=MyISAM
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
CHECKSUM=0
ROW_FORMAT=DYNAMIC
DELAY_KEY_WRITE=0
COMMENT='{$data['title']}模型表'
;
EOF;
} else {
// 新建普通扩展表
$sql = <<<EOF
CREATE TABLE IF NOT EXISTS `{$data['table']}` (
`aid` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '文档id' ,
PRIMARY KEY (`aid`)
)
ENGINE=MyISAM
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
CHECKSUM=0
ROW_FORMAT=DYNAMIC
DELAY_KEY_WRITE=0
COMMENT='{$data['title']}模型扩展表'
;
EOF;
}
try {
Db::execute($sql);
} catch(\Exception $e) {
return false;
}
if ($data['type'] == 2) {
// 添加默认字段
$default = [
'model' => $data['id'],
'level' => '',
'create_time' => request()->time(),
'update_time' => request()->time(),
'status' => 1
];
$data = [
[
'name' => 'id',
'title' => '文档id',
'define' => 'int(11) UNSIGNED NOT NULL',
'type' => 'text',
'show' => 0
],
[
'name' => 'cid',
'title' => '栏目',
'define' => 'int(11) UNSIGNED NOT NULL',
'type' => 'static',
'show' => 0,
'value' => 0,
],
[
'name' => 'uid',
'title' => '用户id',
'define' => 'int(11) UNSIGNED NOT NULL',
'type' => 'text',
'show' => 0,
'value' => 0,
],
[
'name' => 'model',
'title' => '文档模型',
'define' => 'int(11) UNSIGNED NOT NULL',
'type' => 'text',
'show' => 0,
'value' => 0,
],
[
'name' => 'title',
'title' => '标题',
'define' => 'varchar(256) NOT NULL',
'type' => 'text',
'show' => 1
],
[
'name' => 'create_time',
'title' => '创建时间',
'define' => 'int(11) UNSIGNED NOT NULL',
'type' => 'datetime',
'show' => 0,
'value' => 0,
],
[
'name' => 'update_time',
'title' => '更新时间',
'define' => 'int(11) UNSIGNED NOT NULL',
'type' => 'datetime',
'show' => 0,
'value' => 0,
],
[
'name' => 'sort',
'title' => '排序',
'define' => 'int(11) UNSIGNED NOT NULL',
'type' => 'text',
'show' => 1,
'value' => 100,
],
[
'name' => 'status',
'title' => '状态',
'define' => 'tinyint(2) NOT NULL',
'type' => 'radio',
'show' => 1,
'value' => 1,
'options' => '0:禁用
1:启用'
],
[
'name' => 'view',
'title' => '点击量',
'define' => 'int(11) UNSIGNED NOT NULL',
'type' => 'text',
'show' => 0,
'value' => 0
],
[
'name' => 'trash',
'title' => '回收站',
'define' => 'tinyint(2) NOT NULL',
'type' => 'radio',
'show' => 0,
'value' => 0
]
];
foreach ($data as $item) {
$item = array_merge($item, $default);
Db::name('cms_field')->insert($item);
}
}
return true;
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
/**
* 导航模型
* @package app\cms\model
*/
class Nav extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_nav';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
}

View File

@ -0,0 +1,35 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
/**
* 单页模型
* @package app\cms\model
*/
class Page extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_page';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
/**
* 获取单页标题列表
* @author 蔡伟明 <314013107@qq.com>
* @return array|mixed
*/
public static function getTitleList()
{
$result = cache('cms_page_title_list');
if (!$result) {
$result = self::where('status', 1)->column('id,title');
// 非开发模式,缓存数据
if (config('develop_mode') == 0) {
cache('cms_page_title_list', $result);
}
}
return $result;
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
/**
* 滚动图片模型
* @package app\cms\model
*/
class Slider extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_slider';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
}

View File

@ -0,0 +1,17 @@
<?php
namespace app\cms\model;
use think\Model as ThinkModel;
/**
* 客服模型
* @package app\cms\model
*/
class Support extends ThinkModel
{
// 设置当前模型对应的完整数据表名称
protected $name = 'cms_support';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
}

View File

@ -0,0 +1,65 @@
body,
h1,
h2,
h3,
h4,
h5,
h6,
.h1,
.h2,
.h3,
.h4,
.h5,
.h6,
.select2-container--default .select2-search--inline .select2-search__field{
font-family: "Microsoft Yahei", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
/* 单页 */
.document-content p img,
.page-content p img{
width: 100%!important;
}
/* 客服 */
.support {
position: fixed;
top: 200px;
right: 0;
overflow: hidden;
}
.support .support-tag{
width: 38px;
text-align: center;
padding: 8px 10px;
float: left;
background-color: #009EE8;
color: #fff;
margin-top: 20px;
}
.support .support-content{
width: 200px;
float: right;
background: #009EE8;
display: none;
}
.support .support-item{
padding: 5px 0;
border-bottom: 1px dotted #ccc;
}
.support .support-item a{
padding-right: 5px;
}
.support .support-content-inner{
background-color: #fff;
padding: 10px;
margin: 8px;
min-height: 200px;
}
.support .support-wx img{
width: 100%;
}
.support .support-extra{
padding-top: 10px;
border-bottom: 1px dotted #ccc;
margin-bottom: 10px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -0,0 +1,47 @@
/*
* Document : field.js
* Author : CaiWeiMing <314013107@qq.com>
*/
jQuery(function () {
// 字段定义列表
var $field_define_list = {
text: "varchar(128) NOT NULL",
textarea: "varchar(256) NOT NULL",
static: "varchar(128) NOT NULL",
password: "varchar(128) NOT NULL",
checkbox: "varchar(32) NOT NULL",
radio: "varchar(32) NOT NULL",
date: "int(11) UNSIGNED NOT NULL",
time: "int(11) UNSIGNED NOT NULL",
datetime: "int(11) UNSIGNED NOT NULL",
hidden: "varchar(32) NOT NULL",
switch: "varchar(16) NOT NULL",
array: "varchar(32) NOT NULL",
select: "varchar(32) NOT NULL",
linkage: "varchar(32) NOT NULL",
linkages: "varchar(32) NOT NULL",
image: "int(11) UNSIGNED NOT NULL",
images: "varchar(64) NOT NULL",
file: "int(11) UNSIGNED NOT NULL",
files: "varchar(64) NOT NULL",
ueditor: "text NOT NULL",
wangeditor: "text NOT NULL",
editormd: "text NOT NULL",
ckeditor: "text NOT NULL",
summernote: "text NOT NULL",
icon: "varchar(64) NOT NULL",
tags: "varchar(128) NOT NULL",
number: "int(11) UNSIGNED NOT NULL",
bmap: "varchar(32) NOT NULL",
colorpicker: "varchar(32) NOT NULL",
jcrop: "int(11) UNSIGNED NOT NULL",
masked: "varchar(64) NOT NULL",
range: "varchar(128) NOT NULL"
};
// 选择自动类型,自动填写字段定义
var $field_define = jQuery('input[name=define]');
jQuery('select[name=type]').change(function () {
$field_define.val($field_define_list[$(this).val()] || '');
});
});

View File

@ -0,0 +1,320 @@
-- -----------------------------
-- 导出时间 `2016-12-13 22:26:46`
-- -----------------------------
-- -----------------------------
-- 表结构 `dp_cms_advert`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_advert`;
CREATE TABLE `dp_cms_advert` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`typeid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分类id',
`tagname` varchar(30) NOT NULL DEFAULT '' COMMENT '广告位标识',
`ad_type` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '广告类型',
`timeset` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '时间限制:0-永不过期,1-在设内时间内有效',
`start_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '开始时间',
`end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '结束时间',
`name` varchar(60) NOT NULL DEFAULT '' COMMENT '广告位名称',
`content` text NOT NULL COMMENT '广告内容',
`expcontent` text NOT NULL COMMENT '过期显示内容',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='广告表';
-- -----------------------------
-- 表数据 `dp_cms_advert`
-- -----------------------------
-- -----------------------------
-- 表结构 `dp_cms_advert_type`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_advert_type`;
CREATE TABLE `dp_cms_advert_type` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL DEFAULT '' COMMENT '分类名称',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='广告分类表';
-- -----------------------------
-- 表数据 `dp_cms_advert_type`
-- -----------------------------
-- -----------------------------
-- 表结构 `dp_cms_column`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_column`;
CREATE TABLE `dp_cms_column` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`pid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '父级id',
`name` varchar(32) NOT NULL DEFAULT '' COMMENT '栏目名称',
`model` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '文档模型id',
`url` varchar(255) NOT NULL DEFAULT '' COMMENT '链接',
`target` varchar(16) NOT NULL DEFAULT '_self' COMMENT '链接打开方式',
`content` text NOT NULL COMMENT '内容',
`icon` varchar(64) NOT NULL DEFAULT '' COMMENT '字体图标',
`index_template` varchar(32) NOT NULL DEFAULT '' COMMENT '封面模板',
`list_template` varchar(32) NOT NULL DEFAULT '' COMMENT '列表页模板',
`detail_template` varchar(32) NOT NULL DEFAULT '' COMMENT '详情页模板',
`post_auth` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '投稿权限',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`sort` int(11) NOT NULL DEFAULT '100' COMMENT '排序',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
`hide` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '是否隐藏',
`rank_auth` int(11) NOT NULL DEFAULT '0' COMMENT '浏览权限,-1待审核0为开放浏览大于0则为对应的用户角色id',
`type` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '栏目属性0-最终列表栏目1-外部链接2-频道封面',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='栏目表';
-- -----------------------------
-- 表数据 `dp_cms_column`
-- -----------------------------
-- -----------------------------
-- 表结构 `dp_cms_document`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_document`;
CREATE TABLE `dp_cms_document` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`cid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '栏目id',
`model` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '文档模型ID',
`title` varchar(256) NOT NULL DEFAULT '' COMMENT '标题',
`shorttitle` varchar(32) NOT NULL DEFAULT '' COMMENT '简略标题',
`uid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`flag` set('j','p','b','s','a','f','c','h') DEFAULT NULL COMMENT '自定义属性',
`view` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '阅读量',
`comment` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '评论数',
`good` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '点赞数',
`bad` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '踩数',
`mark` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '收藏数量',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`sort` int(11) NOT NULL DEFAULT '100' COMMENT '排序',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
`trash` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '回收站',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='文档基础表';
-- -----------------------------
-- 表数据 `dp_cms_document`
-- -----------------------------
-- -----------------------------
-- 表结构 `dp_cms_field`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_field`;
CREATE TABLE `dp_cms_field` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '字段名称',
`name` varchar(32) NOT NULL,
`title` varchar(32) NOT NULL DEFAULT '' COMMENT '字段标题',
`type` varchar(32) NOT NULL DEFAULT '' COMMENT '字段类型',
`define` varchar(128) NOT NULL DEFAULT '' COMMENT '字段定义',
`value` text NULL COMMENT '默认值',
`options` text NULL COMMENT '额外选项',
`tips` varchar(256) NOT NULL DEFAULT '' COMMENT '提示说明',
`fixed` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '是否为固定字段',
`show` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '是否显示',
`model` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所属文档模型id',
`ajax_url` varchar(256) NOT NULL DEFAULT '' COMMENT '联动下拉框ajax地址',
`next_items` varchar(256) NOT NULL DEFAULT '' COMMENT '联动下拉框的下级下拉框名,多个以逗号隔开',
`param` varchar(32) NOT NULL DEFAULT '' COMMENT '联动下拉框请求参数名',
`format` varchar(32) NOT NULL DEFAULT '' COMMENT '格式,用于格式文本',
`table` varchar(32) NOT NULL DEFAULT '' COMMENT '表名,只用于快速联动类型',
`level` tinyint(2) unsigned NOT NULL DEFAULT '2' COMMENT '联动级别,只用于快速联动类型',
`key` varchar(32) NOT NULL DEFAULT '' COMMENT '键字段,只用于快速联动类型',
`option` varchar(32) NOT NULL DEFAULT '' COMMENT '值字段,只用于快速联动类型',
`pid` varchar(32) NOT NULL DEFAULT '' COMMENT '父级id字段只用于快速联动类型',
`ak` varchar(32) NOT NULL DEFAULT '' COMMENT '百度地图appkey',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`sort` int(11) NOT NULL DEFAULT '100' COMMENT '排序',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COMMENT='文档字段表';
-- -----------------------------
-- 表数据 `dp_cms_field`
-- -----------------------------
INSERT INTO `dp_cms_field` VALUES ('1', 'id', 'ID', 'text', 'int(11) UNSIGNED NOT NULL', '0', '', 'ID', '0', '0', '0', '', '', '', '', '', '0', '', '', '', '', '1480562978', '1480562978', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('2', 'cid', '栏目', 'select', 'int(11) UNSIGNED NOT NULL', '0', '', '请选择所属栏目', '0', '0', '0', '', '', '', '', '', '0', '', '', '', '', '1480562978', '1480562978', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('3', 'uid', '用户ID', 'text', 'int(11) UNSIGNED NOT NULL', '0', '', '', '0', '0', '0', '', '', '', '', '', '0', '', '', '', '', '1480563110', '1480563110', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('4', 'model', '模型ID', 'text', 'int(11) UNSIGNED NOT NULL', '0', '', '', '0', '0', '0', '', '', '', '', '', '0', '', '', '', '', '1480563110', '1480563110', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('5', 'title', '标题', 'text', 'varchar(128) NOT NULL', '', '', '文档标题', '0', '1', '0', '', '', '', '', '', '0', '', '', '', '', '1480575844', '1480576134', '1', '1');
INSERT INTO `dp_cms_field` VALUES ('6', 'shorttitle', '简略标题', 'text', 'varchar(32) NOT NULL', '', '', '简略标题', '0', '1', '0', '', '', '', '', '', '0', '', '', '', '', '1480575844', '1480576134', '1', '1');
INSERT INTO `dp_cms_field` VALUES ('7', 'flag', '自定义属性', 'checkbox', 'set(\'j\',\'p\',\'b\',\'s\',\'a\',\'f\',\'h\',\'c\') NULL DEFAULT NULL', '', 'j:跳转\r\np:图片\r\nb:加粗\r\ns:滚动\r\na:特荐\r\nf:幻灯\r\nh:头条\r\nc:推荐', '自定义属性', '0', '1', '0', '', '', '', '', '', '0', '', '', '', '', '1480671258', '1480671258', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('8', 'view', '阅读量', 'text', 'int(11) UNSIGNED NOT NULL', '0', '', '', '0', '1', '0', '', '', '', '', '', '0', '', '', '', '', '1480563149', '1480563149', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('9', 'comment', '评论数', 'text', 'int(11) UNSIGNED NOT NULL', '0', '', '', '0', '0', '0', '', '', '', '', '', '0', '', '', '', '', '1480563189', '1480563189', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('10', 'good', '点赞数', 'text', 'int(11) UNSIGNED NOT NULL', '0', '', '', '0', '0', '0', '', '', '', '', '', '0', '', '', '', '', '1480563279', '1480563279', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('11', 'bad', '踩数', 'text', 'int(11) UNSIGNED NOT NULL', '0', '', '', '0', '0', '0', '', '', '', '', '', '0', '', '', '', '', '1480563330', '1480563330', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('12', 'mark', '收藏数量', 'text', 'int(11) UNSIGNED NOT NULL', '0', '', '', '0', '0', '0', '', '', '', '', '', '0', '', '', '', '', '1480563372', '1480563372', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('13', 'create_time', '创建时间', 'datetime', 'int(11) UNSIGNED NOT NULL', '0', '', '', '0', '0', '0', '', '', '', '', '', '0', '', '', '', '', '1480563406', '1480563406', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('14', 'update_time', '更新时间', 'datetime', 'int(11) UNSIGNED NOT NULL', '0', '', '', '0', '0', '0', '', '', '', '', '', '0', '', '', '', '', '1480563432', '1480563432', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('15', 'sort', '排序', 'text', 'int(11) NOT NULL', '100', '', '', '0', '1', '0', '', '', '', '', '', '0', '', '', '', '', '1480563510', '1480563510', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('16', 'status', '状态', 'radio', 'tinyint(2) UNSIGNED NOT NULL', '1', '0:禁用\r\n1:启用', '', '0', '1', '0', '', '', '', '', '', '0', '', '', '', '', '1480563576', '1480563576', '100', '1');
INSERT INTO `dp_cms_field` VALUES ('17', 'trash', '回收站', 'text', 'tinyint(2) UNSIGNED NOT NULL', '0', '', '', '0', '0', '0', '', '', '', '', '', '0', '', '', '', '', '1480563576', '1480563576', '100', '1');
-- -----------------------------
-- 表结构 `dp_cms_link`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_link`;
CREATE TABLE `dp_cms_link` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`type` tinyint(2) unsigned NOT NULL DEFAULT '1' COMMENT '类型1-文字链接2-图片链接',
`title` varchar(128) NOT NULL DEFAULT '' COMMENT '链接标题',
`url` varchar(255) NOT NULL DEFAULT '' COMMENT '链接地址',
`logo` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '链接LOGO',
`contact` varchar(255) NOT NULL DEFAULT '' COMMENT '联系方式',
`sort` int(11) NOT NULL DEFAULT '100',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='有钱链接表';
-- -----------------------------
-- 表数据 `dp_cms_link`
-- -----------------------------
-- -----------------------------
-- 表结构 `dp_cms_menu`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_menu`;
CREATE TABLE `dp_cms_menu` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`nid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '导航id',
`pid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '父级id',
`column` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '栏目id',
`page` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '单页id',
`type` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '类型0-栏目链接1-单页链接2-自定义链接',
`title` varchar(128) NOT NULL DEFAULT '' COMMENT '菜单标题',
`url` varchar(255) NOT NULL DEFAULT '' COMMENT '链接',
`css` varchar(64) NOT NULL DEFAULT '' COMMENT 'css类',
`rel` varchar(64) NOT NULL DEFAULT '' COMMENT '链接关系网',
`target` varchar(16) NOT NULL DEFAULT '' COMMENT '打开方式',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`sort` int(11) NOT NULL DEFAULT '100' COMMENT '排序',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COMMENT='菜单表';
-- -----------------------------
-- 表数据 `dp_cms_menu`
-- -----------------------------
INSERT INTO `dp_cms_menu` VALUES ('1', '1', '0', '0', '0', '2', '首页', 'cms/index/index', '', '', '_self', '1492345605', '1492345605', '100', '1');
INSERT INTO `dp_cms_menu` VALUES ('2', '2', '0', '0', '0', '2', '关于我们', 'http://www.dolphinphp.com', '', '', '_self', '1492346763', '1492346763', '100', '1');
INSERT INTO `dp_cms_menu` VALUES ('3', '3', '0', '0', '0', '2', '开发文档', 'http://www.kancloud.cn/ming5112/dolphinphp', '', '', '_self', '1492346812', '1492346812', '100', '1');
INSERT INTO `dp_cms_menu` VALUES ('4', '3', '0', '0', '0', '2', '开发者社区', 'http://bbs.dolphinphp.com/', '', '', '_self', '1492346832', '1492346832', '100', '1');
INSERT INTO `dp_cms_menu` VALUES ('5', '1', '0', '0', '0', '2', '二级菜单', 'http://www.dolphinphp.com', '', '', '_self', '1492347372', '1492347510', '100', '1');
INSERT INTO `dp_cms_menu` VALUES ('6', '1', '5', '0', '0', '2', '子菜单', 'http://www.dolphinphp.com', '', '', '_self', '1492347388', '1492347520', '100', '1');
-- -----------------------------
-- 表结构 `dp_cms_model`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_model`;
CREATE TABLE `dp_cms_model` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL DEFAULT '' COMMENT '模型名称',
`title` varchar(32) NOT NULL DEFAULT '' COMMENT '模型标题',
`table` varchar(64) NOT NULL DEFAULT '' COMMENT '附加表名称',
`type` tinyint(2) NOT NULL DEFAULT '1' COMMENT '模型类别0-系统模型1-普通模型2-独立模型',
`icon` varchar(64) NOT NULL,
`sort` int(11) NOT NULL DEFAULT '100' COMMENT '排序',
`system` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '是否系统模型',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='内容模型表';
-- -----------------------------
-- 表结构 `dp_cms_nav`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_nav`;
CREATE TABLE `dp_cms_nav` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`tag` varchar(32) NOT NULL DEFAULT '' COMMENT '导航标识',
`title` varchar(32) NOT NULL DEFAULT '' COMMENT '菜单标题',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='导航表';
-- -----------------------------
-- 表数据 `dp_cms_nav`
-- -----------------------------
INSERT INTO `dp_cms_nav` VALUES ('1', 'main_nav', '顶部导航', '1492345083', '1492345083', '1');
INSERT INTO `dp_cms_nav` VALUES ('2', 'about_nav', '底部关于', '1492346685', '1492346685', '1');
INSERT INTO `dp_cms_nav` VALUES ('3', 'support_nav', '服务与支持', '1492346715', '1492346715', '1');
-- -----------------------------
-- 表结构 `dp_cms_page`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_page`;
CREATE TABLE `dp_cms_page` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(64) NOT NULL DEFAULT '' COMMENT '单页标题',
`content` mediumtext NOT NULL COMMENT '单页内容',
`keywords` varchar(32) NOT NULL DEFAULT '' COMMENT '关键词',
`description` varchar(250) NOT NULL DEFAULT '' COMMENT '页面描述',
`template` varchar(32) NOT NULL DEFAULT '' COMMENT '模板文件',
`cover` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '单页封面',
`view` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '阅读量',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='单页表';
-- -----------------------------
-- 表数据 `dp_cms_page`
-- -----------------------------
-- -----------------------------
-- 表结构 `dp_cms_slider`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_slider`;
CREATE TABLE `dp_cms_slider` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(32) NOT NULL DEFAULT '' COMMENT '标题',
`cover` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '封面id',
`url` varchar(255) NOT NULL DEFAULT '' COMMENT '链接地址',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`sort` int(11) unsigned NOT NULL DEFAULT '100' COMMENT '排序',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='滚动图片表';
-- -----------------------------
-- 表数据 `dp_cms_slider`
-- -----------------------------
-- -----------------------------
-- 表结构 `dp_cms_support`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_support`;
CREATE TABLE `dp_cms_support` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL DEFAULT '' COMMENT '客服名称',
`qq` varchar(16) NOT NULL DEFAULT '' COMMENT 'QQ',
`msn` varchar(100) NOT NULL DEFAULT '' COMMENT 'msn',
`taobao` varchar(100) NOT NULL DEFAULT '' COMMENT 'taobao',
`alibaba` varchar(100) NOT NULL DEFAULT '' COMMENT 'alibaba',
`skype` varchar(100) NOT NULL DEFAULT '' COMMENT 'skype',
`status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
`sort` int(11) unsigned NOT NULL DEFAULT '100' COMMENT '排序',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='客服表';
-- -----------------------------
-- 表数据 `dp_cms_support`
-- -----------------------------

View File

@ -0,0 +1,15 @@
-- -----------------------------
-- 导出时间 `2016-12-13 22:26:46`
-- -----------------------------
DROP TABLE IF EXISTS `dp_cms_advert`;
DROP TABLE IF EXISTS `dp_cms_advert_type`;
DROP TABLE IF EXISTS `dp_cms_column`;
DROP TABLE IF EXISTS `dp_cms_document`;
DROP TABLE IF EXISTS `dp_cms_field`;
DROP TABLE IF EXISTS `dp_cms_link`;
DROP TABLE IF EXISTS `dp_cms_menu`;
DROP TABLE IF EXISTS `dp_cms_model`;
DROP TABLE IF EXISTS `dp_cms_nav`;
DROP TABLE IF EXISTS `dp_cms_page`;
DROP TABLE IF EXISTS `dp_cms_slider`;
DROP TABLE IF EXISTS `dp_cms_support`;

View File

@ -0,0 +1,34 @@
<?php
// +----------------------------------------------------------------------
// | 海豚PHP框架 [ DolphinPHP ]
// +----------------------------------------------------------------------
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://dolphinphp.com
// +----------------------------------------------------------------------
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
use think\Db;
use think\Exception;
// cms模块卸载文件
// 是否清除数据
$clear = $this->request->get('clear');
if ($clear == 1) {
// 内容模型的表名列表
$table_list = Db::name('cms_model')->column('table');
if ($table_list) {
foreach ($table_list as $table) {
// 删除内容模型表
$sql = 'DROP TABLE IF EXISTS `'.$table.'`;';
try {
Db::execute($sql);
} catch (\Exception $e) {
throw new Exception('删除表:'.$table.' 失败!', 1001);
}
}
}
}

View File

@ -0,0 +1,35 @@
<?php
// +----------------------------------------------------------------------
// | 海豚PHP框架 [ DolphinPHP ]
// +----------------------------------------------------------------------
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://dolphinphp.com
// +----------------------------------------------------------------------
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
namespace app\cms\validate;
use think\Validate;
/**
* 行为验证器
* @package app\cms\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',
'title|行为名称' => 'require|length:1,80',
'remark|行为描述' => 'require|length:1,128'
];
//定义验证提示
protected $message = [
'name.regex' => '行为标识由字母和下划线组成',
];
}

View File

@ -0,0 +1,55 @@
<?php
// +----------------------------------------------------------------------
// | 海豚PHP框架 [ DolphinPHP ]
// +----------------------------------------------------------------------
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://dolphinphp.com
// +----------------------------------------------------------------------
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
namespace app\cms\validate;
use think\Validate;
/**
* 广告验证器
* @package app\cms\validate
* @author 蔡伟明 <314013107@qq.com>
*/
class Advert extends Validate
{
// 定义验证规则
protected $rule = [
'typeid|广告分类' => 'require',
'tagname|广告位标识' => 'require|regex:^[a-z]+[a-z0-9_]{0,20}$|unique:cms_advert',
'name|广告位名称' => 'require|unique:cms_advert',
'start_time' => 'requireIf:timeset,1',
'end_time' => 'requireIf:timeset,1',
'title' => 'requireIf:ad_type,1',
'code' => 'requireIf:ad_type,0',
'size' => 'integer',
'width' => 'integer',
'height' => 'integer',
'src' => 'requireIf:ad_type,2',
];
// 定义验证提示
protected $message = [
'tagname.regex' => '广告位标识由小写字母、数字或下划线组成,不能以数字开头',
'code' => '代码不能为空',
'src' => '请上传图片',
'title' => '文字内容不能为空',
'start_time' => '开始时间不能为空',
'end_time' => '结束时间不能为空',
'size' => '文字大小只能填写数字',
'width' => '宽度只能填写数字',
'height' => '高度只能填写数字',
];
// 定义验证场景
protected $scene = [
'name' => ['name']
];
}

View File

@ -0,0 +1,32 @@
<?php
// +----------------------------------------------------------------------
// | 海豚PHP框架 [ DolphinPHP ]
// +----------------------------------------------------------------------
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://dolphinphp.com
// +----------------------------------------------------------------------
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
namespace app\cms\validate;
use think\Validate;
/**
* 广告分类验证器
* @package app\cms\validate
* @author 蔡伟明 <314013107@qq.com>
*/
class AdvertType extends Validate
{
// 定义验证规则
protected $rule = [
'name|分类名称' => 'require|length:1,30|unique:cms_advert_type'
];
// 定义验证场景
protected $scene = [
'name' => ['name']
];
}

View File

@ -0,0 +1,29 @@
<?php
// +----------------------------------------------------------------------
// | 海豚PHP框架 [ DolphinPHP ]
// +----------------------------------------------------------------------
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://dolphinphp.com
// +----------------------------------------------------------------------
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
namespace app\cms\validate;
use think\Validate;
/**
* 栏目验证器
* @package app\cms\validate
* @author 蔡伟明 <314013107@qq.com>
*/
class Column extends Validate
{
// 定义验证规则
protected $rule = [
'pid|所属栏目' => 'require',
'name|栏目名称' => 'require|unique:cms_column,name^pid',
'model|内容模型' => 'require',
];
}

View File

@ -0,0 +1,36 @@
<?php
// +----------------------------------------------------------------------
// | 海豚PHP框架 [ DolphinPHP ]
// +----------------------------------------------------------------------
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://dolphinphp.com
// +----------------------------------------------------------------------
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
namespace app\cms\validate;
use think\Validate;
/**
* 文档字段验证器
* @package app\cms\validate
* @author 蔡伟明 <314013107@qq.com>
*/
class Field extends Validate
{
//定义验证规则
protected $rule = [
'name|字段名称' => 'require|regex:^[a-z]\w{0,39}$|unique:cms_field,name^model',
'title|字段标题' => 'require|length:1,30',
'type|字段类型' => 'require|length:1,30',
'define|字段定义' => 'require|length:1,100',
'tips|字段说明' => 'length:1,200',
];
//定义验证提示
protected $message = [
'name.regex' => '字段名称由小写字母和下划线组成',
];
}

View File

@ -0,0 +1,40 @@
<?php
// +----------------------------------------------------------------------
// | 海豚PHP框架 [ DolphinPHP ]
// +----------------------------------------------------------------------
// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
// +----------------------------------------------------------------------
// | 官方网站: http://dolphinphp.com
// +----------------------------------------------------------------------
// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
namespace app\cms\validate;
use think\Validate;
/**
* 友情链接验证器
* @package app\cms\validate
* @author 蔡伟明 <314013107@qq.com>
*/
class Link extends Validate
{
// 定义验证规则
protected $rule = [
'title|链接标题' => 'require|length:1,30',
'url|链接地址' => 'require|url',
'logo|链接LOGO' => 'requireIf:type,2',
];
// 定义验证提示
protected $message = [
'logo.requireIf' => '请上传链接LOGO',
];
// 定义验证场景
protected $scene = [
'title' => ['title'],
'url' => ['url' => 'require'],
];
}

Some files were not shown because too many files have changed in this diff Show More