ソースを参照

新增系统配置关联字段、城市专区类型
新增自定义数组类型标题
新增多选项卡配置
新增自定义皮肤配置

Karson 4 年 前
コミット
3d9cde96a1

+ 18 - 17
application/admin/command/Install/fastadmin.sql

@@ -321,6 +321,7 @@ CREATE TABLE `fa_config` (
   `content` text NOT NULL COMMENT '变量字典数据',
   `rule` varchar(100) NOT NULL DEFAULT '' COMMENT '验证规则',
   `extend` varchar(255) NOT NULL DEFAULT '' COMMENT '扩展属性',
+  `setting` varchar(255) NOT NULL DEFAULT '' COMMENT '配置',
   PRIMARY KEY (`id`),
   UNIQUE KEY `name` (`name`)
 ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='系统配置';
@@ -329,23 +330,23 @@ CREATE TABLE `fa_config` (
 -- Records of fa_config
 -- ----------------------------
 BEGIN;
-INSERT INTO `fa_config` VALUES (1, 'name', 'basic', 'Site name', '请填写站点名称', 'string', '我的网站', '', 'required', '');
-INSERT INTO `fa_config` VALUES (2, 'beian', 'basic', 'Beian', '粤ICP备15000000号-1', 'string', '', '', '', '');
-INSERT INTO `fa_config` VALUES (3, 'cdnurl', 'basic', 'Cdn url', '如果静态资源使用第三方云储存请配置该值', 'string', '', '', '', '');
-INSERT INTO `fa_config` VALUES (4, 'version', 'basic', 'Version', '如果静态资源有变动请重新配置该值', 'string', '1.0.1', '', 'required', '');
-INSERT INTO `fa_config` VALUES (5, 'timezone', 'basic', 'Timezone', '', 'string', 'Asia/Shanghai', '', 'required', '');
-INSERT INTO `fa_config` VALUES (6, 'forbiddenip', 'basic', 'Forbidden ip', '一行一条记录', 'text', '', '', '', '');
-INSERT INTO `fa_config` VALUES (7, 'languages', 'basic', 'Languages', '', 'array', '{\"backend\":\"zh-cn\",\"frontend\":\"zh-cn\"}', '', 'required', '');
-INSERT INTO `fa_config` VALUES (8, 'fixedpage', 'basic', 'Fixed page', '请尽量输入左侧菜单栏存在的链接', 'string', 'dashboard', '', 'required', '');
-INSERT INTO `fa_config` VALUES (9, 'categorytype', 'dictionary', 'Category type', '', 'array', '{\"default\":\"Default\",\"page\":\"Page\",\"article\":\"Article\",\"test\":\"Test\"}', '', '', '');
-INSERT INTO `fa_config` VALUES (10, 'configgroup', 'dictionary', 'Config group', '', 'array', '{\"basic\":\"Basic\",\"email\":\"Email\",\"dictionary\":\"Dictionary\",\"user\":\"User\",\"example\":\"Example\"}', '', '', '');
-INSERT INTO `fa_config` VALUES (11, 'mail_type', 'email', 'Mail type', '选择邮件发送方式', 'select', '1', '[\"Please select\",\"SMTP\",\"Mail\"]', '', '');
-INSERT INTO `fa_config` VALUES (12, 'mail_smtp_host', 'email', 'Mail smtp host', '错误的配置发送邮件会导致服务器超时', 'string', 'smtp.qq.com', '', '', '');
-INSERT INTO `fa_config` VALUES (13, 'mail_smtp_port', 'email', 'Mail smtp port', '(不加密默认25,SSL默认465,TLS默认587)', 'string', '465', '', '', '');
-INSERT INTO `fa_config` VALUES (14, 'mail_smtp_user', 'email', 'Mail smtp user', '(填写完整用户名)', 'string', '10000', '', '', '');
-INSERT INTO `fa_config` VALUES (15, 'mail_smtp_pass', 'email', 'Mail smtp password', '(填写您的密码)', 'string', 'password', '', '', '');
-INSERT INTO `fa_config` VALUES (16, 'mail_verify_type', 'email', 'Mail vertify type', '(SMTP验证方式[推荐SSL])', 'select', '2', '[\"None\",\"TLS\",\"SSL\"]', '', '');
-INSERT INTO `fa_config` VALUES (17, 'mail_from', 'email', 'Mail from', '', 'string', '10000@qq.com', '', '', '');
+INSERT INTO `fa_config` VALUES (1, 'name', 'basic', 'Site name', '请填写站点名称', 'string', '我的网站', '', 'required', '', '');
+INSERT INTO `fa_config` VALUES (2, 'beian', 'basic', 'Beian', '粤ICP备15000000号-1', 'string', '', '', '', '', '');
+INSERT INTO `fa_config` VALUES (3, 'cdnurl', 'basic', 'Cdn url', '如果全站静态资源使用第三方云储存请配置该值', 'string', '', '', '', '', '');
+INSERT INTO `fa_config` VALUES (4, 'version', 'basic', 'Version', '如果静态资源有变动请重新配置该值', 'string', '1.0.1', '', 'required', '', '');
+INSERT INTO `fa_config` VALUES (5, 'timezone', 'basic', 'Timezone', '', 'string', 'Asia/Shanghai', '', 'required', '', '');
+INSERT INTO `fa_config` VALUES (6, 'forbiddenip', 'basic', 'Forbidden ip', '一行一条记录', 'text', '', '', '', '', '');
+INSERT INTO `fa_config` VALUES (7, 'languages', 'basic', 'Languages', '', 'array', '{\"backend\":\"zh-cn\",\"frontend\":\"zh-cn\"}', '', 'required', '', '');
+INSERT INTO `fa_config` VALUES (8, 'fixedpage', 'basic', 'Fixed page', '请尽量输入左侧菜单栏存在的链接', 'string', 'dashboard', '', 'required', '', '');
+INSERT INTO `fa_config` VALUES (9, 'categorytype', 'dictionary', 'Category type', '', 'array', '{\"default\":\"Default\",\"page\":\"Page\",\"article\":\"Article\",\"test\":\"Test\"}', '', '', '', '');
+INSERT INTO `fa_config` VALUES (10, 'configgroup', 'dictionary', 'Config group', '', 'array', '{\"basic\":\"Basic\",\"email\":\"Email\",\"dictionary\":\"Dictionary\",\"user\":\"User\",\"example\":\"Example\"}', '', '', '', '');
+INSERT INTO `fa_config` VALUES (11, 'mail_type', 'email', 'Mail type', '选择邮件发送方式', 'select', '1', '[\"Please select\",\"SMTP\",\"Mail\"]', '', '', '');
+INSERT INTO `fa_config` VALUES (12, 'mail_smtp_host', 'email', 'Mail smtp host', '错误的配置发送邮件会导致服务器超时', 'string', 'smtp.qq.com', '', '', '', '');
+INSERT INTO `fa_config` VALUES (13, 'mail_smtp_port', 'email', 'Mail smtp port', '(不加密默认25,SSL默认465,TLS默认587)', 'string', '465', '', '', '', '');
+INSERT INTO `fa_config` VALUES (14, 'mail_smtp_user', 'email', 'Mail smtp user', '(填写完整用户名)', 'string', '10000', '', '', '', '');
+INSERT INTO `fa_config` VALUES (15, 'mail_smtp_pass', 'email', 'Mail smtp password', '(填写您的密码)', 'string', 'password', '', '', '', '');
+INSERT INTO `fa_config` VALUES (16, 'mail_verify_type', 'email', 'Mail vertify type', '(SMTP验证方式[推荐SSL])', 'select', '2', '[\"None\",\"TLS\",\"SSL\"]', '', '', '');
+INSERT INTO `fa_config` VALUES (17, 'mail_from', 'email', 'Mail from', '', 'string', '10000@qq.com', '', '', '', '');
 COMMIT;
 
 -- ----------------------------

+ 52 - 6
application/admin/controller/general/Config.php

@@ -5,6 +5,8 @@ namespace app\admin\controller\general;
 use app\common\controller\Backend;
 use app\common\library\Email;
 use app\common\model\Config as ConfigModel;
+use think\Cache;
+use think\Db;
 use think\Exception;
 use think\Validate;
 
@@ -21,7 +23,7 @@ class Config extends Backend
      * @var \app\common\model\Config
      */
     protected $model = null;
-    protected $noNeedRight = ['check', 'rulelist'];
+    protected $noNeedRight = ['check', 'rulelist', 'selectpage', 'get_fields_list'];
 
     public function _initialize()
     {
@@ -82,7 +84,7 @@ class Config extends Backend
             $params = $this->request->post("row/a", [], 'trim');
             if ($params) {
                 foreach ($params as $k => &$v) {
-                    $v = is_array($v) ? implode(',', $v) : $v;
+                    $v = is_array($v) && $k !== 'setting' ? implode(',', $v) : $v;
                 }
                 if (in_array($params['type'], ['select', 'selects', 'checkbox', 'radio', 'array'])) {
                     $params['content'] = json_encode(ConfigModel::decode($params['content']), JSON_UNESCAPED_UNICODE);
@@ -202,12 +204,12 @@ class Config extends Backend
         if ($params) {
             $config = $this->model->get($params);
             if (!$config) {
-                return $this->success();
+                $this->success();
             } else {
-                return $this->error(__('Name already exist'));
+                $this->error(__('Name already exist'));
             }
         } else {
-            return $this->error(__('Invalid parameters'));
+            $this->error(__('Invalid parameters'));
         }
     }
 
@@ -262,7 +264,51 @@ class Config extends Backend
                 $this->error($email->getError());
             }
         } else {
-            return $this->error(__('Invalid parameters'));
+            $this->error(__('Invalid parameters'));
         }
     }
+
+    public function selectpage()
+    {
+        $id = $this->request->get("id/d");
+        $config = \app\common\model\Config::get($id);
+        if (!$config) {
+            $this->error(__('Invalid parameters'));
+        }
+        $setting = $config['setting'];
+        //自定义条件
+        $custom = isset($setting['conditions']) ? (array)json_decode($setting['conditions'], true) : [];
+        $custom = array_filter($custom);
+
+        $this->request->request(['showField' => $setting['field'], 'keyField' => $setting['primarykey'], 'custom' => $custom, 'searchField' => [$setting['field'], $setting['primarykey']]]);
+        $this->model = \think\Db::connect()->setTable($setting['table']);
+        return parent::selectpage();
+    }
+
+    /**
+     * 获取表列表
+     * @internal
+     */
+    public function get_table_list()
+    {
+        $tableList = [];
+        $dbname = \think\Config::get('database.database');
+        $tableList = \think\Db::query("SELECT `TABLE_NAME` AS `name`,`TABLE_COMMENT` AS `title` FROM `information_schema`.`TABLES` where `TABLE_SCHEMA` = '{$dbname}';");
+        $this->success('', null, ['tableList' => $tableList]);
+    }
+
+    /**
+     * 获取表字段列表
+     * @internal
+     */
+    public function get_fields_list()
+    {
+        $table = $this->request->request('table');
+        $dbname = \think\Config::get('database.database');
+        //从数据库中获取表字段信息
+        $sql = "SELECT `COLUMN_NAME` AS `name`,`COLUMN_COMMENT` AS `title`,`DATA_TYPE` AS `type` FROM `information_schema`.`columns` WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION";
+        //加载主表的列
+        $fieldList = Db::query($sql, [$dbname, $table]);
+        $this->success("", null, ['fieldList' => $fieldList]);
+    }
 }

