sclzdj преди 7 години
родител
ревизия
14f44a777a
променени са 100 файла, в които са добавени 15186 реда и са изтрити 32 реда
  1. 1 0
      .gitattributes
  2. 8 0
      .gitignore
  3. 27 0
      LICENSE.txt
  4. 0 32
      README.md
  5. 1 0
      application/.htaccess
  6. 73 0
      application/admin/controller/Action.php
  7. 486 0
      application/admin/controller/Admin.php
  8. 218 0
      application/admin/controller/Ajax.php
  9. 775 0
      application/admin/controller/Attachment.php
  10. 280 0
      application/admin/controller/Config.php
  11. 375 0
      application/admin/controller/Database.php
  12. 225 0
      application/admin/controller/Hook.php
  13. 35 0
      application/admin/controller/Ie.php
  14. 157 0
      application/admin/controller/Index.php
  15. 90 0
      application/admin/controller/Log.php
  16. 536 0
      application/admin/controller/Menu.php
  17. 617 0
      application/admin/controller/Module.php
  18. 153 0
      application/admin/controller/Packet.php
  19. 604 0
      application/admin/controller/Plugin.php
  20. 154 0
      application/admin/controller/System.php
  21. 85 0
      application/admin/model/Access.php
  22. 27 0
      application/admin/model/Action.php
  23. 90 0
      application/admin/model/Attachment.php
  24. 58 0
      application/admin/model/Config.php
  25. 77 0
      application/admin/model/Hook.php
  26. 126 0
      application/admin/model/HookPlugin.php
  27. 46 0
      application/admin/model/Log.php
  28. 312 0
      application/admin/model/Menu.php
  29. 342 0
      application/admin/model/Module.php
  30. 125 0
      application/admin/model/Packet.php
  31. 309 0
      application/admin/model/Plugin.php
  32. 35 0
      application/admin/validate/Action.php
  33. 41 0
      application/admin/validate/Config.php
  34. 32 0
      application/admin/validate/Hook.php
  35. 35 0
      application/admin/validate/Menu.php
  36. 28 0
      application/admin/validate/Plugin.php
  37. 64 0
      application/admin/view/dispatch_jump.tpl
  38. 74 0
      application/admin/view/ie/index.html
  39. 31 0
      application/admin/view/index/index.html
  40. 472 0
      application/admin/view/layout.html
  41. 203 0
      application/admin/view/menu/index.html
  42. 63 0
      application/admin/view/module/export.html
  43. 161 0
      application/admin/view/module/index.html
  44. 182 0
      application/admin/view/module/install.html
  45. 64 0
      application/admin/view/module/uninstall.html
  46. 162 0
      application/admin/view/plugin/index.html
  47. 300 0
      application/cms/admin/Advert.php
  48. 197 0
      application/cms/admin/AdvertType.php
  49. 277 0
      application/cms/admin/Column.php
  50. 134 0
      application/cms/admin/Content.php
  51. 320 0
      application/cms/admin/Document.php
  52. 331 0
      application/cms/admin/Field.php
  53. 37 0
      application/cms/admin/Index.php
  54. 209 0
      application/cms/admin/Link.php
  55. 255 0
      application/cms/admin/Menu.php
  56. 232 0
      application/cms/admin/Model.php
  57. 169 0
      application/cms/admin/Nav.php
  58. 206 0
      application/cms/admin/Page.php
  59. 200 0
      application/cms/admin/Recycle.php
  60. 202 0
      application/cms/admin/Slider.php
  61. 216 0
      application/cms/admin/Support.php
  62. 158 0
      application/cms/common.php
  63. 103 0
      application/cms/home/Column.php
  64. 92 0
      application/cms/home/Common.php
  65. 147 0
      application/cms/home/Document.php
  66. 29 0
      application/cms/home/Index.php
  67. 40 0
      application/cms/home/Page.php
  68. 49 0
      application/cms/home/Search.php
  69. 601 0
      application/cms/info.php
  70. 888 0
      application/cms/menus.php
  71. 45 0
      application/cms/model/Advert.php
  72. 27 0
      application/cms/model/AdvertType.php
  73. 131 0
      application/cms/model/Column.php
  74. 214 0
      application/cms/model/Document.php
  75. 150 0
      application/cms/model/Field.php
  76. 27 0
      application/cms/model/Link.php
  77. 27 0
      application/cms/model/Menu.php
  78. 237 0
      application/cms/model/Model.php
  79. 27 0
      application/cms/model/Nav.php
  80. 45 0
      application/cms/model/Page.php
  81. 27 0
      application/cms/model/Slider.php
  82. 27 0
      application/cms/model/Support.php
  83. 65 0
      application/cms/public/static/cms/css/main.css
  84. BIN
      application/cms/public/static/cms/img/logo.png
  85. BIN
      application/cms/public/static/cms/img/photo12@2x.jpg
  86. BIN
      application/cms/public/static/cms/img/photo3@2x.jpg
  87. BIN
      application/cms/public/static/cms/img/preview.jpg
  88. BIN
      application/cms/public/static/cms/img/preview.png
  89. 47 0
      application/cms/public/static/cms/js/field.js
  90. 320 0
      application/cms/sql/install.sql
  91. 15 0
      application/cms/sql/uninstall.sql
  92. 34 0
      application/cms/uninstall.php
  93. 35 0
      application/cms/validate/Action.php
  94. 55 0
      application/cms/validate/Advert.php
  95. 32 0
      application/cms/validate/AdvertType.php
  96. 29 0
      application/cms/validate/Column.php
  97. 36 0
      application/cms/validate/Field.php
  98. 40 0
      application/cms/validate/Link.php
  99. 43 0
      application/cms/validate/Menu.php
  100. 0 0
      application/cms/validate/Model.php

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+*.js linguist-language=PHP

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+.idea
+composer.lock
+/runtime/
+/.settings/
+/composer.lock
+/.buildpath
+/.project
+*.log

Файловите разлики са ограничени, защото са твърде много
+ 27 - 0
LICENSE.txt


Файловите разлики са ограничени, защото са твърде много
+ 0 - 32
README.md


+ 1 - 0
application/.htaccess

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

+ 73 - 0
application/admin/controller/Action.php

@@ -0,0 +1,73 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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
+     */
+    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(); // 渲染模板
+    }
+}

+ 486 - 0
application/admin/controller/Admin.php

