diff --git a/app/admin/view/config/storage-alioss.html b/app/admin/view/config/storage-alioss.html
index a819600af..29da187f2 100644
--- a/app/admin/view/config/storage-alioss.html
+++ b/app/admin/view/config/storage-alioss.html
@@ -50,7 +50,7 @@
             </label>
             <div class="layui-input-block">
                 <input id="storage.alioss_bucket" type="text" name="storage.alioss_bucket" required value="{:sysconf('storage.alioss_bucket')}" placeholder="请输入阿里云OSS存储 Bucket (空间名称)" class="layui-input">
-                <p class="help-block">填写阿里云OSS存储空间名称,如:think-admin-oss(需要是全区唯一的值,不存在时会自动创建)</p>
+                <p class="help-block">填写阿里云OSS存储空间名称,如:think-admin-oss</p>
             </div>
         </div>
 
@@ -92,7 +92,7 @@
             <button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消修改吗?" data-close>取消修改</button>
         </div>
 
-        <script>form.render()</script>
+        <script>layui.form.render()</script>
 
     </div>
 </form>
\ No newline at end of file
diff --git a/app/wechat/controller/Fans.php b/app/wechat/controller/Fans.php
index 94a7298b7..bad1d0633 100644
--- a/app/wechat/controller/Fans.php
+++ b/app/wechat/controller/Fans.php
@@ -18,6 +18,7 @@ namespace app\wechat\controller;
 
 use app\wechat\service\WechatService;
 use think\admin\Controller;
+use think\admin\helper\QueryHelper;
 use think\exception\HttpResponseException;
 
 /**
@@ -37,17 +38,18 @@ class Fans extends Controller
      * 微信用户管理
      * @auth true
      * @menu true
-     * @throws \think\admin\Exception
      * @throws \think\db\exception\DataNotFoundException
      * @throws \think\db\exception\DbException
      * @throws \think\db\exception\ModelNotFoundException
      */
     public function index()
     {
-        $this->title = '微信用户管理';
-        $this->where = ['appid' => WechatService::instance()->getAppid()];
-        $query = $this->_query($this->table)->like('nickname')->equal('subscribe,is_black');
-        $query->dateBetween('subscribe_at')->where($this->where)->order('subscribe_time desc')->page();
+        $this->_query($this->table)->layTable(function () {
+            $this->title = '微信用户管理';
+        }, function (QueryHelper $query) {
+            $query->where(['appid' => WechatService::instance()->getAppid()]);
+            $query->like('nickname')->equal('subscribe,is_black')->dateBetween('subscribe_at');
+        });
     }
 
     /**
@@ -56,13 +58,7 @@ class Fans extends Controller
      */
     protected function _index_page_filter(array &$data)
     {
-        $tags = $this->app->db->name('WechatFansTags')->column('name', 'id');
-        foreach ($data as &$vo) {
-            $vo['tags'] = [];
-            foreach (explode(',', $vo['tagid_list']) as $tagid) {
-                if (isset($tags[$tagid])) $vo['tags'][] = $tags[$tagid];
-            }
-        }
+        foreach ($data as &$vo) $vo['subscribe_at'] = format_datetime($vo['subscribe_at']);
     }
 
     /**
@@ -76,19 +72,34 @@ class Fans extends Controller
     }
 
     /**
-     * 清空用户数据
+     * 黑名单列表操作
      * @auth true
      */
