
Vue.js 关于表格合并篇
-
- [1. 需求说明](#1. 需求说明)
- [2. 第一个版本](#2. 第一个版本)
- [3. 让人脑壳疼的地方](#3. 让人脑壳疼的地方)
- [4. 程序思维至关重要](#4. 程序思维至关重要)
- [5. 有必要提一下面向对象编程](#5. 有必要提一下面向对象编程)
1. 需求说明
- 目前是开发管理系统,那肯定免不了有各种导入文件
- 所以封装一个全项目通用的是十分有必要的
- 本文由浅入深,通俗易懂
2. 第一个版本
- 封装组件这玩意对于大家来说其实不是什么难事
- 所以第一个版本一下就写出来了,是这个样子的
- 不同的场景会有一个唯一的sign值,在下载模板以及导入的时候传给后端
- 并且提供了其它参数供选择:otherParams,有时候传文件也需要附带参数
- 代码如下(注释也尽可能详尽了)
javascript
<template>
<div>
<el-dialog
:title="dialogTitle"
:visible.sync="visible"
width="620px"
:close-on-click-modal="false"
@opened="Opened()"
@closed="Closed('ruleForm')"
>
<div>
<!-- 导入说明 -->
<div class="import-explain">
<h3 class="import_item_title">· 导入说明</h3>
<div class="tipContent">
<span
v-for="(item, index) in htmlList"
:key="index"
@click="textTap"
v-html="index + 1 + '.' + item"
></span>
</div>
</div>
<div class="import-file">
<h3 class="import_item_title">
· <slot name="importTitle">导入文件</slot>
</h3>
</div>
<div class="select-file">
<el-upload
ref="upload"
accept=".xls,.xlsx"
:action="uploadUrl"
:data="{ ...queryData, ...params, ...otherParams }"
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
:on-exceed="handleExceed"
:on-change="handleChang"
:on-success="handleSuccess"
:headers="headers"
:auto-upload="false"
:file-list="fileList"
multiple
:limit="limit > 1 ? limit : undefined"
>
<el-button size="small" type="primary">选择文件</el-button>
<span> 文件格式支持xls,xlsx</span>
</el-upload>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取 消</el-button>
<el-button type="primary" @click="handleConfirm('ruleForm')">
确 定 导 入
</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { download } from "@/api/data.js";
import { downloadFileUrl } from "@/utils/index.js";
import { getToken } from "@/utils/auth";
export default {
name: "importFile",
props: {
dialogTitle: {
//弹窗标题
type: String,
default: "导入文件",
},
uploadUrl: {
//导入地址
type: String,
default: "",
},
limit: {
//上传最大文件数
type: Number,
default: 1,
},
headers: {
//文件上传自定义请求头信息
type: Object,
default: function () {
return {
Authorization: getToken(),
};
},
},
htmlList: {
//提示信息列表
type: Array,
default: Array,
},
queryData: {
//导入接口所需的数据
type: Object,
default: Object,
},
otherParams: {
type: Object,
},
},
data() {
return {
visible: false,
params: {
otherParams: undefined, //{}其他参数
}, //导入参数
fileList: [],
selectFile: false, //false 未上传文件 true 已上传文件
};
},
created() {
console.log("create", this.headers);
},
mounted() {},
methods: {
//弹框打开
Opened() {},
//弹框关闭
Closed() {
this.fileList = []; //上传的导入文件置空
this.selectFile = false;
},
//确定导入
handleConfirm() {
if (this.selectFile) {
this.$refs.upload.submit();
} else {
this.$message.error("请选择需要上传的文件!");
}
},
//点击文件列表中已上传的文件时的钩子
handlePreview(file) {
this.$emit("handlePreview", {
file: file,
});
},
//文件列表移除文件时的钩子
handleRemove(file, fileList) {
this.selectFile = false;
this.$emit("handleRemove", {
file: file,
fileList: fileList,
});
},
//删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。
beforeRemove(file, fileList) {
this.$emit("beforeRemove", {
file: file,
fileList: fileList,
});
},
//文件超出个数限制时的钩子
handleExceed(files, fileList) {
this.$emit("handleExceed", {
file: files[0],
fileList: fileList,
});
},
//文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
handleChang(file, fileList) {
// 仅上传一个文件 多次选择文件时 覆盖之前的文件
if (this.limit == 1) {
if (fileList.length > 0) {
this.fileList = [fileList[fileList.length - 1]]; //这一步,是 展示最后一次选择文件
}
}
this.selectFile = true;
},
//文件上传成功时的钩子
handleSuccess(response, file, fileList) {
if (response.code != 200) {
this.$message.error(response.message);
} else {
this.$refs.upload.clearFiles();
this.$emit("handleSuccess", {
response: response,
file: file,
fileList: fileList,
});
}
},
textTap(e) {
if (e.target.dataset.type == "download") {
e.preventDefault();
const url = e.target.dataset.url;
const sign = e.target.dataset.sign;
const title = e.target.innerText;
let params = {};
if (sign) {
params.sign = sign;
}
download(url, params).then((res) => {
downloadFileUrl(res, "application/vnd.ms-excel", title, "xlsx");
});
}
},
},
};
</script>
<style lang="scss" scoped>
.import-file {
margin-top: 15px;
margin-bottom: 15px;
}
.select-file,
.tipContent {
margin-left: 16px;
}
//导入说明
.import-explain {
// margin-top: 15px;
}
:deep(.import_item) {
margin-top: 15px;
}
:deep(.import_item_title) {
display: block;
margin: 0;
width: 100%;
font-size: 18px;
color: #333;
line-height: 2;
}
:deep(.import_item_content) {
margin-left: 16px;
margin-top: 15px;
}
.tipContent {
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-direction: column;
margin-top: 15px;
}
.tipContent span {
line-height: 1.8;
}
.tipContent a {
color: #46a6ff;
}
</style>
- 这个是低耦合的,并且非常通用,大家可以直接CV,该有的各种函数以及检测都到位了,样式也提供在这里
- 调用的时候,也很简单
javascript
<DialogUploadFile
:dialogTitle="'导入'"
:dialogVisible="dialogVisibleImport"
:htmlList="importHtmlList"
:uploadUrl="uploadUrl"
:queryData="{ sign: signTest }"
@handleSuccess="uploadSuccess"
@close="dialogBeforeClose">
</DialogUploadFile>
- importHtmlList,这个就是那个文本了,导入说明。
3. 让人脑壳疼的地方
- 这个组件封装一两年了,也没发现有啥问题,能够正常使用,直到最近...
- 在产品需求这块,发现会有一些情况,加了一些输入框,需要收集其他信息,如下:
- 还有些逻辑:比如切换以及验证
4. 程序思维至关重要
- 那么,各位朋友们,这种问题要怎么解决呢?由基础版升级到这种复杂的
- 难点在于:不能影响之前的逻辑及业务,又要进行功能升级
- 首先想到的应该是加各种 if 了,也就是直接在公用组件里面修改,外部传值进来,如下:
- 这样确实能解决问题,但是吧,以后每次新增功能,都要新增各种 if ,但是十分不建议的:
- 这是公用组件,整个项目都在用,万一哪天被某个人改错了,风险是非常大的,等着挨骂吧
- 甚至于,以后代码量干到几百行,谁还看得懂,请神吧(工具组件应该尽量简洁)
5. 有必要提一下面向对象编程
- 这玩意确实比较抽象,很多人做了好几年程序员也搞不懂
- 首先,肯定不是把你对象请到公司,坐你对面,然后你看着她写代码
- 简而言之:各个组件 / 方法,都应该保持独立,有自己的私有的方法与属性,提供接口给外部调用。你只能通过调用我的方法改变我的属性,不能乱来,就跟打狗还得看主人一样。
- 放在上传组件上来说,这些业务判断是不能放到公用组件的,甚至于说"玷污了"它,只能写到调用它的组件里面去:公用方法或组件尽量不涉及具体业务逻辑
- 所以,其实一个插槽就能搞定
- 这些新的参数(下拉框及时间区间),以及验证必填的逻辑,通通放到父组件去,如下:
javascript
<DialogUploadFile
:dialogTitle="'导入'"
:dialogVisible="dialogVisibleImport"
:checkChildData="checkChildData"
:htmlList="importHtmlList"
:uploadUrl="uploadUrl"
:queryData="{ sign: signTest }"
@handleSuccess="uploadSuccess"
@close="dialogBeforeClose">
<template v-slot:otherDataSlot>
<!-- 通通放在这里 -->
</template>
</DialogUploadFile>
- 验证方法:checkChildData,加一个参数即可,也在调用上传组件的地方定义好
- 在确认导入之前,调用该方法,符合条件返回 true,反之返回false即可
1. 希望本文能对大家有所帮助,如有错误,敬请指出
2. 原创不易,还请各位客官动动发财的小手支持一波(关注、评论、点赞、收藏)
3. 拜谢各位!后续将继续奉献优质好文
4. 如果存在疑问,可以私信我(文底有V)
