一、vue之分页组件(含勾选、过滤、ES6写法)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>勾选和分页组件之vue2.6.10版</title>
  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <style>
    table{
      border-collapse: collapse;
      border: 1px solid #cbcbcb;
      width:1000px;
    }
    table td,table th {
      padding: 5px;
      border: 1px solid #cbcbcb;
    }
    table thead {
      background-color: #e0e0e0;
      color: #000;
      text-align: left;
    }
    .filter{
      width:998px;
      border:1px solid gray;
      padding:10px 0px;
    }
    .line{
      display:flex
    }
    .group{
      width:330px;
    }
    .label{
      display: inline-block;
      width:120px;
      height: 24px;
      line-height: 24px;
      text-align: right;
    }
    .input{
      display: inline-block;
      width:180px;
      height: 24px;
      line-height: 24px;
      border-radius: 3px;
    }
    .select{
      display: inline-block;
      width:188px;
      height: 26px;
      line-height: 26x;
      border-radius: 3px;
    }
  </style>
</head>
<body>
<div id="app">
  <div style="padding-bottom:5px;color:red">
    <button style="color:red" @click="checkDatasOne.getResultOfCheckAndFilter(divideDatasOne.isShowFilter,divideDatasOne.filterOptions)">获取勾选和过滤结果</button>
    <span>{{checkDatasOne.toServerDatas}}</span>
  </div>
  <div style="padding-bottom:5px">
    <img :src="checkDatasOne.stateAllPages&&checkDatasOne.allExcludedIds.length===0?checkImg.yes:checkImg.no" @click="checkDatasOne.clickAllPages(divideDatasOne.tableDatas)"/>
    <span>{{checkDatasOne.textAllPages}}</span>
  </div>
  <div style="padding-bottom:5px">
    <button @click="divideDatasOne.toggleShowFilter()">{{divideDatasOne.isShowFilter?'关闭过滤':'使用过滤'}}</button>
    <button @click="divideDatasOne.emptyFilterOptions({value5:10})">清空过滤</button> 
    <button @click="divideDatasOne.request(1,divideDatasOne.eachPageItemsNum)">刷新</button>
  </div>
  <div style="margin-bottom:5px" class="filter" v-show="divideDatasOne.isShowFilter">
    <div class="line">
      <div class="group">
        <label class="label">标签</label>
        <input class="input" type="text" v-model="divideDatasOne.filterOptions.value1" />
      </div>
      <div class="group">
        <label class="label">这就是长标签</label>
        <input class="input" type="text" v-model="divideDatasOne.filterOptions.value2" />
      </div>
      <div class="group">
        <label class="label">标签</label>
        <input class="input" type="text" v-model="divideDatasOne.filterOptions.value3" />
      </div>
    </div>
    <div class="line" style="padding-top: 10px;">
      <div class="group">
        <label class="label">这就是长标签</label>
        <input class="input" type="text" v-model="divideDatasOne.filterOptions.value4" />
      </div>
      <div class="group">
        <label class="label">下拉框</label>
        <select class="select" v-model="divideDatasOne.filterOptions.value5">
          <option v-for="item in selectOptions" :value="item.back">{{item.front}}</option>
        </select>
      </div>
      <div class="group">
        <label class="label"></label>
        <button style="width:188px;height:28px" @click="divideDatasOne.request(1,divideDatasOne.eachPageItemsNum)">过滤</button>
      </div>
    </div>
  </div>
  <div style="width:1000px">
    <table>
      <thead>
      <tr>
        <th><img :src="checkDatasOne.stateThisPage?checkImg.yes:checkImg.no"
          @click="checkDatasOne.clickThisPage(divideDatasOne.tableDatas,divideDatasOne.allItemsNum)"/></th>
        <th>序号</th>
        <th>数据1</th>
        <th>数据2</th>
        <th>数据3</th>
        <th>数据4</th>
        <th>数据5</th>
        <th>数据6</th>
      </tr>
      </thead>
      <tbody>
      <tr v-for="(data,index) in divideDatasOne.tableDatas">
        <td><img :src="data.state?checkImg.yes:checkImg.no" @click="checkDatasOne.clickSingleItem(data,divideDatasOne.tableDatas,divideDatasOne.allItemsNum)"/></td>
        <td>{{(divideDatasOne.nowPageNum-1)*divideDatasOne.eachPageItemsNum + (index+1)}} </td>
        <td>{{ data.key1 }}</td>
        <td>{{ data.key2 }}</td>
        <td>{{ data.key3 }}</td>
        <td>{{ data.key4 }}</td>
        <td>{{ data.key5 }}</td>
        <td>{{ data.key6 }}</td>
      </tr>
      </tbody>
    </table>
  </div>
  <divide-page :divide-datas="divideDatasOne" :check-datas="checkDatasOne" :fixed-datas="fixedDatas"></divide-page>
