el-table封装(vue2和vue3)

一、vue2

1,使用组件
<!-- 使用封装的表格组件 -->
<template>
	<div>
		<!-- 
		config  配置
		loading 加载
		tableData 数据
		tableHeader 表头
		rowClassName 背景色
		handleSummaryMethod 共计
		handleRowClick 行点击
		handleSortChange 排序
		handleSpanMethod 合并单元格
		handleSelectionClick 选择
			:loading='loading' 
			:tableData="tableData"
		 -->
		<r-table 
			ref='tableRef' 
			:config="config" 
			v-loading="loading"
      		:data="tableData"
			:tableHeader="tableHeader" 
			:row-class-name="rowClassName" 
			:span-method="handleSpanMethod"
			:summary-method="handleSummaryMethod"
			@row-click='handleRowClick' 
			@link-click='handleLinkClick' 
			@sort-change='handleSortChange'
			@selection-change='handleSelectionClick'>
			<template slot="age" slot-scope="scope">
				<span @click.stop='onClick(scope.row)'>else</span>
			</template>
		</r-table>
	</div>
</template>
<script>
	import rTable from '@/components/rTable';
	
    export default {
		components: {
			rTable
		},
		data() {
			return {
				config: {
					isSelection: true,       //显示复选框
					isRowClick: false,       //使用行点击
					isStripe: false,         //隐藏斑马纹,与背景色不可同时用
					tableHeight: 500,        //表格高度
					isShowSummary: true,     //显示共计
					isIndex:false,           //自定义序号
				},
				tableHeader: [
					{
						prop: 'serialNo',       
						colWidth: '55',    
						title: '编码',       
						type: 'customIndex',      
						isSort:false, 
					},
					{
						prop: 'name',       //字段
						colWidth: '140',    //宽度
						title: '名称',       //表头
						isSort:false,       //是否排序
						type: 'link',       //link:跳转,
					},						// button:文字按钮,
											// tag:标签,
											// process:进度条,
											// slot:插槽, 
											// price:三位隔开, 
											// priceLink:三位隔开&&跳转, 
											// customIndex:自定义序号, 
											// 无: 普通
					
					{
						prop: '',      
						colWidth: '',  
						title: '其他',    
						type: '',     
						isSort:false,
						children:[          //children  二级
							{
								prop: 'date',
								colWidth: '160',
								title: '日期-c',
								isSort:false
							},
							{
								
								prop: 'address',
								colWidth: '',
								title: '详细地址-c',
								isSort:false
							},
							{
								prop: 'price',
								colWidth: '140',
								title: '价格-c',
								type:'price',  
								isSort:true
							},
							{
								prop: 'price',
								colWidth: '140',
								title: '价格-2-c',
								type:'priceLink',
								isSort:true
							},
						]
					},
					{
						prop: 'age',
						colWidth: '100',
						title: '插槽',
						type: 'slot',  
						isSort:false
					},
					{
						prop: 'status',
						colWidth: '100',
						title: '状态',
						type: 'tag',
						isSort:false
					},
					{
						prop: 'num',
						colWidth: '140',
						title: '进度',
						type: 'process',
						isRide: true,
						isSort:false
					},
					{
						prop: '',
						colWidth: '100',
						title: '详情',
						type: 'button',
						btnText: '查看',
						isSort:false
					}
				],
				loading: false,
				tableData: [
				 {
					id:2,
					date: '2016-06-04',
					name: '王小虎-2',
					address: '上海市普陀区金沙江路 1517 弄',
					status: false,
					num: 0.52,
					price: 10022,
					
				},
				{
					id:1,
					date: '2016-06-02',
					name: '王小虎-1',
					address: '上海市普陀区金沙江路 1518 弄',
					status: false,
					num: 0.7,
					price: 1000,
					color: 'green'
				}, 
				{
					id:1,
					date: '2016-06-02',
					name: '王小虎-1',
					address: '上海市普陀区金沙江路 1518 弄',
					status: false,
					num: 0.7,
					price: 1001,
					color: 'green'
				},
				{
					id:3,
					date: '2016-06-05',
					name: '王小虎-3',
					address: '上海市普陀区金沙江路 1519 弄',
					status: true,
					price: 156789,
					num: 0.67,
				}, {
					id:4,
					date: '2016-06-06',
					name: '王小虎-4',
					address: '上海市普陀区金沙江路 1516 弄',
					status: false,
					num: 0.43,
					price: 400,
					color: 'orange'
				}],
				spanList:[],  //合并单元格
				spanList2:[],
				orderList: [{ column: '', asc: false }],  // 默认, asc:false 倒序
				queryParams: {
					current: 1,
					size: 20
				},
			}
		},
		mounted() {
			this.getList();
		},
		methods: {
			onClick(row){
				console.log('插槽点击---->',row);
			},
			handleSortChange(value){
				
				//手动只能排序数字,还是需要通接口的
				const params = value.prop;
				if(value.order == 'descending'){
					this.tableData.sort((a,b)=>{
						return b[params] - a[params]
					})
					
				}else if(value.order == 'ascending'){
					this.tableData.sort((a,b)=>{
						return a[params] - b[params]
					})
				}
				this.getList();
				//通接口整理参数
				// switch (value.order) {
				// case 'ascending':
				//   this.orderList[0].column = value.prop;
				//   this.orderList[0].asc = true;
				//   break
				// case 'descending': 
				//   this.orderList[0].column = value.prop;
				//   this.orderList[0].asc = false;
				//   break
			 //  }
			 //  this.getList();
			},
			getList(){
				this.queryParams = this.filterOrderItems( this.queryParams,this.orderList);
				this.getSpanArr(this.tableData);
			},
			getSpanArr(data) {
				this.spanList = [];
				let sIndex = 0;

				// let sIndex2 = 0;
				// this.spanList2 = [];

				for (var i = 0; i < data.length; i++) {
					if (i === 0) {
						this.spanList.push(1);
						
					  	// this.spanList2.push(1);  //另一个字段合并
					} else {
						if (data[i].id === data[i - 1].id) {
							this.spanList[sIndex] ++
							this.spanList.push(0);
						} else {
							this.spanList.push(1);
							sIndex = i;
						}

						//if(data[i].pName == data[i -1 ].pName){
							//this.spanList2[sIndex2]++
							//this.spanList2.push(0);
						//}else{
							//this.spanList2.push(1);
							//sIndex2 = index;
						//}

					}
				}

				//序号值
				let serialNo = 0;
				for(let n in this.spanList){
					if(this.spanList[n] > 0){
					serialNo += 1;
					this.$set(data[n],'serialNo',serialNo);
					}
				}
			},
			handleSpanMethod({row,column,rowIndex,columnIndex}){
				// if (columnIndex != 1 && columnIndex < 7) {
				if (columnIndex < 7 ) {
					const _row = this.spanList[rowIndex];
					const _col = _row > 0 ? 1 : 0;    
					return {
					rowspan: _row,
					colspan: _col
					}
      			}
				//else if (columnIndex == 8) {
					//const _row = this.spanList2[rowIndex];
					//const _col = _row > 0 ? 1 : 0;    
					//return {
					  //rowspan: _row,
					  //colspan: _col
					//}
				//}
			},
			handleSummaryMethod(param) {
				const { columns, data} = param;
				const sums = [];
				columns.forEach((column, index) => {
					if (index === 0) {
						sums[index] = '合计'
						//只计算倒数第5列、第6列
					} else if (index === columns.length - 5 || index === columns.length - 6) {
						const values = data.map(item => Number(item[column.property]))
						if (!values.every(value => isNaN(value))) {
							sums[index] = values.reduce((prev, curr) => {
								const value = Number(curr)
								if (!isNaN(value)) {
									return prev + curr
								} else {
									return prev
								}
							}, 0)
							sums[index] += '';
						} else {
							sums[index] = '';
						}
					} else {
						sums[index] = '';
					}
				})
				return sums;
			},
			handleLinkClick(row, title) {
				if (title == '名称') {
					console.log('名称跳转====>', row.name);
				} else if (title == '详情') {
					console.log('详情跳转----->', row.name);
				} else if (title == '进度') {
					console.log('进度跳转----->', row.name);
				}else{
					console.log('跳转---->', title);
				}
			},
			handleSelectionClick(selection) {
				console.log('勾选---->', selection);
			},
			handleRowClick(row) {
				console.log('行点击---->', row);
			},
			//注:背景色与斑马纹不能同在
			rowClassName({row,rowIndex}) {
				row.index = rowIndex;
				if (row.color) return row.color + '-bg';
			},
			filterOrderItems(params, list) {
			  for (const key in params) {
			    if (key.indexOf('orders[') > -1) {
			      delete params[key];
			    }
			  }
			  if (list && list.length>0) {
			    for (let i = 0; i < list.length; i++) {
			      for (const key in list[i]) {
			        params['orders[' + i + '].' + key] = list[i][key];
			      }
			    }
			  }
			  return params;
			}
		}

	}