@@ -0,0 +1,486 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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\user\model\Role as RoleModel;
+use think\Cache;
+use think\Db;
+use think\Loader;
+use think\helper\Hash;
+
+/**
+ * 后台公共控制器
+ * @package app\admin\controller
+ */
+class Admin extends Common
+{
+    /**
+     * 初始化
+     * @author 蔡伟明 <314013107@qq.com>
+     */
+    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));
+            // 构建侧栏
+            $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 = Loader::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>
+     * @return mixed
+     */
+    public function disable($record = [])
+    {
+        return $this->setStatus('disable', $record);
+    }
+
+    /**
+     * 启用
+     * @param array $record 行为日志内容
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    public function enable($record = [])
+    {
+        return $this->setStatus('enable', $record);
+    }
+
+    /**
+     * 启用
+     * @param array $record 行为日志内容
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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 caiweiming <314013107@qq.com>
+     */
+    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 caiweiming <314013107@qq.com>
+     */
+    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>
+     * @return mixed
+     */
+    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
+     */
+    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();
+    }
+}

+ 218 - 0
application/admin/controller/Ajax.php

@@ -0,0 +1,218 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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\Cache;
+use think\Db;
+
+/**
+ * 用于处理ajax请求的控制器
+ * @package app\admin\controller
+ */
+class Ajax extends Common
+{
+    /**
+     * 获取联动数据
+     * @param string $table 表名
+     * @param int $pid 父级ID
+     * @param string $key 下拉选项的值
+     * @param string $option 下拉选项的名称
+     * @param string $pidkey 父级id字段名
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return \think\response\Json
+     */
+    public function getLevelData($table = '', $pid = 0, $key = 'id', $option = 'name', $pidkey = 'pid')
+    {
+        if ($table == '') {
+            return json(['code' => 0, 'msg' => '缺少表名']);
+        }
+
+        $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 $table 表名
+     * @param string $field 字段名
+     * @param array $map 查询条件
+     * @param string $options 选项,用于显示转换
+     * @param string $list 选项缓存列表名称
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return \think\response\Json
+     */
+    public function getFilterList($table = '', $field = '', $map = [], $options = '', $list = '')
+    {
+        if ($list != '') {
+            $result = [
+                'code' => 1,
+                'msg'  => '请求成功',
+                'list' => Cache::get($list)
+            ];
+            return json($result);
+        }
+        if ($table == '') {
+            return json(['code' => 0, 'msg' => '缺少表名']);
+        }
+        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);
+                }
+            }
+        }
+
+        $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 = '')
+    {
+        $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 = '') {
+        $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 = '')
+    {
+        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;
+            }
+        }
+        return $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('文件不存在');
+        }
+    }
+}

+ 775 - 0
application/admin/controller/Attachment.php