</div>
</body>
<script>
  new Vue({
    el: '#app',
    data(){
      return {
        divideDatasOne:{
          nowPageNum:0,
          allPagesNum:0,
          allItemsNum:0,
          eachPageItemsNum:0,
          tableDatas:[],
          filterOptions:{value5:10},
          isShowFilter:false,
          otherDatas:{}
        },
        checkDatasOne:{
          idKey: 'id',//每条数据的唯一标志
          stateThisPage: false,//当前页所有项是否全选
          allIncludedIds: [],//所有被选中数据的ID构成的数组
          allExcludedIds: [],//所有没被选中数据的ID构成的数组
          textAllPages: '全选未启用,没有选择任何项!',//复选框被点击后的提示文字。
          stateAllPages: false,//复选框被点击后的提示文字。
          toServerDatas: null,
        },
      }
    },
    methods: {
 
    },
    created(){
      this.fixedDatas = {};
      this.selectOptions = [
        { back: 10, front: '来' },
        { back: 20, front: '来自于' },
        { back: 30, front: '来自于国内' },
        { back: 40, front: '来自于国内攻击' },
        { back: 50, front: '来自于国内攻击-2' }
      ];
      this.checkImg = {
        yes: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAA+UlEQVQoFZWSMU4DMRBF/584G7QSRcIxuAZKEykNEiUVHVTQRaKh4AIcgAvQpkukVDlBOAYNSGSlXXuwpViyYYFdS9aMZ/6bsezh5HZ3T2KhqkfosEhWqnjkyd1u3xWKdQMsfaEAB0Zilf8swfdU0w0klmpGpz1BvpbHcklbPf8Okts0CfJtWBTz/Yc++Jc8S3PZVQfKGwiuvMD6XYsMzm1dT/1jXKdQ8E0asHRrAzOzbC6UGINWHPQp1UQ/6wjF2LpmJSKfhti4Bi8+lhWP4I+gAqV1uqSi8j9WRuF3m3eMWVUJBeKxzUoYn7bEX7HDyPmB7QEHbRjyL+/+VnuXDUFOAAAAAElFTkSuQmCC',
        no: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAAbklEQVQoFWM8c+ZMLQMDQxUQcwAxMeAHUFEbC5CoYmNj02ZmZn5FjK6/f/+K/fr16ypIIwdIk7a29hdiNF69ehWkjIOJGMXY1IxqxBYqULEhFDiglPMDlIygKQKPryBSILUgPSCNbaC0B6RJSuQAbowizhJuOsAAAAAASUVORK5CYII=',
      }
    },
    components: {
      dividePage: {
        props: {
          divideDatas: {
            type: Object,
            default: {}
          },
          checkDatas: {
            type: Object,
            default: {}
          },
          fixedDatas: {
            type: Object,
            default: {}
          }
        },
        template: `
          <div v-show="divideDatas.allPagesNum>=1" style="display:flex;width:1000px;margin-top:20px;">
            <div>
              <button
                v-show="divideDatas.allPagesNum>10"
                @click="clickDividePage('front') "
                :disabled="divideDatas.nowPageNum===1"
              >上一页</button>
              <button 
                :disabled="number==='...'"
                v-for="number in divideArray"
                @click="clickDividePage(number)"
                :style="{marginRight:'5px',color:number===divideDatas.nowPageNum?'red':'gray'}"
              >{{ number }}</button>
              <button
                v-show="divideDatas.allPagesNum>10"
                @click="clickDividePage('back')" 
                :disabled="divideDatas.nowPageNum===divideDatas.allPagesNum"
              >下一页</button>
            </div>
            <div style="display:flex; flex:1; justify-content:flex-end;">
              <div style="margin-right:20px;">
                <span>转到第</span>
                <input type="text" v-model="customString" @keydown="clickDividePage('leap',$event)" style="width:30px;">
                <span>页</span>
                <button @click="clickDividePage('leap',{which:13})">Go</button>
              </div>
              <div>
                <span>每页显示</span>
                <select v-model="divideDatas.eachPageItemsNum" @change="selectChange(divideDatas.eachPageItemsNum)">
                  <option v-for="item in numOptions" :value="item.back">{{item.front}}</option>
                </select>
                <span>条,</span>
              </div>
              <div>
                <span>{{frontMoreText}}</span>
                <span>{{totalText}}</span>
                <span>{{divideDatas.allItemsNum}}</span>
                <span>{{totalUnit}}</span>
                <span>{{backMoreText}}</span>
              </div>
            </div>  
          </div
        `,
        data() {
          return {
            customString:''
          }
        },
        created(){
          var that = this;
          //1、请求配置
          this.url = this.fixedDatas.url || '';
          this.method = this.fixedDatas.method || 'post';
          this.isShowParams = this.fixedDatas.isShowParams || false;//显式还是隐式传参。有时需要在请求发出前手动改变。
          //2、响应配置(前端通过这个配置,获取后台的数据)
          this.nowPageNum = this.fixedDatas.nowPageNum || 'nowPageNum';//来自服务器的当前页码
          this.allPagesNum = this.fixedDatas.allPagesNum || 'allPagesNum';//来自服务器的所有页页数
          this.allItemsNum = this.fixedDatas.allItemsNum || 'allItemsNum';//来自服务器的所有页数据数
          this.eachPageItemsNum = this.fixedDatas.eachPageItemsNum || 'eachPageItemsNum';//来自服务器的每页最多数据数
          this.tableDatas = this.fixedDatas.tableDatas || 'tableDatas';//来自服务器的表格数据
          //3、以下配置使用哪种转圈方式(前端根据需要决定,不受后台影响)
          this.partCircle = this.fixedDatas.partCircle;//局部是否转圈。this.fixedDatas.partCircle=$scope.partCircle={isShow =false}。
          this.isUsePartCircle = this.fixedDatas.isUsePartCircle;//局部是否转圈,由当前页的一个变量控制
          this.isUseWholeCircle = this.fixedDatas.isUseWholeCircle;//全局是否转圈,由本项目的一个服务控制
          //4、初始化以下数据,供页面使用(前端根据需要决定,不受后台影响)
          this.frontMoreText = this.fixedDatas.frontMoreText || "";//('文字 ')或者("文字 "+result.numOne+" 文字 ")
          this.totalText = this.fixedDatas.totalText || "";//'共'
          this.totalUnit = this.fixedDatas.totalUnit || '条';//总数据的单位
          this.backMoreText = this.fixedDatas.backMoreText || "";//(' 文字')或者("文字 "+result.numThree+" 文字")
          this.numOptions = [
            { back: 10, front: 10 },
            { back: 20, front: 20 },
            { back: 30, front: 30 },
            { back: 40, front: 40 },
            { back: 50, front: 50 }
          ];
          this.request = this.divideDatas.request = function (nowPageNum,eachPageItemsNum) {
            var isOnce = true;
            //1、向后台发送请求,
            //2、返回正确结果result
            var data=[];
            var allItemsNum = 193;
            var nowPageNum = nowPageNum||1;
            var eachPageItemsNum = eachPageItemsNum||10;
            var allPagesNum = Math.ceil(allItemsNum/eachPageItemsNum);
            for(var i=1;i<=allItemsNum;i++){
              var obj={
                id:'id'+i,
                key1:'数据'+(i+0),
                key2:'数据'+(i+1),
                key3:'数据'+(i+2),
                key4:'数据'+(i+3),
                key5:'数据'+(i+4),
                key6:'数据'+(i+5),
                key7:'数据'+(i+6),
              };
              data.push(obj)
            }
            var tableDatas = data.slice((nowPageNum-1)*eachPageItemsNum,nowPageNum*eachPageItemsNum);
            //3、使用正确结果result
            that.customString = nowPageNum;
            that.divideDatas.nowPageNum = nowPageNum;
            that.divideDatas.allPagesNum = allPagesNum;
            that.divideDatas.allItemsNum = allItemsNum;
            that.divideDatas.eachPageItemsNum = eachPageItemsNum;
            that.divideDatas.tableDatas = tableDatas;//正常逻辑
            if(that.divideDatas.once && isOnce){//只使用一次,常用于初始化一些数据,比如过滤条件
              that.divideDatas.once();
              isOnce = false;
            }
            if(that.divideDatas.trueCb){//异常逻辑
              that.divideDatas.trueCb()
            }
            if(that.checkDatas && that.checkDatas.signCheckbox){//处理勾选
              that.checkDatas.signCheckbox(that.divideDatas.tableDatas)
            }
            that.createDividePage();//创建分页
            //4、处理错误结果
            if(that.divideDatas.errorCb){
              that.divideDatas.errorCb()
            }
          };
          if (!this.divideDatas.isAbandonInit) {
            this.request(1,this.divideDatas.eachPageItemsNum);
          };
          this.divideDatas.toggleShowFilter = function () {
            this.isShowFilter = !this.isShowFilter;
            if (!this.isShowFilter) {
              this.request(1,that.divideDatas.eachPageItemsNum);
            }
          };
          this.divideDatas.emptyFilterOptions = function (extraObject) {
            //清空选项时,所有值恢复成默认
            for(var key in this.filterOptions){
              this.filterOptions[key] = undefined;
            };
            if (extraObject) {
              //小部分选项的默认值不是undefined
              for(var key in extraObject){
                this.filterOptions[key] = extraObject[key];
              };
            };
            this.request(1,that.divideDatas.eachPageItemsNum);
          };
          this.checkDatas.init=function(){//点击“刷新”、“过滤”、“清除过滤”时执行
            this.idKey = idKey ? idKey : 'id';
            this.allIncludedIds = [];
            this.allExcludedIds = [];
            this.textAllPages = '全选未启用,没有选择任何项!';
            this.stateAllPages = false;
            this.stateThisPage = false;
          };
          this.checkDatas.clickAllPages = function (itemArray) {//所有页所有条目全选复选框被点击时执行的函数
            if(this.stateAllPages){
              if(this.allExcludedIds.length>0){
                this.stateAllPages = true;
                this.stateThisPage = true;
                this.textAllPages= '全选已启用,没有排除任何项!';
                itemArray.forEach(function (item) {
                  item.state = true;
                });
              }else if(this.allExcludedIds.length==0){
                this.stateAllPages = false;
                this.stateThisPage = false;
                this.textAllPages= '全选未启用,没有选择任何项!';
                itemArray.forEach(function (item) {
                  item.state = false;
                });
              }
            }else{
              this.stateAllPages = true;
              this.stateThisPage = true;
              this.textAllPages= '全选已启用,没有排除任何项!';
              itemArray.forEach(function (item) {
                item.state = true;
              });
            }
            this.allExcludedIds = [];
            this.allIncludedIds = [];
          };
          this.checkDatas.clickThisPage = function (itemsArray,allItemsNum) {//当前页所有条目全选复选框被点击时执行的函数
            var that = this;
            this.stateThisPage = !this.stateThisPage
            itemsArray.forEach(function (item) {
              item.state = that.stateThisPage;
              if (item.state) {
                that.delID(item[that.idKey], that.allExcludedIds);
                that.addID(item[that.idKey], that.allIncludedIds);
              } else {
                that.delID(item[that.idKey], that.allIncludedIds);
                that.addID(item[that.idKey], that.allExcludedIds);
              }
            });
            if(this.stateAllPages){
              if(this.stateThisPage && this.allExcludedIds.length === 0){
                this.textAllPages = '全选已启用,没有排除任何项!';
              }else{
                this.textAllPages = '全选已启用,已排除'+ this.allExcludedIds.length + '项!排除项的ID为:' + this.allExcludedIds;
              }
            }else{
              if(!this.stateThisPage && this.allIncludedIds.length === 0){
                this.textAllPages='全选未启用,没有选择任何项!';
              }else{
                this.textAllPages = '全选未启用,已选择' + this.allIncludedIds.length + '项!选择项的ID为:' + this.allIncludedIds;
              }
            }
          };
          this.checkDatas.clickSingleItem = function (item, itemsArray, allItemsNum) {//当前页单个条目复选框被点击时执行的函数
            var that = this;
            item.state = !item.state;
            if (item.state) {
              this.stateThisPage = true;
              this.addID(item[this.idKey], this.allIncludedIds);
              this.delID(item[this.idKey], this.allExcludedIds);
              itemsArray.forEach( function (item) {
                if (!item.state) {
                  that.stateThisPage = false;
                }
              });
            } else {
              this.stateThisPage = false;
              this.addID(item[this.idKey], this.allExcludedIds);
              this.delID(item[this.idKey], this.allIncludedIds);
            }
            if(this.stateAllPages){
              if(this.stateThisPage && this.allExcludedIds.length === 0){
                this.textAllPages = '全选已启用,没有排除任何项!';
              }else{
                this.textAllPages = '全选已启用,已排除'+ this.allExcludedIds.length + '项!排除项的ID为:' + this.allExcludedIds;
              }
            }else{
              if(!this.stateThisPage && this.allIncludedIds.length === 0){
                this.textAllPages='全选未启用,没有选择任何项!';
              }else{
                this.textAllPages = '全选未启用,已选择' + this.allIncludedIds.length + '项!选择项的ID为:' + this.allIncludedIds;
              }
            }
          };
          this.checkDatas.signCheckbox = function (itemsArray) {//标注当前页被选中的条目,在翻页成功后执行。
            var that = this;
            if(this.stateAllPages){
              this.stateThisPage = true;
              itemsArray.forEach(function (item) {
                var thisID = item[that.idKey];
                var index = that.allExcludedIds.indexOf(thisID);
                if (index > -1) {
                  item.state = false;
                  that.stateThisPage = false;
                } else {
                  item.state = true;
                }
              });
            }else{
              this.stateThisPage = true;
              itemsArray.forEach( function (item) {
                var thisID = item[that.idKey];
                var index = that.allIncludedIds.indexOf(thisID);
                if (index === -1) {
                  item.state = false;
                  that.stateThisPage = false;
                }
              });
            }
          };
          this.checkDatas.addID = function (id, idArray) {
            var index = idArray.indexOf(id);
            if (index === -1) {
              idArray.push(id);//如果当前页的单项既有勾选又有非勾选,这时勾选当前页全选,需要这个判断,以免重复添加
            }
          };
          this.checkDatas.delID = function (id, idArray) {
            var index = idArray.indexOf(id);
            if (index > -1) {
              idArray.splice(index, 1)
            }
          };
          this.checkDatas.getResultOfCheckAndFilter = function (isShowFilter,filterOptions) {//获取发送给后台的所有参数。
            var toServerDatas;
            var allIncludedIds = that.deepClone(this.allIncludedIds);
            var allExcludedIds = that.deepClone(this.allExcludedIds);
            if (!this.stateAllPages) {
              if (allIncludedIds.length === 0) {
                //return 弹窗告知:没有勾选项
              }
              toServerDatas = {
                isSelectAll: false,
                allIncludedIds: allIncludedIds,
              }
            }else {
              toServerDatas = { //exclude
                isSelectAll: true,
                allExcludedIds: allExcludedIds,
              };
            }
            if (isShowFilter) {
              for(var key in filterOptions){
                toServerDatas[key]=filterOptions[key]
              }
            }
            this.toServerDatas=toServerDatas;//这行代码在实际项目中不需要
            return toServerDatas;
          }
        },
        methods: {
          deepClone : function (arrayOrObject) {
            function isArray(value) { return {}.toString.call(value) === "[object Array]"; }
            function isObject(value) { return {}.toString.call(value) === "[object Object]"; }
            var target = null;
            if (isArray(arrayOrObject)) target = [];
            if (isObject(arrayOrObject)) target = {};
            for (var key in arrayOrObject) {
              var value = arrayOrObject[key];
              if (isArray(value) || isObject(value)) {
                target[key] = deepClone(value);
              } else {
                target[key] = value;
              }
            }
            return target;
          },
          selectChange:function(eachPageItemsNum){
            this.divideDatas.eachPageItemsNum = eachPageItemsNum;
            this.request(1,eachPageItemsNum);
          },
          createDividePage : function () {
            var divideArray = [];
            var allPagesNum = this.divideDatas.allPagesNum;
            var nowPageNum = this.divideDatas.nowPageNum;
            if (allPagesNum >= 1 && allPagesNum <= 10) {
              for (var i = 1; i <= allPagesNum; i++) {
                divideArray.push(i);
              }
            } else if (allPagesNum >= 11) {
              if (nowPageNum > 6) {
                divideArray.push(1);
                divideArray.push(2);
                divideArray.push(3);
                divideArray.push('...');
                divideArray.push(nowPageNum - 1);
                divideArray.push(nowPageNum);
              } else {
                for (i = 1; i <= nowPageNum; i++) {
                  divideArray.push(i);
                }
              }
              // 以上当前页的左边,以下当前页的右边
              if (allPagesNum - nowPageNum >= 6) {
                divideArray.push(nowPageNum + 1);
                divideArray.push(nowPageNum + 2);
                divideArray.push('...');
                divideArray.push(allPagesNum - 2);
                divideArray.push(allPagesNum - 1);
                divideArray.push(allPagesNum);
              } else {
                for (var i = nowPageNum + 1; i <= allPagesNum; i++) {
                  divideArray.push(i);
                }
              }
            }
            this.divideArray = divideArray;
          },
          clickDividePage : function (stringOfNum, event) {
            var allPagesNum = this.divideDatas.allPagesNum;
            var nowPageNum = this.divideDatas.nowPageNum;
            if (stringOfNum === 'front' && nowPageNum != 1) {
              nowPageNum--;
            } else if (stringOfNum === 'back' && nowPageNum != allPagesNum) {
              nowPageNum++;
            } else if (stringOfNum === 'leap') {
              if (event.which != 13) return;//不拦截情形:(1)聚焦输入框、按“Enter”键时;(2)点击“GO”时
              var customNum = Math.ceil(parseFloat(this.customString));
              if (customNum < 1 || customNum == 'NaN') {
                nowPageNum = 1;//不给提示
              } else if(customNum > allPagesNum) {
                nowPageNum = allPagesNum;//不给提示
              } else {
                nowPageNum = customNum;
              }
            } else {
              nowPageNum = Math.ceil(parseFloat(stringOfNum));
            }
            this.request(nowPageNum,this.divideDatas.eachPageItemsNum);
          },
        }
      }
    },
  })
</script>
</html>
附:vue之分页组件的ES6写法,可模仿“单文件”

二、Vue之五级联动--省、市、县、乡、村(含3个版本)
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Vue之五级联动--省、市、县、乡、村(含3个版本)</title>
</head>
<style type="text/css">
  .edit{
    height:40px;
    line-height: 40px;
  }
  .paddingBottom{
    padding-bottom: 40px;
  }
  .question-select {
    height: 60px;
  }
  .question-select select {
    border-radius: 5px;
    box-shadow: 0 0 5px #666;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    border: none;
    outline: none;
    height: 40px;
    padding: 0 20px;
    color: #333;
    font-size: 22px;
  }
  .question-select select.short {
    width: 120px;
  }
  .question-select select.long {
    width: 240px;
  }
  .birth-year{
    width:90px;
    margin-right: 20px;
    height: 100px;
    overflow-y: scroll;
  }
  .birth-month{
    width:70px;
    margin-right: 20px;
  }
  .birth-date{
    width:70px;
  }