</script>
<style scoped>
	::v-deep .orange-bg {
		background-color: #e6a23c !important;
	}

	::v-deep .green-bg {
		background-color: #4cd964 !important;
	}
	
	::v-deep .red-bg {
		background-color: #f56c6c !important;
	}
</style>
2,封装组件
<!-- 封装表格 -->
<template>
    <!-- :data="tableData" 
    v-loading='loading' -->
<div>
    <el-table 
        v-bind="$attrs"
        border 
        style="width: 100%"
        :header-cell-style="{ background:'#f5f5f5',height:'40px',color:'#515a6e'}"
        :style="`height:${config.tableHeight}px;width: 100%`" 
        :max-height="config.tableHeight"
        :stripe="config.isStripe"
        :span-method="spanMethod"
        :row-class-name="rowClassName"
        :summary-method="summaryMethod"
        :show-summary='config.isShowSummary'
        @row-click="rowClick"
        @sort-change="sortChange"
        @selection-change="selectionChange">
        <el-table-column align='center' type="selection" width="55" v-if="config.isSelection" />
        <el-table-column align='center' type="index" label="序号" width="55" v-if="config.isIndex"/>
        <el-table-column align='center' 
            v-for='(item,index) in tableHeader' 
            :key='index'
            :prop="item.prop"
            :width="item.colWidth"
            :label="item.title"
            :sortable="item.isSort?'custom':false"
            show-overflow-tooltip>
            <!-- 一级 -->
            <template slot-scope="scope">
                <template v-if="item.type === 'customIndex'">
                    <span>{{scope.row[item.prop]}}</span>
                </template>
                <template v-else-if="item.type === 'link'">
                    <el-link type="primary" @click.stop="linkClick(scope.row,item.title)">{{scope.row[item.prop]}}</el-link>
                </template>
                <template v-else-if="item.type === 'button'">
                    <el-button type="text" size='mini' @click.stop="linkClick(scope.row,item.title)">{{item.btnText}}</el-button>
                </template>
                <template v-else-if="item.type === 'tag'">
                    <el-tag :type="scope.row[item.prop]?'':'danger'">{{scope.row[item.prop]?'是':'否'}}</el-tag>
                </template>
                <template v-else-if="item.type === 'price'">
                    <span>{{ handleNum(Number(scope.row[item.prop])) }}</span>
                </template>
                <template v-else-if="item.type === 'priceLink'">
                    <el-link type="primary" @click.stop="linkClick(scope.row,item.title)">{{ handleNum(Number(scope.row[item.prop])) }}</el-link>
                </template>
                <template v-else-if="item.type === 'process'">
                    <span @click.stop="linkClick(scope.row,item.title)">
                        <!--0.07*100结果是7.000000000000001 -->
                        <el-progress :percentage="item.isRide?Number(scope.row[item.prop])*10/10*100:Number(scope.row[item.prop])*10/10"  :text-inside="true" :stroke-width="13"></el-progress>
                    </span>
                </template>
                 <!-- 插槽 -->
                <slot v-else-if="item.type === 'slot'"  :name="item.prop"  :row="scope.row" :index="scope.$index"></slot>
                <template v-else>
                    <span>{{scope.row[item.prop]}}</span>
                </template>
            </template>
            
            <!-- 二级 子项 -->
            <template v-for='(v,i) in item.children'>
                <el-table-column align='center'
                    :key='i'
                    :prop="v.prop"
                    :width="v.colWidth"
                    :label="v.title"
                    :sortable="v.isSort?'custom':false"
                    show-overflow-tooltip>
                    <template slot-scope="scope" @click.stop>
                        <template v-if="v.type === 'link'">
                            <el-link type="primary" @click.stop="linkClick(scope.row,v.title)">{{scope.row[v.prop]}}</el-link>
                        </template>
                        <template v-else-if="v.type === 'button'">
                            <el-button type="text" size='mini' @click.stop="linkClick(scope.row,v.title)">{{v.btnText}}</el-button>
                        </template>
                        <template v-else-if="v.type === 'tag'">
                            <el-tag :type="scope.row[v.prop]?'':'danger'">{{scope.row[v.prop]?'是':'否'}}</el-tag>
                        </template>
                        <template v-else-if="v.type === 'price'">
                            <span>{{ handleNum(Number(scope.row[v.prop])) }}</span>
                        </template>
                        <template v-else-if="v.type === 'priceLink'">
                            <el-link type="primary" @click.stop="linkClick(scope.row,v.title)">{{ handleNum(Number(scope.row[v.prop])) }}</el-link>
                        </template>
                        <template v-else-if="v.type === 'process'">
                            <span @click.stop="linkClick(scope.row,v.title)">
                                <el-progress :percentage="v.isRide?Number(scope.row[v.prop])*10/10*100:Number(scope.row[v.prop])*10/10"  :text-inside="true" :stroke-width="13"></el-progress>
                            </span>
                        </template>
                        <template v-else>
                            <span>{{scope.row[v.prop]}}</span>
                        </template>
                    </template>
                </el-table-column>	
            </template>
        </el-table-column>
    </el-table>
