一、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',
},
{
prop: '',
colWidth: '',
title: '其他',
type: '',
isSort:false,
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 }],
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();
},
getList(){
this.queryParams = this.filterOrderItems( this.queryParams,this.orderList);
this.getSpanArr(this.tableData);
},
getSpanArr(data) {
this.spanList = [];
let sIndex = 0;
for (var i = 0; i < data.length; i++) {
if (i === 0) {
this.spanList.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;
}
}
}
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 < 7 ) {
const _row = this.spanList[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] = '合计'
} 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){
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);
}
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(count, () => {
console.log("count改变了");
});
setTimeout(() => {
count.value++;
}, 2000);
const user = reactive({
name: "tom",
info: {
gender: "男",
age: 18,
}
})
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',
},
{
prop: '',
colWidth: '',
title: '其他',
type: '',
isSort:false,
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 }],
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()
}
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] = '合计';
} 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 < 7 ) {
const _row = state.spanList[rowIndex];
const _col = _row > 0 ? 1 : 0;
return {
rowspan: _row,
colspan: _col
}
}
}
const getList = () =>{
getSpanArr(state.tableData);
}
const getSpanArr = (data) => {
state.spanList = [];
let sIndex = 0;
for (var i = 0; i < data.length; i++) {
if (i === 0) {
state.spanList.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;
}
}
}
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) => {
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);
}
return String(num).replace(/\d+/, function (n) {
return n.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
});
};
</script>