huwhois 3 年之前
父节点
当前提交
f9b3e590e1
共有 100 个文件被更改,包括 6700 次插入22 次删除
  1. 1 0
      .example.env
  2. 6 22
      .gitignore
  3. 42 0
      .travis.yml
  4. 32 0
      LICENSE.txt
  5. 1 0
      app/.htaccess
  6. 22 0
      app/AppService.php
  7. 94 0
      app/BaseController.php
  8. 58 0
      app/ExceptionHandle.php
  9. 8 0
      app/Request.php
  10. 42 0
      app/common.php
  11. 26 0
      app/common/model/Article.php
  12. 10 0
      app/common/model/ArticleTags.php
  13. 47 0
      app/common/model/Base.php
  14. 33 0
      app/common/model/Category.php
  15. 6 0
      app/common/model/Module.php
  16. 6 0
      app/common/model/News.php
  17. 7 0
      app/common/model/Search.php
  18. 86 0
      app/common/model/SysLog.php
  19. 39 0
      app/common/model/SysLogin.php
  20. 52 0
      app/common/model/SysLoginFail.php
  21. 33 0
      app/common/model/SysMenu.php
  22. 19 0
      app/common/model/SysRole.php
  23. 120 0
      app/common/model/SysUser.php
  24. 14 0
      app/common/model/System.php
  25. 17 0
      app/event.php
  26. 17 0
      app/index/controller/Index.php
  27. 10 0
      app/middleware.php
  28. 9 0
      app/provider.php
  29. 9 0
      app/service.php
  30. 67 0
      app/sys/common.php
  31. 17 0
      app/sys/config/app.php
  32. 9 0
      app/sys/config/view.php
  33. 102 0
      app/sys/controller/Article.php
  34. 391 0
      app/sys/controller/Base.php
  35. 85 0
      app/sys/controller/Category.php
  36. 376 0
      app/sys/controller/FileManager.php
  37. 97 0
      app/sys/controller/Index.php
  38. 46 0
      app/sys/controller/Login.php
  39. 36 0
      app/sys/controller/SysLog.php
  40. 36 0
      app/sys/controller/SysLogin.php
  41. 96 0
      app/sys/controller/SysMenu.php
  42. 106 0
      app/sys/controller/SysRole.php
  43. 176 0
      app/sys/controller/SysUser.php
  44. 36 0
      app/sys/controller/System.php
  45. 8 0
      app/sys/event.php
  46. 15 0
      app/sys/listener/SysUserLog.php
  47. 5 0
      app/sys/middleware.php
  48. 141 0
      app/sys/middleware/Admin.php
  49. 84 0
      app/sys/view/advertise/index.html
  50. 157 0
      app/sys/view/advertise/save.html
  51. 72 0
      app/sys/view/advertise_type/index.html
  52. 73 0
      app/sys/view/advertise_type/save.html
  53. 291 0
      app/sys/view/article/index.html
  54. 147 0
      app/sys/view/article/save.html
  55. 142 0
      app/sys/view/category/index.html
  56. 225 0
      app/sys/view/category/save.html
  57. 88 0
      app/sys/view/file_manager/uploadimg.html
  58. 91 0
      app/sys/view/index/awere.html
  59. 213 0
      app/sys/view/index/index.html
  60. 58 0
      app/sys/view/layout.html
  61. 126 0
      app/sys/view/login/index.html
  62. 138 0
      app/sys/view/module/index.html
  63. 154 0
      app/sys/view/module/save.html
  64. 26 0
      app/sys/view/public/breadcrumb.html
  65. 68 0
      app/sys/view/public/css_js.html
  66. 71 0
      app/sys/view/public/foot_css_js.html
  67. 0 0
      app/sys/view/public/footer.html
  68. 125 0
      app/sys/view/public/header.html
  69. 82 0
      app/sys/view/sys_log/index.html
  70. 117 0
      app/sys/view/sys_login/index.html
  71. 165 0
      app/sys/view/sys_menu/index.html
  72. 103 0
      app/sys/view/sys_menu/save.html
  73. 117 0
      app/sys/view/sys_role/index.html
  74. 120 0
      app/sys/view/sys_role/save.html
  75. 125 0
      app/sys/view/sys_user/index.html
  76. 96 0
      app/sys/view/sys_user/password.html
  77. 241 0
      app/sys/view/sys_user/save.html
  78. 50 0
      composer.json
  79. 32 0
      config/app.php
  80. 29 0
      config/cache.php
  81. 9 0
      config/console.php
  82. 20 0
      config/cookie.php
  83. 60 0
      config/database.php
  84. 24 0
      config/filesystem.php
  85. 12 0
      config/jump.php
  86. 27 0
      config/lang.php
  87. 45 0
      config/log.php
  88. 8 0
      config/middleware.php
  89. 45 0
      config/route.php
  90. 19 0
      config/session.php
  91. 10 0
      config/trace.php
  92. 25 0
      config/view.php
  93. 2 0
      extend/.gitignore
  94. 8 0
      public/.htaccess
  95. 二进制
      public/favicon.ico
  96. 24 0
      public/index.php
  97. 2 0
      public/robots.txt
  98. 19 0
      public/router.php
  99. 2 0
      public/static/.gitignore
  100. 2 0
      public/storage/.gitignore

+ 1 - 0
.example.env

@@ -0,0 +1 @@
+APP_DEBUG = true

[APP]
DEFAULT_TIMEZONE = Asia/Shanghai

[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = test
USERNAME = username
PASSWORD = password
HOSTPORT = 3306
CHARSET = utf8
DEBUG = true

[LANG]
default_lang = zh-cn

+ 6 - 22
.gitignore

@@ -1,22 +1,6 @@
-# ---> Perl
-/blib/
-/.build/
-_build/
-cover_db/
-inc/
-Build
-!Build/
-Build.bat
-.last_cover_stats
-/Makefile
-/Makefile.old
-/MANIFEST.bak
-/META.yml
-/META.json
-/MYMETA.*
-nytprof.out
-/pm_to_blib
-*.o
-*.bs
-/_eumm/
-
+/.idea
+/.vscode
+/vendor
+*.log
+.env
+composer.lock

+ 42 - 0
.travis.yml

@@ -0,0 +1,42 @@
+sudo: false
+
+language: php
+
+branches:
+  only:
+    - stable
+
+cache:
+  directories:
+    - $HOME/.composer/cache
+
+before_install:
+  - composer self-update
+
+install:
+  - composer install --no-dev --no-interaction --ignore-platform-reqs
+  - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip .
+  - composer require --update-no-dev --no-interaction "topthink/think-image:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0"
+  - composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0"
+  - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip .
+
+script:
+  - php think unit
+
+deploy:
+  provider: releases
+  api_key:
+    secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw=
+  file:
+    - ThinkPHP_Core.zip
+    - ThinkPHP_Full.zip
+  skip_cleanup: true
+  on:
+    tags: true

+ 32 - 0
LICENSE.txt

@@ -0,0 +1,32 @@
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn)
+All rights reserved。
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+Apache Licence是著名的非盈利开源组织Apache采用的协议。
+该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
+允许代码修改,再作为开源或商业软件发布。需要满足
+的条件: 
+1. 需要给代码的用户一份Apache Licence ;
+2. 如果你修改了代码,需要在被修改的文件中说明;
+3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
+带有原来代码中的协议,商标,专利声明和其他原来作者规
+定需要包含的说明;
+4. 如果再发布的产品中包含一个Notice文件,则在Notice文
+件中需要带有本协议内容。你可以在Notice中增加自己的
+许可,但不可以表现为对Apache Licence构成更改。 
+具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

+ 1 - 0
app/.htaccess

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

+ 22 - 0
app/AppService.php

@@ -0,0 +1,22 @@
+<?php
+declare (strict_types = 1);
+
+namespace app;
+
+use think\Service;
+
+/**
+ * 应用服务类
+ */
+class AppService extends Service
+{
+    public function register()
+    {
+        // 服务注册
+    }
+
+    public function boot()
+    {
+        // 服务启动
+    }
+}

+ 94 - 0
app/BaseController.php

@@ -0,0 +1,94 @@
+<?php
+declare (strict_types = 1);
+
+namespace app;
+
+use think\App;
+use think\exception\ValidateException;
+use think\Validate;
+
+/**
+ * 控制器基础类
+ */
+abstract class BaseController
+{
+    /**
+     * Request实例
+     * @var \think\Request
+     */
+    protected $request;
+
+    /**
+     * 应用实例
+     * @var \think\App
+     */
+    protected $app;
+
+    /**
+     * 是否批量验证
+     * @var bool
+     */
+    protected $batchValidate = false;
+
+    /**
+     * 控制器中间件
+     * @var array
+     */
+    protected $middleware = [];
+
+    /**
+     * 构造方法
+     * @access public
+     * @param  App  $app  应用对象
+     */
+    public function __construct(App $app)
+    {
+        $this->app     = $app;
+        $this->request = $this->app->request;
+
+        // 控制器初始化
+        $this->initialize();
+    }
+
+    // 初始化
+    protected function initialize()
+    {}
+
+    /**
+     * 验证数据
+     * @access protected
+     * @param  array        $data     数据
+     * @param  string|array $validate 验证器名或者验证规则数组
+     * @param  array        $message  提示信息
+     * @param  bool         $batch    是否批量验证
+     * @return array|string|true
+     * @throws ValidateException
+     */
+    protected function validate(array $data, $validate, array $message = [], bool $batch = false)
+    {
+        if (is_array($validate)) {
+            $v = new Validate();
+            $v->rule($validate);
+        } else {
+            if (strpos($validate, '.')) {
+                // 支持场景
+                [$validate, $scene] = explode('.', $validate);
+            }
+            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
+            $v     = new $class();
+            if (!empty($scene)) {
+                $v->scene($scene);
+            }
+        }
+
+        $v->message($message);
+
+        // 是否批量验证
+        if ($batch || $this->batchValidate) {
+            $v->batch(true);
+        }
+
+        return $v->failException(true)->check($data);
+    }
+
+}

+ 58 - 0
app/ExceptionHandle.php

@@ -0,0 +1,58 @@
+<?php
+namespace app;
+
+use think\db\exception\DataNotFoundException;
+use think\db\exception\ModelNotFoundException;
+use think\exception\Handle;
+use think\exception\HttpException;
+use think\exception\HttpResponseException;
+use think\exception\ValidateException;
+use think\Response;
+use Throwable;
+
+/**
+ * 应用异常处理类
+ */
+class ExceptionHandle extends Handle
+{
+    /**
+     * 不需要记录信息(日志)的异常类列表
+     * @var array
+     */
+    protected $ignoreReport = [
+        HttpException::class,
+        HttpResponseException::class,
+        ModelNotFoundException::class,
+        DataNotFoundException::class,
+        ValidateException::class,
+    ];
+
+    /**
+     * 记录异常信息(包括日志或者其它方式记录)
+     *
+     * @access public
+     * @param  Throwable $exception
+     * @return void
+     */
+    public function report(Throwable $exception): void
+    {
+        // 使用内置的方式记录异常日志
+        parent::report($exception);
+    }
+
+    /**
+     * Render an exception into an HTTP response.
+     *
+     * @access public
+     * @param \think\Request   $request
+     * @param Throwable $e
+     * @return Response
+     */
+    public function render($request, Throwable $e): Response
+    {
+        // 添加自定义异常处理机制
+
+        // 其他错误交给系统处理
+        return parent::render($request, $e);
+    }
+}

+ 8 - 0
app/Request.php

@@ -0,0 +1,8 @@
+<?php
+namespace app;
+
+// 应用请求对象类
+class Request extends \think\Request
+{
+
+}

+ 42 - 0
app/common.php

@@ -0,0 +1,42 @@
+<?php
+// 应用公共文件
+/*无限级分类*/
+function obj_tree($list, $pk = 'id', $pid = 'pid', $child = 'child', $root = 0)
+{
+    $tree=array();
+    foreach ($list as $key => $val) {
+        if ($val->$pid == $root) {
+            unset($list->$key);
+            if (!empty($list)) {
+                $child = obj_tree($list, $pk, $pid, $child, $val[$pk]);
+                if (!empty($child)) {
+                    $val->child=$child;
+                } else {
+                    $val->child = array();
+                }
+            }
+            $tree[]=$val;
+        }
+    }
+    return $tree;
+}
+
+function make_tree($list, $pk = 'id', $pid = 'pid', $child = 'child', $root = 0)
+{
+    $tree=array();
+    foreach ($list as $key => $val) {
+        if ($val[$pid]==$root) {
+            unset($list[$key]);
+            if (!empty($list)) {
+                $child = make_tree($list, $pk, $pid, $child, $val[$pk]);
+                if (!empty($child)) {
+                    $val['child']=$child;
+                } else {
+                    $val['child']= array();
+                }
+            }
+            $tree[]=$val;
+        }
+    }
+    return $tree;
+}

+ 26 - 0
app/common/model/Article.php

@@ -0,0 +1,26 @@
+<?php
+namespace app\common\model;
+
+class Article extends \think\Model
+{
+    public function createTimeArchive()
+    {
+        $create_times = $this->order('create_time desc')->column('create_time');
+        $timeList = [];
+        foreach($create_times as $value) {
+            $yearAndMonth = date("Y-m", $value);
+            $timeList[] = $yearAndMonth;
+        }
+        $timeList = array_unique($timeList);
+        
+        return $timeList;
+    }
+
+    public static function sort($data)
+    {
+        $article = self::find($data['id']);
+        $article->sort = $data['sort'];
+        
+        return $article->save();
+    }
+}

+ 10 - 0
app/common/model/ArticleTags.php

@@ -0,0 +1,10 @@
+<?php
+namespace app\common\model;
+
+class ArticleTags extends \think\Model
+{
+    public static function tagsList($name)
+    {
+        return self::where('tag', $name)->field('id, cid, title, username, titlepic, description, hits, create_time, url, cname')->order('id', 'desc')->paginate();
+    }
+}

+ 47 - 0
app/common/model/Base.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace app\common\model;
+
+use think\model;
+
+class Base extends model
+{
+    // 删除
+    public static function del($id)
+    {
+        try {
+            self::destroy($id);
+            return json(['code' => 1, 'msg' => '删除成功!']);
+        } catch (\Exception $e) {
+            return json(['code' => 0, 'msg' => $e->getMessage()]);
+        }
+    }
+    
+    // 排序修改
+    public static function sort($data)
+    {
+        try {
+            $info = self::find($data['id']);
+            if ($info->sort != $data['sort']) {
+                $info->sort = $data['sort'];
+                $info->save();
+                return json(['code' => 1, 'msg' => '修改成功!']);
+            }
+        } catch (\Exception $e) {
+            return json(['code' => 0, 'msg' => $e->getMessage()]);
+        }
+    }
+
+    // 状态修改 1,正常; 2,非正常
+    public static function state($id)
+    {
+        try {
+            $info         = self::find($id);
+            $info->status = 3 - $info['status'];
+            $info->save();
+            return json(['code' => 1, 'msg' => '修改成功!', 'status'=>$info->status]);
+        } catch (\Exception $e) {
+            return json(['code' => 0, 'msg' => $e->getMessage()]);
+        }
+    }
+}

+ 33 - 0
app/common/model/Category.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace app\common\model;
+
+class Category extends Base
+{
+    public function module()
+    {
+        return $this->belongsTo('Module', 'module_id');
+    }
+
+    // 获取列表
+    public static function getList()
+    {
+        $list = self::with(['module'])
+            ->order(['sort desc', 'id' => 'desc'])
+            ->select();
+        // halt($list);
+        foreach ($list as $k => $v) {
+            if ($list[$k]['module_id']) {
+                $v['module_name'] = $v->module->getData('module_name');
+                $v['table_name'] = $v->module->getData('table_name');
+            }
+        }
+        return $list;
+    }
+
+
+    public static function articleCategoryies()
+    {
+        return self::where('module_id', 19)->where('status', 1)->field('id, parent_id, category_name, category_folder')->select();
+    }
+}

+ 6 - 0
app/common/model/Module.php

@@ -0,0 +1,6 @@
+<?php
+namespace app\common\model;
+
+class Module extends Base
+{
+}

+ 6 - 0
app/common/model/News.php

@@ -0,0 +1,6 @@
+<?php
+namespace app\common\model;
+
+class News extends \think\Model
+{
+}

+ 7 - 0
app/common/model/Search.php

@@ -0,0 +1,7 @@
+<?php
+namespace app\common\model;
+
+class Enewssearch extends \think\Model
+{
+    protected $pk = 'searchid';
+}

+ 86 - 0
app/common/model/SysLog.php

@@ -0,0 +1,86 @@
+<?php
+/**
+ * +----------------------------------------------------------------------
+ * | 管理员日志模型
+ * +----------------------------------------------------------------------
+ */
+
+
+namespace app\common\model;
+
+// 引入框架内置类
+use think\Model;
+use think\facade\Request;
+use think\facade\Session;
+
+
+class SysLog extends Model
+{
+    // 获取列表
+    public static function queryPage(int $pageSize = 0)
+    {
+        $list = self::order('id desc')->paginate($pageSize);
+        
+        return $list;
+    }
+
+    // 管理员日志记录
+    public static function record()
+    {
+        // 入库信息
+        $userid   = Session::get('adminuser.userid', 0);
+        $username   = Session::get('adminuser.username', '');
+        $url       = Request::url();
+        $title     = '';
+        $content   = Request::except(['s', '_pjax']); //s 变量为系统内置的变量,_pjax为js的变量,无记录的必要
+        $ip        = Request::ip();
+        $userAgent = Request::server('HTTP_USER_AGENT');
+
+        // 标题处理
+        $route = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', Request::controller())) . '/' . strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', Request::action()));
+        
+        $active =  \app\common\model\SysMenu::where('url', $route)->find();
+
+        $title =  $active['name'];
+
+        // 内容处理(过长的内容和涉及密码的内容不进行记录)
+        if ($content) {
+            foreach ($content as $k => $v) {
+                if (is_string($v) && strlen($v) > 200 || stripos($k, 'password') !== false || stripos($k, 'newpassword') !== false || stripos($k, 'repassword') !== false) {
+                    unset($content[$k]);
+                }
+            }
+        }
+
+        // 插入数据
+        if (!empty($title)) {
+            // 查询管理员上一条数据
+            $result = self::where('userid', '=', $userid)
+                ->order('id', 'desc')
+                ->find();
+            if ($result) {
+                if ($result->url != $url) {
+                    self::create([
+                        'title'      => $title ? $title : '',
+                        'username'   => $username,
+                        'content'    => ! is_scalar($content) ? json_encode($content, JSON_UNESCAPED_UNICODE) : $content,
+                        'url'        => $url,
+                        'userid'   => $userid,
+                        'user_agent' => $userAgent,
+                        'ip'         => $ip
+                    ]);
+                }
+            } else {
+                self::create([
+                    'title'      => $title ? $title : '',
+                    'username'   => $username,
+                    'content'    => ! is_scalar($content) ? json_encode($content, JSON_UNESCAPED_UNICODE) : $content,
+                    'url'        => $url,
+                    'userid'   => $userid,
+                    'user_agent' => $userAgent,
+                    'ip'         => $ip
+                ]);
+            }
+        }
+    }
+}

+ 39 - 0
app/common/model/SysLogin.php

@@ -0,0 +1,39 @@
+<?php
+/**
+ * +----------------------------------------------------------------------
+ * | 管理员登录日志模型
+ * +----------------------------------------------------------------------
+ */
+namespace app\common\model;
+
+// 引入框架内置类
+use think\Model;
+
+class SysLogin extends Model
+{
+    protected $pk = 'loginid';
+
+    // 管理员登录日志记录
+    public static function record($info)
+    {
+        // 入库
+        self::create([
+            'userid'    => $info->userid,
+            'username'  => $info->username,
+            'logintime' => $info->per_time,
+            'loginip'   => $info->per_ip,
+            'status'    => 0,
+        ]);
+    }
+
+    public static function queryPage($limit = 30, $userid = 0)
+    {
+        if ($userid==0) {
+            $list = self::order("loginid desc")->paginate($limit);
+        } else {
+            $list = self::where('userid', $userid)->order("loginid desc")->paginate($limit);
+        }
+        
+        return $list;
+    }
+}

+ 52 - 0
app/common/model/SysLoginFail.php

@@ -0,0 +1,52 @@
+<?php
+/**
+ * +----------------------------------------------------------------------
+ * | 管理员登录日志模型
+ * +----------------------------------------------------------------------
+ */
+namespace app\common\model;
+
+// 引入框架内置类
+use app\common\model\Base;
+
+class SysLoginFail extends Base
+{
+    protected $pk = 'ip';
+
+    /**
+     * 检测登录 ip
+     * @param string $ip 登录ip
+     * @param int $time 错误时间
+     * @return bool
+     */
+    public static function checkIp(string $ip, int $time): bool
+    {
+        $maxNums = 10;
+
+        $loginFail = self::find($ip);
+
+        return  empty($loginFail) || $time - $loginFail->time > 3600 || $loginFail->num > $maxNums ? true : false;
+    }
+
+    /**
+     * 记录登录错误次数和最后错误时间
+     * @param string $ip 登录ip
+     * @param int $time 错误时间
+     */
+    public static function saveFail(string $ip, int $time)
+    {
+        $loginFail = self::find($ip);
+        if ($loginFail) {
+            $loginFail->num += 1;
+            $loginFail->lasttime = $time;
+        } else {
+            $loginFail = new static();
+            
+            $loginFail->ip = $ip;
+            $loginFail->num = 1;
+            $loginFail->lasttime = $time;
+        }
+        
+        $loginFail->save();
+    }
+}

+ 33 - 0
app/common/model/SysMenu.php

@@ -0,0 +1,33 @@
+<?php
+namespace app\common\model;
+
+use think\Model;
+use think\facade\Db;
+
+class SysMenu extends Model
+{
+    public static function getUserMenuList($rid)
+    {
+        if ($rid === 1) {   // 超级管理员
+            $data = self::field('id, pid, name, url, type, icon')->select();
+        } else {
+            $roleModel = new SysRole();
+            $permission_ids = $roleModel->getpermissionIds($rid);
+            $data = self::where('id', 'IN', $permission_ids)->field('id, pid, name, url, icon')->select();
+        }
+        return $data;
+    }
+
+    public static function queryButtonPermissionsByRoleid($rid)
+    {
+        if ($rid === 1) {   // 超级管理员
+            $data = self::where('type', 2)->column('url');
+        } else {
+            $roleModel = new SysRole();
+            $permission_ids = $roleModel->getpermissionIds($rid);
+            $data = self::where('id', 'IN', $permission_ids)->where('type', 2)->column('url');
+        }
+
+        return $data;
+    }
+}

+ 19 - 0
app/common/model/SysRole.php

@@ -0,0 +1,19 @@
+<?php
+namespace app\common\model;
+
+use \think\Model;
+
+class SysRole extends Model
+{
+    protected $pk = 'roleid';
+
+    public function getIdNameList()
+    {
+        return $this->column('id, name');
+    }
+
+    public function getpermissionIds($id)
+    {
+        return $this->where('id', $id)->value('permissions');
+    }
+}

+ 120 - 0
app/common/model/SysUser.php