</div>
</template>
<script>
export default {
    name: "Table",
    props: {
        config:{},
        rowClassName: { 
            type: Function
        },
        summaryMethod:{
            type: Function
        },
        spanMethod:{
            type: Function
        },
        tableHeader:{
            type: Array,
            default: () => [],
        },
        loading: Boolean,
        tableData:{
            type: Array,
            default: () => [],
        },
        
      },
    data() {
        return {
            
        }
    },
    mounted() {

    },
    methods: {
        linkClick(row,title){
            this.$emit('link-click', row, title);
        },
        selectionChange(selection) {
           this.$emit('selection-change', selection);
        },
        rowClick(row) {
            if(this.config.isRowClick) this.$emit('row-click', row);
        },
        sortChange(value) {
            if(value.order) this.$emit('sort-change', value);
        },
        handleNum(num){
             //(1)保留两位小数
            var dot = String(num).indexOf(".");
            if(dot != -1){
            var dotCnt = String(num).substring(dot+1,num.length);
                if(dotCnt.length > 2)  num = num.toFixed(2);
            }
            //(2)四舍五入取整数
             // num = Math.round(num)
             //三位隔开
            return String(num).replace(/\d+/, function(n) {
                return  n.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
            });
        }
        
    }

}
</script>

二、vue3

