diff --git a/application/admin/controller/Addon.php b/application/admin/controller/Addon.php
index c9f6ba7..691fa44 100644
--- a/application/admin/controller/Addon.php
+++ b/application/admin/controller/Addon.php
@@ -46,11 +46,11 @@ class Addon extends Backend
     /**
      * 配置
      */
-    public function config($ids = null)
+    public function config($name = null)
     {
-        $name = $this->request->get("name");
+        $name = $name ? $name : $this->request->get("name");
         if (!$name) {
-            $this->error(__('Parameter %s can not be empty', $ids ? 'id' : 'name'));
+            $this->error(__('Parameter %s can not be empty', 'name'));
         }
         if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) {
             $this->error(__('Addon name incorrect'));
diff --git a/application/admin/library/Auth.php b/application/admin/library/Auth.php
index ab123a3..73826e4 100644
--- a/application/admin/library/Auth.php
+++ b/application/admin/library/Auth.php
@@ -345,24 +345,33 @@ class Auth extends \fast\Auth
         if ($this->breadcrumb || !$path) {
             return $this->breadcrumb;
         }
-        $path_rule_id = 0;
-        foreach ($this->rules as $rule) {
-            $path_rule_id = $rule['name'] == $path ? $rule['id'] : $path_rule_id;
+        $titleArr = [];
+        $menuArr = [];
+        $urlArr = explode('/', $path);
+        foreach ($urlArr as $index => $item) {
+            $pathArr[implode('/', array_slice($urlArr, 0, $index + 1))] = $index;
+        }
+        if (!$this->rules && $this->id) {
+            $this->getRuleList();
         }
-        if ($path_rule_id) {
-            $this->breadcrumb = Tree::instance()->init($this->rules)->getParents($path_rule_id, true);
-            foreach ($this->breadcrumb as $k => &$v) {
-                $v['url'] = url($v['name']);
-                $v['title'] = __($v['title']);
+        foreach ($this->rules as $rule) {
+            if (isset($pathArr[$rule['name']])) {
+                $rule['title'] = __($rule['title']);
+                $rule['url'] = url($rule['name']);
+                $titleArr[$pathArr[$rule['name']]] = $rule['title'];
+                $menuArr[$pathArr[$rule['name']]] = $rule;
             }
+
         }
+        ksort($menuArr);
+        $this->breadcrumb = $menuArr;
         return $this->breadcrumb;
     }
 
     /**
      * 获取左侧和顶部菜单栏
      *
-     * @param array  $params URL对应的badge数据
+     * @param array  $params    URL对应的badge数据
      * @param string $fixedPage 默认页
      * @return array
      */
diff --git a/application/config.php b/application/config.php
index 64fda49..4b5f83f 100755
--- a/application/config.php
+++ b/application/config.php
@@ -258,24 +258,26 @@ return [
     //FastAdmin配置
     'fastadmin'              => [
         //是否开启前台会员中心
-        'usercenter'          => true,
+        'usercenter'            => true,
+        //会员注册验证码类型email/mobile/wechat/text/false
+        'user_register_captcha' => 'text',
         //登录验证码
-        'login_captcha'       => true,
+        'login_captcha'         => true,
         //登录失败超过10次则1天后重试
-        'login_failure_retry' => true,
+        'login_failure_retry'   => true,
         //是否同一账号同一时间只能在一个地方登录
-        'login_unique'        => false,
+        'login_unique'          => false,
         //是否开启IP变动检测
-        'loginip_check'      => true,
+        'loginip_check'         => true,
         //登录页默认背景图
-        'login_background'    => "/assets/img/loginbg.jpg",
+        'login_background'      => "/assets/img/loginbg.jpg",
         //是否启用多级菜单导航
-        'multiplenav'         => false,
+        'multiplenav'           => false,
         //自动检测更新
-        'checkupdate'         => false,
+        'checkupdate'           => false,
         //版本号
-        'version'             => '1.0.0.20191101_beta',
+        'version'               => '1.0.0.20191212_beta',
         //API接口地址
-        'api_url'             => 'https://api.fastadmin.net',
+        'api_url'               => 'https://api.fastadmin.net',
     ],
 ];
diff --git a/application/index/controller/User.php b/application/index/controller/User.php
index 0b77b00..46bc2c6 100644
--- a/application/index/controller/User.php
+++ b/application/index/controller/User.php
@@ -2,7 +2,9 @@
 
 namespace app\index\controller;
 
+use addons\wechat\model\WechatCaptcha;
 use app\common\controller\Frontend;
+use app\common\library\Ems;
 use app\common\library\Sms;
 use think\Config;
 use think\Cookie;
@@ -100,8 +102,6 @@ class User extends Frontend
                 'username.length'  => 'Username must be 3 to 30 characters',
                 'password.require' => 'Password can not be empty',
                 'password.length'  => 'Password must be 6 to 30 characters',
-                //'captcha.require'  => 'Captcha can not be empty',
-                //'captcha.captcha'  => 'Captcha is incorrect',
                 'email'            => 'Email is incorrect',
                 'mobile'           => 'Mobile is incorrect',
             ];
@@ -110,11 +110,23 @@ class User extends Frontend
                 'password'  => $password,
                 'email'     => $email,
                 'mobile'    => $mobile,
-                //'captcha'   => $captcha,
                 '__token__' => $token,
             ];
-            $ret = Sms::check($mobile, $captcha, 'register');
-            if (!$ret) {
+            //验证码
+            $captchaResult = true;
+            $captchaType = config("fastadmin.user_register_captcha");
+            if ($captchaType) {
+                if ($captchaType == 'mobile') {
+                    $captchaResult = Sms::check($mobile, $captcha, 'register');
+                } elseif ($captchaType == 'email') {
+                    $captchaResult = Ems::check($mobile, $captcha, 'register');
+                } elseif ($captchaType == 'wechat') {
+                    $captchaResult = WechatCaptcha::check($captcha, 'register');
+                } elseif ($captchaType == 'text') {
+                    $captchaResult = \think\Validate::is($captcha, 'captcha');
+                }
+            }
+            if (!$captchaResult) {
                 $this->error(__('Captcha is incorrect'));
             }
             $validate = new Validate($rule, $msg);
@@ -134,6 +146,7 @@ class User extends Frontend
             && !preg_match("/(user\/login|user\/register|user\/logout)/i", $referer)) {
             $url = $referer;
         }
+        $this->view->assign('captchaType', config('fastadmin.user_register_captcha'));
         $this->view->assign('url', $url);
         $this->view->assign('title', __('Register'));
         return $this->view->fetch();
diff --git a/application/index/view/common/captcha.html b/application/index/view/common/captcha.html
new file mode 100644
index 0000000..8de71d7
--- /dev/null
+++ b/application/index/view/common/captcha.html
@@ -0,0 +1,27 @@
+<!--@formatter:off-->
+{if "[type]" == 'email'}
+    <input type="text" name="captcha" class="form-control input-lg" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_ems_correct')}, event=[event], email:#email)" />
+    <span class="input-group-btn" style="padding:0;border:none;">
+        <a href="javascript:;" class="btn btn-info btn-captcha btn-lg" data-url="{:url('api/email/send')}" data-type="email" data-event="[event]">发送验证码</a>
+    </span>
+{elseif "[type]" == 'mobile'/}
+    <input type="text" name="captcha" class="form-control input-lg" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_sms_correct')}, event=[event], mobile:#mobile)" />
+    <span class="input-group-btn" style="padding:0;border:none;">
+        <a href="javascript:;" class="btn btn-info btn-captcha btn-lg" data-url="{:url('api/sms/send')}" data-type="mobile" data-event="[event]">发送验证码</a>
+    </span>
+{elseif "[type]" == 'wechat'/}
+    {if get_addon_info('wechat')}
+        <input type="text" name="captcha" class="form-control input-lg" data-rule="required;length(4);remote({:addon_url('wechat/captcha/check')}, event=[event])" />
+        <span class="input-group-btn" style="padding:0;border:none;">
+            <a href="javascript:;" class="btn btn-info btn-captcha btn-lg" data-url="{:addon_url('wechat/captcha/send')}" data-type="wechat" data-event="[event]">获取验证码</a>
+        </span>
+    {else/}
+        请在后台插件管理中安装《<a href="https://www.fastadmin.net/store/wechat.html" target="_blank">微信管理插件</a>》
+    {/if}
+{elseif "[type]" == 'text' /}
+    <input type="text" name="captcha" class="form-control input-lg" data-rule="required;length(4)" />
+    <span class="input-group-btn" style="padding:0;border:none;">
+        <img src="{:captcha_src()}" width="100" height="40" onclick="this.src = '{:captcha_src()}?r=' + Math.random();"/>
+    </span>
+{/if}
+<!--@formatter:on-->
\ No newline at end of file
diff --git a/application/index/view/user/register.html b/application/index/view/user/register.html
index 6c145c7..ac8ce6b 100644
--- a/application/index/view/user/register.html
+++ b/application/index/view/user/register.html
@@ -34,18 +34,18 @@
                         <p class="help-block"></p>
                     </div>
                 </div>
+
+                {if $captchaType}
                 <div class="form-group">
                     <label class="control-label">{:__('Captcha')}</label>
                     <div class="controls">
                         <div class="input-group">
-                            <input type="text" name="captcha" class="form-control input-lg" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_sms_correct')}, event=register, mobile:#mobile)" />
-                            <span class="input-group-btn" style="padding:0;border:none;">
-                                <a href="javascript:;" class="btn btn-info btn-captcha btn-lg" data-url="{:url('api/sms/send')}" data-type="mobile" data-event="register">发送验证码</a>
-                            </span>
+                            {include file="common/captcha" event="register" type="$captchaType" /}
                         </div>
                         <p class="help-block"></p>
                     </div>
                 </div>
+                {/if}
 
                 <div class="form-group">
                     <button type="submit" class="btn btn-primary btn-lg btn-block">{:__('Sign up')}</button>
diff --git a/bower.json b/bower.json
index 174f802..03875fe 100755
--- a/bower.json
+++ b/bower.json
@@ -22,7 +22,7 @@
     "tableExport.jquery.plugin": "~1.10.3",
     "jquery-slimscroll": "~1.3.8",
     "jquery.cookie": "~1.4.1",
-    "Sortable": "~1.5.0",
+    "Sortable": "~1.10.0",
     "nice-validator": "~1.1.1",
     "art-template": "^3.1.3",
     "requirejs-plugins": "~1.0.3",
diff --git a/public/assets/css/wechat/menu.css b/public/assets/css/wechat/menu.css
deleted file mode 100644
index b0636f7..0000000
--- a/public/assets/css/wechat/menu.css
+++ /dev/null
@@ -1,342 +0,0 @@
-.weixin-menu-setting{
-    margin:0;
-    margin-bottom:10px;
-    width:100%;
-}
-.mobile-head-title{
-    color: #fff;
-    text-align: center;
-    padding-top: 33px;
-    font-size: 15px;
-    width: auto;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    word-wrap: normal;
-    margin: 0 40px 0 70px;
-}
-.weixin-body {
-    padding:0;
-    margin:0;
-    margin-left:337px;
-}
-.weixin-content,.no-weixin-content{
-    background-color: #f4f5f9;
-    border: 1px solid #e7e7eb;
-    padding:15px;
-}
-.no-weixin-content{
-    border:#fff;
-    background-color: #fff;
-    vertical-align: middle;
-    padding-top:200px;
-    text-align: center;
-}
-@media (max-width: 720px) {
-    .weixin-body {
-        margin-left:0;
-        margin-top:560px;
-    }
-}
-.weixin-menu-title{
-    border-bottom: 1px solid #e7e7eb;
-    font-size: 16px;
-    padding: 0 20px;
-    line-height: 55px;
-    margin-bottom: 20px;
-}
-.mobile-menu-preview{
-    display:block;
-    float:left;
-    position:relative;
-    width: 317px;
-    height: 550px;
-    background: transparent url(../../img/wx_mobile_header_bg.png) no-repeat 0 0;
-    background-position: 0 0;
-    border: 1px solid #e7e7eb;
-}
-
-.mobile-menu-preview .menu-list {
-    position: absolute;
-    height:50px;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    border-top: 1px solid #e7e7eb;
-    background: transparent url(../../img/wx_mobile_footer_bg.png) no-repeat 0 0;
-    background-position: 0 0;
-    background-repeat: no-repeat;
-    padding-left: 43px;
-    margin:0;
-}
-.menu-list .menu-item,.menu-list .add-item{
-    line-height: 50px;
-    position: relative;
-    float: left;
-    text-align: center;
-    width: 33.33%;
-    list-style: none;
-}
-.ui-sortable-placeholder{
-    background-color:#fff;
-}
-.menu-item a,.add-item a{
-    display: block;
-    width: auto;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    word-wrap: normal;
-    color: #616161;
-    text-decoration: none;
-}
-.menu-item.current a.menu-link{
-    border: 1px solid #44b549;
-    line-height: 48px;
-    background-color: #fff;
-    color: #44b549;
-}
-.menu-item .icon-menu-dot{
-    background: url(../../img/wx_mobile_index.png) 0 0 no-repeat;
-    width: 7px;
-    height: 7px;
-    vertical-align: middle;
-    display: inline-block;
-    margin-right: 2px;
-    margin-top: -2px;
-    bottom: 60px;
-    background-color: #fafafa;
-    border-top-width: 0;
-}
-.menu-item .menu-link,.add-item .menu-link{
-    border-left-width: 0;
-    border-left: 1px solid #e7e7eb;
-    text-align: center;
-}
-
-.sub-menu-item a,.add-sub-item a{
-    border: 1px solid #d0d0d0;
-    position:relative;
-    padding:0 0.5em;
-}
-.sub-menu-item.current a{
-    border: 1px solid #44b549;
-    background-color: #fff;
-    color: #44b549;
-    z-index: 1;
-}
-.sub-menu-list li a:hover{
-    background:#f1f1f1;
-}
-.menu-item.current .menu-link{
-    border: 1px solid #44b549;
-    line-height: 48px;
-    background-color: #fff;
-    color: #44b549;
-}
-.sub-menu-box{
-    position: absolute;
-    bottom: 60px;
-    left: 0;
-    width: 100%;
-    background-color: #fff;
-    border-top: none;
-}
-.sub-menu-list{
-    line-height: 50px;
-    margin:0;padding:0;
-}
-.sub-menu-list li{
-    line-height: 44px;
-    margin: -1px -1px 0;
-    list-style: none;
-}
-.sub-menu-box .arrow {
-    position: absolute;
-    left: 50%;
-    margin-left: -6px;
-}
-
-.sub-menu-box .arrow-in {
-    bottom: -5px;
-    display: inline-block;
-    width: 0;
-    height: 0;
-    border-width: 6px;
-    border-style: dashed;
-    border-color: transparent;
-    border-bottom-width: 0;
-    border-top-color: #fafafa;
-    border-top-style: solid;
-}
-.sub-menu-box .arrow-out {
-    bottom: -6px;
-    display: inline-block;
-    width: 0;
-    height: 0;
-    border-width: 6px;
-    border-style: dashed;
-    border-color: transparent;
-    border-bottom-width: 0;
-    border-top-color: #d0d0d0;
-    border-top-style: solid;
-}
-.sub-menu-item.current{
-
-}
-.sub-menu-inner-add{
-    display: block;
-    border-top: 1px solid #e7e7eb;
-    width: auto;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    word-wrap: normal;
-    cursor: pointer;
-}
-.weixin-icon{
-    background: url(../../img/weixin_icon.png) 0 -4418px no-repeat;
-    width: 16px;
-    height: 16px;
-    vertical-align: middle;
-    display: inline-block;
-    line-height: 100px;
-    overflow: hidden;
-}
-.weixin-icon.add-gray {
-    background-position: 0 0;
-}
-.weixin-icon.sort-gray {
-    background: url(../../img/weixin_icon.png) 0 -32px no-repeat;
-    background-position: 0 -32px;
-    margin-top: -1px;
-    display:none;
-    width: 20px;
-}
-.weixin-icon.big-add-gray{
-    background-position: -36px 0;
-    width: 36px;
-    height: 36px;
-    vertical-align: middle;
-}
-.menu-item a.menu-link:hover{
-
-}
-
-.add-item.extra,.add-item.extra{
-    float: none;
-    width: auto;
-    overflow: hidden;
-}
-
-table.btn-bar{width:100%;}
-table.btn-bar td{ text-align: center; }
-
-.item-info .item-head{
-    position:relative;
-    padding: 0;
-    border-bottom: 1px solid #e7e7eb;
-}
-.item-info .item-delete{
-    position:absolute;
-    top:0;
-    right:0;
-}
-
-table.weixin-form td{
-    vertical-align:middle;
-    height:24px;
-    line-height: 24px;
-    padding: 8px 0;
-}
-
-#menu-content{
-    background-color: #fff;
-    padding: 16px 20px;
-    border: 1px solid #e7e7eb;
-}
-.menu-content-tips{
-    color: #8d8d8d;
-    padding-bottom: 10px;
-}
-
-.form-item dl{
-    position:relative;
-    margin:10px 0;
-}
-.form-item dl dt{
-    width:90px;
-    height: 30px;
-    line-height: 30px;
-    text-align: right;
-    position:absolute;
-    vertical-align: middle;
-    top:0;
-    left:0;
-    bottom:0;
-    display:block;
-}
-.form-item dl dd{
-    position:relative;
-    display:block;
-    margin-left: 90px;
-    line-height: 30px;
-}
-.form-item .input-box {
-    display: inline-block;
-    position: relative;
-    height: 30px;
-    line-height: 30px;
-    vertical-align: middle;
-    width: 278px;
-    font-size: 14px;
-    padding: 0 10px;
-    border: 1px solid #e7e7eb;
-    box-shadow: none;
-    -moz-box-shadow: none;
-    -webkit-box-shadow: none;
-    border-radius: 0;
-    -moz-border-radius: 0;
-    -webkit-border-radius: 0;
-    background-color: #fff;
-}
-.form-item .input-box input{
-    width: 100%;
-    background-color: transparent;
-    border: 0;
-    outline: 0;
-    height:30px;
-}
-
-.clickbox{
-    text-align: center;
-    margin:40px 0;
-}
-.create-click{
-    display: inline-block;
-    padding-top: 30px;
-    position: relative;
-    width:240px;
-    height: 120px;
-    border: 2px dotted #d9dadc;
-    text-align: center;
-    margin-bottom: 20px;
-    margin-left: 50px;
-}
-.create-click a{
-    display:block;
-}
-.create-click a strong{
-    display:block;
-}
-
-.keytitle {
-    position:absolute;
-    width:100%;
-    text-align:center;
-    top:0px;
-    height:35px;
-    line-height:35px;
-    background:#f4f5f9;
-}
-dl.is-item dd>label {margin-left:5px;}
\ No newline at end of file
diff --git a/public/assets/img/weixin_icon.png b/public/assets/img/weixin_icon.png
deleted file mode 100644
index 75bd668..0000000
Binary files a/public/assets/img/weixin_icon.png and /dev/null differ
diff --git a/public/assets/img/wx_mobile_footer_bg.png b/public/assets/img/wx_mobile_footer_bg.png
deleted file mode 100644
index 4a89d4b..0000000
Binary files a/public/assets/img/wx_mobile_footer_bg.png and /dev/null differ
diff --git a/public/assets/img/wx_mobile_header_bg.png b/public/assets/img/wx_mobile_header_bg.png
deleted file mode 100644
index 248cfb7..0000000
Binary files a/public/assets/img/wx_mobile_header_bg.png and /dev/null differ
diff --git a/public/assets/img/wx_mobile_index.png b/public/assets/img/wx_mobile_index.png
deleted file mode 100644
index db349df..0000000
Binary files a/public/assets/img/wx_mobile_index.png and /dev/null differ
diff --git a/public/assets/js/backend.js b/public/assets/js/backend.js
index 3432f34..596c2c5 100755
--- a/public/assets/js/backend.js
+++ b/public/assets/js/backend.js
@@ -140,7 +140,7 @@ define(['fast', 'template', 'moment'], function (Fast, Template, Moment) {
             $(document).on('click', '.btn-dialog,.dialogit', function (e) {
                 var that = this;
                 var options = $.extend({}, $(that).data() || {});
-                var url = Backend.api.replaceids(that, $(that).attr('href'));
+                var url = Backend.api.replaceids(that, $(that).data("url") || $(that).attr('href'));
                 var title = $(that).attr("title") || $(that).data("title") || $(that).data('original-title');
                 var button = Backend.api.gettablecolumnbutton(options);
                 if (button && typeof button.callback === 'function') {
@@ -152,7 +152,7 @@ define(['fast', 'template', 'moment'], function (Fast, Template, Moment) {
                         Layer.close(index);
                     });
                 } else {
-                    Backend.api.open(url, title, options);
+                    window[$(that).data("window") || 'self'].Backend.api.open(url, title, options);
                 }
                 return false;
             });
@@ -160,7 +160,7 @@ define(['fast', 'template', 'moment'], function (Fast, Template, Moment) {
             $(document).on('click', '.btn-addtabs,.addtabsit', function (e) {
                 var that = this;
                 var options = $.extend({}, $(that).data() || {});
-                var url = Backend.api.replaceids(that, $(that).attr('href'));
+                var url = Backend.api.replaceids(that, $(that).data("url") || $(that).attr('href'));
                 var title = $(that).attr("title") || $(that).data("title") || $(that).data('original-title');
                 var icon = $(that).attr("icon") || $(that).data("icon");
                 if (typeof options.confirm !== 'undefined') {
diff --git a/public/assets/js/frontend.js b/public/assets/js/frontend.js
index a90e189..0aef458 100644
--- a/public/assets/js/frontend.js
+++ b/public/assets/js/frontend.js
@@ -6,47 +6,60 @@ define(['fast', 'template', 'moment'], function (Fast, Template, Moment) {
             //发送验证码
             $(document).on("click", ".btn-captcha", function (e) {
                 var type = $(this).data("type") ? $(this).data("type") : 'mobile';
-                var element = $(this).data("input-id") ? $("#" + $(this).data("input-id")) : $("input[name='" + type + "']", $(this).closest("form"));
-                var text = type === 'email' ? '邮箱' : '手机号码';
-                if (element.val() === "") {
-                    Layer.msg(text + "不能为空!");
-                    element.focus();
-                    return false;
-                } else if (type === 'mobile' && !element.val().match(/^1[3-9]\d{9}$/)) {
-                    Layer.msg("请输入正确的" + text + "!");
-                    element.focus();
-                    return false;
-                } else if (type === 'email' && !element.val().match(/^[\w\+\-]+(\.[\w\+\-]+)*@[a-z\d\-]+(\.[a-z\d\-]+)*\.([a-z]{2,4})$/)) {
-                    Layer.msg("请输入正确的" + text + "!");
-                    element.focus();
-                    return false;
-                }
-                var that = this;
-                element.isValid(function (v) {
-                    if (v) {
-                        $(that).addClass("disabled", true).text("发送中...");
-                        var data = {event: $(that).data("event")};
-                        data[type] = element.val();
-                        Frontend.api.ajax({url: $(that).data("url"), data: data}, function () {
-                            clearInterval(si[type]);
-                            var seconds = 60;
-                            si[type] = setInterval(function () {
-                                seconds--;
-                                if (seconds <= 0) {
-                                    clearInterval(si);
-                                    $(that).removeClass("disabled").text("发送验证码");
-                                } else {
-                                    $(that).addClass("disabled").text(seconds + "秒后可再次发送");
-                                }
-                            }, 1000);
-                        }, function () {
-                            $(that).removeClass("disabled").text('发送验证码');
-                        });
-                    } else {
-                        Layer.msg("请确认已经输入了正确的" + text + "!");
-                    }
-                });
+                var btn = this;
+                Frontend.api.sendcaptcha = function (btn, type, data, callback) {
+                    $(btn).addClass("disabled", true).text("发送中...");
 
+                    Frontend.api.ajax({url: $(btn).data("url"), data: data}, function (data, ret) {
+                        clearInterval(si[type]);
+                        var seconds = 60;
+                        si[type] = setInterval(function () {
+                            seconds--;
+                            if (seconds <= 0) {
+                                clearInterval(si);
+                                $(btn).removeClass("disabled").text("发送验证码");
+                            } else {
+                                $(btn).addClass("disabled").text(seconds + "秒后可再次发送");
+                            }
+                        }, 1000);
+                        if (typeof callback == 'function') {
+                            callback.call(this, data, ret);
+                        }
+                    }, function () {
+                        $(btn).removeClass("disabled").text('发送验证码');
+                    });
+                };
+                if (['mobile', 'email'].indexOf(type) > -1) {
+                    var element = $(this).data("input-id") ? $("#" + $(this).data("input-id")) : $("input[name='" + type + "']", $(this).closest("form"));
+                    var text = type === 'email' ? '邮箱' : '手机号码';
+                    if (element.val() === "") {
+                        Layer.msg(text + "不能为空!");
+                        element.focus();
+                        return false;
+                    } else if (type === 'mobile' && !element.val().match(/^1[3-9]\d{9}$/)) {
+                        Layer.msg("请输入正确的" + text + "!");
+                        element.focus();
+                        return false;
+                    } else if (type === 'email' && !element.val().match(/^[\w\+\-]+(\.[\w\+\-]+)*@[a-z\d\-]+(\.[a-z\d\-]+)*\.([a-z]{2,4})$/)) {
+                        Layer.msg("请输入正确的" + text + "!");
+                        element.focus();
+                        return false;
+                    }
+                    element.isValid(function (v) {
+                        if (v) {
+                            var data = {event: $(btn).data("event")};
+                            data[type] = element.val();
+                            Frontend.api.sendcaptcha(btn, type, data);
+                        } else {
+                            Layer.msg("请确认已经输入了正确的" + text + "!");
+                        }
+                    });
+                } else {
+                    var data = {event: $(btn).data("event")};
+                    Frontend.api.sendcaptcha(btn, type, data, function (data, ret) {
+                        Layer.open({title: false, area: ["400px", "430px"], content: "<img src='" + data.image + "' width='400' height='400' /><div class='text-center panel-title'>扫一扫关注公众号获取验证码</div>", type: 1});
+                    });
+                }
                 return false;
             });
             //tooltip和popover
diff --git a/public/assets/js/require-backend.min.js b/public/assets/js/require-backend.min.js
index 6038c71..056879a 100644
--- a/public/assets/js/require-backend.min.js
+++ b/public/assets/js/require-backend.min.js
@@ -5766,7 +5766,7 @@ define('backend',['fast', 'template', 'moment'], function (Fast, Template, Momen
             $(document).on('click', '.btn-dialog,.dialogit', function (e) {
                 var that = this;
                 var options = $.extend({}, $(that).data() || {});
-                var url = Backend.api.replaceids(that, $(that).attr('href'));
+                var url = Backend.api.replaceids(that, $(that).data("url") || $(that).attr('href'));
                 var title = $(that).attr("title") || $(that).data("title") || $(that).data('original-title');
                 var button = Backend.api.gettablecolumnbutton(options);
                 if (button && typeof button.callback === 'function') {
@@ -5778,7 +5778,7 @@ define('backend',['fast', 'template', 'moment'], function (Fast, Template, Momen
                         Layer.close(index);
                     });
                 } else {
-                    Backend.api.open(url, title, options);
+                    window[$(that).data("window") || 'self'].Backend.api.open(url, title, options);
                 }
                 return false;
             });
@@ -5786,7 +5786,7 @@ define('backend',['fast', 'template', 'moment'], function (Fast, Template, Momen
             $(document).on('click', '.btn-addtabs,.addtabsit', function (e) {
                 var that = this;
                 var options = $.extend({}, $(that).data() || {});
-                var url = Backend.api.replaceids(that, $(that).attr('href'));
+                var url = Backend.api.replaceids(that, $(that).data("url") || $(that).attr('href'));
                 var title = $(that).attr("title") || $(that).data("title") || $(that).data('original-title');
                 var icon = $(that).attr("icon") || $(that).data("icon");
                 if (typeof options.confirm !== 'undefined') {
@@ -8953,7 +8953,7 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef
                     },
                     dataFilter: function (data) {
                         if (data.code === 1) {
-                            return data.msg ? { "ok": data.msg } : '';
+                            return data.msg ? {"ok": data.msg} : '';
                         } else {
                             return data.msg;
                         }
@@ -9100,7 +9100,9 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef
                             showClose: true
                         };
                         $('.datetimepicker', form).parent().css('position', 'relative');
-                        $('.datetimepicker', form).datetimepicker(options);
+                        $('.datetimepicker', form).datetimepicker(options).on('dp.change', function (e) {
+                            $(this, document).trigger("changed");
+                        });
                     });
                 }
             },
@@ -9141,7 +9143,7 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef
                             $(this).on('cancel.daterangepicker', function (ev, picker) {
                                 $(this).val('').trigger('blur');
                             });
-                            $(this).daterangepicker($.extend({}, options, $(this).data()), callback);
+                            $(this).daterangepicker($.extend(true, options, $(this).data()), callback);
                         });
                     });
                 }
@@ -9203,7 +9205,7 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef
                         var refresh = function (name) {
                             var data = {};
                             var textarea = $("textarea[name='" + name + "']", form);
-                            var container = textarea.closest("dl");
+                            var container = $(".fieldlist[data-name='" + name + "']");
                             var template = container.data("template");
                             $.each($("input,select,textarea", container).serializeArray(), function (i, j) {
                                 var reg = /\[(\w+)\]\[(\w+)\]$/g;
@@ -9231,42 +9233,44 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef
                             textarea.val(JSON.stringify(result));
                         };
                         //监听文本框改变事件
-                        $(document).on('change keyup', ".fieldlist input,.fieldlist textarea,.fieldlist select", function () {
-                            refresh($(this).closest("dl").data("name"));
+                        $(document).on('change keyup changed', ".fieldlist input,.fieldlist textarea,.fieldlist select", function () {
+                            refresh($(this).closest(".fieldlist").data("name"));
                         });
                         //追加控制
                         $(".fieldlist", form).on("click", ".btn-append,.append", function (e, row) {
-                            var container = $(this).closest("dl");
+                            var container = $(this).closest(".fieldlist");
+                            var tagName = container.data("tag") || "dd";
                             var index = container.data("index");
                             var name = container.data("name");
                             var template = container.data("template");
                             var data = container.data();
                             index = index ? parseInt(index) : 0;
                             container.data("index", index + 1);
-                            var row = row ? row : {};
+                            row = row ? row : {};
                             var vars = {index: index, name: name, data: data, row: row};
                             var html = template ? Template(template, vars) : Template.render(Form.config.fieldlisttpl, vars);
-                            $(html).insertBefore($(this).closest("dd"));
-                            $(this).trigger("fa.event.appendfieldlist", $(this).closest("dd").prev());
+                            $(html).insertBefore($(tagName + ":last", container));
+                            $(this).trigger("fa.event.appendfieldlist", $(this).closest(tagName).prev());
                         });
                         //移除控制
-                        $(".fieldlist", form).on("click", "dd .btn-remove", function () {
-                            var container = $(this).closest("dl");
-                            $(this).closest("dd").remove();
+                        $(".fieldlist", form).on("click", ".btn-remove", function () {
+                            var container = $(this).closest(".fieldlist");
+                            var tagName = container.data("tag") || "dd";
+                            $(this).closest(tagName).remove();
                             refresh(container.data("name"));
                         });
-                        //拖拽排序
-                        $("dl.fieldlist", form).dragsort({
-                            itemSelector: 'dd',
-                            dragSelector: ".btn-dragsort",
-                            dragEnd: function () {
-                                refresh($(this).closest("dl").data("name"));
-                            },
-                            placeHolderTemplate: "<dd></dd>"
-                        });
-                        //渲染数据
+                        //渲染数据&拖拽排序
                         $(".fieldlist", form).each(function () {
                             var container = this;
+                            var tagName = $(this).data("tag") || "dd";
+                            $(this).dragsort({
+                                itemSelector: tagName,
+                                dragSelector: ".btn-dragsort",
+                                dragEnd: function () {
+                                    refresh($(this).closest(".fieldlist").data("name"));
+                                },
+                                placeHolderTemplate: $("<" + tagName + "/>")
+                            });
                             var textarea = $("textarea[name='" + $(this).data("name") + "']", form);
                             if (textarea.val() == '') {
                                 return true;
@@ -10180,10 +10184,15 @@ define('table',['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstr
                     $(Table.config.disabledbtn, toolbar).toggleClass('disabled', !ids.length);
                 });
                 // 绑定TAB事件
-                $('.panel-heading ul[data-field] li a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
-                    var field = $(this).closest("ul").data("field");
+                $('.panel-heading [data-field] a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+                    var field = $(this).closest("[data-field]").data("field");
                     var value = $(this).data("value");
-                    $("select[name='" + field + "'] option[value='" + value + "']", table.closest(".bootstrap-table").find(".commonsearch-table")).prop("selected", true);
+                    var object = $("[name='" + field + "']", table.closest(".bootstrap-table").find(".commonsearch-table"));
+                    if (object.prop('tagName') == "SELECT") {
+                        $("option[value='" + value + "']", object).prop("selected", true);
+                    } else {
+                        object.val(value);
+                    }
                     table.bootstrapTable('refresh', {pageNumber: 1});
                     return false;
                 });
diff --git a/public/assets/js/require-form.js b/public/assets/js/require-form.js
index c054900..7d429e0 100755
--- a/public/assets/js/require-form.js
+++ b/public/assets/js/require-form.js
@@ -20,7 +20,7 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U
                     },
                     dataFilter: function (data) {
                         if (data.code === 1) {
-                            return data.msg ? { "ok": data.msg } : '';
+                            return data.msg ? {"ok": data.msg} : '';
                         } else {
                             return data.msg;
                         }
@@ -167,7 +167,9 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U
                             showClose: true
                         };
                         $('.datetimepicker', form).parent().css('position', 'relative');
-                        $('.datetimepicker', form).datetimepicker(options);
+                        $('.datetimepicker', form).datetimepicker(options).on('dp.change', function (e) {
+                            $(this, document).trigger("changed");
+                        });
                     });
                 }
             },
@@ -270,7 +272,7 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U
                         var refresh = function (name) {
                             var data = {};
                             var textarea = $("textarea[name='" + name + "']", form);
-                            var container = textarea.closest("dl");
+                            var container = $(".fieldlist[data-name='" + name + "']");
                             var template = container.data("template");
                             $.each($("input,select,textarea", container).serializeArray(), function (i, j) {
                                 var reg = /\[(\w+)\]\[(\w+)\]$/g;
@@ -298,42 +300,44 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U
                             textarea.val(JSON.stringify(result));
                         };
                         //监听文本框改变事件
-                        $(document).on('change keyup', ".fieldlist input,.fieldlist textarea,.fieldlist select", function () {
-                            refresh($(this).closest("dl").data("name"));
+                        $(document).on('change keyup changed', ".fieldlist input,.fieldlist textarea,.fieldlist select", function () {
+                            refresh($(this).closest(".fieldlist").data("name"));
                         });
                         //追加控制
                         $(".fieldlist", form).on("click", ".btn-append,.append", function (e, row) {
-                            var container = $(this).closest("dl");
+                            var container = $(this).closest(".fieldlist");
+                            var tagName = container.data("tag") || "dd";
                             var index = container.data("index");
                             var name = container.data("name");
                             var template = container.data("template");
                             var data = container.data();
                             index = index ? parseInt(index) : 0;
                             container.data("index", index + 1);
-                            var row = row ? row : {};
+                            row = row ? row : {};
                             var vars = {index: index, name: name, data: data, row: row};
                             var html = template ? Template(template, vars) : Template.render(Form.config.fieldlisttpl, vars);
-                            $(html).insertBefore($(this).closest("dd"));
-                            $(this).trigger("fa.event.appendfieldlist", $(this).closest("dd").prev());
+                            $(html).insertBefore($(tagName + ":last", container));
+                            $(this).trigger("fa.event.appendfieldlist", $(this).closest(tagName).prev());
                         });
                         //移除控制
-                        $(".fieldlist", form).on("click", "dd .btn-remove", function () {
-                            var container = $(this).closest("dl");
-                            $(this).closest("dd").remove();
+                        $(".fieldlist", form).on("click", ".btn-remove", function () {
+                            var container = $(this).closest(".fieldlist");
+                            var tagName = container.data("tag") || "dd";
+                            $(this).closest(tagName).remove();
                             refresh(container.data("name"));
                         });
-                        //拖拽排序
-                        $("dl.fieldlist", form).dragsort({
-                            itemSelector: 'dd',
-                            dragSelector: ".btn-dragsort",
-                            dragEnd: function () {
-                                refresh($(this).closest("dl").data("name"));
-                            },
-                            placeHolderTemplate: "<dd></dd>"
-                        });
-                        //渲染数据
+                        //渲染数据&拖拽排序
                         $(".fieldlist", form).each(function () {
                             var container = this;
+                            var tagName = $(this).data("tag") || "dd";
+                            $(this).dragsort({
+                                itemSelector: tagName,
+                                dragSelector: ".btn-dragsort",
+                                dragEnd: function () {
+                                    refresh($(this).closest(".fieldlist").data("name"));
+                                },
+                                placeHolderTemplate: $("<" + tagName + "/>")
+                            });
                             var textarea = $("textarea[name='" + $(this).data("name") + "']", form);
                             if (textarea.val() == '') {
                                 return true;
diff --git a/public/assets/js/require-frontend.min.js b/public/assets/js/require-frontend.min.js
index 78171e2..9ac07a6 100644
--- a/public/assets/js/require-frontend.min.js
+++ b/public/assets/js/require-frontend.min.js
@@ -5625,47 +5625,60 @@ define('frontend',['fast', 'template', 'moment'], function (Fast, Template, Mome
             //发送验证码
             $(document).on("click", ".btn-captcha", function (e) {
                 var type = $(this).data("type") ? $(this).data("type") : 'mobile';
-                var element = $(this).data("input-id") ? $("#" + $(this).data("input-id")) : $("input[name='" + type + "']", $(this).closest("form"));
-                var text = type === 'email' ? '邮箱' : '手机号码';
-                if (element.val() === "") {
-                    Layer.msg(text + "不能为空!");
-                    element.focus();
-                    return false;
-                } else if (type === 'mobile' && !element.val().match(/^1[3-9]\d{9}$/)) {
-                    Layer.msg("请输入正确的" + text + "!");
-                    element.focus();
-                    return false;
-                } else if (type === 'email' && !element.val().match(/^[\w\+\-]+(\.[\w\+\-]+)*@[a-z\d\-]+(\.[a-z\d\-]+)*\.([a-z]{2,4})$/)) {
-                    Layer.msg("请输入正确的" + text + "!");
-                    element.focus();
-                    return false;
-                }
-                var that = this;
-                element.isValid(function (v) {
-                    if (v) {
-                        $(that).addClass("disabled", true).text("发送中...");
-                        var data = {event: $(that).data("event")};
-                        data[type] = element.val();
-                        Frontend.api.ajax({url: $(that).data("url"), data: data}, function () {
-                            clearInterval(si[type]);
-                            var seconds = 60;
-                            si[type] = setInterval(function () {
-                                seconds--;
-                                if (seconds <= 0) {
-                                    clearInterval(si);
-                                    $(that).removeClass("disabled").text("发送验证码");
-                                } else {
-                                    $(that).addClass("disabled").text(seconds + "秒后可再次发送");
-                                }
-                            }, 1000);
-                        }, function () {
-                            $(that).removeClass("disabled").text('发送验证码');
-                        });
-                    } else {
-                        Layer.msg("请确认已经输入了正确的" + text + "!");
+                var btn = this;
+                Frontend.api.sendcaptcha = function (btn, type, data, callback) {
+                    $(btn).addClass("disabled", true).text("发送中...");
+
+                    Frontend.api.ajax({url: $(btn).data("url"), data: data}, function (data, ret) {
+                        clearInterval(si[type]);
+                        var seconds = 60;
+                        si[type] = setInterval(function () {
+                            seconds--;
+                            if (seconds <= 0) {
+                                clearInterval(si);
+                                $(btn).removeClass("disabled").text("发送验证码");
+                            } else {
+                                $(btn).addClass("disabled").text(seconds + "秒后可再次发送");
+                            }
+                        }, 1000);
+                        if (typeof callback == 'function') {
+                            callback.call(this, data, ret);
+                        }
+                    }, function () {
+                        $(btn).removeClass("disabled").text('发送验证码');
+                    });
+                };
+                if (['mobile', 'email'].indexOf(type) > -1) {
+                    var element = $(this).data("input-id") ? $("#" + $(this).data("input-id")) : $("input[name='" + type + "']", $(this).closest("form"));
+                    var text = type === 'email' ? '邮箱' : '手机号码';
+                    if (element.val() === "") {
+                        Layer.msg(text + "不能为空!");
+                        element.focus();
+                        return false;
+                    } else if (type === 'mobile' && !element.val().match(/^1[3-9]\d{9}$/)) {
+                        Layer.msg("请输入正确的" + text + "!");
+                        element.focus();
+                        return false;
+                    } else if (type === 'email' && !element.val().match(/^[\w\+\-]+(\.[\w\+\-]+)*@[a-z\d\-]+(\.[a-z\d\-]+)*\.([a-z]{2,4})$/)) {
+                        Layer.msg("请输入正确的" + text + "!");
+                        element.focus();
+                        return false;
                     }
-                });
-
+                    element.isValid(function (v) {
+                        if (v) {
+                            var data = {event: $(btn).data("event")};
+                            data[type] = element.val();
+                            Frontend.api.sendcaptcha(btn, type, data);
+                        } else {
+                            Layer.msg("请确认已经输入了正确的" + text + "!");
+                        }
+                    });
+                } else {
+                    var data = {event: $(btn).data("event")};
+                    Frontend.api.sendcaptcha(btn, type, data, function (data, ret) {
+                        Layer.open({title: false, area: ["400px", "430px"], content: "<img src='" + data.image + "' width='400' height='400' /><div class='text-center panel-title'>扫一扫关注公众号获取验证码</div>", type: 1});
+                    });
+                }
                 return false;
             });
             //tooltip和popover
diff --git a/public/assets/js/require-table.js b/public/assets/js/require-table.js
index d7aa506..878c319 100644
--- a/public/assets/js/require-table.js
+++ b/public/assets/js/require-table.js
@@ -203,10 +203,15 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                     $(Table.config.disabledbtn, toolbar).toggleClass('disabled', !ids.length);
                 });
                 // 绑定TAB事件
-                $('.panel-heading ul[data-field] li a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
-                    var field = $(this).closest("ul").data("field");
+                $('.panel-heading [data-field] a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+                    var field = $(this).closest("[data-field]").data("field");
                     var value = $(this).data("value");
-                    $("select[name='" + field + "'] option[value='" + value + "']", table.closest(".bootstrap-table").find(".commonsearch-table")).prop("selected", true);
+                    var object = $("[name='" + field + "']", table.closest(".bootstrap-table").find(".commonsearch-table"));
+                    if (object.prop('tagName') == "SELECT") {
+                        $("option[value='" + value + "']", object).prop("selected", true);
+                    } else {
+                        object.val(value);
+                    }
                     table.bootstrapTable('refresh', {pageNumber: 1});
                     return false;
                 });