前置条件
需要引入使用jsx
为了渲染表格的单元格内容formatter,jsx引入使用看这篇
一、效果
封装的el-table公共组件table.vue
注意深拷贝用的lodash方法,以及使用了jsx插件渲染列的标签和事件
二、语法糖setup封装版本
封装table.vue
<template>
<div class="page-view">
<el-table v-loading="tableData.tableLoading" ref="multipleTableRef" class="jg-table" :data="tableData.data"
style="width: 100%;" @selection-change="handleSelectionChange" @row-click="rowClick" @sort-change="sortChange">
<template v-for="(item, index) in tableData.columnsData " :key="index">
<!-- 多选 -->
<el-table-column v-if="item.type === 'selection'" :type="item.type" :width="item.width" />
<!-- 单选 -->
<el-table-column v-else-if="item.type === 'radio'" :width="item.width" class-name="no-radio-label">
<template #default="{ row, $index }">
<el-radio @change="changeRadio" v-model="radioIndex" :label="$index"></el-radio>
</template>
</el-table-column>
<!-- 渲染formatter -->
<!-- <el-table-column v-else :property="item.field" :label="item.title" :width="item.width"
:min-width="item.minWidth" :max-idth="item.maxWidth" :showOverflowTooltip="item.showOverflowTooltip"
:sortable="item.sortable" :formatter="item.formatter" /> -->
<el-table-column v-else :property="item.field" :label="item.title" :width="item.width"
:min-width="item.minWidth" :max-idth="item.maxWidth" :showOverflowTooltip="item.showOverflowTooltip"
:sortable="item.sortable" :formatter="item.formatter">
<template #header>
<div style="display: inline-block; align-items: center;">
<span>{{ item.title }}</span>
</div>
</template>
</el-table-column>
</template>
</el-table>
<pre>{{ tableData }}</pre>
</div>
</template>
<script setup lang="jsx">
import { ref, defineOptions, defineEmits, defineProps, watch } from 'vue' // 按需引入ref函数
import _ from 'lodash'
defineOptions({
name: "JgTable"
})
const emit = defineEmits(['update:editItem', 'update:handleSelectionChange', 'update:changeRadio', 'update:sortChange'])
const props = defineProps({
description: {
type: String,
default: () => {
return ' '
}
},
tableDataObj: {
type: Object,
default: () => {
return {}
}
},
})
let tableData = ref({})
let radioIndex = ref(null)
watch([() => props.tableDataObj, () => props.rulesData], ([tableDataObj, rulesData]) => {
// console.log('监听');
tableData.value = _.cloneDeep(tableDataObj) // 使用lodash的深拷贝方法
}, { deep: true, immediate: true })
// 编辑-暂不使用 目前由子组件自己触发
function editItem(row, type) {
console.log(row, type,);
// 子传父
emit('update:editItem', { row: row, type: type });
}
// 多选
const handleSelectionChange = (val) => {
// console.log(val);
// 子传父
emit('update:handleSelectionChange', val);
}
// 单选
const changeRadio = (val) => {
// console.log('单选', val, radioIndex.value.aa);
let item = tableData.value.data?.[val] || {}
emit('update:changeRadio', item);
}
// 表格点击事件
const rowClick = (val) => {
// console.log(val);
}
// 排序
const sortChange = (val) => {
// console.log(val);
emit('update:sortChange', val);
}
</script>
<style lang="scss" scoped>
// 去除单选框的内容
:deep(.no-radio-label) {
.el-radio__label {
display: none;
}
}
</style>
使用index.vue
<template>
<JgTable :tableDataObj="tableData" @update:editItem="updateEditItem"
@update:handleSelectionChange="updateHandleSelectionChange" @update:changeRadio="updateChangeRadio"
@update:sortChange="updateSortChange"></JgTable>
</template>
<script setup lang="jsx">
import { ref, defineComponent, toRefs, reactive, onMounted, getCurrentInstance } from 'vue'
import { Delete, Edit, Download, QuestionFilled, EditPen, Tickets } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
defineOptions({
name: 'EquipmentDefect'
})
const nameList = ref([])
let tableData = ref({
tableLoading: false,
// title:名称 field:key type:单选复选框 width minWidth maxWidth showOverflowTooltip formatter:格式化内容
columnsData: [
{ title: '多选', type: 'selection', width: 55 },
{ title: '单选', type: 'radio', width: 55 },
{ title: '缺陷名称', field: 'aa', formType: 'input', width: 120 },
{ title: '缺陷描述', field: 'bb', formType: 'input', showOverflowTooltip: true },
{ title: '缺陷发布时间', field: 'cc1', formType: 'date', dateType: 'YYYY-MM-DD', width: 140 },
{ title: '缺陷发布时间2', field: 'cc2', formType: 'dateTime', dateType: 'YYYY-MM-DD HH:mm:ss', width: 150 },
// { title: '缺陷发布时间', field: 'cc1', formType: 'datetime', dateType: 'YYYY-MM-DD HH:mm:ss' },
{
title: '设备类型', field: 'dd', formType: 'select', width: 120, selectList: nameList,
formatter: (row) => {
const val = nameList.value.find(item => item.value === row.dd)?.name || ''
return [<span>{val}</span>]
}
},
{ title: '设备厂家', field: 'ee', formType: 'input' },
{ title: '设备版本', field: 'ff', formType: 'input', width: 160, showOverflowTooltip: true },
{
title: '缺陷设备数量', field: 'gg', formType: 'input', sortable: 'custom',width: 160,
formatter: (row, column) => {
// console.log(112,row, column); // 直接{row}或{column} 不会显示内容
return [<span onClick={() => clickItem(row, 'view')}> {row.gg} </span>]
}
},
{
title: '缺陷反馈状态', field: 'hh', formType: 'radio', minWidth: 100, selectList: [
{ name: '已反馈', value: 1 },
{ name: '未反馈', value: 0 },
],
formatter: (row) => (
row ? <el-tag style="border: none;" class="cursor" type={row.hh === 1 ? 'primary' : 'warning'}>{row.hh === 1 ? '已反馈' : row.hh === 0 ? '未反馈' : ''}</el-tag> : ''
)
},
{
title: '缺陷处置进度', field: 'ii', formType: 'progress',
formatter: (row) => (
<el-progress class="table_progress" width={13} stroke-width={2} type="circle" percentage={row.ii} />
)
},
// {
// title: '测试', field: 'ceshi',width: 200,
// formatter: (row, column) => {
// // console.log(112,row, column); // 直接{row}或{column} 不会显示内容
// return [<el-tag type={row.ceshi == "可发放" ? "success" : "danger"} onClick={() => editItem(row, 'view')}> {row.ceshi} </el-tag>, <span>{column.label}</span>]
// }
// },
{
title: '操作', field: 'caozuo', width: 220,
formatter: (row) => (
[
<el-button link type="primary" size="small" icon={Tickets} onClick={() => editItem(row, 'view')} >查看</el-button >,
<el-button link type="primary" size="small" icon={EditPen} onClick={() => editItem(row, 'edit')} >编辑</el-button >,
<el-button link type="primary" size="small" icon={Delete} onClick={() => editItem(row, 'delete')} >删除</el-button >,
]
)
},
],
data: Array.from({ length: 7 }).map((ele, index) => {
return {
aa: '客户端不适配',
bb: '客户端无法适配凝思 4.2.40',
cc1: '2023-7-20',
cc2: '2023-7-20 10:10:10',
dd: Math.floor(Math.random() * 5),
ee: '帕拉迪',
ff: 'PLD Sys 61012100',
gg: Math.floor(Math.random() * 20),
hh: Math.floor(Math.random() * 2),
ii: Math.floor(Math.random() * 101),
ceshi: 123,
}
})
})
const clickItem = (row, type) => {
console.log(row, type);
ElMessage({
type: 'success',
message: '点击',
})
}
// 编辑或查看
const editItem = (row, type) => {
switch (type) {
case 'view':
case 'edit':
console.log(row, type);
break;
case 'delete':
deleteItem(row, type)
break;
default:
break;
}
}
// 删除
const deleteItem = (row, type) => {
ElMessageBox.confirm(
'确定删除该列?',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
ElMessage({
type: 'success',
message: 'Delete completed',
})
})
.catch(() => {
})
}
// 子组件调用父组件的测试事件
const updateEditItem = (data) => {
console.log('子组件调用父组件的测试事件', data);
editItem(data.row, data.type)
}
const updateHandleSelectionChange = (data) => {
console.log('updateHandleSelectionChange', data);
}
const updateChangeRadio = (data) => {
console.log('updateChangeRadio', data);
}
const updateSortChange = (data) => {
console.log(data);
}
onMounted(() => {
setTimeout(() => {
nameList.value = [
{ name: '横向隔离', value: 0 },
{ name: '纵向加密', value: 1 },
{ name: '防火墙', value: 2 },
{ name: '监测装置', value: 3 },
{ name: '运维网关', value: 4 },
{ name: '其他', value: 5 },
]
}, 1000);
})
// 将变量和函数返回,以便在模版中使用
</script>
<style lang="scss" scoped></style>
三、vue原生语法版本
封装的table.vue
<template>
<div class="page-view flex-center">
<el-table v-loading="tableData.tableLoading" ref="multipleTableRef" class="jg-table" :data="tableData.data"
style="width: 100%;" @selection-change="handleSelectionChange" @row-click="rowClick" @sort-change="sortChange">
<template v-for="(item, index) in tableData.columnsData " :key="index">
<!-- 多选 -->
<el-table-column v-if="item.type === 'selection'" :type="item.type" :width="item.width" />
<!-- 单选 -->
<el-table-column v-else-if="item.type === 'radio'" :width="item.width" class-name="no-radio-label">
<template #default="{ row, $index }">
<el-radio @change="changeRadio" v-model="radioIndex" :label="row"></el-radio>
</template>
</el-table-column>
<!-- 渲染formatter -->
<el-table-column v-else :property="item.field" :label="item.title" :width="item.width" :min-width="item.minWidth"
:max-idth="item.maxWidth" :showOverflowTooltip="item.showOverflowTooltip" :sortable="item.sortable"
:formatter="item.formatter" />
</template>
</el-table>
</div>
</template>
<script lang="jsx">
import { ref, defineComponent, getCurrentInstance, h } from 'vue' // 按需引入ref函数
import { Edit, Download, QuestionFilled, EditPen, Tickets, Delete } from '@element-plus/icons-vue'
export default defineComponent({
name: 'JgTable',
props: {
description: {
type: String,
default: () => {
return ' '
}
},
tableDataObj: {
type: Object,
default: () => {
return {}
}
},
},
emits: ['update:editItem'],
setup(props, { emit }) {
const { proxy } = getCurrentInstance()
let tableData = ref({})
let radioIndex = ref(null)
watch([() => props.tableDataObj, () => props.rulesData], ([tableDataObj, rulesData]) => {
// console.log('监听');
tableData.value = proxy.$_.cloneDeep(tableDataObj) // 使用lodash的深拷贝方法
}, { deep: true, immediate: true })
function editItem(row, type) {
console.log(row, type,);
// 子传父
emit('update:editItem', { row: row, type: type });
}
const myDiv = () => h('div', null, 'Hello, 1')
// 多选
const handleSelectionChange = (val) => {
console.log(val);
// 子传父
emit('update:handleSelectionChange', val);
}
// 单选
const changeRadio = (val) => {
// console.log('单选', val, radioIndex.value.aa);
emit('update:changeRadio', val);
}
// 表格点击事件
const rowClick = (val) => {
// console.log(val);
}
// 排序
const sortChange = (val) => {
// console.log(val);
emit('update:sortChange', val);
}
// 将变量和函数返回,以便在模版中使用
return {
myDiv,
radioIndex,
tableData,
editItem,
handleSelectionChange,
changeRadio,
rowClick,
sortChange,
EditPen,
Tickets,
Delete,
}
}
})
</script>
<style lang="scss" scoped>
// 去除单选框的内容
:deep(.no-radio-label) {
.el-radio__label {
display: none;
}
}
</style>
index.vue使用
<template>
<JgTable :tableDataObj="tableData" @update:editItem="updateEditItem"
@update:handleSelectionChange="updateHandleSelectionChange" @update:changeRadio="updateChangeRadio"
@update:sortChange="updateSortChange"></JgTable>
</template>
<script lang="jsx">
import { ref, defineComponent, toRefs, reactive, onMounted, getCurrentInstance } from 'vue'
import { Delete, Edit, Download, QuestionFilled, EditPen, Tickets } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
export default {
name: "EquipmentDefect",
setup() {
let tableData = ref({
tableLoading: false,
// title:名称 field:key type:单选复选框 width minWidth maxWidth showOverflowTooltip formatter:格式化内容
columnsData: [
{ title: '多选', type: 'selection', width: 55 },
{ title: '单选', type: 'radio', width: 55 },
{ title: '缺陷名称', field: 'aa', formType: 'input', width: 120 },
{ title: '缺陷描述', field: 'bb', formType: 'input', showOverflowTooltip: true },
{ title: '缺陷发布时间', field: 'cc', formType: 'date', dateType: 'YYYY-MM-DD', width: 140 },
// { title: '缺陷发布时间', field: 'cc', formType: 'datetime', dateType: 'YYYY-MM-DD HH:mm:ss' },
{
title: '设备类型', field: 'dd', formType: 'select', width: 120, selectList: [
{ name: '横向隔离', value: 0 },
{ name: '纵向加密', value: 1 },
{ name: '防火墙', value: 2 },
{ name: '监测装置', value: 3 },
{ name: '运维网关', value: 4 },
{ name: '其他', value: 5 },
],
formatter: (row) => {
const val = [
{ name: '横向隔离', value: 0 },
{ name: '纵向加密', value: 1 },
{ name: '防火墙', value: 2 },
{ name: '监测装置', value: 3 },
{ name: '运维网关', value: 4 },
{ name: '其他', value: 5 },
].find(item => item.value === row.dd)?.name || ''
return <span>{val}</span>
}
},
{ title: '设备厂家', field: 'ee', formType: 'input' },
{ title: '设备版本', field: 'ff', formType: 'input', width: 160, showOverflowTooltip: true },
{
title: '缺陷设备数量', field: 'gg', formType: 'input', sortable: 'custom',
formatter: (row, column) => {
// console.log(112,row, column); // 直接{row}或{column} 不会显示内容
return [<span onClick={() => clickItem(row, 'view')}> {row.gg} </span>]
}
},
{
title: '缺陷反馈状态', field: 'hh', formType: 'radio', minWidth: 100, selectList: [
{ name: '已反馈', value: 1 },
{ name: '未反馈', value: 0 },
],
formatter: (row) => (
row ? <el-tag style="border: none;" class="cursor" type={row.hh === 1 ? 'green' : 'warning'}>{row.hh === 1 ? '已反馈' : row.hh === 0 ? '未反馈' : ''}</el-tag> : ''
)
},
{
title: '缺陷处置进度', field: 'ii', formType: 'progress',
formatter: (row) => (
<el-progress class="table_progress" width="13" stroke-width="2" type="circle" percentage={row.ii} />
)
},
// {
// title: '测试', field: 'ceshi',width: 200,
// formatter: (row, column) => {
// // console.log(112,row, column); // 直接{row}或{column} 不会显示内容
// return [<el-tag type={row.ceshi == "可发放" ? "success" : "danger"} onClick={() => editItem(row, 'view')}> {row.ceshi} </el-tag>, <span>{column.label}</span>]
// }
// },
{
title: '操作', field: 'caozuo', width: 220,
formatter: (row) => (
[
<el-button link type="primary" size="small" icon={Tickets} onClick={() => editItem(row, 'view')} >查看</el-button >,
<el-button link type="primary" size="small" icon={EditPen} onClick={() => editItem(row, 'edit')} >编辑</el-button >,
<el-button link type="primary" size="small" icon={Delete} onClick={() => editItem(row, 'delete')} >删除</el-button >,
]
)
},
],
data: Array.from({ length: 7 }).map((ele, index) => {
return {
aa: '客户端不适配',
bb: '客户端无法适配凝思 4.2.40',
cc: '2023-7-20',
dd: Math.floor(Math.random() * 5),
ee: '帕拉迪',
ff: 'PLD Sys 61012100',
gg: Math.floor(Math.random() * 20),
hh: Math.floor(Math.random() * 2),
ii: Math.floor(Math.random() * 101),
ceshi: 123,
}
})
})
const clickItem = (row, type) => {
console.log(row, type);
ElMessage({
type: 'success',
message: '点击',
})
}
// 编辑或查看
const editItem = (row, type) => {
switch (type) {
case 'view':
case 'edit':
console.log(row, type);
break;
case 'delete':
deleteItem(row, type)
break;
default:
break;
}
}
// 删除
const deleteItem = (row, type) => {
ElMessageBox.confirm(
'确定删除该列?',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
ElMessage({
type: 'success',
message: 'Delete completed',
})
})
.catch(() => {
})
}
// 子组件调用父组件的测试事件
const updateEditItem = (data) => {
console.log('子组件调用父组件的测试事件', data);
editItem(data.row, data.type)
}
const updateHandleSelectionChange = (data) => {
console.log('updateHandleSelectionChange', data);
}
const updateChangeRadio = (data) => {
console.log('updateChangeRadio', data);
}
const updateSortChange = (data) => {
console.log(data);
}
onMounted(() => {
})
// 将变量和函数返回,以便在模版中使用
return {
tableData,
ElMessageBox,
clickItem, // 点击
editItem, // 查看编辑
deleteItem, // 删除
// 子传父表格事件
updateEditItem,
updateHandleSelectionChange,
updateChangeRadio,
updateSortChange,
// icon
Edit,
Download,
QuestionFilled,
EditPen,
Tickets,
Delete,
};
},
};
</script>
<style lang="scss" scoped></style>