-    public function truncate()
+    public function black()
     {
+        $data = $this->_vali([
+            'openid.require' => '操作用户不能为空!',
+            'black.require'  => '操作类型不能为空!',
+        ]);
         try {
-            $this->_query('WechatFans')->empty();
-            $this->_query('WechatFansTags')->empty();
-            $this->success('清空用户数据成功!');
-        } catch (\think\exception\HttpResponseException $exception) {
+            foreach (array_chunk(explode(',', $data['openid']), 20) as $openids) {
+                if ($data['black']) {
+                    WechatService::WeChatUser()->batchBlackList($openids);
+                    $this->app->db->name('WechatFans')->whereIn('openid', $openids)->update(['is_black' => 1]);
+                } else {
+                    WechatService::WeChatUser()->batchUnblackList($openids);
+                    $this->app->db->name('WechatFans')->whereIn('openid', $openids)->update(['is_black' => 0]);
+                }
+            }
+            if (empty($data['black'])) {
+                $this->success('移出黑名单成功!');
+            } else {
+                $this->success('拉入黑名单成功!');
+            }
+        } catch (HttpResponseException $exception) {
             throw  $exception;
         } catch (\Exception $exception) {
-            $this->error("清空用户数据失败,{$exception->getMessage()}");
+            $this->error("黑名单操作失败,请稍候再试!<br>{$exception->getMessage()}");
         }
     }
 
@@ -99,48 +110,23 @@ class Fans extends Controller
      */
     public function remove()
     {
-        $this->_applyFormToken();
         $this->_delete($this->table);
     }
 
     /**
-     * 用户拉入黑名单
+     * 清空用户数据
      * @auth true
      */
-    public function blackAdd()
+    public function truncate()
     {
         try {
-            $this->_applyFormToken();
-            foreach (array_chunk(explode(',', $this->request->post('openid')), 20) as $openids) {
-                WechatService::WeChatUser()->batchBlackList($openids);
-                $this->app->db->name('WechatFans')->whereIn('openid', $openids)->update(['is_black' => '1']);
-            }
-            $this->success('拉入黑名单成功!');
+            $this->_query('WechatFans')->empty();
+            $this->_query('WechatFansTags')->empty();
+            $this->success('清空用户数据成功!');
         } catch (HttpResponseException $exception) {
             throw  $exception;
         } catch (\Exception $exception) {
-            $this->error("拉入黑名单失败,请稍候再试!<br>{$exception->getMessage()}");
+            $this->error("清空用户数据失败,{$exception->getMessage()}");
         }
     }
-
-    /**
-     * 用户移出黑名单
-     * @auth true
-     */
-    public function blackDel()
-    {
-        try {
-            $this->_applyFormToken();
-            foreach (array_chunk(explode(',', $this->request->post('openid')), 20) as $openids) {
-                WechatService::WeChatUser()->batchUnblackList($openids);
-                $this->app->db->name('WechatFans')->whereIn('openid', $openids)->update(['is_black' => '0']);
-            }
-            $this->success('移出黑名单成功!');
-        } catch (HttpResponseException $exception) {
-            throw  $exception;
-        } catch (\Exception $exception) {
-            $this->error("移出黑名单失败,请稍候再试!<br>{$exception->getMessage()}");
-        }
-    }
-
 }
diff --git a/app/wechat/view/config/options_form_api.html b/app/wechat/view/config/options_form_api.html
index adff7da78..775e4af34 100644
--- a/app/wechat/view/config/options_form_api.html
+++ b/app/wechat/view/config/options_form_api.html
@@ -43,24 +43,24 @@
         <div class="layui-form-item">
             <label class="layui-form-label"><b>PushApi</b><br><span class="nowrap color-desc">消息推送接收</span></label>
             <div class="layui-input-block">
-                <div class="relative">
+                <label class="relative block">
                     <input value="服务器授权IP:{$geoip}" disabled class="layui-input layui-bg-gray">
-                    <a data-copy="{$geoip}" class="input-right-icon"><i class="iconfont icon-copy"></i></a>
-                </div>
-                <div class="relative margin-top-5">
+                    <a data-copy="{$geoip}" class="layui-icon layui-icon-release input-right-icon"></a>
+                </label>
+                <label class="relative block margin-top-5">
                     <input value="消息推送地址:{$thrNotify}" disabled class="layui-input layui-bg-gray">
-                    <a data-copy="{$thrNotify}" class="input-right-icon"><i class="iconfont icon-copy"></i></a>
-                </div>
+                    <a data-copy="{$thrNotify}" class="layui-icon layui-icon-release input-right-icon"></a>
+                </label>
                 <p class="help-block">公众号服务平台消息推送接口及服务器授权IP地址,需在公众号接口开发处配置。</p>
             </div>
         </div>
 
