记录一下自己单独负责的一个后台管理项目所遇到的一些BUG以及一些东西的使用(vue+ele)

494 阅读13分钟
    1.关于ele里面的tabel组件中,表头与下面的每一行内容列不对齐问题
    解决办法是在index.html或者app.vue入口文件中加入代码,保证全局生效,如下
    body .el-table th.gutter {
        display: table-cell !important;
    }
    
    2.有关登陆的验证,写一个路由拦截器,验证本地存储里面有没有token做访问限制
        router.beforeEach((to, from, next) => {
        if (to.path === '/login') {
            return next()
        }
        const LoginStatus = localStorage.getItem('LoginStatus')
        if (LoginStatus) {
            next()
        } else {
            next('/login')
            }
        })
    
    3.登陆结束之后开始写侧边栏,由于是第一版,刚开始并没有将侧边栏写成动态生成的,到后面改了再来补充,因为有个权限问题
    ,每个人的权限不同,登陆之后的侧边栏显示也不同(后期补充)
    关于侧边栏就两点,第一点就是侧边栏的每一栏都对应一个路由,实现跳转,开启路由模式即可,即:router='true',并且设置之
    后当然刷新页面之后并不会按照当前路由去展开对应的侧边栏,体验不好,此时需要设置另外一个属性为
    :defalut-active='$router.path',此时不论你怎么去刷新,都会按照你的当前路由去展开对应的侧边栏,但是还有一个需要就是
    如果你去点击随意一个侧边栏,需要刷新页面,这个需求我直接就是注册一个ele里面的select事件写的,然后用location.reload()
    想不到别的更好的办法,后面发现再补充
    
    4.关于首页的布局,按照ele提供的布局直接写的,但是出现了一个问题,就是ele里面的侧边栏导航会影响布局,到目前我也不知
    道是为什么,审查了一下元素貌似是定位导致的,你如果有这个侧边栏的情况并且是在左边,然后用左边定位,右边自适应,你右
    边会出现内容显示不全,我能做的就是直接写死一个高度,当然这个方法不好,因为你不知道你后面需要写的内容的高度是多少,
    你可能会问我用内容撑开啊,但是我也不知道为什么就是撑不开,还是写死高度好一点,网上查了一下貌似就是这个ele里面的侧边
    栏有点问题,找到更好的办法再去补充(没办法菜啊)
    
    5.纵观整个项目table组件用的很多,基本来说ele里面提供的table表组件里面的几种都用到了,除了多级表头的那种,现在来总
    结一下
    第一:最基础的table表组件的用法就是按照ele里面的那个照着撸就可以,让后台按照你给的数据格式弄就可以,大家都省力
    第二:动态渲染表头还有对应的下面的内容,这一种是表头也是动态的内容,上面的第一种是表头写死的最基础的,第二种的话你
    可以将表头和下面对应的内容都分别写成一个数组的形式,比如header:[],data-list:[],header这个数组代表的是头部的数据,
    下面的data-list代表的每一行的数据,有多少行里面就有多少个数组,按照这个形式让后台返回给你,或者自己写个模板丢给后
    台让他按照这个给你,此时就差渲染了,以下就是我渲染的方式:
        <el-table
          :data="data_list"
          style="min-width:100%"
          border
          size="small"
          :header-cell-style="{fontWeight:'bolder',color:'#333',fontSize:'14px'}"
          max-height="350"
        >
          <el-table-column
            :key="index"
            :label="data"
            v-for="(data,key,index) in header"
            align="center"
          >
            <template slot-scope="scope">
              <span>{{data_list[scope.$index][key]}}</span>
            </template>
          </el-table-column>
        </el-table>
    由于多级表头我没有遇到,所以也不好说,额外说一下合并单元格,因为第二种的表头是动态的,按照我的那种方法就必须要表头
    的数据长度要和下面的数据的长度要一致,不然渲染不出来,所以后台会返回可能大量一样的名字,此时需要合并了,但是很气的
    是ele并没有提供合并表头的方法,多级表头的百度有合并的方法,但是我这不是多级表头,ele里面获取的行和列的下标的方法
    并不能获取表头的下标,这就很烦了,想了一段时间只能换一种方法了,想到给表头家类名,这个ele里面有对应的方法
    :header-cell-class-name="headerStyle",这个headerStyle就是加类名的方法,如下:
        headerStyle({ row, column, rowIndex, columnIndex }) {
            // console.log(rowIndex, columnIndex);
            if (rowIndex === 0) {
            if (columnIndex === 4) {
                return "headerStyle";
             }
            }
        },
        因为在后台给你数据的时候你能看得到需要合并的数据从哪里开始到哪里结束,我项目里面是从下标为4开始需要合并,以上操作就是给下标为4的那一项加上了一个headerStyle的类名,然后就可以开始执行合并操作了,因为是动态渲染,你需要将渲染
        的方法写到updated里面保证他的渲染之后能够合并,我用的jquery方法获取到这个类名并且合并,如下:
        $(".headerStyle").prop("colSpan", Number(this.headernum));,别的方法目前没有想到,后期想到补充。
        以上是我在此项目中遇到的table组件的应用场景
        
    6.今天完善了侧边栏动态生成的写法:
    <el-menu
        :default-active="$route.path"
        class="el-menu-vertical-demo"
        :unique-opened="true"
        @open="handleOpen"
        @close="handleClose"
        @select="hadleselect"
        :router="true"
        text-color="#fff"
      >
        <el-submenu :index="item.ModuleID" v-for="item in navBarData" :key="item.ModuleID">
          <template slot="title">
            <i :class="item.Incog" style="color:#fff;"></i>
            <span>{{item.ModuleName}}</span>
          </template>
          <el-menu-item
            :index="item1.ModuleUrl"
            v-for="item1 in item.children"
            :key="item1.ModuleID"
          >
            <img :src="item1.Incog" alt />
            {{item1.ModuleName}}
          </el-menu-item>
    </el-submenu>
    
    根据上图可以看到每个二级菜单和一级菜单都有一个小图标,只不过一级菜单的小图标是ele里面的类名小图标,但是由于二级菜
    单的图标太多不能用ele里面的小图标所以改用了img标签,路径由后台返回我的项目中images文件夹里面图片的路径,但是你会发
    现渲染出来之后图片不见了,虽然元素能够获取到并且路径也是自己想要的路径,但是就是看不到,审查元素发现这个路径虽然有
    但是没有任何作用,那么问题来了,如何解决这个呢?首先你得了解一件事,在项目打包之后之前你写死的那个路径放在服务器上
    根本找不到对应的图片,所以导致了你渲染不出来,此时你可以将所有图片放置于public文件夹内,因为该文件夹不会被打包,这
    里涉及到一些webpack打包的原理,在vue-cli3.0里面这一步工作cli已经帮你封装好了,所以你只需让后台返回的路径改一下就可
    以,此时就可以正常渲染出来了,类似于cli2.0里面的static文件夹,设置之后也可以达到该效果,这个文件夹也不会被打包。网
    上还有另外的require方法,其实就是写死,然后把路径丢入这个包里,但是由于图标过多的话,这种写法并不好,肯定没有动态
    渲染来得好,所以不做详细介绍,可以去百度一波写法。
    
    7.项目中用到了上传组件,即el-upload组件,如下
     <el-upload
      class="upload-demo"
      ref="upload"
      action="https://jsonplaceholder.typicode.com/posts/"
      :on-preview="handlePreview"
      :on-remove="handleRemove"
      :on-change="handleChange"
      :file-list="fileLists"
      :http-request="uploadFile"
      :auto-upload="false"
      :before-upload="beforeAvatarUpload"
    >
          <span
            v-if="this.fileLists.length == 0"
            style="position:absolute;top:50%;transform:translateY(-50%);left:100px;font-weight:500;font-size:12px;"
          >未选择任何文件</span>
          <span v-else></span>
          <el-button slot="trigger" size="mini" type="primary">选取文件</el-button>
          <el-button
            style="margin-left: 270px;"
            size="mini"
            type="success"
            @click="submitUpload"
          >上传到服务器</el-button>
    </el-upload>
    在该项目中我用到的是自定义上传方法,即:http-request="uploadFile",该参数可以自定义实现自定义上传,组件中的action参
    数是必须的,不加上的话会报错,至于里面的地址随便你写什么,没什么实质的作用,但是必须要写。下面详细说一下上传步骤
    第一步在自定义的uploadFile方法里面每次你上传文件都可以获取到文件的信息,将文件信息赋值给submitUpload方法即点击提
    交按钮的方法里面的new FormData()生成的值文件信息里面,可以事先在data里面写上一个变量来赋值,我写的是uploadFileData,
    this.uploadFileData = new FormData(),赋值操作直接用append方法,即this.uploadFileData.append("files", file.file);
    如果接口中还要其余的参数,直接append即可,接下来一定要调用ele里面的提交方法,方法为this.$refs.upload.submit();
    最后就可以直接调接口的post请求即可,将this.uploadFileData直接丢进去即可,代码如下
    
        uploadFile: function(file) {
          this.uploadFileData.append("files", file.file);
        },
        submitUpload() {
        this.uploadFileData = new FormData();
        this.uploadFileData.append( "CustomerID", localStorage.getItem("CustomerID") );
        this.uploadFileData.append("Type", "4");
        this.uploadFileData.append("OperID", localStorage.getItem("OperID"));
        this.uploadFileData.append("LCType", localStorage.getItem("lcType"));
        this.uploadFileData.append("BCType", localStorage.getItem("bzType"));
        this.uploadFileData.append("TimeType", localStorage.getItem("TimeType"));
        this.$refs.upload.submit();
        this.$http
        .post(
          this.$Api.globalUrl + "/Api/ExcleImport/ExcleImportData",
          this.uploadFileData
        )
        .then(res => {})
        
    8.对了,还有一种表格形式,就是表格里面嵌套表格,多个表格的嵌套形式,在ele里面有这个案例,可以直接使用即可,只不过
    表头如果是动态生成的话,额外设置一下即可,只需要给出树形数据即可,可以参考ele里面的案例数据模板给定,代码如下:
         <el-table
          :data="tableData1"
          row-key="placeId"
          border
          :default-expand-all="false"
          size="mini"
          :header-cell-style="{fontWeight:'bolder',color:'#333',fontSize:'14px'}"
          :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
        >
          <el-table-column
            v-for="(item,index) in header"
            :key="index"
            :prop="item.prop"
            :label="item.label"
            align="center"
          ></el-table-column>
          <el-table-column label="操作" align="center">
            <template slot-scope="scope">
              <el-button size="mini" @click="edit(scope.row)">设置</el-button>
              <!-- <el-button size="mini" @click="see(scope.row)">查看</el-button> -->
            </template>
          </el-table-column>
        </el-table>
        
    9.有个页面中涉及到了判断密码强度的判定,网上有关的方法有很多,我这里选择了一种,方法如下:
    <div class="faBox">
        <span class="sobox">密码强度</span>
        <span class="sobox">
          <span class="litterSpan" :class="{litterSpan1:number ===1}">弱</span>
          <span class="litterSpan" :class="{litterSpan2:number ===2}">中</span>
          <span class="litterSpan" :class="{litterSpan3:number ===3}">强</span>
        </span>
    </div>
    
    checkPassword(sValue) {
      var modes = 0;
      //正则表达式验证符合要求的
      if (sValue.length < 1) return modes;
      if (/\d/.test(sValue)) modes++; //数字
      if (/[a-z]/.test(sValue)) modes++; //小写
      if (/[A-Z]/.test(sValue)) modes++; //大写
      if (/(?=[\x21-\x7e]+)[^A-Za-z0-9]/.test(sValue)) modes++; //特殊字符
  //逻辑处理
  switch (modes) {
    case 1:
      return 1;
      break;
    case 2:
      return 2;
      break;
    case 3:
    case 4:
      return sValue.length < 12 ? 3 : 3;
      break;
  }
  return modes;
},
    其中sValue为文本框输入的密码的字符串,拿到该输入的字符串通过正则去匹配规则,分为含有数字,大写,小写,特殊字符还有
    字符串长度的种类,去判断密码的强度,并且动态设置类名修改显示的效果
    
    10.项目刚开始的用的mock模拟数据,自定义了一下接口,后面写好了接口直接换就可以,让后台给你想要的数据格式即可,方便
    开发,还有就是配置一下接口前面的共有部分,方便后面切换本地和远程接口地址,总不能每次都要去项目里面一个接口一个接口
    的CV替换,配置如下:在项目中的src文件夹里面创建一个文件夹,取名随意,然后在里面创建一个js文件,取名也随意,内容为
    // let globalUrl = 'http://121.46.233.20:9002'
    let globalUrl = 'http://localhost:58302'
    let baseUrl = {
      globalUrl
    }
    export default baseUrl
    然后在main.js里面引入改文件,然后将项目里面的接口全都替换为如下形式:
    this.$http
      .get(this.$Api.globalUrl + "/Api/Login/ISLoginNew", {
        params: {
          uname: this.username,
          pwd: this.password
        }
      })
      这样操作下来方便后期的维护和测试本地的接口,可以直接修改该文件里面即可全部修改
      
    11.ele里面的tabs组件,再配合echars图表切换的时候,图表无法显示的问题,这时候在每个切换单元里面加个lazy属性即可解决,
    这个也是一个坑,不加这个属性你怎么切换图表都出不来。
    
    12.在使用el-time-picker该时间组件时,如果你要对自己选择的两个时间做限制,比如后面的时间大于前面的时间,可以利用配
    置项,比如
    <el-time-picker
        format="HH"
        value-format="HH"
        prefix-icon="none"
        size="mini"
        style="width:65px;"
        v-model="form.Fstrattime2"
        :picker-options="{selectableRange:`00:00:00 -${form.Fendtime2 ? form.Fendtime2+':00:00' : '23:59:00'}`}"
      ></el-time-picker>
      selectableRange: `${addbuyForm.preClockOffTime ? addbuyForm.preClockOffTime+':00' : '00:00:00'}-23:59:00`
      其中绑定的值为第一个时间,然后用第二个时间做限制,实现后面的前面的时间不能超过后面的时间,当然第二个时间也要这么
      去写,只不过属性对调一下,达到相互限制的效果。
      
    13.使用el-date-picker该组件限制起始时间小于结束时间的时候,可以如下操作,由于此方法只能对比时间戳,用上面这个字符串
    模板的方法显然不行(也可能是没有找到正确的用法吧)
    
        html部分如下:
        <div class="demo-input-suffix">
          <span class="title">转预算时间:</span>
          <el-date-picker
            :editable="false"
            v-model="startBudgetTime"
            :picker-options="pickerOptionsStart"
            size="mini"
            type="datetime"
            value-format="timestamp"
            format="yyyy-MM-dd HH:mm:ss"
            placeholder="选择开始日期"
            @change="changeEnd"/>-
          <el-date-picker
            :editable="false"
            v-model="endBudgetTime"
            :picker-options="pickerOptionsEnd"
            size="mini"
            type="datetime"
            value-format="timestamp"
            format="yyyy-MM-dd HH:mm:ss"
            default-time="['23:59:59']"
            placeholder="选择结束日期"
            @change="changeStart"/>
        </div>
        js部分如下:
        export default {
          data() {
            return {
              // 限制开始时间
              pickerOptionsStart: {},
              pickerOptionsEnd: {},
              startBudgetTime: '', // 预算开始时间
              endBudgetTime: '', // 预算结束时间
            }
          },
          methods: {
            // 结束时间限制开始时间
            changeStart() {
              if (!this.endBudgetTime) {
                this.pickerOptionsStart = {
                  disabledDate: {}
                }
                return
              }
              this.pickerOptionsStart = Object.assign({}, this.pickerOptionsStart, {
                // 可通过箭头函数的方式访问到this
                disabledDate: (time) => {
                  var times = ''
                  times = time.getTime() > this.endBudgetTime
                  return times
                }
              })
            },
            // 开始时间 控制结束时间
            changeEnd() {
              if (!this.startBudgetTime) {
                this.pickerOptionsEnd = {
                  disabledDate: {}
                }
                return
              }
              this.pickerOptionsEnd = Object.assign({}, this.pickerOptionsEnd, {
                disabledDate: (time) => {
                  return time.getTime() < this.startBudgetTime
                }
              })
            },
          }
        }
    
    14.在使用时间组件的时候,在你选择时间的时候出现报错并且不能选择,报错消息为类似于date.getFullyear这种的报错,可能
    是数据类型有问题,或者出现在编辑回显的情况下,可能是返回的数据形式不符合自己制定的要求,此时可以把type设置为string
    或者让后台返回的数据格式为自己制定的形式。
    
    15.当ele的树形tree控件出现出现的级数太多导致横向的字体显示不完全的解决办法,首先想到加横向滚动条,但是样式实在是太
    丑果断放弃,后面改用没有显示出来的字用省略号表示,然后加一个类似于tittle的样子,就是鼠标放上去能够看到层级上完整的
    字,代码如下:
        <el-tree
          :data="Treedata"
          show-checkbox
          default-expand-all
          node-key="id"
          ref="tree"
          highlight-current
          :props="defaultProps"
        >
              <span class="span-ellipsis" slot-scope="{ node}">
                <span :title="node.label">{{ node.label }}</span>
              </span>
        </el-tree>
        样式为
        .span-ellipsis {
            width: 100%;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
            display: block;
        }
    
    16.ele里面的table组件如何在选中一行的情况下选中含有相同属性的剩下的几项,并且同步可以实现同步取消选择。
    利用table组件里面自带的方法比如select-change等一些事件配合内置的toggleRowSelection方法可以实现同步选择上去,但是不
    能同步取消,那么换种思路,不使用表格自带的type=select的选择框,自己直接写单选框来实现,内容如下:
         <el-table
        ref="multipleTable"
        :data="tableData"
        tooltip-effect="dark"
        style="width: 100%;"
        >
        <el-table-column label="选择" width="120">
          <template slot-scope="scope">
            <div>
              <el-checkbox v-model="scope.row.selected" @change="selectChange(scope.row)"></el-checkbox>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="日期" width="120">
          <template slot-scope="scope">{{ scope.row.date }}</template>
        </el-table-column>
        <el-table-column prop="name" label="姓名" width="120">
        </el-table-column>
        <el-table-column prop="address" label="地址" show-overflow-tooltip>
        </el-table-column>
      </el-table>
      
    <script>
          const vm = new Vue({
            el: '#app',
            data: {
              tableData: [
                {
                  selected: false,
                  date: '2016-05-03',
                  name: '王小虎',
                  address: '上海市普陀区金沙江路 1518 弄',
                },
                {
                  selected: false,
                  date: '2016-05-02',
                  name: '王小虎',
                  address: '上海市普陀区金沙江路 1518 弄',
                },
                {
                  selected: false,
                  date: '2016-05-04',
                  name: '张三',
                  address: '上海市普陀区金沙江路 1518 弄',
                },
                {
                  selected: false,
                  date: '2016-05-01',
                  name: '李四',
                  address: '上海市普陀区金沙江路 1518 弄',
                },
                {
                  selected: false,
                  date: '2016-05-08',
                  name: '王小虎',
                  address: '上海市普陀区金沙江路 1518 弄',
                },
                {
                  selected: false,
                  date: '2016-05-02',
                  name: '王五',
                  address: '上海市普陀区金沙江路 1518 弄',
                },
                {
                  selected: false,
                  date: '2016-05-02',
                  name: '赵六',
                  address: '上海市普陀区金沙江路 1518 弄',
                },
              ],
            },
            methods: {
              selectChange(row) {
                console.log(row)
                const name = row.name
                this.tableData.forEach((item, index) => {
                  if (item.name === name) {
                    // 有相同的
                    item.selected = row.selected
                  }
                })
              },
            },
          })
    </script>
    直接使用ele里面的单选框组件,不使用自带的type=select,然后在tabledata里面额外加上一个专门用来控制选择的属性,来实现
    选择同步或者取消。
    
    17.到此ele部分差不多,其余的组件并没有什么额外的拓展使用,可能有一些页面用到了很多的计算属性,表单之中,这个正常使
    用就可以。还有项目中还用到了大量的echarts的使用,关于echarts的话额外写一篇来记录里面各种各样的图的使用以及配置项。