+ 10 - 0
application/admin/lang/zh-cn/general/config.php

@@ -33,7 +33,17 @@ return [
     'Array'                       => '数组',
     'Array key'                   => '键名',
     'Array value'                 => '键值',
+    'City'                        => '城市地区',
+    'Selectpage'                  => '关联表',
+    'Selectpages'                 => '关联表(多选)',
     'Custom'                      => '自定义',
+    'Please select table'         => '关联表',
+    'Selectpage table'            => '关联表',
+    'Selectpage primarykey'       => '存储字段',
+    'Selectpage field'            => '显示字段',
+    'Selectpage conditions'       => '筛选条件',
+    'Field title'                 => '字段名',
+    'Field value'                 => '字段值',
     'Content'                     => '数据列表',
     'Rule'                        => '校验规则',
     'Site name'                   => '站点名称',

+ 4 - 0
application/admin/view/common/meta.html

@@ -8,6 +8,10 @@
 <!-- Loading Bootstrap -->
 <link href="__CDN__/assets/css/backend{$Think.config.app_debug?'':'.min'}.css?v={$Think.config.site.version}" rel="stylesheet">
 
+{if $Think.config.fastadmin.adminskin}
+<link href="__CDN__/assets/css/skins/{$Think.config.fastadmin.adminskin}.css?v={$Think.config.site.version}" rel="stylesheet">
+{/if}
+
 <!-- HTML5 shim, for IE6-8 support of HTML5 elements. All other JS at the end of file. -->
 <!--[if lt IE 9]>
   <script src="__CDN__/assets/js/html5shiv.js"></script>

+ 74 - 19
application/admin/view/general/config/index.html

@@ -59,35 +59,35 @@
                                         <div class="col-sm-8 col-xs-12">
                                             {switch $item.type}
                                             {case string}
-                                            <input {$item.extend} type="text" name="row[{$item.name}]" value="{$item.value|htmlentities}" class="form-control" data-rule="{$item.rule}" data-tip="{$item.tip}"/>
+                                            <input {$item.extend_html} type="text" name="row[{$item.name}]" value="{$item.value|htmlentities}" class="form-control" data-rule="{$item.rule}" data-tip="{$item.tip}"/>
                                             {/case}
                                             {case text}
-                                            <textarea {$item.extend} name="row[{$item.name}]" class="form-control" data-rule="{$item.rule}" rows="5" data-tip="{$item.tip}">{$item.value|htmlentities}</textarea>
+                                            <textarea {$item.extend_html} name="row[{$item.name}]" class="form-control" data-rule="{$item.rule}" rows="5" data-tip="{$item.tip}">{$item.value|htmlentities}</textarea>
                                             {/case}
                                             {case editor}
-                                            <textarea {$item.extend} name="row[{$item.name}]" id="editor-{$item.name}" class="form-control editor" data-rule="{$item.rule}" rows="5" data-tip="{$item.tip}">{$item.value|htmlentities}</textarea>
+                                            <textarea {$item.extend_html} name="row[{$item.name}]" id="editor-{$item.name}" class="form-control editor" data-rule="{$item.rule}" rows="5" data-tip="{$item.tip}">{$item.value|htmlentities}</textarea>
                                             {/case}
                                             {case array}
-                                            <dl class="fieldlist" data-name="row[{$item.name}]">
+                                            <dl {$item.extend_html} class="fieldlist" data-name="row[{$item.name}]">
                                                 <dd>
-                                                    <ins>{:__('Array key')}</ins>
-                                                    <ins>{:__('Array value')}</ins>
+                                                    <ins>{:isset($item["setting"]["key"])&&$item["setting"]["key"]?$item["setting"]["key"]:__('Array key')}</ins>
+                                                    <ins>{:isset($item["setting"]["value"])&&$item["setting"]["value"]?$item["setting"]["value"]:__('Array value')}</ins>
                                                 </dd>
                                                 <dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
                                                 <textarea name="row[{$item.name}]" class="form-control hide" cols="30" rows="5">{$item.value|htmlentities}</textarea>
                                             </dl>
                                             {/case}
                                             {case date}
-                                            <input {$item.extend} type="text" name="row[{$item.name}]" value="{$item.value}" class="form-control datetimepicker" data-date-format="YYYY-MM-DD" data-tip="{$item.tip}" data-rule="{$item.rule}"/>
+                                            <input {$item.extend_html} type="text" name="row[{$item.name}]" value="{$item.value}" class="form-control datetimepicker" data-date-format="YYYY-MM-DD" data-tip="{$item.tip}" data-rule="{$item.rule}"/>
                                             {/case}
                                             {case time}
-                                            <input {$item.extend} type="text" name="row[{$item.name}]" value="{$item.value}" class="form-control datetimepicker" data-date-format="HH:mm:ss" data-tip="{$item.tip}" data-rule="{$item.rule}"/>
+                                            <input {$item.extend_html} type="text" name="row[{$item.name}]" value="{$item.value}" class="form-control datetimepicker" data-date-format="HH:mm:ss" data-tip="{$item.tip}" data-rule="{$item.rule}"/>
                                             {/case}
                                             {case datetime}
-                                            <input {$item.extend} type="text" name="row[{$item.name}]" value="{$item.value}" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-tip="{$item.tip}" data-rule="{$item.rule}"/>
+                                            <input {$item.extend_html} type="text" name="row[{$item.name}]" value="{$item.value}" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-tip="{$item.tip}" data-rule="{$item.rule}"/>
                                             {/case}
                                             {case number}
-                                            <input {$item.extend} type="number" name="row[{$item.name}]" value="{$item.value}" class="form-control" data-tip="{$item.tip}" data-rule="{$item.rule}"/>
+                                            <input {$item.extend_html} type="number" name="row[{$item.name}]" value="{$item.value}" class="form-control" data-tip="{$item.tip}" data-rule="{$item.rule}"/>
                                             {/case}
                                             {case checkbox}
                                             {foreach name="item.content" item="vo"}
@@ -101,7 +101,7 @@
                                             {/case}
                                             {case value="select" break="0"}{/case}
                                             {case value="selects"}
-                                            <select {$item.extend} name="row[{$item.name}]{$item.type=='selects'?'[]':''}" class="form-control selectpicker" data-tip="{$item.tip}" {$item.type=='selects'?'multiple':''}>
+                                            <select {$item.extend_html} name="row[{$item.name}]{$item.type=='selects'?'[]':''}" class="form-control selectpicker" data-tip="{$item.tip}" {$item.type=='selects'?'multiple':''}>
                                                 {foreach name="item.content" item="vo"}
                                                 <option value="{$key}" {in name="key" value="$item.value" }selected{/in}>{$vo}</option>
                                                 {/foreach}
@@ -136,6 +136,15 @@
                                             <label for="row[{$item.name}]-yes"><input id="row[{$item.name}]-yes" name="row[{$item.name}]" type="radio" value="1" {$item.value?'checked':''} data-tip="{$item.tip}" /> {:__('Yes')}</label>
                                             <label for="row[{$item.name}]-no"><input id="row[{$item.name}]-no" name="row[{$item.name}]" type="radio" value="0" {$item.value?'':'checked'} data-tip="{$item.tip}" /> {:__('No')}</label>
                                             {/case}
+                                            {case city}
+                                            <div style="position:relative">
+                                            <input {$item.extend_html} type="text" name="row[{$item.name}]" id="c-{$item.name}" value="{$item.value|htmlentities}" class="form-control" data-toggle="city-picker" data-tip="{$item.tip}" data-rule="{$item.rule}" />
+                                            </div>
+                                            {/case}
+                                            {case value="selectpage" break="0"}{/case}
+                                            {case value="selectpages"}
+                                            <input {$item.extend_html} type="text" name="row[{$item.name}]" id="c-{$item.name}" value="{$item.value|htmlentities}" class="form-control selectpage" data-source="{:url('general/config/selectpage')}?id={$item.id}" data-primary-key="{$item.setting.primarykey}" data-field="{$item.setting.field}" data-multiple="{$item.type=='selectpage'?'false':'true'}" data-tip="{$item.tip}" data-rule="{$item.rule}" />
+                                            {/case}
                                             {case custom}
                                             {$item.extend_html}
                                             {/case}
@@ -170,21 +179,21 @@
                 <form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="{:url('general.config/add')}">
                     {:token()}
                     <div class="form-group">
-                        <label class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
+                        <label class="control-label col-xs-12 col-sm-2">{:__('Group')}:</label>
                         <div class="col-xs-12 col-sm-4">
-                            <select name="row[type]" class="form-control selectpicker">
-                                {foreach name="typeList" item="vo"}
-                                <option value="{$key}" {in name="key" value="string" }selected{/in}>{$vo}</option>
+                            <select name="row[group]" class="form-control selectpicker">
+                                {foreach name="groupList" item="vo"}
+                                <option value="{$key}" {in name="key" value="basic" }selected{/in}>{$vo}</option>
                                 {/foreach}
                             </select>
                         </div>
                     </div>
                     <div class="form-group">
-                        <label class="control-label col-xs-12 col-sm-2">{:__('Group')}:</label>
+                        <label class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
                         <div class="col-xs-12 col-sm-4">
-                            <select name="row[group]" class="form-control selectpicker">
-                                {foreach name="groupList" item="vo"}
-                                <option value="{$key}" {in name="key" value="basic" }selected{/in}>{$vo}</option>
+                            <select name="row[type]" id="c-type" class="form-control selectpicker">
+                                {foreach name="typeList" item="vo"}
+                                <option value="{$key}" {in name="key" value="string" }selected{/in}>{$vo}</option>
                                 {/foreach}
                             </select>
                         </div>
@@ -201,6 +210,52 @@
                             <input type="text" class="form-control" id="title" name="row[title]" value="" data-rule="required"/>
                         </div>
                     </div>
+                    <div class="form-group hidden tf tf-selectpage tf-selectpages">
+                        <label for="c-selectpage-table" class="control-label col-xs-12 col-sm-2">{:__('Selectpage table')}:</label>
+                        <div class="col-xs-12 col-sm-4">
+                            <select id="c-selectpage-table" name="row[setting][table]" class="form-control selectpicker" data-live-search="true">
+                                <option value="">{:__('Please select table')}</option>
+                            </select>
+                        </div>
+                    </div>
+                    <div class="form-group hidden tf tf-selectpage tf-selectpages">
+                        <label for="c-selectpage-primarykey" class="control-label col-xs-12 col-sm-2">{:__('Selectpage primarykey')}:</label>
+                        <div class="col-xs-12 col-sm-4">
+                            <select name="row[setting][primarykey]" class="form-control selectpicker" id="c-selectpage-primarykey"></select>
+                        </div>
+                    </div>
+                    <div class="form-group hidden tf tf-selectpage tf-selectpages">
+                        <label for="c-selectpage-field" class="control-label col-xs-12 col-sm-2">{:__('Selectpage field')}:</label>
+                        <div class="col-xs-12 col-sm-4">
+                            <select name="row[setting][field]" class="form-control selectpicker" id="c-selectpage-field"></select>
+                        </div>
+                    </div>
+                    <div class="form-group hidden tf tf-selectpage tf-selectpages">
+                        <label class="control-label col-xs-12 col-sm-2">{:__('Selectpage conditions')}:</label>
+                        <div class="col-xs-12 col-sm-8">
+                            <dl class="fieldlist" data-name="row[setting][conditions]">
+                                <dd>
+                                    <ins>{:__('Field title')}</ins>
+                                    <ins>{:__('Field value')}</ins>
+                                </dd>
+
+                                <dd><a href="javascript:;" class="append btn btn-sm btn-success"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
+                                <textarea name="row[setting][conditions]" class="form-control hide" cols="30" rows="5"></textarea>
+                            </dl>
+                        </div>
+                    </div>
+                    <div class="form-group hidden tf tf-array">
+                        <label for="c-array-key" class="control-label col-xs-12 col-sm-2">{:__('Array key')}:</label>
+                        <div class="col-xs-12 col-sm-4">
+                            <input type="text" name="row[setting][key]" class="form-control" id="c-array-key">
+                        </div>
+                    </div>
+                    <div class="form-group hidden tf tf-array">
+                        <label for="c-array-value" class="control-label col-xs-12 col-sm-2">{:__('Array value')}:</label>
+                        <div class="col-xs-12 col-sm-4">
+                            <input type="text" name="row[setting][value]" class="form-control" id="c-array-value">
+                        </div>
+                    </div>
                     <div class="form-group">
                         <label for="value" class="control-label col-xs-12 col-sm-2">{:__('Value')}:</label>
                         <div class="col-xs-12 col-sm-4">

+ 1 - 1
application/admin/view/index/index.html

@@ -4,7 +4,7 @@
         <!-- 加载样式及META信息 -->
         {include file="common/meta" /}
     </head>
-    <body class="hold-transition skin-green sidebar-mini fixed {if $Think.config.fastadmin.multiplenav}multiplenav{/if}" id="tabs">
+    <body class="hold-transition skin-green {$Think.config.fastadmin.adminskin} sidebar-mini fixed {:$Think.config.fastadmin.multipletab?'multipletab':''} {:$Think.config.fastadmin.multiplenav?'multiplenav':''}" id="tabs">
 
         <div class="wrapper">
 

+ 108 - 92
application/admin/view/index/login.html

@@ -1,105 +1,121 @@
 <!DOCTYPE html>
 <html lang="{$config.language}">
-    <head>
-        {include file="common/meta" /}
-
-        <style type="text/css">
-            body {
-                color:#999;
-                background:url('{$background}');
-                background-size:cover;
-            }
-            a {
-                color:#fff;
-            }
-            .login-panel{margin-top:150px;}
-            .login-screen {
-                max-width:400px;
-                padding:0;
-                margin:100px auto 0 auto;
+<head>
+    {include file="common/meta" /}
 
-            }
-            .login-screen .well {
-                border-radius: 3px;
-                -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
-                box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
-                background: rgba(255,255,255, 0.2);
-            }
-            .login-screen .copyright {
-                text-align: center;
-            }
-            @media(max-width:767px) {
-                .login-screen {
-                    padding:0 20px;
-                }
-            }
-            .profile-img-card {
-                width: 100px;
-                height: 100px;
-                margin: 10px auto;
-                display: block;
-                -moz-border-radius: 50%;
-                -webkit-border-radius: 50%;
-                border-radius: 50%;
-            }
-            .profile-name-card {
-                text-align: center;
-            }
+    <style type="text/css">
+        body {
+            color: #999;
+            background: url('{$background}');
+            background-size: cover;
+        }
 
-            #login-form {
-                margin-top:20px;
-            }
-            #login-form .input-group {
-                margin-bottom:15px;
+        a {
+            color: #fff;
+        }
+
+        .login-panel {
+            margin-top: 150px;
+        }
+
+        .login-screen {
+            max-width: 400px;
+            padding: 0;
+            margin: 100px auto 0 auto;
+
+        }
+
+        .login-screen .well {
+            border-radius: 3px;
+            -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+            box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
+            background: rgba(255, 255, 255, 0.2);
+            border: 1px solid rgba(227, 227, 227, 0.4);
+        }
+
+        .login-screen .input-group .input-group-addon, .login-screen input.form-control {
+            /*border-color: transparent;*/
+        }
+
+        .login-screen .copyright {
+            text-align: center;
+        }
+
+        @media (max-width: 767px) {
+            .login-screen {
+                padding: 0 20px;
             }
+        }
+
+        .profile-img-card {
+            width: 100px;
+            height: 100px;
+            margin: 10px auto;
+            display: block;
+            -moz-border-radius: 50%;
+            -webkit-border-radius: 50%;
+            border-radius: 50%;
+        }
+
+        .profile-name-card {
+            text-align: center;
+        }
+
+        #login-form {
+            margin-top: 20px;
+        }
 