@@ -0,0 +1,775 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\admin\controller;
+
+use app\common\builder\ZBuilder;
+use app\admin\model\Attachment as AttachmentModel;
+use think\Image;
+use think\File;
+use think\Hook;
+use think\Db;
+
+/**
+ * 附件控制器
+ * @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'] = '<a class="img-link" href="'. PUBLIC_PATH . $value['path'].'"
+                    data-toggle="tooltip"
+                    title="点击查看大图"
+                    target="_blank">
+                    <img class="image" src="'. PUBLIC_PATH . $thumb.'"></a>';
+                } else {
+                    $value['type'] = '<a class="img-link" href="'. $value['path'].'"
+                    data-toggle="tooltip"
+                    title="点击查看大图"
+                    target="_blank">
+                    <img class="image" src="'. $value['path'].'"></a>';
+                }
+            } 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', '类型', '', '', '', 'js-gallery'],
+                ['name', '名称'],
+                ['size', '大小', 'byte'],
+                ['driver', '上传驱动', parse_attr(Db::name('admin_config')->where('name', 'upload_driver')->value('options'))],
+                ['create_time', '上传时间', 'datetime'],
+                ['status', '状态', 'switch'],
+                ['right_button', '操作', 'btn']
+            ])
+            ->addTopButtons('enable,disable,delete') // 批量添加顶部按钮
+            ->addRightButtons('delete') // 批量添加右侧按钮
+            ->setRowList($data_list) // 设置表格数据
+            ->fetch(); // 渲染模板
+    }
+
+    /**
+     * 上传附件
+     * @param string $dir 保存的目录:images,files,videos,voices
+     * @param string $from 来源,wangeditor:wangEditor编辑器, ueditor:ueditor编辑器, editormd:editormd编辑器等
+     * @param string $module 来自哪个模块
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    public function upload($dir = '', $from = '', $module = '')
+    {
+        // 临时取消执行时间限制
+        set_time_limit(0);
+        if ($dir == '') $this->error('没有指定上传目录');
+        if ($from == 'ueditor') return $this->ueditor();
+        if ($from == 'jcrop') return $this->jcrop();
+        return $this->saveFile($dir, $from, $module);
+    }
+
+    /**
+     * 保存附件
+     * @param string $dir 附件存放的目录
+     * @param string $from 来源
+     * @param string $module 来自哪个模块
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return string|\think\response\Json
+     */
+    private function saveFile($dir = '', $from = '', $module = '')
+    {
+        // 附件大小限制
+        $size_limit = $dir == 'images' ? config('upload_image_size') : config('upload_file_size');
+        $size_limit = $size_limit * 1024;
+        // 附件类型限制
+        $ext_limit = $dir == 'images' ? config('upload_image_ext') : config('upload_file_ext');
+        $ext_limit = $ext_limit != '' ? parse_attr($ext_limit) : '';
+
+        // 获取附件数据
+        $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 (!(($dir == 'images' && config('upload_thumb_water') == 1 && config('upload_thumb_water_pic') > 0) || ($dir == 'images' && config('upload_image_thumb')) != '')){
+            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'];
+                }
+                switch ($from) {
+                    case 'wangeditor':
+                        return $file_path;
+                        break;
+                    case 'ueditor':
+                        return json([
+                            "state" => "SUCCESS",          // 上传状态,上传成功时必须返回"SUCCESS"
+                            "url"   => $file_path, // 返回的地址
+                            "title" => $file_exists['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_exists['id'],
+                            'path'   => $file_path
+                        ]);
+                }
+            }
+        }
+        // 判断附件大小是否超过限制
+        if ($size_limit > 0 && ($file->getInfo('size') > $size_limit)) {
+            switch ($from) {
+                case 'wangeditor':
+                    return "error|附件过大";
+                    break;
+                case 'ueditor':
+                    return json(['state' => '附件过大']);
+                    break;
+                case 'editormd':
+                    return json(["success" => 0, "message" => '附件过大']);
+                    break;
+                case 'ckeditor':
+                    return ck_js($callback, '', '附件过大');
+                    break;
+                default:
+                    return json([
+                        'code'   => 0,
+                        'class'  => 'danger',
+                        'info'   => '附件过大'
+                    ]);
+            }
+        }
+
+        // 判断附件格式是否符合
+        $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 != '') {
+            switch ($from) {
+                case 'wangeditor':
+                    return "error|{$error_msg}";
+                    break;
+                case 'ueditor':
+                    return json(['state' => $error_msg]);
+                    break;
+                case 'editormd':
+                    return json(["success" => 0, "message" => $error_msg]);
+                    break;
+                case 'ckeditor':
+                    return ck_js($callback, '', $error_msg);
+                    break;
+                default:
+                    return json([
+                        'code'   => 0,
+                        'class'  => 'danger',
+                        'info'   => $error_msg
+                    ]);
+            }
+        }
+
+        // 附件上传钩子,用于第三方文件上传扩展
+        if (config('upload_driver') != 'local') {
+            $hook_result = Hook::listen('upload_attachment', $file, ['from' => $from, 'module' => $module], true);
+            if (false !== $hook_result) {
+                return $hook_result;
+            }
+        }
+
+        // 移动到框架应用根目录/uploads/ 目录下
+        $info = $file->move(config('upload_path') . DS . $dir);
+
+        if($info){
+            // 水印功能
+            if ($dir == 'images' && config('upload_thumb_water') == 1 && config('upload_thumb_water_pic') > 0) {
+                $this->create_water($info->getRealPath());
+            }
+
+            // 缩略图路径
+            $thumb_path_name = '';
+            // 生成缩略图
+            if ($dir == 'images' && config('upload_image_thumb') != '') {
+                $thumb_path_name = $this->create_thumb($info, $info->getPathInfo()->getfileName(), $info->getFilename());
+            }
+
+            // 获取附件信息
+            $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
+            ];
+
+            // 写入数据库
+            if ($file_add = AttachmentModel::create($file_info)) {
+                $file_path = PUBLIC_PATH. $file_info['path'];
+                switch ($from) {
+                    case 'wangeditor':
+                        return $file_path;
+                        break;
+                    case 'ueditor':
+                        return json([
+                            "state" => "SUCCESS",          // 上传状态,上传成功时必须返回"SUCCESS"
+                            "url"   => $file_path, // 返回的地址
+                            "title" => $file_info['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_add['id'],
+                            'path'   => $file_path
+                        ]);
+                }
+            } else {
+                switch ($from) {
+                    case 'wangeditor':
+                        return "error|上传失败";
+                        break;
+                    case 'ueditor':
+                        return json(['state' => '上传失败']);
+                        break;
+                    case 'editormd':
+                        return json(["success" => 0, "message" => '上传失败']);
+                        break;
+                    case 'ckeditor':
+                        return ck_js($callback, '', '上传失败');
+                        break;
+                    default:
+                        return json(['code' => 0, 'class' => 'danger', 'info' => '上传失败']);
+                }
+            }
+        }else{
+            switch ($from) {
+                case 'wangeditor':
+                    return "error|".$file->getError();
+                    break;
+                case 'ueditor':
+                    return json(['state' => $file->getError()]);
+                    break;
+                case 'editormd':
+                    return json(["success" => 0, "message" => $file->getError()]);
+                    break;
+                case 'ckeditor':
+                    return ck_js($callback, '', $file->getError());
+                    break;
+                default:
+                    return json(['code' => 0, 'class' => 'danger', 'info' => $file->getError()]);
+            }
+        }
+    }
+
+    /**
+     * 处理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') . DS . 'images' . DS . date('Ymd', $this->request->time());
+        $file_path    = $dir . DS . $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);
+        $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()
+        ];
+
+        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', '');
+        $module    = $this->request->param('module', '');
+
+        // 上传图片
+        if ($file_path == '') {
+            $file = $this->request->file('file');
+            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') . DS . '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 (config('upload_thumb_water') == 1 && config('upload_thumb_water_pic') > 0) {
+                $this->create_water($new_file_path);
+            }
+
+            // 是否创建缩略图
+            $thumb_path_name = '';
+            if (config('upload_image_thumb') != '') {
+                $thumb_path_name = $this->create_thumb($new_file_path, $dir_name, $file_name);
+            }
+
+            // 保存图片
+            $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
+            ];
+
+            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 缩略图名
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return string 缩略图路径
+     */
+    private function create_thumb($file = '', $dir = '', $save_name = '')
+    {
+        // 获取要生成的缩略图最大宽度和高度
+        list($thumb_max_width, $thumb_max_height) = explode(',', config('upload_image_thumb'));
+        // 读取图片
+        $image = Image::open($file);
+        // 生成缩略图
+        $image->thumb($thumb_max_width, $thumb_max_height, config('upload_image_thumb_type'));
+        // 保存缩略图
+        $thumb_path = config('upload_path') . DS . '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 要添加水印的文件路径
+     * @author 蔡伟明 <314013107@qq.com>
+     */
+    private function create_water($file = '')
+    {
+        $path = model('admin/attachment')->getFilePath(config('upload_thumb_water_pic'), 1);
+        $thumb_water_pic = realpath(ROOT_PATH . 'public/' . $path);
+
+        // 读取图片
+        $image = Image::open($file);
+        // 添加水印
+        $image->water($thumb_water_pic, config('upload_thumb_water_position'), config('upload_thumb_water_alpha'));
+        // 保存水印图片,覆盖原图
+        $image->save($file);
+    }
+
+    /**
+     * 遍历获取目录下的指定类型的附件
+     * @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>
+     * @return mixed
+     */
+    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>
+     * @return mixed
+     */
+    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]);
+    }
+}

+ 280 - 0
application/admin/controller/Config.php

@@ -0,0 +1,280 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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
+     */
+    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
+     */
+    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
+     */
+    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>
+     * @return mixed
+     */
+    public function delete($record = [])
+    {
+        return $this->setStatus('delete');
+    }
+
+    /**
+     * 启用配置
+     * @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>
+     * @return mixed
+     */
+    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]);
+    }
+}

+ 375 - 0
application/admin/controller/Database.php

@@ -0,0 +1,375 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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
+     */
+    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('备份文件删除成功!');
+        }
+    }
+}

+ 225 - 0
application/admin/controller/Hook.php

@@ -0,0 +1,225 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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
+     */
+    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>
+     */
+    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>
+     * @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 array $record 行为日志内容
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    public function delete($record = [])
+    {
+        $ids   = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
+        $map['id']     = ['in', $ids];
+        $map['system'] = 1;
+        if (HookModel::where($map)->find()) {
+            $this->error('禁止删除系统钩子');
+        }
+        return $this->setStatus('delete');
+    }
+
+    /**
+     * 设置状态
+     * @param string $type 类型
+     * @param array $record 行为日志内容
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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)]);
+    }
+}

+ 35 - 0
application/admin/controller/Ie.php

@@ -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\admin\controller;
+
+use app\common\controller\Common;
+
+/**
+ * ie提示页面控制器
+ * @package app\admin\controller
+ */
+class Ie extends Common
+{
+    /**
+     * 显示ie提示
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    public function index(){
+        // ie浏览器判断
+        if (get_browser_type() == 'ie') {
+            return $this->fetch();
+        } else {
+            $this->redirect('admin/index/index');
+        }
+    }
+}

+ 157 - 0
application/admin/controller/Index.php

@@ -0,0 +1,157 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\admin\controller;
+
+use think\Cache;
+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()
+    {
+        if (!empty(config('wipe_cache_type'))) {
+            foreach (config('wipe_cache_type') as $item) {
+                if ($item == 'LOG_PATH') {
+                    $dirs = (array) glob(constant($item) . '*');
+                    foreach ($dirs as $dir) {
+                        array_map('unlink', glob($dir . '/*.log'));
+                    }
+                    array_map('rmdir', $dirs);
+                } else {
+                    array_map('unlink', glob(constant($item) . '/*.*'));
+                }
+            }
+            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
+     */
+    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']
+            ]);
+        }
+    }
+}

