Skip to content

Vue

Vue2 代码片段

vscode 开发 Vue2 项目时便于小白学习使用的,复制代码片段添加在 vscode 中的代码片段就可以了。

{
    "Print to console": {
        "prefix": "vue",
        "body": [
            "<template>",
            "  <div class='main'>",
            "  </div>",
            "</template>",
            "",
            "<script>",
            "// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)",
            "// 例如:import 《组件名称》 from '《组件路径》';",
            "export default {",
            "  name:'',",
            "  // import引入的组件需要注入到对象中才能使用",
            "  components: {},",
            "  data () {",
            "  // 这里存放数据",
            "    return {}",
            "  },",
            "  // 计算属性",
            "  computed: {},",
            "  // 监控data中的数据变化",
            "  watch: {},",
            "  // 方法集合",
            "  methods: {},",
            "  // 生命周期 - 创建完成(可以访问当前this实例)",
            "  created () {},",
            "  // 生命周期 - 挂载完成(可以访问DOM元素)",
            "  mounted () {},",
            "  // 生命周期 - 创建之前",
            "  beforeCreate () {},",
            "  // 生命周期 - 挂载之前",
            "  beforeMount () {},",
            "  // 生命周期 - 更新之前",
            "  beforeUpdate () {},",
            "  // 生命周期 - 更新之后",
            "  updated () {},",
            "  // 生命周期 - 销毁之前",
            "  beforeDestroy () {},",
            "  // 生命周期 - 销毁完成",
            "  destroyed () {},",
            "  // 如果页面有keep-alive缓存功能,这个函数会触发",
            "  activated () {}",
            "}",
            "</script>",
            "",
            "<style lang='less' scoped>",
            "// @import url($3); 引入公共css类",
            ".main{",
            "  width: 100vw;",
            "  min-height: 100vh;",
            "}",
            "</style>",
            "$4"
        ],
        "description": "Log output to console"
    }
}

Vue2 表单验证

非常不规范,非常糟糕的代码,入门时手写的表单验证,将就着可以看,懒得更改了

<template>
  <div class="form">
    <div class="input-box name-box">
      <p class="placeholder" v-show="namePlaceholder">姓名<span>*</span></p>
      <input type="text" v-model="name" @focus="namefocus" @blur="nameblur" />
    </div>
    <span class="errorTip" v-show="this.name == '' && nameTip == true"
      >请输入姓名</span
    >
    <div class="input-box phone-box">
      <p class="placeholder" v-show="telPlaceholder">电话<span>*</span></p>
      <input
        type="text"
        v-model="phone"
        @focus="phonefocus"
        @blur="phoneblur"
      />
    </div>
    <span class="errorTip" v-show="!this.telCheck() && telTip == true"
      >请输入电话</span
    >
    <div class="input-box email-box">
      <p class="placeholder" v-show="mailPlaceholder">邮箱<span>*</span></p>
      <input
        type="text"
        v-model="email"
        @focus="emailfocus"
        @blur="emailblur"
      />
    </div>
    <span class="errorTip" v-show="!this.mailCheck() && mailTip == true"
      >请输入邮箱</span
    >
    <div class="input-box textarea-box">
      <p class="placeholder" v-show="textareaPlaceholder">留言<span>*</span></p>
      <textarea
        v-model="textarea"
        @focus="textareafocus"
        @blur="textareablur"
      ></textarea>
    </div>
    <span class="errorTip" v-show="this.textarea == '' && textareaTip == true"
      >请输入留言</span
    >
    <button @click="submit">提交</button>
  </div>
</template>