@@ -0,0 +1,120 @@
+<?php
+
+namespace app\common\model;
+
+// 引入框架内置类
+use think\Model;
+use think\facade\Db;
+use think\facade\Request;
+use think\facade\Event;
+
+use app\common\model\SysLoginFail;
+use app\common\model\SysLogin;
+use app\common\model\SysRole;
+use think\facade\Session;
+
+class SysUser extends Model
+{
+    protected $pk = 'userid';
+
+    public static function queryList($roleid)
+    {
+        if ($roleid == 1) {
+            return Db::table('phome_sys_user')->alias('u')
+                ->join('phome_sys_role r', 'u.roleid = r.roleid')
+                ->field('u.userid,u.username,u.nickname,u.truename,u.email,u.status,u.per_time,u.per_ip,u.login_num,u.create_time,r.name as rolename')
+                ->select();
+        } else {
+            return Db::table('phome_sys_user')->alias('u')
+                ->join('phome_sys_role r', 'u.roleid = r.roleid')
+                ->where('u.roleid', '<>', 1)
+                ->field('u.userid,u.username,u.nickname,u.truename,u.email,u.status,u.per_time,u.per_ip,u.login_num,u.create_time,r.name as rolename')
+                ->select();
+        }
+    }
+
+    public function getUsernames($role_id = null)
+    {
+        if ($role_id == null) {
+            return $this->column('id, username');
+        } else {
+            $data = $this->where('role_id', $role_id)->column('username');
+
+            if ($data) {
+                return implode(',', $data);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    public static function checkLogin()
+    {
+        $ip = Request::ip();
+        $time = time();
+        $datetime = date('Y-m-d H:i:s', $time);
+
+        if (!SysLoginFail::checkIp($ip, $time)) {
+            return json(['code' => 1, 'msg' => '登录次数超过10次, 请等1小时后再试']);
+        }
+
+        $username  = Request::param("username", '', 'strip_tags');
+        $password  = Request::param("password", '', 'strip_tags');
+
+        if (!$username || !$password) {
+            return json(['code' => 1, 'msg' => '用户名/密码/验证码不能为空']);
+        }
+
+        if (false === Request::checkToken('__token__')) {
+            return json(['error' => 1, 'msg' => '验证有误']);
+        }
+
+        $info = self::where('username', $username)->find();
+
+        if (!$info) {
+            SysLoginFail::saveFail($ip, $time);
+            return json(['code' => 2, 'msg' => '用户名/密码不正确']);
+        }
+
+        if (md5($password . $info->salt) != $info->password) {
+            // 记录失败
+            SysLoginFail::saveFail($ip, $time);
+
+            SysLogin::record($info->userid, $info->username, $datetime, $ip, 1);
+
+            return json(['code' => 2, 'msg' => '用户名/密码不正确']);
+        }
+
+        if ($info->status == 1) {
+            // 更新登录IP,登录时间和更新次数
+            $perTime = $info->per_time;
+            $perIp = $info->per_ip;
+
+            $info->per_time = date('Y-m-d H:i:s', $time);
+            $info->per_ip = $ip;
+            $info->login_num += 1;
+
+            $info->save();
+
+            Session::set('adminuser', [
+                'userid'   => $info->userid,
+                'username' => $info->username,
+                'perTime'  => $perTime,
+                'perIp'    => $perIp,
+                'nickname' => $info->nickname,
+                'avatar'   => $info->avatar,
+                'loginNum' => $info->loginNum,
+                'roleid'   => $info->roleid
+            ]);
+
+            // 记录登录
+            SysLogin::record($info->userid, $info->username, $datetime, $ip);
+
+            return json(['code' => 0, 'msg' => '登录成功']);
+        } else {
+            // 记录失败
+            SysLogin::record($info->userid, $info->username, $datetime, $ip, 1);
+            return json(['code' => 2, 'msg' => '用户已被禁用!']);
+        }
+    }
+}

+ 14 - 0
app/common/model/System.php

@@ -0,0 +1,14 @@
+<?php
+/**
+ * +----------------------------------------------------------------------
+ * | 管理员登录日志模型
+ * +----------------------------------------------------------------------
+ */
+namespace app\common\model;
+
+// 引入框架内置类
+use think\Model;
+
+class System extends Model
+{
+}

+ 17 - 0
app/event.php

@@ -0,0 +1,17 @@
+<?php
+// 事件定义文件
+return [
+    'bind'      => [
+    ],
+
+    'listen'    => [
+        'AppInit'  => [],
+        'HttpRun'  => [],
+        'HttpEnd'  => [],
+        'LogLevel' => [],
+        'LogWrite' => [],
+    ],
+
+    'subscribe' => [
+    ],
+];

+ 17 - 0
app/index/controller/Index.php

@@ -0,0 +1,17 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+
+class Index extends BaseController
+{
+    public function index()
+    {
+        return '<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:) </h1><p> ThinkPHP V' . \think\facade\App::version() . '<br/><span style="font-size:30px;">14载初心不改 - 你值得信赖的PHP框架</span></p><span style="font-size:25px;">[ V6.0 版本由 <a href="https://www.yisu.com/" target="yisu">亿速云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=64890268" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ee9b1aa918103c4fc"></think>';
+    }
+
+    public function hello($name = 'ThinkPHP6')
+    {
+        return 'hello,' . $name;
+    }
+}

+ 10 - 0
app/middleware.php

@@ -0,0 +1,10 @@
+<?php
+// 全局中间件定义文件
+return [
+    // 全局请求缓存
+    // \think\middleware\CheckRequestCache::class,
+    // 多语言加载
+    // \think\middleware\LoadLangPack::class,
+    // Session初始化
+    \think\middleware\SessionInit::class
+];

+ 9 - 0
app/provider.php

@@ -0,0 +1,9 @@
+<?php
+use app\ExceptionHandle;
+use app\Request;
+
+// 容器Provider定义文件
+return [
+    'think\Request'          => Request::class,
+    'think\exception\Handle' => ExceptionHandle::class,
+];

+ 9 - 0
app/service.php

@@ -0,0 +1,9 @@
+<?php
+
+use app\AppService;
+
+// 系统服务定义文件
+// 服务在完成全局初始化之后执行
+return [
+    AppService::class,
+];

+ 67 - 0
app/sys/common.php

@@ -0,0 +1,67 @@
+<?php
+// 这是系统自动生成的公共文件
+
+/**
+ * PHP格式化字节大小
+ * @param number $size      字节数
+ * @param string $delimiter 数字和单位分隔符
+ * @return string            格式化后的带单位的大小
+ */
+function format_bytes(int $size, string $delimiter = ''): string
+{
+    $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
+    for ($i = 0; $size >= 1024 && $i < 5; $i++) {
+         $size = (int) $size / 1024;
+    }
+    return round($size, 2) . $delimiter . $units[$i];
+}
+
+/**
+ * 使用递归遍历获取文件夹的大小
+ */
+function get_dir_size($dirname)
+{
+    static $tot; //这里把$tot定义为静态的,表示$tot全局只有这一个变量
+    $ds = opendir($dirname); //创建一个目录资源, 传入的目录就是资源
+    while ($file = readdir($ds)) { //从目录中读取到条目
+        //这里的$path 表示这个路径下的文件夹,如果不这么去定义,里边执行递归语句的时候,找不到是那个文件夹
+        $path = $dirname . "/" . $file;
+
+        //判断,如果是 . 或者 ..的目录就过滤掉
+        if ($file != "." && $file != "..") {
+            if (is_dir($path)) { //判断如果找到的是目录
+                get_dir_size($path); //如果得到是文件夹,然后递归调用一次方法传入的$path文件夹路径就是判断得到的文件夹赋值给$dirname
+            } else {
+                $tot += filesize($path);
+            }
+        }
+    }
+    return $tot;
+}
+
+/**
+ * 递归删除文件夹
+ */
+function deldir($path)
+{
+    //如果是目录则继续
+    if (is_dir($path)) {
+        //扫描一个文件夹内的所有文件夹和文件并返回数组
+        $p = scandir($path);
+        foreach ($p as $val) {
+            //排除目录中的.和..
+            if ($val != "." && $val != "..") {
+                //如果是目录则递归子目录,继续操作
+                if (is_dir($path . $val)) {
+                    //子目录中操作删除文件夹和文件
+                    deldir($path . $val . DIRECTORY_SEPARATOR);
+                    //目录清空后删除空文件夹
+                    @rmdir($path . $val . DIRECTORY_SEPARATOR);
+                } else {
+                    //如果是文件直接删除
+                    unlink($path . $val);
+                }
+            }
+        }
+    }
+}

+ 17 - 0
app/sys/config/app.php

@@ -0,0 +1,17 @@
+<?php
+// +----------------------------------------------------------------------
+// | 应用设置
+// +----------------------------------------------------------------------
+
+return [
+    // 应用Trace
+    'app_trace'              => true,
+    // 是否开启权限认证
+    'auth_on'               => true,
+    // 默认分页显示数量
+    'page_size'             => 20,
+    // 版本信息
+    'sys_version'          => '0.1',
+    // 默认密码
+    'default_password' => 'qwe123',
+];

+ 9 - 0
app/sys/config/view.php

@@ -0,0 +1,9 @@
+<?php
+// +----------------------------------------------------------------------
+// | 模板设置
+// +----------------------------------------------------------------------
+
+return [
+    // 开启模板布局
+    'layout_on'    => true,
+];

+ 102 - 0
app/sys/controller/Article.php

@@ -0,0 +1,102 @@
+<?php
+declare (strict_types = 1);
+/**
+ * +----------------------------------------------------------------------
+ * 后台文章控制制器
+ * @author huwhis@163.com
+ * @version   0.0.1
+ * +----------------------------------------------------------------------
+ */
+namespace app\sys\controller;
+
+// 引入框架内置类
+use think\facade\View;
+
+use app\common\model\Category as CategoryModel;
+
+/**
+ * 文章管理  
+
+ */
+class Article extends Base
+{
+    protected $modelName = "Article";
+
+    public function index()
+    {
+        $categories = CategoryModel::articleCategoryies();
+
+        $categories_url = [];
+        $categories_name = [];
+        foreach ($categories as $value) {
+            $categories_url[$value->id] = $value->url;
+            $categories_name[$value->id] = $value->category_name;
+        }
+
+        $category_tree = obj_tree($categories);
+
+        $where = [];
+
+        if ($cid = $this->app->request->param('cid')) {
+            $where['cid'] = $cid;
+        }
+
+        $data = $this->modelName::where($where)->field('id,cid,title,username,hits,sort,create_time')->order(['sort desc', 'id desc'])->paginate();
+
+        View::assign('category_tree', $category_tree);
+        View::assign('categories_url', $categories_url);
+        View::assign('categories_name', $categories_name);
+        View::assign('data', $data);
+        View::assign('cid', $cid);
+        return View::fetch();
+    }
+
+    public function save($id = 0, $cid = 0)
+    {
+        if ($this->app->request->isPost()) {
+            $params = $this->app->request->param();
+
+            if (!$params['cid']) {
+                $this->error('请选择栏目');
+            }
+            if ($params['title'] == '') {
+                $this->error("标题不能为空");
+            }
+            $params['content'] =  $params['editorValue'];
+            unset($params['editorValue']);
+            try {
+                if ($params['id'] != 0) {
+                   $this->modelName::update($params);
+                } else {
+                    $params['userid'] = $this->getSysUser()->userid;
+                    $params['username'] = $this->getSysUser()->username;
+                    unset($params['id']);
+                    $this->modelName::create($params);
+                }
+            } catch (\Exception $e) {
+                $msg = $e->getMessage();
+
+                $this->error("错误提示:".$msg);
+            }
+            $this->success('操作成功', (string) url('index?cid='.$params['cid']));
+        } else {
+            if (!$cid) {
+                $this->error('请选择栏目');
+            }
+
+            if ($id) {
+                $data = $this->modelName::find($id);
+            } else {
+                $data = null;
+            }
+            
+            $category_info = CategoryModel::field('id, parent_id, category_name')->find($cid);
+    
+            View::assign('cid', $cid);
+            View::assign('category_info', $category_info);
+            View::assign('data', $data);
+         
+            return View::fetch();
+        }
+    }
+}

+ 391 - 0
app/sys/controller/Base.php

@@ -0,0 +1,391 @@
+<?php
+
+/**
+ * +----------------------------------------------------------------------
+ * | 基础控制器
+ * +----------------------------------------------------------------------
+ * AUTHOR: huwhois
+ * EMAIL: huwhois@163.com
+ * DATETIME: 2020/03/08
+ */
+
+declare(strict_types=1);
+
+namespace app\sys\controller;
+
+// 引入框架内置类
+use think\App;
+use think\exception\HttpResponseException;
+use think\exception\ValidateException;
+use think\facade\Cache;
+use think\facade\Config;
+use think\facade\Request;
+use think\facade\Session;
+use think\facade\View;
+use think\Response;
+use think\Validate;
+
+
+/**
+ * 控制器基础类
+ */
+abstract class Base
+{
+    use \liliuwei\think\Jump;
+    /**
+     * Request实例
+     * @var \think\Request
+     */
+    protected $request;
+
+    /**
+     * 应用实例
+     * @var \think\App
+     */
+    protected $app;
+
+    /**
+     * 是否批量验证
+     * @var bool
+     */
+    protected $batchValidate = false;
+
+    /**
+     * 控制器中间件
+     * @var array
+     */
+    protected $middleware = ['app\sys\middleware\Admin'];
+
+    // /**
+    //  * 分页数量
+    //  * @var int
+    //  */
+    // protected $pageSize = 0;
+
+    // /**
+    //  * 系统设置
+    //  * @var array
+    //  */
+    // protected $system = [];
+    /**
+     * 用户信息
+     */
+    protected $sysUser;
+
+    /**
+     * 构造方法
+     * @access public
+     * @param App $app 应用对象
+     */
+    public function __construct(App $app)
+    {
+        $this->app     = $app;
+        $this->request = $this->app->request;
+
+        // 控制器初始化
+        $this->initialize();
+    }
+
+    // 初始化
+    protected function initialize()
+    {
+        // 左侧菜单
+        $rid = Session::get('adminuser.roleid');
+
+        $menus = Cache::get('menus');
+        if (!$menus) {
+            $menus = \app\common\model\SysMenu::getUserMenuList($rid);
+
+            Cache::set('menus', $menus);
+        }
+
+        $controller =  strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', Request::controller()));
+
+        $action = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', Request::action()));
+
+        $route =  $controller . '/' . $action;
+
+        // layer open
+        if (Request::has('_layer')) {
+            View::assign('layer', true);
+        } else {
+            View::assign('layer', false);
+        }
+
+        // // pjax
+        // if (Request::has('_pjax')) {
+        //     View::assign(['pjax' => true]);
+        // } else {
+        //     View::assign(['pjax' => false]);
+        // }
+
+        // 当前活动菜单父id & 面包导航
+        $this->getBreadCrumb($route);
+
+        $menus = obj_tree($menus);
+        View::assign('menus', $menus);
+
+
+
+        // 内容管理,获取栏目列表
+        // if (class_exists('\app\common\model\Cate')) {
+        //     $cates = \app\common\model\Cate::getList();
+        // }
+        // View::assign(['cates' => unlimitedForLayer($cates['data'] ?? [])]);
+
+        // index应用地址
+        // $domainBind = Config::get('app.domain_bind');
+        // if ($domainBind) {
+        //     $domainBindKey = array_search('index', $domainBind);
+        //     $domainBindKey = $domainBindKey == '*' ? 'www.' : ($domainBindKey ? $domainBindKey . '.' : '');
+        //     $indexUrl      = Request::scheme() . '://' . $domainBindKey . Request::rootDomain() . '/';
+        // }
+        // View::assign(['indexUrl' => $indexUrl ?? '/']);
+
+        // 查询系统设置
+        // $system       = \app\common\model\System::find(1);
+        // $this->system = $system;
+        View::assign([
+            // 'system' => $system,
+            'sys_version'    => Config::get('app.sys_version'),
+            'default_lang' => env('lang.default_lang', 'en')
+        ]);
+    }
+
+    /**
+     * 当前活动菜单父id & 面包导航
+     */
+    public function getBreadCrumb($route)
+    {
+        $active =  \app\common\model\SysMenu::where('url', $route)->find();
+
+        $active_pid = 0;
+
+        $breadCrumb = [];
+
+        if ($active) {
+            $active_pid = $active->pid;
+
+            $result = \app\common\model\SysMenu::find($active_pid);
+            // 如果还有上级
+            if ($result) {
+                $res = \app\common\model\SysMenu::find($result->pid);
+                // 如果还有上级
+                if ($res) {
+                    $active_pid = $result->pid;
+                    $breadCrumb[] = [
+                        'url'   => $res['url'],
+                        'title' => $res['name'],
+                    ];
+                }
+                $breadCrumb[] = [
+                    'url'   => $result['url'],
+                    'title' => $result['name'],
+                ];
+            }
+            $breadCrumb[] = [
+                'url'   => $active['url'],
+                'title' => $active['name'],
+            ];
+        } else {
+            $breadCrumb[] = [
+                'url'   => null,
+                'title' => '我的桌面',
+            ];
+        }
+
+        View::assign('active_pid', $active_pid);
+
+        View::assign(['breadCrumb' => $breadCrumb]);
+    }
+
+    /**
+     * 验证数据
+     * @access protected
+     * @param array        $data     数据
+     * @param string|array $validate 验证器名或者验证规则数组
+     * @param array        $message  提示信息
+     * @param bool         $batch    是否批量验证
+     * @return array|string|true
+     * @throws ValidateException
+     */
+    protected function validate(array $data, $validate, array $message = [], bool $batch = false)
+    {
+        if (is_array($validate)) {
+            $v = new Validate();
+            $v->rule($validate);
+        } else {
+            if (strpos($validate, '.')) {
+                // 支持场景
+                list($validate, $scene) = explode('.', $validate);
+            }
+            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
+            $v     = new $class();
+            if (!empty($scene)) {
+                $v->scene($scene);
+            }
+        }
+
+        $v->message($message);
+
+        //是否批量验证
+        if ($batch || $this->batchValidate) {
+            $v->batch(true);
+        }
+
+        $result = $v->failException(false)->check($data);
+        if (true !== $result) {
+            return $v->getError();
+        } else {
+            return $result;
+        }
+    }
+
+    // /**
+    //  * 返回封装后的API数据到客户端
+    //  * @param mixed   $data   要返回的数据
+    //  * @param integer $code   返回的code
+    //  * @param mixed   $msg    提示信息
+    //  * @param string  $type   返回数据格式
+    //  * @param array   $header 发送的Header信息
+    //  * @return Response
+    //  */
+    // protected function result($data, int $code = 0, $msg = '', string $type = '', array $header = []): Response
+    // {
+    //     $result = [
+    //         'code' => $code,
+    //         'msg'  => $msg,
+    //         'time' => time(),
+    //         'data' => $data,
+    //     ];
+
+    //     $type     = $type ?: 'json';
+    //     $response = Response::create($result, $type)->header($header);
+
+    //     throw new HttpResponseException($response);
+    // }
+
+    // // 列表
+    // public function index()
+    // {
+    //     // 获取主键
+    //     $pk = MakeBuilder::getPrimarykey($this->tableName);
+    //     // 获取列表数据
+    //     $columns = MakeBuilder::getListColumns($this->tableName);
+    //     // 获取搜索数据
+    //     $search = MakeBuilder::getListSearch($this->tableName);
+    //     // 获取当前模块信息
+    //     $model  = '\app\common\model\\' . $this->modelName;
+    //     $module = \app\common\model\Module::where('table_name', $this->tableName)->find();
+    //     // 搜索
+    //     if (Request::param('getList') == 1) {
+    //         $where         = MakeBuilder::getListWhere($this->tableName);
+    //         $orderByColumn = Request::param('orderByColumn') ?? $pk;
+    //         $isAsc         = Request::param('isAsc') ?? 'desc';
+    //         return $model::getList($where, $this->pageSize, [$orderByColumn => $isAsc]);
+    //     }
+    //     // 检测单页模式
+    //     $isSingle = MakeBuilder::checkSingle($this->modelName);
+    //     if ($isSingle) {
+    //         return $this->jump($isSingle);
+    //     }
+    //     // 获取新增地址
+    //     $addUlr = MakeBuilder::getAddUrl($this->tableName);
+    //     // 构建页面
+    //     return TableBuilder::getInstance()
+    //         ->setUniqueId($pk)                              // 设置主键
+    //         ->addColumns($columns)                          // 添加列表字段数据
+    //         ->setSearch($search)                            // 添加头部搜索
+    //         ->addColumn('right_button', '操作', 'btn')      // 启用右侧操作列
+    //         ->addRightButtons($module->right_button)        // 设置右侧操作列
+    //         ->addTopButtons($module->top_button)            // 设置顶部按钮组
+    //         ->setAddUrl($addUlr)                            // 设置新增地址
+    //         ->fetch();
+    // }
+
+    /**
+     * 新增 or 修改
+     * @param int $id  info id 
+     * @return mix 
+     */
+    public function save($id = 0)
+    {
+        if ($this->app->request->isPost()) {
+            $params = $this->app->request->param();
+
+            try {
+                $id = $params['id']; 
+                unset($params['id']);
+
+                if ($id != 0) {
+                    $this->modelName::update($params, ['id' => $id]);
+                } else {
+                    $this->modelName::create($params);
+                }
+            } catch (\Exception $e) {
+                $msg = $e->getMessage();
+
+                $this->error("错误提示:".$msg);
+            }
+            $this->success('操作成功', (String)url('index'));
+        } else {
+            if ($id != 0) {
+                $data = $this->modelName::find($id);
+            } else {
+                $data = null;
+            }
+            
+            View::assign('data', $data);
+
+            return View::fetch();
+        }
+    }
+
+    /**
+     * 删除
+     * @param int|array $id  info id
+     * @return array
+     */
+    public function delete($id)
+    {
+        if (Request::isPost()) {
+            if ($this->modelName::destroy($id)) {
+                return ['code' => 1,'msg'=>'删除成功'];
+            } else {
+                return ['code' => 0,'msg'=>'删除失败'];
+            }
+        }
+    }
+
+    // 排序
+    public function sort()
+    {
+        if (Request::isPost()) {
+            $data  = Request::post();
+            return $this->modelName::sort($data);
+        }
+    }
+
+    // 状态变更
+    public function state(string $id)
+    {
+        if (Request::isPost()) {
+            return $this->modelName::state($id);
+        }
+    }
+
+    // // 导出
+    // public function export()
+    // {
+    //     \app\common\model\Base::export($this->tableName, $this->modelName);
+    // }
+
+    // 获取当前登录用户信息
+    protected function getSysUser()
+    {
+        if (!$this->sysUser) {
+            $this->sysUser = \app\common\model\SysUser::where('userid', session('adminuser.userid'))->find();
+        }
+        return $this->sysUser;
+    }
+}

+ 85 - 0
app/sys/controller/Category.php

@@ -0,0 +1,85 @@
+<?php
+declare (strict_types = 1);
+/**
+ * +----------------------------------------------------------------------
+ * | 栏目控制制器
+ * +----------------------------------------------------------------------
+ */
+namespace app\admin\controller;
+
+// 引入框架内置类
+use think\facade\View;
+
+class Category extends Base
+{
+    protected  $modelName = "\app\common\model\Category";
+
+    public function index()
+    {
+        $list = $this->modelName::getList();
+
+        $list = obj_tree($list, $pk = 'id', $pid = 'parent_id');
+
+        View::assign('list', $list);
+
+        return View::fetch();
+    }
+
+    /**
+     * 新增 or 修改
+     * @param int $id  info id 
+     * @return mix 
+     */
+    public function save($id = 0)
+    {
+        if ($this->app->request->isPost()) {
+            $params = $this->app->request->param();
+            
+            if ($params['category_name'] == '') {
+                $this->error("名称不能为空");
+            }
+
+            try {
+                $id = $params['id']; 
+                unset($params['id']);
+
+                if ($id != 0) {
+                    $this->modelName::update($params, ['id' => $id]);
+                } else {
+                    $this->modelName::create($params);
+                }
+            } catch (\Exception $e) {
+                $msg = $e->getMessage();
+
+                $this->error("错误提示:".$msg);
+            }
+            $this->success('操作成功', (String)url('index'));
+        } else {
+            if ($id != 0) {
+                $data = $this->modelName::find($id);
+            } else {
+                $data = null;
+            }
+            
+            View::assign('data', $data);
+
+            $list = obj_tree($this->modelName::select());
+
+            View::assign('list', $list);
+
+            return View::fetch();
+        }
+    }
+
+    // 删除
+    public function delete($id)
+    {
+        if ($this->app->request->isPost()) {
+            if ($this->modelName::where('parent_id', $id)->value('id')) {
+                return ['code'=>0, 'msg'=>'子栏目不为空, 若要删除请先清空子栏目'];
+            }
+
+            return $this->modelName::del($id);
+        }
+    }
+}

+ 376 - 0
app/sys/controller/FileManager.php

@@ -0,0 +1,376 @@
+<?php
+/**
+ * upload文件浏览器
+ *
+ * @version      0.0.0
+ * @author      by huwhois
+ * @time        2017/11/27
+ */
+namespace app\admin\controller;
+
+use think\App;
+use think\Request;
+use think\facade\View;
+use think\Image;
+
+class FileManager extends Base
+{
+    protected $img_path = ''; 
+    protected $t_suffix = '_thumb';
+    protected $t_width = 400;
+    protected $t_height = 300;
+    protected $request;
+
+    public function uploadimg($thumb=false, $width=400, $height=300)
+    {
+        if ($this->request->isPost()) {
+            $file = $this->request->file('upload_file');
+            if ($file) {
+                try {
+                    validate(
+                        [
+                            'file' => [
+                                // 限制文件大小(单位b),这里限制为20M
+                                'fileSize' => 20 * 1024 * 1024,
+                                // 限制文件后缀,多个后缀以英文逗号分割
+                                'fileExt'  => 'jpg,png,gif,jpeg,webp'
+                            ]
+                        ],
+                        [
+                            'file.fileSize' => '文件太大',
+                            'file.fileExt' => '不支持的文件后缀',
+                        ]
+                    )->check(['file' => $file]);
+
+                    $savename = \think\facade\Filesystem::disk('public')->putFile( '/', $file);
+                    $thumbname = "";
+                    if ($thumb) {
+                        $thumbname = $this->makeThumb($savename, $width, $height);
+                    }
+                } catch (\think\exception\ValidateException $e) {
+                    $this->error($e->getMessage());
+                }
+                unset($file);
+                return [
+                    'code'=>2,
+                    'picname' => '/storage/' . str_replace('\\', '/', $savename),
+                    'thumbname'=>'/storage/' . $thumbname
+                ];
+            } else {
+                $this->error('图片不能为空');  
+            }
+        } else {
+            View::assign('thumb', $thumb);
+            View::assign('width', $width);
+            View::assign('height', $height);
+
+            return View::fetch();
+        }
+    }
+
+    /**
+     * 生成缩略图
+     * @param string $filename 必须参数, 图片路径名(相对or绝对)(/public/uploads/...)
+     * @param int $width 缩略图宽值, 默认 384px;
+     * @param int $height 缩略图高值, 默认 224px;
+     * @param int $type 缩略图裁剪方式, 默认值 1, 固定尺寸缩放; 其他: 1, 等比例缩放;
+     * 2, 缩放后填充; 3, 居中裁剪; 4, 左上角裁剪; 5, 右下角裁剪
+     * @return string $thumbname 缩略图文件名
+     */
+    public function makeThumb($filename, $width =384, $height = 224, $type = 1)
+    {
+        $file ='./storage/' . str_replace('\\', '/', $filename);
+        
+        $ext = pathinfo($file, PATHINFO_EXTENSION);
+
+        $thumbname = str_replace('.' . $ext, '', str_replace('\\', '/', $filename)) . $this->t_suffix . '.' . $ext;
+
+        $image = Image::open($file);
+
+        $result = $image->thumb($width, $height, $type)->save('./storage/' . $thumbname);
+
+        return $result ? $thumbname : '';
+    }
+
+    public function index()
+    {
+        $data = $this->explorer();
+
+        View::assign('dirs', $data['dirs']);
+        View::assign('files', $data['files']);
+        View::assign('counts', $data['counts']);
+        View::assign('activeurl', $data['activeurl']);
+        View::assign('activepath', $data['activepath']);
+
+        return View::fetch();
+    }
+
+    /**
+     * 获取当前文件相关信息
+     * @return array $data 文件信息数据组
+     */
+    protected function explorer()
+    {
+        $param = $this->request->param();
+ 
+        $activepath =  isset($param['activepath'])? $param['activepath'] : '';
+        
+        $inpath = "";
+
+        $inpath = $this->img_path . $activepath;
+
+        $dirhandle = scandir($inpath);
+       
+        $dirs = $files = [];
+
+        define('BKM', 1024);
+        
+        foreach ($dirhandle as $val) {
+            if ($val == "." || $val == "..") {
+                continue;
+            } elseif (is_dir($inpath . DIRECTORY_SEPARATOR . $val)) {
+                $dirs[] = $val;
+            } else {
+                $arr = [];
+                $file = '';
+                $file = $inpath . DIRECTORY_SEPARATOR . $val;
+                
+                $arr['name'] = $val;
+                
+                $arr['extension'] = pathinfo($file, PATHINFO_EXTENSION);
+
+                $filesize = floatval(filesize($file));
+                if ($filesize>BKM) {
+                    $filesize =  round($filesize/BKM, 2);
+
+                    if ($filesize>BKM) {
+                        $filesize = round($filesize/BKM, 2);
+                    
+                        $filesize .="M";
+                    } else {
+                        $filesize .="K";
+                    }
+                } else {
+                    $filesize .="B";
+                }
+                $arr['size'] = $filesize;
+
+                $filetime = filemtime("$file");
+                $arr['time'] = date("Y-m-d H:i:s", $filetime);
+
+                $files[] = $arr;
+            }
+        }
+        $counts = count($dirs)+count($files);
+
+        $activeurl = preg_replace("#[\/][^\/]*$#i", "", $activepath);
+       
+        $data = [
+            'dirs'      => $dirs,
+            'files'     => $files,
+            'counts'    => $counts,
+            'activeurl' => $activeurl,
+            'activepath'=> $activepath,
+        ];
+
+        return $data;
+    }
+
+    public function delDir()
+    {
+        if ($this->request->isAjax()) {
+             $activepath = $this->request->param('activepath');
+            $activepath = str_replace('/', DIRECTORY_SEPARATOR, $activepath);
+            $dir = $this->request->param('dir');
+
+            $dir_name = app()->getRootPath.'public'.DIRECTORY_SEPARATOR.$activepath.DIRECTORY_SEPARATOR.$dir;
+    
+            if (count(scandir($dir_name)) > 2) {
+                return ['status'=>1,'msg'=>'不可删除非空目录'];
+            }
+
+            if (rmdir($dir_name)) {
+                return ['status'=>2,'msg'=>'success'];
+            } else {
+                return ['status'=>0,'msg'=>'failed'];
+            }
+        }
+    }
+    
+    public function del()
+    {
+        if ($this->request->isAjax()) {
+            $activepath = $this->request->param('activepath');
+            $activepath = str_replace('/', DIRECTORY_SEPARATOR, $activepath);
+            $filename = $this->request->param('filename');
+
+            if (unlink(app()->getRootPath.'public'.DIRECTORY_SEPARATOR.$activepath.DIRECTORY_SEPARATOR.$filename)) {
+                return ['status'=>1,'msg'=>'success'];
+            } else {
+                return ['status'=>0,'msg'=>'failed'];
+            }
+        }
+    }
+
+    // public function upload()
+    // {
+    //     if ($this->request->isGet()) {
+    //         return View::fetch()();
+    //     } else {
+    //         $isthumb = $this->request->has('isthumb', 'post') ? $this->request->post('isthumb') : 0;
+    //         $mode = $this->request->has('formername', 'post') ? $this->request->post('formername') : 0;
+    //         $file = request()->file('image');
+
+    //         $info = $this->saveUpload($file, $mode);
+
+    //         if (!$info) {
+    //             $this->error($info->getError());
+    //         }
+   
+    //         $filename = $info->getSaveName();
+    //         unset($info);
+    //         if ($isthumb == 1) {
+    //             $width = $this->request->has('width', 'post') ? $this->request->post('width') : $this->t_width;
+    //             $height = $this->request->has('height', 'post') ? $this->request->post('height') : $this->t_height;
+                
+    //             $thumbname = $this->makeThumb($filename, $width, $height);
+
+    //             if (!$thumbname) {
+    //                 $this->error('缩略图生成失败');
+    //             }
+    //         } else {
+    //             $thumbname = '';
+    //         }
+
+    //         if ($this->request->isAjax()) {
+    //             return ['code'=>2, 'picname' => $filename, 'thumbname'=>$thumbname];
+    //         } else {
+    //             $this->success('上传成功', '/file_manager/index');
+    //         }
+    //     }
+    // }
+
+    /**
+     * 保存上传的图片
+     * @param object $file 获取的上传对象
+     * @param boolean $mode 是否保存原文件名 0(false), 生成新名称; 1(true), 保留原名
+     * @return object 返回文件保存对象
+     */
+    protected function saveUpload($file, $mode)
+    {
+        $validate = ['size'=>2097152,'ext'=>'jpg,png,gif,jpeg'];
+
+        if ($mode) {
+            $upload = $file->validate($validate)->move(app()->getRootPath . 'public' . DIRECTORY_SEPARATOR . 'uploads', '');
+        } else {
+            $upload = $file->validate($validate)->move(app()->getRootPath . 'public' . DIRECTORY_SEPARATOR . 'uploads');
+        }
+
+        return $upload;
+    }
+
+    /**
+     * 站内选择
+     * @return void
+     */
+    public function selectPicture()
+    {
+        $data = $this->explorer();
+
+        View::assign('data', $data);
+        return View::fetch()();
+    }
+
+    public function uploadPicture()
+    {
+        return View::fetch()();
+    }
+
+    // public function onlinePicture()
+    // {
+    //     if ($this->request->isAjax()) {
+    //         $param = $this->request->param();
+    //         $urlimg = $param['filename'];
+    //         $download = $param['download'];
+    //         $isthumb = $param['isthumb'];
+    //         $formername = $param['formername'];
+
+    //         $res = \mylib\GetImageByurl::isImg($urlimg);
+    //         if (!$res) {
+    //             $this->error('该图片不合法');
+    //         } else {
+    //             if (!$download) {
+    //                 return ['code'=>2,'filename'=>$urlimg];
+    //             } else {
+    //                 $basepath = \config('url_domain_root') . '/uploads';
+    //                 // var_dump($param);
+                    
+    //                 if ($isthumb!=1  &&  preg_match("#" . $basepath . "#i", $urlimg)) {
+    //                     $this->error('图片已在服务其中, 可直接选用');
+    //                 }
+
+    //                 // 按文件夹日期夹存放图片
+    //                 $today = date('Ymd', time());
+    //                 $savePath = $this->img_path . $today;
+    //                 if (!is_dir($savePath)) {
+    //                     if (mkdir($savePath) == false) {
+    //                         $this->error('下载失败, 请稍后重试');
+    //                     }
+    //                 }
+                    
+    //                 if ($formername==1) {
+    //                     // 获取原文件名
+    //                     $fileinfo = pathinfo($urlimg);
+    //                     if (!isset($fileinfo['extension']) || !in_array($fileinfo['extension'], ['jpg', 'jpeg', 'png', 'gif'])) {
+    //                         $ext = \mylib\GetImageByurl::getEXTENSION($urlimg);
+    //                     }
+    //                     $filename = $fileinfo['basename'] . '.' . $ext;
+    //                 } else {
+    //                     $filename = '';
+    //                 }
+                            
+    //                 $filename = \mylib\GetImageByurl::getImageByurl($urlimg, $filename, $savePath);
+                    
+    //                 if ($filename) {
+    //                     // 生成缩略图
+    //                     if ($isthumb==1) {
+    //                         $width = $this->request->has('width', 'post') ? $this->request->post('width') : $this->t_width;
+    //                         $height = $this->request->has('height', 'post') ? $this->request->post('height') : $this->t_height;
+                            
+    //                         $thumbname = $this->makeThumb($today . DIRECTORY_SEPARATOR . $filename, $width, $height);
+
+    //                         if (!$thumbname) {
+    //                             $this->error('缩略图生成失败');
+    //                         }
+    //                     } else {
+    //                         $thumbname = '';
+    //                     }
+
+    //                     return ['code'=>2, 'filename' => $today . DIRECTORY_SEPARATOR . $filename, 'thumbname'=>$thumbname];
+    //                 } else {
+    //                     $this->error('图片下载失败');
+    //                 }
+    //             }
+    //         }   
+    //     } else {
+    //         return View::fetch()();
+    //     }
+    // }
+
+    /**
+     * 判断(远程)文件是否为图片
+     */
+    // public function isImg()
+    // {
+    //     if ($this->request->isAjax()) {
+    //         $filename = $this->request->param('filename');
+
+    //         $res = \mylib\GetImageByurl::isImg($filename);
+    //         if (!$res) {
+    //             $this->error('该图片不合法');
+    //         } else {
+    //             return ['code'=>2,'msg'=>'图片地址可用'];
+    //         }
+    //     }
+    // }
+}

+ 97 - 0
app/sys/controller/Index.php

@@ -0,0 +1,97 @@
+<?php
+
+
+declare (strict_types = 1);
+
+namespace app\sys\controller;
+
+use think\facade\App;
+use think\Facade\Db;
+use think\Facade\Config;
+use think\Facade\View;
+
+class Index  extends Base
+{
+    
+    public function index()
+    {
+        // 系统信息
+        $mysqlVersion = Db::query('SELECT VERSION() AS ver');
+        $config       = [
+            'url'             => $_SERVER['HTTP_HOST'],
+            'document_root'   => $_SERVER['DOCUMENT_ROOT'],
+            'server_os'       => PHP_OS,
+            'server_port'     => $_SERVER['SERVER_PORT'],
+            'server_ip'       => isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '',
+            'server_soft'     => $_SERVER['SERVER_SOFTWARE'],
+            'php_version'     => PHP_VERSION,
+            'mysql_version'   => $mysqlVersion[0]['ver'],
+            'max_upload_size' => ini_get('upload_max_filesize'),
+            'tp_version'         => App::version(),
+        ];
+
+        View::assign([
+            'config'        => $config,
+            // 'user'          => $user,
+            // 'message'       => $message ?? 0,
+            // 'messageCatUrl' => $messageCatUrl,
+            'indexTips'     => $this->getIndexTips(),
+        ]);
+        return View::fetch();
+    }
+
+    // 检查提示信息
+    private function getIndexTips()
+    {
+        $user = $this->getSysUser();
+        
+        $defaultPassword = Config::get('app.default_password') ?: 'admin';
+
+        if ($user->password == md5($defaultPassword.$user->salt)) {
+            return '<h6 class="mb-0"><i class="icon fas fa-fw fa-exclamation-triangle"></i> 请尽快修改后台初始密码!</h6>';
+        }
+        return '';
+    }
+
+    public function usedspace()
+    {
+        if ($this->request->isAjax()) {
+            $dirname = $this->app->getRootPath();
+            
+            $dirsize = get_dir_size($dirname);
+            
+            return ['code'=>1, 'size'=>format_bytes($dirsize)];
+        }
+    }
+
+    public function clearcache()
+    {
+        if ($this->request->isAjax()) {
+            $runtime_path = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR;
+            
+            try {
+                // deldir($runtime_path);
+                $cache_dir = $runtime_path . 'cache' . DIRECTORY_SEPARATOR;
+                $admin_dir = $runtime_path . 'admin' . DIRECTORY_SEPARATOR;
+                $index_dir = $runtime_path . 'index' . DIRECTORY_SEPARATOR;
+                $api_dir = $runtime_path . 'api' . DIRECTORY_SEPARATOR;
+                $adminedata_dir = $runtime_path . 'adminedata' . DIRECTORY_SEPARATOR;
+                $edata_dir = $runtime_path . 'edata' . DIRECTORY_SEPARATOR;
+                $top_dir = $runtime_path . 'top' . DIRECTORY_SEPARATOR;
+                $common_dir = $runtime_path . 'common' . DIRECTORY_SEPARATOR;
+
+                deldir($cache_dir);
+                deldir($admin_dir);
+                deldir($index_dir);
+                deldir($api_dir);
+                deldir($adminedata_dir);
+                deldir($edata_dir);
+                deldir($top_dir);
+                deldir($common_dir);
+            } catch (\Exception $e) {
+                return ['code'=>0, 'msg'=>$e->getMessage()];
+            }
+            return ['code'=>1, 'msg'=>'清除成功'];
+        }
+    }
+}

+ 46 - 0
app/sys/controller/Login.php

@@ -0,0 +1,46 @@
+<?php
+declare (strict_types = 1);
+/**
+ * +----------------------------------------------------------------------
+ * | 后台登录控制制器
+ * +----------------------------------------------------------------------
+ */
+namespace app\sys\controller;
+
+// 引入框架内置类
+
+use think\facade\Session;
+use think\facade\View;
+
+use app\common\model\SysUser;
+
+use think\facade\Cache;
+
+class Login
+{
+    // 登录页面
+    public function index()
+    {
+        // 已登录自动跳转
+        if (Session::has('adminuser')) {
+            return redirect((string)url('index/index'));
+        }
+        $restore_url = Session::has('restore') ? Session::get('restore'): (string)url('index/index');
+
+        View::assign('restore_url', $restore_url);
+
+        return View::fetch();
+    }
+
+    // 校验登录
+    public function checkLogin(){
+        return SysUser::checkLogin();
+    }
+
+    // 退出登录
+    public function logout(){
+        Cache::clear();
+        Session::delete('adminuser');
+        return redirect((string) url('login/index'));
+    }
+}

+ 36 - 0
app/sys/controller/SysLog.php

@@ -0,0 +1,36 @@
+<?php
+declare (strict_types = 1);
+/**
+ * +----------------------------------------------------------------------
+ * | 管理员日志控制器
+ * +----------------------------------------------------------------------
+ */
+namespace app\sys\controller;
+
+// 引入框架内置类
+use think\facade\View;
+
+use app\common\model\SysLog as SysLogModel;
+
+class SysLog extends Base
+{
+    public function index($limit = 30)
+    {
+        $list = SysLogModel::queryPage($limit, 0);
+
+        View::assign('list', $list);
+
+        return View::fetch();
+    }
+
+    public function delete($id)
+    {
+        if ($this->app->request->isPost()) {
+            if (SysLogModel::destroy($id)) {
+                return ['code' => 1,'msg'=>'删除成功'];
+            } else {
+                return ['code' => 0,'msg'=>'删除失败'];
+            }
+        }
+    }
+}

+ 36 - 0
app/sys/controller/SysLogin.php

@@ -0,0 +1,36 @@
+<?php
+declare (strict_types = 1);
+/**
+ * +----------------------------------------------------------------------
+ * | 管理员登录日志控制器
+ * +----------------------------------------------------------------------
+ */
+namespace app\sys\controller;
+
+// 引入框架内置类
+use think\facade\View;
+
+use app\common\model\SysLogin as SysLoginModel;
+
+class SysLogin extends Base
+{
+    public function index($limit = 30)
+    {
+        $list = SysLoginModel::queryPage($limit, 0);
+
+        View::assign('list', $list);
+
+        return View::fetch();
+    }
+
+    public function delete($id)
+    {
+        if ($this->app->request->isPost()) {
+            if (SysLoginModel::destroy($id)) {
+                return ['code' => 1,'msg'=>'删除成功'];
+            } else {
+                return ['code' => 0,'msg'=>'删除失败'];
+            }
+        }
+    }
+}

+ 96 - 0
app/sys/controller/SysMenu.php

@@ -0,0 +1,96 @@
+<?php
+namespace app\sys\controller;
+
+// 引入框架内置类
+use think\facade\View;
+
+// 引入表格和表单构建器
+use app\common\facade\MakeBuilder;
+// use app\common\builder\FormBuilder;
+use app\common\builder\TableBuilder;
+use app\admin\model\SysMenu as SysMenuModel;
+use think\facade\Request;
+
+class SysMenu extends Base
+{
+    public function index()
+    {
+        $list = SysMenuModel::select();
+
+        View::assign('list', obj_tree($list));
+
+        View::assign('type',  ['目录', '菜单', '按钮']);
+
+        return View::fetch();
+    }
+
+    public function save($id = 0)
+    {
+        if ($this->app->request->isPost()) {
+            $param = $this->app->request->param();
+            
+            if ($param['name'] == '') {
+                $this->error("目录名不能为空");
+            }
+
+            try {
+                if ($param['id'] != 0) {
+                    SysMenuModel::update([
+                        'pid' => $param['pid'],
+                        'name' => $param['name'],
+                        'url' => $param['url'] ?: null,
+                        'type' => $param['type'],
+                        'icon' => $param['icon']
+                    ], ['id'=>$param['id']]);
+                } else {
+                    SysMenuModel::create([
+                        'pid' => $param['pid'],
+                        'name' => $param['name'],
+                        'url' => $param['url'] ?: null,
+                        'type' => $param['type'],
+                        'icon' => $param['icon']
+                    ]);
+                }
+                // 删除菜单目录缓存
+                \think\Facade\Cache::set('menus', null);
+            } catch (\Exception $e) {
+                $msg = $e->getMessage();
+
+                $this->error("错误提示:".$msg);
+            }
+            $this->success('操作成功', 'sys_menu/index');
+        } else {
+            if ($id != 0) {
+                $data = SysMenuModel::find($id);
+            } else {
+                $data = [
+                    'id' => 0,
+                    'pid' => 0,
+                    'name' => '',
+                    'url' => '',
+                    'type' => 0,
+                    'icon' =>''
+                ];
+            }
+            
+            View::assign('data', $data);
+
+            return View::fetch();
+        }
+    }
+
+    public function delete($id = null)
+    {
+        if ($this->app->request->isAjax()) {
+            if (SysMenuModel::where('pid', $id)->value('id')) {
+                return ['code'=>0, 'msg'=>'此权限子权限不为空, 若要删除请先清空子权限'];
+            }
+
+            if (SysMenuModel::destroy($id)) {
+                return ['code' => 1,'msg'=>"删除成功"];
+            } else {
+                return ['code' => 0,'msg'=>"删除失败"];
+            }
+        }
+    }
+}

+ 106 - 0
app/sys/controller/SysRole.php

@@ -0,0 +1,106 @@
+<?php
+namespace app\sys\controller;
+
+use think\facade\View;
+
+use app\admin\model\SysMenu;
+use app\admin\model\SysUser;
+use app\admin\model\SysRole as SysRoleModel;
+use think\facade\Session;
+
+class SysRole extends Base
+{
+    public function index()
+    {
+        $list = SysRoleModel::field('roleid,name,permissions,remark,create_time')->select();
+
+        View::assign('list', $list);
+
+        return View::fetch();
+    }
+
+    public function save($id = 0)
+    {
+        if ($this->app->request->isPost()) {
+            $params = $this->app->request->param();
+            if ($params['name'] == '') {
+                $this->error("目录名不能为空");
+            }
+
+            $permissions = '';
+            if (!empty($params['perm_check'])) {
+                $perm_checks = $params['perm_check'];
+                $permissions = implode(',', $perm_checks);
+            }
+            unset($params['perm_check']);
+            $params['permissions'] = $permissions;
+
+            try {
+                $id = $params['roleid']; 
+                unset($params['roleid']);
+    
+                if ($id != 0) {
+                    SysRoleModel::update($params, ['roleid' => $id]);
+                } else {
+                    SysRoleModel::create($params);
+                }
+            } catch (\Exception $e) {
+                $msg = $e->getMessage();
+
+                $this->error("错误提示:".$msg);
+            }
+            $this->success('操作成功', 'sys_role/index');
+        } else {
+            if ($id != 0) {
+                $data = SysRoleModel::find($id);
+                // 已有权限数组
+                $rolePowers = explode(',', $data->permissions);
+            } else {
+                $data = [
+                    'roleid' => 0,
+                    'name' => '',
+                    'permissions' => '',
+                    'remark' =>''
+                ];
+                $rolePowers = [];
+            }
+            // var_dump($data);
+            // exit;
+            $sysMenu = new sysMenu();
+            $dataPermission = obj_tree($sysMenu->select());
+            return View::fetch('save', [
+                'data' => $data,
+                'rolePowers' => $rolePowers,
+                'dataPermission' => $dataPermission
+            ]);
+        }
+    }
+
+    public function delete($id = null)
+    {
+        if ($this->app->request->isAjax()) {
+            $U_role_id = SysUser::where('userid', Session::get('adminuser.roleid'))->value('roleid');
+
+            if (is_array($id)) {
+                if (in_array('1', $id)) {
+                    return ['status'=>0,'msg'=>'超级管理员无法删除'];
+                }
+                if (in_array($U_role_id, $id)) {
+                    return ['status'=>0,'msg'=>'当前登录用户角色无法删除'];
+                }
+            } else {
+                if ($id == 1) {
+                    return ['status'=>0,'msg'=>'超级管理员无法删除'];
+                }
+                if ($id == $U_role_id) {
+                    return ['status'=>0,'msg'=>'当前登录用户角色无法删除'];
+                }
+            }
+            if (SysRoleModel::destroy($id)) {
+                return ['code' => 1,'msg'=>'操作成功'];
+            } else {
+                return ['code' => 0,'msg'=>'操作失败'];
+            }
+        }
+    }
+}

+ 176 - 0
app/sys/controller/SysUser.php

@@ -0,0 +1,176 @@
+<?php
+declare (strict_types = 1);
+/**
+ * +----------------------------------------------------------------------
+ * | 后台登录控制制器
+ * +----------------------------------------------------------------------
+ */
+namespace app\sys\controller;
+
+// 引入框架内置类
+use think\facade\Request;
+use think\facade\Event;
+use think\facade\Session;
+use think\facade\View;
+use think\captcha\facade\Captcha;
+use think\facade\Config;
+
+use app\common\model\SysUser as SysUserModel;
+use app\common\model\SysRole as SysRoleModel;
+
+
+class SysUser extends Base
+{
+    public function index()
+    {
+        $roleid = $this->getSysUser()->roleid;
+
+        $list = SysUserModel::queryList($roleid);
+
+        View::assign('list', $list);
+
+        return View::fetch();
+    }
+
+    // public function info($id = 0)
+    // {
+    //     if ($id != 0) {
+    //         $data = $this->model->find($id);
+    //     } else {
+    //         $data = ['id'=>0, 'username' => '', 'role_id' => 0, 'truename' => '', 'email' => '', 'note' => ''];
+    //     }
+
+    //     $dataRole = SysRoleModel::column('name','id');
+
+    //     return  View::fetch('save',[
+    //         'data' => $data,
+    //         'dataRole' => $dataRole
+    //     ]);
+    // }
+
+    public function save($id = 0)
+    {
+        if ($this->app->request->isPost()) {
+            $params = $this->app->request->param();
+            if ($params['username'] =='' || $params['roleid'] =='') {
+                $this->error("用户名 or 角色 不能为空!");
+            }
+
+            try {
+                $id = $params['userid'];
+                unset($params['userid']);
+
+                if ($id != 0) {
+                    if (empty($params['password'])) {
+                        unset($params['password']);
+                    } else {
+                        $params['salt'] = generate_stochastic_string();
+                        $params['password'] = md5($params['password'].$params['salt']);
+                    }
+                    SysUserModel::update($params, ['userid' => $id]);
+                } else {
+                    $params['password'] = empty($params['password']) ? Config::get('app.default_password') : $params['password'];
+                    $params['salt'] = generate_stochastic_string();
+                    $params['password'] = md5($params['password'].$params['salt']);
+                    SysUserModel::create($params);
+                }
+            } catch (\Exception $e) {
+                $msg = $e->getMessage();
+
+                $this->error("错误代码:".$msg);
+            }
+            $this->success('操作成功', 'sys_user/index');
+        } else {
+            if ($id != 0) {
+                $data = SysUserModel::find($id);
+            } else {
+                $data = null;
+            }
+    
+            $dataRole = SysRole::column('name','roleid');
+
+            View::assign('data', $data);
+
+            View::assign('dataRole', $dataRole);
+    
+            return  View::fetch();
+        }
+    }
+
+    public function isAvailable($id = null, $username = '')
+    {
+        if ($this->app->request->isAjax()) {
+            $data = $this->model->where('username', $username)->find();
+            if ($data && $data->id != $id) {
+                return ['code' => 2, 'msg'=>'用户名已存在, 请使用其他用户名'];
+            } else {
+                return ['code' => 0, 'msg'=>'用户名可用'];
+            }
+        }
+    }
+
+    public function delete($id = null)
+    {
+        if ($this->app->request->isAjax()) {
+            if (is_array($id)) {
+                if (in_array(session('uid'), $id)) {
+                    return ['code'=>0,'msg'=>'当前登录用户无法删除'];
+                }
+            } else {
+                if ($id == session('uid')) {
+                    return ['code'=>0,'msg'=>'当前登录用户无法删除'];
+                }
+            }
+            if (SysUserModel::destroy($id)) {
+                return ['code' => 1,'msg'=>'删除成功'];
+            } else {
+                return ['code' => 0,'msg'=>'删除失败'];
+            }
+        }
+    }
+
+    // 停用or启用管理员
+    public function status($id, $status)
+    {
+        if ($this->app->request->isAjax()) {
+            if ($id == session('uid')) {
+                return ['code'=>0,'msg'=>'当前登录用户无法停用'];
+            }
+            if ($this->model->save(['status' => $status], ['id' => $id])) {
+                return ['code'=>1,'msg'=>'操作成功'];
+            } else {
+                return ['code'=>0,'msg'=>'操作失败'];
+            }
+        }
+    }
+
+    // 修改密码
+    public function password()
+    {
+        if ($this->app->request->isPOST()) {
+            $params = $this->app->request->param();
+            
+            $info = SysUserModel::field('password,salt')->find(session('adminuser.userid'));
+
+            if ($info->password != md5($params['oldpassword'].$info->salt)) {
+                $this->error('原密码不正确');
+            }
+
+            if ($params['newpassword']!=$params['repassword']) {
+                $this->error('两次新密码不一致,请核查');
+            }
+
+            $info->salt = generate_stochastic_string();
+            $info->password = md5($params['newpassword'] . $info->salt);
+
+            if ($info->save()) {
+                session(null);
+                $this->success("修改成功,请重新登陆", 'login/index');
+            } else {
+                $this->error('修改失败,请稍后重试');
+            }
+        } else {
+            return View::fetch();
+        }
+    }
+}

+ 36 - 0
app/sys/controller/System.php

@@ -0,0 +1,36 @@
+<?php
+declare (strict_types = 1);
+/**
+ * +----------------------------------------------------------------------
+ * | 管理员日志控制器
+ * +----------------------------------------------------------------------
+ */
+namespace app\sys\controller;
+
+// 引入框架内置类
+use think\facade\View;
+
+use app\admin\model\System as SystemModel;
+
+class System extends Base
+{
+    public function index()
+    {
+        $list = SystemModel::select();
+
+        View::assign('list', $list);
+
+        return View::fetch();
+    }
+
+    public function delete($id)
+    {
+        if ($this->app->request->isPost()) {
+            if (SystemModel::destroy($id)) {
+                return ['code' => 1,'msg'=>'删除成功'];
+            } else {
+                return ['code' => 0,'msg'=>'删除失败'];
+            }
+        }
+    }
+}

+ 8 - 0
app/sys/event.php

@@ -0,0 +1,8 @@
+<?php
+// 这是系统自动生成的event定义文件
+return [
+    'listen'  =>    [
+        'SysLog'    =>    ['app\admin\listener\SysUserLog'],
+        // 更多事件监听
+    ],
+];

+ 15 - 0
app/sys/listener/SysUserLog.php

@@ -0,0 +1,15 @@
+<?php
+declare (strict_types = 1);
+
+namespace app\sys\listener;
+
+use app\common\model\SysLog;
+
+class SysUserLog
+{
+    public function handle($admin)
+    {
+        // 事件监听处理
+        SysLog::record($admin);
+    }
+}

+ 5 - 0
app/sys/middleware.php

@@ -0,0 +1,5 @@
+<?php
+// 这是系统自动生成的middleware定义文件
+return [
+
+];

+ 141 - 0
app/sys/middleware/Admin.php

@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * +----------------------------------------------------------------------
+ * | 后台中间件
+ * +----------------------------------------------------------------------
+ */
+
+namespace app\sys\middleware;
+
+use think\facade\Config;
+use think\facade\Session;
+use think\facade\Cache;
+use think\Response;
+use think\exception\HttpResponseException;
+use think\facade\Request;
+use think\model\Collection;
+
+class Admin
+{
+    public function handle($request, \Closure $next)
+    {
+        // 获取当前用户
+        $admin_id = Session::get('adminuser.userid');
+
+        if (empty($admin_id)) {
+            return redirect((string)url('login/index'));
+        }
+
+        // 查找当前控制器和方法,控制器首字母大写,方法名首字母小写 如:Index/index
+        $route = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', Request::controller())) . '/' . strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', Request::action()));
+
+        // 权限认证
+        if (!$this->checkAuth($route, Session::get('adminuser.roleid'))) {
+            $this->error('您无此操作权限!');
+        }
+
+        // 进行操作日志的记录
+        $this->syslogRecord($route);
+
+        // 中间件handle方法的返回值必须是一个Response对象。
+        return $next($request);
+    }
+
+    protected function syslogRecord($route = '')
+    {
+        // 定义方法白名单(不记录日志)
+        $allow = [
+            
+        ];
+        
+        $action = Request::action();
+
+        if ($action != 'index' && !in_array($route, $allow)) {
+            \app\common\model\SysLog::record();
+        }
+    }
+
+    /**
+     * 检查权限
+     * @param  string|array  $route     需要验证的规则列表,支持逗号分隔的权限规则或索引数组
+     * @param  integer  $rid      认证用户角色ID
+     * @return boolean           通过验证返回true;失败返回false
+     */
+    public function checkAuth($route, $rid)
+    {
+        // 超级管理员不检查权限
+        if ($rid==1) {
+            return true;
+        }
+
+        $menus = Cache::get('menus');
+        if (!$menus) {
+            $menus = \app\common\model\SysMenu::getUserMenuList($rid);
+
+            Cache::set('menus', $menus);
+        }
+
+        if (!Config::get('app.auth_on')) {
+            return true;
+        }
+
+        // 定义方法白名单
+        $allow = [
+            'index/index',      // 首页
+            'index/usedspace',      //  使用空间
+            'index/clearcache',      // 清除缓存
+            'file_manager/uploadimg',      // 图片上传
+        ];
+
+        // 查询所有不验证的方法并放入白名单
+        $menuOpen = \app\common\model\SysMenu::where('open', 1)->column('url');
+        
+        $allow = array_merge($allow, $menuOpen);
+
+        foreach ($menus as $value) {
+            if ($value->type == 0) {
+                continue;
+            }
+            $allow[] = $value->url;
+        }
+
+        $allow = array_unique($allow);
+
+        if (in_array($route, $allow)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 操作错误跳转的快捷方法 抽的 liliuwei Jump error 方法
+     * @access protected
+     * @param  mixed $msg 提示信息
+     * @return void
+     */
+    protected function error($msg = '')
+    {
+        $url = Request::isAjax() ? '' : 'javascript:history.back(-1);';
+
+        $result = [
+            'code' => 0,
+            'msg' => $msg,
+            'data' => '',
+            'url' => $url,
+            'wait' => 3,
+        ];
+
+        $type = Request::isJson() || Request::isAjax() ? 'json' : 'html';;
+
+        if ('html' == strtolower($type)) {
+            $type = 'view';
+            $dispatch_error_tmpl = app()->getRootPath().'/vendor/liliuwei/thinkphp-jump/src/tpl/dispatch_jump.tpl';
+            $response = Response::create($dispatch_error_tmpl, $type)->assign($result)->header([]);
+        } else {
+            $response = Response::create($result, $type)->header([]);
+        }
+
+        throw new HttpResponseException($response);
+    }
+}

+ 84 - 0
app/sys/view/advertise/index.html

@@ -0,0 +1,84 @@
+<article class="cl pd-20">
+    <div class="cl pd-5 bg-1 bk-gray">
+        <span class="l">
+            <a href="javascript:;" onclick="del_all()" class="btn btn-danger radius">
+                <i class="Hui-iconfont">&#xe6e2;</i> 批量删除</a>
+            <a href="javascript:layer_show('添加广告', '{:url('save?_layer=true&id=0')}');" class="btn btn-primary radius">
+                <i class="Hui-iconfont">&#xe600;</i> 添加广告</a>
+        </span>
+    </div>
+    <div class="mt-10" style="min-width:800px;">
+        <table class="table table-border table-bordered table-bg">
+            <thead>
+                <tr class="text-c">
+                    <th width="25">
+                        <input type="checkbox" name="allcheck">
+                    </th>
+                    <th width="50px">ID</th>
+                    <th>名称</th>
+                    <th>类别</th>
+                    <th>图片</th>
+                    <th>链接地址</th>
+                    <th>栏目ID</th>
+                    <th>文章ID</th>
+                    <th>状态</th>
+                    <th>排序</th>
+                    <th>添加时间</th>
+                    <th>更新时间</th>
+                    <th>操作</th>
+                </tr>
+            </thead>
+            <tbody>
+                {foreach $list as $val}
+                <tr class="text-c">
+                    <td>
+                        <input type="checkbox" value="{$val.id}" name="checkbox[]">
+                    </td>
+                    <td>{$val.id}</td>
+                    <td>{$val.name}</td>
+                    <td>{$val.advertiseType.name}</td>
+                    <td><a href="{$val.picture}" target="_blank" class="pictureview" style="text-decoration: none;">
+                            <img src="{$val.thumb}" title="{$val.name}" style="max-width: 100px; max-height: 38px;">
+                        </a></td>
+                    <td>{$val.url}</td>
+                    <td>{$val.cid}</td>
+                    <td>{$val.artid}</td>
+                    <td class="td-status">
+                        <a href="javascript:;" onclick="state(this,'{$val.id}')" style="text-decoration: none;" title="已{$val.status==1? '启用' : '停用'}">
+                            <span class="f-20 c-primary"><i class="Hui-iconfont">{$val.status==1?'&#xe601;' : '&#xe677;'}</i></span></a>
+                    </td>
+                    <td>{$val.sort}</td>
+                    <td>{$val.create_time|date='Y-m-d H:i:s'}</td>
+                    <td>{$val.update_time|date='Y-m-d H:i:s'}</td>
+                    <td class="td-manage">
+                        <a class="btn btn-primary radius" title="编辑" href="javascript:layer_show('修改广告', '{:url('save?_layer=true&id='.$val.id)}');">
+                            <i class="Hui-iconfont">&#xe6df;</i>修改
+                        </a>
+                        <a class="btn btn-danger radius" title="删除" href="javascript:;" onclick="del(this,'{$val.id}')" class="ml-5">
+                            <i class="Hui-iconfont">&#xe6e2;</i>删除
+                        </a>
+                    </td>
+                </tr>
+                {/foreach}
+            </tbody>
+        </table>
+    </div>
+    <div class="cl pd-5 bg-1 bk-gray mt-20 ">
+        <span class="r">{notempty name="data"}{$data->render()|raw}{/notempty}</span>
+    </div>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    // 图片预览
+    $('.pictureview').hover(function(){
+        console.log($(this).children('img').attr('src'));
+        var url = $(this).children('img').attr('src');
+        var img = '<img class="thumbnail" id="thumbview" src="'+url+'">';
+        $(this).append(img);
+    },function(){
+        $('#thumbview').remove();
+    });
+
+</script>
+<!--/请在上方写此页面业务相关的脚本-->

+ 157 - 0
app/sys/view/advertise/save.html

@@ -0,0 +1,157 @@
+<article class="cl pd-20">
+    <form action="" method="post" class="form form-horizontal" id="form-save">
+        <input type="hidden" name="id" id="id" value="{$data.id}">
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>名称:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <input type="text" class="input-text" id="name" name="name" value="{$data.name}"
+                    autocomplete="new-password">
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>标题图:</label>
+                <div class="formControls col-xs-6 col-sm-4">
+                    <div style="width: 200px;height: 200px;">
+                     <a href="javascript:void(0);" onclick="uploadPicture()" >
+                        <img id="view-img" src="{$data.thumb ? $data.thumb : '/static/images/upload_picture.png'}" alt="标题图" title="{$data.title_pic ? '更换' : '添加'}标题图" style="max-width: 200px;max-height: 200px;">
+                     </a>
+                    </div>
+                    <input type="text" class="input-text" value="{$data.picture}" name="picture" id="picture" style="display: none;">
+                    <input type="text" class="input-text" value="{$data.thumb}" name="thumb" id="thumb" style="display: none;">
+                </div>
+                <label class="form-label col-xs-2 col-sm-2">
+                    <a class="btn btn-success radius" href="javascript:uploadPicture();">{$data.picture ? '更换' : '添加'}标题图</a></label>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red"></span>url:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <input type="text" class="input-text" id="url" name="url" value="{$data.url}"
+                    placeholder="文章or网站访问连接">
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                文章栏目id:</label>
+            <div class="formControls col-xs-4 col-sm-4">
+                <input type="text" class="input-text" id="cid" name="cid" value="{$data.cid}">
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">文章id:</label>
+            <div class="formControls col-xs-4 col-sm-4">
+                <input type="text" class="input-text" id="artid" name="artid" value="{$data.artid}">
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>类别:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <span class="select-box">
+                    <select class="select" id="type_id" name="type_id">
+                        <option value="" >请选择类别</option>
+                        {foreach $listTypes as $k=>$op}
+                        <option value="{$k}" {eq name="data.type_id" value="$k" }selected{/eq}>{$op}</option>
+                        {/foreach}
+                    </select>
+                </span>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red"></span>状态:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <div class="radio-box">
+                    <input type="radio" name="status" id="status-1" value="1" {$data==null || $data.status==1 ? 'checked' : ""}>
+                    <label for="typeId-1">启用</label>
+                </div>
+                <div class="radio-box">
+                    <input type="radio" name="status" id="status-2" value="2" {$data.status==2 ? 'checked' : ""}>
+                    <label for="status-2">禁用</label>
+                </div>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red"></span>排序:</label>
+                <div class="formControls col-xs-4 col-sm-4">
+                    <input type="text" class="input-text" id="sort" name="sort"
+                        placeholder="数字越大, 越靠前">
+                </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">备注:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <textarea id="remark" name="remark" cols="" rows="" class="textarea" placeholder="备注...200个字符以内"
+                    dragonfly="true" onKeyUp="textarealength(this,200)">{$data.remark}</textarea>
+                <p class="textarea-numberbar">
+                    <em class="textarea-length">0</em>/200
+                </p>
+            </div>
+        </div>
+        <div class="row cl">
+            <div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-3">
+                <input class="btn btn-success radius" type="button" id="form-save-button"
+                    value="&nbsp;&nbsp;提交&nbsp;&nbsp;">
+                <input class="btn btn-default radius" type="button" value="&nbsp;&nbsp;取消&nbsp;&nbsp;"
+                    onClick="layer_close();">
+            </div>
+        </div>
+    </form>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    // 图片上传
+    function uploadPicture() {
+        layer.open({
+            type: 2,
+            area: ['700px', '500px'],
+            fix: false, //不固定
+            maxmin: true,
+            shade: 0.4,
+            title: '添加缩略图',
+            content: '{:url("file_manager/uploadimg", ["_layer"=>true, "thumb"=>true, "width"=>400])}'
+        });
+    }
+
+    $(function () {
+        $("#form-save-button").click(function () {
+            if (getblen($("#remark").val()) > 200) {
+                layer.msg('备注过长', {
+                    icon: 5,
+                    time: 1000
+                });
+                return false;
+            }
+
+            var data = $("#form-save").serializeArray();
+            $.ajax({
+                type: 'POST',
+                url: '{:url("save")}',
+                data: data,
+                dataType: 'json',
+                success: function (res) {
+                    // console.log(res);
+                    if (res.code = 0) {
+                        layer.msg(data.msg, {
+                            icon: 5,
+                            time: 1000
+                        });
+                        return false;
+                    } else {
+                        layer.msg(res.msg, { icon: 1 }, function () {
+                            parent.location.reload(); // 父页面刷新
+                            var index = parent.layer.getFrameIndex(window.name); //获取窗口索引
+                            parent.layer.close(index);
+                        });
+                    }
+                }
+            })
+        })
+    })
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 72 - 0
app/sys/view/advertise_type/index.html

@@ -0,0 +1,72 @@
+<article class="cl pd-20">
+    <div class="cl pd-5 bg-1 bk-gray">
+        <span class="l">
+            <a href="javascript:;" onclick="del_all()" class="btn btn-danger radius">
+                <i class="Hui-iconfont">&#xe6e2;</i> 批量删除</a>
+            <a href="javascript:layer_show('添加广告', '{:url('save?_layer=true&id=0')}');" class="btn btn-primary radius">
+                <i class="Hui-iconfont">&#xe600;</i> 添加广告</a>
+        </span>
+        <span class="r">
+            共有:<span class="f-18 c-red">{$list->count()}</span>条
+        </span>
+    </div>
+    <div class="mt-10" style="min-width:800px;">
+        <table class="table table-border table-bordered table-bg">
+            <thead>
+                <tr class="text-c">
+                    <th width="25">
+                        <input type="checkbox" name="allcheck">
+                    </th>
+                    <th width="50px">ID</th>
+                    <th>名称</th>
+                    <th>备注</th>
+                    <th>状态</th>
+                    <th>添加时间</th>
+                    <th>更新时间</th>
+                    <th>操作</th>
+                </tr>
+            </thead>
+            <tbody>
+                {foreach $list as $val}
+                <tr class="text-c">
+                    <td>
+                        <input type="checkbox" value="{$val.id}" name="checkbox[]">
+                    </td>
+                    <td>{$val.id}</td>
+                    <td>{$val.name}</td>
+                    <td>{$val.remark}</td>
+                    <td class="td-status">
+                        <a href="javascript:;" onclick="state(this,'{$val.id}')" style="text-decoration: none;" title="已{$val.status==1? '启用' : '停用'}">
+                            <span class="f-20 c-primary"><i class="Hui-iconfont">{$val.status==1?'&#xe601;' : '&#xe677;'}</i></span></a>
+                    </td>
+                    <td>{$val.create_time|date='Y-m-d H:i:s'}</td>
+                    <td>{$val.update_time|date='Y-m-d H:i:s'}</td>
+                    <td class="td-manage">
+                        <a class="btn btn-primary radius" title="编辑" href="javascript:layer_show('修改', '{:url('save?_layer=true&id='.$val.id)}');">
+                            <i class="Hui-iconfont">&#xe6df;</i>修改
+                        </a>
+                        <a class="btn btn-danger radius" title="删除" href="javascript:;" onclick="del(this,'{$val.id}')" class="ml-5">
+                            <i class="Hui-iconfont">&#xe6e2;</i>删除
+                        </a>
+                    </td>
+                </tr>
+                {/foreach}
+            </tbody>
+        </table>
+    </div>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    // 图片预览
+    $('.pictureview').hover(function(){
+        console.log($(this).children('img').attr('src'));
+        var url = $(this).children('img').attr('src');
+        var img = '<img class="thumbnail" id="thumbview" src="'+url+'">';
+        $(this).append(img);
+    },function(){
+        $('#thumbview').remove();
+    });
+
+</script>
+<!--/请在上方写此页面业务相关的脚本-->

+ 73 - 0
app/sys/view/advertise_type/save.html

@@ -0,0 +1,73 @@
+<article class="cl pd-20">
+    <form action="" method="post" class="form form-horizontal" id="form-save">
+        <input type="hidden" name="id" id="id" value="{$data.id}">
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>名称:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <input type="text" class="input-text" id="name" name="name" value="{$data.name}"
+                    autocomplete="new-password">
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red"></span>状态:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <div class="radio-box">
+                    <input type="radio" name="status" id="status-1" value="1" {$data==null || $data.status==1 ? 'checked' : ""}>
+                    <label for="typeId-1">启用</label>
+                </div>
+                <div class="radio-box">
+                    <input type="radio" name="status" id="status-2" value="2" {$data.status==2 ? 'checked' : ""}>
+                    <label for="status-2">禁用</label>
+                </div>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">备注:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" id="remark" name="remark" value="{$data.remark}">
+            </div>
+        </div>
+        <div class="row cl">
+            <div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-3">
+                <input class="btn btn-success radius" type="button" id="form-save-button"
+                    value="&nbsp;&nbsp;提交&nbsp;&nbsp;">
+                <input class="btn btn-default radius" type="button" value="&nbsp;&nbsp;取消&nbsp;&nbsp;"
+                    onClick="layer_close();">
+            </div>
+        </div>
+    </form>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    $(function () {
+        $("#form-save-button").click(function () {
+            var data = $("#form-save").serializeArray();
+            $.ajax({
+                type: 'POST',
+                url: '{:url("save")}',
+                data: data,
+                dataType: 'json',
+                success: function (res) {
+                    // console.log(res);
+                    if (res.code = 0) {
+                        layer.msg(data.msg, {
+                            icon: 5,
+                            time: 1000
+                        });
+                        return false;
+                    } else {
+                        layer.msg(res.msg, { icon: 1 }, function () {
+                            parent.location.reload(); // 父页面刷新
+                            var index = parent.layer.getFrameIndex(window.name); //获取窗口索引
+                            parent.layer.close(index);
+                        });
+                    }
+                }
+            })
+        })
+    })
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 291 - 0
app/sys/view/article/index.html

@@ -0,0 +1,291 @@
+<article class="cl pd-10" style="min-width: 900px;">
+    <div class="cl pd-5 bg-1 bk-gray mt-20">
+        <span class="l">
+            <a href="javascript:;" onclick="del_all()" class="btn btn-danger radius">
+                <i class="Hui-iconfont">&#xe6e2;</i> 批量删除</a>
+            <a class="btn btn-primary radius" href="javascript:save(0);">
+                <i class="Hui-iconfont">&#xe600;</i> 添加文章</a>
+        </span>
+        <span class="select-box inline ml-50">
+            <select name="cid" id="sel_option" class="select">
+                <option value="0" {eq name='cid' value="0" }selected{/eq}>所有栏目</option>
+                {foreach $category_tree as $value}
+                <option value="{$value.id}" {eq name='cid' value="$value.id" }selected{/eq}>{$value.category_name}</option>
+                {notempty name="value.child"}
+                {foreach $value.child as $val}
+                <option value="{$val.id}" {eq name='cid' value="$val.id" }selected{/eq}>--{$val.category_name}</option>
+                {notempty name="val.child"}
+                {foreach $val.child as $vo}
+                <option value="{$vo.id}" {eq name='cid' value="$vo.id" }selected{/eq}>&nbsp;&nbsp;└ --{$vo.category_name}
+                </option>
+                {/foreach}
+                {/notempty}
+                {/foreach}
+                {/notempty}
+                {/foreach}
+            </select>
+        </span>
+        <span class="r">共有数据:<strong id="total">{notempty name="data"}{$data->total()}{else/}0{/notempty}</strong>
+            条</span>
+    </div>
+    <div id="edatalist">
+        <table class="table table-border table-bordered table-bg">
+            <thead>
+                <tr class="text-c">
+                    <th width="25px;">
+                        <input type="checkbox" name="" value="">
+                    </th>
+                    <th width="80px;">ID</th>
+                    <th style="min-width: 260px;">标题</th>
+                    <th width="240px;">栏目</th>
+                    <th width="160px;">发布者</th>
+                    <th width="180px;">发布时间</th>
+                    <th width="140px;">点击量</th>
+                    <th width="80px;">排序</th>
+                    <th width="200px;">操作</th>
+                </tr>
+            </thead>
+            <tbody>
+                {foreach $data as $val}
+                <tr class="text-c va-m">
+                    <td>
+                        <input type="checkbox" value="{$val.id}" name="checkbox[]">
+                    </td>
+                    <td>{$val.id}</td>
+                    <td class="text-l">
+                        <a href="/index/article/detail/id/{$val.id}" style="text-decoration:none" title="浏览"
+                            target="_blank">{$val.title}</a>
+                    </td>
+                    <td>{:isset($categories_name[$val['cid']]) ? $categories_name[$val['cid']] : ""}</td>
+                    <td>{$val.username}</td>
+                    <td>{$val.create_time}</td>
+                    <td>{$val.hits}</td>
+                    <td><input type="text" class="input-text input-sort" value="{$val.sort}" data-id="{$val.id}" style="text-align: center;"></td>
+                    <td class="td-manage">
+                        <a href="{:url('save?cid='.$val['cid'].'&id='.$val['id'])}" title="编辑"
+                            style="text-decoration:none" class="ml-10">
+                            <i class="Hui-iconfont">&#xe6df;</i>
+                        </a>
+                        <a href="javascript:;" title="删除" style="text-decoration:none" class="ml-10"
+                            onClick="del(this,'{$val.id}')">
+                            <i class="Hui-iconfont">&#xe6e2;</i>
+                        </a>
+                    </td>
+                </tr>
+                {/foreach}
+            </tbody>
+        </table>
+    </div>
+    <div class="cl pd-5 bg-1 bk-gray mt-20 ">
+        <span class="r">{notempty name="data"}{$data->render()}{/notempty}</span>
+    </div>
+</article>
+<script>
+    $("#sel_option").change(function () {
+        var cid = $("#sel_option").val();
+        console.log(cid);
+        if (cid) {
+            window.location.href = '/admin/article/index?cid=' + cid;
+        } else {
+            window.location.href = '/admin/article/index';
+        }
+        return false;
+    })
+
+    function save(id) {
+        var cid = $("#sel_option").val();
+        // console.log(cid);
+        // return false;
+        if (!cid || cid == 0) {
+            layer.msg("请选择栏目", {
+                icon: 5,
+                time: 2000
+            });
+            return false;
+        }
+        window.location.href = '/admin/article/save?id='+id+'&cid='+cid;
+        return false;
+    }
+
+    // 排序
+    $(".input-sort").change(function () {
+        var sort = $(this).val();
+        var id = $(this).data('id');
+        // console.log(sort);
+        // console.log(id);
+        $.post('sort', {
+                'id': id,
+                'sort': sort
+            }, function (res) {
+                console.log(res);
+                if (res) {
+                    layer.msg('排序成功', {
+                        icon: 1,
+                        time: 2000
+                    });
+                } else {
+                    layer.msg('排序失败', {
+                        icon: 5,
+                        time: 2000
+                    });
+                }
+            }, 'json');
+    })
+
+    //列表删除条目
+    function del(obj, id) {
+        layer.confirm('确认要删除吗?', function (index) {
+            $.post('delete', {
+                'id': id
+            }, function (data) {
+                if (data.code == 2) {
+                    $(obj).parents("tr").remove();
+                    layer.msg(data.msg, {
+                        icon: 1,
+                        time: 1000
+                    });
+                } else {
+                    layer.msg(data.msg, {
+                        icon: 5,
+                        time: 2000
+                    });
+                    return false;
+                }
+            }, 'json');
+        });
+    }
+
+    //列表多选删除条目
+    function del_all() {
+        if (isNull(controller)) {
+            url = 'del';
+        } else {
+            url = '/admin/' + controller + '/del';
+        }
+        var checkbox = $('.text-c input[name="checkbox[]"]');
+        var ids = new Array();
+        checkbox.each(function (x) {
+            if (this.checked)
+                ids.push(this.value);
+        })
+
+        var length = ids.length;
+
+        if (length == 0) {
+            layer.msg('请选择要删除的选项', {
+                icon: 5,
+                time: 1000
+            });
+            return false;
+        }
+
+        layer.confirm('确认要删除选中项吗?', function (index) {
+            $.post('delete', {
+                'id': ids
+            }, function (data) {
+                if (data.code == 2) {
+                    checkbox.each(function (x) {
+                        if (this.checked)
+                            $(this).parents("tr").remove();
+                    })
+                    layer.msg(data.msg, {
+                        icon: 1,
+                        time: 1000
+                    });
+                } else {
+                    layer.msg(data.msg, {
+                        icon: 5,
+                        time: 2000
+                    });
+                    return false;
+                }
+            }, 'json');
+        });
+    }
+
+    /* 置顶 or 首页 */
+    function top_home(action, obj, column, id) {
+        var col = '首页';
+        var style = 'style="margin-left: 10%"';
+        if (column == 'top') {
+            col = '置顶';
+            style = '';
+        }
+
+        var value = 2;
+        var cancel = '';
+        var color = 'default';
+        if (action == 'up') {
+            value = 1;
+            action = 'down';
+            cancel = '取消';
+            color = 'success';
+        } else {
+            action = 'up';
+        }
+
+        var text = "<a href=\"javascript:void(0);\" title='" + cancel + col + "' onClick=\"top_home('" + action + "',this,'" + column + "'," + id + ")\" style=\"text-decoration:none\">";
+        text += '<span class="label label-' + color + ' radius" ' + style + '>' + col + '</span></a>';
+
+        $.post('top', {
+            'id': id,
+            'column': column,
+            'value': value
+        }, function (data) {
+            if (data.code == 2) {
+                $(obj).after(text);
+                $(obj).remove();
+                layer.msg(data.msg, {
+                    icon: 6,
+                    time: 1000
+                });
+            } else {
+                layer.msg(data.msg, {
+                    icon: 5,
+                    time: 1000
+                });
+            }
+        }, 'json');
+    }
+
+    // 产品上加/下架 or 文章上线.下线
+    function duty(action, obj, id) {
+        var duty = 2,
+            title = '上线',
+            button = 'default',
+            status = '下线';
+
+        if (action == 'up') {
+            duty = 1;
+            title = '下线';
+            status = '正常';
+            button = 'success';
+            action = 'down';
+        } else {
+            action = 'up';
+        }
+
+
+        var text = "<a href=\"javascript:void(0);\" title=" + title + " onClick=\"duty('" + action + "', this," + id + ")\" >";
+        text += '<span class="label label-' + button + ' radius">' + status + '</span></a>';
+
+        $.post("duty", {
+            'id': id,
+            'status': duty
+        }, function (data) {
+            if (data.code == 2) {
+                $(obj).after(text);
+                $(obj).remove();
+                layer.msg(data.msg, {
+                    icon: 6,
+                    time: 1000
+                });
+            } else {
+                layer.msg(data.msg, {
+                    icon: 5,
+                    time: 1000
+                });
+            }
+        }, 'json');
+    }
+</script>

+ 147 - 0
app/sys/view/article/save.html

@@ -0,0 +1,147 @@
+<article class="cl pd-20">
+    <form action="{:url('save?cid='.$cid.'&id='.$data['id'])}" method="post" class="form form-horizontal"
+        id="form-admin-article">
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>标题:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <input type="text" class="input-text" value="{$data.title}" placeholder="请填写标题" id="title" name="title">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                作者: </label>
+            <div class="formControls col-xs-4 col-sm-2">
+                <input type="text" class="input-text" value="{$data.writer}" placeholder="作者" id="writer" name="writer">
+            </div>
+            <label class="form-label col-xs-4 col-sm-2">
+                信息来源:</label>
+            <div class="formControls col-xs-4 col-sm-2">
+                <input type="text" class="input-text" value="{$data.source}" placeholder="信息来源" id="source"
+                    name="source">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                标题图:</label>
+            <div class="formControls col-xs-6 col-sm-4">
+                <div style="width: 200px;height: 200px;">
+                    <a href="javascript:void(0);" onclick="addTitlePic()">
+                        <img id="view-img"
+                            src="{$data.titlepic ? $data.titlepic : '/static/images/upload_picture.png'}"
+                            alt="标题图" title="{$data.title_pic ? '更换' : '添加'}标题图"
+                            style="max-width: 200px;max-height: 200px;">
+                    </a>
+                </div>
+                <input type="text" class="input-text" value="{$data.titlepic}" name="titlepic" id="thumb"
+                    style="display: none;">
+                <!-- <input type="text" class="input-text" value="{//$value}" name="images[]" id="picture"
+                    style="display: none;"> -->
+            </div>
+            <label class="form-label col-xs-2 col-sm-2">
+                <a class="btn btn-success radius" href="javascript:addTitlePic();">{$data.title_pic ? '更换' :
+                    '添加'}标题图</a></label>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                关键词:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <input type="text" class="input-text" value="{$data.keywords}" placeholder="关键词" id="source"
+                    name="keywords">
+                <span class="c-red">多个关键词用英文','分割</span>
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">内容简介:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <textarea name="summary" id="summary" cols="" rows="" class="textarea" placeholder="说点什么...最多输入500个字符"
+                    datatype="*10-100" dragonfly="true" onKeyUp="textarealength(this,500)">{$data.summary}</textarea>
+                <p class="textarea-numberbar">
+                    <em class="textarea-length">0</em>/500
+                </p>
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                文章正文:</label>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-1 col-sm-1"></label>
+            <div class="formControls col-xs-11 col-sm-10">
+                <script id="editor" type="text/plain"
+                    style="width:100%;height:400px;">{$data.content|raw}</script>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-2 col-sm-2">
+                点击量:</label>
+            <div class="formControls col-xs-4 col-sm-2">
+                <input type="number" class="input-text" value="{$data.clicks}" id="clicks" name="clicks"
+                    style="width: 120px;">
+            </div>
+            <label class="form-label col-xs-2 col-sm-2">
+                排序:</label>
+            <div class="formControls col-xs-4 col-sm-2">
+                <input type="number" class="input-text" value="{$data.clicks}" id="clicks" name="clicks"
+                    style="width: 120px;">
+            </div>
+        </div>
+        <div class="row cl">
+            <div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-3">
+                <button type="submit" class="btn btn-success radius" id="admin-powers-save" name="">确&nbsp;定</button>
+                <button type="button" class="btn btn-default radius" onclick="window.history.back();"
+                    style="margin-left:20px;">取&nbsp;消</button>
+            </div>
+        </div>
+    </form>
+</article>
+
+<script type="text/javascript" src="/static/plugins/lib/ueditor/1.4.3/ueditor.config.js"></script>
+<script type="text/javascript" src="/static/plugins/lib/ueditor/1.4.3/ueditor.all.min.js"></script>
+<script type="text/javascript" src="/static/plugins/lib/ueditor/1.4.3/lang/zh-cn/zh-cn.js"></script>
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    $(function () {
+        //实例化编辑器   
+        var ue = UE.getEditor('editor');
+    })
+    $("#form-admin-article").submit(function () {
+        if (isNull($("#title").val())) {
+            layer.msg('标题不可为空', {
+                icon: 5,
+                time: 1000
+            });
+            return false;
+        }
+
+        var summary = $("#summary").val();
+        if (getblen(summary) > 200) {
+            layer.msg('内容简介过长', {
+                icon: 5,
+                time: 1000
+            });
+            return false;
+        }
+    })
+
+    //添加标题图
+    function addTitlePic() {
+        layer.open({
+            type: 2,
+            area: ['700px', '500px'],
+            fix: false, //不固定
+            maxmin: true,
+            shade: 0.4,
+            title: '添加缩略图',
+            content: '{:url("file_manager/uploadimg", ["thumb"=>1,"_layer"=>true])}'
+        });
+    }
+</script>

+ 142 - 0
app/sys/view/category/index.html

@@ -0,0 +1,142 @@
+<article class="cl pd-20">
+    <div class="cl pd-5 bg-1 bk-gray">
+        <span class="l">
+            <a href="javascript:;" onclick="del_all()" class="btn btn-danger radius">
+                <i class="Hui-iconfont">&#xe6e2;</i> 批量删除</a>
+            <a class="btn btn-primary radius" href="javascript:save(0);">
+                <i class="Hui-iconfont">&#xe600;</i> 添加栏目</a>
+        </span>
+    </div>
+    <div class="mt-10" style="min-width:920px;">
+        <table class="table table-border table-bordered table-hover table-bg">
+            <thead>
+                <tr class="text-c">
+                    <th width="60px">ID</th>
+                    <th>栏目名称</th>
+                    <th>英文名称</th>
+                    <th>栏目目录</th>
+                    <th>表名</th>
+                    <th>模型名</th>
+                    <th>列表模板</th>
+                    <th>详情模板</th>
+                    <th>导航状态</th>
+                    <th style="width: 60px;">排序</th>
+                    <th>添加时间</th>
+                    <th>更新时间</th>
+                    <th>操作</th>
+                </tr>
+            </thead>
+            <tbody>
+                {foreach $list as $value}
+                <tr class="text-c">
+                    <td>{$value.id}</td>
+                    <td class="text-l">{$value.category_name}</td>
+                    <td>{$value.en_name}</td>
+                    <td>{$value.category_folder}</td>
+                    <td>{$value.table_name}</td>
+                    <td>{$value.module_name}</td>
+                    <td>{$value.template_list}</td>
+                    <td>{$value.template_show}</td>
+                    <td>{$value.is_menu}</td>
+                    <td><input type="text" class="input-text input-sort" value="{$value.sort}" data-id="{$value.id}"
+                            style="text-align: center;"></td>
+                    <td>{$value.create_time|date="Y-m-d"}</td>
+                    <td>{$value.update_time|date="Y-m-d"}</td>
+                    <td class="td-manage">
+                        <a class="btn btn-primary radius" title="编辑" href="javascript:save({$value.id});"
+                            style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6df;</i>编辑
+                        </a>
+                        <a class="btn btn-danger radius" title="删除" href="javascript:;"
+                            onclick="del(this,'{$value.id}')" class="ml-5" style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6e2;</i>删除
+                        </a>
+                    </td>
+                </tr>
+                {notempty name="value.child"} {foreach $value.child as $val}
+                <tr class="text-c">
+                    <td>{$val.id}</td>
+                    <td>{$val.category_name}</td>
+                    <td>{$val.en_name}</td>
+                    <td>{$val.category_folder}</td>
+                    <td>{$val.table_name}</td>
+                    <td>{$val.module_name}</td>
+                    <td>{$val.template_list}</td>
+                    <td>{$val.template_show}</td>
+                    <td>{$val.is_menu}</td>
+                    <td><input type="text" class="input-text input-sort" value="{$val.sort}" data-id="{$val.id}"
+                            style="text-align: center;"></td>
+                    <td>{$val.create_time|date="Y-m-d"}</td>
+                    <td>{$val.update_time|date="Y-m-d"}</td>
+                    <td class="td-manage">
+                        <a class="btn btn-primary radius" title="编辑" href="javascript:save({$val.id});"
+                            style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6df;</i>编辑
+                        </a>
+                        <a class="btn btn-danger radius" title="删除" href="javascript:;" onclick="del(this,'{$val.id}')"
+                            class="ml-5" style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6e2;</i>删除
+                        </a>
+                    </td>
+                </tr>
+                {notempty name="val.child"} {foreach $val.child as $vo}
+                <tr class="text-c">
+                    <td>{$vo.id}</td>
+                    <td>{$vo.category_name}</td>
+                    <td>{$vo.en_name}</td>
+                    <td>{$vo.category_folder}</td>
+                    <td>{$vo.table_name}</td>
+                    <td>{$vo.module_name}</td>
+                    <td>{$vo.template_list}</td>
+                    <td>{$vo.template_show}</td>
+                    <td>{$vo.is_menu}</td>
+                    <td><input type="text" class="input-text input-sort" value="{$vo.sort}" data-id="{$vo.id}"
+                            style="text-align: center;"></td>
+                    <td>{$vo.create_time|date="Y-m-d"}</td>
+                    <td>{$vo.update_time|date="Y-m-d"}</td>
+                    <td class="td-manage">
+                        <a class="btn btn-primary radius" title="编辑" href="javascript:save({$vo.id});"
+                            style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6df;</i>编辑
+                        </a>
+                        <a class="btn btn-danger radius" title="删除" href="javascript:;" onclick="del(this,'{$vo.id}')"
+                            class="ml-5" style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6e2;</i>删除
+                        </a>
+                    </td>
+                </tr>
+                {/foreach} {/notempty}{/foreach} {/notempty} {/foreach}
+            </tbody>
+        </table>
+    </div>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    function save(id) {
+        var title = id == 0 ? '添加栏目' : '修改栏目'
+        var url = "{:url('save')}" + "?_layer=true&id=" + id
+        layer_show(title, url, 800, 600);
+    }
+
+    // 排序
+    $(".input-sort").change(function () {
+        var sort = $(this).val();
+        var id = $(this).data('id');
+
+        console.log(id);
+        console.log(sort);
+        $.post('sort', {
+            'id': id,
+            'sort': sort
+        }, function (data) {
+            if (data.code == 1) {
+                topalert({ type: 0, content: data.msg, speed: 2000 });
+            } else {
+                topalert({ type: 1, content: data.msg, speed: 2000 });
+                return false;
+            }
+        }, 'json');
+    });
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 225 - 0
app/sys/view/category/save.html

@@ -0,0 +1,225 @@
+<article class="cl pd-20">
+    <form action="" method="post" class="form form-horizontal" id="form-save">
+        <input type="hidden" name="id" id="id" value="{$data.id}">
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>上级栏目:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <span class="select-box">
+                    <select class="select" id="sel_Sub" name="pid">
+                        <option value="0" {eq name='data.pid' value="0" }selected{/eq}>--顶级栏目--</option>
+                        {foreach $list as $value}
+                        <option value="{$value.id}" {eq name='value.id' value="$data.pid" }selected{/eq}>
+                            {$value.category_name}</option>
+                        {notempty name="value.child"} {foreach $value.child as $val}
+                        <option value="{$val.id}" {eq name='val.id' value="$data.pid" }selected{/eq}>&nbsp;&nbsp;└
+                            --{$val.category_name}</option>
+                        {/foreach} {/notempty} {/foreach}
+                    </select>
+                </span>
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>栏目名称:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.category_name}" placeholder="请填写栏目名称"
+                    id="category_name" name="category_name">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                url:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.url}" placeholder="绑定路由地址" id="url" name="url">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                栏目目录:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.directory}" placeholder="栏目静态文件目录" id="directory"
+                    name="directory">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                模型名:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.tablename}" placeholder="请填写模型名(表名, 小写驼峰, 无前缀)"
+                    id="tablename" name="tablename">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                列表模板:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.template_list}" placeholder="请填写列表模板"
+                    id="template_list" name="template_list">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                详情模板:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.template_detail}" placeholder="请填写详情模板"
+                    id="template_detail" name="template_detail">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                栏目标题:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.title}" placeholder="SEO标题" id="title" name="title">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                栏目关键字:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.keywords}" placeholder="SEO关键字" id="keywords"
+                    name="keywords">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">描述:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <textarea name="description" id="description" cols="" rows="" class="textarea"
+                    placeholder="SEO描述...最多输入500个字符" dragonfly="true" nullmsg="备注不能为空!"
+                    onKeyUp="textarealength(this,500)">{$data.note}</textarea>
+                <p class="textarea-numberbar">
+                    <em class="textarea-length">0</em>/500
+                </p>
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">简介:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <textarea name="summary" id="summary" cols="" rows="" class="textarea" placeholder="SEO描述...最多输入255个字符"
+                    dragonfly="true" nullmsg="备注不能为空!" onKeyUp="textarealength(this,3000)">{$data.summary}</textarea>
+                <p class="textarea-numberbar">
+                    <em class="textarea-length">0</em>/3000
+                </p>
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red"></span>状态:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <div class="radio-box">
+                    <input type="radio" name="status" id="status-1" value="1" {$data==null || $data.status==1
+                        ? 'checked' : "" }>
+                    <label for="status-1">启用</label>
+                </div>
+                <div class="radio-box">
+                    <input type="radio" name="status" id="status-2" value="2" {$data.status==2 ? 'checked' : "" }>
+                    <label for="status-2">禁用</label>
+                </div>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">排序:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="number" min=0 max=100 class="input-text" value="{$data.sort}" name="sort"
+                    style="width:120px;">
+                    <span class="c-red">数字越大, 越靠前</span>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red"></span>导航:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <div class="radio-box">
+                    <input type="radio" name="is_menu" id="is_menu-1" value="1" {$data==null || $data.is_menu==1
+                        ? 'checked' : "" }>
+                    <label for="is_menu-1">是</label>
+                </div>
+                <div class="radio-box">
+                    <input type="radio" name="is_menu" id="is_menu-2" value="2" {$data.is_menu==2 ? 'checked' : "" }>
+                    <label for="is_menu-2">否</label>
+                </div>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red"></span>新标签打开:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <div class="radio-box">
+                    <input type="radio" name="is_blank" id="is_blank-1" value="1" {$data==null || $data.is_blank==1
+                        ? 'checked' : "" }>
+                    <label for="is_blank-1">是</label>
+                </div>
+                <div class="radio-box">
+                    <input type="radio" name="is_blank" id="is_blank-2" value="2" {$data.is_blank==2 ? 'checked' : "" }>
+                    <label for="is_blank-2">否</label>
+                </div>
+            </div>
+        </div>
+        <div class="row cl">
+            <div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-3">
+                <button type="button" class="btn btn-success radius" id="form-save-button" name="">确&nbsp;定</button>
+                <button type="button" class="btn btn-default radius" onclick="layer_close();"
+                    style="margin-left:20px;">取&nbsp;消</button>
+            </div>
+        </div>
+    </form>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    $(function () {
+        $("#form-save-button").click(function () {
+            if (getblen($("#description").val()) > 500) {
+                layer.msg('描述过长', {
+                    icon: 5,
+                    time: 1000
+                });
+                return false;
+            }
+
+            if (getblen($("#summary").val()) > 3000) {
+                layer.msg('简介过长', {
+                    icon: 5,
+                    time: 1000
+                });
+                return false;
+            }
+
+            var data = $("#form-save").serializeArray();
+            $.ajax({
+                type: 'POST',
+                url: '{:url("save")}',
+                data: data,
+                dataType: 'json',
+                success: function (res) {
+                    // console.log(res);
+                    if (res.code = 0) {
+                        layer.msg(data.msg, {
+                            icon: 5,
+                            time: 1000
+                        });
+                        return false;
+                    } else {
+                        layer.msg(res.msg, { icon: 1 }, function () {
+                            parent.location.reload(); // 父页面刷新
+                            var index = parent.layer.getFrameIndex(window.name); //获取窗口索引
+                            parent.layer.close(index);
+                        });
+                    }
+                }
+            })
+        })
+    })
+
+</script>