-        </style>
-    </head>
-    <body>
-        <div class="container">
-            <div class="login-wrapper">
-                <div class="login-screen">
-                    <div class="well">
-                        <div class="login-form">
-                            <img id="profile-img" class="profile-img-card" src="__CDN__/assets/img/avatar.png" />
-                            <p id="profile-name" class="profile-name-card"></p>
-
-                            <form action="" method="post" id="login-form">
-                                <div id="errtips" class="hide"></div>
-                                {:token()}
-                                <div class="input-group">
-                                    <div class="input-group-addon"><span class="glyphicon glyphicon-user" aria-hidden="true"></span></div>
-                                    <input type="text" class="form-control" id="pd-form-username" placeholder="{:__('Username')}" name="username" autocomplete="off" value="" data-rule="{:__('Username')}:required;username" />
-                                </div>
-
-                                <div class="input-group">
-                                    <div class="input-group-addon"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></div>
-                                    <input type="password" class="form-control" id="pd-form-password" placeholder="{:__('Password')}" name="password" autocomplete="off" value="" data-rule="{:__('Password')}:required;password" />
-                                </div>
-                                {if $Think.config.fastadmin.login_captcha}
-                                <div class="input-group">
-                                    <div class="input-group-addon"><span class="glyphicon glyphicon-option-horizontal" aria-hidden="true"></span></div>
-                                    <input type="text" name="captcha" class="form-control" placeholder="{:__('Captcha')}" data-rule="{:__('Captcha')}:required;length(4)" />
-                                    <span class="input-group-addon" style="padding:0;border:none;cursor:pointer;">
+        #login-form .input-group {
+            margin-bottom: 15px;
+        }
+
+    </style>
+</head>
+<body>
+<div class="container">
+    <div class="login-wrapper">
+        <div class="login-screen">
+            <div class="well">
+                <div class="login-form">
+                    <img id="profile-img" class="profile-img-card" src="__CDN__/assets/img/avatar.png"/>
+                    <p id="profile-name" class="profile-name-card"></p>
+
+                    <form action="" method="post" id="login-form">
+                        <div id="errtips" class="hide"></div>
+                        {:token()}
+                        <div class="input-group">
+                            <div class="input-group-addon"><span class="glyphicon glyphicon-user" aria-hidden="true"></span></div>
+                            <input type="text" class="form-control" id="pd-form-username" placeholder="{:__('Username')}" name="username" autocomplete="off" value="" data-rule="{:__('Username')}:required;username"/>
+                        </div>
+
+                        <div class="input-group">
+                            <div class="input-group-addon"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></div>
+                            <input type="password" class="form-control" id="pd-form-password" placeholder="{:__('Password')}" name="password" autocomplete="off" value="" data-rule="{:__('Password')}:required;password"/>
+                        </div>
+                        {if $Think.config.fastadmin.login_captcha}
+                        <div class="input-group">
+                            <div class="input-group-addon"><span class="glyphicon glyphicon-option-horizontal" aria-hidden="true"></span></div>
+                            <input type="text" name="captcha" class="form-control" placeholder="{:__('Captcha')}" data-rule="{:__('Captcha')}:required;length(4)"/>
+                            <span class="input-group-addon" style="padding:0;border:none;cursor:pointer;">
                                         <img src="{:rtrim('__PUBLIC__', '/')}/index.php?s=/captcha" width="100" height="30" onclick="this.src = '{:rtrim('__PUBLIC__', '/')}/index.php?s=/captcha&r=' + Math.random();"/>
                                     </span>