</style>
<body>
  <!-- 以下是新版本(组件)HTML -->
  <div class="edit">以下是新版本(组件),符合实际应用场景</div>
  <div id="newVersionComponent">
    <five-grade :all-datas="allDatas"></five-grade>
  </div>
  <!-- 以下是新版本(普通)HTML -->
  <div class="edit">以下是新版本(普通),符合实际应用场景</div>
  <div id="newVersionCommon">
    <div class="question-select">
      <select v-model="singleProvince" class="short" @change="selectName(singleProvince)">
        <option v-for="key in allProvinces" :value="key" v-text="key"></option>
      </select>
      <select v-model="singleCity" v-show="singleProvince" class="short" @change="selectName(singleProvince,singleCity)">
        <option v-for="key in allCitys" :value="key" v-text="key"></option>
      </select>
      <select v-model="singleCounty" v-show="singleCity" class="short" @change="selectName(singleProvince,singleCity,singleCounty)">
        <option v-for="key in allCountys" :value="key" v-text="key"></option>
      </select>
      <select v-model="singleTown" v-show="singleCounty" class="long" @change="selectName(singleProvince,singleCity,singleCounty,singleTown)">
        <option v-for="key in allTowns" :value="key" v-text="key"></option>
      </select>
      <select v-model="singleVillage" v-show="singleTown" class="long" @change="selectName(singleProvince,singleCity,singleCounty,singleTown,singleVillage)">
        <option v-for="key in allVillages" :value="key" v-text="key"></option>
      </select>
    </div>
    <div class="paddingBottom">{{address}}</div>
  </div>
  <!-- 以下是旧版本(普通)HTML -->
  <div class="edit">以下是旧版本(普通),不符合实际应用场景</div>
  <div id="oldVersion">
    <div class="question-select">
      <select v-model="singleProvince" v-if="singleProvince" class="short">
        <option v-for="(value,key) in allProvinces" :value="key" v-text="key"></option>
      </select>
      <select v-model="singleCity" v-if="singleCity" class="short">
        <option v-for="(value,key) in allCitys" :value="key" v-text="key"></option>
      </select>
      <select v-model="singleCounty" v-if="singleCounty" class="short">
        <option v-for="(value,key) in allCountys" :value="key" v-text="key"></option>
      </select>
      <select v-model="singleTown" v-if="singleTown" class="long">
        <option v-for="(value,key) in allTowns" :value="key" v-text="key"></option>
      </select>
      <select v-model="singleVillage" v-if="singleVillage" class="long">
        <option v-for="(value,key) in allVillages" :value="key" v-text="key"></option>
      </select>
    </div>
    <div class="paddingBottom">{{address}}</div>
  </div>
  <div class="edit">以下是年月日分别选择</div>
  <div id="yearMonthDate">
    <div class="question-select">
      <select v-model="yearMonthDate.year" @click="clickYear" @change="changeYear" class="birth-year">
        <option v-for="item in years" :key="item.key" :label="item.key" :value="item.value">
        </option>
      </select>
      <select v-model="yearMonthDate.month" @click="clickMonth" @change="changeMonth" class="birth-month">
        <option v-for="item in months" :key="item.key" :label="item.key" :value="item.value">
        </option>
      </select>
      <select v-model="yearMonthDate.day" @click="clickDay" class="birth-date">
        <option v-for="item in days" :key="item.key" :label="item.key" :value="item.value">
        </option>
      </select>
    </div>
    <div class="paddingBottom">您选择的年月日是:{{yearMonthDate.year}}年{{yearMonthDate.month}}月{{yearMonthDate.day}}日</div>
  </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- Vue.js v2.6.10 -->
<script type="text/javascript">
  //以下是新版本(组件)JS
  Vue.component('five-grade', {
    template: `
      <div>
        <div class="question-select">
          <select v-model="singleProvince" class="short" @change="selectName(singleProvince)">
            <option v-for="key in allProvinces" :value="key" v-text="key"></option>
          </select>
          <select v-model="singleCity" v-show="singleProvince" class="short" @change="selectName(singleProvince,singleCity)">
            <option v-for="key in allCitys" :value="key" v-text="key"></option>
          </select>
          <select v-model="singleCounty" v-show="singleCity" class="short" @change="selectName(singleProvince,singleCity,singleCounty)">
            <option v-for="key in allCountys" :value="key" v-text="key"></option>
          </select>
          <select v-model="singleTown" v-show="singleCounty" class="long" @change="selectName(singleProvince,singleCity,singleCounty,singleTown)">
            <option v-for="key in allTowns" :value="key" v-text="key"></option>
          </select>
          <select v-model="singleVillage" v-show="singleTown" class="long" @change="selectName(singleProvince,singleCity,singleCounty,singleTown,singleVillage)">
            <option v-for="key in allVillages" :value="key" v-text="key"></option>
          </select>
        </div>
        <div class="paddingBottom">{{address}}</div>
      </div>
    `,
    props: {      
      allDatas: {
        type: Object
      }
    },
    data: function(){
      return {
        allProvinces: [],
        singleProvince: '',
        allCitys: [],
        singleCity: '',
        allCountys: [],
        singleCounty: '',
        allTowns: [],
        singleTown: '',
        allVillages: [],
        singleVillage: '',
        address: '',
      }
    },
    beforeMount: function () {
      this.selectName();
    },
    methods: {
      selectName: function(singleProvince,singleCity,singleCounty,singleTown,singleVillage){
        var siteArray = [singleProvince,singleCity,singleCounty,singleTown,singleVillage];
        var modelArray = ['singleProvince','singleCity','singleCounty','singleTown','singleVillage'];
        var optionsArray = ['allProvinces','allCitys','allCountys','allTowns','allVillages'];
        var allDatasNext;
        var address = "你选择的地址是:";
        for(var i=0;i<siteArray.length;i++){//遍历数组所有项
          if(!siteArray[i]) this[modelArray[i]] = '';
        }
        for(var i=0;i<siteArray.length;i++){//遍历数组至undefined第1次出现时停止
          allDatasNext = i == 0 ? this.allDatas : allDatasNext[siteArray[i-1]];
          if(!siteArray[i]) {
            var array = [];
            for (var key in allDatasNext) {
              array.push(key)
            }
            this[optionsArray[i]] = array;
            break;
          }
        }
        if(this.singleProvince) address += this.singleProvince;
        if(this.singleCity) address += '-' + this.singleCity;
        if(this.singleCounty) address += '-' + this.singleCounty;
        if(this.singleTown) address += '-' + this.singleTown;
        if(this.singleVillage) address += '-' + this.singleVillage;
        this.address = address;
      }
    },
  });
  new Vue({
    el: '#newVersionComponent',
    data(){
      return {
        allDatas : makeallDatas()
      }
    },
    methods:{
    }
  })
  //以下是新版本(普通)JS
  var allDatas = makeallDatas();
  var vm = new Vue({
    el: '#newVersionCommon',
    data: {
      allDatas: allDatas,
      allProvinces: [],
      singleProvince: '',
      allCitys: [],
      singleCity: '',
      allCountys: [],
      singleCounty: '',
      allTowns: [],
      singleTown: '',
      allVillages: [],
      singleVillage: '',
      address: '',
    },
    beforeMount: function () {
      this.selectName()
    },
    methods: {
      selectName: function(singleProvince,singleCity,singleCounty,singleTown,singleVillage){
        var siteArray = [singleProvince,singleCity,singleCounty,singleTown,singleVillage];
        var modelArray = ['singleProvince','singleCity','singleCounty','singleTown','singleVillage'];
        var optionsArray = ['allProvinces','allCitys','allCountys','allTowns','allVillages'];
        var allDatasNext;
        var address = "你选择的地址是:";
        for(var i=0;i<siteArray.length;i++){//遍历数组所有项
          if(!siteArray[i]) this[modelArray[i]] = '';
        }
        for(var i=0;i<siteArray.length;i++){//遍历数组至undefined第1次出现时停止
          allDatasNext = i == 0 ? this.allDatas : allDatasNext[siteArray[i-1]];
          if(!siteArray[i]) {
            var array = [];
            for (var key in allDatasNext) {
              array.push(key)
            }
            this[optionsArray[i]] = array;
            break;
          }
        }
        if(this.singleProvince) address += this.singleProvince;
        if(this.singleCity) address += '-' + this.singleCity;
        if(this.singleCounty) address += '-' + this.singleCounty;
        if(this.singleTown) address += '-' + this.singleTown;
        if(this.singleVillage) address += '-' + this.singleVillage;
        this.address = address;
      }
    }
  });
  //以下是旧版本(普通)JS
  var allProvinces = makeallDatas();
  var vm = new Vue({
    el: '#oldVersion',
    data: {
      allProvinces: allProvinces,
      singleProvince: '北京市',
      allCitys: {},
      singleCity: '',
      allCountys: {},
      singleCounty: '',
      allTowns: {},
      singleTown: '',
      allVillages: {},
      singleVillage: '',
      address: '',
    },
    beforeMount: function () {
      this.update("allProvinces","singleProvince","allCitys","singleCity")
    },
    methods: {
      update: function (thisall,thisName,nextall,nextName) {
        //1个上级更新,会导致1个下级更新,进而导致1个下下级的更新...
        for (var key in this[thisall]) {
          if (key === this[thisName]) {
            this[nextall] = this[thisall][key];
            for (var key in this[nextall]) {
              this[nextName] = key;
              break;
            }
          }
        }
        var address = "你选择的地址是:";
        if(this.singleProvince) address += this.singleProvince;
        if(this.singleCity) address += '-' + this.singleCity;
        if(this.singleCounty) address += '-' + this.singleCounty;
        if(this.singleTown) address += '-' + this.singleTown;
        if(this.singleVillage) address += '-' + this.singleVillage;
        this.address = address;
      }
    },
    watch: {
      singleProvince: function () {
        this.update("allProvinces","singleProvince","allCitys","singleCity")
      },
      singleCity: function () {
        this.update("allCitys","singleCity","allCountys","singleCounty") 
      },
      singleCounty: function () {
        this.update("allCountys","singleCounty","allTowns","singleTown") 
      },
      singleTown: function () {
        this.update("allTowns","singleTown","allVillages","singleVillage")
      }
    }
  })
  //以下是年月日分别选择(普通)JS
  var vm = new Vue({
    el: '#yearMonthDate',
    data: function(){
      return {
        yearMonthDate: {
          year:1900,
          month:1,
          day:1,
        },
        years: [{
          key: 1900,
          value: 1900,
        }],
        months: [{
          key: 1,
          value: 1,
        }],
        days: [{
          key: 1,
          value: 1,
        }],
        address: '',
      }
    },
    beforeMount: function () {
      //this.selectName()
    },
    methods: {
      clickYear: function(){
        this.years.length = 0;
        var thisYear = new Date().getFullYear();
        for(var i = 1900; i <= thisYear; i++){
          this.years.push({
            key: i,
            value: i,
          })
        }
      },
      changeYear: function(){
        this.yearMonthDate.month = 1
        this.yearMonthDate.day = 1
      },
      clickMonth: function(){
        this.months.length = 0;
        for(var i = 1; i <= 12; i++){
          this.months.push({
            key: i,
            value: i,
          })
        }
        this.yearMonthDate.day = 1
      },
      changeMonth: function(){
        this.yearMonthDate.day = 1
      },
      clickDay: function(){
        this.days.length = 0;
        var dayNum = 0;
        var longMonth = [1,3,5,7,8,10,12];
        var shortMonth = [4,6,9,11];
        if (longMonth.indexOf(this.yearMonthDate.month) > -1){
          dayNum = 31;
        }else if (shortMonth.indexOf(yearMonthDate.month) > -1){
          dayNum = 30;
        }else if ( this.yearMonthDate.month === 2){//1900年及以后的闰年
          if(this.yearMonthDate.year != 1900 && this.yearMonthDate.year % 4 === 0){
            dayNum = 29;
          }else{
            dayNum = 28;
          }
        }
        for(var i = 1; i <= dayNum; i++){
          this.days.push({
            key: i,
            value: i,
          })
        }
      }
    }
  });
  //以下是所有版本JS
  function makeallDatas() {
    var allDatas = {
      "北京市": {
        "区": {
          "通州区": {
            "中仓街道办事处": {
              "滨河社区居委会": "110112001029",
              "运河湾社区居委会": "110112001030"
            },
            "漷县镇": {
              "后元化村委会": "110112106260",
              "前元化村委会": "110112106261"
            }
          },
          "昌平区": {
            "天通苑南街道办事处":{
              "东辰社区居委会":"110114009001",
              "佳运园社区居委会":"110114009002",
              "天通苑第二社区居委会":"110114009003",
              "天通西苑第一社区居委会":"110114009004",
              "天通东苑第一社区居委会":"110114009005",
              "天通东苑第二社区居委会":"110114009006",
              "天通苑第一社区居委会":"110114009007",
              "嘉诚花园社区居委会":"110114009008",
              "清水园社区居委会":"110114009009",
              "北方明珠社区居委会":"110114009010",
              "天通东苑第四社区居委会":"110114009011",
              "顶秀清溪社区居委会":"110114009012",
              "奥北中心社区居委会":"110114009013",
              "陈营村委会":"110114009201"
            },
            "霍营街道办事处": {
              "华龙苑南里社区居委会": "110114010001",
              "华龙苑北里社区居委会": "110114010002",
              "蓝天园社区居委会": "110114010003",
              "天鑫家园社区居委会": "110114010004",
              "霍营小区社区居委会": "110114010005",
              "上坡佳园社区居委会": "110114010006",
              "华龙苑中里社区居委会": "110114010007",
              "流星花园社区居委会": "110114010008",
              "龙回苑社区居委会": "110114010009",
              "和谐家园社区居委会": "110114010010",
              "田园风光雅苑社区居委会": "110114010011",
              "龙锦苑一区社区居委会": "110114010012",
              "龙锦苑东一区社区居委会": "110114010013",
              "龙锦苑东二区社区居委会": "110114010014",
              "龙锦苑东五区社区居委会": "110114010015",
              "龙锦苑东三区社区居委会": "110114010016",
              "龙锦苑东四区社区居委会": "110114010017",
              "紫金新干线社区居委会": "110114010018",
              "霍家营村委会": "110114010201"
            }
          }
        },
        "县": {
          "密云县": {
            "密云镇": {
              "小唐庄社区居委会": "110228100001",
              "李各庄社区居委会": "110228100002",
              "大唐庄社区居委会": "110228100003",
              "季庄村委会": "110228100205",
            },
            "溪翁庄镇": {
              "东智北村委会": "110228101209",
              "石墙沟村委会": "110228101210",
              "黑山寺村委会": "110228101211",
              "立新庄村委会": "110228101212",
            },
          },
          "延庆县": {
            "旧县镇": {
              "常里营村委会": "110229104215",
              "盆窑村委会": "110229104216",
              "团山村委会": "110229104217",
              "大柏老村委会": "110229104218",
            },
            "珍珠泉乡": {
              "双金草村委会": "110229214209",
              "小川村委会": "110229214210",
              "小铺村委会": "110229214211",
              "仓米道村委会": "110229214212",
            }
          }
        }
      },
      "河南省": {
        "郑州市": {
          "金水区": {
            "凤凰台街道办事处": {
              "凤凰台社区居民委员会": "410105013004",
              "王庄社区居民委员会": "410105013005",
              "张庄社区居民委员会": "410105013006",
              "凤凰城社区居民委员会": "410105013007",
            },
            "金光路街道办事处": {
              "徐庄村委会": "410105564201",
              "贾陈村委会": "410105564202",
              "柳园口村委会": "410105564203",
              "马楼村委会": "410105564204",
            }
          },
          "登封市": {
            "嵩阳街道办事处": {
              "玉溪路居委会": "410185001014",
              "苹果园居委会": "410185001015",
              "颖河路居委会": "410185001016",
              "守敬路居委会": "410185001017",
            },
            "少林街道办事处": {
              "塔沟居委会": "410185002001",
              "少林居委会": "410185002002",
              "耿庄村委会": "410185002203",
              "王庄村委会": "410185002204",
            },
            "送表矿区": {
              "东送表村委会": "410185400201",
              "梁庄村委会": "410185400202",
              "安庄村委会": "410185400203",
              "刘楼村委会": "410185400204",
            }
          }
        },
        "信阳市": {
          "浉河区": {
            "老城街道办事处": {
              "义阳居委会": "411502001001",
              "三里店居委会": "411502001003",
              "东方红居委会": "411502001004",
              "浉河居委会": "411502001007",
            },
            "民权街道办事处": {
              "民权居委会": "411502002001",
              "新生居委会": "411502002002",
              "成功居委会": "411502002003",
              "白果树居委会": "411502002004",
            },
            "车站街道办事处": {
              "工区东居委会": "411502003001",
              "工区路居委会": "411502003002",
              "六里棚居委会": "411502003003",
              "新公房居委会": "411502003004",
            }
          },
          "固始县": {
            "段集镇": {
              "街道居委会": "411525109001",
              "段集村委会": "411525109201",
              "棠树岗村委会": "411525109202",
              "青峰村委会": "411525109203",
              "下楼村委会": "411525109204",
              "桂岭村委会": "411525109205",
              "窑沟村委会": "411525109206",
              "五尖山村委会": "411525109207",
              "柳林村委会": "411525109208",
              "齐山村委会": "411525109209",
              "钓鱼台村委会": "411525109210",
              "蒋营村委会": "411525109211",
              "庙山村委会": "411525109212",
              "汪旱庄村委会": "411525109213",
              "乐道村委会": "411525109214",
              "姚老家村委会": "411525109215",
              "赵营村委会": "411525109216",
              "童庙村委会": "411525109217",
              "高庙村委会": "411525109218"
            },
            "武庙集镇": {
              "街道居委会": "411525113001",
              "汪小庄村委会": "411525113201",
              "黄土岭村委会": "411525113202",
              "平阳村委会": "411525113203",
              "长江河村委会": "411525113204",
              "锁口村委会": "411525113205",
              "皮冲村委会": "411525113206",
              "刘中楼村委会": "411525113207",
              "迎水寺村委会": "411525113208",
              "余楼村委会": "411525113209",
              "钱老楼村委会": "411525113210",
              "新店村委会": "411525113211",
              "徐小店村委会": "411525113212",
              "邓岭村委会": "411525113213",
              "太平村委会": "411525113214",
              "汪楼村委会": "411525113215",
              "李瓦房村委会": "411525113216"
            },
            "祖师庙镇": {
              "祖师庙居委会": "411525117001",
              "仰天洼社区居委会": "411525117002",
              "小店村委会": "411525117201",
              "王行村委会": "411525117202",
              "大冲村委会": "411525117203",
              "松林村委会": "411525117204",
              "刘楼村委会": "411525117205",
              "羁马村委会": "411525117206",
              "黄楼村委会": "411525117207",
              "彭畈村委会": "411525117208",
              "童圩村委会": "411525117209",
              "仓房村委会": "411525117210",
              "毛店村委会": "411525117211",
              "万岗村委会": "411525117212",
              "七冲村委会": "411525117213",
              "三区村委会": "411525117214",
              "杨楼村委会": "411525117215"
            }
          },
          "息县": {
            "杨店乡": {
              "杨店村委会": "411528204200",
              "安寨村委会": "411528204201",
              "何庄村委会": "411528204202",
              "李大庄村委会": "411528204203",
            },
            "张陶乡": {
              "张陶村委会": "411528205200",
              "曹林村委会": "411528205201",
              "陈圈行村委会": "411528205202",
              "大陈庄村委会": "411528205203",
            },
            "白土店乡": {
              "白土店村委会": "411528206200",
              "白衣阁村委会": "411528206201",
              "大江庄村委会": "411528206202",
              "桂庄村委会": "411528206203",
            },
            "岗李店乡": {
              "岗李店村委会": "411528207200",
              "大彭庄村委会": "411528207201",
              "方老庄村委会": "411528207202",
              "贾后寨村委会": "411528207203",
            }
          }
        },
      },
    };
    return allDatas;
  }