<script>
export default {
  components: {},
  props: {},
  data() {
    return {
      name: "",
      phone: "",
      email: "",
      textarea: "",
      nameTip: false,
      telTip: false,
      mailTip: false,
      textareaTip: false,
      namePlaceholder: true,
      telPlaceholder: true,
      mailPlaceholder: true,
      textareaPlaceholder: true,
    };
  },
  watch: {
    name() {
      if (this.name) {
        this.nameTip = false;
      }
    },
    phone() {
      if (this.phone) {
        this.telTip = false;
      }
    },
    email() {
      if (this.email) {
        this.mailTip = false;
      }
    },
    textarea() {
      if (this.textarea) {
        this.textareaTip = false;
      }
    },
  },
  created() {},
  mounted() {},
  computed: {},
  filters: {},
  methods: {
    // 光标
    namefocus() {
      this.nameTip = false;
      this.namePlaceholder = false;
    },
    nameblur() {
      this.nameTip = true;
      if (this.name === "") {
        this.namePlaceholder = true;
      }
    },
    phonefocus() {
      this.telTip = false;
      this.telPlaceholder = false;
    },
    phoneblur() {
      this.telTip = true;
      if (this.phone === "") {
        this.telPlaceholder = true;
      }
    },
    emailfocus() {
      this.mailTip = false;
      this.mailPlaceholder = false;
    },
    emailblur() {
      this.mailTip = true;
      if (this.email === "") {
        this.mailPlaceholder = true;
      }
    },
    textareafocus() {
      this.textareaTip = false;
      this.textareaPlaceholder = false;
    },
    textareablur() {
      this.textareaTip = true;
      if (this.textarea === "") {
        this.textareaPlaceholder = true;
      }
    },

    // 表单验证
    nameBlur() {
      if (this.name === "") {
        this.nameTip = true;
        return false;
      } else {
        this.nameTip = false;
        return true;
      }
    },
    telCheck() {
      const reg = /^[1][3,4,5,6,7,8,9][0-9]{9}$/;
      if (!reg.test(this.phone) || this.phone === "") {
        return false;
      } else {
        return true;
      }
    },
    telBlur() {
      if (!this.telCheck()) {
        this.telTip = true;
        return false;
      } else {
        this.telTip = false;
        return true;
      }
    },
    mailCheck() {
      // eslint-disable-next-line
      const reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
      if (!reg.test(this.email) || this.email === "") {
        return false;
      } else {
        return true;
      }
    },
    mailBlur() {
      if (!this.mailCheck()) {
        this.mailTip = true;
        return false;
      } else {
        this.mailTip = false;
        return true;
      }
    },
    textareaBlur() {
      if (this.textarea === "") {
        this.textareaTip = true;
        return false;
      } else {
        this.textareaTip = false;
        return true;
      }
    },

    // 提交
    submit() {
      this.name = this.name.trim();
      this.phone = this.phone.trim();
      this.email = this.email.trim();
      this.textarea = this.textarea.trim();
      if (
        this.nameBlur() &&
        this.telBlur() &&
        this.mailBlur() &&
        this.textareaBlur()
      ) {
        console.log(
          `name:${this.name};\nphone:${this.phone};\nemail:${this.email};\ntextarea:${this.textarea}`
        );
      } else {
        this.nameBlur();
        this.telBlur();
        this.mailBlur();
        this.textareaBlur();
      }
    },
  },
};
</script>

Vue2 时间戳转时间

方法一:转载黄轶老师的 format 方法:出处(黄轶老师 github https://github.com/ustbhuangyi);

<!-- **.vue -->
<template>
  <!-- time时间戳 -->
  <div>{{time | formatDate}}</div>
  <!-- 输出结果 -->
  <!-- <div>2016-07-23 21:52</div> -->
</template>
<script>
import {formatDate} from './common/date.js';
export default {
  filters: {
    formatDate(time) {
      var date = new Date(time);
      return formatDate(date, 'yyyy-MM-dd hh:mm');
    }
  }
}
</script>

// date.js
export function formatDate (date, fmt) {
  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
  }
  let o = {
    'M+': date.getMonth() + 1,
    'd+': date.getDate(),
    'h+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds()
  };
  for (let k in o) {
    if (new RegExp(`(${k})`).test(fmt)) {
      let str = o[k] + '';
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
    }
  }
  return fmt;
};

function padLeftZero (str) {
  return ('00' + str).substr(str.length);
};

方法二:

<template>
 <div>{{time | formatDate}}</div>
</template>
<script type="text/ecmascript-6">
 export default {
  data() {
   return {
    time: 1631692556
   };
  },
    created () {
        var times = this.time;
        times = times + '';
        var timesLength = times.length
        if(timesLength == 10){
            this.time = this.time * 1000
        }
    },
  filters: {
   formatDate: function (value) {
    let date = new Date(value);
    let y = date.getFullYear();
    let MM = date.getMonth() + 1;
    MM = MM < 10 ? ('0' + MM) : MM;
    let d = date.getDate();
    d = d < 10 ? ('0' + d) : d;
    let h = date.getHours();
    h = h < 10 ? ('0' + h) : h;
    let m = date.getMinutes();
    m = m < 10 ? ('0' + m) : m;
    let s = date.getSeconds();
    s = s < 10 ? ('0' + s) : s;
    return y + '-' + MM + '-' + d + ' ' + h + ':' + m + ':' + s;
   }
  }
 };