+ 88 - 0
app/sys/view/file_manager/uploadimg.html

@@ -0,0 +1,88 @@
+ <!-- <div class="cl pd-5 bg-1 bk-gray">
+        <a href="{:url('/file_manager/selectpicture')}" class="btn btn-default radius sethumb">
+            <i class="Hui-iconfont">&#xe600;</i> 站内选择 </a>&nbsp;&nbsp;
+        <a href="javascript:void(0);" class="btn btn-primary radius sethumb">
+            <i class="Hui-iconfont">&#xe600;</i> 本地上传 </a>&nbsp;&nbsp;
+        <a href="{:url('/file_manager/onlinepicture')}" class="btn btn-default radius sethumb">
+            <i class="Hui-iconfont">&#xe600;</i> 网络图片 </a>&nbsp;&nbsp;
+    </div> -->
+    <!-- 本地上传 -->
+    <div class="step1 active" style="margin-left:30px;">
+        <form id="submit_form" method="post" action="" enctype="multipart/form-data">
+            <input type="hidden" name="thumb" value={$thumb}>
+            <input type="hidden" name="width" value={$width}>
+            <input type="hidden" name="height" value={$height}>
+            <div class="row cl" style="margin-top:20px;">
+                <label class="form-label col-xs-2 col-sm-2"><span class="c-red">*</span>图片要求: </label>
+                <div class="formControls col-xs-8 col-sm-8">
+                    格式 jpg,png,gif,jpeg. 大小不超过2M.
+                </div>
+                <div class="col-3"> </div>
+            </div>
+            <div class="row cl" style="margin-top:20px;">
+                <label class="form-label col-xs-2 col-sm-2">
+                    <span class="c-red">*</span>本地上传:</label>
+                <div class="formControls col-xs-4 col-sm-4">
+                    <input type="file" class="input-text" name="upload_file" id="upload_file">
+                </div>
+                <div class="col-3"> </div>
+            </div>
+            <div class="row cl" style="margin-top:30px;margin-left:30px;">
+                <div class="col-xs-6 col-sm-5 col-xs-offset-2 col-sm-offset-2">
+                    <input class="btn btn-primary radius" type="button" value="&nbsp;确&nbsp;定&nbsp;" onCLick="uploadImg()">
+                    <input class="btn btn-default radius" type="button" value="&nbsp;取&nbsp;消&nbsp;" onClick="layer_close();">
+                </div>
+            </div>
+        </form>
+    </div>
+    <!-- 本地上传end -->
+    
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+
+    //step1本地上传图片
+    function uploadImg() {
+        if ($("#upload_file").val() == '') {
+            layer.msg("请选择要上传的文件", {
+                icon: 6,
+                time: 1000
+            });
+            return false;
+        } else {
+            var formData = new FormData($("#submit_form")[0]);
+            $.ajax({
+                url: '{:url("file_manager/uploadimg")}',
+                type: 'POST',
+                async: true,
+                cache: false,
+                data: formData,
+                processData: false,
+                contentType: false,
+                dataType: "json",
+                beforeSend: function () {
+                    uploading = true;
+                },
+                success: function (data) {
+                    if (data.code == 2) {
+                        window.parent.$("#picture").val(data.picname);
+                        var img=data.picname;
+                        if (data.thumbname) {
+                            img = data.thumbname;
+                            window.parent.$("#thumb").val(data.thumbname);
+                        }
+                        window.parent.$("#view-img").attr('src',img);
+                        layer_close();
+                    } else {
+                        layer.msg(data.msg, {
+                            icon: 5,
+                            time: 1000
+                        });
+                        return false;
+                    }
+                    uploading = false;
+                }
+            });
+        }
+    }
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 91 - 0
app/sys/view/index/awere.html