</script>
</html>

三、simple-dialog三层弹窗组件
1、不可拖拽(含插槽)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Vue多层弹窗</title>
    <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
    <style>
      .simpleDialog {
        position: fixed;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        display: flex;
        justify-content: center;
        align-items: center;
      }
      .simpleDialog .mask {
        position: fixed;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        background: black;
        opacity: 0.5;
      }
      .simpleDialog .content {
        position: fixed;
        background: white;
        opacity: 1;
        display: flex;
        flex-direction: column;
      }
      .simpleDialog .content .title {
        display: flex;
        background: blue;
        color: white;
        padding: 10px;
        cursor: pointer;
      }
      .simpleDialog .content .conform {
        display: flex;
        justify-content: center;
        padding: 10px;
        background: blue;
      }
    </style>
  </head>
  <body>
    <div id="el">
      <button @click="clickButton()" style="margin-top: 30px;">
        点击-出现-弹窗
      </button>
      <simple-dialog :required-data="requiredDataOut">
        插槽一
        <simple-dialog :required-data="requiredDataMid">
          插槽二
          <simple-dialog :required-data="requiredDataIn">
            插槽三
          </simple-dialog>
        </simple-dialog>
      </simple-dialog>
    </div>
    <script>
      Vue.component('simple-dialog', {
        template: `
          <div>
            <div class="simpleDialog" v-show="requiredData.isShow">
              <div class="mask" v-show="requiredData.isShow"></div>
              <div class="content" v-show="requiredData.isShow">
                <div class="title">
                  <span>系统消息</span>
                </div>
                <div :style="{width:requiredData.width||'800px',height:requiredData.height||'400px'}">
                  <slot></slot>
                </div>
                <div class="conform">
                  <button v-on:click="close()">关闭</button>
                  <button v-on:click="open()">打开</button>
                </div>
              <div>
            </div>
          </div>
        `,
        props: {      
          requiredData: {
            type: Object
          }
        },
        data: function() {
          return {}
        },
        methods: {
          close: function () {
            this.requiredData.isShow = false;
            if(this.requiredData.closeFn) this.requiredData.closeFn();
          },
          open: function () {
            if(this.requiredData.openFn) this.requiredData.openFn();
          }                              
        }
      });
      new Vue({
        el: '#el',
        data(){
          var that = this;
          return {
            requiredDataOut : {
              isShow : false,
              width : '900px',
              height : '600px',
              openFn : function () {
                that.requiredDataMid.isShow = true;
              }
            },
            requiredDataMid : {
              isShow : false,
              width : '600px',
              height : '400px',
              openFn : function () {
                that.requiredDataIn.isShow = true;
              },
            },
            requiredDataIn : {
              isShow : false,
              width : '300px',
              height : '200px',
            },
            // 一层弹窗可以如下使用
            // requiredDataOut = {
            //   isShow : false,
            // };
            // clickButton() {
            //   requiredDataOut.isShow = true;
            // };
          }
        },
        methods:{
          clickButton() {
            this.requiredDataOut.isShow = true;
          }
        }
      })
    </script>
  </body>
</html>
2、可拖拽(含插槽)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Vue多层弹窗</title>
    <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
    <script>
      function drag(wholeTitleId, wholeContentId) {
        var wholeTitleId = wholeTitleId||'titleId';
        var wholeContentId = wholeContentId||'contentId';
        var oDiv = document.getElementById(wholeContentId);
        if(!oDiv) return;
        oDiv.onmousedown = down;
        function processThis(fn, nowThis) {
          return function (event) {
            fn.call(nowThis, event);
          };
        }
        function down(event) {
          event = event || window.event;
          if (event.target.id != wholeTitleId) return;
          this.initOffsetLeft = this.offsetLeft;
          this.initOffsetTop = this.offsetTop;
          this.initClientX = event.clientX;
          this.initClientY = event.clientY;
          this.maxOffsetWidth =
            (document.documentElement.clientWidth || document.body.clientWidth) -
            this.offsetWidth;
          this.maxOffsetHeight =
            (document.documentElement.clientHeight ||
              document.body.clientHeight) - this.offsetHeight;
          if (this.setCapture) {
            this.setCapture();
            this.onmousemove = processThis(move, this);
            this.onmouseup = processThis(up, this);
          } else {
            document.onmousemove = processThis(move, this);
            document.onmouseup = processThis(up, this);
          }
        }
        function move(event) {
          var nowLeft = this.initOffsetLeft + (event.clientX - this.initClientX);
          var nowTop = this.initOffsetTop + (event.clientY - this.initClientY);
          this.style.left = nowLeft + 'px';
          this.style.top = nowTop + 'px';
        }
        function up() {
          if (this.releaseCapture) {
            this.releaseCapture();
            this.onmousemove = null;
            this.onmouseup = null;
          } else {
            document.onmousemove = null;
            document.onmouseup = null;
          }
        }
      };
    </script>
    <style>
      .simpleDialog {
        position: fixed;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        display: flex;
        justify-content: center;
        align-items: center;
      }
      .simpleDialog .mask {
        position: fixed;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        background: black;
        opacity: 0.5;
      }
      .simpleDialog .content {
        position: fixed;
        background: white;
        opacity: 1;
        display: flex;
        flex-direction: column;
      }
      .simpleDialog .content .title {
        display: flex;
        background: blue;
        color: white;
        padding: 10px;
        cursor: pointer;
      }
      .simpleDialog .content .conform {
        display: flex;
        justify-content: center;
        padding: 10px;
        background: blue;
      }
    </style>
  </head>
  <body>
    <div id="el">
      <button @click="clickButton()" style="margin-top: 30px;">
        点击-出现-弹窗
      </button>
      <simple-dialog :required-data="requiredDataOut">
        插槽一
        <simple-dialog :required-data="requiredDataMid">
          插槽二
          <simple-dialog :required-data="requiredDataIn">
            插槽三
          </simple-dialog>
        </simple-dialog>
      </simple-dialog>
    </div>
    <script>
      Vue.component('simple-dialog', {
        template: `
          <div>
            <div class="simpleDialog" v-show="requiredData.isShow">
              <div class="mask" v-show="requiredData.isShow"></div>
              <div class="content" v-show="requiredData.isShow" :id="requiredData.contentId||'contentId'">
                <div class="title" :id="requiredData.titleId||'titleId'">
                  <span>系统消息</span>
                </div>
                <div :style="{width:requiredData.width||'800px',height:requiredData.height||'400px'}">
                  <slot></slot>
                </div>
                <div class="conform">
                  <button v-on:click="close()">关闭</button>
                  <button v-on:click="open()">打开</button>
                </div>
              <div>
            </div>
          </div>
        `,
        props: {      
          requiredData: {
            type: Object
          }
        },
        data: function() {
          return {}
        },
        methods: {
          close: function () {
            this.requiredData.isShow = false;
            if(this.requiredData.closeFn) this.requiredData.closeFn();
            var content = this.requiredData.contentId;
            document.getElementById(content).style.cssText = "position:fixed;display:flex;";
          },
          open: function () {
            if(this.requiredData.openFn) this.requiredData.openFn();
          }                              
        },
      });
      new Vue({
        el: '#el',
        data(){
          var that = this;
          return {
            requiredDataOut : {
              isShow : false,
              width : '900px',
              height : '600px',
              titleId : "titleId1",
              contentId : "contentId1",
              openFn : function () {
                that.requiredDataMid.isShow = true;
                drag(that.requiredDataMid.titleId,that.requiredDataMid.contentId);
              }
            },
            requiredDataMid : {
              isShow : false,
              width : '600px',
              height : '400px',
              titleId : "titleId2",
              contentId : "contentId2",
              openFn : function () {
                that.requiredDataIn.isShow = true;
                drag(that.requiredDataIn.titleId,that.requiredDataIn.contentId);
              },
            },
            requiredDataIn : {
              isShow : false,
              width : '300px',
              height : '200px',
              titleId : "titleId3",
              contentId : "contentId3",
            },
            // 一层弹窗可以如下使用
            // requiredDataOut = {
            //   isShow : false,
            // };
            // clickButton() {
            //   requiredDataOut.isShow = true;
            // };
          }
        },
        methods:{
          clickButton() {
            this.requiredDataOut.isShow = true;
            drag(this.requiredDataOut.titleId,this.requiredDataOut.contentId);
          }
        }
      })
    </script>
  </body>