</script>

移动端分页加载

data() {
	return {
		current: 1,//当前页,第一次创建出了页面获取的时候直接this.current,加载的时候++this.current
	}
},
created() {
  window.addEventListener('scroll', this.onScroll)// 监听页面滚动
},
onScroll () {//页面滚动到底部
	let innerHeight = document.querySelector('#app').clientHeight// 可滚动容器的高度
	let outerHeight = document.documentElement.clientHeight// 屏幕尺寸高度
	let scrollTop = document.documentElement.scrollTop// 可滚动容器超出当前窗口显示范围的高度
	if (outerHeight + scrollTop >= innerHeight) {
		this.detailsAjax()// 加载更多操作
	}
},

detailsAjax() {//上拉加载
	this.$api.get_news_list_list({//axios
		category_id: this.tabActive,//参数
		current: ++this.current,//当前为第二页,每次加载就加一页
		pageSize:7,//每次加载显示多少钱数据
	}).then((res) => {
		console.log('*************************************', res);
		this.List = [...this.List, ...res.data];//将原来的数据和获取到的数据合并显示出来
	})
},

vue-video-player 样式更改

安装插件

yarn add vue-video-player

js 引入文件

import {videoPlayer} from 'vue-video-player'
import 'vue-video-player/src/custom-theme.css'// 必须加这个样式
import 'video.js/dist/video-js.css'

倍速播放在optionsplaybackRates

playbackRates: [0.5, 1.0, 1.5, 2.0], // 数组为空则不显示倍速播放按钮

css 样式更改(可建立单独文件 vue-video-player.less 放置在 assets/css 文件夹中)

/deep/ .video-player {
  .video-js {
    .vjs-button>.vjs-icon-placeholder:before {
      font-size: 3em;
      line-height: 1.5;
    }

    //播放按钮样式
    .vjs-big-play-button {
      width: 79px !important;
      height: 79px !important;
      background-color: rgba(0, 0, 0, 0.6);
      border-radius: 50%;
      border: none;

      .vjs-icon-placeholder {
        width: 30px;
        height: 30px;
        position: absolute;
        top: 50%;
        left: 55%;
        transform: translate(-50%, -50%);
        background: url(../../assets/img/icon-play.png) no-repeat center center;
        background-size: 26px 32px;

        &::before {
          content: '';
          opacity: 0;
        }
      }
    }

    // 控制栏样式
    .vjs-control-bar {
      height: 0.7547rem;
      background-color: transparent;
      background-image: linear-gradient(rgba(0, 0, 0, .001) 0%, rgba(0, 0, 0, .1) 15%, rgba(0, 0, 0, .5) 100%);

      // 进度条走完的颜色
      .vjs-progress-holder {

        .vjs-play-progress,
        .vjs-mouse-display {
          background-color: #ffffff;

          .vjs-time-tooltip {
            display: none;
          }
        }
      }

      .vjs-control {
        width: 5em;
      }

      // 隐藏声音控制按钮
      // 注意!!!如果视频默认自动播放是必须静音状态的
      .vjs-volume-panel {
        display: none;

        // 隐藏声音大小的拖动条
        .vjs-volume-control {
          display: none;
        }
      }

      // 隐藏两个时间中间的斜杠
      .vjs-time-divider {
        display: none;
      }

      // 将第二个时间在控制栏右边显示
      .vjs-duration {
        order: 3;
      }

      // 进度条的小点上下居中
      .vjs-play-progress::before {
        top: 50%;
        transform: translateY(-50%);
      }

      // 时间显示样式
      .vjs-time-control {
        position: relative;

        .vjs-duration-display,
        .vjs-current-time-display {
          font-size: 2em;
          line-height: 0;
          position: absolute;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%);
        }
      }

      // 倍速播放的样式
      .vjs-playback-rate {
        position: relative;

        .vjs-menu {
          display: none;
        }

        .vjs-playback-rate-value {
          font-size: 2em;
          line-height: 2.25;
        }
      }
    }
  }
}