-                                </div>
-                                {/if}
-                                <div class="form-group checkbox">
-                                    <label class="inline" for="keeplogin">
-                                        <input type="checkbox" name="keeplogin" id="keeplogin" value="1" />
-                                        {:__('Keep login')}
-                                    </label>
-                                </div>
-                                <div class="form-group">
-                                    <button type="submit" class="btn btn-success btn-lg btn-block">{:__('Sign in')}</button>
-                                </div>
-                            </form>
                         </div>
-                    </div>
+                        {/if}
+                        <div class="form-group checkbox">
+                            <label class="inline" for="keeplogin">
+                                <input type="checkbox" name="keeplogin" id="keeplogin" value="1"/>
+                                {:__('Keep login')}
+                            </label>
+                        </div>
+                        <div class="form-group">
+                            <button type="submit" class="btn btn-success btn-lg btn-block">{:__('Sign in')}</button>
+                        </div>
+                    </form>
                 </div>
             </div>
         </div>
-        {include file="common/script" /}
-    </body>
+    </div>
+</div>
+{include file="common/script" /}
+</body>
 </html>

+ 1 - 1
application/common/controller/Backend.php

@@ -149,7 +149,7 @@ class Backend extends Controller
             }
             // 判断是否需要验证权限
             if (!$this->auth->match($this->noNeedRight)) {
-                // 判断控制器和方法判断是否有对应权限
+                // 判断控制器和方法是否有对应权限
                 if (!$this->auth->check($path)) {
                     Hook::listen('admin_nopermission', $this);
                     $this->error(__('You have no permission'), '');

+ 3 - 0
application/common/model/Config.php

@@ -21,6 +21,9 @@ class Config extends Model
     protected $append = [
         'extend_html'
     ];
+    protected $type = [
+        'setting' => 'json',
+    ];
 
     /**
      * 读取配置类型

+ 4 - 2
application/config.php

@@ -273,10 +273,12 @@ return [
         'login_background'      => "/assets/img/loginbg.jpg",
         //是否启用多级菜单导航
         'multiplenav'           => false,
+        //是否开启多选项卡(仅在开启多级菜单时起作用)
+        'multipletab'           => true,
+        //后台皮肤,为空时表示使用skin-green
+        'adminskin'             => '',
         //允许跨域的域名,多个以,分隔
         'cors_request_domain'   => 'localhost,127.0.0.1',
-        //自动检测更新
-        'checkupdate'           => false,
         //版本号
         'version'               => '1.2.0',
         //API接口地址

+ 2 - 2
application/index/view/user/index.html

@@ -17,7 +17,7 @@
             {include file="common/sidenav" /}
         </div>
         <div class="col-md-9">
-            <div class="panel panel-default ">
+            <div class="panel panel-default">
                 <div class="panel-body">
                     <h2 class="page-header">
                         {:__('Member center')}
@@ -73,4 +73,4 @@
             </div>
         </div>
     </div>
-</div>
+</div>

+ 15 - 7
public/assets/css/backend.css

@@ -385,13 +385,6 @@ form.form-horizontal .control-label {
 .nav-addtabs li:hover > .close-tab {
   display: block;
 }
-.multiplenav .content-wrapper,
-.multiplenav .right-side {
-  padding-top: 94px;
-}
-.multiplenav #firstnav .nav-addtabs {
-  padding-right: 450px;
-}
 #firstnav {
   height: 50px;
   border-bottom: 1px solid transparent;
@@ -420,6 +413,7 @@ form.form-horizontal .control-label {
 }
 /*次栏菜单栏*/
 #secondnav {
+  display: none;
   height: 44px;
   position: absolute;
   top: 50px;
@@ -495,6 +489,20 @@ form.form-horizontal .control-label {
   border-color: #222e32;
   color: #222e32;
 }
+.multiplenav .content-wrapper,
+.multiplenav .right-side {
+  padding-top: 50px;
+}
+.multiplenav #firstnav .nav-addtabs {
+  padding-right: 450px;
+}
+.multipletab #secondnav {
+  display: block;
+}
+.multipletab.multiplenav .content-wrapper,
+.multipletab.multiplenav .right-side {
+  padding-top: 94px;
+}
 .main-sidebar .sidebar-form {
   overflow: visible;
 }