1,使用组件
<template>
	<div>
		<div class="long-text-box">
     		<span class="text">13993199751_18037893546_101aaa</span>
    	</div>
		<!-- 
		config  配置
		loading 加载
		tableData 数据
		tableHeader 表头
		rowClassName 背景色
		handleSummaryMethod 共计
		handleRowClick 行点击
		handleSortChange 排序
		handleSpanMethod 合并单元格
		handleSelectionClick 选择
		 -->
		<r-table 
			ref='tableRef' 
			:config="state.config" 
			v-loading='state.loading' 
			:data="state.tableData"
			:tableHeader="state.tableHeader" 
			:row-class-name="rowClassName" 
			:span-method="handleSpanMethod"
			:summary-method="handleSummaryMethod"
			@row-click='handleRowClick' 
			@link-click='handleLinkClick' 
			@sort-change='handleSortChange'
			@selection-change='handleSelectionClick'>
			<template v-slot:age slot-scope="scope">
				<span @click.stop='onClick(scope.row)'>else</span>
			</template>
		</r-table>
		<div>计算器{{ count }}</div>
	</div>
</template>
<script setup lang="ts" name="index">
// 引入
import { defineAsyncComponent, reactive, onMounted,ref,watch } from 'vue';

const count = ref(0);
//watch 监听一个响应式数据
watch(count, () => {
    console.log("count改变了");
});
setTimeout(() => {
    count.value++;
}, 2000);