</html>
 
四、el-dialog三层弹窗
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>vue2.6.10组件之el-dialog多层弹窗</title>
  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script src="https://cdn.bootcss.com/element-ui/2.10.1/index.js"></script>
  <link href="https://cdn.bootcss.com/element-ui/2.10.1/theme-chalk/index.css" rel="stylesheet">
  <style>
    #app{
      display: flex;
      justify-content: space-between;
    }
  </style>
</head>
<body>
<div id="app">
  <el-button type="text" @click="outerVisible = true">点击打开外层弹窗</el-button>
  <el-dialog
    width="70%"
    title="外层"
    :visible.sync="outerVisible"
  > 
    以下是外层弹窗的插槽<br/>
    <div style="height:200px;border:1px solid #ccc;padding:20px;">
      width="70%",决定弹窗的宽<br/>
      style="height:200px",通过给弹窗插槽的部分标签设置高,决定弹窗的高,如此框<br/>
      :visible.sync="middleVisible",传递引用,此处改变,别处也改变<br/>
      :visible="middleVisible",传递普通值,此处改变,别处不改变<br/>
      <el-dialog
        width="50%"
        title="中层"
        :visible.sync="middleVisible"
        append-to-body
      >
        以下是中层弹窗的插槽<br/>
        <el-dialog
          width="30%"
          title="内层"
          :visible.sync="innerVisible"
          append-to-body
        >
          以下是内层弹窗的插槽<br/>
          <div slot="footer" class="dialog-footer">
            <el-button @click="innerVisible = false">关闭内层</el-button>
            <el-button type="primary" @click="innerVisible = false">关闭内层</el-button>
          </div>
          以上是内层弹窗的插槽<br/>
        </el-dialog>
        <div slot="footer" class="dialog-footer">
          <el-button @click="middleVisible = false">关闭中层</el-button>
          <el-button type="primary" @click="innerVisible = true">打开内层</el-button>
        </div>
        以上是中层弹窗的插槽<br/>
      </el-dialog>
    </div>
    <div slot="footer" class="dialog-footer">
      <el-button @click="outerVisible = false">关闭外层</el-button>
      <el-button type="primary" @click="middleVisible = true">打开中层</el-button>
    </div>
    以上是外层弹窗的插槽(代码中,此句位于按钮下面)<br/>
  </el-dialog>
</div>
</body>
<script>
  new Vue({
    el: '#app',
    data() {
      return {
        outerVisible: false,
        middleVisible: false,
        innerVisible: false
      };
    },
    methods: {
       
    },
    components: {
       
    },
  })
</script>
</html>
附、elementUI各弹窗的区别
1、第1组(非悬停时消失)
(1)Tooltip 文字提示,常用于展示鼠标hover时的提示信息。
(2)Popover 弹出框,与Tooltip类似。
2、第2组(3秒钟后自动消失)
(1)Message 消息提示,常用于主动操作后的反馈提示。
(2)Notification 通知,常用于系统级通知的被动提醒。
3、第3组(点击确认后消失)
(1)MessageBox 弹窗,模拟系统的消息提示框alert、confirm和prompt而实现的一套模态对话框组件,用于消息提示、确认消息和提交内容。
(2)Dialog 对话框,弹出较为复杂的内容.

五、ElementUI之70个组件
/*
组件的四个配置
<div ref="btn" @click="clickButton()">我是一个按钮</div>
(1)属性名:
(2)事件名:@click,用户-点击-时-触发
(3)方法名:click,使-点击事件-触发,this.$refs.btn.click()
(4)插槽名:
*/
/*
1、Basic 基础组件
(1)Button 按钮
(2)Border 边框
(3)Color 色彩
(4)Container 布局容器
(5)Icon 图标
(6)Layout 布局
(7)Link 链接
(8)Scrollbar 滚动条
(9)Space 间距
(10)Typography 排版
2、配置组件
(1)Config Provider 全局配置
3、Form 表单组件
(1)Autocomplete 自动补全输入框
(2)Cascader 级联选择器
(3)Checkbox 多选框
(4)ColorPicker 取色器(颜色选择器)
(5)DatePicker 日期选择器
(6)DateTimePicker 日期时间选择器
(7)Form 表单
(8)Input 输入框
(9)Input Number 数字输入框
(10)Radio 单选框
(11)Rate 评分
(12)Select 选择器
(13)Select V2 虚拟列表选择器
(14)Slider 滑块
(15)Switch 开关
(16)TimePicker 时间选择器
(17)TimeSelect 时间选择
(18)Transfer 穿梭框
(19)Upload 上传
4、Data 数据展示
(1)Avatar 头像
(2)Badge 徽章
(3)Calendar 日历
(4)Card 卡片
(5)Carousel 走马灯
(6)Collapse 折叠面板
(7)Descriptions 描述列表
(8)Empty 空状态
(9)Image 图片
(10)Infinite Scroll 无限滚动
(11)Pagination 分页
(12)Progress 进度条
(13)Result 结果
(14)Skeleton 骨架屏
(15)Table 表格
(16)Virtualized Table 虚拟化表格
(17)Tag 标签
(18)Timeline 时间线
(19)Tree 树形控件
(20)TreeSelect 树形选择
(21)Tree V2 虚拟化树形控件
5、Navigation 导航
(1)Affix 固钉
(2)Backtop 回到顶部
(3)Breadcrumb 面包屑
(4)Dropdown 下拉菜单
(5)Menu 菜单
(6)Page Header 页头
(7)Steps 步骤条
(8)Tabs 标签页
6、Feedback 反馈组件
(1)Alert 提示
(2)Dialog 对话框
(3)Drawer 抽屉
(4)Loading 加载
(5)Message 消息提示
(6)MessageBox 消息弹框
(7)Notification 通知
(8)Popconfirm 气泡确认框
(9)Popover 弹出框(气泡卡片)
(10)Tooltip 文字提示
7、Others 其他
(1)Divider 分割线
*/
1、Basic 基础组件
(1)Button 按钮
  <el-button type="primary">Primary</el-button>
(2)Border 边框
  <template>
    <table class="demo-border">
      <tbody>
        <tr>
          <td class="text">Thickness</td>
          <td class="line">Demo</td>
        </tr>
      </tbody>
    </table>
  </template>
  <style scoped>
  :root {
    --green:green;
  }
  .demo-border .line div {
    width: 100%;
    height: 0;
    border-top: 1px solid var(--green);
  }
  </style>
(3)Color 色彩
(4)Container 布局容器
  <template>
    <div class="common-layout">
      <el-container>
        <el-aside width="200px">Aside</el-aside>
        <el-container>
          <el-header>Header</el-header>
          <el-main>Main</el-main>
        </el-container>
      </el-container>
    </div>
  </template>
(5)Icon 图标
  <el-icon>
    <Delete />
  </el-icon>
(6)Layout 布局
  <el-row>
    <el-col :span="12"><div class="grid-content ep-bg-purple" /></el-col>
    <el-col :span="12"><div class="grid-content ep-bg-purple-light" /></el-col>
  </el-row>
(7)Link 链接
  <el-link href="https://element.eleme.io" target="_blank">default</el-link>
(8)Scrollbar 滚动条
  通过height属性设置滚动条高度,若不设置则根据父容器高度自适应;当元素宽度大于滚动条宽度时,会显示横向滚动条
  <el-scrollbar height="400px">
    <p v-for="item in 20" :key="item" class="scrollbar-demo-item">{{ item }}</p>
  </el-scrollbar>
(9)Space 间距
  <el-space wrap>
    <el-card v-for="i in 3" :key="i" class="box-card" style="width: 250px">
      <template #header>
        <div class="card-header">
          <span>Card name</span>
          <el-button class="button" text>Operation button</el-button>
        </div>
      </template>
      <div v-for="o in 4" :key="o" class="text item">
        {{ 'List item ' + o }}
      </div>
    </el-card>
  </el-space>
(10)Typography 排版
  <script lang="ts" setup>
    import { isDark } from '~/composables/dark'
  </script>
  <template>
    <div v-if="!isDark" class="demo-term-box">
      <img src="/images/typography/term-helvetica.png" alt="" />
      <img src="/images/typography/term-arial.png" alt="" />
    </div>
    <div v-else class="demo-term-box">
      <img src="/images/typography/term-helvetica-dark.png" alt="" />
      <img src="/images/typography/term-arial-dark.png" alt="" />
    </div>
  </template>
2、配置组件
(1)Config Provider 全局配置
A、i18n 配置
  <template>
  <div>
    <el-button mb-2 @click="toggle">Switch Language</el-button><br />
    <el-config-provider :locale="locale">
      <el-table mb-1 :data="[]" />
      <el-pagination :total="100" />
    </el-config-provider>
  </div>
  </template>
  <script lang="ts" setup>
    import { computed, ref } from 'vue'
    import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
    import en from 'element-plus/dist/locale/en.mjs'
    const language = ref('zh-cn')
    const locale = computed(() => (language.value === 'zh-cn' ? zhCn : en))
    const toggle = () => {
      language.value = language.value === 'zh-cn' ? 'en' : 'zh-cn'
    }
  </script>
B、对按钮进行配置
  <template>
    <div>
      <div m="b-2">
        <el-checkbox v-model="config.autoInsertSpace">autoInsertSpace</el-checkbox>
      </div>
      <el-config-provider :button="config">
        <el-button>中文</el-button>
      </el-config-provider>
    </div>
  </template>
  <script lang="ts" setup>
  import { reactive } from 'vue'
  const config = reactive({
    autoInsertSpace: true,
  })
  </script>
C、对消息进行配置
  <template>
    <div>
      <el-config-provider :message="config">
        <el-button @click="open">OPEN</el-button>
      </el-config-provider>
    </div>
  </template>
  <script lang="ts" setup>
    import { reactive } from 'vue'
    import { ElMessage } from 'element-plus'
    const config = reactive({
      max: 3,
    })
    const open = () => {
      ElMessage('This is a message.')
    }
  </script>
3、Form 表单组件
(1)Autocomplete 自动补全输入框
  <el-autocomplete
    v-model="state1"
    :fetch-suggestions="querySearch"
    clearable
    class="inline-input w-50"
    placeholder="Please Input"
    @select="handleSelect"
  />
(2)Cascader 级联选择器
  注意,级联选择器、树形控件、树形选择的区别
  <el-cascader
    v-model="value"
    :options="options"
    :props="props"
    @change="handleChange"
  />
(3)Checkbox 多选框
  <div>
    <el-checkbox v-model="checked1" label="Option 1" size="large" />
    <el-checkbox v-model="checked2" label="Option 2" size="large" />
  </div>
(4)ColorPicker 取色器(颜色选择器)
  <div class="demo-color-block">
    <span class="demonstration">With default value</span>
    <el-color-picker v-model="color1" />
  </div>
(5)DatePicker 日期选择器
  <el-date-picker
    v-model="value1"
    type="date"
    placeholder="Pick a day"
    :size="size"
  />