vue-video-player 多个视频播放暂停逻辑的 demo

// vue-video-player多个视频播放暂停的demo
<template>
  <div>
    <ul class="mui-table-view">
      <li class="mui-table-view-cell mui-media" v-for="(item,index) in videolist" :key="item.title">
        <div class="mui-media-body">
          <p class="mui-ellipsis">
            <span>标题:{{ item.title }}</span>
            <span>上传时间:2020.5.21 13:14:88</span>
          </p>
        </div>
        <h1>
          <video-player
            class="video-player vjs-custom-skin"
            ref="videoPlayer"
            :playsinline="true"
            :options="playerOptions[index]"
            @play="onPlayerPlay($event,index)"
            @pause="onPlayerPause($event)"
          ></video-player>
        </h1>
      </li>
    </ul>
  </div>
</template>
<script>



import { videoPlayer } from "vue-video-player";
require("video.js/dist/video-js.css");
require("vue-video-player/src/custom-theme.css");

export default {
    components: {
    videoPlayer
  },
  data() {
    return {
      videolist: [
        { title: "标题A", movie: "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-learning-vue/52d32740-aecd-11ea-b244-a9f5e5565f30.mp4" },
        { title: "标题B", movie: "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-learning-vue/52d32740-aecd-11ea-b244-a9f5e5565f30.mp4" },
        { title: "标题C", movie: "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-learning-vue/52d32740-aecd-11ea-b244-a9f5e5565f30.mp4" },
        { title: "标题D", movie: "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-learning-vue/52d32740-aecd-11ea-b244-a9f5e5565f30.mp4" }
      ],
      playsinline: true,
      playerOptions: [],
      options: "",
    };
  },
  created() {
    this.getMovieList();
  },

  methods: {
    getMovieList() {
    // 这里正常来说应该是从后台获取的数据,以下操作都是在成功的回调函数里
      for (var i = 0; i < this.videolist.length; i++) {
          let arrs = {
            playbackRates: [1.0, 2.0, 3.0], //播放速度
            autoplay: false, //如果true,浏览器准备好时开始回放。
            muted: false, // 默认情况下将会消除任何音频。
            loop: false, // 导致视频一结束就重新开始。
            preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
            language: "zh-CN",
            aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
            fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
            sources: [
              {
                type: "video/mp4",
                type: "video/ogg",
                src: this.videolist[i].movie //url地址
              }
            ],
            poster: "", //封面地址
            notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。
            controlBar: {
              timeDivider: true,
              durationDisplay: true,
              remainingTimeDisplay: false,
              fullscreenToggle: true //全屏按钮
            }
          };
          this.playerOptions.push(arrs);
        }
    },
    onPlayerPlay(player, index) {
 
     
       var that = this.$refs.videoPlayer;
       for (let i = 0; i < that.length; i++) {
          if(i != index)
          that[i].player.pause()
       }
    },
    onPlayerPause(player) {

    }
  }
};
</script>
<style lang="less" scoped>
.mui-table-view {
  h1 {
    font-size: 14px;
    line-height: 24px;
    text-indent: 2em;
  }
  .mui-ellipsis {
    font-size: 14px;
    color: #226aff;
    display: flex;
    justify-content: space-between;
    margin: 5px 0;
  }
  video {
    width: 100%;
    height: 300px;
  }
}
</style>

Vue 自定义组件实现 v-model

v-model:1、监听<input>等元素本身的 input 事件。2、动态绑定父/子组件传递过来的值。

子组件

<template>
    <div>
        <input type="text"/>
    </div>
</template>

<script>
    export default {
        name: "son",
    }
</script>

父组件

<template>
    <div>
        {{value}}<son></son>
    </div>
</template>

<script>
    import son from './son.vue';
    export default {
        name: "parent",
        components:{
            son
        },
        data() {
            return {
                value: ""
            }
        }
    }
</script>

子组件需要做两件事,监控自己的 input 事件,并动态的实时来接受父组件传递过来的值,所以子组件修改如下:

<template>
    <div>
        <input type="text" :value="value" @input="changeValue"/>
    </div>
</template>

<script>
    export default {
        name: "son",
        // 这里接受父组件传递过来的value值,并且在上面input元素响应绑定
        props:["value"],
        methods: {
            // input事件实时监控输入框值得变化
            changeValue(e) {
                // 获取事件对象的值,不明白可以控制台打印一下
                let val = e.target.value;
                // 将变化后的值传递给父组件的input事件
                this.$emit("input", val);
            }
        },
    }
