在平常的vue项目开发中,我们经常会遇见现有的UI无法满足需求的情况。今天就给大家分享下,如何对element-ui的表格组件,进行二级封装改造。
例如:随着项目功能的不断变化,简单的element-ui表格已经不能满足需求。客户要求表格不再只是展示数据,而是要进行数据编辑,并支持日期时间、数字、多选树时。我们就要对现有UI组件进行二次改造。
可编辑表格是在Element-ui里Table表格的基础上进行二次封装,使其支持多种输入框类型,支持输入框类型如下:普通类型(input)、数字类型(number)、下拉选(select)、单选(radio)、日期框(datetime || date)、下拉树(selectTree:引用的为vue第三方插件vue-treeselect)。
文件如下(示例):
src
├── ...
├── main.js
├── App.vue
├── components
├── EditTable //可编辑表格组件
├── index.vue //主要内容
├── SelectTree //多选树组件
├── index.vue //主要内容
├── utils // 定义的公共方法
├── views // 页面
├── Table
├── index.vue //使用组件
代码如下:
多选树组件使用的是 @riophae/vue-treeselect
npm install @riophae/vue-treeselect
element-ui、vue等自行安装
代码如下:在components/SelectTree/index.vue中新增以下代码(对@riophae/vue-treeselect的二次封装,方便使用)
<template>
<treeselect v-model="formInline[selectTreeParams.code]" :multiple="selectTreeParams.options.multiple"
:disabled="disabled" :limit="selectTreeParams.options.limit" :appendToBody="
selectTreeParams.options.appendToBody
? selectTreeParams.options.appendToBody
: false
" :normalizer="normalizer" :options="selectTreeParams.data" :placeholder="'请选择' + selectTreeParams.title"
:class="disabled ? 'treeSelect-new' : ''" />
template>
<script>
// 引入多选树依赖
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
props: {
formInline: {
type: Object,
required: false,
default: () => ({}),
},
selectTreeParams: {
type: Object,
required: false,
default: () => ({
title: "运维单位",
code: "opsDeptId",
data: [], //数据源
labelCode: {
id: "id",
label: "shortName",
children: "children",
},
options: {
multiple: false, //是否多选
limit: 1,
// disableBranchNodes: true,
},
}),
},
disabled: {
type: Boolean,
default: false,
},
},
watch: {},
components: {
Treeselect,
},
data() {
return {};
},
created() {
let that = this;
},
mounted() { },
methods: {
/** 转换组织机构树数据结构 */
normalizer(node) {
let that = this;
// console.log('node',node);
let children = that.selectTreeParams.labelCode.children;
let label = that.selectTreeParams.labelCode.label;
let id = that.selectTreeParams.labelCode.id;
if (node[children] && !node[children].length) {
delete node[children];
}
return {
id: node[id],
label: node[label],
children: node[children],
};
},
},
};
script>
<style scoped>
style>
代码如下:在components/EditTable/index.vue中新增以下代码
<template>
<div class="mhy-app-editTable glfg">
<div class="mhy-editTable-table">
<el-table :data="tableOptions.data" ref="tableDataRef"
:height="tableOptions.height ? tableOptions.height : 'auto'" border>
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="序号" type="index" width="60" align="center" fixed="left">
el-table-column>
<el-table-column align="center" :prop="item.code" :width="item.width != undefined ? item.width : ''"
:fixed="item.fixed != undefined ? item.fixed : false" :label="item.title"
v-for="(item, i) in tableOptions.title" :key="'item_' + i">
<template slot="header" slot-scope="{ column, $index }">
<i v-if="item.required" style="color: red">*i>
<span>{{ item.title }}span>
template>
<template slot-scope="scope">
<div class="editTable-input" v-if="item.type == 'input'">
<el-input v-model.trim="scope.row[item.code]" :placeholder="'请输入' + item.title"
:disabled="cellDisabled(item, scope.row)" @change="cellEvent(item, scope)">el-input>
div>
<div class="editTable-input" v-if="item.type == 'number'">
<el-input-number v-model.trim="scope.row[item.code]" :placeholder="'请输入' + item.title"
:disabled="cellDisabled(item, scope.row)" :controls="false" :min="0" :precision="2"
@change="cellEvent(item, scope)" @blur="cellEvent(item, scope)">el-input-number>
div>
<div class="editTable-input" v-if="item.type == 'select' && item.code != 'county'">
<el-select v-model="scope.row[item.code]" :placeholder="'请选择' + item.title"
:disabled="cellDisabled(item, scope.row)" @change="cellEvent(item, scope)" clearable>
<el-option v-for="item3 in tableOptions.options[item.options].data"
:key="item3[tableOptions.options[item.options].value]"
:label="item3[tableOptions.options[item.options].label]"
:value="item3[tableOptions.options[item.options].value]">
el-option>
el-select>
div>
<div class="editTable-input" v-if="item.type == 'select' && item.code == 'county'">
<el-select v-model="scope.row[item.code]" :placeholder="'请选择' + item.title"
:disabled="cellDisabled(item, scope.row)" @change="cellEvent(item, scope)" clearable>
<template v-for="item3 in tableOptions.options[item.options].data">
<el-option v-if="item3.parentCode == scope.row['city']"
:key="item3[tableOptions.options[item.options].value]"
:label="item3[tableOptions.options[item.options].label]"
:value="item3[tableOptions.options[item.options].value]">
el-option>
template>
el-select>
div>
<div class="editTable-input" v-if="item.type == 'radio'">
<el-radio v-model="scope.row[item.code]" v-for="(item1, i1) in item.options" :label="item1.value"
:key="'item1_' + i1">{{ item1.label }}el-radio>
div>
<div class="editTable-input" v-if="item.type == 'selectTree'">
<SelectTree ref="selectTreeRef" :disabled="disabled" :formInline="scope.row"
:selectTreeParams="getSelectTreeParams(item)" />
div>
<div class="editTable-input" v-if="item.type == 'date' || item.type == 'datetime'">
<el-date-picker v-model="scope.row[item.code]" :type="item.type" :disabled="cellDisabled(item, scope.row)"
:value-format="
item.type == 'date' ? 'yyyy-MM-dd' : 'yyyy-MM-dd HH:mm:ss'
" placeholder="请选择日期">
el-date-picker>
div>
template>
el-table-column>
<el-table-column v-if="tableOptions.btns.list.length !== 0" label="操作" align="center"
class-name="small-padding fixed-width" :width="tableOptions.btns.width" fixed="right">
<template slot-scope="scope">
<el-button v-for="(item2, i2) in tableOptions.btns.list" :key="'item2_' + i2" :icon="item2.icon"
:disabled="Disabled(scope.row)" @click="columnEvent(item2.type, scope)" type="success" plain size="mini">
{{ item2.name }}
el-button>
template>
el-table-column>
el-table>
div>
div>
template>
<script>
import SelectTree from "@/components/SelectTree/index.vue"; //下拉选择树
export default {
props: {
tableOptions: {
type: Object,
required: false,
default: () => ({}),
},
type: {
type: String,
required: false,
default: "dd",
},
disabled: {
type: Boolean,
default: false
}
},
components: { SelectTree },
data() {
return {
//选择运维单位参数
selectTreeParams: {
title: "运维单位",
code: "",
data: [], //数据源
labelCode: {
id: "id",
label: "shortName",
children: "children",
},
options: {
multiple: false, //是否多选
disableBranchNodes: true, //是否阻止选择分支
appendToBody: true,
},
},
};
},
created() {
let that = this;
that.init();
},
mounted() { },
methods: {
init() {
let that = this;
},
//点击表格操作列按钮
columnEvent(type, scope) {
let that = this;
that.$emit("columnEvent", {
type: type,
row: scope.row,
index: scope.$index,
});
},
//表格操作单元格事件
cellEvent(item, scope) {
let that = this;
if (item.change) {
that.$emit("cellEvent", {
code: item.code,
type: item.type,
row: scope.row,
index: scope.$index,
});
}
},
//获取表格选中行
getTableSelect() {
let that = this;
return that.$refs.tableDataRef.selection;
},
//单元格是否可编辑
cellDisabled(item, row) {
// console.log(row);
if (item.disable == true) {
return true;
} else {
if (item.isIdDisable && item.isIdDisable.bool == true) {
// alert(row[item.isIdDisable.code]);
return (
row[item.isIdDisable.code] != undefined &&
row[item.isIdDisable.code] != ""
);
}
}
return false;
},
// 操作列按钮是否可编辑
Disabled(row) {
let that = this;
if (that.type == "ht") {
if (row.orderId != null && row.orderId) {
return true;
} else {
return false;
}
} else if (that.type == "dd") {
if (row.reqId != null && row.reqId) {
return true;
} else {
return false;
}
}
},
//获取下拉树参数
getSelectTreeParams(item) {
let that = this;
that.selectTreeParams.code = item.code;
that.selectTreeParams.data = that.tableOptions.options[item.options].data;
return that.selectTreeParams;
},
//验证表格参数
validateTableCell() {
let that = this;
let resultJson = { result: false, msg: '' };
let tableData = that.tableOptions.data;//表格数据
// let tableCellValidate=that.tableOptions.title.
// console.log("验证:"+JSON.stringify(tableData));
//循环验证
for (let i = 0; i < tableData.length; i++) {
let tempTableRow = tableData[i];
for (let rowCell in tempTableRow) {
let tempValue = tempTableRow[rowCell];
let cellJson = that.getCellValidate(rowCell);
let cellValidate = cellJson.rulesJson;
let resultMsg = that.tableOptions.name + '第' + (i + 1) + '行' + cellJson.title + ',';
// console.log("当前列:"+JSON.stringify(cellJson));
let input = ((tempValue == '' || tempValue == null) && cellJson.type === 'input')
let select = ((tempValue == '' || tempValue == null) && cellJson.type === 'select')
let date = (tempValue == null && cellJson.type === 'date')
if (cellJson.required && (input || select || date)) {
resultJson.result = true;
resultJson.msg = (resultMsg + '不能为空');//错误信息
return resultJson;
}
else {
if (cellValidate != undefined) {
for (let j = 0; j < cellValidate.length; j++) {
//循环验证规则
let reg = cellValidate[j].reg;//正则
if (reg != undefined) {
let tempReg = new RegExp(reg);
if (!tempReg.test(tempValue)) {
// console.log("验证成果");
resultJson.result = true;
resultJson.msg = (resultMsg + cellValidate[j].msg);//错误信息
return resultJson;
}
else {
}
} else {
//执行自定义验证
let callbackResult = cellValidate[j].callback(tempValue);
if (callbackResult.result) {
callbackResult.msg = (resultMsg + callbackResult.msg);
resultJson = callbackResult;
return resultJson;
}
}
}
}
}
}
}
return resultJson;
},
getCellValidate(code) {
let that = this;
let tableValidate = that.tableOptions.title;//表格验证
let cellValidate = {};
for (let i = 0; i < tableValidate.length; i++) {
let tempCode = tableValidate[i].code;
if (tempCode == code) {
cellValidate = tableValidate[i];
}
}
return cellValidate;
}
},
};
script>
<style scoped>
style>
代码示例如下:
<template>
<div class="app-container">
<div class="edit-table">
<p class="title">1.可编辑表格p>
<p class="describe">
可编辑表格是在Element-ui里Table表格的基础上进行二次封装,使其支持多种输入框类型,支持输入框类型如下:普通类型(input)、数字类型(number)、下拉选(select)、单选(radio)、日期框(datetime
|| date)、下拉树(selectTree:引用的为vue第三方插件vue-treeselect)p>
<div class="editTable-btns app-flex">
<el-button type="success" icon="el-icon-edit" @click="addRow">添加行
el-button>
<el-button type="danger" icon="el-icon-delete" @click="deleteRow">删除行
el-button>
<el-button type="primary" icon="el-icon-check" @click="submitTable">保存
el-button>
div>
<EditTable ref="editTableRef" :table-options="tableOptions" @cellEvent="cellEvent" @columnEvent="columnEvent" />
div>
div>
template>
<script>
import EditTable from '@/components/EditTable/index.vue'; // 可编辑表格
// 表格行对象
const BasicTableColumn = {
id: null, //表格行ID
input: null,
radio: 1,
select: null,
number: null,
selectTree: null,
date: null
}
export default {
name: 'Table',
// 注册组件
components: {
EditTable,
},
data() {
return {
// 可编辑表格配置项
tableOptions: {
// 表格名称
name: '可编辑表格',
// 表格高度 可不写,默认为auto
height: 400,
// 表格中select选择框、vue-treeselect选项树数据项
options: {
selectTreeOptions: { label: 'name', value: 'id', data: [] }, // 运维单位
selectOptions: {
label: 'name',
value: 'code',
data: [
{ name: '选项1', code: 1 },
{ name: '选项2', code: 2 },
{ name: '选项3', code: 3 }
]
}
},
// 表格标题
/*
* title: 表头标题中文名称
* code: 表头标题英文字段
* required: 是否是必填项
* type: 类型
* width: 表头标题宽度 默认值auto
* disable: 是否禁用
* change: 编辑当前单元格是否触发事件,默认不开启,如需操作当前单元格进行事件处理时,需配置为true
* isIdDisable: 项目特殊添加,是否根据前置条件禁用 此项目是在有外来数据带入的情况下,禁止修改表格当前行(*默认可不配置此选项)
* rulesJson: 自定义校验规则 此配置可不填默认,当required为true时,默认非空验证,配置此项后,按当前配置进行表格验证
*/
title: [
{
title: 'Input输入框',
code: 'input',
required: false,
type: 'input',
// width: 240,
disable: false,
change: true,
isIdDisable: {
bool: true,
code: 'orderId'
}
},
{
title: 'Radio单选框',
code: 'radio',
required: false,
type: 'radio',
// width: 240,
options: [
{ label: '是', value: 1 },
{ label: '否', value: 0 }
]
},
{
title: 'Select选择器',
code: 'select',
required: true,
type: 'select',
// width: 240,
options: 'selectOptions',
},
{
title: 'InputNumber计数器',
code: 'number',
required: true,
change: true,
type: 'number',
// width: 240,
disable: false,
rulesJson: [
{ reg: '^[0-9]*$', msg: "只能输入数字" }
]
},
{
title: 'vue-treeselect多选树',
code: 'selectTree',
required: false,
// width: 240,
type: 'selectTree',
options: 'selectTreeOptions',
},
{
title: "DatePicker日期选择器",
code: "date",
required: false,
type: "date",
// width: 240,
disable: false,
},
],
data: [],
btns: {
width: 300,
list: [
{
name: '操作列按钮',
icon: 'el-icon-tickets',
type: 'glZy'
}
]
}
},
};
},
created() {
},
methods: {
// 可编辑表格事件
// 添加行
addRow() {
const that = this;
// Utils为定义的公共方法,目的在于深拷贝数据
/**
* 深拷贝json对象
* @param {JSON} obj
* @returns {string}
*/
// copyArray(obj) { return JSON.parse(JSON.stringify(obj)) }
that.tableOptions.data.push(that.Utils.copyArray(BasicTableColumn));
},
// 删除行
deleteRow() {
const that = this;
const selectActive = that.$refs['editTableRef'].getTableSelect();
if (selectActive.length == 0) {
return that.$message.warning('请至少选择一个');
}
that.tableOptions.data = that.tableOptions.data.filter(
(t) => !selectActive.some((s) => s === t)
);
},
// 操作列按钮
columnEvent(obj) {
const that = this;
const index = obj.index;
alert('操作列按钮')
// 书写表格尾部操作列的逻辑
},
// 单元格数据变化
// 只有表格头部字段的change属性设置为true时,修改编辑输入框时才会触发该事件
cellEvent(obj) {
const that = this;
const code = obj.code; // 当前操作行的字段
const index = obj.index; // 当前操作行
// 参数obj中包含当前操作行的数据,当前操作行的输入框字段、输入框类型,当前操作行的索引值
},
// 保存表格
submitTable() {
let that = this;
//循环验证表格
let resultBool = that.$refs.editTableRef.validateTableCell()
if (resultBool.result) {
that.$message.warning(resultBool.msg);
return;
}
if(that.tableOptions.data.length === 0) {
that.$message.warning('请至少添加一条数据!');
return;
}
}
}
}
script>
<style scoped>
style>
例如:以上就是今天要讲的内容,本文仅仅简单介绍了对element-ui中的表格进行二次封装的使用,代码不够简化,大家可以多多提意见。