-        <div class="hr-line-dashed margin-top-30"></div>
-        <input type="hidden" name="wechat.type" value="api">
+    </div>
 
-        <div class="layui-form-item text-center">
-            <button class="layui-btn" type="submit">保存配置</button>
-        </div>
+    <div class="hr-line-dashed"></div>
+    <input type="hidden" name="wechat.type" value="api">
 
+    <div class="layui-form-item text-center">
+        <button class="layui-btn" type="submit">保存配置</button>
     </div>
 </form>
\ No newline at end of file
diff --git a/app/wechat/view/config/options_form_thr.html b/app/wechat/view/config/options_form_thr.html
index 28a442ee9..edc185a57 100644
--- a/app/wechat/view/config/options_form_thr.html
+++ b/app/wechat/view/config/options_form_thr.html
@@ -46,28 +46,29 @@
         <div class="layui-form-item">
             <label class="layui-form-label"><b>AppKey</b><br><span class="nowrap color-desc">服务接口密钥</span></label>
             <div class="layui-input-block">
-                <input name="wechat.thr_appkey" required placeholder="请输入32位第三方平台接口密钥AppKey(必填)" value="{:sysconf('wechat.thr_appkey')}" maxlength="32" pattern="^[0-9a-z]{32}$" class="layui-input">
-                <p class="help-block">公众号服务平台接口密钥, 通过微信第三方授权自动获取, 若没有值请进行微信第三方授权。</p>
+                <label class="relative block">
+                    <input name="wechat.thr_appkey" required placeholder="请输入32位第三方平台接口密钥AppKey(必填)" value="{:sysconf('wechat.thr_appkey')}" maxlength="32" pattern="^[0-9a-z]{32}$" class="layui-input">
+                    <p class="help-block">公众号服务平台接口密钥, 通过微信第三方授权自动获取, 若没有值请进行微信第三方授权。</p>
+                </label>
             </div>
         </div>
 
         <div class="layui-form-item">
             <label class="layui-form-label"><b>PushApi</b><br><span class="nowrap color-desc">服务推送接口</span></label>
             <div class="layui-input-block">
-                <div class="relative margin-top-5">
+                <label class="relative block">
                     <input value="{$thrNotify}" disabled class="layui-input layui-bg-gray">
-                    <a data-copy="{$thrNotify}" class="input-right-icon"><i class="iconfont icon-copy"></i></a>
-                </div>
+                    <a data-copy="{$thrNotify}" class="layui-icon layui-icon-release input-right-icon"></a>
+                </label>
                 <p class="help-block">公众号绑定服务平台接口通知 URL, 公众号消息接收与回复等。</p>
             </div>
         </div>
+    </div>
 
-        <div class="hr-line-dashed margin-top-30"></div>
-        <input type="hidden" name="wechat.type" value="thr">
-
-        <div class="layui-form-item text-center">
-            <button class="layui-btn" type="submit">保存配置</button>
-        </div>
+    <div class="hr-line-dashed"></div>
+    <input type="hidden" name="wechat.type" value="thr">
 
+    <div class="layui-form-item text-center">
+        <button class="layui-btn" type="submit">保存配置</button>
     </div>
 </form>
\ No newline at end of file
diff --git a/app/wechat/view/fans/index.html b/app/wechat/view/fans/index.html
index 27a4c81d3..f79b5f6a3 100644
--- a/app/wechat/view/fans/index.html
+++ b/app/wechat/view/fans/index.html
@@ -1,12 +1,9 @@
-{extend name="../../admin/view/main"}
+{extend name="../../admin/view/table"}
 
 {block name="button"}