</script>

父组件亦然:

<template>
    <div>
        <!-- 与父组件的value绑定,并且通过input事件,将子组件的值传递给父组件-->
        {{value}}<son :value="value" @input="value = arguments[0]"></son>
    </div>
</template>

<script>
    import son from './son.vue';
    export default {
        name: "parent",
        components:{
            son
        },
        data() {
            return {
                value: ""
            }
        },
    }
</script>

至此自己的组件的双向绑定就完成了,当然父组件可以直接用 v-model 来与 value 进行绑定了

<template>
    <div>
        <!-- 注意这里已经可以使用v-model来进行数据双向绑定了 -->
        {{value}}<son v-model="value"></son>
    </div>
</template>

<script>
    import son from './son.vue';
    export default {
        name: "parent",
        components:{
            son
        },
        data() {
            return {
                value: ""
            }
        },
    }
</script>

Vue 强制页面刷新

在 app.vue 中定义 reload()方法

<template>
  <div id="app">
    <router-view v-if="isReload"/>
  </div>
</template>
 
<script>
export default {
  name: 'App',
  provide() {
    return {
      reload: this.reload
    }
  },
  data() {
    return {
      isReload: true
    }
  },
  methods: {
    reload() {
      this.isReload = false
      this.$nextTick(() => {
        this.isReload = true
      })
    }
  }
}
</script>

在需要强制刷新的页面引用

<script>
export default {
  inject: ['reload'],
  methods: {
    clickReload() { // 点击之后强制刷新
       this.reload()
     }
  }
}
</script>

Vue 移动端适配

安装插件 npm install postcss-px-to-viewport --save-dev

配置根目录下的.postcssrc.js 文件

// module.exports = {
//   plugins: {
//     autoprefixer: {}, // 用来给不同的浏览器自动添加相应前缀,如-webkit-,-moz-等等
//     "postcss-px-to-viewport": {
//       unitToConvert: "px", // 要转化的单位
//       viewportWidth: 850, // UI设计稿的宽度
//       unitPrecision: 6, // 转换后的精度,即小数点位数
//       propList: ["*"], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
//       viewportUnit: "vw", // 指定需要转换成的视窗单位,默认vw
//       fontViewportUnit: "vw", // 指定字体需要转换成的视窗单位,默认vw
//       selectorBlackList: ["wrap"], // 指定不转换为视窗单位的类名,
//       minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
//       mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
//       replace: true, // 是否转换后直接更换属性值
//       exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配
//       landscape: false // 是否处理横屏情况
//     }
//   }
// }

// 使用vant插件的配置
const path = require('path');
module.exports = ({ webpack }) => {
  const designWidth = webpack.resourcePath.includes(path.join('node_modules', 'vant')) ? 375 : 850;
  return {
    plugins: {
      autoprefixer: {},
      "postcss-px-to-viewport": {
        unitToConvert: "px",
        viewportWidth: designWidth,
        unitPrecision: 6,
        propList: ["*"],
        viewportUnit: "vw",
        fontViewportUnit: "vw",
        selectorBlackList: [],
        minPixelValue: 1,
        mediaQuery: true,
        exclude: [],
        landscape: false
      }
    }
  }
}

运行 vue 项目显示 network:unavailable

解决 vs code 运行vue项目显示network:unavailable 运行npm run serve的显示如下

  - Local:   http://localhost:8080/
  - Network: unavailable

找到vue项目中的devServer配置,如果没有就在项目下新建一个vue.config.js文件 将以下代码复制进去

module.exports = {
    devServer: {
        public: '197.10.59.150:8080',
        hot: true,
        disableHostCheck: true,
    }
};

完成此步骤在终端运行 npm run serve,Network:已经有链接产生但是点击进去无响应 需要将 public: '197.10.59.150:8080',这里的 ip 地址换成本机的 ip 地址 ip 地址的查找:控制面板 → 网络和 Internet→ 网络和共享中心 → 更改适配器设置 双击点开当前连接的网络,点击详细信息,把 IPv4 地址复制进 public

然后继续运行,成功解决问题。

element-table 滚动监听

el-table 设置固定高度,出现滚动条,加 ref