+ 90 - 0
application/admin/controller/Log.php

@@ -0,0 +1,90 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\admin\controller;
+
+use app\common\builder\ZBuilder;
+use app\admin\model\Log as LogModel;
+use think\Db;
+
+/**
+ * 系统日志控制器
+ * @package app\admin\controller
+ */
+class Log extends Admin
+{
+    /**
+     * 日志列表
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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', 'long2ip'],
+                ['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>
+     */
+    public function details($id = null)
+    {
+        if ($id === null) $this->error('缺少参数');
+        $info = LogModel::getAll(['admin_log.id' => $id]);
+        $info = $info[0];
+        $info['action_ip'] = long2ip($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();
+    }
+}

+ 536 - 0
application/admin/controller/Menu.php

@@ -0,0 +1,536 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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\Cache;
+
+/**
+ * 节点管理
+ * @package app\admin\controller
+ */
+class Menu extends Admin
+{
+    /**
+     * 节点首页
+     * @param string $group 分组
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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
+     */
+    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
+     */
+    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>
+     */
+    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>
+     * @return mixed
+     */
+    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>
+     * @return mixed
+     */
+    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>
+     * @return void
+     */
+    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>
+     * @return void
+     */
+    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>
+     * @return void
+     */
+    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('操作失败');
+        }
+    }
+}

+ 617 - 0
application/admin/controller/Module.php

@@ -0,0 +1,617 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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\Cache;
+use util\Database;
+use util\Sql;
+use util\File;
+use util\PHPZip;
+use util\Tree;
+use think\Db;
+use think\Hook;
+
+/**
+ * 模块管理控制器
+ * @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' => '本地模块'];
+        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>
+     * @return mixed
+     */
+    public function install($name = '', $confirm = 0)
+    {
+        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(APP_PATH.$name.'/install.php');
+        if (file_exists($install_file)) {
+            @include($install_file);
+        }
+
+        // 执行安装模块sql文件
+        $sql_file = realpath(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(APP_PATH. $name. '/public', ROOT_PATH. 'public');
+            // 删除静态资源目录
+            File::del_dir(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
+     */
+    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(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(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(ROOT_PATH. 'public/static/'. $name, APP_PATH.$name.'/public/static/'. $name);
+            // 删除静态资源目录
+            File::del_dir(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>
+     * @return mixed
+     */
+    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 = ROOT_PATH. 'export/module/'. $name;
+
+        // 删除旧的导出数据
+        if (is_dir($module_dir)) {
+            File::del_dir($module_dir);
+        }
+
+        // 复制模块目录到导出目录
+        File::copy_dir(APP_PATH. $name, $module_dir);
+        // 复制静态资源目录
+        File::copy_dir(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 (!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
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+/**
+ * 菜单信息
+ */
+return {$menus};
+
+INFO;
+        // 写入到文件
+        return file_put_contents(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
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+/**
+ * 模块信息
+ */
+return {$info};
+
+INFO;
+        // 写入到文件
+        return file_put_contents(ROOT_PATH. 'export/module/'. $name. '/info.php', $content);
+    }
+
+    /**
+     * 设置状态
+     * @param string $type 类型:disable/enable
+     * @param array $record 行为日志内容
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return void
+     */
+    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>
+     * @return void
+     */
+    public function disable($record = [])
+    {
+        $this->setStatus('disable');
+    }
+
+    /**
+     * 启用模块
+     * @param array $record 行为日志内容
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return void
+     */
+    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;
+    }
+}

+ 153 - 0
application/admin/controller/Packet.php

@@ -0,0 +1,153 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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
+     */
+    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>
+     * @return mixed
+     */
+    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('卸载成功');
+    }
+}

+ 604 - 0
application/admin/controller/Plugin.php

@@ -0,0 +1,604 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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\Cache;
+use util\Sql;
+use think\Db;
+use think\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' => '本地插件'];
+        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 = '')
+    {
+        $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>
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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>
+     */
+    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>
+     * @return void
+     */
+    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>
+     * @return void
+     */
+    public function disable($record = [])
+    {
+        $this->setStatus('disable');
+    }
+
+    /**
+     * 启用插件/启用插件数据
+     * @param array $record 行为日志内容
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return void
+     */
+    public function enable($record = [])
+    {
+        $this->setStatus('enable');
+    }
+
+    /**
+     * 删除插件数据
+     * @param array $record
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return void
+     */
+    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>
+     * @return array
+     */
+    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']);
+            }
+        }
+    }
+}

+ 154 - 0
application/admin/controller/System.php

@@ -0,0 +1,154 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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
+     */
+    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)->where('id','neq', 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)
+                    ->where('id','neq', 1)
+                    ->order('sort asc,id asc')
+                    ->column('name,title,tips,type,value,options,ajax_url,next_items,param,table,level,key,option,ak,format');
+
+                foreach ($data_list as &$value) {
+                    // 解析options
+                    if ($value['options'] != '') {
+                        $value['options'] = parse_attr($value['options']);
+                    }
+                }
+
+                // 默认模块列表
+                if (isset($data_list['home_default_module'])) {
+                    $list_module['index'] = '默认';
+                    $data_list['home_default_module']['options'] = array_merge($list_module, ModuleModel::getModule());
+                }
+
+                // 使用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();
+            }
+        }
+    }
+}

+ 85 - 0
application/admin/model/Access.php

@@ -0,0 +1,85 @@
+<?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\Request;
+
+/**
+ * 统一授权模型
+ * @package app\admin\model
+ */
+class Access extends Model
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__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::instance()->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;
+        }
+    }
+}

+ 27 - 0
application/admin/model/Action.php

@@ -0,0 +1,27 @@
+<?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 Action extends Model
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__ADMIN_ACTION__';
+
+    // 自动写入时间戳
+    protected $autoWriteTimestamp = true;
+}

+ 90 - 0
application/admin/model/Attachment.php

@@ -0,0 +1,90 @@
+<?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 Attachment extends Model
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__ADMIN_ATTACHMENT__';
+
+    // 自动写入时间戳
+    protected $autoWriteTimestamp = true;
+
+    /**
+     * 根据附件id获取路径
+     * @param  string|array $id 附件id
+     * @param  int $type 类型:0-补全目录,1-直接返回数据库记录的地址
+     * @return string|array     路径
+     */
+    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[$key] = ($type == 0 ? PUBLIC_PATH : '').$value['path'];
+                } else {
+                    $paths[$key] = $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 mixed
+     */
+    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');
+    }
+}