const user = reactive({
    name: "tom",
    info: {
        gender: "男",
        age: 18,
    }
})
//watch 监听多个数据
watch([count, user], () => {
    console.log("监听多个数据");
});
setTimeout(() => {
    count.value++;
}, 2000);
setTimeout(() => {
    user.info.age++;
}, 4000);

// 引入组件
const rTable = defineAsyncComponent(() => import('/@/components/table/table.vue'));

const state = reactive({
	config: {
		isSelection: true,       //显示复选框
		isRowClick: false,       //使用行点击
		isStripe: false,         //隐藏斑马纹,与背景色不可同时用
		tableHeight: 500,        //表格高度
		isShowSummary: false,    //显示共计
		isIndex: false,   		 //自定义序号
	},
	tableHeader: [
		{
			prop: 'serialNo',       
			colWidth: '55',    
			title: '编码',       
			type: 'customIndex',      
			isSort:false, 
		},
		{
			prop: 'name',       //字段
			colWidth: '140',    //宽度
			title: '名称',       //表头
			isSort:false,       //是否排序
			type: 'link',       //link:跳转,
		},						// button:文字按钮,
								// tag:标签,
								// process:进度条,
								// slot:插槽, 
								// price:三位隔开, 
								// priceLink:三位隔开&&跳转, 
								// customIndex:自定义序号, 
								// 无: 普通
		{
			prop: '',      
			colWidth: '',  
			title: '其他',    
			type: '',     
			isSort:false,
			children:[          //children  二级
				{
					prop: 'date',
					colWidth: '160',
					title: '日期-c',
					isSort:false,
					type: '', 
				},
				{
					
					prop: 'address',
					colWidth: '',
					title: '详细地址-c',
					isSort:false,
					type: '', 
				},
				{
					prop: 'price',
					colWidth: '140',
					title: '价格-c',
					type:'price',  
					isSort:true,
				},
				{
					prop: 'price',
					colWidth: '140',
					title: '价格-2-c',
					type:'priceLink',
					isSort:true
				},
			]
		},
		{
			prop: 'age',
			colWidth: '100',
			title: '插槽',
			type: 'slot',  
			isSort:false
		},
		{
			prop: 'status',
			colWidth: '100',
			title: '状态',
			type: 'tag',
			isSort:false
		},
		{
			prop: 'num',
			colWidth: '140',
			title: '进度',
			type: 'process',
			isRide: true,
			isSort:false
		},
		{
			prop: '',
			colWidth: '100',
			title: '详情',
			type: 'button',
			btnText: '查看',
			isSort:false
		}
	],
	loading: false,
	tableData: [
		{
		id:2,
		date: '2016-06-04',
		name: '王小虎-2',
		address: '上海市普陀区金沙江路 1517 弄',
		status: false,
		num: 0.52,
		price: 10022,
		
	},
	{
		id:1,
		date: '2016-06-02',
		name: '王小虎-1',
		address: '上海市普陀区金沙江路 1518 弄',
		status: false,
		num: 0.7,
		price: 1000,
		color: 'green'
	}, 
	{
		id:1,
		date: '2016-06-02',
		name: '王小虎-1',
		address: '上海市普陀区金沙江路 1518 弄',
		status: false,
		num: 0.7,
		price: 1001,
		color: 'green'
	},
	{
		id:3,
		date: '2016-06-05',
		name: '王小虎-3',
		address: '上海市普陀区金沙江路 1519 弄',
		status: true,
		price: 156789,
		num: 0.67,
	}, {
		id:4,
		date: '2016-06-06',
		name: '王小虎-4',
		address: '上海市普陀区金沙江路 1516 弄',
		status: false,
		num: 0.43,
		price: 400,
		color: 'orange'
	}],
	spanList:[],  //合并单元格
	orderList: [{ column: '', asc: false }],  // 默认, asc:false 倒序
	queryParams: {
		current: 1,
		size: 20
	},
})
const onClick = (row:Object)=>{
	console.log('插槽点击---->',row);
}
const handleSortChange=(value:Object)=>{
				
	//手动只能排序数字,还是需要通接口的
	const params = value.prop;
	if(value.order == 'descending'){
		state.tableData.sort((a,b)=>{
			return b[params] - a[params]
		})
		
	}else if(value.order == 'ascending'){
		state.tableData.sort((a,b)=>{
			return a[params] - b[params]
		})
	}
	getList()
	
	//通接口整理参数
	// switch (value.order) {
	// case 'ascending':
	//   this.orderList[0].column = value.prop;
	//   this.orderList[0].asc = true;
	//   break
	// case 'descending': 
	//   this.orderList[0].column = value.prop;
	//   this.orderList[0].asc = false;
	//   break
	//  }
	//  this.getList();
}
const handleSelectionClick = (selection:Array)=>{
	console.log('勾选---->', selection);
}
const handleRowClick = (row:Object) => {
	console.log('行点击---->', row);
}
//注:背景色与斑马纹不能同在
const rowClassName = ({row,rowIndex})=>{
	row.index = rowIndex;
	if (row.color) return row.color + '-bg';
}
const handleLinkClick = (row:Object, title:String)=>{
	if (title == '名称') {
		console.log('名称跳转====>', row.name);
	} else if (title == '详情') {
		console.log('详情跳转----->', row.name);
	} else if (title == '进度') {
		console.log('进度跳转----->', row.name);
	}else{
		console.log('跳转---->', title);
	}
}
const handleSummaryMethod = (param:Object)=>{
	const { columns, data} = param;
	const sums = [];
	columns.forEach((column, index) => {
		if (index === 0) {
			sums[index] = '合计';
			//只计算倒数第2列
		} else if (index === columns.length - 2) {
			const values = data.map(item => Number(item[column.property]))
			if (!values.every(value => isNaN(value))) {
				sums[index] = values.reduce((prev, curr) => {
					const value = Number(curr)
					if (!isNaN(value)) {
						return prev + curr
					} else {
						return prev
					}
				}, 0)
				sums[index] += '';
			} else {
				sums[index] = '';
			}
		} else {
			sums[index] = '';
		}
	})
	return sums;
}
const handleSpanMethod = ({row,column,rowIndex,columnIndex}) => {
	// if (columnIndex != 1 && columnIndex < 7) {
	if (columnIndex < 7 ) {
		const _row = state.spanList[rowIndex];
		const _col = _row > 0 ? 1 : 0;    
		return {
		rowspan: _row,
		colspan: _col
		}
	}
	//else if (columnIndex == 8) {
		//const _row = state.spanList2[rowIndex];
		//const _col = _row > 0 ? 1 : 0;    
		//return {
			//rowspan: _row,
			//colspan: _col
		//}
	//}
}

