效果描述:点击输入框弹出table面板,面板包括标题、列表分页、检索条件、确定取消安钮、单选全选框。面板回显已选中条目,切换页时仍能显示。
思路:组件中包括面板整体样式、标题 、按钮、分页组件;页面中将搜索条件、th、tr用slot传入组件模板中,形成每页的table。监听面板打开关闭的变量进行重新渲染,点击确定时页面父组件监听事件传出选中条目的数组,符合条件则执行面板关闭及后续。点击检索按钮时通过this.$refs.comName.method(‘tag’);调用组件内检索方法、改变组件内检索条件。
技能点:主要为slot使用方法以及业务梳理,合理提取组件。外表及方法共用,仅传入url和检索条件,确定时传出选定数组。若为多个table且表头不同,则在一个父组件内通过变量控制显示与数组赋值。slot用v-if控制显示(不可v-show,此属性为控制dom元素display属性),不可出现同名slot,但slot内可用template显示不同的html内容。 slot-scope=" " 为vue2.5+中写法。

<table class="table table-hover table-bordered">
    <tbody>
        <slot name="ths"></slot>
        <slot name="item"
              v-for="item in items"
              :item="item">
        </slot>
    </tbody>
</table>
<tr slot="ths">
    <th>ID</th>
    <th>Project</th>
    <th>Version</th>
</tr>

html中的组件外框公共模板

<script type="text/template" id="table-wrapper">
<!--弹出面板-->

<div class="modal fade" tabindex="-1" style="z-index:99999;" id="modal-table">
	<div class="modal-dialog">
		<div class="modal-content">
			<div class="modal-header no-padding">
				<div class="table-header">
					<button type="button" class="close" aria-hidden="true" @click="closeAssocModal">
						<span class="white">×</span>
					</button>
					{{title}}
				</div>
			</div>
			<div class="modal-body no-padding"style="min-height:445px">
				<div class="staff-search">
					<slot name="search-condition"></slot>
				</div>
				<table  class="table table-striped table-bordered table-hover no-margin-bottom no-border-top">
    				<thead>
      			    	<slot name="ths"></slot>
					</thead>
					<tbody>
						<tr v-for="(item,index) in items"
              			 	:item="item" :class="{ isSelected: item.isSelected }" @click="pushItem(item,index)">
							<slot name="item" :item="item"></slot>
						</tr>   			
   					</tbody>
			 	</table>
			</div>
			
			<div class="modal-footer no-margin-top">
				<button class="btn btn-sm btn-danger pull-left" @click="closeAssocModal">
					<i class="ace-icon fa fa-times"></i>
					关闭
				</button>
				<button class="btn btn-sm btn-success pull-left" @click="addItems()">
					<i class="ace-icon fa fa-times"></i>
					确认
				</button>
				<ul class="pagination pull-right no-margin">
					<navigation :pages="employeePages" :current.sync="employeePageNo" @navpage="employeePageList" ref="associNavigation"></navigation>
				</ul>
			</div>
		</div><!-- /.modal-content -->
	</div><!-- /.modal-dialog -->
	<loading class="associationLayer" v-show="associationLayer"></loading>	
</div>

</script>

页面内代码

<template>
	<ttable-wrapper :title="'企业检索'" @add-items="confirmAssociation" @close-asso-modal="closeAssoModal" ref="association" :if-add-employee="ifAddAssociateItems" :selected-items="selectedAssoItems" :url="''">
	
			<template slot="search-condition">
				<div class="form-group col-sm-10">
                  	 <label class="col-sm-4 control-label no-padding-right">企业名称:</label>
                  	 <div class="col-sm-8">
                  	 	 <input type="text" autocomplete="off" maxlength="20" class="col-sm-9" v-model="enterName" @keyup.enter="getEnterList()">
						 <button @click="getEnterList()" class="btn btn-primary col-sm-2 tbl-search"><i class="ace-icon fa fa-search"></i></button>
                  	 </div>
				</div>
			</template>
			<template slot="ths">
				<tr>
				    <th class="center" >
	                    <label class="position-relative">
	                        <input type="checkbox" class="ace association-check-all" @click="checkedAllAssoc">
	                        <span class="lbl"></span>
	                    </label>
	                </th>
	                <th>企业名称</th>
					<th>负责人</th>
					<th>联系方式</th>
				</tr>
			</template>
					
			<template slot="item" scope="scope">
			     
				<td class="td-center" style="cursor: pointer;"><label class="position-relative"><input type="checkbox" class="ace" :checked="scope.item.isSelected"> <span class="lbl"></span></label></td>
				<td>
					{{scope.item.name}}
				</td>
				<td>
					{{scope.item.leader}}
				</td>
				<td>
					{{scope.item.telephone}}
				</td>
			</template>
	</table-wrapper>	