@@ -0,0 +1,91 @@
+<ul class="dropDown-menu menu radius box-shadow">
+    <li><a href="javascript:;" onclick="article_add('添加资讯','article-add.html')"><i
+                class="Hui-iconfont">&#xe616;</i> 资讯</a></li>
+    <li><a href="javascript:;" onclick="picture_add('添加资讯','picture-add.html')"><i
+                class="Hui-iconfont">&#xe613;</i> 图片</a></li>
+    <li><a href="javascript:;" onclick="product_add('添加资讯','product-add.html')"><i
+                class="Hui-iconfont">&#xe620;</i> 产品</a></li>
+    <li><a href="javascript:;" onclick="member_add('添加用户','member-add.html','','510')"><i
+                class="Hui-iconfont">&#xe60d;</i> 用户</a></li>
+</ul>
+   
+   
+   <div class="wrapper">
+        <!-- Navbar -->
+        <nav class="main-header navbar navbar-expand navbar-white navbar-light">
+            <!-- Left navbar links -->
+            <ul class="navbar-nav">
+                <li class="nav-item">
+                    <a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a>
+                </li>
+            </ul>
+
+            <!-- Left navbar links -->
+            <ul class="navbar-nav js_left_menu">
+                <li class="nav-item active">
+                    <a class="nav-link" href="javascript:;">
+                        <i class="fas fa-cog"></i>
+                        <span>主导航</span>
+                    </a>
+                </li>
+                {//if count($cates)}
+                <li class="nav-item">
+                    <a class="nav-link" href="javascript:;">
+                        <i class="fa fa-th-large"></i>
+                        <span>内容管理</span>
+                    </a>
+                </li>
+                {///if}
+            </ul>
+
+            <!-- Right navbar links -->
+            <ul class="navbar-nav ml-auto">
+                <!-- User Account Menu -->
+                <li class="nav-item dropdown user user-menu">
+                    <!-- Menu Toggle Button -->
+                    <a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
+                        <img src="{//:session('admin.image') ?: '/static/plugins/AdminLTE/dist/img/user2-160x160.jpg'}"
+                            class="user-image">
+                        <span class="d-none d-lg-block">{:session('admin.nickname') ?: session('admin.username')}</span>
+                    </a>
+                    <ul class="dropdown-menu">
+                        <li class="user-header">
+                            <img src="{//:session('admin.image') ?: '/static/plugins/AdminLTE/dist/img/user2-160x160.jpg'}"
+                                class="img-circle">
+                            <h5>上次登录时间:{:session('admin.login_time')}</h5>
+                            <h5>上次登录IP:{:session('admin.login_ip')}</h5>
+                        </li>
+                        <li class="user-footer">
+                            <div class="pull-left">
+                                <a href="{//:url('Admin/edit',['id'=>session('admin.id')])}"
+                                    class="btn btn-default btn-flat">资料</a>
+                            </div>
+                            <div class="pull-right">
+                                <a href="{//:url('Login/logout')}" class="btn btn-default btn-flat">退出</a>
+                            </div>
+                        </li>
+                    </ul>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" data-widget="fullscreen" href="#" role="button" title="全屏">
+                        <i class="fas fa-expand-arrows-alt"></i>
+                    </a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" data-widget="control-sidebar" data-slide="true" href="#" role="button"
+                        title="自定义">
+                        <i class="fas fa-palette"></i>
+                    </a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link js_clear_cash" href="javascript:;" title="清空缓存">
+                        <i class="fas fa-sync-alt"></i>
+                    </a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" href="{//$indexUrl}" target="_blank" title="前台首页">
+                        <i class="fas fa-home"></i>
+                    </a>
+                </li>
+            </ul>
+        </nav>

+ 213 - 0
app/sys/view/index/index.html

@@ -0,0 +1,213 @@
+
+
+<article class="cl pd-20">
+    {if $indexTips}
+    <div class="row">
+        <div class="Huialert Huialert-danger"><i class="Hui-iconfont">&#xe6a6;</i>{$indexTips|raw}</div>
+    </div>
+    {/if}
+    <div class="row">
+        <div class="col-lg-8">
+            <div class="row">
+                <!--快捷方式-->
+                <div class="col-12 col-sm-6">
+                    <div class="card">
+                        <div class="card-header ui-sortable-handle">
+                            <h3 class="card-title"><i class="Hui-iconfont">&#xe61b;</i>快捷方式</h3>
+                        </div>
+                        <!-- /.card-header -->
+                        <div class="card-body">
+                            <a class="btn btn-app" href="{:url('system/edit',['id'=>1])}">
+                                <div><i class="Hui-iconfont">&#xe62e;</i></div>
+                                <span>系统设置</span>
+                            </a>
+                            <a class="btn btn-app" href="{:url('advertise/index')}">
+                                <div><i class="Hui-iconfont">&#xe613;</i></div>
+                                <span>轮播图管理</span>
+                            </a>
+                            <a class="btn btn-app" href="{:url('article/index')}">
+                                <div><i class="Hui-iconfont">&#xe616;</i></div>
+                                <span>管理文章</span>
+                            </a>
+                            <a class="btn btn-app" href="{:url('category/index')}">
+                                <div><i class="Hui-iconfont">&#xe681;</i></div>
+                                <span>管理栏目</span>
+                            </a>
+                        </div>
+                        <div class="card-body">
+                        </div>
+                        <div class="card-body">
+                        </div>
+                        <div class="card-body">
+                        </div>
+                        <!-- /.card-body -->
+                    </div>
+                    <!-- /.card -->
+                </div>
+                <!--数据统计-->
+                <div class="col-12 col-sm-6">
+                    <div class="card">
+                        <div class="card-header ui-sortable-handle">
+                            <h3 class="card-title"><i class="Hui-iconfont">&#xe621;</i> 数据统计</h3>
+                        </div>
+                        <!-- /.card-header -->
+                        <div class="card-body cl">
+                            <div class="row">
+                                <!-- ./col -->
+                                <div class="col-6 f-l">
+                                    <div class="inner">
+                                        <h3> aasd</h3>
+                                        <p>待处理留言</p>
+                                    </div>
+                                </div>
+                                <!-- ./col -->
+                                <div class="col-6 f-l">
+                                    <div class="inner">
+                                        <h3>bbsd</h3>
+                                        <p>一周用户注册</p>
+                                    </div>
+                                </div>
+                                <!-- ./col -->
+                            </div>
+                        </div>
+                        <!-- /.card-body -->
+                    </div>
+                    <!-- /.card -->
+                </div>
+            </div>
+            <div class="row">
+                <!--系统信息-->
+                <div class="col-12">
+                    <div class="card">
+                        <div class="card-header ui-sortable-handle">
+                            <h3 class="card-title"><i class="Hui-iconfont">&#xe72b;</i> 系统信息</h3>
+                        </div>
+                        <!-- /.card-header -->
+                        <div class="card-body" style="display: block;">
+                            <table class="table table-striped">
+                                <tr>
+                                    <td>网站域名</td>
+                                    <td>{$config.url}</td>
+                                </tr>
+                                <tr>
+                                    <td>网站目录</td>
+                                    <td>{$config.document_root}</td>
+                                </tr>
+                                <tr>
+                                    <td>服务器操作系统</td>
+                                    <td>{$config.server_os}</td>
+                                </tr>
+                                <tr>
+                                    <td>服务器端口</td>
+                                    <td>{$config.server_port}</td>
+                                </tr>
+                                <tr>
+                                    <td>服务器IP</td>
+                                    <td>{$config.server_ip}</td>
+                                </tr>
+                                <tr>
+                                    <td>WEB运行环境</td>
+                                    <td>{$config.server_soft}</td>
+                                </tr>
+                                <tr>
+                                    <td>MySQL数据库版本</td>
+                                    <td>{$config.mysql_version}</td>
+                                </tr>
+                                <tr>
+                                    <td>运行PHP版本</td>
+                                    <td>{$config.php_version}</td>
+                                </tr>
+                                <tr>
+                                    <td>最大上传限制</td>
+                                    <td>{$config.max_upload_size}</td>
+                                </tr>
+                                <tr>
+                                    <td>已用空间</td>
+                                    <td>
+                                        <span id="Info6">
+                                            <img src="/static/images/loading.gif" alt="正在载入" class="mid"> Loading...
+                                        </span>
+                                        <span class="ml-50 c-blue">
+                                            <a href="javascript:clearCache();">清除缓存</a>
+                                        </span>
+                                    </td>
+                                </tr>
+                            </table>
+                        </div>
+                        <!-- /.card-body -->
+                    </div>
+                    <!-- /.card -->
+                </div>
+            </div>
+        </div>
+        <div class="col-lg-4">
+            <div class="row">
+                <!--版本信息-->
+                <div class="col-12">
+                    <div class="card">
+                        <div class="card-header ui-sortable-handle">
+                            <h3 class="card-title"><i class="Hui-iconfont">&#xe6cd;</i> 版本信息</h3>
+                        </div>
+                        <!-- /.card-header -->
+                        <div class="card-body" style="display: block;">
+                            <table class="table  table-border table-bordered">
+                                <tr>
+                                    <td class="text-center">当前版本</td>
+                                    <td>HuwhoisCMS V{$sys_version}</td>
+                                </tr>
+                                <tr>
+                                    <td class="text-center">基于框架</td>
+                                    <td>ThinkPHP6 + Hui.Admin</td>
+                                </tr>
+                                <tr>
+
+                                </tr>
+                                <tr>
+                                    <td>
+                                        <a href="https://www.kancloud.cn/manual/thinkphp6_0" target="_blank"
+                                            class="btn btn-flat btn-success ">ThinkPHP6 开发手册</a>
+                                    </td>
+                                    <td>
+                                        <a href="http://h-ui.net/Hui-overview.shtml" target="_blank"
+                                            class="btn btn-flat btn-secondary "> Hui.Admin 文档</a>
+                                    </td>
+                                </tr>
+                            </table>
+                        </div>
+                        <!-- /.card-body -->
+                    </div>
+                    <!-- /.card -->
+                </div>
+            </div>
+        </div>
+    </div>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    $(document).ready(function () {
+        usedSpace();
+    })
+
+    function usedSpace(params) {
+        $.post('/admin/index/usedspace', function (data) {
+            if (data.code == 1) {
+                $("#Info6").html(data.size);
+            }
+        }, 'json');
+    }
+
+    function clearCache() {
+        $.post('/admin/index/clearcache', function (data) {
+            if (data.code == 1) {
+                layer.msg('清除成功', {
+                    icon: 1,
+                    time: 1000
+                });
+                $("#Info6").html('<img src="/static/images/loading.gif" alt="正在载入" class="mid"> Loading...');
+                usedSpace();
+            }
+        }, 'json');
+    }
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 58 - 0
app/sys/view/layout.html

@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html lang="{$default_lang??'en'}">
+
+<head>
+    <meta charset="utf-8">
+    <meta name="renderer" content="webkit|ie-comp|ie-stand">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport"
+        content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
+    <meta http-equiv="Cache-Control" content="no-siteapp" />
+    <link rel="Bookmark" href="/favicon.ico">
+    <link rel="Shortcut Icon" href="/favicon.ico" />
+    <title>后台管理</title>
+    <link rel="stylesheet" type="text/css" href="/static/plugins/h-ui/css/H-ui.min.css" />
+    <link rel="stylesheet" type="text/css" href="/static/plugins/h-ui.admin/css/H-ui.admin.css" />
+    <link rel="stylesheet" type="text/css" href="/static/plugins/lib/Hui-iconfont/1.0.8/iconfont.css" />
+    <link rel="stylesheet" type="text/css" href="/static/plugins/h-ui.admin/skin/default/skin.css" id="skin" />
+    <link rel="stylesheet" type="text/css" href="/static/admin/css/style.css" />
+
+    <script type="text/javascript" src="/static/plugins/lib/jquery/1.9.1/jquery.min.js"></script>
+    <script type="text/javascript" src="/static/plugins/lib/layer/2.4/layer.js"></script>
+    <script type="text/javascript" src="/static/plugins/h-ui/js/H-ui.js"></script>
+    <script type="text/javascript" src="/static/plugins/h-ui.admin/js/H-ui.admin.page.js"></script>
+    <script type="text/javascript" src="/static/admin/js/admin.js"></script>
+</head>
+
+<body>
+    {if $layer == false}
+    {include file="public/header" /}
+    <section class="Hui-article-box">
+        <nav class="breadcrumb">
+            <i class="Hui-iconfont">&#xe67f;</i><a href="/admin/index/index" class="maincolor">首页</a>
+            {foreach $breadCrumb as $bread}
+            <span class="c-999 en">&gt;</span>
+            <span class="c-666"><a href="{$bread.url ? url($bread.url) : 'javascript:void(0);'}" title="{$bread.title}">{$bread.title}</a></span>
+            {/foreach}
+            <a class="btn btn-success radius r" style="line-height:1.6em;margin-top:3px"
+                href="javascript:location.replace(location.href);" title="刷新"><i class="Hui-iconfont">&#xe68f;</i></a>
+        </nav>
+        <div class="Hui-article">
+            {__CONTENT__}
+            <footer class="footer mt-20">
+                <div class="container-fluid">
+                    <strong>Copyright &copy; 2013-{:date("Y")} <a href="javascript:void(0);">国家材料环境腐蚀平台</a>.</strong>
+                    All rights reserved.
+                    <b>Version</b> {:config('app.sys_version')}
+                    <!-- <p>感谢jQuery、layer、laypage、Validform、UEditor、My97DatePicker、iconfont、Datatables、WebUploaded、icheck、highcharts、bootstrap-Switch<br> Copyright &copy;2015 H-ui.admin v3.0 All Rights Reserved.<br> 本后台系统由<a href="http://www.h-ui.net/" target="_blank" title="H-ui前端框架">H-ui前端框架</a>提供前端技术支持</p> -->
+                </div>
+            </footer>
+        </div>
+    </section>
+    {else/}
+    {__CONTENT__}
+    {/if}
+
+</body>
+
+</html>

+ 126 - 0
app/sys/view/login/index.html

@@ -0,0 +1,126 @@
+{__NOLAYOUT__}
+<!DOCTYPE HTML>
+<html>
+
+<head>
+    <meta charset="utf-8">
+    <meta name="renderer" content="webkit|ie-comp|ie-stand">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport"
+        content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
+    <meta http-equiv="Cache-Control" content="no-siteapp" />
+    <link href="/static/plugins/h-ui/css/H-ui.min.css" rel="stylesheet" type="text/css" />
+    <link href="/static/plugins/h-ui.admin/css/H-ui.login.css" rel="stylesheet" type="text/css" />
+    <link href="/static/plugins/h-ui.admin/css/style.css" rel="stylesheet" type="text/css" />
+    <link href="/static/plugins/lib/Hui-iconfont/1.0.8/iconfont.css" rel="stylesheet" type="text/css" />
+    <title>后台登录</title>
+</head>
+
+<body>
+    <div class="loginWraper">
+        <div id="loginform" class="loginBox form form-horizontal">
+            <input type="hidden" value="{$restore_url}" id="restore_url">
+            <div class="row cl">
+                <label class="form-label col-xs-3"><i
+                        class="Hui-iconfont">&#xe60d;</i></label>
+                <div class="formControls col-xs-8">
+                    <input id="username" name="username" type="text" placeholder="账户:" class="input-text size-L">
+                </div>
+            </div>
+            <div class="row cl">
+                <label class="form-label col-xs-3"><i
+                        class="Hui-iconfont">&#xe60e;</i></label>
+                <div class="formControls col-xs-8">
+                    <input id="password" name="password" type="password" placeholder="密码:" class="input-text size-L">
+                </div>
+            </div>
+            <div class="row cl">
+                <div class="formControls col-xs-8 col-xs-offset-3">
+                    <input type="hidden" name="__token__" value="{:token()}"/>
+                    <button class="btn btn-success radius size-L login">&nbsp;登&nbsp;&nbsp;&nbsp;&nbsp;录&nbsp;</button>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="footer">Copyright HUWHOIS</div>
+    <script type="text/javascript" src="/static/plugins/lib/jquery/1.9.1/jquery.min.js"></script>
+    <script type="text/javascript" src="/static/plugins/h-ui/js/H-ui.js"></script>
+    <script type="text/javascript" src="/static/plugins/lib/layer/2.4/layer.js"></script>
+    <script>
+        // 回车触发登录
+        $(document).keyup(function (event) {
+            if (event.keyCode == 13) {
+                $(".login").trigger("click");
+            }
+        });
+        $(function () {
+            // 后台登录
+            $(".login").click(function () {
+                var username = $("input[name='username']").val();
+                var password = $("input[name='password']").val();
+                var __token__ = $("input[name='__token__']").val();
+
+                if (!username) {
+                    layer.alert('请输入用户名', {
+                        icon: 2
+                    }, function (index) {
+                        layer.close(index);
+                        $("input[name='username']").focus();
+                    });
+                    return false;
+                }
+                if (!password) {
+                    layer.alert('请输入密码', {
+                        icon: 2
+                    }, function (index) {
+                        layer.close(index);
+                        $("input[name='password']").focus();
+                    });
+                    return false;
+                }
+                
+                $.ajax({
+                    type: "post",
+                    url: "{:url('checkLogin')}",
+                    data: {
+                        username: username,
+                        password: password,
+                        __token__: __token__
+                    },
+                    dataType: "json",
+                    beforeSend: function () {
+                        loadIndex = layer.load();
+                    },
+                    success: function (res) {
+                        if (res.code==1) {
+                            // sessionStorage.setItem('permissions', res.permissions);
+                            layer.close(loadIndex);
+                            // console.log(res.href);
+                            var restore_url = $("#restore_url").val();
+                            window.location.href = restore_url;
+                        } else {
+                            layer.close(loadIndex);
+                            layer.alert(res.msg, {
+                                icon: 2
+                            }, function (index) {
+                                layer.close(index);
+                                $(".login-codeimg").attr("src", $(".login-codeimg").attr("src") + '?' + Math.random());
+                            });
+                        }
+                    },
+                    error: function (xhr) {
+                        // layer.close(loadIndex);
+                        // layer.alert('登录失败,请稍后重试', {
+                        //     icon: 2
+                        // }, function (index) {
+                        //     layer.close(index);
+                        //     window.location.reload();
+                        // });
+                    }
+                });
+            })
+        })
+    </script>
+</body>
+
+</html>

+ 138 - 0
app/sys/view/module/index.html

@@ -0,0 +1,138 @@
+<article class="cl pd-10" style="min-width: 900px;">
+    <div class="cl pd-5 bg-1 bk-gray mt-20">
+        <span class="l">
+            <a href="javascript:;" onclick="del_all()" class="btn btn-danger radius">
+                <i class="Hui-iconfont">&#xe6e2;</i> 批量删除</a>
+            <a class="btn btn-primary radius" href="javascript:save(0);">
+                <i class="Hui-iconfont">&#xe600;</i> 添加模型</a>
+        </span>
+        <span class="r">共有数据:<strong id="total">{notempty name="list"}{$list->total()}{else/}0{/notempty}</strong>
+            条</span>
+    </div>
+    <div id="edatalist">
+        <table class="table table-border table-bordered table-bg">
+            <thead>
+                <tr class="text-c">
+                    <th width="25px;">
+                        <input type="checkbox" name="" value="">
+                    </th>
+                    <th width="80px;">ID</th>
+                    <th width="160px;">模块名称</th>
+                    <th width="160px;">表名称</th>
+                    <th width="160px;">模型名称</th>
+                    <th width="180px;">表描述</th>
+                    <th width="100px;">表类型</th>
+                    <th width="80px;">主键</th>
+                    <th width="80px;">排序</th>
+                    <th width="140px;">创建时间</th>
+                    <th>操作</th>
+                </tr>
+            </thead>
+            <tbody>
+                {foreach $list as $val}
+                <tr class="text-c va-m">
+                    <td>
+                        <input type="checkbox" value="{$val.id}" name="checkbox[]">
+                    </td>
+                    <td>{$val.id}</td>
+                    <td>{$val.module_name}</td>
+                    <td>{$val.table_name}</td>
+                    <td>{$val.model_name}</td>
+                    <td>{$val.remark}</td>
+                    <td>{$val.table_type}</td>
+                    <td>{$val.pk}</td>
+                    <td><input type="text" class="input-text input-sort" value="{$val.sort}" data-id="{$val.id}"
+                            style="text-align: center;"></td>
+                    <td>{$val.create_time|date="Y-m-d H:i:s"}</td>
+                    <td class="td-manage">
+                        <a class="btn btn-primary radius" href="javascript:save({$val.id});"
+                            title="编辑" style="text-decoration:none" class="ml-10">
+                            <i class="Hui-iconfont">&#xe6df;</i>编辑
+                        </a>
+                        <a class="btn btn-danger radius" href="javascript:;" title="删除" style="text-decoration:none"
+                            class="ml-10" onClick="del(this,'{$val.id}')">
+                            <i class="Hui-iconfont">&#xe6e2;</i>删除
+                        </a>
+                    </td>
+                </tr>
+                {/foreach}
+            </tbody>
+        </table>
+    </div>
+    <div class="cl pd-5 bg-1 bk-gray mt-20 ">
+        <span class="r">{notempty name="list"}{$list->render()|raw}{/notempty}</span>
+    </div>
+</article>
+<script>
+    function save(id) {
+        var title = id == 0 ? '添加模型' : '修改模型'
+        var url = "{:url('save')}" + "?_layer=true&id=" + id
+        layer_show(title, url, 800, 700);
+    }
+
+    // 排序
+    $(".input-sort").change(function () {
+        var sort = $(this).val();
+        var id = $(this).data('id');
+        // console.log(sort);
+        // console.log(id);
+        $.post('sort', {
+            'id': id,
+            'sort': sort
+        }, function (res) {
+            console.log(res);
+            if (res) {
+                layer.msg('排序成功', {
+                    icon: 1,
+                    time: 2000
+                });
+            } else {
+                layer.msg('排序失败', {
+                    icon: 5,
+                    time: 2000
+                });
+            }
+        }, 'json');
+    })
+    
+    // 产品上加/下架 or 文章上线.下线
+    function duty(action, obj, id) {
+        var duty = 2,
+            title = '上线',
+            button = 'default',
+            status = '下线';
+
+        if (action == 'up') {
+            duty = 1;
+            title = '下线';
+            status = '正常';
+            button = 'success';
+            action = 'down';
+        } else {
+            action = 'up';
+        }
+
+
+        var text = "<a href=\"javascript:void(0);\" title=" + title + " onClick=\"duty('" + action + "', this," + id + ")\" >";
+        text += '<span class="label label-' + button + ' radius">' + status + '</span></a>';
+
+        $.post("duty", {
+            'id': id,
+            'status': duty
+        }, function (data) {
+            if (data.code == 2) {
+                $(obj).after(text);
+                $(obj).remove();
+                layer.msg(data.msg, {
+                    icon: 6,
+                    time: 1000
+                });
+            } else {
+                layer.msg(data.msg, {
+                    icon: 5,
+                    time: 1000
+                });
+            }
+        }, 'json');
+    }
+</script>

+ 154 - 0
app/sys/view/module/save.html

@@ -0,0 +1,154 @@
+<article class="cl pd-20">
+    <form action="" method="post" class="form form-horizontal" id="form-save">
+        <input type="hidden" name="id" id="id" value="{$data.id}">
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>模块名称:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.module_name}" placeholder="请填写模块名称"
+                    id="module_name" name="module_name">
+            </div>
+            <span class="c-666"><i class="Hui-iconfont">&#xe6cd;</i> 填写中文名称,如:友情链接</span>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>表名称:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.table_name}" placeholder="请填写表名称"
+                    id="table_name" name="table_name">
+            </div>
+            <span class="c-666"><i class="Hui-iconfont">&#xe6cd;</i> 除去表前缀的数据表名称,全部小写并以`_`分割,如:user_group</span>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                模型名称:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.model_name}" placeholder="请填写请填写模型名称" id="model_name" name="model_name">
+            </div>
+            <span class="c-666"><i class="Hui-iconfont">&#xe6cd;</i> 除去表前缀的数据表名称,驼峰法命名,且首字母大写,如:UserGroup</span>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                表描述:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.table_comment}" placeholder="请填写表描述" id="table_comment"
+                    name="table_comment">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                表类型:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <span class="select-box">
+                    <select class="select" id="table_type" name="table_type">
+                        <option value="0" {eq name="data.table_type" value="0" }selected{/eq}>请选择</option>
+                        <option value="1" {eq name="data.table_type" value="1" }selected{/eq}>CMS</option>
+                        <option value="2" {eq name="data.table_type" value="2" }selected{/eq}>后台</option>
+                    </select>
+                </span>
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                主键:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.pk}" placeholder="请填写主键"
+                    id="pk" name="pk">
+            </div>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                字段列表:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" value="{$data.list_fields}" placeholder="请填写详情模板"
+                    id="list_fields" name="list_fields">
+            </div>
+            <span class="c-666"><i class="Hui-iconfont">&#xe6cd;</i> 前台列表页可调用字段,默认为*,仅用作前台CMS调用时使用</span>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">排序:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="number" min=0 max=100 class="input-text" value="{$data.sort}" name="sort"
+                    style="width:120px;">
+                </div>
+            <span class="c-666"><i class="Hui-iconfont">&#xe6cd;</i> 降序(数字越大, 越靠前)</span>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red"></span>单页模式:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <div class="radio-box">
+                    <input type="radio" name="is_single" id="is_single-1" value="1" {$data==null || $data.is_single==1
+                        ? 'checked' : "" }>
+                    <label for="is_single-1">是</label>
+                </div>
+                <div class="radio-box">
+                    <input type="radio" name="is_single" id="is_single-2" value="0" {$data.is_single==0 ? 'checked' : "" }>
+                    <label for="is_single-2">否</label>
+                </div>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">备注:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <textarea name="remark" id="remark" cols="" rows="" class="textarea"
+                    placeholder="SEO描述...最多输入500个字符" dragonfly="true" nullmsg="备注不能为空!"
+                    onKeyUp="textarealength(this,500)">{$data.remark}</textarea>
+                <p class="textarea-numberbar">
+                    <em class="textarea-length">0</em>/500
+                </p>
+            </div>
+            <div class="col-3"> </div>
+        </div>
+    </form>
+    <div class="row cl">
+        <div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-3">
+            <button type="button" class="btn btn-success radius" id="form-save-button" name="">确&nbsp;定</button>
+            <button type="button" class="btn btn-default radius" onclick="layer_close();"
+                style="margin-left:20px;">取&nbsp;消</button>
+        </div>
+    </div>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    $(function () {
+        $("#form-save-button").click(function () {
+            if (getblen($("#remark").val()) > 500) {
+                layer.msg('备注过长', {
+                    icon: 5,
+                    time: 1000
+                });
+                return false;
+            }
+
+            var data = $("#form-save").serializeArray();
+            $.ajax({
+                type: 'POST',
+                url: '{:url("save")}',
+                data: data,
+                dataType: 'json',
+                success: function (res) {
+                    if (res.code == 0) {
+                        layer.msg(res.msg, {
+                            icon: 5,
+                            time: 1000
+                        });
+                        return false;
+                    } else {
+                        layer.msg(res.msg, { icon: 1 }, function () {
+                            parent.location.reload(); // 父页面刷新
+                            var index = parent.layer.getFrameIndex(window.name); //获取窗口索引
+                            parent.layer.close(index);
+                        });
+                    }
+                }
+            })
+        })
+    })
+
+</script>

+ 26 - 0
app/sys/view/public/breadcrumb.html

@@ -0,0 +1,26 @@
+{if isset($page_title) && !empty($page_title)}
+<div class="content-header">
+    <div class="container-fluid">
+        {$page_title|raw|default=''}
+    </div>
+</div>
+{elseif $breadCrumb /}
+<div class="content-header">
+    <div class="container-fluid">
+        <div class="row">
+            <div class="col-sm-6">
+                <h1 class="m-0">
+                    {$breadCrumb.left.0}
+                    <small>{$breadCrumb.left.1}</small>
+                </h1>
+            </div><!-- /.col -->
+            <div class="col-sm-6">
+                <ol class="breadcrumb float-sm-right">
+                    <li class="breadcrumb-item"><a href="{:url('index/index')}">Home</a></li>
+                    <li class="breadcrumb-item active"><a href="{:url($breadCrumb.right.url)}">{$breadCrumb.right.title}</a></li>
+                </ol>
+            </div><!-- /.col -->
+        </div><!-- /.row -->
+    </div><!-- /.container-fluid -->
+</div>
+{/if}

+ 68 - 0
app/sys/view/public/css_js.html

@@ -0,0 +1,68 @@
+<!-- layui -->
+<link rel="stylesheet" href="/static/plugins/layui/css/layui.css">
+<!-- Google Font: Source Sans Pro -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/google-fonts/google.fonts.css">
+<!-- Font Awesome -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/fontawesome-free/css/all.min.css">
+<!-- Ionicons -->
+<link rel="stylesheet" href="https://cdn.staticfile.org/ionicons/2.0.1/css/ionicons.min.css">
+<!-- Tempusdominus Bootstrap 4 -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/tempusdominus-bootstrap-4/css/tempusdominus-bootstrap-4.min.css">
+<!-- iCheck -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/icheck-bootstrap/icheck-bootstrap.min.css">
+<!-- Theme style -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/dist/css/AdminLTE.min.css">
+<!-- overlayScrollbars -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/overlayScrollbars/css/OverlayScrollbars.min.css">
+<!-- Daterange picker -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/daterangepicker/daterangepicker.css">
+<!-- Bootstrap Color Picker -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.min.css">
+<!-- Toastr -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/toastr/toastr.min.css">
+<!-- pace-progress -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/pace-progress/themes/black/pace-theme-flat-top.css">
+<!-- jQuery -->
+<script src="/static/plugins/AdminLTE/plugins/jquery/jquery.min.js"></script>
+<!-- layui -->
+<script src="/static/plugins/layui/layui.js"></script>
+<!-- webuploader -->
+<!-- <link rel="stylesheet" href="/static/plugins/webuploader-0.1.5/webuploader.css">
+<script src="/static/plugins/webuploader-0.1.5/webuploader.js"></script> -->
+{//include file="index/webuploader" /}
+{if $system.editor == '1'}
+<!-- ueditor -->
+<script src="/static/plugins/ueditor/ueditor.config.js"></script>
+<script src="/static/plugins/ueditor/ueditor.all.min.js"> </script>
+<script src="/static/plugins/ueditor/lang/zh-cn/zh-cn.js"></script>
+{else}
+<!-- ckeditor4 -->
+<script src="/static/plugins/ckeditor/ckeditor.js"></script>
+{/if}
+<!-- Bootstrap Table -->
+<link rel="stylesheet" href="/static/plugins/bootstrap-table/bootstrap-table.min.css" />
+<!-- layer 弹层组件 -->
+<script>
+    layui.use('layer',
+        function () {
+            var layer = layui.layer;
+        })
+</script>
+<!-- zTree 树节点组件 -->
+<script type="text/javascript" src="/static/plugins/zTree_v3/js/jquery.ztree.core.js"></script>
+<script type="text/javascript" src="/static/plugins/zTree_v3/js/jquery.ztree.excheck.js"></script>
+<!-- jQueryTagsInput -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/jQueryTagsInput/jquery.tagsinput.css">
+<script src="/static/plugins/AdminLTE/plugins/jQueryTagsInput/jquery.tagsinput.js"></script>
+<!-- Select2 -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/select2/css/select2.min.css">
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/select2-bootstrap4-theme/select2-bootstrap4.min.css">
+<script src="/static/plugins/AdminLTE/plugins/select2/js/select2.full.min.js"></script>
+<!-- CodeMirror -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/codemirror/codemirror.css">
+<link rel="stylesheet" href="/static/plugins/AdminLTE/plugins/codemirror/theme/monokai.css">
+
+<!-- SIYUCMS -->
+<link rel="stylesheet" href="/static/plugins/AdminLTE/dist/css/siyucms.css">
+<script src="/static/plugins/siyu-ui.js?v=20201013"></script>
+<script src="/static/plugins/siyucms.js?v=20201013"></script>

+ 71 - 0
app/sys/view/public/foot_css_js.html

@@ -0,0 +1,71 @@
+<!-- Bootstrap 4 -->
+<script src="/static/plugins/AdminLTE/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
+<!-- daterangepicker -->
+<script src="/static/plugins/AdminLTE/plugins/moment/moment.min.js"></script>
+<script src="/static/plugins/AdminLTE/plugins/moment/locale/zh-cn.js"></script>
+<script src="/static/plugins/AdminLTE/plugins/daterangepicker/daterangepicker.js"></script>
+<!-- bootstrap color picker -->
+<script src="/static/plugins/AdminLTE/plugins/bootstrap-colorpicker/js/bootstrap-colorpicker.min.js"></script>
+<!-- Tempusdominus Bootstrap 4 -->
+<script src="/static/plugins/AdminLTE/plugins/tempusdominus-bootstrap-4/js/tempusdominus-bootstrap-4.min.js"></script>
+<!-- overlayScrollbars -->
+<script src="/static/plugins/AdminLTE/plugins/overlayScrollbars/js/jquery.overlayScrollbars.min.js"></script>
+<!-- Toastr -->
+<script src="/static/plugins/AdminLTE/plugins/toastr/toastr.min.js"></script>
+<!-- pace-progress -->
+<script src="/static/plugins/AdminLTE/plugins/pace-progress/pace.min.js"></script>
+<!-- Bootstrap Table 表格插件样式 -->
+<script src="/static/plugins/bootstrap-table/bootstrap-table.min.js"></script>
+<script src="/static/plugins/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
+<script src="/static/plugins/bootstrap-table/extensions/mobile/bootstrap-table-mobile.js"></script>
+<script src="/static/plugins/bootstrap-table/extensions/toolbar/bootstrap-table-toolbar.min.js"></script>
+<link rel="stylesheet" href="/static/plugins/bootstrap-table/extensions/fixed-columns/bootstrap-table-fixed-columns.min.css"/>
+<script src="/static/plugins/bootstrap-table/extensions/fixed-columns/bootstrap-table-fixed-columns.min.js"></script>
+<!-- AdminLTE App -->
+<script src="/static/plugins/AdminLTE/dist/js/adminlte.js"></script>
+<!-- AdminLTE for demo purposes -->
+<script src="/static/plugins/AdminLTE/dist/js/demo.js"></script>
+<!-- pjax -->
+<script src="/static/plugins/AdminLTE/plugins/pjax/jquery.pjax.js"></script>
+<!-- jQueryForm -->
+<script src="/static/plugins/AdminLTE/plugins/jQueryForm/jquery.form.js"></script>
+<!-- CodeMirror -->
+<script src="/static/plugins/AdminLTE/plugins/codemirror/codemirror.js"></script>
+<script src="/static/plugins/AdminLTE/plugins/codemirror/mode/css/css.js"></script>
+<script src="/static/plugins/AdminLTE/plugins/codemirror/mode/xml/xml.js"></script>
+<script src="/static/plugins/AdminLTE/plugins/codemirror/mode/javascript/javascript.js"></script>
+<script src="/static/plugins/AdminLTE/plugins/codemirror/mode/htmlmixed/htmlmixed.js"></script>
+<!-- jquery-treegrid -->
+<link rel="stylesheet" href="/static/plugins/jquery-treegrid/css/jquery.treegrid.css">
+<script src="/static/plugins/jquery-treegrid/js/jquery.treegrid.js"></script>
+<script src="/static/plugins/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.js"></script>
+
+{literal}
+<script type="text/javascript">
+    $(function () {
+        // 跳转页
+        $(document).on('pjax:complete', function (event, xhr, textStatus, options) {
+            var url = xhr.getResponseHeader('X-PJAX-URL');
+            if (url) {
+                $.pjax({url: url, container: '.content-wrapper'})
+            }
+        });
+
+        // a 链接
+        $(document).pjax('a[target!=_blank]', '.content-wrapper');
+
+        // form 表单
+        $(document).on('submit', 'form[data-pjax]', function (event) {
+            $.pjax.submit(event, '.content-wrapper');
+        });
+
+        // 阻止超时导致的链接跳转(ajax默认超时时间650毫秒,超时后强制刷新整个页面)
+        $(document).on('pjax:timeout', function (event) {
+            event.preventDefault()
+        });
+
+        // 重新加载
+        //$.pjax.reload('.content-wrapper');
+    })
+</script>
+{/literal}

+ 0 - 0
app/sys/view/public/footer.html


+ 125 - 0
app/sys/view/public/header.html

@@ -0,0 +1,125 @@
+<!--_header 作为公共模版分离出去-->
+<header class="navbar-wrapper">
+    <div class="navbar navbar-fixed-top">
+        <div class="container-fluid cl">
+            <a class="logo navbar-logo f-l mr-10 hidden-xs" href="/admin">后台管理</a>
+            <span class="logo navbar-slogan f-l mr-10 hidden-xs">v{$sys_version}</span>
+            <nav class="nav navbar-nav">
+                <ul class="cl">
+                    <li style="position: absolute;left: 200px;">
+                        <a href="javascript:;"><i class="Hui-iconfont">&#xe67f;</i> 主菜单 </a>
+                        <a href="javascript:;"><i class="Hui-iconfont">&#xe621;</i> 内容管理 </a>
+                    </li>
+                </ul>
+            </nav>
+            <nav id="Hui-userbar" class="nav navbar-nav navbar-userbar hidden-xs">
+                <ul class="cl">
+                    <li class="li_time">
+                        <i class="Hui-iconfont">&#xe690;</i>
+                        <span id="top_time"></span>
+                    </li>
+                    <li>
+                        <a aria-hidden="false" class="logo navbar-logo f-l mr-10 hidden-xs" href="/admin">
+                            <i class="Hui-iconfont">&#xe625;</i>系统首页</a>
+                        <a aria-hidden="false" class="logo navbar-logo f-l mr-10 hidden-xs" href="/adminedata"
+                            target="_blank">
+                            <i class="Hui-iconfont">&#xe67f;</i>数据(data网)管理</a>
+                        <a aria-hidden="false" class="logo navbar-logo f-l mr-10 hidden-xs" href="/index"
+                            target="_blank">
+                            <i class="Hui-iconfont">&#xe67f;</i>网站首页</a>
+                    </li>
+                    <li class="dropDown dropDown_hover">
+                        <a href="javascript:;" class="dropDown_A " style="width: 240px;">admin <i
+                                class="Hui-iconfont">&#xe6d5;</i></a>
+                        <ul class="dropDown-menu menu radius box-shadow">
+                            <li>
+                                <img src="{:session('adminuser.avatar') ?: '/static/images/user-64x64.png'}"
+                                    class="img-circle">
+                                <a href="javascript:void(0);">上次登录时间:{:session('adminuser.perTime')}</a>
+                                <a href="javascript:void(0);">上次登录IP:{:session('adminuser.perIp')}</a>
+                            </li>
+                            <li><a href="javascript:void(0);">超级管理员</a></li>
+                            <li><a href="javascript:modifyPassword();">修改密码</a></li>
+                            <li><a href="javascript:logout();">退出</a></li>
+                        </ul>
+                    </li>
+                    <!-- <li id="Hui-msg"> <a href="#" title="消息"><span class="badge badge-danger">1</span><i
+                                class="Hui-iconfont" style="font-size:18px">&#xe68a;</i></a> </li> -->
+                    <li id="Hui-skin" class="dropDown right dropDown_hover"> <a href="javascript:;" class="dropDown_A"
+                            title="换肤"><i class="Hui-iconfont" style="font-size:18px">&#xe62a;</i></a>
+                        <ul class="dropDown-menu menu radius box-shadow">
+                            <li><a href="javascript:;" data-val="default" title="默认(黑色)">默认(黑色)</a></li>
+                            <li><a href="javascript:;" data-val="blue" title="蓝色">蓝色</a></li>
+                            <li><a href="javascript:;" data-val="green" title="绿色">绿色</a></li>
+                            <li><a href="javascript:;" data-val="red" title="红色">红色</a></li>
+                            <li><a href="javascript:;" data-val="yellow" title="黄色">黄色</a></li>
+                            <li><a href="javascript:;" data-val="orange" title="橙色">橙色</a></li>
+                        </ul>
+                    </li>
+                </ul>
+            </nav>
+        </div>
+    </div>
+</header>
+<!--/_header 作为公共模版分离出去-->
+<!--_menu 作为公共模版分离出去-->
+<aside class="Hui-aside">
+    <div class="menu_dropdown bk_2">
+        <dl id="menu-article">
+            <dt>
+                <a href="{:url('index/index')}"><i class="Hui-iconfont">&#xe67f;</i> 首页</a>
+            </dt>
+        </dl>
+        {foreach $menus as $value}
+        <dl id="menu-article">
+            {eq name="value.type" value="1"}
+            <dt {eq name="value.id" value="$active_pid"} class="selected"{/eq}>
+                <a href="{:url($value.url)}"><i class="Hui-iconfont">{$value.icon|raw}</i> {$value.name}</a>
+            </dt>
+            {else/}
+            <dt {eq name="value.id" value="$active_pid"} class="selected"{/eq}>
+                <i class="Hui-iconfont">{$value.icon|raw}</i> {$value.name}
+                <i class="Hui-iconfont menu_dropdown-arrow">&#xe6d5;</i>
+            </dt>
+            <dd {eq name="value.id" value="$active_pid" }style="display:block"{/eq}>
+                {notempty name='value.child'} {foreach $value.child as $val}
+                <ul>
+                    <li>
+                        <a href="{:url($val.url)}"><i class="Hui-iconfont">{$val.icon|raw}</i> {$val.name}</a>
+                    </li>
+                </ul>
+                {/foreach} {/notempty}
+            </dd>
+            {/eq}
+        </dl>
+        {/foreach}
+    </div>
+</aside>
+<div class="dislpayArrow hidden-xs"><a class="pngfix" href="javascript:void(0);" onClick="displaynavbar(this)"></a>
+</div>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    // 退出登陆
+    function logout() {
+        layer.confirm('纳尼,要退出?', function (index) {
+            window.location.href = "{:url('login/logout')}";
+        })
+    }
+
+    // 修改密码
+    function modifyPassword() {
+        var url = "{:url('sys_user/password')}" + "?_layer=true";
+        layer_show('修改密码', url, 800, 400);
+    }
+
+    // 按钮权限问题
+    function isAuth(key) {
+        var permissions = sessionStorage.getItem('permissions').split(",") || '[]';
+        
+        // console.log(JSON.parse('[]'));
+        // console.log(permissions);
+        return permissions.indexOf(key) !== -1 || false
+    }
+</script>
+<!--/请在上方写此页面业务相关的脚本-->

+ 82 - 0
app/sys/view/sys_log/index.html

@@ -0,0 +1,82 @@
+<article class="cl pd-20">
+    <div class="cl pd-5 bg-1 bk-gray">
+        <span class="l">
+            <a href="javascript:;" onclick="del_all()" class="btn btn-danger radius">
+                <i class="Hui-iconfont">&#xe6e2;</i> 批量删除</a>
+        </span>
+        <!-- <span class="r">共有数据:<strong>54</strong> 条</span>  -->
+    </div>
+    <div class="mt-10">
+        <table class="table table-border table-bordered table-hover table-bg">
+            <thead>
+                <tr class="text-c">
+                    <th width="25">
+                        <input type="checkbox" value="" name="allcheck">
+                    </th>
+                    <th width="80px">ID</th>
+                    <th width="40px">userid</th>
+                    <th width="80px">用户名</th>
+                    <th width="100px">标题</th>
+                    <th>内容</th>
+                    <th width="140px">时间</th>
+                    <th width="120px">ip</th>
+                    <th>操作</th>
+                </tr>
+            </thead>
+            <tbody>
+                {notempty name="list"}
+                {foreach $list as $val}
+                <tr class="text-c">
+                    <td>
+                        <input type="checkbox" value="{$val.id}" name="checkbox[]">
+                    </td>
+                    <td>{$val.id}</td>
+                    <td>{$val.userid}</td>
+                    <td>{$val.username}</td>
+                    <td>{$val.title}</td>
+                    <td>{$val.content}</td>
+                    <td>{$val.create_time}</td>
+                    <td>{$val.ip}</td>
+                    <td class="td-manage">
+                        <a title="删除" href="javascript:;" onclick="del(this,'{$val.loginid}')" class="ml-5"
+                            style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6e2;</i>
+                        </a>
+                    </td>
+                </tr>
+                {/foreach}
+                {/notempty}
+            </tbody>
+        </table>
+    </div>
+    <div class="cl pd-5 bg-1 bk-gray mt-20 ">
+        <span class="r">{$list->render()|raw}</span>
+    </div>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    // 删除条目
+    function del(obj, id) {
+        layer.confirm('确认要删除吗?', function (index) {
+            $.post('delete', {
+                'id': id
+            }, function (data) {
+                if (data.code == 1) {
+                    $(obj).parents("tr").remove();
+                    layer.msg(data.msg, {
+                        icon: 1,
+                        time: 1000
+                    });
+                } else {
+                    layer.msg(data.msg, {
+                        icon: 5,
+                        time: 2000
+                    });
+                    return false;
+                }
+            }, 'json');
+        });
+    }
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 117 - 0
app/sys/view/sys_login/index.html

@@ -0,0 +1,117 @@
+<article class="cl pd-20" style="min-width: 900px;max-width: 80%;">
+    <div class="cl pd-5 bg-1 bk-gray">
+        <span class="l">
+            <a href="javascript:;" onclick="del_all()" class="btn btn-danger radius">
+                <i class="Hui-iconfont">&#xe6e2;</i> 批量删除</a>
+        </span>
+        <!-- <span class="r">共有数据:<strong>54</strong> 条</span>  -->
+    </div>
+    <div class="mt-10">
+        <table class="table table-border table-bordered table-hover table-bg">
+            <thead>
+                <tr class="text-c">
+                    <th width="25">
+                        <input type="checkbox" value="" name="selectAll">
+                    </th>
+                    <th width="100px">ID</th>
+                    <th width="100px">userid</th>
+                    <th>用户名</th>
+                    <th>登录时间</th>
+                    <th>登录ip</th>
+                    <th>登录状态</th>
+                    <th>操作</th>
+                </tr>
+            </thead>
+            <tbody>
+                {notempty name="list"}
+                {foreach $list as $val}
+                <tr class="text-c">
+                    <td>
+                        <input type="checkbox" value="{$val.loginid}" name="checkbox[]">
+                    </td>
+                    <td>{$val.loginid}</td>
+                    <td>{$val.userid}</td>
+                    <td>{$val.username}</td>
+                    <td>{$val.logintime|date="Y-m-d H:i:s"}</td>
+                    <td>{$val.loginip}</td>
+                    <td class="f-status">
+                    {switch val.status}
+                        {case 1}<span class="label radius label-danger">>密码错误</span{/case}
+                        {case 2}<span class="label radius label-default">禁用用户</span>{/case}
+                        {default /}<span class="label radius label-success">成功</span>
+                    {/switch}
+                       
+                    </td>
+                    <td class="td-manage">
+                        <a title="删除" href="javascript:;" onclick="del(this,'{$val.loginid}')" class="ml-5"
+                            style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6e2;</i>
+                        </a>
+                    </td>
+                </tr>
+                {/foreach}
+                {/notempty}
+            </tbody>
+        </table>
+    </div>
+    <div class="cl pd-5 bg-1 bk-gray mt-20 ">
+        <span class="r">{notempty name="data"}{$data->render()|raw}{/notempty}</span>
+    </div>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    // 删除条目
+    function del(obj, id) {
+        layer.confirm('确认要删除吗?', function (index) {
+            $.post('delete', {
+                'id': id
+            }, function (data) {
+                if (data.code == 1) {
+                    $(obj).parents("tr").remove();
+                    layer.msg(data.msg, {
+                        icon: 1,
+                        time: 1000
+                    });
+                } else {
+                    layer.msg(data.msg, {
+                        icon: 5,
+                        time: 2000
+                    });
+                    return false;
+                }
+            }, 'json');
+        });
+    }
+
+    // 批量删除
+    function del_all() {
+        var checkbox = $('.text-c input[name="checkbox[]"]');
+        var ids = new Array();
+        checkbox.each(function (x) {
+            if (this.checked)
+                ids.push(this.value);
+        })
+        console.log(ids);
+
+        layer.confirm('确认要删除吗?', function (index) {
+            $.post('delete', {
+                'id': ids
+            }, function (data) {
+                if (data.code == 1) {
+                    layer.msg(data.msg, {
+                        icon: 1,
+                        time: 1000
+                    });
+                    window.location.reload();
+                } else {
+                    layer.msg(data.msg, {
+                        icon: 5,
+                        time: 1000
+                    });
+                }
+            }, 'json')
+        });
+    }
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 165 - 0
app/sys/view/sys_menu/index.html

@@ -0,0 +1,165 @@
+<article class="cl pd-20">
+    <div class="cl pd-5 bg-1 bk-gray">
+        <span class="l">
+            <!-- <a href="javascript:;" onclick="del_all()" class="btn btn-danger radius">
+                    <i class="Hui-iconfont">&#xe6e2;</i> 批量删除</a> -->
+            <a class="btn btn-primary radius" href="javascript:save(0);">
+                <i class="Hui-iconfont">&#xe600;</i> 添加目录</a>
+        </span>
+        <span class="r">共有数据:<strong>54</strong> 条</span> 
+    </div>
+    <div class="mt-10" style="min-width:800px;">
+        <table class="table table-border table-bordered table-hover table-bg">
+            <thead>
+                <tr class="text-c">
+                    <!-- <th width="25px">
+                        <input type="checkbox" value="" name="">
+                    </th> -->
+                    <th width="40px">ID</th>
+                    <th>名称</th>
+                    <th width="120px">上级菜单</th>
+                    <th width="60px">图标</th>
+                    <th width="60px">类型</th>
+                    <th width="160px">url</th>
+                    <th width="120px">状态</th>
+                    <th>操作</th>
+                </tr>
+            </thead>
+            <tbody>
+                {foreach $list as $value}
+                <tr class="text-c">
+                    <!-- <td>
+                        <input type="checkbox" value="{//$value.id}" name="checkbox[]">
+                    </td> -->
+                    <td>{$value.id}</td>
+                    <td class="text-l">{notempty name="value.child"}
+                        <a href="javascript:void(0);" title="展开" data-status="1"
+                            onClick="loadchild(this,'{$value.id}')">
+                            <i class="Hui-iconfont">&#xe6d7;</i></a>{/notempty}
+                        {$value.name}
+                    </td>
+                    <td></td>
+                    <td><i class="Hui-iconfont">{$value.icon|raw}</i></td>
+                    <td><span class="label label-default">{$type[$value.type]}</span></td>
+                    <td>{$value.url}</td>
+                    <td>{$value.perms}</td>
+                    <td class="f-14">
+                        <a title="编辑" href="javascript:save({$value.id});" style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6df;</i>
+                        </a>
+                        <a title="删除" href="javascript:;" onclick="del(this,'{$value.id}')" class="ml-5"
+                            style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6e2;</i>
+                        </a>
+                    </td>
+                </tr>
+                {notempty name="value.child"} {foreach $value.child as $val}
+                <tr class="text-c parent_{$value.id}">
+                    <!-- <td>
+                        <input type="checkbox" value="{$val.id}" name="checkbox[]">
+                    </td> -->
+                    <td>{$val.id}</td>
+                    <td class="text-l">{notempty name="val.child"}
+                        <a class="child ml-20" href="javascript:void(0);" title="展开" data-status="0"
+                            onClick="loadchild(this,'{$val.id}')">
+                            <i class="Hui-iconfont">&#xe6d7;</i>
+                            {else/}
+                            <a class="child ml-20" href="javascript:void(0);">
+                            {/notempty}
+                        {$val.name}</a>
+                    </td>
+                    <td>{$value.name}</td>
+                    <td><i class="Hui-iconfont">{$val.icon|raw}</i></td>
+                    <td><span class="label label-success">{$type[$val.type]}</span></td>
+                    <td>{$val.url}</td>
+                    <td>{$value.perms}</td>
+                    <td class="f-14">
+                        <a title="编辑" href="javascript:save({$val.id});" style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6df;</i>
+                        </a>
+                        <a title="删除" href="javascript:;" onclick="del(this,'{$val.id}')" class="ml-5"
+                            style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6e2;</i>
+                        </a>
+                    </td>
+                </tr>
+                {notempty name="val.child"} {foreach $val.child as $vo}
+                <tr class="text-c grandparent_{$value.id} parent_{$val.id}" style="display: none;">
+                    <!-- <td>
+                        <input type="checkbox" value="{$vo.id}" name="checkbox[]">
+                    </td> -->
+                    <td>{$vo.id}</td>
+                    <td class="text-l"><span style="margin-left: 80px;">{$vo.name}</span></td>
+                    <td>{$val.name}</td>
+                    <td></td>
+                    <td><span class="label label-default">{$type[$vo.type]}</span></td>
+                    <td>{$vo.url}</td>
+                    <td>{$vo.perms}</td>
+                    <td class="f-14">
+                        <a title="编辑" href="javascript:save({$vo.id});" style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6df;</i>
+                        </a>
+                        <a title="删除" href="javascript:;" onclick="del(this,'{$vo.id}')" class="ml-5"
+                            style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6e2;</i>
+                        </a>
+                    </td>
+                </tr>
+                {/foreach} {/notempty}{/foreach} {/notempty} {/foreach}
+            </tbody>
+        </table>
+    </div>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    function save(id) {
+        var title = id==0 ? '添加菜单' : '修改菜单'
+        var url = "{:url('save')}" + "?_layer=true&id=" + id
+        layer_show(title, url, 800, 400);
+    }
+
+    function loadchild(obj, id) {
+        var status = $(obj).data('status');
+        // console.log(obj);
+        // console.log(id);
+        // console.log(status);
+
+        if (status == 1) {
+            $(".parent_" + id).css("display", "none");
+            $(".parent_" + id).find("td a.child").children('i').html('&#xe6d7;');
+            $(".parent_" + id).find("td a.child").data('status', 0);
+            $(".grandparent_" + id).css("display", "none");
+            $(obj).children('i').html('&#xe6d7;');
+            $(obj).data('status', 0);
+            return false;
+        }
+        $(".parent_" + id).css('display', '');
+        $(obj).children('i').html('&#xe6d5;');
+        $(obj).data('status', 1);
+    }
+
+    function del(obj, id) {
+        layer.confirm('确认要删除吗?', function (index) {
+            $.post('delete', {
+                'id': id
+            }, function (data) {
+                if (data.code == 1) {
+                    console.log($(obj));
+                    $(obj).parents("tr").remove();
+                    layer.msg(data.msg, {
+                        icon: 1,
+                        time: 1000
+                    });
+                } else {
+                    layer.msg(data.msg, {
+                        icon: 5,
+                        time: 2000
+                    });
+                    return false;
+                }
+            }, 'json');
+        });
+    }
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 103 - 0
app/sys/view/sys_menu/save.html

@@ -0,0 +1,103 @@
+<article class="cl pd-20">
+    <form action="" method="post" class="form form-horizontal" id="form-admin-menu">
+        <input type="hidden" id="id" name="id" value="{$data.id}">
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">上级权限</label>
+            <div class="formControls col-xs-8 col-sm-4">
+                <span class="select-box" style="width:150px;">
+                    <select class="select" id="pid" name="pid">
+                        <option value="0" {eq name='data.pid' value="0" }selected{/eq}>--新增顶级权限--</option>
+                        {foreach $menus as $value}
+                        <option value="{$value.id}" {eq name='data.pid' value="$value.id" }selected{/eq}>
+                            ----{$value.name}----</option>
+                        {notempty name="value.child"}
+                        {foreach $value.child as $val}
+                        <option value="{$val.id}" {eq name='data.pid' value="$val.id" }selected{/eq}>
+                            &nbsp;&nbsp;&nbsp;&nbsp;* {$val.name}---</option>
+                        {/foreach}
+                        {/notempty}
+                        {/foreach}
+                    </select>
+                </span>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span> 菜单名称:</label>
+            <div class="formControls col-xs-8 col-sm-4">
+                <input type="text" class="input-text" value="{$data.name}" placeholder="请输入目录名称" id="name"
+                    name="name" />
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span> url:</label>
+            <div class="formControls col-xs-8 col-sm-4">
+                <input type="text" class="input-text" value="{$data.url}" placeholder="请输入url,目录无需填写" id="url"
+                    name="url" />
+            </div>
+            <span style="color:red;">* 小写字母</span>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">目录类型:</label>
+            <div class="formControls col-xs-8 col-sm-2">
+                <span class="select-box" style="width:150px;">
+                    <select class="select" id="type" name="type">
+                        <option value="0" {eq name='data.type' value="0" }selected{/eq}>目录</option>
+                        <option value="1" {eq name='data.type' value="1" }selected{/eq}>菜单</option>
+                        <option value="2" {eq name='data.type' value="2" }selected{/eq}>按钮</option>
+                    </select>
+                </span>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">图标样式:</label>
+            <div class="formControls col-xs-4 col-sm-2">
+                <input type="text" class="input-text" value="{$data.icon}" placeholder="图片值" id="icon" name="icon" />
+            </div>
+            <div class="formControls col-xs-2 col-sm-2">
+                预览:<i class="Hui-iconfont">{$data.icon|raw}</i>
+            </div>
+            <span style="color:red;">* 图标示例 <a href="/static/plugins/lib/Hui-iconfont/1.0.8/demo.html" target="_blank"
+                    style="color:blue;" rel="noopener noreferrer"> demo</a></span>
+        </div>
+        <div class="row cl">
+            <div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-3">
+                <button type="button" class="btn btn-success radius" id="admin-powers-save" name="">确&nbsp;定</button>
+                <button type="button" class="btn btn-default radius" onclick="layer_close();"
+                    style="margin-left:20px;">取&nbsp;消</button>
+            </div>
+        </div>
+    </form>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    $(function () {
+        $("#admin-powers-save").click(function () {
+            var data = $("#form-admin-menu").serializeArray();
+            $.ajax({
+                type: 'POST',
+                url: '{:url("save")}',
+                data: data,
+                dataType: 'json',
+                success: function(res) {
+                    // console.log(res);
+                    if (res.code=0) {
+                        layer.msg(data.msg, {
+                            icon: 5,
+                            time: 1000
+                        });
+                        return false;
+                    } else {
+                        layer.msg(res.msg,{icon:1},function(){
+                            parent.location.reload(); // 父页面刷新
+                            var index = parent.layer.getFrameIndex(window.name); //获取窗口索引
+                            parent.layer.close(index);
+                        });
+                    }
+                }
+            })
+        });
+    })
+</script>

+ 117 - 0
app/sys/view/sys_role/index.html

@@ -0,0 +1,117 @@
+<article class="cl pd-20">
+    <div class="cl pd-5 bg-1 bk-gray">
+        <span class="l">
+            <a href="javascript:;" onclick="del_all()" class="btn btn-danger radius">
+                <i class="Hui-iconfont">&#xe6e2;</i> 批量删除</a>
+            <a class="btn btn-primary radius" href="javascript:save(0);">
+                <i class="Hui-iconfont">&#xe600;</i> 添加角色</a>
+        </span>
+        <span class="r">共有数据:<strong>{$list|count}</strong> 条</span> 
+    </div>
+    <div class="mt-10">
+        <table class="table table-border table-bordered table-hover table-bg">
+            <thead>
+                <tr>
+                    <th scope="col" colspan="6">角色管理</th>
+                </tr>
+                <tr class="text-c">
+                    <th width="25">
+                        <input type="checkbox" value="" name="">
+                    </th>
+                    <th width="40px">ID</th>
+                    <th width="120px">角色名称</th>
+                    <th>权限(menu_ids)</th>
+                    <th width="160px">备注</th>
+                    <th width="80px">操作</th>
+                </tr>
+            </thead>
+            <tbody>
+                {foreach $list as $val}
+                <tr class="text-c">
+                    <td>
+                        <input type="checkbox" value="{$val.roleid}" name="checkbox[]">
+                    </td>
+                    <td>{$val.roleid}</td>
+                    <td>{$val.name}</td>
+                    <td>{$val.permissions}</td>
+                    <td>{$val.remark}</td>
+                    <td class="f-14">
+                        <a title="编辑" href="javascript:save({$val.roleid});" style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6df;</i>
+                        </a>
+                        <a title="删除" href="javascript:;" onclick="del(this,'{$val.roleid}')" class="ml-5"
+                            style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6e2;</i>
+                        </a>
+                    </td>
+                </tr>
+                {/foreach}
+            </tbody>
+        </table>
+    </div>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    function save(id) {
+        var title = id==0 ? '添加角色' : '修改角色'
+        var url = "{:url('save')}" + "?_layer=true&id=" + id
+        layer_show(title, url, 800, 700);
+    }
+    // 删除条目
+    function del(obj, id) {
+        layer.confirm('确认要删除吗?', function (index) {
+            $.post('delete', {
+                'id': id
+            }, function (data) {
+                if (data.code == 1) {
+                    $(obj).parents("tr").remove();
+                    layer.msg(data.msg, {
+                        icon: 1,
+                        time: 1000
+                    });
+                } else {
+                    layer.msg(data.msg, {
+                        icon: 5,
+                        time: 2000
+                    });
+                    return false;
+                }
+            }, 'json');
+        });
+    }
+
+    // 批量删除
+    function del_all() {
+        var checkbox = $('.text-c input[name="checkbox[]"]');
+        var ids = new Array();
+        checkbox.each(function (x) {
+            if (this.checked)
+                ids.push(this.value);
+        })
+        console.log(ids);
+
+        layer.confirm('确认要删除吗?', function (index) {
+            $.post('delete', {
+                'id': ids
+            }, function (data) {
+                if (data.code == 1) {
+                    layer.msg(data.msg, {
+                        icon: 1,
+                        time: 1000
+                    });
+                    checkbox.each(function (x) {
+                        if (this.checked)
+                            $(this).parents("tr").remove();
+                    })
+                } else {
+                    layer.msg(data.msg, {
+                        icon: 5,
+                        time: 1000
+                    });
+                }
+            }, 'json')
+        });
+    }
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 120 - 0
app/sys/view/sys_role/save.html

@@ -0,0 +1,120 @@
+<article class="cl pd-20">
+	<form action="" method="post" class="form form-horizontal" id="form-save">
+		<input type="hidden" id="roleid" name="roleid" value="{$data.roleid}">
+		<div class="row cl">
+			<label class="form-label col-xs-4 col-sm-2"><span class="c-red">*</span>角色名称:</label>
+			<div class="formControls col-xs-8 col-sm-9">
+				<input type="text" class="input-text" value="{$data.name}" placeholder="" id="name" name="name"
+					datatype="*4-16" nullmsg="角色名称">
+			</div>
+		</div>
+		<div class="row cl">
+			<label class="form-label col-xs-4 col-sm-2">备注:</label>
+			<div class="formControls col-xs-8 col-sm-9">
+				<input type="text" class="input-text" value="{$data.remark}" placeholder="备注" id="remark" name="remark">
+			</div>
+		</div>
+		<div class="row cl">
+			<label class="form-label col-xs-4 col-sm-2">权限选择:</label>
+			<div class="formControls col-xs-8 col-sm-9">
+				{foreach $dataPermission as $value}
+				<dl class="permission-list">
+					<dt>
+						<label>
+							<input type="checkbox" value="{$value.id}" id="check_{$value.id}" name="perm_check[]" {if
+								condition="in_array($value.id, $rolePowers)" }checked{/if}>
+							{$value.name}</label>
+					</dt>
+					{notempty name='value.child'}
+					{foreach $value.child as $val}
+					<dd>
+						<dl class="cl permission-list2">
+							<dt>
+								<label class="">
+									<input type="checkbox" value="{$val.id}" name="perm_check[]" id="check_{$val.id}"
+										{if condition="in_array($val.id, $rolePowers)" }checked{/if}>
+									{$val.name}</label>
+							</dt>
+							{notempty name='val.child'}
+							<dd>
+								{foreach $val.child as $vo}
+								<label class="">
+									<input type="checkbox" value="{$vo.id}" name="perm_check[]" id="check_{$val.id}" {if
+										condition="in_array($vo.id, $rolePowers)" }checked{/if}>
+									{$vo.name}</label>
+								{/foreach}
+							</dd>
+							{/notempty}
+						</dl>
+					</dd>
+					{/foreach}
+					{/notempty}
+				</dl>
+				{/foreach}
+			</div>
+		</div>
+		<div class="row cl">
+			<div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-3">
+				<button type="button" class="btn btn-success radius" id="form-save-button"><i class="icon-ok"></i>
+					确定</button>
+				<button type="button" class="btn btn-default radius" onClick="layer_close();"><i
+						class="Hui-iconfont">&#xe66b;</i>取消</button>
+			</div>
+		</div>
+	</form>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+	$(function () {
+		$(".permission-list dt input:checkbox").click(function () {
+			$(this).closest("dl").find("dd input:checkbox").prop("checked", $(this).prop("checked"));
+		});
+		$(".permission-list2 dd input:checkbox").click(function () {
+			var l = $(this).parent().parent().find("input:checked").length;
+			var l2 = $(this).parents(".permission-list").find(".permission-list2 dd").find("input:checked").length;
+			if ($(this).prop("checked")) {
+				$(this).closest("dl").find("dt input:checkbox").prop("checked", true);
+				$(this).parents(".permission-list").find("dt").first().find("input:checkbox").prop("checked", true);
+			}
+			else {
+				if (l == 0) {
+					$(this).closest("dl").find("dt input:checkbox").prop("checked", false);
+				}
+				if (l2 == 0) {
+					$(this).parents(".permission-list").find("dt").first().find("input:checkbox").prop("checked", false);
+				}
+			}
+		});
+
+		$("#form-save-button").click(function () {
+			var data = $("#form-save").serializeArray();
+			$.ajax({
+				type: 'POST',
+				url: '{:url("save")}',
+				data: data,
+				dataType: 'json',
+				success: function (res) {
+					console.log(res);
+					if (res.code = 0) {
+						layer.msg(data.msg, {
+							icon: 5,
+							time: 1000
+						});
+						return false;
+					} else {
+						layer.msg(res.msg, {
+							icon: 1,
+							time: 1000
+						}, function () {
+							parent.location.reload(); // 父页面刷新
+							var index = parent.layer.getFrameIndex(window.name); //获取窗口索引
+							parent.layer.close(index);
+						});
+					}
+				}
+			})
+		});
+	})
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 125 - 0
app/sys/view/sys_user/index.html

@@ -0,0 +1,125 @@
+<article class="cl pd-20">
+    <div class="cl pd-5 bg-1 bk-gray">
+        <span class="l">
+            <a href="javascript:;" onclick="del_all()" class="btn btn-danger radius">
+                <i class="Hui-iconfont">&#xe6e2;</i> 批量删除</a>
+            <a href="javascript:save(0);" class="btn btn-primary radius">
+                <i class="Hui-iconfont">&#xe600;</i> 添加管理员</a>
+        </span>
+    </div>
+    <div class="mt-10" style="min-width:800px;">
+        <table class="table table-border table-bordered table-bg">
+            <thead>
+                <tr class="text-c">
+                    <th width="25">
+                        <input type="checkbox" name="allcheck" value="">
+                    </th>
+                    <th width="40px">ID</th>
+                    <th width="120px">登录名</th>
+                    <th width="120px">用户组</th>
+                    <th width="120px">姓名</th>
+                    <th width="120px">昵称</th>
+                    <th width="160px">邮箱</th>
+                    <th width="80px">登录次数</th>
+                    <th width="140px">最后登录时间</th>
+                    <th width="120px">最后登录ip</th>
+                    <th width="140px">创建时间</th>
+                    <th>操作</th>
+                </tr>
+            </thead>
+            <tbody>
+                {foreach $list as $val}
+                <tr class="text-c">
+                    <td>
+                        <input type="checkbox" value="{$val.userid}" name="checkbox[]">
+                    </td>
+                    <td>{$val.userid}</td>
+                    <td>{$val.username}</td>
+                    <td>{$val.rolename}</td>
+                    <td>{$val.truename}</td>
+                    <td>{$val.nickname}</td>
+                    <td>{$val.email}</td>
+                    <td>{$val.login_num}</td>
+                    <td>{$val.per_time}</td>
+                    <td>{$val.per_ip}</td>
+                    <td>{$val.create_time}</td>
+                    <td class="td-manage">
+                        <a title="编辑" href="javascript:save({$val.userid});" style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6df;</i>
+                        </a>
+                        <a title="删除" href="javascript:;" onclick="del(this,'{$val.userid}')" class="ml-5"
+                            style="text-decoration:none">
+                            <i class="Hui-iconfont">&#xe6e2;</i>
+                        </a>
+                    </td>
+                </tr>
+                {/foreach}
+            </tbody>
+        </table>
+    </div>
+</article>
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    function save(id) {
+        var title = id == 0 ? '添加管理员' : '修改管理员'
+        var url = "{:url('save')}" + "?_layer=true&id=" + id
+        layer_show(title, url, 800, 600);
+    }
+
+    // 列表删除条目
+    function del(obj, id) {
+        layer.confirm('确认要删除吗?', function (index) {
+            $.post('delete', {
+                'id': id
+            }, function (data) {
+                if (data.code == 1) {
+                    $(obj).parents("tr").remove();
+                    layer.msg(data.msg, {
+                        icon: 1,
+                        time: 1000
+                    });
+                } else {
+                    layer.msg(data.msg, {
+                        icon: 5,
+                        time: 2000
+                    });
+                    return false;
+                }
+            }, 'json');
+        });
+    }
+
+    // 批量删除条目
+    function del_all() {
+        var checkbox = $('.text-c input[name="checkbox[]"]');
+        var ids = new Array();
+        checkbox.each(function (x) {
+            if (this.checked)
+                ids.push(this.value);
+        })
+        // console.log(ids);
+
+        layer.confirm('确认要删除吗?', function (index) {
+            $.post('delete', {
+                'id': ids
+            }, function (data) {
+                if (data.code == 1) {
+                    layer.msg(data.msg, {
+                        icon: 1,
+                        time: 1000
+                    });
+                    checkbox.each(function (x) {
+                        if (this.checked)
+                            $(this).parents("tr").remove();
+                    })
+                } else {
+                    layer.msg(data.msg, {
+                        icon: 5,
+                        time: 1000
+                    });
+                }
+            }, 'json')
+        });
+    }
+</script>
+<!--/请在上方写此页面业务相关的脚本-->

+ 96 - 0
app/sys/view/sys_user/password.html

@@ -0,0 +1,96 @@
+<article class="cl pd-20">
+    <form action="" method="post" class="form form-horizontal" id="form-save">
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>原密码:</label>
+            <div class="formControls col-xs-8 col-sm-4">
+                <input type="password" class="input-text" autocomplete="off" value="" placeholder="初始密码"
+                    id="oldpassword" name="oldpassword">
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>新密码:</label>
+            <div class="formControls col-xs-8 col-sm-4">
+                <input type="password" class="input-text" autocomplete="off" value="" placeholder="请输入新密码"
+                    id="newpassword" name="newpassword">
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>确认密码:</label>
+            <div class="formControls col-xs-8 col-sm-4">
+                <input type="password" class="input-text" autocomplete="off" value="" placeholder="请再次输入新密码"
+                    id="repassword" name="repassword">
+            </div>
+        </div>
+        <div class="row cl">
+            <div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-2">
+                <button class="btn btn-primary radius" type="button"
+                    id="form-save-button">&nbsp;&nbsp;提交&nbsp;&nbsp;</button>
+                <button class="btn btn-default radius" type="button" onClick="layer_close();">&nbsp;&nbsp;取消&nbsp;&nbsp;
+            </div>
+        </div>
+    </form>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    $("#form-save-button").click(function () {
+        var oldpassword = $("#oldpassword").val();
+        var newpassword = $("#newpassword").val();
+        var repassword = $("#repassword").val();
+
+        if (isNull(oldpassword)) {
+            layer.msg('* 原密码不能为空', {
+                icon: 5,
+                time: 1000
+            });
+            return false;
+        }
+
+        if (!pwdFormat6Mix(newpassword)) {
+            layer.msg('* 密码6位及以上, 至少包括字母和数字', {
+                icon: 5,
+                time: 1000
+            });
+            return false;
+        }
+
+        if (newpassword != repassword) {
+            layer.msg('* 两次密码不一致,请重新输入', {
+                icon: 5,
+                time: 1000
+            });
+            return false;
+        }
+
+        $.ajax({
+                type: 'POST',
+                url: '{:url("password")}',
+                data: {
+                    oldpassword: oldpassword,
+                    newpassword: newpassword,
+                    repassword: repassword,
+                },
+                dataType: 'json',
+                success: function (res) {
+                    if (res.code = 0) {
+                        layer.msg(res.msg, {
+                            icon: 5,
+                            time: 1000
+                        });
+                        return false;
+                    } else {
+                        layer.msg(res.msg, { icon: 1 }, function () {
+                            parent.location.reload(); // 父页面刷新
+                            var index = parent.layer.getFrameIndex(window.name); //获取窗口索引
+                            parent.layer.close(index);
+                        });
+                    }
+                }
+            })
+
+    })
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 241 - 0
app/sys/view/sys_user/save.html

@@ -0,0 +1,241 @@
+<article class="cl pd-20">
+    <form action="" method="post" class="form form-horizontal" id="form-save">
+        <input type="hidden" name="userid" id="userid" value="{$data.userid}">
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>用户名:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <input type="text" class="input-text" id="username" name="username" value="{$data.username}"
+                    autocomplete="new-password">
+            </div>
+        </div>
+        {empty name="$data.id"}
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>初始密码:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="password" class="input-text" id="password" name="password" value=""
+                    placeholder="初始密码, 6位及以上, 至少包括字母和数字" autocomplete="new-password">
+            </div>
+            <div class=" form-label col-xs-4 col-sm-4" style="text-align: left;">
+                <span class="c-red" id="span_password">* 为空时使用系统配置的默认密码</span>
+            </div>
+        </div>
+        {else/}
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                修改密码:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <input type="password" class="input-text" id="password" name="password" value=""
+                    placeholder="6位及以上, 至少包括字母和数字" autocomplete="new-password">
+            </div>
+        </div>
+        {/empty}
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                确认密码:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <input type="password" class="input-text" id="repassword" name="repassword" value=""
+                    placeholder="确认密码, 两次密码需一致">
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                昵称: </label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <input type="text" class="input-text" id="nickname" name="nickname" value="{$data.nickname}"
+                    placeholder="昵称">
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                姓名: </label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <input type="text" class="input-text" id="truename" name="truename" value="{$data.truename}"
+                    placeholder="姓名">
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">邮箱:</label>
+            <div class="formControls col-xs-4 col-sm-6">
+                <input type="text" class="input-text" id="email" name="email" value="{$data.email}"
+                    autocomplete='email'>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red">*</span>角色:</label>
+            <div class="formControls col-xs-4 col-sm-4">
+                <span class="select-box" style="width:150px;">
+                    <select class="select" id="roleid" name="roleid">
+                        {foreach $dataRole as $k=>$op}
+                        <option value="{$k}" {eq name="data.roleid" value="$k" }selected{/eq}>{$op}</option>
+                        {/foreach}
+                    </select>
+                </span>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                头像:</label>
+                <div class="formControls col-xs-6 col-sm-4">
+                    <div style="width: 200px;height: 200px;">
+                     <a href="javascript:void(0);" onclick="uploadPicture()" >
+                        <img id="view-img" src="{$data.avatar ? $data.avatar : '/static/images/upload_picture.png'}" alt="头像" title="{$data.title_pic ? '更换' : '添加'}头像" style="max-width: 200px;max-height: 200px;">
+                     </a>
+                    </div>
+                    <input type="text" class="input-text" value="{$data.avatar}" name="avatar" id="picture" style="display: none;">
+                </div>
+                <label class="form-label col-xs-2 col-sm-2">
+                    <a class="btn btn-success radius" href="javascript:uploadPicture();">{$data.avatar ? '更换' : '添加'}头像</a></label>
+            <div class="col-3"> </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">
+                <span class="c-red"></span>状态:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <div class="radio-box">
+                    <input type="radio" name="status" id="status-1" value="1" {$data==null || $data.status==1 ? 'checked' : ""}>
+                    <label for="typeId-1">启用</label>
+                </div>
+                <div class="radio-box">
+                    <input type="radio" name="status" id="status-2" value="2" {$data.status==2 ? 'checked' : ""}>
+                    <label for="status-2">禁用</label>
+                </div>
+            </div>
+        </div>
+        <div class="row cl">
+            <label class="form-label col-xs-4 col-sm-2">备注:</label>
+            <div class="formControls col-xs-8 col-sm-6">
+                <textarea id="remark" name="remark" cols="" rows="" class="textarea" placeholder="备注...200个字符以内"
+                    dragonfly="true" onKeyUp="textarealength(this,200)">{$data.remark}</textarea>
+                <p class="textarea-numberbar">
+                    <em class="textarea-length">0</em>/200
+                </p>
+            </div>
+        </div>
+        <div class="row cl">
+            <div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-3">
+                <input class="btn btn-success radius" type="button" id="form-save-button"
+                    value="&nbsp;&nbsp;提交&nbsp;&nbsp;">
+                <input class="btn btn-default radius" type="button" value="&nbsp;&nbsp;取消&nbsp;&nbsp;"
+                    onClick="layer_close();">
+            </div>
+        </div>
+    </form>
+</article>
+
+<!--请在下方写此页面业务相关的脚本-->
+<script type="text/javascript">
+    // 图片上传
+    function uploadPicture() {
+        layer.open({
+            type: 2,
+            area: ['700px', '500px'],
+            fix: false, //不固定
+            maxmin: true,
+            shade: 0.4,
+            title: '添加缩略图',
+            content: '{:url("file_manager/uploadimg", ["_layer"=>true])}'
+        });
+    }
+    $(function () {
+        // 密码正则验证复杂度
+        function checkPassword(password) {
+            var res = pwdFormat6Mix(password);
+            if (!res) {
+                layer.msg('* 密码6位及以上, 至少包括字母和数字', {
+                    icon: 5,
+                    time: 1000
+                });
+                return false;
+            } else {
+                return true;
+            }
+        }
+
+        // 确认密码
+        function confirmPassword(password, repassword) {
+            if (password != repassword) {
+                layer.msg('两次密码不一致', {
+                    icon: 5,
+                    time: 1000
+                });
+                return false;
+            } else {
+                return true;
+            }
+        }
+
+        $("#password").blur(function () {
+            if (!isNull($("#password").val())) {
+                return checkPassword($("#password").val());
+            }
+        })
+
+        $("#repassword").blur(function () {
+            return confirmPassword($("#password").val(), $("#repassword").val());
+        })
+
+        $("#form-save-button").click(function () {
+            if (!$("#username").val() || !$("#roleid").val) {
+                layer.msg('用户名 or 角色 不能为空!', {
+                    icon: 5,
+                    time: 1000
+                });
+                return false;
+            }
+            
+            var password = $("#password").val();
+            var repassword = $("#repassword").val();
+            if (!isNull(password) && !checkPassword(password)) {
+                return false;
+            }
+            if (!confirmPassword(password, repassword)) {
+                return false;
+            }
+
+            var email = $("#email").val();
+            if (!isNull(email) && testEmail(email) == false) {
+                layer.msg('邮箱格式不正确', {
+                    icon: 5,
+                    time: 1000
+                });
+                return false;
+            }
+
+            if (getblen($("#remark").val()) > 200) {
+                layer.msg('备注过长', {
+                    icon: 5,
+                    time: 1000
+                });
+                return false;
+            }
+
+            var data = $("#form-save").serializeArray();
+            $.ajax({
+                type: 'POST',
+                url: '{:url("save")}',
+                data: data,
+                dataType: 'json',
+                success: function (res) {
+                    // console.log(res);
+                    if (res.code = 0) {
+                        layer.msg(data.msg, {
+                            icon: 5,
+                            time: 1000
+                        });
+                        return false;
+                    } else {
+                        layer.msg(res.msg, { icon: 1 }, function () {
+                            parent.location.reload(); // 父页面刷新
+                            var index = parent.layer.getFrameIndex(window.name); //获取窗口索引
+                            parent.layer.close(index);
+                        });
+                    }
+                }
+            })
+        })
+    })
+</script>
+<!--请在上方写此页面业务相关的脚本-->

+ 50 - 0
composer.json

@@ -0,0 +1,50 @@
+{
+    "name": "topthink/think",
+    "description": "the new thinkphp framework",
+    "type": "project",
+    "keywords": [
+        "framework",
+        "thinkphp",
+        "ORM"
+    ],
+    "homepage": "http://thinkphp.cn/",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "liu21st",
+            "email": "liu21st@gmail.com"
+        },
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }        
+    ],
+    "require": {
+        "php": ">=7.1.0",
+        "topthink/framework": "^6.0.0",
+        "topthink/think-orm": "^2.0",
+        "topthink/think-multi-app": "^1.0",
+        "liliuwei/thinkphp-jump": "^1.5"
+    },
+    "require-dev": {
+        "symfony/var-dumper": "^4.2",
+        "topthink/think-trace":"^1.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "app\\": "app"
+        },
+        "psr-0": {
+            "": "extend/"
+        }
+    },
+    "config": {
+        "preferred-install": "dist"
+    },
+    "scripts": {
+        "post-autoload-dump": [
+            "@php think service:discover",
+            "@php think vendor:publish"
+        ]
+    }
+}