const getList = () =>{
	// this.queryParams = this.filterOrderItems( this.queryParams,this.orderList);
	getSpanArr(state.tableData);
}
const getSpanArr = (data) => {
	state.spanList = [];
	let sIndex = 0;

	// let sIndex2 = 0;
	// this.spanList2 = [];

	for (var i = 0; i < data.length; i++) {
		if (i === 0) {
			state.spanList.push(1);
			
			// this.spanList2.push(1);  //另一个字段合并
		} else {
			if (data[i].id === data[i - 1].id) {
				state.spanList[sIndex] ++
				state.spanList.push(0);
			} else {
				state.spanList.push(1);
				sIndex = i;
			}

			//if(data[i].pName == data[i -1 ].pName){
				//this.spanList2[sIndex2]++
				//this.spanList2.push(0);
			//}else{
				//this.spanList2.push(1);
				//sIndex2 = index;
			//}
		}
	}

	//序号值
	let serialNo = 0;
	for(let n in state.spanList){
		if(state.spanList[n] > 0){
			serialNo += 1;
			data[n].serialNo = serialNo;	
		}
	}
}

onMounted(() => {
	getList();
});
</script>
<style scoped>
	::v-deep .orange-bg {
		background-color: #e6a23c !important;
	}

	::v-deep .green-bg {
		background-color: #4cd964 !important;
	}
	
	::v-deep .red-bg {
		background-color: #f56c6c !important;
	}
	.long-text-box {
		width:120px;
		padding:4px 6px;
		border: 1px solid #c0c0cc;
		border-radius: 4px;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
		direction: rtl;
  	}
	.long-text-box .text {
		direction: ltr;
		unicode-bidi: bidi-override; 
	};