-<!--{if auth("blackAdd")}-->
-<button data-action='{:url("blackAdd")}' data-rule="openid#{key}" data-csrf="{:systoken('blackAdd')}" class='layui-btn layui-btn-sm layui-btn-primary'>拉入黑名单</button>
-<!--{/if}-->
-
-<!--{if auth("blackDel")}-->
-<button data-action='{:url("blackDel")}' data-rule="openid#{key}" data-csrf="{:systoken('blackDel')}" class='layui-btn layui-btn-sm layui-btn-primary'>移出黑名单</button>
+<!--{if auth("black")}-->
+<button data-action='{:url("black")}' data-table-id="UserData" data-rule="openid#{openid};black#1" class='layui-btn layui-btn-sm layui-btn-primary'>拉入黑名单</button>
+<button data-action='{:url("black")}' data-table-id="UserData" data-rule="openid#{openid};black#0" class='layui-btn layui-btn-sm layui-btn-primary'>移出黑名单</button>
 <!--{/if}-->
 
 <!--{if auth("truncate")}-->
@@ -14,77 +11,66 @@
 <!--{/if}-->
 
 <!--{if auth("sync")}-->
-<button data-queue='{:url("sync")}' data-confirm="确定要创建同步用户数据的后台任务?" class='layui-btn layui-btn-sm layui-btn-primary'>同步用户数据</button>
+<button data-queue='{:url("sync")}' data-table-id="UserData" data-confirm="确定要创建同步用户数据的后台任务?" class='layui-btn layui-btn-sm layui-btn-primary'>同步用户数据</button>
 <!--{/if}-->
 {/block}
 
 {block name="content"}
 <div class="think-box-shadow">
     {include file='fans/index_search'}