+ 58 - 0
application/admin/model/Config.php

@@ -0,0 +1,58 @@
+<?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 Config extends Model
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__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;
+    }
+}

+ 77 - 0
application/admin/model/Hook.php

@@ -0,0 +1,77 @@
+<?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 Hook extends Model
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__ADMIN_HOOK__';
+
+    // 自动写入时间戳
+    protected $autoWriteTimestamp = true;
+
+    /**
+     * 添加钩子
+     * @param array $hooks 钩子
+     * @param string $plugin_name 插件名称
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return bool
+     */
+    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
+     */
+    public static function deleteHooks($plugin_name = '')
+    {
+        if (!empty($plugin_name)) {
+            if (false === self::where('plugin', $plugin_name)->delete()) {
+                return false;
+            }
+        }
+        return true;
+    }
+}

+ 126 - 0
application/admin/model/HookPlugin.php

@@ -0,0 +1,126 @@
+<?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 app\admin\model\Hook as HookModel;
+
+/**
+ * 钩子-插件模型
+ * @package app\admin\model
+ */
+class HookPlugin extends Model
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__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
+     */
+    public static function addHooks($hooks = [], $plugin_name = '')
+    {
+        if (!empty($hooks) && is_array($hooks)) {
+            // 添加钩子
+            if (!HookModel::addHooks($hooks, $plugin_name)) {
+                return false;
+            }
+
+            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
+     */
+    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;
+    }
+}

+ 46 - 0
application/admin/model/Log.php

@@ -0,0 +1,46 @@
+<?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 Log extends Model
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__ADMIN_LOG__';
+
+    // 自动写入时间戳
+    protected $autoWriteTimestamp = true;
+
+    /**
+     * 获取所有日志
+     * @param array $map 条件
+     * @param string $order 排序
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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;
+    }
+}

+ 312 - 0
application/admin/model/Menu.php

@@ -0,0 +1,312 @@
+<?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'] = ['notin', $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;
+            $menus = self::where($map)->order('sort,id')->limit($max)->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')) {
+                    $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']);
+                }
+            }
+            // 非开发模式,缓存菜单
+            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,
+                'module' => $module
+            ];
+            // 非开发模式,只显示可以显示的菜单
+            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];
+            $map['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'), $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);
+    }
+}

+ 342 - 0
application/admin/model/Module.php

@@ -0,0 +1,342 @@
+<?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 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(APP_PATH.'*', GLOB_ONLYDIR));
+            if ($dirs === false || !file_exists(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(APP_PATH. $name . '/info.php')) {
+                $info = include 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(APP_PATH. $name . '/menus.php')) {
+            // 从菜单文件获取
+            $menus = include APP_PATH. $name . '/menus.php';
+        }
+        return $menus;
+    }
+}

+ 125 - 0
application/admin/model/Packet.php

@@ -0,0 +1,125 @@
+<?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\Db;
+use util\Sql;
+
+/**
+ * 数据包模型
+ * @package app\admin\model
+ */
+class Packet extends Model
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__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
+     */
+    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;
+    }
+}

+ 309 - 0
application/admin/model/Plugin.php

@@ -0,0 +1,309 @@
+<?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 Plugin extends Model
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__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 static function getConfig($name = '', $item = '')
+    {
+        $config = cache('plugin_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('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 static function setConfig($name = '', $value = '')
+    {
+        $item = '';
+        if (strpos($name, '.')) {
+            list($name, $item) = explode('.', $name);
+        }
+
+        // 获取缓存
+        $config = cache('plugin_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('plugin_config_'.$name, $config);
+        }
+
+        return true;
+    }
+}

+ 35 - 0
application/admin/validate/Action.php

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

+ 41 - 0
application/admin/validate/Config.php

@@ -0,0 +1,41 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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'],
+    ];
+}

+ 32 - 0
application/admin/validate/Hook.php

@@ -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\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' => '钩子名称由字母和下划线组成',
+    ];
+}

+ 35 - 0
application/admin/validate/Menu.php

@@ -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\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'    => '请选择所属节点',
+    ];
+}

+ 28 - 0
application/admin/validate/Plugin.php

@@ -0,0 +1,28 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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',
+    ];
+}

+ 64 - 0
application/admin/view/dispatch_jump.tpl

@@ -0,0 +1,64 @@
+<!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 -->
+
+
+<script type="text/javascript">
+    (function(){
+        var wait = document.getElementById('wait'),
+            href = document.getElementById('href').href;
+        var interval = setInterval(function(){
+            var time = --wait.innerHTML;
+            if(time <= 0) {
+                location.href = href;
+                clearInterval(interval);
+            };
+        }, 1000);
+
+        // 禁止跳转
+        window.stop = function (){
+            clearInterval(interval);
+        }
+    })();
+</script>
+</body>
+</html>

+ 74 - 0
application/admin/view/ie/index.html

@@ -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>

+ 31 - 0
application/admin/view/index/index.html

@@ -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">
+        
+    </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}

+ 472 - 0
application/admin/view/layout.html

@@ -0,0 +1,472 @@
+<!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}
+
+
+    {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">
+    <link rel="stylesheet" href="__LIBS__/magnific-popup/magnific-popup.min.css">
+    <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" id="css-main">
+    {/eq}
+    <link rel="stylesheet" id="css-theme" href="__ADMIN_CSS__/themes/{:config('system_color')}.min.css">
+
+    <!--页面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">
+    <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': {$field_triggers|default=[]|json_encode}, // 触发器集合
+            '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': {$field_clear|default=[]|json_encode},
+            'get_filter_list': '{:url("admin/ajax/getFilterList")}',
+            'curr_url': '{:url("", $Request.route)}',
+            'curr_params': {$Request.param|json_encode},
+            '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}
+                            {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}
+                        </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>
+                    </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 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="{:config('mall.www_url')}" target="_blank" data-toggle="tooltip" data-placement="bottom" data-original-title="打开前台">
+                    <i class="fa fa-external-link-square"></i>
+                </a>
+            </li>
+            <li></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"]'}{$v.url_value|url}{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">
+       
+    </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"></script>
+<script src="__ADMIN_JS__/core/bootstrap.min.js"></script>
+<script src="__ADMIN_JS__/core/jquery.slimscroll.min.js"></script>
+<script src="__ADMIN_JS__/core/jquery.scrollLock.min.js"></script>
+<script src="__ADMIN_JS__/core/jquery.appear.min.js"></script>
+<script src="__ADMIN_JS__/core/jquery.countTo.min.js"></script>
+<script src="__ADMIN_JS__/core/jquery.placeholder.min.js"></script>
+<script src="__ADMIN_JS__/core/js.cookie.min.js"></script>
+<script src="__LIBS__/magnific-popup/magnific-popup.min.js"></script>
+<script src="__ADMIN_JS__/app.js"></script>
+<script src="__ADMIN_JS__/dolphin.js"></script>
+<script src="__ADMIN_JS__/builder/form.js"></script>
+<script src="__ADMIN_JS__/builder/aside.js"></script>
+<script src="__ADMIN_JS__/builder/table.js"></script>
+<script src="__LIBS__/bootstrap-notify/bootstrap-notify.min.js"></script>
+<script src="__LIBS__/sweetalert/sweetalert.min.js"></script>
+<script src="__LIBS__/js-xss/xss.min.js"></script>
+{/eq}
+
+<!-- Page JS Plugins -->
+<script src="__LIBS__/layer/layer.js"></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});
+        {/notempty}
+    });
+</script>
+
+<!--页面js-->
+{block name="script"}{/block}
+
+<!--插件js钩子-->
+{:hook('page_plugin_js')}
+
+{// 额外HTML代码 }
+{$extra_html|default=''}
+</body>
+</html>

+ 203 - 0
application/admin/view/menu/index.html

@@ -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}</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}</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}

