Quellcode durchsuchen

添加表格模板渲染条目功能
新增表格模板示例
新增表格未加载完成禁止提交
新增控制台图表动态加载的功能
修改requrejs加载超时为30秒
修复commonsearch存在多个时导致搜索框无法正常加载的BUG
优化后台微信管理中菜单管理的排版问题
优化语言包文字显示

Karson vor 8 Jahren
Ursprung
Commit
260034a732

+ 60 - 0
application/admin/controller/example/Tabletemplate.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace app\admin\controller\example;
+
+use app\common\controller\Backend;
+
+/**
+ * 表格模板示例
+ *
+ * @icon fa fa-table
+ * @remark 可以通过使用表格模板将表格中的行渲染成一样的展现方式,基于此功能可以任意定制自己想要的展示列表
+ */
+class Tabletemplate extends Backend
+{
+
+    protected $model = null;
+
+    public function _initialize()
+    {
+        parent::_initialize();
+        $this->model = model('AdminLog');
+    }
+
+    /**
+     * 查看
+     */
+    public function index()
+    {
+        if ($this->request->isAjax())
+        {
+            list($where, $sort, $order, $offset, $limit) = $this->buildparams(NULL);
+            $total = $this->model
+                    ->where($where)
+                    ->order($sort, $order)
+                    ->count();
+            $list = $this->model
+                    ->where($where)
+                    ->order($sort, $order)
+                    ->limit($offset, $limit)
+                    ->select();
+            $result = array("total" => $total, "rows" => $list);
+
+            return json($result);
+        }
+        return $this->view->fetch();
+    }
+    
+    /**
+     * 详情
+     */
+    public function detail($ids)
+    {
+        $row = $this->model->get(['id' => $ids]);
+        if (!$row)
+            $this->error(__('No Results were found'));
+        $this->view->assign("row", $row->toArray());
+        return $this->view->fetch();
+    }
+
+}

+ 2 - 0
application/admin/lang/zh-cn.php

