Vue项目开发过程中遇到的小问题的解决方法总结

480 阅读3分钟

因为在测试过程中,测试经常要进行暴力测试,输入一段非常长的文字,然后发起请求,请求报错后让前端来改,然后就感觉非常暴躁,所以在一开始给全局设置一个输入框允许输入的最大长度,如果后续有个别输入框确实有长文字输入需求,那就单独设置。因此,我决定现在把前端工作中有可能遇到的问题都总结下,提前把这类型的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: image.png 图2: image.png

下载导出文件的方法

在项目开发过程中经常要进行下载导出文件,请求后端接口会得到一个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插件的使用

  1. 引入

npm install mavon-editor --save

  1. 全局注册 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替换到文本原位置![...](0) -> ![...](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>