(6)DateTimePicker 日期时间选择器
  <el-date-picker
    v-model="datetimeValue"
    type="datetimerange"
    range-separator="To"
    start-placeholder="Start date"
    end-placeholder="End date"
  />
  import { ref } from 'vue'
  const datetimeValue = ref<[Date, Date]>([
    new Date(2000, 10, 10, 10, 10),
    new Date(2000, 10, 11, 10, 10),
  ])
  /* 1、format 日期格式
      format="YYYY/MM/DD HH:mm:ss" //客户端24小时补0显示,
      format="YYYY/MM/DD hh:mm:ss a" //客户端12小时补0显示
      value-format="YYYY-MM-DD HH:mm:ss" //服务端24小时补0显示
      value-format="YYYY-MM-DD hh:mm:ss a" //服务端12小时补0显示,取值"x"为毫秒
      H,大写为24小时,小写为12小时
      时间字母,双写为补0,单写为不补0
      a,为am;A,为AM
    2、type 日期类型(year/month/date/datetime/ week/datetimerange/daterange)
      type="date" //日期
      type="datetime" //日期时间
      type="datetimerange" //日期时间范围
    3、range-separator="To" //范围的分隔符
    4、default-value 选择器打开时,默认显示的时间
    5、default-time 选择日期后,默认显示的时间,未指定时,默认为00:00:00
    6、size 输入框尺寸 large/default/small
    7、placeholder 非范围选择时的占位内容
    8、start-placeholder 范围选择时开始日期的占位内容
    9、end-placeholder 范围选择时结束日期的占位内容
  */
(7)Form 表单
  <el-form :model="form" label-width="120px">
    <el-form-item label="Activity name">
      <el-input v-model="form.name" />
    </el-form-item>
  </el-form>
(8)Input 输入框
  <el-input v-model="input" placeholder="Please input" />
(9)Input Number 数字输入框
  <el-input-number v-model="num" :min="1" :max="10" @change="handleChange" />
(10)Radio 单选框
  <el-radio-group v-model="radio1" class="ml-4">
    <el-radio label="1" size="large">Option 1</el-radio>
    <el-radio label="2" size="large">Option 2</el-radio>
  </el-radio-group>
(11)Rate 评分
  <div class="demo-rate-block">
    <span class="demonstration">Default</span>
    <el-rate v-model="value1" />
  </div>
(12)Select 选择器
  <el-select v-model="value" class="m-2" placeholder="Select" size="large">
    <el-option
      v-for="item in options"
      :key="item.value"
      :label="item.label"
      :value="item.value"
    />
  </el-select>
(13)Select V2 虚拟列表选择器
  <el-select-v2
    v-model="value"
    :options="options"
    placeholder="Please select"
    size="large"
  />
(14)Slider 滑块
  <div class="slider-demo-block">
    <span class="demonstration">Default value</span>
    <el-slider v-model="value1" />
  </div>
(15)Switch 开关
  <el-switch
    v-model="value2"
    class="ml-2"
    style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
  />
(16)TimePicker 时间选择器
  <el-time-picker
    v-model="value2"
    arrow-control
    placeholder="Arbitrary time"
  />
(17)TimeSelect 时间选择
  <el-time-select
    v-model="value"
    start="08:30"
    step="00:15"
    end="18:30"
    placeholder="Select time"
  />
(18)Transfer 穿梭框
  <el-transfer v-model="value" :data="data" />
(19)Upload 上传
  <el-upload
    v-model:file-list="fileList"
    class="upload-demo"
    action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
    multiple
    :on-preview="handlePreview"
    :on-remove="handleRemove"
    :before-remove="beforeRemove"
    :limit="3"
    :on-exceed="handleExceed"
    :http-request="httpRequest"
  >
    <el-button type="primary">Click to upload</el-button>
    <template #tip>
      <div class="el-upload__tip">
        jpg/png files with a size less than 500KB.
      </div>
    </template>
  </el-upload>
  /*
    1、on-preview:点击文件列表中已上传的文件时的钩子
    2、on-remove:文件列表移除文件时的钩子
    3、on-success:文件上传成功时的钩子
    4、on-error:文件上传失败时的钩子
    5、on-progress:文件上传时的钩子
    6、on-change:文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
    7、on-exceed:当超出限制时,执行的钩子函数
    8、before-upload:上传文件之前的钩子,参数为上传的文件,若返回false或者返回Promise且被reject,则停止上传
    9、before-remove:删除文件之前的钩子,参数为上传的文件和文件列表,若返回false或者返回Promise且被reject,则停止删除
    10、http-request:覆盖默认的Xhr行为,允许自行实现上传文件的请求,如切片。
  */
  data() {
    return {
      fileList: [],
      chunkSize: 1024 * 1024 * 6, // 6M 为一个切片,超过6M,则采用上传大文件的方法进行文件上传
      percentage: {}, // 进度条
    }
  },
  httpRequest(params) { // 来源:
    let {file: {size}} = params;
    size > this.chunkSize ? this.uploadBigFile(params, this.chunkSize) : this.uploadFile(params)
  },
  uploadBigFile(params, chunkSize) {
    let {file, filename, onSuccess} = params;
    let {size, type, name} = file;
    const chunkLength = Math.ceil(file.size / chunkSize)
    let chunks = Array.from({length: chunkLength}).map((v, i) => file.slice(i * chunkSize, i * chunkSize + chunkSize))
    let loadeds = [];
    let chunkRequests = chunks.map((chunk, i) => {
      const formData = new FormData();
      formData.append(filename, chunk);
      loadeds[i] = [];
      const config = {
        'Content-type': 'multipart/form-data',
        onUploadProgress: progress => {
          loadeds[i].push(progress.loaded)
          this.calculationPercentage(file, i, loadeds, size);
        }
      }
      return this.$post(this.$servers.openServer, 'tb/minio/upload', formData, config);
    })
    this.$axios.all(chunkRequests).then(res => {
      let fileNames = res.map(({data: {minioPath}}) => minioPath)
      const params = {fileName: name, contentType: type, fileSize: size, fileNames}
      this.$post(this.$servers.openServer, 'tb/minio/merge', params).then(onSuccess)
    })
  },
  uploadFile(params) {
    let {file, filename, onSuccess} = params;
    const formData = new FormData();
    formData.append(filename, file);
    const config = {
      'Content-type': 'multipart/form-data',
      onUploadProgress: progress => this.percentage = Math.floor(progress.loaded / progress.total * 100)
    }
    this.$post(this.$servers.openServer, 'tb/minio/upload', formData, config).then(onSuccess)
  },
4、Data 数据展示
(1)Avatar 头像
  <div v-for="size in sizeList" :key="size" class="block">
    <el-avatar :size="size" :src="circleUrl" />
  </div>
(2)Badge 徽章
  <el-badge :value="12" class="item">
    <el-button>comments</el-button>
  </el-badge>
(3)Calendar 日历
  <el-calendar v-model="value" />
(4)Card 卡片
  <el-card class="box-card">
    <template #header>
      <div class="card-header">
        <span>Card name</span>
        <el-button class="button" text>Operation button</el-button>
      </div>
    </template>
    <div v-for="o in 4" :key="o" class="text item">{{ 'List item ' + o }}</div>
  </el-card>
(5)Carousel 走马灯
  <el-carousel height="150px">
    <el-carousel-item v-for="item in 4" :key="item">
      <h3 class="small justify-center" text="2xl">{{ item }}</h3>
    </el-carousel-item>
  </el-carousel>
(6)Collapse 折叠面板
  <el-collapse v-model="activeNames" @change="handleChange">
    <el-collapse-item title="Consistency" name="1">
      <div>
        Consistent with real life: in line with the process and logic of real
        life, and comply with languages and habits that the users are used to;
      </div>
      <div>
        Consistent within interface: all elements should be consistent, such
        as: design style, icons and texts, position of elements, etc.
      </div>
    </el-collapse-item>
  </el-collapse>
(7)Descriptions 描述列表
  <el-descriptions title="User Info">
    <el-descriptions-item label="Username">kooriookami</el-descriptions-item>
  </el-descriptions>
(8)Empty 空状态
  <el-empty description="description" />
  <el-empty image="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png"/>
(9)Image 图片
  <div v-for="fit in fits" :key="fit" class="block">
    <span class="demonstration">{{ fit }}</span>
    <el-image style="width: 100px; height: 100px" :src="url" :fit="fit" />
  </div>
(10)Infinite Scroll 无限滚动(无限滚动加载)
  <ul
    v-infinite-scroll="load"
    class="list"
    :infinite-scroll-disabled="disabled"
  >
    <li v-for="i in count" :key="i" class="list-item">{{ i }}</li>
  </ul>
  import { computed, ref } from 'vue'
  const count = ref(10)
  const loading = ref(false)
  const noMore = computed(() => count.value >= 20)
  const disabled = computed(() => loading.value || noMore.value) //为false,加载
  //(loading.value || noMore.value)与(!loading.value && !noMore.value)等效,都为false,执行
  const load = () => {
    loading.value = true
    setTimeout(() => {
      count.value += 2
      loading.value = false //为false
    }, 2000)
  }
(11)Pagination 分页
  <el-pagination
    v-model:current-page="currentPage"
    v-model:page-size="pageSize"
    :page-sizes="[100, 200, 300, 400]"
    :small="small"
    :disabled="disabled"
    :background="background"
    layout="total, sizes, prev, pager, next, jumper"
    :total="400"
    @size-change="handleSizeChange"
    @current-change="handleCurrentChange"
  />
  const currentPage = ref(4);
  const pageSize = ref(100);
  const handleSizeChange = (val: number) => {
    console.log(`${val} items per page`)
  }
  const handleCurrentChange = (val: number) => {
    console.log(`current page: ${val}`)
  }
(12)Progress 进度条
  <el-progress :percentage="100" status="success" />
(13)Result 结果
  不同颜色圆形背景下的,对号、错号、感叹号图标
  <el-result
    icon="success"
    title="Success Tip"
    sub-title="Please follow the instructions"
  >
    <template #extra>
      <el-button type="primary">Back</el-button>
    </template>
  </el-result>
(14)Skeleton 骨架屏
  <el-skeleton /> <br />
  <el-skeleton style="--el-skeleton-circle-size: 100px">
    <template #template>
      <el-skeleton-item variant="circle" />
    </template>
  </el-skeleton>
(15)Table 表格
  <el-table
    :key="tableKey" // 改变tablekey,table重新渲染;通过条件显示的table内元素,会出现闪烁的情况,在单个的el-table-column上面进行key值的设置,可以规避闪烁;
    :table-layout="tableLayout" // 表格布局,tableLayout取值为“fixed”或“auto”
    :span-method="arraySpanMethod" // 实现合并行或列
    show-summary // 在表格尾部展示合计行
    :summary-method="getSummaries" // 自定义的合计计算方法
    height="250" // 固定表头,纵向内容过多时,可选择固定表头(表体出现滚动条)
    max-height="250" // 流体高度,此时若表格所需的高度大于最大高度,则会显示一个滚动条
    :data="tableData" 
    highlight-current-row // 选择单行数据时使用色块表示
    :row-class-name="tableRowClassName" // 带状态表格,不同的行设置不同的行颜色
    :default-sort="{ prop: 'date', order: 'descending' }" // 设置默认的排序列和排序顺序--“排序”
    @cell-click="cellHandleclick" // 当某个单元格被点击时会触发该事件,参数为row, column, cell, event
    @row-click="rowHandleclick" // 当某一行被点击时会触发该事件,参数为row, column, event
    @current-change="handleCurrentChange" // 当表格的当前行发生变化的时候会触发该事件,回调参数为currentRow,oldCurrentRow,比如勾选该行后,不向后台发送请求,仅改变行颜色(自悟)
  >
    // el-table-column 嵌套可以实现多级表头
    <el-table-column type="selection" fixed /> // 本栏可勾选,固定
    <el-table-column type="index" :index="indexMethod" /> // 自定义type=index列的行号
    <el-table-column
      sortable // 排序标识;以该列为基准排序--“排序”
      :sort-method="sortMethod" // 排序,优先级高
      :sort-by="sortBy" // 排序,优先级低
      prop="date"
      label="Date"
      column-key="date" // 标识是哪个column的筛选条件
      :filters="[ // 数据过滤的选项
        { text: '2016-05-01', value: '2016-05-01' }, // 过滤出date为'2016-05-01'的数据
        { text: '2016-05-02', value: '2016-05-02' },
        { text: '2016-05-03', value: '2016-05-03' },
        { text: '2016-05-04', value: '2016-05-04' },
      ]"
      :filter-method="filterHandler" // 过滤方法的定义见下方45行
    />
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" show-overflow-tooltip /> // 多余的内容会在hover时,以tooltip的形式显示出来
    <el-table-column type="expand"> // 展开行。正常情况下,以下内容不展开,点击才展开
      <template #default="props">
        <div m="4">
          <p m="t-0 b-2">State: {{ props.row.state }}</p>
          <p m="t-0 b-2">City: {{ props.row.city }}</p>
          <h3>Family</h3>
          <el-table :data="props.row.family" :border="childBorder">
            <el-table-column label="State" prop="state" />
            <el-table-column label="City" prop="city" />
          </el-table>
        </div>
      </template>
    </el-table-column>
    <el-table-column prop="example" label="举例" fixed="right" > // 右侧固定
      <template #header="scope">
        <div>{{scope.column.label}}</div>
      </template>
      <template #default="scope">
        <div>{{scope.row.id}}</div>
      </template>
    </el-table-column>
  </el-table>
  const tableData = [
    {
      date: '2016-05-03',
      name: 'Tom',
      address: 'No. 189, Grove St, Los Angeles',
    },
    {
      date: '2016-05-02',
      name: 'Tom',
      address: 'No. 189, Grove St, Los Angeles',
    }
  ];
  const tableRowClassName = (row,rowIndex) =>
  {
    if (row.readed) {
      return 'warning-row'
    }
    return 'success-row'
  }
  const filterHandler = (value,row,column) =>
  {
    const property = column['property']
    return row[property] === value
  }
  const getSummaries = (param: SummaryMethodProps) => {
    const { columns, data } = param
    const sums: string[] = []
    columns.forEach((column, index) => {
      if (index === 0) {
        sums[index] = 'Total Cost'
        return
      }
      const values = data.map((item) => Number(item[column.property]))
      if (!values.every((value) => Number.isNaN(value))) {
        // 在模板字符串中,用`<div> ${ /* 注释内容 */'' } </div>`加注释,常用在html中
        // 在模板字符串中,用`字符串 ${变量}`把“字符串和变量拼接起来”,常用在js中
        sums[index] = `$ ${values.reduce((prev, curr) => {
          const value = Number(curr)
          if (!Number.isNaN(value)) {
            return prev + curr
          } else {
            return prev
          }
        }, 0)}`
      } else {
        sums[index] = 'N/A'
      }
    })
    return sums
  }
