开头先BB两句
回头看自己以前的代码,感觉就像屎一样。我会想,为什么不封装一下,如果用的是ElementUI框架,也可以在此基础上进行二次封装。
譬如说,这个用来对列表数据进行增删改查的弹框。
开始封装
原本只是个小功能,但是别的模块也需要用到。
我的想法就是,把弹框标题,table表头,必填字节,接口请求路径,增删改查CRUD,等等,放在一个json对象里面。通过父组件向子组件传参的方式,展示不同内容,调用不同的接口。
极大提高了代码的复用性。
json对象如下所示
// 示例:
let example = {
// 弹框标题
popTitle: "编辑主题",
// table
columnList: [
{
prop: "themeName",
label: "主题名称",
},
{
prop: "themeDescribe",
label: "主题描述",
},
],
// 必填的字段
requiredKeys: ["themeName"],
tableData: this.themeList,
// 主键,默认为id
idKey: "id",
// 删除的参数名称,默认为ids
deleteKey: "ids",
// 批量的参数名称,默认为ids
batchDeleteKey: "ids",
// 接口请求路径,增删改查CRUD
interfaceUrl: {
add: "/target/addTheme",
edit: "/target/updateTheme",
delete: "/target/deleteTheme",
// 批量删除
batchDelete: "/target/deleteTheme",
list: "/target/themelist",
},
};
table表头作为列表传入,数据结构如下
columnList: [
{
prop: "themeName",
label: "主题名称",
},
{
prop: "themeDescribe",
label: "主题描述",
},
],
在子组件中循环渲染出表头
<el-table-column
v-for="(item, index) in columnList"
:key="index"
:show-overflow-tooltip="item.showOverflowTooltip || true"
:align="item.align || 'center'"
:header-align="item.headerAlign || item.align || 'center'"
:label="item.label"
:width="item.width"
>
<template slot-scope="scope">
<span v-if="scope.row.statusBtn === false">{{ scope.row[item.prop] }}</span>
<el-input
v-else-if="scope.row.statusBtn === true"
v-model="scope.row[item.prop]"
size="mini"
/>
</template>
</el-table-column>
在父组件中调用
<!-- 编辑主题的弹框 -->
<edit-table-modal
ref="editTableModal"
:visible.sync="editTableModal.show"
:tableObject="themeTableObject"
v-if="editTableModal.show"
@ok="editTableFunction"
/>
完整代码,点击下方详细信息查看
<template>
<el-dialog
v-if="visible"
v-dialogDrag
:visible.sync="visible"
:modal="true"
:before-close="cancelModal"
:title="title"
center
>
<div
v-loading="loading"
class="base-box"
element-loading-text="加载中"
element-loading-spinner="el-icon-loading"
>
<el-button
type="primary"
icon="el-icon-plus"
style="margin-bottom: 10px"
size="mini"
@click="addRow()"
>添加</el-button
>
<el-button
v-if="selectionList.length"
type="danger"
icon="el-icon-close"
style="margin-bottom: 10px"
size="mini"
@click="batchDelete()"
>批量删除</el-button
>
<el-table
:data="tableData"
style="width: 100%"
:height="tableHeight"
border
size="small"
:header-cell-class-name="headerClassNameFun"
@selection-change="handleSelectionChange"
>
<el-table-column
align="center"
header-align="center"
type="selection"
width="55"
/>
<el-table-column
v-for="(item, index) in columnList"
:key="index"
:show-overflow-tooltip="item.showOverflowTooltip || true"
:align="item.align || 'center'"
:header-align="item.headerAlign || item.align || 'center'"
:label="item.label"
:width="item.width"
>
<template slot-scope="scope">
<span v-if="scope.row.statusBtn === false">{{ scope.row[item.prop] }}</span>
<el-input
v-else-if="scope.row.statusBtn === true"
v-model="scope.row[item.prop]"
size="mini"
/>
</template>
</el-table-column>
<el-table-column
align="center"
header-align="center"
label="操作"
:width="optColumnWidth"
>
<template slot-scope="scope">
<el-button
v-if="scope.row.statusBtn === false"
type="primary"
size="mini"
icon="el-icon-edit"
@click="editCheck(scope.row)"
>编 辑</el-button
>
<el-button
v-else-if="scope.row.statusBtn === true"
type="success"
size="mini"
icon="el-icon-check"
@click="sureCheck(scope.row)"
>保 存</el-button
>
<el-button
type="danger"
size="mini"
icon="el-icon-delete"
@click="deleteFun(scope, 'delete')"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</div>
<div slot="footer" class="dialog-footer">
<el-button v-loading="loading" type="primary" @click="submit">确定</el-button>
<el-button @click="cancelModal">取消</el-button>
</div>
</el-dialog>
</template>
<script>
export default {
components: {},
props: {
tableHeight: {
type: String,
default: "50vh",
},
optColumnWidth: {
type: Number,
default: 200,
},
visible: {
type: Boolean,
default: false,
},
tableObject: {
type: Object,
default: null,
},
},
data() {
return {
loading: false,
title: "编辑",
selectionList: [],
url: {},
tableData: [],
editObject: {},
basicObject: {},
columnList: [],
idKey: "id",
deleteKey: "id",
batchDeleteKey: "ids",
requiredKeys: [],
};
},
computed: {},
created() {
// tableObject 是从父组件传过来的json对象
// 示例:
let example = {
// 弹框标题
popTitle: "编辑主题",
// table
columnList: [
{
prop: "themeName",
label: "主题名称",
},
{
prop: "themeDescribe",
label: "主题描述",
},
],
// 必填的字段
requiredKeys: ["themeName"],
tableData: this.themeList,
// 主键,默认为id
idKey: "id",
// 删除的参数名称,默认为ids
deleteKey: "ids",
// 批量的参数名称,默认为ids
batchDeleteKey: "ids",
// 接口请求路径,增删改查CRUD
interfaceUrl: {
add: "/target/addTheme",
edit: "/target/updateTheme",
delete: "/target/deleteTheme",
// 批量删除
batchDelete: "/target/deleteTheme",
list: "/target/themelist",
},
};
if (this.tableObject) this.init(this.tableObject);
},
methods: {
init(item) {
this.title = item.popTitle || item.label;
this.columnList = item.columnList;
this.url = item.interfaceUrl;
this.tableData = [];
if (item.idKey) this.idKey = item.idKey;
if (item.deleteKey) this.deleteKey = item.deleteKey;
if (item.batchDeleteKey) this.batchDeleteKey = item.batchDeleteKey;
if (item.requiredKeys) this.requiredKeys = item.requiredKeys;
item.tableData.forEach((element) => {
this.tableData.push({ ...element, statusBtn: false });
});
this.basicObject = {};
this.columnList.forEach((element) => {
this.basicObject[element.prop] = "";
});
},
submit() {
this.cancelModal();
},
// 判断值内容是否全部填写
flagFun() {
const flag = this.tableData.every((item) => {
return Object.values(item).every((element) => element !== "");
});
return flag;
},
// 勾选弹框表格添加一行
addRow() {
if (this.tableData.length > 0) {
const statusBtnFlag = this.tableData.some((item) => item.statusBtn);
if (statusBtnFlag) {
this.$sweetAlert.warnWithTimer("请先保存当前编辑项!");
return;
}
}
const addTempObject = { ...this.basicObject, statusBtn: true };
addTempObject[this.idKey] = "addTempKey" + new Date().getTime();
this.tableData.unshift(addTempObject);
},
editCheck(row) {
row.statusBtn = true;
// this.$forceUpdate();
this.editObject = row;
},
// 勾选弹框保存按钮
sureCheck(row) {
let flag = false;
for (const key in row) {
const element = row[key];
if (this.requiredKeys.includes(key) && element === "") {
flag = true;
break;
}
}
const values = Object.values(row);
if (flag) {
this.$sweetAlert.warnWithTimer("请填写编辑项的内容!");
return;
} else {
const formData = JSON.parse(JSON.stringify(row));
if (formData[this.idKey].includes("addTempKey")) {
delete formData[this.idKey];
}
Promise.all([this.interfaceOperate(formData)]).then((result) => {
row.statusBtn = false;
this.$forceUpdate();
});
}
},
interfaceOperate(form, type, methodType) {
return new Promise((resolve, reject) => {
let httpurl = this.url.add;
const formData = {
...form,
};
if (formData[this.idKey]) {
httpurl = this.url.edit;
}
if (type) httpurl = this.url[type];
if (formData.statusBtn != undefined) delete formData.statusBtn;
let method = "post";
if (methodType) method = methodType;
this.loading = true;
this.$api.httpAction(httpurl, formData, method).then((res) => {
this.loading = false;
if (res.code == 200) {
this.$sweetAlert.successWithTimer(res.msg);
resolve();
} else {
this.$sweetAlert.errorWithTimer(res.msg);
}
});
});
},
// 删除一行
deleteFun(scope) {
const formData = {};
formData[this.deleteKey] = scope.row[this.idKey];
const type = "delete";
this.confirmDelete(formData, type, () => {
this.tableData.splice(scope.$index, 1);
});
},
batchDelete() {
if (this.selectionList.length == 0) {
this.$sweetAlert.warnWithTimer("至少选择一条数据!");
return;
}
const array = this.selectionList.map((item) => item[this.idKey]);
const ids = array.join(",");
const formData = {};
formData[this.batchDeleteKey] = ids;
const type = "batchDelete";
this.confirmDelete(formData, type, () => {
this.tableData = this.tableData.filter(
(item) => !array.includes(item[this.idKey])
);
});
},
confirmDelete(formData, type, callback) {
const msg = "您是否确定删除?";
const title = "温馨提示";
this.$confirm(msg, title, {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
if (type == "delete") {
if (formData[this.deleteKey].includes("addTempKey")) return callback();
} else if (type == "batchDelete") {
let tempDeleteOptions = formData[this.batchDeleteKey];
tempDeleteOptions = tempDeleteOptions.split(",");
tempDeleteOptions = tempDeleteOptions.filter(
(item) => !item.includes("addTempKey")
);
if (tempDeleteOptions.length > 0) {
formData[this.batchDeleteKey] = tempDeleteOptions.join(",");
} else {
return callback();
}
}
Promise.all([this.interfaceOperate(formData, type)]).then((result) => {
callback();
});
})
.catch(() => {});
},
handleSelectionChange(val) {
console.log(val);
this.selectionList = val;
},
headerClassNameFun({ row, column, rowIndex, columnIndex }) {
// columnIndex 列下标
if (column == 0 || column == row.length - 1) {
return "";
}
if (
this.columnList[columnIndex - 1] &&
this.requiredKeys.includes(this.columnList[columnIndex - 1].prop)
) {
return "star";
} else {
return "";
}
},
cancelModal() {
if (this.tableData.length > 0) {
const statusBtnFlag = this.tableData.some((item) => item.statusBtn);
if (statusBtnFlag) {
this.$sweetAlert.warnWithTimer("请先保存当前编辑项!");
return;
}
}
// 关闭弹窗,触发父组件修改visible值
this.$emit("update:visible", false);
this.$emit("ok");
},
},
};
</script>
<style lang="scss" scoped>
.box-item {
margin: 10px 0;
}
.bot-btn-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
overflow-y: auto;
max-height: 300px;
& > div {
width: 33.33%;
margin-top: 20px;
}
& > div:hover {
font-weight: bolder;
}
}
</style>