+ 63 - 0
application/admin/view/module/export.html

@@ -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}

+ 161 - 0
application/admin/view/module/index.html

@@ -0,0 +1,161 @@
+{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}</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|default='暂无简介'}</p>
+                                            </div>
+                                        </div>
+                                        <div class="text-center push">
+                                            {$module.actions}
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            {/volist}
+                            {else/}
+                                {notempty name="modules"}
+                                <div class="col-md-12">
+                                    <div class="table-responsive">
+                                        <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|default='暂无简介'}</td>
+                                                <td>{$module.actions}</td>
+                                            </tr>
+                                            {/volist}
+                                            </tbody>
+                                        </table>
+                                    </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}

+ 182 - 0
application/admin/view/module/install.html

@@ -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}
+                                                </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}
+                                            </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}
+                                            </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}

+ 64 - 0
application/admin/view/module/uninstall.html

@@ -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}

+ 162 - 0
application/admin/view/plugin/index.html

@@ -0,0 +1,162 @@
+{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}</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|default='暂无简介'}</p>
+                                            </div>
+                                        </div>
+                                        <div class="text-center push">
+                                            {$plugin.actions}
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            {/volist}
+                            {else/}
+                                {notempty name="plugins"}
+                                <div class="col-md-12">
+                                    <div class="table-responsive">
+                                        <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|default='暂无简介'}</td>
+                                                <td>{$plugin.actions}</td>
+                                            </tr>
+                                            {/volist}
+                                            </tbody>
+                                        </table>
+                                    </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}

+ 300 - 0
application/cms/admin/Advert.php

@@ -0,0 +1,300 @@
+<?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\Advert as AdvertModel;
+use app\cms\model\AdvertType as AdvertTypeModel;
+use think\Validate;
+
+/**
+ * 广告控制器
+ * @package app\cms\admin
+ */
+class Advert extends Admin
+{
+    /**
+     * 广告列表
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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
+     */
+    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
+     */
+    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>
+     * @return mixed
+     */
+    public function delete($record = [])
+    {
+        return $this->setStatus('delete');
+    }
+
+    /**
+     * 启用广告
+     * @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>
+     * @return mixed
+     */
+    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]);
+    }
+}

+ 197 - 0
application/cms/admin/AdvertType.php

@@ -0,0 +1,197 @@
+<?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\AdvertType as AdvertTypeModel;
+
+/**
+ * 广告分类控制器
+ * @package app\cms\admin
+ */
+class AdvertType extends Admin
+{
+    /**
+     * 广告列表
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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
+     */
+    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 广告分类id
+     * @author 蔡伟明 <314013107@qq.com>
+     */
+    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>
+     * @return mixed
+     */
+    public function delete($record = [])
+    {
+        return $this->setStatus('delete');
+    }
+
+    /**
+     * 启用广告分类
+     * @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>
+     * @return mixed
+     */
+    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]);
+    }
+}

+ 277 - 0
application/cms/admin/Column.php

@@ -0,0 +1,277 @@
+<?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\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;
+
+/**
+ * 栏目控制器
+ * @package app\cms\admin
+ */
+class Column extends Admin
+{
+    /**
+     * 栏目列表
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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
+     */
+    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(APP_PATH.'cms/view/column/')['file'];
+        $template_detail = File::get_dirs(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>
+     */
+    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']);
+                return $this->success('编辑成功', 'index');
+            } else {
+                return $this->error('编辑失败');
+            }
+        }
+
+        // 获取数据
+        $info = ColumnModel::get($id);
+
+        $template_list   = File::get_dirs(APP_PATH.'cms/view/column/')['file'];
+        $template_detail = File::get_dirs(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>
+     * @return mixed
+     */
+    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>
+     * @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');
+        $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]);
+    }
+}

+ 134 - 0
application/cms/admin/Content.php

@@ -0,0 +1,134 @@
+<?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\Document;
+use think\Db;
+
+/**
+ * 内容控制器
+ * @package app\cms\admin
+ */
+class Content extends Admin
+{
+    /**
+     * 空操作,用于显示各个模型的文档列表
+     * @author 蔡伟明 <314013107@qq.com>
+     */
+    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(); // 渲染模板
+        }
+    }
+}

+ 320 - 0
application/cms/admin/Document.php

@@ -0,0 +1,320 @@
+<?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\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
+     */
+    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);
+
+            // 独立模型只取该模型的字段,不包含系统字段
+            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;
+                }
+            }
+
+            // 添加额外表单项信息
+            $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
+     */
+    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);
+
+        // 独立模型只取该模型的字段,不包含系统字段
+        if ($model != '') {
+            $info['model']  = $model;
+            $where['model'] = $model;
+        } else {
+            $where['model'] = ['in', [0, $info['model']]];
+        }
+
+        // 获取文档模型字段
+        $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;
+            }
+        }
+
+        // 获取相同内容模型的栏目
+        $columns = Db::name('cms_column')->where(['model' => $where['model']])->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>
+     * @return mixed
+     */
+    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));
+        return $this->success('删除成功');
+    }
+
+    /**
+     * 启用文档
+     * @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 = [])
+    {
+        $table_name     = input('param.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 = [])
+    {
+        $id       = input('post.pk', '');
+        $field    = input('post.name', '');
+        $value    = input('post.value', '');
+        $table    = input('post.table', '');
+        $document = Db::name($table)->where('id', $id)->value($field);
+        $details  = '表名('.$table.'),字段(' . $field . '),原值(' . $document . '),新值:(' . $value . ')';
+        return parent::quickEdit(['document_edit', 'cms_document', $id, UID, $details]);
+    }
+}

+ 331 - 0
application/cms/admin/Field.php

@@ -0,0 +1,331 @@
+<?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\Model;
+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]);
+    }
+}

+ 37 - 0
application/cms/admin/Index.php

@@ -0,0 +1,37 @@
+<?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 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(); // 渲染模板
+    }
+}

+ 209 - 0
application/cms/admin/Link.php

@@ -0,0 +1,209 @@
+<?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\Link as LinkModel;
+
+/**
+ * 友情链接控制器
+ * @package app\cms\admin
+ */
+class Link extends Admin
+{
+    /**
+     * 友情链接列表
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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
+     */
+    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>
+     */
+    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>
+     * @return mixed
+     */
+    public function delete($record = [])
+    {
+        return $this->setStatus('delete');
+    }
+
+    /**
+     * 启用友情链接
+     * @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>
+     * @return mixed
+     */
+    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]);
+    }
+}