-    <table class="layui-table margin-top-10" lay-skin="line">
-        {notempty name='list'}
-        <thead>
-        <tr>
-            <th class='list-table-check-td think-checkbox'>
-                <label><input data-auto-none data-check-target='.list-check-box' type='checkbox'></label>
-            </th>
-            <th width="180px" class='text-left nowrap'>微信昵称</th>
-            <th width="180px"></th>
-            <th width="150px" class='text-left nowrap'>性别语言</th>
-            <th width="180px" class='text-left nowrap'>订阅时间</th>
-            <th width="80px"></th>
-            <th></th>
-        </tr>
-        </thead>
-        {/notempty}
-        <tbody>
-        {foreach $list as $key=>$vo}
-        <tr>
-            <td class='list-table-check-td think-checkbox'>
-                <label><input class="list-check-box" value='{$vo.openid}' type='checkbox'></label>
-            </td>
-            <td class='text-left nowrap'>
-                <div class="headimg" data-tips-image data-lazy-src="{$vo.headimgurl|default=''}"></div>
-                <div class="inline-block">
-                    <p>昵称:{$vo.nickname|default='--'}</p>
-                    <p>区域:{$vo.country|default='--'} {$vo.province|default=''} {$vo.city|default=''}</p>
-                </div>
-            </td>
-            <td class="text-left padding-0">
-                <div style="max-height:60px;overflow:auto">{foreach $vo.tags as $t}<p><span class="layui-badge layui-bg-cyan margin-right-5">{$t|default='--'}</span></p>{/foreach}</div>
-            </td>
-            <td class='text-left nowrap'>
-                性别:{switch name='vo.sex'}{case value='1'}男{/case}{case value='2'}女{/case}{default}未知{/switch}<br>
-                语言:{$vo.language|raw}
-            </td>
-            <td class='text-left nowrap'>
-                日期:{$vo.subscribe_at|format_datetime|str_replace=' ','<br>时间:',###|raw}
-            </td>
-            <td class='text-center nowrap'>
-                <div class="notselect">
-                    {eq name='vo.subscribe' value='0'}<span class="layui-badge">未订阅</span>{else}<span class="layui-badge layui-bg-green">已订阅</span>{/eq}
-                    {eq name='vo.is_black' value='0'}<span class="layui-badge layui-bg-green">未拉黑</span>{else}<span class="layui-badge">已拉黑</span>{/eq}
-                </div>
-            </td>
-            <td class="nowrap">
-
-                <!--{if auth('blackAdd') and $vo.is_black eq 0}-->
-                <a class="layui-btn layui-btn-sm" data-action="{:url('blackAdd')}" data-value="openid#{$vo.openid}" data-csrf="{:systoken('blackAdd')}">加入黑名单</a>
-                <!--{elseif auth('blackDel') and $vo.is_black eq 1}-->
-                <a class="layui-btn layui-btn-sm" data-action="{:url('blackDel')}" data-value="openid#{$vo.openid}" data-csrf="{:systoken('blackDel')}">移出黑名单</a>
-                <!--{/if}-->
-
-                <!--{if auth("remove")}-->
-                <a class="layui-btn layui-btn-sm layui-btn-danger" data-action="{:url('remove')}" data-value="id#{$vo.id}" data-csrf="{:systoken('remove')}" data-confirm="确定要删除该用户吗?">删 除</a>
-                <!--{/if}-->
-
-            </td>
-        </tr>
-        {/foreach}
-        </tbody>
-    </table>
-
-    {empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
-
+    <table id="UserData" data-url="{:sysuri()}" data-target-search="form.form-search"></table>
 </div>
 {/block}
+
+{block name='script'}
+<script>
+    $(function () {
+        $('#UserData').layTable({
+            even: true, height: 'full',
+            sort: {field: 'subscribe_time', type: 'desc'},
+            cols: [[
+                {checkbox: true},
+                {field: 'nickname', title: '微信昵称', minWidth: 100, align: 'center'},
+                {field: 'province', title: '所在区域', minWidth: 120, templet: '<div>{{d.country}} {{d.province}} {{d.city}}</div>'},
+                {field: 'sex', title: '性别', align: 'center', minWidth: 80, templet: '<div>{{d.sex==1 ? "男" : (d.sex==2 ? "女" : "未知")}}</div>'},
+                {field: 'language', title: '所属国家', align: 'center', minWidth: 100, templet: '<div>{{d.language}}</div>'},
+                {field: 'subscribe_time', title: '关注时间', minWidth: 170, align: 'center', sort: true, templet: '<div>{{d.subscribe_at}}</div>'},
+                {
+                    field: 'subscribe', title: '订阅状态', align: "center", templet: function (d) {
+                        if (d.subscribe > 0) return '<span class="layui-badge layui-bg-green">已订阅</span>';
+                        return '<span class="layui-badge">未订阅</span>';
+                    }
+                },
+                {field: 'is_black', title: '是否黑名单', align: 'center', minWidth: 110, templet: '#StatusSwitchTpl'},
+                {toolbar: '#toolbar', title: '操作面板', align: 'center', fixed: 'right'}
+            ]]
+        });
+
+        // 数据状态切换操作
+        layui.form.on('switch(StatusSwitch)', function (obj) {
+            var data = {openid: obj.value, black: obj.elem.checked > 0 ? 1 : 0};
+            $.form.load("{:url('black')}", data, 'post', function (ret) {
+                if (ret.code < 1) $.msg.error(ret.info, 3, function () {
+                    $('#UserData').trigger('reload'); // 操作异常时重载数据
+                });
+                return false;
+            }, false);
+        });
+    });
+</script>
+
+<!-- 数据状态切换模板 -->
+<script type="text/html" id="StatusSwitchTpl">
+    <!--{if auth("black")}-->
+    <input type="checkbox" value="{{d.openid}}" lay-skin="switch" lay-text="已拉黑|未拉黑" lay-filter="StatusSwitch" {{d.is_black>0?'checked':''}}>
+    <!--{else}-->
+    {{d.status ? '<b class="color-red">已拉黑</b>' : '<b class="color-green">未拉黑</b>'}}
+    <!--{/if}-->
+</script>
+
+<script type="text/html" id="toolbar">
+    <!--{if auth("remove")}-->
+    <a class="layui-btn layui-btn-sm layui-btn-danger" data-action="{:url('remove')}" data-value="id#{{d.id}}" data-confirm="确定要删除该用户吗?">删 除</a>
+    <!--{/if}-->
+</script>
+{/block}
\ No newline at end of file