因为在测试过程中,测试经常要进行暴力测试,输入一段非常长的文字,然后发起请求,请求报错后让前端来改,然后就感觉非常暴躁,所以在一开始给全局设置一个输入框允许输入的最大长度,如果后续有个别输入框确实有长文字输入需求,那就单独设置。因此,我决定现在把前端工作中有可能遇到的问题都总结下,提前把这类型的bug尽量扼杀掉,这样就把工作量转移给后端或者产品,如此甚好。。。。。。
全局设置输入框允许输入长度
- @/main.js
//全局设置输入框允许输入长度
document.addEventListener("input", (e: any) => {
// input框 type='text'
//e.target.getAttribute('maxlength') === null,本身没有设置maxlength长度,防止全局设置覆盖所在页面设置的长度
if (e.target.type === "text" && e.target.getAttribute("maxlength") === null) {
e.target.setAttribute("maxlength", "255"); // 限制最长输入255个字符
}
// input框 type='textarea',且本身没有设置maxlength长度
if (
e.target.type === "textarea" &&
e.target.getAttribute("maxlength") === null
) {
e.target.setAttribute("maxlength", "255"); // 限制最长输入255个字符
}
});
全局设置点击el-dialog弹窗外层不消失的属性
- @/main.js
import ElementUI from "element-ui";
ElementUI.Dialog.props.closeOnClickModal.default = false; //全局设置点击外层不消失的属性默认值
防重复点击(自定义指令实现)。
- @/main.js
import Vue from "vue";
Vue.directive("resetButton", {
inserted(el, binding) {
el.addEventListener("click", () => {
if (!el.disabled) {
el.disabled = true;
el.style.cursor = "not-allowed";
setTimeout(() => {
el.disabled = false;
el.style.cursor = "pointer";
}, binding.value || 1000);
}
});
},
});
- 设置按钮间隔两秒之后,才能再次点击
<el-button v-resetButton="2000" @click="confirmClick">确 定</el-button>
树状结构的数据转换成平的数据
在工作中经常要用到树状结构的数据,所以一般遇到这样的数据时,后端会直接返回树状数据,但是在某些特定情况下,需要把这样的树状数据拉平方便开发使用。比如生成树状结构的表格,具体案例,见之前的文章juejin.cn/post/700764…
- 需要处理的数据如下:
let data = [
{
projectType: "配置项顶级软件",
children: [
{
softType: "非嵌入",
children: [
{
developmentType: "一类",
children: [
{
lifeCycleType: "一类模型",
},
],
},
{
developmentType: "二类",
children: [
{
lifeCycleType: "二类模型",
},
],
},
{
developmentType: "三类",
children: [
{
lifeCycleType: "三类模型",
},
],
},
{
developmentType: "四类",
children: [
{
lifeCycleType: "四类迭代",
},
{
lifeCycleType: "四类瀑布",
},
],
},
],
},
{
softType: "应用软件",
children: [
{
developmentType: "一类",
children: [
{
lifeCycleType: "一类模型",
},
],
},
{
developmentType: "二类",
children: [
{
lifeCycleType: "二类模型",
},
],
},
{
developmentType: "三类",
children: [
{
lifeCycleType: "三类模型",
},
],
},
],
},
],
},
]
第一种处理方式
这种处理方式主要用来解决类excel的表格的树状结构数据
- 使用递归处理方法
let plainData = [];
function treeToPlianTable(arr) {
arr.forEach((a) => {
if (a.children && a.children.length > 0) {
treeToPlianTable(a.children);
//在非最底层的层级遍历时,就在已存在数组中添加自己的属性,如果在自己的属性在已有数组中不存在才添加。
plainData.forEach((p) => {
for (let k in a) {
if (!Object.prototype.hasOwnProperty.call(p, k)) {
p[k] = a[k];
}
}
});
} else {
//最底层一级遍历时,就要把对象push进数组。
plainData.push(a);
}
});
}
treeToPlianTable(data);
console.log(plainData);//可以在这里查看数据
另一种处理方式
这种方式就是把树状结构的数组完全弄成,一个平铺的数组
function treeToPath(tree) {
let queen = [...tree];
let result = [];
while (queen.length) {
let first = queen.shift();
if (first.children && first.children.length > 0) {
queen = queen.concat(first.children);
first["children"] = true;
}
result.push(first);
}
return result;
}
一键勾选当前表格所有同类column
表格一键全选同类item且屏蔽所有非同类item。勾空一个item,一键解除所有同类item且恢复所有的item的checkbox,本示例以prop=longDemandNumber的属性为判断依据,进行一键勾选
<template>
<div class="table">
<el-table
:data="tableData"
border
ref="multipleTable"
@selection-change="handleSelectionChange"
height="100%"
>
<el-table-column
prop="selects"
type="selection"
width="55"
:selectable="isSelectable"
>
</el-table-column>
<el-table-column
type="index"
width="50"
align="center"
label="序号"
:selectable="isSelectable"
>
</el-table-column>
<el-table-column
v-for="column in tableColumn"
:key="column.id"
:label="column.label"
:prop="column.prop"
>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
selectItems: [],
tableData: [
{
auditStatus: null,
auditStatusLabel: null,
auditView: null,
createTime: "2021-11-13",
createrId: null,
createrName: null,
demandItemId: "1459383103477473282",
demandItemName: "111122",
demandItemPkId: "d0927e61d3014c2ecc771dc43b54460c",
endTime: "2024",
longDemandId: "1458306217682808833",
longDemandItemNumber: "LD-ITEM-202111130002",
longDemandName: "需求管理Test22",
longDemandNumber: "LD-202111100002",
longDemandPkId: "57d6cd2d377f06e4cca618fb4d86b119",
progress: 0,
startTime: "2023",
updateTime: null,
updaterId: null,
updaterName: null,
},
{
auditStatus: null,
auditStatusLabel: null,
auditView: null,
createTime: "2021-11-13",
createrId: null,
createrName: null,
demandItemId: "1459339433550209026",
demandItemName: "234222",
demandItemPkId: "41138c671aad57bd0ae837af122318ca",
endTime: "2024",
longDemandId: "1458306217682808833",
longDemandItemNumber: "LD-ITEM-202111130001",
longDemandName: "需求管理Test22",
longDemandNumber: "LD-202111100002",
longDemandPkId: "57d6cd2d377f06e4cca618fb4d86b119",
progress: 0,
startTime: "2023",
updateTime: null,
updaterId: null,
updaterName: null,
},
{
auditStatus: "BUSINESS_AUDIT_STATUS_3",
auditStatusLabel: "审批通过",
auditView: null,
createTime: "2021-11-11",
createrId: null,
createrName: null,
demandItemId: "1458729900398284801",
demandItemName: "测试流程回调",
demandItemPkId: "73cce9b1085dccf700f2c8c3564e1c72",
endTime: "2024",
longDemandId: "1458306217682808833",
longDemandItemNumber: "LD-ITEM-202111110014",
longDemandName: "需求管理Test22",
longDemandNumber: "LD-202111100002",
longDemandPkId: "57d6cd2d377f06e4cca618fb4d86b119",
progress: 0,
startTime: "2023",
updateTime: null,
updaterId: null,
updaterName: null,
},
{
auditStatus: null,
auditStatusLabel: null,
auditView: null,
createTime: "2021-11-11",
createrId: null,
createrName: null,
demandItemId: "1458685687348240385",
demandItemName: "测试新增需求项审批222--需求管理Test22",
demandItemPkId: "0bf491e426935ca2b63405d8e0d9f0e9",
endTime: "2024",
longDemandId: "1458306217682808833",
longDemandItemNumber: "LD-ITEM-202111110008",
longDemandName: "需求管理Test22",
longDemandNumber: "LD-202111100002",
longDemandPkId: "57d6cd2d377f06e4cca618fb4d86b119",
progress: 0,
startTime: "2023",
updateTime: null,
updaterId: null,
},
{
auditStatus: null,
auditStatusLabel: null,
auditView: null,
createTime: "2021-11-11",
createrId: null,
createrName: null,
demandItemId: "1458682028671848449",
demandItemName: "测试新增需求项审批11--需求管理Test22",
demandItemPkId: "598aa4b3c7972c5dc85eb188584b8933",
endTime: "2024",
longDemandId: "1458306217682808833",
longDemandItemNumber: "LD-ITEM-202111110007",
longDemandName: "需求管理Test22",
longDemandNumber: "LD-202111100002",
longDemandPkId: "57d6cd2d377f06e4cca618fb4d86b119",
progress: 0,
startTime: "2023",
updateTime: null,
updaterId: null,
updaterName: null,
},
{
auditStatus: null,
auditStatusLabel: null,
auditView: null,
createTime: "2021-11-11",
createrId: null,
createrName: null,
demandItemId: "1458675982440026114",
demandItemName: "12311",
demandItemPkId: "349d20f52dc252226b450c61887fc38c",
endTime: "2024",
longDemandId: "1458306217682808833",
longDemandItemNumber: "LD-ITEM-202111110006",
longDemandName: "需求管理Test22",
longDemandNumber: "LD-202111100002",
longDemandPkId: "57d6cd2d377f06e4cca618fb4d86b119",
progress: 0,
startTime: "2023",
updateTime: null,
updaterId: null,
updaterName: null,
},
{
auditStatus: null,
auditStatusLabel: null,
auditView: null,
createTime: "2021-11-11",
createrId: null,
createrName: null,
demandItemId: "1458671334131929089",
demandItemName: "123",
demandItemPkId: "4fc2873658040d5d0232ea0270d3fefc",
endTime: "2027",
longDemandId: "1459041570618294274",
longDemandItemNumber: "LD-ITEM-202111110005",
longDemandName: "需求管理Test33",
longDemandNumber: "LD-202111100003",
longDemandPkId: "a242feafd2bb51a08ad8cfb8f33de28d",
progress: 0,
startTime: "2025",
updateTime: null,
updaterId: null,
updaterName: null,
},
{
auditStatus: null,
auditStatusLabel: null,
auditView: null,
createTime: "2021-11-11",
createrId: null,
createrName: null,
demandItemId: "1458671232311005186",
demandItemName: "234",
demandItemPkId: "9f8a8532a970bfa03188379631a1b146",
endTime: "2027",
longDemandId: "1459041570618294274",
longDemandItemNumber: "LD-ITEM-202111110004",
longDemandName: "需求管理Test33",
longDemandNumber: "LD-202111100003",
longDemandPkId: "a242feafd2bb51a08ad8cfb8f33de28d",
progress: 0,
startTime: "2025",
updateTime: null,
updaterId: null,
updaterName: null,
},
{
auditStatus: null,
auditStatusLabel: null,
auditView: null,
createTime: "2021-11-11",
createrId: null,
createrName: null,
demandItemId: "1458670966681538561",
demandItemName: "11",
demandItemPkId: "36ba7345756e77cc2a83a4ef3a35de33",
endTime: "2024",
longDemandId: "1458306217682808833",
longDemandItemNumber: "LD-ITEM-202111110003",
longDemandName: "需求管理Test22",
longDemandNumber: "LD-202111100002",
longDemandPkId: "57d6cd2d377f06e4cca618fb4d86b119",
progress: 0,
startTime: "2023",
updateTime: null,
updaterId: null,
updaterName: null,
},
{
auditStatus: null,
auditStatusLabel: null,
auditView: null,
createTime: "2021-11-11",
createrId: null,
createrName: null,
demandItemId: "1458669885389967362",
demandItemName: "测试新增需求项审批22--Du需求管理Test22",
demandItemPkId: "3c8f35fec501ceda244245f08696cd14",
endTime: "2024",
longDemandId: "1458306217682808833",
longDemandItemNumber: "LD-ITEM-202111110002",
longDemandName: "需求管理Test22",
longDemandNumber: "LD-202111100002",
longDemandPkId: "57d6cd2d377f06e4cca618fb4d86b119",
progress: 0,
startTime: "2023",
updateTime: null,
updaterId: null,
updaterName: null,
},
],
tableColumn: [
{
label: "需求名称",
prop: "longDemandName",
},
{
label: "需求项",
prop: "demandItemName",
},
{
label: "审批状态",
prop: "auditStatusLabel",
},
{
label: "提交时间",
prop: "createTime",
},
],
selectItemsLength: 0,
};
},
methods: {
handleSelectionChange(selectItems) {
this.selectItems = selectItems;
if (selectItems.length == 0) {
this.selectItemsLength = 0;
}
if (this.selectItemsLength == 0 && this.selectItems.length == 1) {
//初次勾选一个,然后勾选当前表格其余所有同类型的item.
this.isSelectionAllOrEmpty(false);
} else if (
this.selectItemsLength != 0 &&
this.selectItems.length < this.selectItemsLength
) {
//清空勾选一个item,则当前表格所有已勾选的同类型item全部清空勾选,且全部item选择框恢复可选状态
this.isSelectionAllOrEmpty(true);
}
},
isSelectionAllOrEmpty(isAllToEmnpty) {
if (isAllToEmnpty) {
this.$refs.multipleTable.clearSelection();
} else {
this.$nextTick(() => {
this.tableData.forEach((t) => {
if (t.longDemandNumber === this.selectItems[0].longDemandNumber) {
this.$refs.multipleTable.toggleRowSelection(t, true);
}
});
this.selectItemsLength = this.selectItems.length;
});
}
},
isSelectable(row) {
if (this.selectItems.length > 0) {
if (row.longDemandNumber === this.selectItems[0].longDemandNumber) {
//只要选中一项,则选中当前表格数据的所有item,
return true;
} else {
//然后把不符合要求的所有项,全部置灰处理
return false;
}
} else {
//清空选中数组后,则全部item的选择框恢复可选
return true;
}
},
},
};
</script>
<style lang="scss" scoped>
/deep/.el-table {
//把最顶端全选checkbox用css隐藏掉
th {
.cell {
&::before {
content: "";
position: absolute;
}
.el-checkbox__inner {
display: none;
position: relative;
}
}
}
}
</style>
- 效果图如下:
图1:
图2:
下载导出文件的方法
在项目开发过程中经常要进行下载导出文件,请求后端接口会得到一个blob对象,或者自己自行把数据转换成blob类型。这都是看前端和后端之间的约定
let blob = new Blob([data], {
type: "application/vnd.ms-excel;charset=utf-8",
});
downloadFile(blob, '导出文件');//一般文件名从headers中的content-disposition中获取。
function downloadFile(blob, fileName) {
let downloadElement = document.createElement("a");
let href = window.URL.createObjectURL(blob); //创建下载的链接
downloadElement.href = href;
downloadElement.download = fileName; //下载后文件名
document.body.appendChild(downloadElement);
downloadElement.click(); //点击下载
document.body.removeChild(downloadElement); //下载完成移除元素
window.URL.revokeObjectURL(href); //释放掉blob对象
}
上传文件的方法
web上传文件一本都是使用
<input type="file" onchange="fileUpload">这样的input标签实现,但是感觉每次都要写一个这样的dom标签属实有些麻烦,所以我用js代码动态生成dom标签以达到重用的目的。可能我写的不是很正确,但是确实解决我在项目中需要上传的问题,如果有更好的办法,可以一起探讨
initInputFileDom() {
if (document.getElementById("inputUploadElCus")) {
//如果input file标签存在,则先删除该标签,以免出现过多的这样的标签
this.removeInputFileDom();
}
let inputEl = document.createElement("input");
inputEl.setAttribute("id", "inputUploadElCus");
inputEl.setAttribute("type", "file");
inputEl.setAttribute("multiple", "multiple");//上传多个文件的属性
inputEl.style.display = "none";//设置为none,避免出现在页面中
document.body.appendChild(inputEl);//把这个input标签加载到body中
inputEl.addEventListener("change", this.getFileData);//监听change事件
inputEl.click();//点击input标签
},
removeInputFileDom() {
document
.getElementById("inputUploadElCus")
.parentNode.removeChild(document.getElementById("inputUploadElCus"));
},
getFileData(e) {
let formData = new FormData(); //创建一个空对象实例
formData.append("file", e.target.files[0]);//上传单个文件的处理方法
console.log("getFileData", e.target.files[0]);
for (let i = 0; i < e.target.files.length; i++) {
formData.append("file", e.target.files[i]);
}//上传多个文件时的处理方法。
//获取并处理了这个file文件对象后,就可以按照后端的要求发起请求了....
//...请求中...
//请求结束,需要使用removeInputFileDom方法清除自己建立的这个Dom标签。
},
MarkDown插件的使用
- 引入
npm install mavon-editor --save
- 全局注册 main.js
import mavonEditor from "mavon-editor";
import "mavon-editor/dist/css/index.css";
Vue.use(mavonEditor);
mdEditor.vue
<template>
<div class="mdEditor">
<mavon-editor
ref="md"
:toolbars="toolbars"
v-model="handbook"
:ishljs="true"
@imgAdd="$imgAdd"
@imgDel="$imgDel"
@save="saveClick"
/>
</div>
</template>
<script>
export default {
name: "mdEditor",
data() {
return {
handbook: "",
toolbars: {
bold: true, // 粗体
italic: true, // 斜体
header: true, // 标题
underline: true, // 下划线
strikethrough: true, // 中划线
mark: true, // 标记
superscript: true, // 上角标
subscript: true, // 下角标
quote: true, // 引用
ol: true, // 有序列表
ul: true, // 无序列表
link: true, // 链接
imagelink: true, // 图片链接
code: true, // code
table: true, // 表格
fullscreen: true, // 全屏编辑
readmodel: true, // 沉浸式阅读
htmlcode: true, // 展示html源码
help: true, // 帮助
/* 1.3.5 */
undo: true, // 上一步
redo: true, // 下一步
trash: true, // 清空
save: true, // 保存(触发events中的save事件)
/* 1.4.2 */
navigation: true, // 导航目录
/* 2.1.8 */
alignleft: true, // 左对齐
aligncenter: true, // 居中
alignright: true, // 右对齐
/* 2.2.1 */
subfield: true, // 单双栏模式
preview: true, // 预览
},
};
},
methods: {
$imgAdd(filename, file) {
console.log("$imgAdd ", filename, file);
//上传图片数据,获取图片地址,然后拼接相应的md图片语法 如 
var formdata = new FormData();
formdata.append("image", file);
this.postImg({
url: "server url",
method: "post",
data: formdata,
headers: { "Content-Type": "multipart/form-data" },
})
.then((url) => {
// 第二步.将返回的url替换到文本原位置 -> 
this.$refs.md.$img2Url(filename, url); //结合后端回传的图片地址,拼接为一个md语法的图片,然后上传到服务器
})
.catch((err) => {
console.log("error", err);
});
},
$imgDel(filename) {
console.log("$imgDel ", filename);
},
saveClick(val) {
this.$emit("saveMDClick", val);
},
postImg() {
return new Promise((resolve, reject) => {
reject("暂无接口");
});
},
},
};
</script>
mdEditor.vue:
<template>
<div
class="mdEditor"
style="flex:1; height:100%;background:#000;padding:100px 50px 0;box-sizing:border-box;"
>
<mdbox @saveMDClick="saveMDClick"></mdbox>
</div>
</template>
<script>
import mdbox from "@/components/echartCompent/mdEditor.vue";
export default {
name: "mdEditor",
components: {
mdbox,
},
methods: {
saveMDClick(val) {
console.log("saveMDClick val", val); //这是要保存的数据
},
},
};
</script>
<style></style>