+ 255 - 0
application/cms/admin/Menu.php

@@ -0,0 +1,255 @@
+<?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\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
+     */
+    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
+     */
+    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
+     */
+    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>
+     * @return mixed
+     */
+    public function delete($ids = null)
+    {
+        // 检查是否有子菜单
+        if (MenuModel::where('pid', $ids)->find()) {
+            $this->error('请先删除或移动该菜单下的子菜单');
+        }
+        return $this->setStatus('delete');
+    }
+
+    /**
+     * 启用菜单
+     * @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>
+     * @return mixed
+     */
+    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]);
+    }
+}

+ 232 - 0
application/cms/admin/Model.php

@@ -0,0 +1,232 @@
+<?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\Model as DocumentModel;
+use app\admin\model\Menu as MenuModel;
+use think\Db;
+use think\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
+     */
+    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
+     */
+    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>
+     * @return mixed|void
+     */
+    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 {
+                return $this->error('删除内容模型字段失败');
+            }
+        } else {
+            return $this->error('删除内容模型表失败');
+        }
+    }
+}

+ 169 - 0
application/cms/admin/Nav.php

@@ -0,0 +1,169 @@
+<?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\Nav as NavModel;
+use app\cms\model\Menu as MenuModel;
+
+/**
+ * 导航控制器
+ * @package app\cms\admin
+ */
+class Nav extends Admin
+{
+    /**
+     * 导航列表
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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
+     */
+    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
+     */
+    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>
+     * @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>
+     * @return mixed
+     */
+    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]);
+    }
+}

+ 206 - 0
application/cms/admin/Page.php

@@ -0,0 +1,206 @@
+<?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\Page as PageModel;
+
+/**
+ * 单页控制器
+ * @package app\cms\admin
+ */
+class Page extends Admin
+{
+    /**
+     * 单页列表
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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
+     */
+    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
+     */
+    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>
+     * @return mixed
+     */
+    public function delete($record = [])
+    {
+        return $this->setStatus('delete');
+    }
+
+    /**
+     * 启用单页
+     * @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>
+     * @return mixed
+     */
+    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]);
+    }
+}

+ 200 - 0
application/cms/admin/Recycle.php

@@ -0,0 +1,200 @@
+<?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\Document as DocumentModel;
+use think\Db;
+
+/**
+ * 回收站控制器
+ * @package app\cms\admin
+ */
+class Recycle extends Admin
+{
+    /**
+     * 文档列表
+     * @param string $model 内容模型id
+     * @author 蔡伟明 <314013107@qq.com>
+     */
+    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>
+     * @return mixed
+     */
+    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('删除成功');
+    }
+}

+ 202 - 0
application/cms/admin/Slider.php

@@ -0,0 +1,202 @@
+<?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\Slider as SliderModel;
+
+/**
+ * 滚动图片控制器
+ * @package app\cms\admin
+ */
+class Slider extends Admin
+{
+    /**
+     * 滚动图片列表
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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
+     */
+    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
+     */
+    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>
+     * @return mixed
+     */
+    public function delete($record = [])
+    {
+        return $this->setStatus('delete');
+    }
+
+    /**
+     * 启用单页
+     * @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>
+     * @return mixed
+     */
+    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]);
+    }
+}

+ 216 - 0
application/cms/admin/Support.php

@@ -0,0 +1,216 @@
+<?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\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
+     */
+    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
+     */
+    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>
+     * @return mixed
+     */
+    public function delete($record = [])
+    {
+        return $this->setStatus('delete');
+    }
+
+    /**
+     * 启用客服
+     * @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>
+     * @return mixed
+     */
+    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 - 0
application/cms/common.php

@@ -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 '刚刚';
+            }
+        }
+    }
+}

+ 103 - 0
application/cms/home/Column.php

@@ -0,0 +1,103 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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
+     */
+    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 int $id
+     * @author 蔡伟明 <314013107@qq.com>
+     */
+    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);
+    }
+}

+ 92 - 0
application/cms/home/Common.php

@@ -0,0 +1,92 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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();
+    }
+}

+ 147 - 0
application/cms/home/Document.php

@@ -0,0 +1,147 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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>
+     */
+    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|false|\PDOStatement|string|\think\Model
+     */
+    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|false|\PDOStatement|string|\think\Model
+     */
+    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;
+    }
+}

+ 29 - 0
application/cms/home/Index.php

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

+ 40 - 0
application/cms/home/Page.php

@@ -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\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
+     */
+    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(); // 渲染模板
+    }
+}

+ 49 - 0
application/cms/home/Search.php

@@ -0,0 +1,49 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\home;
+
+use think\Db;
+
+/**
+ * 前台搜索控制器
+ * @package app\cms\admin
+ */
+class Search extends Common
+{
+    /**
+     * 搜索列表
+     * @param string $keyword 关键词
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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(); // 渲染模板
+    }
+}

+ 601 - 0
application/cms/info.php

@@ -0,0 +1,601 @@
+<?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' => [
+        'group' => [
+            "tab_title"   => '栏目授权',
+            "table_name"  => "cms_column",
+            "primary_key" => "id",
+            "parent_id"   => "pid",
+            "node_name"   => "name"
+        ],
+    ],
+];

+ 888 - 0
application/cms/menus.php

@@ -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,
+                                    ],
+                                ],
+                            ],
+                        ],
+                    ],
+                ],
+            ],
+        ],
+    ],
+];

+ 45 - 0
application/cms/model/Advert.php

@@ -0,0 +1,45 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\model;
+
+use think\Model as ThinkModel;
+
+/**
+ * 广告模型
+ * @package app\cms\model
+ */
+class Advert extends ThinkModel
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__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) : '';
+    }
+}

+ 27 - 0
application/cms/model/AdvertType.php

@@ -0,0 +1,27 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\model;
+
+use think\Model as ThinkModel;
+
+/**
+ * 广告分类模型
+ * @package app\cms\model
+ */
+class AdvertType extends ThinkModel
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__CMS_ADVERT_TYPE__';
+
+    // 自动写入时间戳
+    protected $autoWriteTimestamp = true;
+}