(16)Virtualized Table 虚拟化表格
  <template>
    <el-table-v2
      :columns="columns"
      :data="data"
      :width="700"
      :height="400"
      fixed
    />
  </template>
  <script lang="ts" setup>
    const generateColumns = (length = 10, prefix = 'column-', props?: any) =>
      Array.from({ length }).map((_, columnIndex) => ({
        ...props,
        key: `${prefix}${columnIndex}`,
        dataKey: `${prefix}${columnIndex}`,
        title: `Column ${columnIndex}`,
        width: 150,
      }))
    const generateData = (
      columns: ReturnType<typeof generateColumns>,
      length = 200,
      prefix = 'row-'
    ) =>
      Array.from({ length }).map((_, rowIndex) => {
        return columns.reduce(
          (rowData, column, columnIndex) => {
            rowData[column.dataKey] = `Row ${rowIndex} - Col ${columnIndex}`
            return rowData
          },
          {
            id: `${prefix}${rowIndex}`,
            parentId: null,
          }
        )
      })
    const columns = generateColumns(10)
    const data = generateData(columns, 1000)
  </script>
(17)Tag 标签
  <el-tag class="ml-2" type="warning">Tag 4</el-tag>
(18)Timeline 时间线
  <template>
    <el-timeline>
      <el-timeline-item
        v-for="(activity, index) in activities"
        :key="index"
        :timestamp="activity.timestamp"
      >
        {{ activity.content }}
      </el-timeline-item>
    </el-timeline>
  </template>
  <script lang="ts" setup>
    const activities = [
      {
        content: 'Event start',
        timestamp: '2018-04-15',
      },
      {
        content: 'Approved',
        timestamp: '2018-04-13',
      },
      {
        content: 'Success',
        timestamp: '2018-04-11',
      },
    ]
  </script>
(19)Tree 树形控件
  注意,级联选择器、树形控件、树形选择的区别
  <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick" />
(20)TreeSelect 树形选择
  注意,级联选择器、树形控件、树形选择的区别
  <el-tree-select
    v-model="value"
    :data="data"
    :render-after-expand="false"
    show-checkbox
  />
(21)Tree V2 虚拟化树形控件
  <el-tree-v2 :data="data" :props="props" :height="208" /> 
5、Navigation 导航
(1)Affix 固钉
  <el-affix :offset="120">
    <el-button type="primary">Offset top 120px</el-button>
  </el-affix>
(2)Backtop 回到顶部
  <el-backtop :right="100" :bottom="100" />
(3)Breadcrumb 面包屑
<template>
  <el-breadcrumb separator="/">
    <el-breadcrumb-item :to="{ path: '/' }">homepage</el-breadcrumb-item>
    <el-breadcrumb-item><a href="/">promotion management</a></el-breadcrumb-item>
    <el-breadcrumb-item>promotion list</el-breadcrumb-item>
    <el-breadcrumb-item>promotion detail</el-breadcrumb-item>
  </el-breadcrumb>
</template>
(4)Dropdown 下拉菜单
  <el-dropdown>
    <span class="el-dropdown-link">
      Dropdown List
      <el-icon class="el-icon--right">
        <arrow-down />
      </el-icon>
    </span>
    <template #dropdown>
      <el-dropdown-menu>
        <el-dropdown-item>Action 1</el-dropdown-item>
        <el-dropdown-item>Action 2</el-dropdown-item>
        <el-dropdown-item>Action 3</el-dropdown-item>
        <el-dropdown-item disabled>Action 4</el-dropdown-item>
        <el-dropdown-item divided>Action 5</el-dropdown-item>
      </el-dropdown-menu>
    </template>
  </el-dropdown>
(5)Menu 菜单
  <el-menu
    :default-active="activeIndex"
    class="el-menu-demo"
    mode="horizontal"
    @select="handleSelect"
  >
    <el-menu-item index="1">Processing Center</el-menu-item>
    <el-sub-menu index="2">
      <template #title>Workspace</template>
      <el-menu-item index="2-1">item one</el-menu-item>
      <el-menu-item index="2-2">item two</el-menu-item>
      <el-menu-item index="2-3">item three</el-menu-item>
      <el-sub-menu index="2-4">
        <template #title>item four</template>
        <el-menu-item index="2-4-1">item one</el-menu-item>
        <el-menu-item index="2-4-2">item two</el-menu-item>
        <el-menu-item index="2-4-3">item three</el-menu-item>
      </el-sub-menu>
    </el-sub-menu>
    <el-menu-item index="3" disabled>Info</el-menu-item>
    <el-menu-item index="4">Orders</el-menu-item>
  </el-menu>
(6)Page Header 页头
  <el-page-header @back="onBack">
    <template #breadcrumb>
      <el-breadcrumb separator="/">
        <el-breadcrumb-item :to="{ path: './page-header.html' }">
          homepage
        </el-breadcrumb-item>
        <el-breadcrumb-item
          ><a href="./page-header.html">route 1</a></el-breadcrumb-item
        >
        <el-breadcrumb-item>route 2</el-breadcrumb-item>
      </el-breadcrumb>
    </template>
    <template #content>
      <div class="flex items-center">
        <el-avatar
          class="mr-3"
          :size="32"
          src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
        />
        <span class="text-large font-600 mr-3"> Title </span>
        <span
          class="text-sm mr-2"
          style="color: var(--el-text-color-regular)"
        >
          Sub title
        </span>
        <el-tag>Default</el-tag>
      </div>
    </template>
    <template #extra>
      <div class="flex items-center">
        <el-button>Print</el-button>
        <el-button type="primary" class="ml-2">Edit</el-button>
      </div>
    </template>
    <el-descriptions :column="3" size="small" class="mt-4">
      <el-descriptions-item label="Username">kooriookami</el-descriptions-item>
      <el-descriptions-item label="Telephone">18100000000</el-descriptions-item>
      <el-descriptions-item label="Place">Suzhou</el-descriptions-item>
      <el-descriptions-item label="Remarks">
        <el-tag size="small">School</el-tag>
      </el-descriptions-item>
      <el-descriptions-item label="Address">No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
      </el-descriptions-item>
    </el-descriptions>
    <p class="mt-4 text-sm">
      Element Plus team uses <b>weekly</b> release strategy under normal
      circumstance, but critical bug fixes would require hotfix so the actual
      release number <b>could be</b> more than 1 per week.
    </p>
  </el-page-header>
(7)Steps 步骤条
  <el-steps :active="active" finish-status="success">
    <el-step title="Step 1" />
    <el-step title="Step 2" />
    <el-step title="Step 3" />
  </el-steps>
(8)Tabs 标签页
  <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
    <el-tab-pane label="User" name="first">User</el-tab-pane>
    <el-tab-pane label="Config" name="second">Config</el-tab-pane>
    <el-tab-pane label="Role" name="third">Role</el-tab-pane>
    <el-tab-pane label="Task" name="fourth">Task</el-tab-pane>
  </el-tabs>
6、Feedback 反馈组件
(1)Alert 提示
  Alert不是浮层元素,不会自动消失或关闭
  <el-alert title="success alert" type="success" />
(2)Dialog 对话框
  <el-dialog
    v-model="dialogVisible"
    title="Tips"
    width="30%"
    :before-close="handleClose"
  >
    <span>This is a message</span>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogVisible = false">Cancel</el-button>
        <el-button type="primary" @click="dialogVisible = false">
          Confirm
        </el-button>
      </span>
    </template>
  </el-dialog>
(3)Drawer 抽屉
  <el-drawer
    v-model="drawer"
    title="I am the title"
    :direction="direction"
    :before-close="handleClose"
  >
    <span>Hi, there!</span>
  </el-drawer>
(4)Loading 加载
  <el-table v-loading="loading" :data="tableData" style="width: 100%">
    <el-table-column prop="date" label="Date" width="180" />
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" />
  </el-table>
(5)Message 消息提示
  Message用于主动操作后的反馈提示
  Notification用于系统级通知的被动提醒
  <template>
    <el-button :plain="true" @click="open">Show message</el-button>
  </template>
  <script lang="ts" setup>
    import { h } from 'vue'
    import { ElMessage } from 'element-plus'
    const open = () => {
      ElMessage('this is a message.')
    }
    const open = () => {
      ElMessage({
        message: 'Warning, this is a warning message.',
        type: 'warning',
      })
    }
  </script>
(6)MessageBox 消息弹出框
  简略版的Dialog(自悟)
  <template>
    <el-button text @click="open">Click to open the Message Box</el-button>
  </template>
  <script lang="ts" setup>
    import { ElMessage, ElMessageBox } from 'element-plus'
    import type { Action } from 'element-plus'
    const open = () => {
      ElMessageBox.alert('This is a message', 'Title', {
        // if you want to disable its autofocus
        // autofocus: false,
        confirmButtonText: 'OK',
        callback: (action: Action) => {
          ElMessage({
            type: 'info',
            message: `action: ${action}`,
          })
        },
      })
    }
  </script>
(7)Notification 通知
  Message用于主动操作后的反馈提示
  Notification用于系统级通知的被动提醒
  <template>
    <el-button plain @click="open"> Success </el-button>
  </template>
  <script lang="ts" setup>
    import { ElNotification } from 'element-plus'
    const open = () => {
      ElNotification({
        title: 'Success',
        message: 'This is a success message',
        type: 'success',//可有、可无、可选
      })
    }
  </script>
(8)Popconfirm 气泡确认框
  在被点击元素旁边弹出一个简单的气泡确认框
  <template>
    <el-popconfirm title="Are you sure to delete this?">
      <template #reference>
        <el-button>Delete</el-button>
      </template>
    </el-popconfirm>
  </template>
(9)Popover 弹出框(气泡卡片)
  在被点击元素旁边弹出一个简单的气泡卡片
  <el-popover
    placement="top-start"
    title="Title"
    :width="200"
    trigger="hover"
    content="this is content, this is content, this is content"
  >
    <template #reference>
      <el-button class="m-2">Hover to activate</el-button>
    </template>
  </el-popover>
(10)Tooltip 文字提示
  在被悬停元素旁边弹出一个简单的气泡卡片
  <el-tooltip
    class="box-item"
    effect="dark"
    content="Top Left prompts info"
    placement="top-start"
  >
    <el-button>top-start</el-button>
  </el-tooltip>
7、Others 其他
(1)Divider 分割线
  <el-divider content-position="left">Rabindranath Tagore</el-divider>
  <el-divider>
    <el-icon>
      <star-filled />
    </el-icon>
  </el-divider>
  <el-divider content-position="right">Rabindranath Tagore</el-divider>