</template>

组件封装

Vue.component("table-wrapper",{
    data:function(){
        return {
            employeePageNo:1,	//当前页
            employeePages:0,	//总共页数
            employPageSize:10,	
            senddata:{},
            items:[],	//table列表数据
            associationLayer:false,//loading标志
            tempSelectedItems:[], //组件内确定时外传数据
            
        };
    },
    props:["title","ifAddEmployee","url","selectedItems"],//面板标题、监听打开面板、地址、数据、选中项  
    template:'#table-wrapper',
    methods:{
    	//点击分页页码
	    employeePageList:function(curPage) {
	    	if(curPage){
	    		this.employeePageNo = curPage;
		        this.getEmployeeList();
	    	}
	    },
	    //点击确定按钮 添加
	    addItems:function(){
	    	this.$emit("add-items",this.tempSelectedItems);
	    },
	    //点击关闭按钮时
	    closeAssocModal:function(){
	    	this.$emit("close-asso-modal");// 替换data-dismiss="modal"
	    },
	    //获取table列表
	    getEmployeeList:function(tag){
        	var This = this;
        	var url = This.url;
        	var data = This.senddata;
        	if(tag){
        		data.pageNum = 1;
        		This.employeePageNo = 1;
        	}else{
        		data.pageNum = This.employeePageNo;
        	}
        	data.pageSize = This.employPageSize;
            ajax('post',url,data,function(response){
                This.associationLayer = false;//关闭等待框
                if(response.code==1){
                	var list = response.data.records||response.data;
	                if(list.length>0){ 
	                	 $.each(list,function (index,item) {//选中数组渲染与回显
	                		 item.isSelected = false;
	                         $.each(This.tempSelectedItems,function(eindex,dispatchItem){
	                         	if(item.id==dispatchItem.id){
	                             	item.isSelected = true;
	                             }
	                         });
	                     });
	                	 This.items = list;
	                     This.employeePages = response.data.pages || Math.ceil(response.data.totalCount/This.employPageSize) ||Math.ceil(response.totalCount/This.employPageSize);	       
	                }else{
	                	var msg = "未检索到相关内容";
	                	errorMsgTip(msg);
	                }
	             }
            });
            if(tag){
            	var pageNum = data.current||data.pageNum;
            	this.$refs.associNavigation.goPage(pageNum);//分页组件首页点击效果
            }
            $(".association-check-all").prop("checked", false);
        },
      //面板中选择 新增或删除
        pushItem:function(item,index){
        	/*添加删除*/
        	event.preventDefault();
        	item.isSelected = !item.isSelected;
        	var This = this;
        	if(item.isSelected){
        		This.tempSelectedItems.push(item);
        	}else{
        		var items = This.tempSelectedItems;
        		for(var i=items.length-1;i>=0;i--){
        			if(item.id === items[i].id){	//索引值为选中数组中的索引值
        				This.tempSelectedItems.splice(i,1);
        			}
        		}
        	}
        },
        //全选  页面传递全选标志  
        checkedAllAssoc:function(event){
        	var ifChecked = event.target.checked;
        	var This = this;
        	if(ifChecked){
        		$.each(This.items,function (index,item) {
        			item.isSelected = true;//显示
        			var flag = false;
        			$.each(This.tempSelectedItems,function(indexSelected,itemSelected){
        				if(itemSelected.id == item.id){
        					flag = true;//已选定数组中已存在
        					return false;
            			}
        			});
        			if(!flag){//未存在则存入
        				This.tempSelectedItems.push(item);
        			}
                });
        	}else{
        		$.each(this.items,function (index,item) {
        			item.isSelected = false;
        			$.each(This.tempSelectedItems,function(indexSelected,itemSelected){
        				if(itemSelected.id == item.id){
        					This.tempSelectedItems.splice(i,1);
        					return false;
            			}
        			});
        			
                });
        	}
        }
    },
    watch: {
    	ifAddEmployee:function(curVal,oldVal){
    		if(curVal){
    			var This = this;
    			this.senddata = {};
    			this.getEmployeeList('search');//初始搜索 条件空、页码1
    			this.associationLayer = true;
    			this.tempSelectedItems = [];
    			$.each(this.selectedItems,function(index,item){
    				This.tempSelectedItems.push(item);
    			});
    		}
       }
    }
})

参考:Vue实战(六)通用Table组件