ファイルの差分が大きいため隠しています
+ 1 - 1
public/assets/css/backend.min.css


+ 17 - 12
public/assets/css/frontend.css

@@ -13,6 +13,7 @@ body {
 body {
   padding-top: 50px;
   font-size: 13px;
+  background: #f4f6f8;
 }
 .dropdown:hover .dropdown-menu {
   display: block;
@@ -100,7 +101,10 @@ form.form-horizontal .control-label {
 }
 .panel-default {
   padding: 0 15px;
-  border-color: #e4ecf3;
+  border: none;
+  -webkit-box-shadow: none;
+  -moz-box-shadow: none;
+  box-shadow: none;
 }
 .panel-default > .panel-heading {
   position: relative;
@@ -109,6 +113,14 @@ form.form-horizontal .control-label {
   background: #fff;
   border-bottom: 1px solid #f5f5f5;
 }
+.panel-default h2.page-header {
+  margin-top: 0;
+  height: 50px;
+  line-height: 31px;
+  font-size: 18px;
+  padding: 10px 0;
+  border-bottom: 1px solid #f5f5f5;
+}
 .panel-default > .panel-heading .panel-title {
   color: #313131;
 }
@@ -320,7 +332,6 @@ footer.footer .copyright a:hover {
   -moz-background-clip: padding;
   border-radius: 4px;
   background-clip: padding-box;
-  border: 1px solid #e4ecf3;
 }
 .login-section {
   margin: 50px auto;
@@ -346,7 +357,7 @@ footer.footer .copyright a:hover {
   font-size: 16px;
   text-align: center;
   color: #616161;
-  background-color: #f5f5f5;
+  background-color: #ececec;
   -webkit-transition: all 0.3s ease;
   -moz-transition: all 0.3s ease;
   -o-transition: all 0.3s ease;
@@ -400,13 +411,6 @@ main.content {
   padding: 20px 0 10px 0;
   margin-bottom: 20px;
   background-color: #fff;
-  -webkit-border-radius: 4px;
-  -webkit-background-clip: padding-box;
-  -moz-border-radius: 4px;
-  -moz-background-clip: padding;
-  border-radius: 4px;
-  background-clip: padding-box;
-  border: 1px solid #e4ecf3;
 }
 .sidenav .list-group:last-child {
   margin-bottom: 0;
@@ -439,7 +443,8 @@ main.content {
   background-clip: padding-box;
 }
 .sidenav .list-group .list-group-item:hover {
-  background-color: #f5f5f5;
+  border-left: 2px solid rgba(245, 245, 245, 0.38);
+  background-color: rgba(245, 245, 245, 0.38);
 }
 .sidenav .list-group .list-group-item > a {
   display: block;
@@ -448,7 +453,7 @@ main.content {
 }
 .sidenav .list-group .list-group-item.active {
   border-left: 2px solid #46c37b;
-  background-color: #f5f5f5;
+  background-color: rgba(245, 245, 245, 0.38);
 }
 .sidenav .list-group .list-group-item.active > a {
   color: #46c37b;

ファイルの差分が大きいため隠しています
+ 1 - 1
public/assets/css/frontend.min.css


BIN
public/assets/img/bg-middle.jpg


+ 1 - 5
public/assets/js/backend/dashboard.js

@@ -101,10 +101,6 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'table', 'echarts', 'echart
                 myChart.resize();
             });
 
-            $(document).on("click", ".btn-checkversion", function () {
-                top.window.$("[data-toggle=checkupdate]").trigger("click");
-            });
-
             $(document).on("click", ".btn-refresh", function () {
                 setTimeout(function () {
                     myChart.resize();
@@ -115,4 +111,4 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'table', 'echarts', 'echart
     };
 
     return Controller;
-});
+});

+ 47 - 0
public/assets/js/backend/general/config.js

@@ -27,6 +27,53 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                 }, 1500);
             });
 
+            //渲染关联显示字段和存储字段
+            var renderselect = function (id, data, defaultvalue) {
+                var html = [];
+                for (var i = 0; i < data.length; i++) {
+                    html.push("<option value='" + data[i].name + "' " + (defaultvalue == data[i].name ? "selected" : "") + " data-subtext='" + data[i].title + "'>" + data[i].name + "</option>");
+                }
+                var select = $(id);
+                $(select).html(html.join(""));
+                select.trigger("change");
+                if (select.data("selectpicker")) {
+                    select.selectpicker('refresh');
+                }
+            };
+            //关联表切换
+            $(document).on('change', "#c-selectpage-table", function (e, first) {
+                var that = this;
+                Fast.api.ajax({
+                    url: "general/config/get_fields_list",
+                    data: {table: $(that).val()},
+                }, function (data, ret) {
+                    renderselect("#c-selectpage-primarykey", data.fieldList, first ? $("#c-selectpage-primarykey").data("value") : '');
+                    renderselect("#c-selectpage-field", data.fieldList, first ? $("#c-selectpage-field").data("value") : '');
+                    return false;
+                });
+                return false;
+            });
+            //如果编辑模式则渲染已知数据
+            if (['selectpage', 'selectpages'].indexOf($("#c-type").val()) > -1) {
+                $("#c-selectpage-table").trigger("change", true);
+            }
+
+            //切换类型时
+            $(document).on("change", "#c-type", function () {
+                var value = $(this).val();
+                $(".tf").addClass("hidden");
+                $(".tf.tf-" + value).removeClass("hidden");
+                if (["selectpage", "selectpages"].indexOf(value) > -1 && $("#c-selectpage-table option").size() == 1) {
+                    //异步加载表列表
+                    Fast.api.ajax({
+                        url: "general/config/get_table_list",
+                    }, function (data, ret) {
+                        renderselect("#c-selectpage-table", data.tableList);
+                        return false;
+                    });
+                }
+            });
+
             //切换显示隐藏变量字典列表
             $(document).on("change", "form#add-form select[name='row[type]']", function (e) {
                 $("#add-content-container").toggleClass("hide", ['select', 'selects', 'checkbox', 'radio'].indexOf($(this).val()) > -1 ? false : true);

+ 1 - 1
public/assets/js/require-backend.min.js

@@ -11258,7 +11258,7 @@ define('table',['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstr
                     });
                 });
                 //当内容渲染完成后
-                table.on('post-body.bs.table', function (e, settings, json, xhr) {
+                table.on('post-body.bs.table', function (e, data) {
                     $(Table.config.refreshbtn, toolbar).find(".fa").removeClass("fa-spin");
                     if ($(Table.config.checkboxtd + ":first", table).find("input[type='checkbox'][data-index]").size() > 0) {
                         // 拖拽选择,需要重新绑定事件

+ 1 - 1
public/assets/js/require-table.js

@@ -198,7 +198,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                     });
                 });
                 //当内容渲染完成后
-                table.on('post-body.bs.table', function (e, settings, json, xhr) {
+                table.on('post-body.bs.table', function (e, data) {
                     $(Table.config.refreshbtn, toolbar).find(".fa").removeClass("fa-spin");
                     if ($(Table.config.checkboxtd + ":first", table).find("input[type='checkbox'][data-index]").size() > 0) {
                         // 拖拽选择,需要重新绑定事件

ファイルの差分が大きいため隠しています
+ 887 - 874
public/assets/less/backend.less


+ 15 - 7
public/assets/less/frontend.less

@@ -30,6 +30,7 @@ body {
 body {
     padding-top: 50px;
     font-size:13px;
+    background:#f4f6f8;
 }
 
 .dropdown:hover .dropdown-menu {
@@ -121,7 +122,8 @@ form.form-horizontal .control-label {
 
 .panel-default {
     padding: 0 15px;
-    border-color: #e4ecf3;
+    border: none;
+    .box-shadow(none);
     > .panel-heading {
         position: relative;
         font-size: 16px;
@@ -129,6 +131,14 @@ form.form-horizontal .control-label {
         background: #fff;
         border-bottom: 1px solid #f5f5f5;
     }
+    h2.page-header {
+        margin-top:0;
+        height:50px;
+        line-height:31px;
+        font-size:18px;
+        padding:10px 0;
+        border-bottom: 1px solid #f5f5f5;
+    }
     > .panel-heading {
         .panel-title {
             color: #313131;
@@ -308,7 +318,6 @@ footer.footer{
     padding: 15px;
     margin-bottom: 20px;
     .border-radius(4px);
-    border: 1px solid #e4ecf3;
 }
 .login-section {
     margin: 50px auto;
@@ -327,7 +336,7 @@ footer.footer{
             font-size: 16px;
             text-align: center;
             color: #616161;
-            background-color: #f5f5f5;
+            background-color: #ececec;
             .transition(all 0.3s ease);
             &:hover {
                 background-color: #fafafa;
@@ -377,8 +386,6 @@ main.content {
     padding: 20px 0 10px 0;
     margin-bottom: 20px;
     background-color: #fff;
-    .border-radius(4px);
-    border: 1px solid #e4ecf3;
     .list-group{
         &:last-child {
             margin-bottom: 0;
@@ -399,7 +406,8 @@ main.content {
                 .border-radius(0);
             }
             &:hover {
-                background-color: #f5f5f5;
+                border-left: 2px solid rgba(245, 245, 245, 0.38);
+                background-color: rgba(245, 245, 245, 0.38);
             }
             > a {
                 display: block;
@@ -408,7 +416,7 @@ main.content {
             }
             &.active {
                 border-left: 2px solid #46c37b;
-                background-color: #f5f5f5;
+                background-color: rgba(245, 245, 245, 0.38);
                 > a {
                     color: #46c37b;
                 }