@@ -69,6 +69,7 @@ return [
     'Status'                                                => '状态',
     'Operate'                                               => '操作',
     'Append'                                                => '追加',
+    'Select'                                                => '选择',
     'Memo'                                                  => '备注',
     'Parent'                                                => '父级',
     'Params'                                                => '参数',
@@ -78,6 +79,7 @@ return [
     'Begin time'                                            => '开始时间',
     'End time'                                              => '结束时间',
     'Create time'                                           => '创建时间',
+    'Update time'                                           => '更新时间',
     'Flag'                                                  => '标志',
     'Redirect now'                                          => '立即跳转',
     'Operation completed'                                   => '操作成功!',

+ 7 - 0
application/admin/lang/zh-cn/wechat/autoreply.php

@@ -0,0 +1,7 @@
+<?php
+
+return [
+    'Text'      => '文本',
+    'Event key' => '响应标识',
+    'Remark' => '备注',
+];

+ 8 - 0
application/admin/lang/zh-cn/wechat/response.php

@@ -0,0 +1,8 @@
+<?php
+
+return [
+    'Resource title' => '资源标题',
+    'Event key'      => '事件标识',
+    'Text'           => '文本',
+    'App'            => '应用',
+];

+ 2 - 2
application/admin/view/common/header.html

@@ -1,9 +1,9 @@
 <!-- Logo -->
 <a href="javascript:;" class="logo">
     <!-- 迷你模式下Logo的大小为50X50 -->
-    <span class="logo-mini"><b>F</b>AST</span>
+    <span class="logo-mini">{$site.name|mb_substr=0,4|strtoupper}</span>
     <!-- 普通模式下Logo -->
-    <span class="logo-lg"><b>Fast</b>Admin</span>
+    <span class="logo-lg"><b>{$site.name|mb_substr=0,4}</b>{$site.name|mb_substr=4}</span>
 </a>
 <!-- 顶部通栏样式 -->
 <nav class="navbar navbar-static-top">

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

@@ -8,7 +8,7 @@
                     <div id="toolbar" class="toolbar">
                         {:build_toolbar('refresh,delete')}
                         <a class="btn btn-info btn-disabled disabled btn-selected" href="javascript:;"><i class="fa fa-leaf"></i> 获取选中项</a>
-                        <a class="btn btn-warning btn-singlesearch" href="javascript:;"><i class="fa fa-leaf"></i> 单独设置搜索条件</a>
+                        <a class="btn btn-success btn-singlesearch" href="javascript:;"><i class="fa fa-leaf"></i> 单独设置搜索条件</a>
                         <div class="dropdown btn-group">
                             <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> <?= __('More') ?></a>
                             <ul class="dropdown-menu text-left" role="menu">

+ 67 - 0
application/admin/view/example/tabletemplate/index.html

@@ -0,0 +1,67 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        {:build_toolbar('refresh,delete')}
+                        <a class="btn btn-info btn-disabled disabled btn-selected" href="javascript:;"><i class="fa fa-leaf"></i> 获取选中项</a>
+                        <a class="btn btn-success btn-toggle-view" href="javascript:;"><i class="fa fa-leaf"></i> 切换视图</a>
+                    </div>
+                    <table id="table" class="table table-striped table-hover" width="100%">
+
+                    </table>
+
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>
+<style type="text/css">
+    .example {
+        height:100%;position: relative;
+    }
+    .example > span {
+        position:absolute;left:15px;top:15px;
+    }
+</style>
+
+<script id="itemtpl" type="text/html">
+    <!--
+    如果启用了templateView,默认调用的是itemtpl这个模板,可以通过设置templateFormatter来修改
+    在当前模板中可以使用三个变量(item:行数据,i:当前第几行,data:所有的行数据)
+    此模板引擎使用的是art-template的native,可参考官方文档
+    -->
+
+    <div class="col-sm-4 col-md-3">
+        <!--下面四行是为了展示随机图片和标签,可移除-->
+        <% var imagearr = ['https://ws2.sinaimg.cn/large/006tNc79gy1fgphwokqt9j30dw0990tb.jpg', 'https://ws2.sinaimg.cn/large/006tNc79gy1fgphwt8nq8j30e609f3z4.jpg', 'https://ws1.sinaimg.cn/large/006tNc79gy1fgphwn44hvj30go0b5myb.jpg', 'https://ws1.sinaimg.cn/large/006tNc79gy1fgphwnl37mj30dw09agmg.jpg', 'https://ws3.sinaimg.cn/large/006tNc79gy1fgphwqsvh6j30go0b576c.jpg']; %>
+        <% var image = imagearr[item.id % 5]; %>
+        <% var labelarr = ['primary', 'success', 'info', 'danger', 'warning']; %>
+        <% var label = labelarr[item.id % 5]; %>
+        <div class="thumbnail example">
+            <span class="btn btn-<%=label%>">ID:<%=item.id%></span>
+            <img src="<%=image%>" class="img-responsive" alt="<%=item.title%>">
+            <div class="caption">
+                <h4><%=item.title?item.title:'无'%></h4>
+                <p class="text-muted">操作者IP:<%=item.ip%></p>
+                <p class="text-muted">操作时间:<%=Moment(item.createtime).format("YYYY-MM-DD HH:mm:ss")%></p>
+                <p>
+                    <!--详情的事件需要在JS中手动绑定-->
+                    <a href="#" class="btn btn-primary btn-success btn-detail" data-id="<%=item.id%>"><i class="fa fa-camera"></i> 详情</a> 
+
+                    <!--如果需要响应编辑或删除事件,可以给元素添加 btn-edit或btn-del的类和data-id这个属性值-->
+                    <a href="#" class="btn btn-primary btn-edit" data-id="<%=item.id%>"><i class="fa fa-pencil"></i> 编辑</a> 
+                    <a href="#" class="btn btn-danger btn-del" data-id="<%=item.id%>"><i class="fa fa-times"></i> 删除</a>
+                    <span class="pull-right" style="margin-top:10px;">
+                        <!--如果需要多选操作,请确保有下面的checkbox元素存在,可移除-->
+                        <input name="checkbox" data-id="<%=item.id%>" type="checkbox" />
+                    </span>
+                </p>
+            </div>
+        </div>
+    </div>
+</script>

+ 1 - 1
application/admin/view/general/attachment/add.html

@@ -23,7 +23,7 @@
     <div class="form-group">
         <label for="c-local" class="control-label col-xs-12 col-sm-2"></label>
         <div class="col-xs-12 col-sm-8">
-            <button id="plupload-local" class="btn btn-primary plupload" data-input-id="c-local" data-url="{:url('ajax/upload')}" data-after-upload="afteruploadcallback"><i class="fa fa-upload"></i> {:__("Upload to local")}</button>
+            <button id="plupload-local" class="btn btn-primary plupload" data-input-id="c-local" data-url="{:url('ajax/upload')}"><i class="fa fa-upload"></i> {:__("Upload to local")}</button>
         </div>
     </div>
 

+ 11 - 0
application/admin/view/general/config/index.html

@@ -1,3 +1,14 @@
+<style type="text/css">
+    @media (max-width: 375px) {
+        .edit-form tr td input{width:100%;}
+        .edit-form tr th:first-child,.edit-form tr td:first-child{
+            width:20%;
+        }
+        .edit-form tr th:last-child,.edit-form tr td:last-child{
+            display: none;
+        }
+    }
+</style>
 <div class="panel panel-default panel-intro">
     <div class="panel-heading">
         <div class="panel-lead"><em>系统配置</em>可以在此增改系统的变量和分组,也可以自定义分组和变量,如果需要删除请从数据库中删除</div>

+ 47 - 45
application/admin/view/wechat/menu/index.html

@@ -6,7 +6,7 @@
         <div id="myTabContent" class="tab-content">
             <div class="tab-pane fade active in" id="one">
                 <div class="widget-body no-padding">
-                    <div class="weixin-menu-setting">
+                    <div class="weixin-menu-setting clearfix">
                         <div class="mobile-menu-preview">
                             <div class="mobile-head-title">{$site.name}</div>
                             <ul class="menu-list" id="menu-list">
@@ -15,53 +15,55 @@
                                 </li>
                             </ul>
                         </div>
-                        <div class="weixin-content">
-                            <div class="item-info">
-                                <form id="form-item" class="form-item" data-value="" >
-                                    <div class="item-head">
-                                        <h4 id="current-item-name">添加子菜单</h4>
-                                        <div class="item-delete"><a href="javascript:;" id="item_delete">删除菜单</a></div>
-                                    </div>
-                                    <div style="margin-top: 20px;">
-                                        <dl>
-                                            <dt id="current-item-option"><span class="is-sub-item">子</span>菜单标题:</dt>
-                                            <dd><div class="input-box"><input id="item_title" name="item-title" type="text" value=""></div></dd>
-                                        </dl>
-                                        <dl class="is-item">
-                                            <dt id="current-item-type"><span class="is-sub-item">子</span>菜单内容:</dt>
-                                            <dd>
-                                                <input id="type1" type="radio" name="type" value="click"><label for="type1" data-editing="1"><span class="lbl_content">发送消息</span></label>
-                                                <input id="type2" type="radio" name="type" value="view" ><label for="type2"  data-editing="1"><span class="lbl_content">跳转网页</span></label>
-                                                <input id="type3" type="radio" name="type" value="scancode_push"><label for="type3" data-editing="1"><span class="lbl_content">扫码推</span></label>
-                                                <input id="type4" type="radio" name="type" value="scancode_waitmsg"><label for="type4" data-editing="1"><span class="lbl_content">扫码推提示框</span></label>
-                                                <input id="type5" type="radio" name="type" value="pic_sysphoto"><label for="type5" data-editing="1"><span class="lbl_content">拍照发图</span></label>
-                                                <input id="type6" type="radio" name="type" value="pic_photo_or_album"><label for="type6" data-editing="1"><span class="lbl_content">拍照相册发图</span></label>
-                                                <input id="type7" type="radio" name="type" value="pic_weixin"><label for="type7" data-editing="1"><span class="lbl_content">相册发图</span></label>
-                                                <input id="type8" type="radio" name="type" value="location_select"><label for="type8" data-editing="1"><span class="lbl_content">地理位置选择</span></label>
-                                            </dd>
-                                        </dl>
-                                        <div id="menu-content" class="is-item">
-                                            <div class="viewbox is-view">
-                                                <p class="menu-content-tips">点击该<span class="is-sub-item">子</span>菜单会跳到以下链接</p>
-                                                <dl>
-                                                    <dt>页面地址:</dt>
-                                                    <dd><div class="input-box"><input type="text" id="url" name="url"></div>
-                                                    </dd>
-                                                </dl>
-                                            </div>
-                                            <div class="clickbox is-click" style="display: none;">
-                                                <input type="hidden" name="key" id="key" value="" />
-                                                <span class="create-click"><a href="{:url('wechat.response/select')}" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a></span>
-                                                <span class="create-click"><a href="{:url('wechat.response/add')}" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a></span>
+                        <div class="weixin-body">
+                            <div class="weixin-content" style="display:none">
+                                <div class="item-info">
+                                    <form id="form-item" class="form-item" data-value="" >
+                                        <div class="item-head">
+                                            <h4 id="current-item-name">添加子菜单</h4>
+                                            <div class="item-delete"><a href="javascript:;" id="item_delete">删除菜单</a></div>
+                                        </div>
+                                        <div style="margin-top: 20px;">
+                                            <dl>
+                                                <dt id="current-item-option"><span class="is-sub-item">子</span>菜单标题:</dt>
+                                                <dd><div class="input-box"><input id="item_title" name="item-title" type="text" value=""></div></dd>
+                                            </dl>
+                                            <dl class="is-item">
+                                                <dt id="current-item-type"><span class="is-sub-item">子</span>菜单内容:</dt>
+                                                <dd>
+                                                    <input id="type1" type="radio" name="type" value="click"><label for="type1" data-editing="1"><span class="lbl_content">发送消息</span></label>
+                                                    <input id="type2" type="radio" name="type" value="view" ><label for="type2"  data-editing="1"><span class="lbl_content">跳转网页</span></label>
+                                                    <input id="type3" type="radio" name="type" value="scancode_push"><label for="type3" data-editing="1"><span class="lbl_content">扫码推</span></label>
+                                                    <input id="type4" type="radio" name="type" value="scancode_waitmsg"><label for="type4" data-editing="1"><span class="lbl_content">扫码推提示框</span></label>
+                                                    <input id="type5" type="radio" name="type" value="pic_sysphoto"><label for="type5" data-editing="1"><span class="lbl_content">拍照发图</span></label>
+                                                    <input id="type6" type="radio" name="type" value="pic_photo_or_album"><label for="type6" data-editing="1"><span class="lbl_content">拍照相册发图</span></label>
+                                                    <input id="type7" type="radio" name="type" value="pic_weixin"><label for="type7" data-editing="1"><span class="lbl_content">相册发图</span></label>
+                                                    <input id="type8" type="radio" name="type" value="location_select"><label for="type8" data-editing="1"><span class="lbl_content">地理位置选择</span></label>
+                                                </dd>
+                                            </dl>
+                                            <div id="menu-content" class="is-item">
+                                                <div class="viewbox is-view">
+                                                    <p class="menu-content-tips">点击该<span class="is-sub-item">子</span>菜单会跳到以下链接</p>
+                                                    <dl>
+                                                        <dt>页面地址:</dt>
+                                                        <dd><div class="input-box"><input type="text" id="url" name="url"></div>
+                                                        </dd>
+                                                    </dl>
+                                                </div>
+                                                <div class="clickbox is-click" style="display: none;">
+                                                    <input type="hidden" name="key" id="key" value="" />
+                                                    <span class="create-click"><a href="{:url('wechat.response/select')}" id="select-resources"><i class="weixin-icon big-add-gray"></i><strong>选择现有资源</strong></a></span>
+                                                    <span class="create-click"><a href="{:url('wechat.response/add')}" id="add-resources"><i class="weixin-icon big-add-gray"></i><strong>添加新资源</strong></a></span>
+                                                </div>
                                             </div>
                                         </div>
-                                    </div>
-                                </form>
-                            </div>
+                                    </form>
+                                </div>
 
-                        </div>
-                        <div class="no-weixin-content">
-                            点击左侧菜单进行编辑操作
+                            </div>
+                            <div class="no-weixin-content">
+                                点击左侧菜单进行编辑操作
+                            </div>
                         </div>
                     </div>
                     <div class="row">

+ 2 - 0
application/extra/site.php

@@ -28,4 +28,6 @@ return array (
     'user' => '会员配置',
     'example' => '示例分组',
   ),
+  'aaaa' => '',
+  'ffff' => 'key2',
 );

+ 22 - 0
public/assets/css/backend.css

@@ -77,6 +77,20 @@ body.is-dialog {
   -webkit-overflow-scrolling: touch;
   overflow: auto;
 }
+@media only screen and (min-width: 481px) {
+  .row-flex {
+    display: flex;
+    flex-wrap: wrap;
+  }
+  .row-flex > [class*='col-'] {
+    display: flex;
+    flex-direction: column;
+  }
+  .row-flex.row:after,
+  .row-flex.row:before {
+    display: flex;
+  }
+}
 .common-search-table {
   min-height: 20px;
   padding: 15px;
@@ -86,6 +100,14 @@ body.is-dialog {
 .searchit {
   border-bottom: 1px dashed #3c8dbc;
 }
+table.table-template {
+  overflow: hidden;
+}
+.sp_container .msg-box {
+  position: absolute;
+  right: 0;
+  top: 0;
+}
 .toast-top-right-index {
   top: 62px;
   right: 12px;

Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 1
public/assets/css/backend.min.css


+ 20 - 11
public/assets/css/wechat/menu.css

@@ -1,7 +1,7 @@
 .weixin-menu-setting{
     margin:0;
-    position:relative;
     margin-bottom:10px;
+    width:100%;
 }
 .mobile-head-title{
     color: #fff;
@@ -15,12 +15,12 @@
     word-wrap: normal;
     margin: 0 40px 0 70px;
 }
+.weixin-body {
+    padding:0;
+    margin:0;
+    margin-left:337px;
+}
 .weixin-content,.no-weixin-content{
-    position:absolute;
-    left: 335px;
-    top:0;
-    right:0px;
-    bottom:0;
     background-color: #f4f5f9;
     border: 1px solid #e7e7eb;
     padding:15px;
@@ -31,7 +31,12 @@
     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;
@@ -41,13 +46,16 @@
     margin-bottom: 20px;
 }
 .mobile-menu-preview{
-    position: relative;
+    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;
@@ -257,7 +265,7 @@ table.weixin-form td{
     margin:10px 0;
 }
 .form-item dl dt{
-    width:6em;
+    width:90px;
     height: 30px;
     line-height: 30px;
     text-align: right;
@@ -267,12 +275,12 @@ table.weixin-form td{
     left:0;
     bottom:0;
     display:block;
-
 }
 .form-item dl dd{
     position:relative;
     display:block;
-    margin-left: 7em;
+    margin-left: 90px;
+    line-height: 30px;
 }
 .form-item .input-box {
     display: inline-block;
@@ -297,6 +305,7 @@ table.weixin-form td{
     background-color: transparent;
     border: 0;
     outline: 0;
+    height:30px;
 }
 
 .clickbox{

+ 3 - 3
public/assets/js/backend.js

@@ -204,7 +204,7 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'moment'], function ($
                 if (layerfooter.size() > 0) {
                     footer.on("click", ".btn", function () {
                         if ($(this).hasClass("disabled") || $(this).parent().hasClass("disabled")) {
-                            //return;
+                            return;
                         }
                         $(".btn:eq(" + $(this).index() + ")", layerfooter).trigger("click");
                     });
@@ -347,13 +347,13 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'moment'], function ($
             Toastr.options = Backend.config.toastr;
             //点击包含.btn-dialog的元素时弹出dialog
             $(document).on('click', '.btn-dialog,.dialogit', function (e) {
-                Backend.api.open(Backend.api.fixurl($(this).attr('href')), $(this).attr('title'));
                 e.preventDefault();
+                Backend.api.open(Backend.api.fixurl($(this).attr('href')), $(this).attr('title'));
             });
             //点击包含.btn-addtabs的元素时事件
             $(document).on('click', '.btn-addtabs,.addtabsit', function (e) {
-                Backend.api.addtabs($(this).attr("href"), $(this).attr("title"));
                 e.preventDefault();
+                Backend.api.addtabs($(this).attr("href"), $(this).attr("title"));
             });
             //点击加入到Shortcut
             $(document).on('click', '#ribbon ol li:last a[data-url]', function (e) {

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

@@ -36,7 +36,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'table', 'echarts', 'echart
                         left: 'left',
                         top: 'top',
                         right: '10',
-                        bottom:30
+                        bottom: 30
                     }],
                 series: [{
                         name: '成交',
@@ -48,7 +48,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'table', 'echarts', 'echart
                         },
                         lineStyle: {
                             normal: {
-                                width:1.5
+                                width: 1.5
                             }
                         },
                         data: Orderdata.paydata
@@ -63,7 +63,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'table', 'echarts', 'echart
                         },
                         lineStyle: {
                             normal: {
-                                width:1.5
+                                width: 1.5
                             }
                         },
                         data: Orderdata.createdata
@@ -72,8 +72,37 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'table', 'echarts', 'echart
 
             // 使用刚指定的配置项和数据显示图表。
             myChart.setOption(option);
-            $(window).resize(function(){
-               myChart.resize();
+
+            //动态添加数据,可以通过Ajax获取数据然后填充
+            setInterval(function () {
+                Orderdata.column.push((new Date()).toLocaleTimeString().replace(/^\D*/, ''));
+                var amount = Math.floor(Math.random() * 200) + 20;
+                Orderdata.createdata.push(amount);
+                Orderdata.paydata.push(Math.floor(Math.random() * amount) + 1);
+
+                //按自己需求可以取消这个限制
+                if (Orderdata.column.length >= 20) {
+                    //移除最开始的一条数据
+                    Orderdata.column.shift();
+                    Orderdata.paydata.shift();
+                    Orderdata.createdata.shift();
+                }
+                myChart.setOption({
+                    xAxis: {
+                        data: Orderdata.column
+                    },
+                    series: [{
+                            name: '成交',
+                            data: Orderdata.paydata
+                        },
+                        {
+                            name: '下单',
+                            data: Orderdata.createdata
+                        }]
+                });
+            }, 2000);
+            $(window).resize(function () {
+                myChart.resize();
             });
         }
     };

+ 7 - 3
public/assets/js/backend/example/bootstraptable.js

@@ -20,13 +20,16 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                 url: $.fn.bootstrapTable.defaults.extend.index_url,
                 columns: [
                     [
+                        //该列为复选框字段,如果后台的返回state值将会默认选中
                         {field: 'state', checkbox: true, },
                         {field: 'id', title: 'ID', operate: false},
+                        //默认隐藏该列
+                        {field: 'admin_id', title: __('Admin_id'), visible: false, operate: false},
                         //直接响应搜索
                         {field: 'username', title: __('Username'), formatter: Table.api.formatter.search},
                         //模糊搜索
                         {field: 'title', title: __('Title'), operate: 'LIKE %...%', placeholder: '模糊搜索,*表示任意字符', style: 'width:200px'},
-                        //通过Ajax渲染searchList
+                        //通过Ajax渲染searchList,也可以使用JSON数据
                         {field: 'url', title: __('Url'), align: 'left', searchList: $.getJSON('ajax/typeahead?search=a&field=row[user_id]'), formatter: Controller.api.formatter.url},
                         //点击IP时同时执行搜索此IP,同时普通搜索使用下拉列表的形式
                         {field: 'ip', title: __('IP'), searchList: ['127.0.0.1', '127.0.0.2'], events: Controller.api.events.ip, formatter: Controller.api.formatter.ip},
@@ -70,7 +73,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
             bindevent: function () {
                 Form.api.bindevent($("form[role=form]"));
             },
-            formatter: {
+            formatter: {//渲染的方法
                 url: function (value, row, index) {
                     return '<div class="input-group input-group-sm" style="width:250px;"><input type="text" class="form-control input-sm" value="' + value + '"><span class="input-group-btn input-group-sm"><a href="' + value + '" target="_blank" class="btn btn-default btn-sm"><i class="fa fa-link"></i></a></span></div>';
                 },
@@ -89,8 +92,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                             + Table.api.formatter.operate(value, row, index, $("#table"));
                 },
             },
-            events: {
+            events: {//绑定事件的方法
                 ip: {
+                    //格式为:方法名+空格+DOM元素
                     'click .btn-ip': function (e, value, row, index) {
                         var options = $("#table").bootstrapTable('getOptions');
                         //这里我们手动将数据填充到表单然后提交

+ 126 - 0
public/assets/js/backend/example/tabletemplate.js

@@ -0,0 +1,126 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function ($, undefined, Backend, Table, Form, Template) {
+
+    var Controller = {
+        index: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    index_url: 'example/tabletemplate/index',
+                    add_url: '',
+                    edit_url: '',
+                    del_url: 'example/tabletemplate/del',
+                    multi_url: '',
+                }
+            });
+
+            var table = $("#table");
+
+            Template.helper("Moment", Moment);
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                templateView: true,
+                columns: [
+                    [
+                        {field: 'state', checkbox: true, },
+                        {field: 'id', title: 'ID', operate: false},
+                        //直接响应搜索
+                        {field: 'username', title: __('Username'), formatter: Table.api.formatter.search},
+                        //模糊搜索
+                        {field: 'title', title: __('Title'), operate: 'LIKE %...%', placeholder: '模糊搜索,*表示任意字符', style: 'width:200px'},
+                        //通过Ajax渲染searchList
+                        {field: 'url', title: __('Url'), align: 'left', searchList: $.getJSON('ajax/typeahead?search=a&field=row[user_id]'), formatter: Controller.api.formatter.url},
+                        //点击IP时同时执行搜索此IP,同时普通搜索使用下拉列表的形式
+                        {field: 'ip', title: __('IP'), searchList: ['127.0.0.1', '127.0.0.2'], events: Controller.api.events.ip, formatter: Controller.api.formatter.ip},
+                        //browser是一个不存在的字段
+                        //通过formatter来渲染数据,同时为它添加上事件
+                        {field: 'browser', title: __('Browser'), operate: false, events: Controller.api.events.browser, formatter: Controller.api.formatter.browser},
+                        //启用时间段搜索
+                        {field: 'createtime', title: __('Create time'), formatter: Table.api.formatter.datetime, operate: 'BETWEEN', type: 'datetime', addclass: 'datetimepicker', data: 'data-date-format="YYYY-MM-DD HH:mm:ss"'},
+                        //我们向操作栏额外添加上一个详情按钮,并保留已有的编辑和删除控制,同时为这个按钮添加上点击事件
+                        {field: 'operate', title: __('Operate'), events: Controller.api.events.operate, formatter: Controller.api.formatter.operate}
+                    ],
+                ],
+                //禁用默认搜索
+                search: false,
+                //启用普通表单搜索
+                commonSearch: false,
+                //可以控制是否默认显示搜索单表,false则隐藏,默认为false
+                searchFormVisible: false
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+
+            //指定搜索条件
+            $(document).on("click", ".btn-toggle-view", function () {
+                var options = table.bootstrapTable('getOptions');
+                table.bootstrapTable('refreshOptions', {templateView: !options.templateView});
+            });
+
+            //点击详情
+            $(document).on("click", ".btn-detail[data-id]", function () {
+                Backend.api.open('example/bootstraptable/detail/ids/' + $(this).data('id'), __('Detail'));
+            });
+
+            //获取选中项
+            $(document).on("click", ".btn-selected", function () {
+                //在templateView的模式下不能调用table.bootstrapTable('getSelections')来获取选中的ID,只能通过下面的Table.api.selectedids来获取
+                Layer.alert(JSON.stringify(Table.api.selectedids(table)));
+            });
+        },
+        add: function () {
+            Controller.api.bindevent();
+        },
+        edit: function () {
+            Controller.api.bindevent();
+        },
+        api: {
+            bindevent: function () {
+                Form.api.bindevent($("form[role=form]"));
+            },
+            formatter: {
+                url: function (value, row, index) {
+                    return '<div class="input-group input-group-sm" style="width:250px;"><input type="text" class="form-control input-sm" value="' + value + '"><span class="input-group-btn input-group-sm"><a href="' + value + '" target="_blank" class="btn btn-default btn-sm"><i class="fa fa-link"></i></a></span></div>';
+                },
+                ip: function (value, row, index) {
+                    return '<a class="btn btn-xs btn-ip bg-success"><i class="fa fa-map-marker"></i> ' + value + '</a>';
+                },
+                browser: function (value, row, index) {
+                    //这里我们直接使用row的数据
+                    return '<a class="btn btn-xs btn-browser">' + row.useragent.split(" ")[0] + '</a>';
+                },
+                operate: function (value, row, index) {
+                    //返回字符串加上Table.api.formatter.operate的结果
+                    //默认需要按需显示排序/编辑/删除按钮,则需要在Table.api.formatter.operate将table传入
+                    //传入了table以后如果edit_url为空则不显示编辑按钮,如果del_url为空则不显显删除按钮
+                    return '<a class="btn btn-info btn-xs btn-detail"><i class="fa fa-list"></i> ' + __('Detail') + '</a> '
+                            + Table.api.formatter.operate(value, row, index, $("#table"));
+                },
+            },
+            events: {
+                ip: {
+                    'click .btn-ip': function (e, value, row, index) {
+                        var options = $("#table").bootstrapTable('getOptions');
+                        //这里我们手动将数据填充到表单然后提交
+                        $("#commonSearchContent_" + options.idTable + " form [name='ip']").val(value);
+                        $("#commonSearchContent_" + options.idTable + " form").trigger('submit');
+                        Toastr.info("执行了自定义搜索操作");
+                    }
+                },
+                browser: {
+                    'click .btn-browser': function (e, value, row, index) {
+                        Layer.alert("该行数据为: <code>" + JSON.stringify(row) + "</code>");
+                    }
+                },
+                operate: $.extend({
+                    'click .btn-detail': function (e, value, row, index) {
+                        Backend.api.open('example/tabletemplate/detail/ids/' + row['id'], __('Detail'));
+                    }
+                }, Table.api.events.operate)
+            }
+        }
+    };
+    return Controller;
+});

+ 2 - 1
public/assets/js/backend/wechat/autoreply.js

@@ -27,7 +27,8 @@ define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefin
                         {field: 'text', title: __('Text')},
                         {field: 'eventkey', title: __('Event key')},
                         {field: 'remark', title: __('Remark')},
-                        {field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime},
+                        {field: 'createtime', title: __('Create time'), formatter: Table.api.formatter.datetime},
+                        {field: 'updatetime', title: __('Update time'), formatter: Table.api.formatter.datetime},
                         {field: 'status', title: __('Status'), formatter: Table.api.formatter.status},
                         {field: 'operate', title: __('Operate'), events: Table.api.events.operate, formatter: Table.api.formatter.operate}
                     ]

+ 15 - 23
public/assets/js/bootstrap-table-commonsearch.js

@@ -8,19 +8,19 @@
 !function ($) {
     'use strict';
 
-    var firstLoad = false, ColumnsForSearch = [];
+    var ColumnsForSearch = [];
 
     var sprintf = $.fn.bootstrapTable.utils.sprintf;
 
     var initCommonSearch = function (pColumns, that) {
-        var vFormCommon = createFormCommon(pColumns, that), timeoutId = 0;
+        var vFormCommon = createFormCommon(pColumns, that);
 
-        var vModal = sprintf("<div id=\"commonSearchContent_%s\" class=\"common-search-table %s\">", that.options.idTable, that.options.searchFormVisible ? "" : "hidden");
+        var vModal = sprintf("<div class=\"commonsearch-table %s\">", that.options.searchFormVisible ? "" : "hidden");
         vModal += vFormCommon.join('');
         vModal += "</div>";
         that.$container.prepend($(vModal));
 
-        var form = $("#commonSearchForm" + "_" + that.options.idTable);
+        var form = $("form.form-commonsearch", that.$container);
 
         //绑定日期时间元素事件
         if ($(".datetimepicker", form).size() > 0) {
@@ -48,12 +48,13 @@
 
         // 表单提交
         form.on("submit", function (event) {
+            event.preventDefault();
             that.onColumnCommonSearch();
             return false;
         });
 
         // 重置搜索
-        form.on("click", "#btnResetCommon" + "_" + that.options.idTable, function (event) {
+        form.on("click", "button[type=reset]", function (event) {
             form[0].reset();
             that.onColumnCommonSearch();
         });
@@ -63,7 +64,7 @@
     var createFormCommon = function (pColumns, that) {
         var htmlForm = [];
         var opList = ['=', '>', '>=', '<', '<=', '!=', 'LIKE', 'LIKE %...%', 'NOT LIKE', 'IN(...)', 'NOT IN(...)', 'BETWEEN', 'NOT BETWEEN', 'IS NULL', 'IS NOT NULL'];
-        htmlForm.push(sprintf('<form class="form-inline" id="commonSearchForm_%s" action="%s" >', that.options.idTable, that.options.actionForm));
+        htmlForm.push(sprintf('<form class="form-inline form-commonsearch" action="%s" >', that.options.actionForm));
         htmlForm.push('<fieldset>');
         if (that.options.titleForm.length > 0)
             htmlForm.push(sprintf("<legend>%s</legend>", that.options.titleForm));
@@ -83,7 +84,7 @@
                 if (vObjCol.searchList) {
                     if (typeof vObjCol.searchList === 'object' && typeof vObjCol.searchList.then === 'function') {
                         htmlForm.push(sprintf('<select class="form-control" name="%s" %s>%s</select>', vObjCol.field, style, sprintf('<option value="">%s</option>', that.options.formatCommonChoose())));
-                        (function (vObjCol, options) {
+                        (function (vObjCol, that) {
                             $.when(vObjCol.searchList).done(function (ret) {
                                 if (ret.data && ret.data.searchlist && $.isArray(ret.data.searchlist)) {
                                     var optionList = [];
@@ -91,10 +92,10 @@
                                         var isSelect = value.id === vObjCol.defaultValue ? 'selected' : '';
                                         optionList.push(sprintf("<option value='" + value.id + "' %s>" + value.name + "</option>", isSelect));
                                     });
-                                    $("#commonSearchForm_" + options.idTable + " select[name='" + vObjCol.field + "']").append(optionList.join(''));
+                                    $("form.form-commonsearch select[name='" + vObjCol.field + "']", that.$container).append(optionList.join(''));
                                 }
                             });
-                        })(vObjCol, that.options);
+                        })(vObjCol, that);
                     } else if (typeof vObjCol.searchList == 'function') {
                         htmlForm.push(vObjCol.searchList.call(this, vObjCol));
                     } else {
@@ -139,29 +140,27 @@
         var searchReset = that.options.formatCommonResetButton();
         htmlBtn.push('<div class="form-group" style="margin:5px">');
         htmlBtn.push('<div class="col-sm-12 text-center">');
-        htmlBtn.push(sprintf('<button type="submit" id="btnSubmitCommon%s" class="btn btn-success" >%s</button> ', "_" + that.options.idTable, searchSubmit));
-        htmlBtn.push(sprintf('<button type="button" id="btnResetCommon%s" class="btn btn-default" >%s</button> ', "_" + that.options.idTable, searchReset));
+        htmlBtn.push(sprintf('<button type="submit" class="btn btn-success" >%s</button> ', searchSubmit));
+        htmlBtn.push(sprintf('<button type="reset" class="btn btn-default" >%s</button> ', searchReset));
         htmlBtn.push('</div>');
         htmlBtn.push('</div>');
         return htmlBtn;
     };
 
     var isSearchAvailble = function (that) {
+
         //只支持服务端搜索
         if (!that.options.commonSearch || that.options.sidePagination != 'server' || !that.options.url) {
             return false;
         }
 
-        if (!that.options.idTable) {
-            return false;
-        }
         return true;
     };
 
     var getSearchQuery = function (that) {
         var op = {};
         var filter = {};
-        $("#commonSearchContent_" + that.options.idTable + " input.operate").each(function (i) {
+        $("form.form-commonsearch input.operate", that.$container).each(function (i) {
             var name = $(this).data("name");
             var sym = $(this).val();
             var obj = $("[name='" + name + "']");
@@ -206,7 +205,6 @@
         commonSearch: false,
         titleForm: "Common search",
         actionForm: "",
-        idTable: undefined,
         searchFormVisible: true,
         searchClass: 'searchit',
         renderDefault: true,
@@ -266,7 +264,7 @@
 
         initCommonSearch(that.columns, that);
 
-        var searchContainer = $("#commonSearchContent_" + that.options.idTable);
+        var searchContainer = $(".commonsearch-table", that.$container);
 
         that.$toolbar.find('button[name="commonSearch"]')
                 .off('click').on('click', function () {
@@ -299,12 +297,6 @@
         if (!isSearchAvailble(this)) {
             return;
         }
-        if (!firstLoad) {
-            var height = parseInt($(".bootstrap-table").height());
-            height += 10;
-            $("#" + this.options.idTable).bootstrapTable("resetView", {height: height});
-            firstLoad = true;
-        }
     };
 
     BootstrapTable.prototype.initSearch = function () {

+ 60 - 0
public/assets/js/bootstrap-table-template.js

@@ -0,0 +1,60 @@
+!function ($) {
+    'use strict';
+
+    $.extend($.fn.bootstrapTable.defaults, {
+        templateView: false,
+        templateFormatter: "itemtpl",
+        templateParentClass: "row row-flex",
+        templateTableClass: "table-template",
+
+    });
+
+    var BootstrapTable = $.fn.bootstrapTable.Constructor,
+            _initContainer = BootstrapTable.prototype.initContainer,
+            _initBody = BootstrapTable.prototype.initBody,
+            _initRow = BootstrapTable.prototype.initRow;
+
+    BootstrapTable.prototype.initContainer = function () {
+        _initContainer.apply(this, Array.prototype.slice.apply(arguments));
+        var that = this;
+        if (!that.options.templateView) {
+            return;
+        }
+
+    };
+
+    BootstrapTable.prototype.initBody = function () {
+        var that = this;
+        $.extend(that.options, {
+            showHeader: !that.options.templateView ? $.fn.bootstrapTable.defaults.showHeader : false,
+            showFooter: !that.options.templateView ? $.fn.bootstrapTable.defaults.showFooter : false,
+        });
+        $(that.$el).toggleClass(that.options.templateTableClass, that.options.templateView);
+        
+        _initBody.apply(this, Array.prototype.slice.apply(arguments));
+
+        if (!that.options.templateView) {
+            return;
+        } else {
+            //由于Bootstrap是基于Table的,添加一个父类容器
+            $("> *", that.$body).wrapAll($("<div />").addClass(that.options.templateParentClass));
+        }
+    };
+
+    BootstrapTable.prototype.initRow = function (item, i, data, parentDom) {
+        var that = this;
+        //如果未启用则使用原生的initRow方法
+        if (!that.options.templateView) {
+            return _initRow.apply(that, Array.prototype.slice.apply(arguments));
+        }
+        var $content = '';
+        if (typeof that.options.templateFormatter === 'function') {
+            $content = that.options.templateFormatter.call(that, item, i, data);
+        } else {
+            var Template = require('template');
+            $content = Template(that.options.templateFormatter, {item: item, i: i, data: data});
+        }
+        return $content;
+    };
+
+}(jQuery);

+ 6 - 0
public/assets/js/require-backend.js

@@ -19,6 +19,7 @@ require.config({
         'echarts-theme': 'echarts-theme',
         'adminlte': 'adminlte',
         'bootstrap-table-commonsearch': 'bootstrap-table-commonsearch',
+        'bootstrap-table-template': 'bootstrap-table-template',
         //
         // 以下的包从bower的libs目录加载
         'jquery': '../libs/jquery/dist/jquery.min',
@@ -81,6 +82,10 @@ require.config({
             deps: ['bootstrap-table'],
             exports: '$.fn.bootstrapTable.defaults'
         },
+        'bootstrap-table-template': {
+            deps: ['bootstrap-table', 'template'],
+            exports: '$.fn.bootstrapTable.defaults'
+        },
         'tableexport': {
             deps: ['jquery'],
             exports: '$.fn.extend'
@@ -121,6 +126,7 @@ require.config({
             'css': '../libs/require-css/css.min'
         }
     },
+    waitSeconds: 30,
     charset: 'utf-8' // 文件编码
 });
 

Datei-Diff unterdrückt, da er zu groß ist
+ 154 - 42
public/assets/js/require-backend.min.js


+ 7 - 0
public/assets/js/require-form.js

@@ -67,6 +67,9 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'upload', 'validator'], func
                 return false;
             },
             bindevent: function (form, onBeforeSubmit, onAfterSubmit) {
+                //移除提交按钮的disabled类
+                $(".layer-footer .btn.disabled", form).removeClass("disabled");
+                //绑定表单事件
                 form.validator($.extend({
                     validClass: 'has-success',
                     invalidClass: 'has-error',
@@ -117,6 +120,10 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'upload', 'validator'], func
                             source: 'ajax/selectpage',
                         });
                     });
+                    //给隐藏的元素添加上validate验证触发事件
+                    $(form).on("change", ".selectpage-input-hidden", function () {
+                        $(this).trigger("validate");
+                    });
                 }
 
                 //绑定cxselect元素事件

+ 1 - 0
public/assets/js/require-frontend.js

@@ -114,6 +114,7 @@ require.config({
             'css': '../libs/require-css/css.min'
         }
     },
+    waitSeconds: 30,
     charset: 'utf-8' // 文件编码
 });
 

+ 1 - 0
public/assets/js/require-frontend.min.js

@@ -128,6 +128,7 @@ require.config({
             'css': '../libs/require-css/css.min'
         }
     },
+    waitSeconds: 30,
     charset: 'utf-8' // 文件编码
 });
 

+ 35 - 7
public/assets/js/require-table.js

@@ -1,4 +1,4 @@
-define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table', 'bootstrap-table-lang', 'bootstrap-table-mobile', 'bootstrap-table-export', 'bootstrap-table-commonsearch'], function ($, undefined, Backend, Toastr, Moment) {
+define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table', 'bootstrap-table-lang', 'bootstrap-table-mobile', 'bootstrap-table-export', 'bootstrap-table-commonsearch', 'bootstrap-table-template'], function ($, undefined, Backend, Toastr, Moment) {
     var Table = {
         list: {},
         // Bootstrap-table 基础配置
@@ -141,8 +141,9 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table',
                 });
 
                 // 处理选中筛选框后按钮的状态统一变更
-                table.on('check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table', function () {
-                    $(Table.config.disabledbtn, toolbar).toggleClass('disabled', !table.bootstrapTable('getSelections').length);
+                table.on('check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table fa.event.check', function () {
+                    var ids = Table.api.selectedids(table);
+                    $(Table.config.disabledbtn, toolbar).toggleClass('disabled', !ids.length);
                 });
 
                 // 刷新按钮事件
@@ -154,7 +155,7 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table',
                     var ids = Table.api.selectedids(table);
                     Backend.api.open(options.extend.add_url + "/ids" + (ids.length > 0 ? '/' : '') + ids.join(","), __('Add'));
                 });
-                // 编辑按钮事件
+                // 批量编辑按钮事件
                 $(toolbar).on('click', Table.config.editbtn, function () {
                     var ids = Table.api.selectedids(table);
                     //循环弹出多个编辑框
@@ -215,6 +216,27 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table',
                         placeHolderTemplate: ""
                     });
                 });
+                $(table).on("click", "input[data-id][name='checkbox']", function (e) {
+                    table.trigger('fa.event.check');
+                });
+                $(table).on("click", "[data-id].btn-edit", function (e) {
+                    e.preventDefault();
+                    Backend.api.open(options.extend.edit_url + "/ids/" + $(this).data("id"), __('Edit'));
+                });
+                $(table).on("click", "[data-id].btn-del", function (e) {
+                    e.preventDefault();
+                    var id = $(this).data("id");
+                    var that = this;
+                    var index = Backend.api.layer.confirm(
+                            __('Are you sure you want to delete this item?'),
+                            {icon: 3, title: __('Warning'), shadeClose: true},
+                            function () {
+                                Table.api.multi("del", id, table, that);
+                                Backend.api.layer.close(index);
+                            }
+                    );
+
+                });
 
                 var id = table.attr("id");
                 Table.list[id] = table;
@@ -355,9 +377,15 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table',
             // 获取选中的条目ID集合
             selectedids: function (table) {
                 var options = table.bootstrapTable('getOptions');
-                return $.map(table.bootstrapTable('getSelections'), function (row) {
-                    return row[options.pk];
-                });
+                if (options.templateView) {
+                    return $.map($("input[data-id][name='checkbox']:checked"), function (dom) {
+                        return $(dom).data("id");
+                    });
+                } else {
+                    return $.map(table.bootstrapTable('getSelections'), function (row) {
+                        return row[options.pk];
+                    });
+                }
             },
             // 切换复选框状态
             toggleattr: function (table) {

+ 22 - 0
public/assets/less/backend.less

@@ -100,6 +100,20 @@ body.is-dialog {
         }
     }
 }
+@media only screen and (min-width : 481px) {
+    .row-flex {
+        display: flex;
+        flex-wrap: wrap;
+    }
+    .row-flex > [class*='col-'] {
+        display: flex;
+        flex-direction: column;
+    }
+    .row-flex.row:after, 
+    .row-flex.row:before {
+        display: flex;
+    }
+}
 .common-search-table {
     min-height: 20px;
     padding: 15px;
@@ -110,6 +124,14 @@ body.is-dialog {
     border-bottom:1px dashed @link-color;
 }
 
+table.table-template{
+    overflow:hidden;
+}
+.sp_container .msg-box{
+    position: absolute;
+    right: 0;
+    top: 0;
+}
 .toast-top-right-index{
     top:62px;
     right:12px;