</style>
2,封装组件
<!-- 封装组件 -->
<template>
  <div>
    <el-table 
      style="width: 100%" 
      :style="`height:${config.tableHeight}px;`" 
      :max-height="config.tableHeight"
      border 
      v-bind="$attrs" 
      @row-click="rowClick" 
      :stripe="config.isStripe" 
      :show-summary='config.isShowSummary'
      @sort-change="sortChange" 
      @selection-change="selectionChange"
    >
      <el-table-column align='center' type="selection" width="55" v-if="config.isSelection" />
      <el-table-column align='center' type="index" label="序号" width="55" v-if="config.isIndex"/>
      <el-table-column align='center' v-for='(item, index) in tableHeader' :key='index' :prop="item.prop"
        :width="item.colWidth" :label="item.title" :sortable="item.isSort ? 'custom' : false" show-overflow-tooltip>
        <!-- 一级 v-slot="scope"-->
        <template v-slot="scope" @click.stop>
          <template v-if="item.type === 'customIndex'">
              <span>{{scope.row[item.prop]}}</span>
          </template>
          <template v-else-if="item.type === 'link'">
            <el-link type="primary" @click.stop="linkClick(scope.row, item.title)">{{ scope.row[item.prop]
            }}</el-link>
          </template>
          <template v-else-if="item.type === 'button'">
            <el-button type="text" size='mini' @click.stop="linkClick(scope.row, item.title)">{{ item.btnText
            }}</el-button>
          </template>
          <template v-else-if="item.type === 'tag'">
            <el-tag :type="scope.row[item.prop] ? '' : 'danger'">{{ scope.row[item.prop] ? '是' : '否' }}</el-tag>
          </template>
          <template v-else-if="item.type === 'price'">
            <span>{{ handleNum(Number(scope.row[item.prop])) }}</span>
          </template>
          <template v-else-if="item.type === 'priceLink'">
            <el-link type="primary" @click.stop="linkClick(scope.row, item.title)">{{
              handleNum(Number(scope.row[item.prop])) }}</el-link>
          </template>
          <template v-else-if="item.type === 'process'">
            <span @click.stop="linkClick(scope.row, item.title)">

              <el-progress
                :percentage="item.isRide ? Number(scope.row[item.prop]) * 10 / 10 * 100 : Number(scope.row[item.prop]) * 10 / 10"
                :text-inside="true" :stroke-width="13"></el-progress>
            </span>
          </template>
          <!--0.07*100结果是7.000000000000001 -->
          <!-- 插槽 -->
          <slot v-else-if="item.type === 'slot'" :name="item.prop" :row="scope.row" :index="scope.$index"></slot>
          <template v-else>
            <span>{{ scope.row[item.prop] }}</span>
          </template>

          <!-- 二级 子项 -->
          <template v-for='(v, i) in item.children' :key='i'>
            <el-table-column align='center' :prop="v.prop" :width="v.colWidth" :label="v.title"
              :sortable="v.isSort ? 'custom' : false" show-overflow-tooltip>
              <template v-slot="scope" @click.stop>

                <template v-if="v.type === 'link'">
                  <el-link type="primary" @click.stop="linkClick(scope.row, v.title)">{{ scope.row[v.prop]
                  }}</el-link>
                </template>
                <template v-else-if="v.type === 'button'">
                  <el-button type="text" size='mini' @click.stop="linkClick(scope.row, v.title)">{{
                    v.btnText }}</el-button>
                </template>
                <template v-else-if="v.type === 'tag'">
                  <el-tag :type="scope.row[v.prop] ? '' : 'danger'">{{ scope.row[v.prop] ? '是' : '否'
                  }}</el-tag>
                </template>
                <template v-else-if="v.type === 'price'">
                  <span>{{ handleNum(Number(scope.row[v.prop])) }}</span>
                </template>
                <template v-else-if="v.type === 'priceLink'">
                  <el-link type="primary" @click.stop="linkClick(scope.row, v.title)">
                    {{ handleNum(Number(scope.row[v.prop])) }}
                  </el-link>
                </template>
                <template v-else-if="v.type === 'process'">
                  <span @click.stop="linkClick(scope.row, v.title)">
                    <el-progress
                      :percentage="v.isRide ? Number(scope.row[v.prop]) * 10 / 10 * 100 : Number(scope.row[v.prop]) * 10 / 10"
                      :text-inside="true" :stroke-width="13"></el-progress>
                  </span>
                </template>
                <template v-else>
                  <span>{{ scope.row[v.prop] }}</span>
                </template>
              </template>
            </el-table-column>
          </template>
        </template>

      </el-table-column>
    </el-table>
  </div>