+ 32 - 0
config/app.php

@@ -0,0 +1,32 @@
+<?php
+// +----------------------------------------------------------------------
+// | 应用设置
+// +----------------------------------------------------------------------
+
+return [
+    // 应用地址
+    'app_host'         => env('app.host', ''),
+    // 应用的命名空间
+    'app_namespace'    => '',
+    // 是否启用路由
+    'with_route'       => true,
+    // 默认应用
+    'default_app'      => 'index',
+    // 默认时区
+    'default_timezone' => 'Asia/Shanghai',
+
+    // 应用映射(自动多应用模式有效)
+    'app_map'          => [],
+    // 域名绑定(自动多应用模式有效)
+    'domain_bind'      => [],
+    // 禁止URL访问的应用列表(自动多应用模式有效)
+    'deny_app_list'    => [],
+
+    // 异常页面的模板文件
+    'exception_tmpl'   => app()->getThinkPath() . 'tpl/think_exception.tpl',
+
+    // 错误显示信息,非调试模式有效
+    'error_message'    => '页面错误!请稍后再试~',
+    // 显示错误信息
+    'show_error_msg'   => false,
+];

+ 29 - 0
config/cache.php

@@ -0,0 +1,29 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | 缓存设置
+// +----------------------------------------------------------------------
+
+return [
+    // 默认缓存驱动
+    'default' => env('cache.driver', 'file'),
+
+    // 缓存连接方式配置
+    'stores'  => [
+        'file' => [
+            // 驱动方式
+            'type'       => 'File',
+            // 缓存保存目录
+            'path'       => '',
+            // 缓存前缀
+            'prefix'     => '',
+            // 缓存有效期 0表示永久缓存
+            'expire'     => 0,
+            // 缓存标签前缀
+            'tag_prefix' => 'tag:',
+            // 序列化机制 例如 ['serialize', 'unserialize']
+            'serialize'  => [],
+        ],
+        // 更多的缓存连接
+    ],
+];