max-height="500"

ref="table"

this.$refs.table.bodyWrapper.addEventListener('scroll', (res) => {
  let height = res.target;
  let clientHeight = height.clientHeight;
  let scrollTop = height.scrollTop;
  let scrollHeight = height.scrollHeight;
  if(clientHeight + scrollTop >= scrollHeight){
    console.log('table滚动到底部,可以加载数据了');
  }
},true);

element-table-左右(右:上(一整行)下(多列))结构

<template>
	<div>
		<el-table :data="data" height="60vh" border stripe style="width: 100%" :header-cell-style="{ background: '#F1F4F9' }" :span-method="cellMerge">
			<el-table-column prop="id" label="序号" width="80" show-overflow-tooltip align="center"> </el-table-column>

			<el-table-column prop="title" width="1" show-overflow-tooltip align="center"> </el-table-column>

			<el-table-column label="问题来源" width="120" show-overflow-tooltip align="center">
				<template slot-scope="scope"> {{ scope.row.source == 1 ? '巡视' : scope.row.source == 2 ? '常规巡察' : scope.row.source == 3 ? '巡察回头看' : scope.row.source == 4 ? '延伸巡视' : scope.row.source == 5 ? '专项巡察' : '专项整治' }} </template>
			</el-table-column>

			<el-table-column label="整改状态" width="120" show-overflow-tooltip align="center">
				<template slot-scope="scope"> {{ scope.row.submitState == 0 ? '未提交' : scope.row.submitState == 1 ? '整改中' : scope.row.submitState == 2 ? '已整改' : scope.row.submitState == -1 ? '不再纳入整改' : scope.row.submitState == 5 ? '审核中' : '转为日常管理' }} </template>
			</el-table-column>

			<el-table-column label="是否超期" width="120" show-overflow-tooltip align="center">
				<template slot-scope="scope"> {{ scope.row.completedTimeout == 2 ? '已超期' : '未超期' }} </template>
			</el-table-column>
			<el-table-column prop="columnSix" label="处理处分" width="150" show-overflow-tooltip align="center"> </el-table-column>
			<!-- <el-table-column v-if="isFourFour" prop="recoverDamages" :label="trendTwo" width="150" show-overflow-tooltip align="center"> </el-table-column> -->
		</el-table>
	</div>
</template>

<script>
export default {
	name: 'HelpAbout',
	data() {
		return {
			data: [
				{
					id: 1,
					title: '问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容',
					source: 1,
					submitState: 0,
					completedTimeout: 2,
					columnSix: '李雷',
				},
				{
					id: 2,
					title: '问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容',
					source: 1,
					submitState: 0,
					completedTimeout: 2,
					columnSix: '李雷',
				},
				{
					id: 3,
					title: '问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容',
					source: 1,
					submitState: 0,
					completedTimeout: 2,
					columnSix: '李雷',
				},
				{
					id: 4,
					title: '问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容',
					source: 1,
					submitState: 0,
					completedTimeout: 2,
					columnSix: '李雷',
				},
				{
					id: 5,
					title: '问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容问题描述内容',
					source: 1,
					submitState: 0,
					completedTimeout: 2,
					columnSix: '李雷',
				},
			],
		}
	},
	created() {
		this.changedata()
	},
	mounted() {},
	methods: {
		// 重新定义数据结构
		changedata() {
			let arr = []
			for (let i in this.data) {
				arr.push(this.data[i])
				arr.push(this.data[i])
			}
			this.data = arr
		},
		// 改变表格样式
		cellMerge({ row, column, rowIndex, columnIndex }) {
			// 合并列
			if (columnIndex === 1) {
				if (rowIndex % 2 === 0) {
					return {
						rowspan: 1,
						colspan: 5,
					}
				}
			} else {
				if (columnIndex > 1) {
					if (rowIndex % 2 === 0) {
						return {
							rowspan: 0,
							colspan: 0,
						}
					}
				}
			}
			// 合并行
			if (rowIndex % 2 == 0) {
				if (columnIndex === 0) {
					return {
						rowspan: 2,
						colspan: 1,
					}
				}
			} else {
				if (columnIndex === 0) {
					return {
						rowspan: 0,
						colspan: 0,
					}
				}
			}
		},
	},
}
</script>

报错:TypeError: Cannot read property 'components' of undefined

不要引入空类型的混入文件