</template>
<script setup lang="ts" name="elementTable">

const props = defineProps({
  config: {
    type: Object,
    default: () => { },
  },
  tableHeader: {
    type: Array,
    default: () => [],
  },
  tableData: {
    type: Array,
    default: () => [],
  }
});

// 定义子组件向父组件传值/事件
const emit = defineEmits(['link-click', 'selection-change', 'row-click', 'sort-change']);

const linkClick = (row: Object, title: String) => {
  emit('link-click', row, title);
};
const selectionChange = (selection: Array) => {
  emit('selection-change', selection);
};
const rowClick = (row: Object) => {
  if (props.config.isRowClick) emit('row-click', row);
};
const sortChange = (value: Object) => {
  if (value.order) emit('sort-change', value);
};
const handleNum = (num: number) => {
  //(1)保留两位小数
  var dot = String(num).indexOf('.');
  if (dot != -1) {
    var dotCnt = String(num).substring(dot + 1, num.length);
    if (dotCnt.length > 2) num = num.toFixed(2);
  }
  //(2)四舍五入取整数
  // num = Math.round(num)
  //三位隔开
  return String(num).replace(/\d+/, function (n) {
    return n.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
  });
};
</script>
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
封装Table组件可以使代码更加简洁、易读,并且可以复用。以下是一个简单的封装Table组件的示例: ``` import React from 'react'; import PropTypes from 'prop-types'; import { Table } from 'antd'; const CustomTable = ({ columns, dataSource, loading, pagination }) => { return ( <Table columns={columns} dataSource={dataSource} loading={loading} pagination={pagination} /> ); }; CustomTable.propTypes = { columns: PropTypes.array.isRequired, dataSource: PropTypes.array.isRequired, loading: PropTypes.bool.isRequired, pagination: PropTypes.object.isRequired, }; export default CustomTable; ``` 在这个示例中,我们使用了Ant Design的Table组件,并将其封装为CustomTable组件。CustomTable组件接收四个props:columns(表格列的配置描述)、dataSource(表格数据)、loading(表格是否正在加载)和pagination(分页器的配置项)。这些props都是必需的,并且我们使用PropTypes对其进行了类型检查。 使用CustomTable组件时,只需要像下面这样传入props即可: ``` import React from 'react'; import CustomTable from './CustomTable'; const columns = [ { title: 'Name', dataIndex: 'name', key: 'name', }, { title: 'Age', dataIndex: 'age', key: 'age', }, ]; const dataSource = [ { key: '1', name: 'John Brown', age: 32, }, { key: '2', name: 'Jim Green', age: 42, }, ]; const pagination = { pageSize: 10, }; const Example = () => { return ( <CustomTable columns={columns} dataSource={dataSource} loading={false} pagination={pagination} /> ); }; export default Example; ``` 以上就是一个简单的封装Table组件的示例。根据实际需求,可以在CustomTable组件中添加更多的props和配置项。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值