+ 131 - 0
application/cms/model/Column.php

@@ -0,0 +1,131 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\model;
+
+use think\Model as ThinkModel;
+use util\Tree;
+
+/**
+ * 栏目模型
+ * @package app\cms\model
+ */
+class Column extends ThinkModel
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__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] = '顶级栏目';
+
+        // 排除指定节点及其子节点
+        if ($id !== 0) {
+            $hide_ids    = array_merge([$id], self::getChildsId($id));
+            $where['id'] = ['notin', $hide_ids];
+        }
+        $where['status'] = 1;
+
+        $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;
+    }
+}

+ 214 - 0
application/cms/model/Document.php

@@ -0,0 +1,214 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+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 $table = '__CMS_DOCUMENT__';
+
+    // 自动写入时间戳
+    protected $autoWriteTimestamp = true;
+
+    /**
+     * 获取文档列表
+     * @param array $map 筛选条件
+     * @param array $order 排序
+     * @author 蔡伟明 <314013107@qq.com>
+     * @return mixed
+     */
+    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 mixed
+     */
+    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
+     */
+    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;
+                }
+            }
+        }
+    }
+}

+ 150 - 0
application/cms/model/Field.php

@@ -0,0 +1,150 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\model;
+
+use think\Model as ThinkModel;
+use think\Db;
+
+/**
+ * 字段模型
+ * @package app\cms\model
+ */
+class Field extends ThinkModel
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__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}'");
+    }
+}

+ 27 - 0
application/cms/model/Link.php

@@ -0,0 +1,27 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\model;
+
+use think\Model as ThinkModel;
+
+/**
+ * 友情链接模型
+ * @package app\cms\model
+ */
+class Link extends ThinkModel
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__CMS_LINK__';
+
+    // 自动写入时间戳
+    protected $autoWriteTimestamp = true;
+}

+ 27 - 0
application/cms/model/Menu.php

@@ -0,0 +1,27 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\model;
+
+use think\Model as ThinkModel;
+
+/**
+ * 菜单模型
+ * @package app\cms\model
+ */
+class Menu extends ThinkModel
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__CMS_MENU__';
+
+    // 自动写入时间戳
+    protected $autoWriteTimestamp = true;
+}

+ 237 - 0
application/cms/model/Model.php

@@ -0,0 +1,237 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\model;
+
+use think\Model as ThinkModel;
+use think\Db;
+
+/**
+ * 内容模型
+ * @package app\cms\model
+ */
+class Model extends ThinkModel
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__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;
+    }
+}

+ 27 - 0
application/cms/model/Nav.php

@@ -0,0 +1,27 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\model;
+
+use think\Model as ThinkModel;
+
+/**
+ * 导航模型
+ * @package app\cms\model
+ */
+class Nav extends ThinkModel
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__CMS_NAV__';
+
+    // 自动写入时间戳
+    protected $autoWriteTimestamp = true;
+}

+ 45 - 0
application/cms/model/Page.php

@@ -0,0 +1,45 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\model;
+
+use think\Model as ThinkModel;
+
+/**
+ * 单页模型
+ * @package app\cms\model
+ */
+class Page extends ThinkModel
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__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;
+    }
+}

+ 27 - 0
application/cms/model/Slider.php

@@ -0,0 +1,27 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\model;
+
+use think\Model as ThinkModel;
+
+/**
+ * 滚动图片模型
+ * @package app\cms\model
+ */
+class Slider extends ThinkModel
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__CMS_SLIDER__';
+
+    // 自动写入时间戳
+    protected $autoWriteTimestamp = true;
+}

+ 27 - 0
application/cms/model/Support.php

@@ -0,0 +1,27 @@
+<?php
+// +----------------------------------------------------------------------
+// | 海豚PHP框架 [ DolphinPHP ]
+// +----------------------------------------------------------------------
+// | 版权所有 2016~2017 河源市卓锐科技有限公司 [ http://www.zrthink.com ]
+// +----------------------------------------------------------------------
+// | 官方网站: http://dolphinphp.com
+// +----------------------------------------------------------------------
+// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+
+namespace app\cms\model;
+
+use think\Model as ThinkModel;
+
+/**
+ * 客服模型
+ * @package app\cms\model
+ */
+class Support extends ThinkModel
+{
+    // 设置当前模型对应的完整数据表名称
+    protected $table = '__CMS_SUPPORT__';
+
+    // 自动写入时间戳
+    protected $autoWriteTimestamp = true;
+}

+ 65 - 0
application/cms/public/static/cms/css/main.css

@@ -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;
+}

BIN
application/cms/public/static/cms/img/logo.png


BIN
application/cms/public/static/cms/img/photo12@2x.jpg


BIN
application/cms/public/static/cms/img/photo3@2x.jpg


BIN
application/cms/public/static/cms/img/preview.jpg


BIN
application/cms/public/static/cms/img/preview.png


+ 47 - 0
application/cms/public/static/cms/js/field.js

@@ -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()] || '');
+    });
+});

+ 320 - 0
application/cms/sql/install.sql

@@ -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`
+-- -----------------------------

+ 15 - 0
application/cms/sql/uninstall.sql

@@ -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`;

+ 34 - 0
application/cms/uninstall.php

@@ -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);
+            }
+        }
+    }
+}

+ 35 - 0
application/cms/validate/Action.php

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

+ 55 - 0
application/cms/validate/Advert.php

@@ -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']
+    ];
+}

+ 32 - 0
application/cms/validate/AdvertType.php

@@ -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']
+    ];
+}

+ 29 - 0
application/cms/validate/Column.php

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

+ 36 - 0
application/cms/validate/Field.php

@@ -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' => '字段名称由小写字母和下划线组成',
+    ];
+}

+ 40 - 0
application/cms/validate/Link.php

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

+ 43 - 0
application/cms/validate/Menu.php

@@ -0,0 +1,43 @@
+<?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 Menu extends Validate
+{
+    // 定义验证规则
+    protected $rule = [
+        'column|栏目' => 'requireIf:type,0',
+        'page|单页' => 'requireIf:type,1',
+        'title|菜单标题' => 'requireIf:type,2|length:1,30',
+        'url|URL' => 'requireIf:type,2',
+    ];
+
+    // 定义验证提示
+    protected $message = [
+        'column.requireIf' => '请选择栏目',
+        'page.requireIf'   => '请选择单页',
+        'title.requireIf'  => '菜单标题不能为空',
+        'url.requireIf'    => 'URL不能为空'
+    ];
+
+    // 定义验证场景
+    protected $scene = [
+        'title' => ['title']
+    ];
+}

+ 0 - 0
application/cms/validate/Model.php


Някои файлове не бяха показани, защото твърде много файлове са промени