ElementUI table+dialog实现一个简单的可编辑的表格
table组件如何实现可编辑呢?
我的需求是把table组件那样的表格,实现它点击可以弹出一个框,然后在这个框里面输入你的东西,然后将他回显回去,当然,输入的有可能是时间啥的。
为什么要弹出弹层不在框上直接编辑呢?因为框太小了,用户可能看不见输入的是啥。
我们可以利用element组件的插槽,获取数值,然后回显到表格里。
HTML部分
我用了一个弹层来写这个功能,如果测试效果,可以直接在主页面引入 ,将弹层的控制开关改为true。
<template>
<el-drawer v-model="drawerVisible" size="calc(100% - 210px)" @close="resetForm">
<el-container>
<el-form ref="ruleFormRef2" label-position="right" :model="form" label-width="138px">
<div class="form-left">
<div class="sepcial_about">
<div class="tableForm">
<el-table :data="testDatas" border style="width: 700px">
<el-table-column v-for="(col, idx) in columnList" :key="col.prop" :index="idx" :width="idx === 1 ? '180px' : ''">
<template #header>
<p>
{{ col.label }}
</p>
</template>
<template #default="{ row }">
<p :class="col.prop == 'remark' ? 'p_remark' : 'p1111'" @click="e => handleEdit(0, row, col)">
{{ col.prop == "time" ? changeTime(row[col.prop]) : row[col.prop] }}
</p>
</template>
</el-table-column>
<el-table-column width="120">
<template #header>
<p class="p_specail">操作</p>
</template>
<template #default="scope">
<p class="p1111">
<el-button link type="primary" size="large" @click.prevent="delRow(scope, 0)">删除</el-button>
</p>
</template>
</el-table-column>
</el-table>
<el-button type="primary" class="mt-4" style="width: 100%" @click="addRow(0)">新增</el-button>
</div>
</div>
</div>
</el-form>
<el-dialog v-model="dialogVisible" title="添加" width="30%" center align-center>
<span>{{ sureMsg }}</span>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="toSure">确 认</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="addDialog" :title="addTitle" width="30%" center align-center>
<el-date-picker v-if="isTime" v-model="addinput" type="daterange" format="YYYY/MM/DD" value-format="YYYY-MM-DD" />
<el-input
v-if="isNum"
resize="none"
maxlength="15"
show-word-limit
v-model="addinput"
type="textarea"
size="large"
placeholder="请输入"
/>
<el-input
v-if="!isNum && !isTime"
resize="none"
maxlength="500"
show-word-limit
v-model="addinput"
type="textarea"
size="large"
placeholder="请输入"
/>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="sava">保存</el-button>
</span>
</template>
</el-dialog>
</el-container>
</el-drawer>
</template>
JS
1.渲染数据集合
自定义的表头,HTML结构中通过v-for 循环表头列表,进行label的取值来构建最基础的表格
// 表格之中的数据
const testDatas: any = ref([]);
// 表头列表
const columnList: any = ref([
{ prop: "projectName", label: "测试1" },
{ prop: "time", label: "时间段" },
{ prop: "projectContentSummary", label: "测试2" }
]);
2.handleEdit函数
这个函数主要是用来控制弹层的弹出,通过行的标签名字判断需要什么输入的值。根据whatSava的值来判断你点击的是哪个表格,这个参数在HTML结构写死,cellRow和cellCol就是在渲染的时候带入的行的值和列的值。可以获得标签名字或者prop字段用来做判断。当然,这些名字也可以用来设置弹出弹框的弹框名字。
然后判断prop的值来看是不是需要输入 或者是 时间段格式的,让各自的开关变为true或者false
并且根据cellRow[cellCol.prop]这个值来对弹出来的框进行赋值,也就是回显。
const addinput = ref("");
const addTitle = ref("");
const addCode = ref();
const addProp = ref();
const isTime = ref(false);
const savaWhat = ref();
const isNum = ref(false);
const handleEdit = (whatSava: any, cellRow: any, cellCol: any) => {
if (whatSava == "0") {
savaWhat.value = 0;
}
if (cellCol.prop == "ceshi1" || cellCol.prop == "ceshi2") {
isTime.value = false;
if (cellCol.prop == "ceshi2") {
isNum.value = true;
} else {
isNum.value = false;
}
addTitle.value = cellCol.label;
addProp.value = cellCol.prop;
addCode.value = cellRow.index;
addinput.value = cellRow[cellCol.prop];
addDialog.value = true;
} else {
isTime.value = true;
addTitle.value = cellCol.label;
addProp.value = cellCol.prop;
addCode.value = cellRow.index;
addinput.value = cellRow[cellCol.prop];
addDialog.value = true;
}
};
3.保存弹框函数
这个函数就是弹出层的保存函数,先找到你点击的是哪个框,然后把弹框里面输入的东西赋值给他,如果是数字的话,就用正则限制一下,不让他输入数字以外的东西。最后要清空一下,防止下次回显。
const sava = () => {
let idx = testDatas.value.findIndex((p: any) => p.index == addCode.value);
if (addProp.value == "ceshi2") {
if (!/^[0-9]*$/.test(addinput.value)) {
ElMessage.warning("请输入数字");
return;
}
testDatas.value[idx][addProp.value] = addinput.value;
} else {
testDatas.value[idx][addProp.value] = addinput.value;
}
addinput.value = "";
addDialog.value = false;
};
4.清洗时间格式函数
因为我们日期时间选择器,得到的是一个数组,所以我们回显的时候,应该拿数组的第一项和第二项,然后拼接起来,得到我们回显的正确格式。
//时间格式清洗
const changeTime = (data: any) => {
if (!data) {
return "";
} else {
let data1 = data[0] + "~" + data[1];
return data1;
}
};
5.添加函数
只需要我们知道添加的是哪个表格的,单表格就可以忽略whatAdd这个参数。
逻辑就是先判断他是不是为空,为空的话给他单纯的加一行,不为空的话,得到数组的长度,给他加一个index,我在写的过程中,发现我们没有办法获得一个标识,所以我写了一个index来当作唯一标识,当然,这个在获取后端接口的时候,需要你获取后端给你数组的长度,然后再依次相加就不会导致错误的出现。
// 添加一行
const addRow = (whatAdd: number) => {
if (whatAdd == 0) {
let obj: any = {};
if (testDatas.value.length == 0) {
obj.index = "0";
columnList.value.forEach((p: any) => {
obj[p.prop] = "";
});
testDatas.value[0] = obj;
} else {
const weiyi = testDatas.value[testDatas.value.length - 1].index;
columnList.value.forEach((p: any) => {
obj[p.prop] = "";
});
obj.index = String(Number(weiyi) + 1);
testDatas.value[testDatas.value.length] = obj;
}
}
};
6.删除函数
因为删除标签在table表格之中,所以我们可以用scope准确的获得是哪一行,然后用whatDel判断你删除的是哪个表格。
//删除
const delRow = (scope: any, whatDel: number) => {
const arrIndex = scope.$index; //唯一标识
//将testDatas中的第arrIndex项数据删除
if (whatDel == 0) {
testDatas.value.splice(arrIndex, 1);
}
};
CSS
<style scoped lang="scss">
:deep(.el-textarea__inner) {
height: 200px;
}
:deep(.cell) {
text-align: center !important;
height: 45px;
padding: 0;
}
.p1111 {
height: 50px;
line-height: 50px !important;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0;
margin: 0;
text-align: center !important;
}
.tableForm {
width: 700px;
margin-left: 35px;
margin-top: 20px;
}
.sepcial_about {
width: 500px;
padding-left: 40px;
box-sizing: border-box;
margin-bottom: 60px;
position: relative;
> :first-child {
width: 1000px;
margin-bottom: 10px;
}
}
.sepcial_about .mt-4 {
position: absolute;
width: 40px !important;
height: 30px !important;
bottom: 10px;
left: 800px;
z-index: 999;
}
.sepcial_about :deep(.el-table) {
display: block;
height: 100%;
}
:deep(.el-date-editor) {
width: 100% !important;
height: 50px;
text-align: center;
}
.form-left {
padding-top: 20px;
}
:deep(.el-form-item__content) {
/* margin-top: 20px; */
margin-left: 40px !important;
}
:deep(.el-form-item__label) {
font-size: 16px;
color: black;
}
</style>
全部代码
<template>
<el-drawer v-model="drawerVisible" size="calc(100% - 210px)" @close="resetForm">
<el-container>
<el-form ref="ruleFormRef2" label-position="right" :model="form" label-width="138px">
<div class="form-left">
<div class="sepcial_about">
<div class="tableForm">
<el-table :data="testDatas" border style="width: 700px">
<el-table-column v-for="(col, idx) in columnList" :key="col.prop" :index="idx" :width="idx === 1 ? '180px' : ''">
<template #header>
<p>
{{ col.label }}
</p>
</template>
<template #default="{ row }">
<p :class="col.prop == 'remark' ? 'p_remark' : 'p1111'" @click="e => handleEdit(0, row, col)">
{{ col.prop == "time" ? changeTime(row[col.prop]) : row[col.prop] }}
</p>
</template>
</el-table-column>
<el-table-column width="120">
<template #header>
<p class="p_specail">操作</p>
</template>
<template #default="scope">
<p class="p1111">
<el-button link type="primary" size="large" @click.prevent="delRow(scope, 0)">删除</el-button>
</p>
</template>
</el-table-column>
</el-table>
<el-button type="primary" class="mt-4" style="width: 100%" @click="addRow(0)">新增</el-button>
</div>
</div>
</div>
</el-form>
<el-dialog v-model="addDialog" :title="addTitle" width="30%" center align-center>
<el-date-picker v-if="isTime" v-model="addinput" type="daterange" format="YYYY/MM/DD" value-format="YYYY-MM-DD" />
<el-input
v-if="isNum"
resize="none"
maxlength="15"
show-word-limit
v-model="addinput"
type="textarea"
size="large"
placeholder="请输入"
/>
<el-input
v-if="!isNum && !isTime"
resize="none"
maxlength="500"
show-word-limit
v-model="addinput"
type="textarea"
size="large"
placeholder="请输入"
/>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="sava">保存</el-button>
</span>
</template>
</el-dialog>
</el-container>
</el-drawer>
</template>
<script setup lang="tsx" name="addConfig">
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
const drawerVisible = ref(false);
const addDialog = ref(false);
const addinput = ref("");
const testDatas: any = ref([]);
const columnList: any = ref([
{ prop: "ceshi1", label: "测试1" },
{ prop: "time", label: "时间段" },
{ prop: "ceshi2", label: "测试2" }
]);
const addTitle = ref("");
const addCode = ref();
const addProp = ref();
const isTime = ref(false);
const savaWhat = ref();
const isNum = ref(false);
const handleEdit = (whatSava: any, cellRow: any, cellCol: any) => {
if (whatSava == "0") {
savaWhat.value = 0;
}
if (cellCol.prop == "ceshi1" || cellCol.prop == "ceshi2") {
isTime.value = false;
if (cellCol.prop == "ceshi2") {
isNum.value = true;
} else {
isNum.value = false;
}
addTitle.value = cellCol.label;
addProp.value = cellCol.prop;
addCode.value = cellRow.index;
addinput.value = cellRow[cellCol.prop];
addDialog.value = true;
} else {
isTime.value = true;
addTitle.value = cellCol.label;
addProp.value = cellCol.prop;
addCode.value = cellRow.index;
addinput.value = cellRow[cellCol.prop];
addDialog.value = true;
}
};
const sava = () => {
let idx = testDatas.value.findIndex((p: any) => p.index == addCode.value);
if (addProp.value == "ceshi2") {
if (!/^[0-9]*$/.test(addinput.value)) {
ElMessage.warning("请输入数字");
return;
}
testDatas.value[idx][addProp.value] = addinput.value;
} else {
testDatas.value[idx][addProp.value] = addinput.value;
}
addinput.value = "";
addDialog.value = false;
};
let form: any = reactive({});
//时间格式清洗
const changeTime = (data: any) => {
if (!data) {
return "";
} else {
let data1 = data[0] + "~" + data[1];
return data1;
}
};
//清空数据
const resetForm = () => {
form.companyName = "";
form.linkman = "";
form.companyType = "";
form.contactTitle = "";
form.contactPhone = "";
form.id1 = [];
form.id2 = [];
form.idCardPhoto = [];
form.publicCreditInformationReportOfEnterpriseInstitutions = [];
form.businessLicenseOrLegalRepresentativeCertificate = [];
form.lastYearBankCertificationDocuments = [];
form.taxPaymentMaterialsForThePastYear = [];
form.employeePaymentCertificateForThePastYear = [];
form.declarationOfMajorIllegalRecords = [];
form.representativeProjects = [];
form.actualImplementationSituation = [];
form.other = [];
testDatas.value = [];
};
// 添加一行
const addRow = (whatAdd: number) => {
if (whatAdd == 0) {
let obj: any = {};
if (testDatas.value.length == 0) {
obj.index = "0";
columnList.value.forEach((p: any) => {
obj[p.prop] = "";
});
testDatas.value[0] = obj;
} else {
const weiyi = testDatas.value[testDatas.value.length - 1].index;
columnList.value.forEach((p: any) => {
obj[p.prop] = "";
});
obj.index = String(Number(weiyi) + 1);
testDatas.value[testDatas.value.length] = obj;
}
}
};
//删除
const delRow = (scope: any, whatDel: number) => {
const arrIndex = scope.$index; //唯一标识
//将testDatas中的第arrIndex项数据删除
if (whatDel == 0) {
testDatas.value.splice(arrIndex, 1);
}
};
const acceptParams = () => {
drawerVisible.value = true;
};
defineExpose({
acceptParams
});
</script>
<style scoped lang="scss">
:deep(.el-textarea__inner) {
height: 200px;
}
:deep(.cell) {
text-align: center !important;
height: 45px;
padding: 0;
}
.p1111 {
height: 50px;
line-height: 50px !important;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0;
margin: 0;
text-align: center !important;
}
.tableForm {
width: 700px;
margin-left: 35px;
margin-top: 20px;
}
.sepcial_about {
width: 500px;
padding-left: 40px;
box-sizing: border-box;
margin-bottom: 60px;
position: relative;
> :first-child {
width: 1000px;
margin-bottom: 10px;
}
}
.sepcial_about .mt-4 {
position: absolute;
width: 40px !important;
height: 30px !important;
bottom: 10px;
left: 800px;
z-index: 999;
}
.sepcial_about :deep(.el-table) {
display: block;
height: 100%;
}
:deep(.el-date-editor) {
width: 100% !important;
height: 50px;
text-align: center;
}
.form-left {
padding-top: 20px;
}
:deep(.el-form-item__content) {
/* margin-top: 20px; */
margin-left: 40px !important;
}
:deep(.el-form-item__label) {
font-size: 16px;
color: black;
}
</style>