+ 9 - 0
config/console.php

@@ -0,0 +1,9 @@
+<?php
+// +----------------------------------------------------------------------
+// | 控制台配置
+// +----------------------------------------------------------------------
+return [
+    // 指令定义
+    'commands' => [
+    ],
+];

+ 20 - 0
config/cookie.php

@@ -0,0 +1,20 @@
+<?php
+// +----------------------------------------------------------------------
+// | Cookie设置
+// +----------------------------------------------------------------------
+return [
+    // cookie 保存时间
+    'expire'    => 0,
+    // cookie 保存路径
+    'path'      => '/',
+    // cookie 有效域名
+    'domain'    => '',
+    //  cookie 启用安全传输
+    'secure'    => false,
+    // httponly设置
+    'httponly'  => false,
+    // 是否使用 setcookie
+    'setcookie' => true,
+    // samesite 设置,支持 'strict' 'lax'
+    'samesite'  => '',
+];

+ 60 - 0
config/database.php

@@ -0,0 +1,60 @@
+<?php
+
+return [
+    // 默认使用的数据库连接配置
+    'default'         => env('database.driver', 'mysql'),
+
+    // 自定义时间查询规则
+    'time_query_rule' => [],
+
+    // 自动写入时间戳字段
+    // true为自动识别类型 false关闭
+    // 字符串则明确指定时间字段类型 支持 int timestamp datetime date
+    'auto_timestamp'  => true,
+
+    // 时间字段取出后的默认时间格式
+    'datetime_format' => 'Y-m-d H:i:s',
+
+    // 数据库连接配置信息
+    'connections'     => [
+        'mysql' => [
+            // 数据库类型
+            'type'            => env('database.type', 'mysql'),
+            // 服务器地址
+            'hostname'        => env('database.hostname', '127.0.0.1'),
+            // 数据库名
+            'database'        => env('database.database', ''),
+            // 用户名
+            'username'        => env('database.username', 'root'),
+            // 密码
+            'password'        => env('database.password', ''),
+            // 端口
+            'hostport'        => env('database.hostport', '3306'),
+            // 数据库连接参数
+            'params'          => [],
+            // 数据库编码默认采用utf8
+            'charset'         => env('database.charset', 'utf8'),
+            // 数据库表前缀
+            'prefix'          => env('database.prefix', ''),
+
+            // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
+            'deploy'          => 0,
+            // 数据库读写是否分离 主从式有效
+            'rw_separate'     => false,
+            // 读写分离后 主服务器数量
+            'master_num'      => 1,
+            // 指定从服务器序号
+            'slave_no'        => '',
+            // 是否严格检查字段是否存在
+            'fields_strict'   => true,
+            // 是否需要断线重连
+            'break_reconnect' => false,
+            // 监听SQL
+            'trigger_sql'     => env('app_debug', true),
+            // 开启字段缓存
+            'fields_cache'    => false,
+        ],
+
+        // 更多的数据库配置信息
+    ],
+];