六、vue报错与解决
1、问题1
(1)现象,[Vue warn]: Invalid prop: custom validator check failed for prop "type".    vue.runtime.esm.js?
(2)解决,
A、这个问题不影响项目的正常运行
B、出现这个报错的原因是,iview支持数字类型的输入,vue自带的-vue错误检查工具-vue.runtime.esm.js-不支持数字类型的输入,因而报错
C、解决方案,开发者应当在浏览器安装-vue错误检查插件-https://github.com/vuejs/vue-devtools,取代vue.runtime.esm.js,给vue检查错误
2、问题2
(1)现象,el-select的blur事件不能通过{trigger:'blur'}触发,只能通过{@blur="selectBlur"}触发
(2)解决,
  <el-form-item label="所属页卡" prop="card_id" >
    <el-select  v-model="form.card_id" placeholder="请选择所属页卡" @blur="selectBlur">
      <el-option
        v-for="item in configType"
        :key="item.value"
        :label="item.label"
        :value="item.value">
      </el-option>
    </el-select>
  </el-form-item>
  rules: {//trigger: 'blur'无效,trigger: 'change'有效,
    card_id:[{ required: true, message: '请选择所属页卡', trigger: 'change', validator: function(rule, value, callback){
        if (!value) {
          callback(new Error());
        }else{
          callback()
        } 
      } 
    }],
  },
  selectBlur(){
    this.$refs["form"].validateField("card_id"); 
  },

七、vue效果与实现
1、一行显示多个表单项
  <el-form :model="nowCard" :label-position="'right'" label-width="130px">
    <el-form-item label="内容配置" class="blue-label"></el-form-item>
    <el-row>
      <el-col :span="10">
        <el-form-item label="页卡名称">
          <el-input
            v-model="nowCard.card_name"
            type="text"
            maxlength="200"
            placeholder="请输入页卡名称" 
            style="width:300px;"
            :disabled="nowCard.create_user == 'system'"
          />
        </el-form-item>
      </el-col>
      <el-col :span="10">
        <el-form-item label="页卡序号">
          <el-input
            v-model="nowCard.order_index"
            type="text"
            maxlength="200"
            :placeholder="'请输入页卡序号('+ addPagecardText +'时不输入)'"
            style="width:300px;"
            :disabled="nowCard.isAdd"
          />
        </el-form-item>
      </el-col>
    </el-row>
    <div style="display: flex">
      <el-form-item label="是否显示" prop="coursePage" >
        <el-radio-group v-model="nowCard.is_show">
          <el-radio label="0" size="large">不显示</el-radio>
          <el-radio label="1" size="large">显示</el-radio>
        </el-radio-group>
      </el-form-item>
      <div style="flex: 1;"></div>
      <div class="generate-page" @click="generatePage">生成页面</div>
    </div>
  </el-form>
2、弹窗上下固定,中间滚动
  <el-dialog
    title="详情"
    :visible.sync="dialogPreviewVisible"
    width="1150px"
  >
    <div style="height:640px;overflow: auto;">
      <AlbumPreview :sid="item.f_id"></AlbumPreview>
    </div>
    <el-button>确定</el-button>
  </el-dialog>
3、多个表格都是尾行跨栏合并,部分内容个别表格显示。无论是表头还是表体,数据都是按行写的
  var spanMethod = function({ row, column, rowIndex, columnIndex }){
    if (rowIndex === 1 && columnIndex === 1) {//第2行,第2栏,跨4栏合并
      return {
        rowspan: 1,
        colspan: 4
      };  
    }
  };
  <el-table
    v-if="orderState === 0 ||orderState === 1 "
    v-for="(tableNopay, indexRow) in rowsNopay"
    :data="tableNopay"
    :header-cell-style="{color:'#b2b8c4',background:'#eaeef5'}"
    style="width: 100%;margin-bottom: 40px;"
    :span-method="spanMethod"  
  >
    <el-table-column
      v-for="(item, indexCol) in colsNopay[indexRow]"
      :key="indexCol"
      :prop="item.prop"
      :width="item.width"
      :label="item.label"
    > 
      <template #header="scope" v-if="indexCol === 0">
        <span>下单时间:</span>
        <span>{{scope.column.label}}</span>
      </template>
      <template #header="scope" v-if="indexCol === 4">
        <span>待支付,剩余时间 </span>
        <span style="display: inline-block;color:red; padding-left: 10px;">{{scope.column.label}}</span>
      </template>
      <template #default="scope" v-if="indexCol === 1">/* 第2栏使用插槽,其内为空时,则使用“后备内容”,默认为该栏的prop值 */
        <div v-if="scope.row.isColspan" style="text-align: right;">
          <span>
            <span>共{{ scope.row.totalNum }}件产品,总计</span>
            <span style="color:red">¥{{ scope.row.totalMoney }}</span>
          </span>
          <span style="padding-left: 40px;">
            <el-button @click="cancelOrder">取消订单</el-button>
            <el-button @click="immediatePay" type="primary">立即支付</el-button>
          </span>
        </div>
      </template>
    </el-table-column>
  </el-table>
4、多个表头最后一栏插入倒计时-以待付款订单为例
  <script setup>
    import Title from './title.vue';
    import Header from '../base/header.vue';
    import LeftNav from './left.vue';
    import Nav from '../base/nav.vue';
    import { ref, reactive, onMounted } from 'vue'
    var orderState = ref(0)   
    var timerArray = reactive([])
    var colsNopay = reactive([]);//待付款-表头
    var rowsNopay = reactive([]);//待付款-表体
    var colsYespay = reactive([]);//支付成功-表头
    var rowsYespay = reactive([]);//支付成功-表体
    var orderTabs = reactive([//订单选项卡
      {
        name: "全部订单",
        state: 0,
      },
      {
        name: "待付款",
        state: 1,
      },
      {
        name: "支付成功",
        state: 2,
      },
    ]);
    var spanMethod = function({ row, column, rowIndex, columnIndex }){
      if (rowIndex === 1 && columnIndex === 1) {
        return {
          rowspan: 1,
          colspan: 4
        };  
      }
    };
    //以下切换订单Tab
    var clickOrderState = function(item){
      orderState.value = item.state
      if(timerArray.length>0){
        for(var i = 0; i < timerArray.length; i++){
          clearInterval(timerArray[i]);
        }
      }
      if(orderState.value === 0 || orderState.value === 1){//处理全部订单或待付款
        var objArray = []
        timerArray.length = 0;
        for(var i = 0; i < colsNopay.length; i++){
          var obj = colsNopay[i][colsNopay[i].length-1];//获取每个表头的最后一项,里面包含-倒计时区域
          var total = 0;
          countDown(obj)//立即执行一次倒计时,以免-倒计时区域-出现1秒钟的空白
          objArray.push(obj)//存储所有的-倒计时区域
          var interval = setInterval(function(){//for循环结束时,objArray存储了所有的-倒计时区域,又过1秒钟后,多个定时器同时执行
            var index = total%objArray.length;//获取本定时器对应的-倒计时区域的序号
            countDown(objArray[index])//执行倒计时,传入倒计时区域
            total++
          },1000)
          obj.interval = interval
          timerArray.push(interval)
        }
      }
    }
    //以下是倒计时
    var countDown = function (obj) {
      console.log( '倒计时正在运行,请判断是否必要!!!' );
      function addZero(number) {
        return number.toString()[1] ? number : "0" + number;
      };
      var nowMilliseconds = new Date().getTime();
      var futureMilliseconds = new Date(obj.expires).getTime();
      if(nowMilliseconds >= futureMilliseconds){
        clearInterval(obj.interval);
        return 
      }
      var seconds = Math.floor((futureMilliseconds - nowMilliseconds)/1000) 
      var str = "";
      var day = Math.floor(seconds / 86400);
      var hour = Math.floor((seconds % 86400) / 3600);
      var minute = Math.floor(((seconds % 86400) % 3600) / 60);
      var second = Math.floor(((seconds % 86400) % 3600) % 60);
      if ( day > 0) {
        str += day + "天";
      }
      if ( day > 0 || hour > 0) {
        str += addZero(hour) + "小时";
      }
      if ( day > 0 || hour > 0 || minute > 0) {
        str += addZero(minute) + "分";
      }
      str += addZero(second) + "秒";
      obj.label = str;
    }
    var clickOrderPagination = function(item){}
    var cancelOrder = function(){}
    var immediatePay = function(){}
    onMounted(function() {
      console.log("组件加载完成,引用getData获取原始数据");
      //以下待支付
      //以下服务器数据-2条订单信息-待付款
      var serverNopayDatas = [
        {
          orderTime: '2010-11-11 12:00:00',
          expires: '2025-10-10 18:00:00',
          courseName: '课程1',
          oldPrice: 1999.0,
          payPrice: 1999.0,
          favour: '-0.00',
          forPay: 1999.0,
          orderCode: '0123456789',
          totalNum: 1,
          totalMoney: 1999.0,
        },{
          orderTime: '2014-12-12 12:00:00',
          expires: '2024-10-10 18:00:00',
          courseName: '课程2',
          oldPrice: 1000.0,
          payPrice: 1000.0,
          favour: '-0.00',
          forPay: 1000.0,
          orderCode: '9876543210',
          totalNum: 1,
          totalMoney: 1000.0,
        },
      ];
      //以下服务器数据-前端加工
      for(var i = 0; i<serverNopayDatas.length; i++){
        var item = serverNopayDatas[i];
        var singleCol = [//每个表头有5列
          {
            prop: 'date',
            label: item.orderTime,
            width: '300px'
          },
          {
            prop: 'oldPrice',
            label: '原价(元)',
          },
          {
            prop: 'payPrice',
            label: '应付(元)',
          },
          {
            prop: 'favour',
            label: '优惠金额',
          },
          {
            prop: 'forPay',
            label: '',
            width: '300px',
            expires: item.expires,
          },
        ];
        var singleRow = [//每个表体有2行
          {
            date: item.courseName,
            oldPrice: item.oldPrice,
            payPrice: item.payPrice,
            favour: item.favour,
            forPay: item.forPay,
          },
          {
            date: '订单号:' + item.orderCode,
            isColspan: true,
            totalNum: item.totalNum,
            totalMoney: item.totalMoney,
          }
        ];
        colsNopay.push(singleCol)
        rowsNopay.push(singleRow)
      }
      //以下已支付
      //以下服务器数据-2条订单信息-已付款
      var serverYespayDatas = [
        {
          orderTime: '2011-11-11 12:00:00',
          courseName: '课程3',
          oldPrice: 1999.0,
          payPrice: 1999.0,
          favour: '-0.00',
          forPay: 1999.0,
          orderCode: '0123456789',
          totalNum: 1,
          totalMoney: 1999.0,
        },{
          orderTime: '2013-12-12 12:00:00',
          courseName: '课程4',
          oldPrice: 1000.0,
          payPrice: 1000.0,
          favour: '-0.00',
          forPay: 1000.0,
          orderCode: '9876543210',
          totalNum: 1,
          totalMoney: 1000.0,
        },
      ];
      //以下服务器数据-前端加工
      for(var i = 0; i<serverYespayDatas.length; i++){
        var item = serverYespayDatas[i];
        var singleCol = [//每个表头有5列
          {
            prop: 'date',
            label: '下单时间:' + item.orderTime,
            width: '300px'
          },
          {
            prop: 'oldPrice',
            label: '原价(元)',
          },
          {
            prop: 'payPrice',
            label: '应付(元)',
          },
          {
            prop: 'favour',
            label: '优惠金额',
          },
          {
            prop: 'forPay',
            label: '已支付',
            width: '300px'
          },
        ];
        var singleRow = [//每个表体有2行
          {
            date: item.courseName,
            oldPrice: item.oldPrice,
            payPrice: item.payPrice,
            favour: item.favour,
            forPay: item.forPay,
          },
          {
            date: '订单号:' + item.orderCode,
            isColspan: true,
            totalNum: item.totalNum,
            totalMoney: item.totalMoney,
          }
        ];
        colsYespay.push(singleCol)
        rowsYespay.push(singleRow)
      }
      clickOrderState({state:0})
    });
    onBeforeUnmount(function() {
      if(timerArray.length>0){
        for(var i = 0; i < timerArray.length; i++){
          clearInterval(timerArray[i]);
        }
      }
    });
  </script>

八、Vue与React的异同 
1、相同点:
(1)用于创建UI的js库
(2)单向数据流
(3)用虚拟DOM
(4)基于组件
2、不同点:
(1)vue使用html模板;react使用jsx模板
(2)vue有双向绑定语法;react没有
(3)vue有computed和watch;react没有