+ 24 - 0
config/filesystem.php

@@ -0,0 +1,24 @@
+<?php
+
+return [
+    // 默认磁盘
+    'default' => env('filesystem.driver', 'local'),
+    // 磁盘列表
+    'disks'   => [
+        'local'  => [
+            'type' => 'local',
+            'root' => app()->getRuntimePath() . 'storage',
+        ],
+        'public' => [
+            // 磁盘类型
+            'type'       => 'local',
+            // 磁盘路径
+            'root'       => app()->getRootPath() . 'public/storage',
+            // 磁盘路径对应的外部URL路径
+            'url'        => '/storage',
+            // 可见性
+            'visibility' => 'public',
+        ],
+        // 更多的磁盘配置信息
+    ],
+];

+ 12 - 0
config/jump.php

@@ -0,0 +1,12 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: liliuwei
+ * Date: 2019/5/23
+ * Time: 22:50
+ */
+return[
+    // 默认跳转页面对应的模板文件
+    'dispatch_success_tmpl' => app()->getRootPath().'/vendor/liliuwei/thinkphp-jump/src/tpl/dispatch_jump.tpl',
+    'dispatch_error_tmpl'   => app()->getRootPath().'/vendor/liliuwei/thinkphp-jump/src/tpl/dispatch_jump.tpl',
+];

+ 27 - 0
config/lang.php

@@ -0,0 +1,27 @@
+<?php
+// +----------------------------------------------------------------------
+// | 多语言设置
+// +----------------------------------------------------------------------
+
+return [
+    // 默认语言
+    'default_lang'    => env('lang.default_lang', 'zh-cn'),
+    // 允许的语言列表
+    'allow_lang_list' => [],
+    // 多语言自动侦测变量名
+    'detect_var'      => 'lang',
+    // 是否使用Cookie记录
+    'use_cookie'      => true,
+    // 多语言cookie变量
+    'cookie_var'      => 'think_lang',
+    // 多语言header变量
+    'header_var'      => 'think-lang',
+    // 扩展语言包
+    'extend_list'     => [],
+    // Accept-Language转义为对应语言包名称
+    'accept_language' => [
+        'zh-hans-cn' => 'zh-cn',
+    ],
+    // 是否支持语言分组
+    'allow_group'     => false,
+];

+ 45 - 0
config/log.php

@@ -0,0 +1,45 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | 日志设置
+// +----------------------------------------------------------------------
+return [
+    // 默认日志记录通道
+    'default'      => env('log.channel', 'file'),
+    // 日志记录级别
+    'level'        => [],
+    // 日志类型记录的通道 ['error'=>'email',...]
+    'type_channel' => [],
+    // 关闭全局日志写入
+    'close'        => false,
+    // 全局日志处理 支持闭包
+    'processor'    => null,
+
+    // 日志通道列表
+    'channels'     => [
+        'file' => [
+            // 日志记录方式
+            'type'           => 'File',
+            // 日志保存目录
+            'path'           => '',
+            // 单文件日志写入
+            'single'         => false,
+            // 独立日志级别
+            'apart_level'    => [],
+            // 最大日志文件数量
+            'max_files'      => 0,
+            // 使用JSON格式记录
+            'json'           => false,
+            // 日志处理
+            'processor'      => null,
+            // 关闭通道日志写入
+            'close'          => false,
+            // 日志输出格式化
+            'format'         => '[%s][%s] %s',
+            // 是否实时写入
+            'realtime_write' => false,
+        ],
+        // 其它日志通道配置
+    ],
+
+];

+ 8 - 0
config/middleware.php

@@ -0,0 +1,8 @@
+<?php
+// 中间件配置
+return [
+    // 别名或分组
+    'alias'    => [],
+    // 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
+    'priority' => [],
+];

+ 45 - 0
config/route.php

@@ -0,0 +1,45 @@
+<?php
+// +----------------------------------------------------------------------
+// | 路由设置
+// +----------------------------------------------------------------------
+
+return [
+    // pathinfo分隔符
+    'pathinfo_depr'         => '/',
+    // URL伪静态后缀
+    'url_html_suffix'       => 'html',
+    // URL普通方式参数 用于自动生成
+    'url_common_param'      => true,
+    // 是否开启路由延迟解析
+    'url_lazy_route'        => false,
+    // 是否强制使用路由
+    'url_route_must'        => false,
+    // 合并路由规则
+    'route_rule_merge'      => false,
+    // 路由是否完全匹配
+    'route_complete_match'  => false,
+    // 访问控制器层名称
+    'controller_layer'      => 'controller',
+    // 空控制器名
+    'empty_controller'      => 'Error',
+    // 是否使用控制器后缀
+    'controller_suffix'     => false,
+    // 默认的路由变量规则
+    'default_route_pattern' => '[\w\.]+',
+    // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
+    'request_cache_key'     => false,
+    // 请求缓存有效期
+    'request_cache_expire'  => null,
+    // 全局请求缓存排除规则
+    'request_cache_except'  => [],
+    // 默认控制器名
+    'default_controller'    => 'Index',
+    // 默认操作名
+    'default_action'        => 'index',
+    // 操作方法后缀
+    'action_suffix'         => '',
+    // 默认JSONP格式返回的处理方法
+    'default_jsonp_handler' => 'jsonpReturn',
+    // 默认JSONP处理方法
+    'var_jsonp_handler'     => 'callback',
+];

+ 19 - 0
config/session.php

@@ -0,0 +1,19 @@
+<?php
+// +----------------------------------------------------------------------
+// | 会话设置
+// +----------------------------------------------------------------------
+
+return [
+    // session name
+    'name'           => 'PHPSESSID',
+    // SESSION_ID的提交变量,解决flash上传跨域
+    'var_session_id' => '',
+    // 驱动方式 支持file cache
+    'type'           => 'file',
+    // 存储连接标识 当type使用cache的时候有效
+    'store'          => null,
+    // 过期时间
+    'expire'         => 1440,
+    // 前缀
+    'prefix'         => '',
+];

+ 10 - 0
config/trace.php

@@ -0,0 +1,10 @@
+<?php
+// +----------------------------------------------------------------------
+// | Trace设置 开启调试模式后有效
+// +----------------------------------------------------------------------
+return [
+    // 内置Html和Console两种方式 支持扩展
+    'type'    => 'Html',
+    // 读取的日志通道名
+    'channel' => '',
+];

+ 25 - 0
config/view.php

@@ -0,0 +1,25 @@
+<?php
+// +----------------------------------------------------------------------
+// | 模板设置
+// +----------------------------------------------------------------------
+
+return [
+    // 模板引擎类型使用Think
+    'type'          => 'Think',
+    // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
+    'auto_rule'     => 1,
+    // 模板目录名
+    'view_dir_name' => 'view',
+    // 模板后缀
+    'view_suffix'   => 'html',
+    // 模板文件名分隔符
+    'view_depr'     => DIRECTORY_SEPARATOR,
+    // 模板引擎普通标签开始标记
+    'tpl_begin'     => '{',
+    // 模板引擎普通标签结束标记
+    'tpl_end'       => '}',
+    // 标签库标签开始标记
+    'taglib_begin'  => '{',
+    // 标签库标签结束标记
+    'taglib_end'    => '}',
+];

+ 2 - 0
extend/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 8 - 0
public/.htaccess

@@ -0,0 +1,8 @@
+<IfModule mod_rewrite.c>
+  Options +FollowSymlinks -Multiviews
+  RewriteEngine On
+
+  RewriteCond %{REQUEST_FILENAME} !-d
+  RewriteCond %{REQUEST_FILENAME} !-f
+  RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
+</IfModule>

二进制
public/favicon.ico


+ 24 - 0
public/index.php

@@ -0,0 +1,24 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// [ 应用入口文件 ]
+namespace think;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+// 执行HTTP应用并响应
+$http = (new App())->http;
+
+$response = $http->run();
+
+$response->send();
+
+$http->end($response);

+ 2 - 0
public/robots.txt

@@ -0,0 +1,2 @@
+User-agent: *
+Disallow:

+ 19 - 0
public/router.php

@@ -0,0 +1,19 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+// $Id$
+
+if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
+    return false;
+} else {
+    $_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php';
+
+    require __DIR__ . "/index.php";
+}

+ 2 - 0
public/static/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 2 - 0
public/storage/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

部分文件因为文件数量过多而无法显示