Vue-next: 下一代Vue

仓库地址: https://github.com/vuejs/vue-next

目前已知Vue的新版本,采用TypeScript编写.

vuejs logo
image-2931

已完成的功能如下:

We have achieved most of the architectural goals and new features planned for v3:

Compiler

  • Modular architecture
  • “Block tree” optimization
  • More aggressive static tree hoisting
  • Source map support
  • Built-in identifier prefixing (aka “stripWith”)

    Built-in pretty-printing

    Lean ~10kb brotli-compressed browser build after dropping source map and identifier prefixing

    Runtime

    Significantly faster
    Simultaneous Composition API + Options API support, with typings
    Proxy-based change detection
    Fragments
    Portals
    Suspense w/ async setup()

    从开发进度来看,预计2020年第一季度发布正式版,期待!

Vue:动态生成表单

开始

  1. 2019-11-04更新: 增加多个组件的测试.

因代码历史背景,无法使用NodeJS.所以这个版本的是纯js调用,不会涉及到NodeJS.

有时候会遇到想动态配置一下表单,根据动态参数自动生成表单并且可以验证.于是想了想,顺便找了找代码..发现只有使用NodeJS是最简单的.但又无法使用,难受…

只有持续鼓捣…还好…

vuejs logo
image-2725

模板部分

代码其实很简单,就是把模板代码定义在一个js文件(组件)中.然后在HTML中引用即可.

文件名: dyform_item.js

var template=`
<div>
  <el-form-item :label="widget.name" :props="dataModel" :label-width="widget.labelwidth" label-width="120px">
    <template v-if="widget.type == 'input'" >
      <el-input 
        v-if="widget.options.dataType == 'number' || widget.options.dataType == 'integer' || widget.options.dataType == 'float'"
        :type="widget.options.dataType"
        v-model="dataModel"
        v-on:change="childData($event)"
        :placeholder="widget.options.placeholder"
        :style="{width: widget.options.width?widget.options.width:'100%'}"
      ></el-input>
      <el-input 
        v-else
        :type="widget.options.dataType"
        v-model="dataModel"
        v-on:change="childData($event)"
        :placeholder="widget.options.placeholder"
        :style="{width: widget.options.width?widget.options.width:'100%'}"
      ></el-input>
    </template>

    <template v-if="widget.type == 'textarea'">
      <el-input type="textarea" :rows="5"
        v-model="dataModel"
        :disabled="widget.options && widget.options.disabled"
        v-on:change="childData($event)"
        :placeholder="(widget.options && widget.options.placeholder) ?widget.options.placeholder:''"
        :style="{width: (widget.options && widget.options.width)?widget.options.width:'100%'}"
      ></el-input>
    </template>

    <template v-if="widget.type == 'number'">
      <el-input-number 
        v-model="dataModel" 
        :style="{width: (widget.options && widget.options.width)?widget.options.width:'100%'}"
        :step="(widget.options && widget.options.step)?widget.options.step:1"
        v-on:change="childData($event)"
        controls-position="right"
      ></el-input-number>
    </template>

    <template v-if="widget.type == 'radio'">
      <el-radio-group v-model="dataModel"
      v-on:change="childData($event)"
        :style="{width: widget.options.width?widget.options.width:'100%'}"
      >
        <el-radio
          :style="{display: widget.options.inline ? 'inline-block' : 'block'}"
          :label="item.value" v-for="(item, index) in (widget.options.remote ? widget.options.remoteOptions : widget.options.options)" :key="index"
        >
          <template v-if="widget.options.remote">{{item.label}}</template>
          <template v-else>{{widget.options.showLabel ? item.label : item.value}}</template>
        </el-radio>
      </el-radio-group>
    </template>

    <template v-if="widget.type == 'checkbox'">
      <el-checkbox-group v-model="checkboxModel" v-on:change="childData($event)"
        :style="{width: widget.options.width?widget.options.width:'100%'}"
      >
     <el-checkbox
        :style="{display: widget.options.inline ? 'inline-block' : 'block'}"
           v-for="(item, index) in ((widget.options.remote && widget.options.remoteOptions) ? widget.options.remoteOptions : widget.options.options)" v-model="dataModel" :label="item.value" :key="index"
        >
          {{widget.options.remote?item.label:widget.options.showLabel?item.label:item.value}}
        </el-checkbox>
      </el-checkbox-group>
    </template>

    <template v-if="widget.type == 'time'">
      <el-time-picker 
        v-model="timePickerModel"
        v-on:change="childData($event)"
        :is-range="(widget.options && widget.options.isRange)?true:false"
        :placeholder="(widget.options && widget.options.placeholder)?widget.options.placeholder:''"
        :start-placeholder="(widget.options && widget.options.startPlaceholder)?widget.options.startPlaceholder:''"
        :end-placeholder="(widget.options && widget.options.endPlaceholder)?widget.options.endPlaceholder:''"
        :readonly="(widget.options && widget.options.readonly)?true:false"
        :disabled="(widget.options && widget.options.disabled)?true:false"
        :editable="(widget.options && widget.options.editable)?true:false"
        :clearable="(widget.options && widget.options.clearable)?true:false"
        :arrowControl="(widget.options && widget.options.arrowControl)?true:false"
        :value-format="(widget.options && widget.options.format)?widget.options.format:''"
        :style="{width: (widget.options && widget.options.width)?widget.options.width:'100%'}"
      >
      </el-time-picker>
    </template>

    <template v-if="widget.type=='date'">
      <el-date-picker
        v-model="dataModel"
        v-on:change="childData($event)"
        :type="(widget.options&&widget.options.type)?widget.options.type:'date'"
        :placeholder="(widget.options && widget.options.placeholder)?widget.options.placeholder:''"
        :start-placeholder="(widget.options && widget.options.startPlaceholder)?widget.options.startPlaceholder:''"
        :end-placeholder="(widget.options && widget.options.endPlaceholder)?widget.options.endPlaceholder:''"
        :readonly="(widget.options && widget.options.readonly)?true:false"
        :disabled="(widget.options && widget.options.disabled)?true:false"
        :editable="(widget.options && widget.options.editable)?true:false"
        :clearable="(widget.options && widget.options.clearable)?true:false"
        :value-format="(widget.options&&widget.options.timestamp) ? 'timestamp' : (widget.options&&widget.options.format)?widget.options.format:null"
        :format="(widget.options && widget.options.format)?widget.options.format:null"
        :style="{width: (widget.options && widget.options.width)?widget.options.width:'100%'}"
      >
      </el-date-picker>
    </template>

    <template v-if="widget.type =='rate'">
      <el-rate v-model="dataModel"
        v-on:change="childData($event)"
        :max="(widget.options && widget.options.max)?widget.options.max:5"
        :disabled="(widget.options && widget.options.disabled)?true:false"
        :allow-half="(widget.options && widget.options.allowHalf)?true:false"
      ></el-rate>
    </template>

    <template v-if="widget.type == 'color'">
      <el-color-picker 
        v-model="dataModel"
        v-on:change="childData($event)"
        :disabled="(widget.options && widget.options.disabled)?true:false"
        :show-alpha="(widget.options && widget.options.showAlpha)?true:false"
      ></el-color-picker>
    </template>

    <template v-if="widget.type == 'select'">
      <el-select
        v-model="dataModel"
        v-on:change="childData($event)"
        :disabled="widget.options.disabled"
        :multiple="widget.options.multiple"
        :clearable="widget.options.clearable"
        :placeholder="widget.options.placeholder"
        :style="{width: widget.options.width?widget.options.width:'100%'}"
      >
        <el-option v-for="item in (widget.options.remote ? widget.options.remoteOptions : widget.options.options)" :key="item.value" :value="item.value" :label="widget.options.showLabel || widget.options.remote?item.label:item.value"></el-option>
      </el-select>
    </template>

    <template v-if="widget.type=='switch'">
      <el-switch
        v-model="dataModel"
        v-on:change="childData($event)"
        :disabled="(widget.options && widget.options.disabled)?true:false"
      >
      </el-switch>
    </template>
  </el-form-item>
  </div>`;

var dyform_item=Vue.extend({
    template:template,
    props: ['widget'],
    data () {
        return {
          timePickerModel:[],
          checkboxModel:[],
          dataModel: this.widget.value,
        }
    },
    methods: {
      childData:function(value){
        this.$emit('change',this.widget.code,value);
      }
    },
    watch:{
      dataModel:function(newVal,oldVal){
        if(widget.type == 'number'){
          return parseInt(this.dataModel);
        }
        if(widget.options.dataType == 'number' || widget.options.dataType == 'integer' || widget.options.dataType == 'float'){
          return widget.options.dataType == 'float'?parseFloat(this.dataModel):parseInt(this.dataModel)
        }
        return this.dataModel;
      }

    },
    created () {

      },
      watch: {

      }
}); 

使用示例(HTML)

之后就是在HTML里面使用定义好的模板了.

文件名: index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <title>自动生成表单</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link href="https://cdn.bootcss.com/element-ui/2.12.0/theme-chalk/index.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
    <script src="https://cdn.bootcss.com/element-ui/2.12.0/index.js"></script>
</head>

<body>
    <script type="text/javascript" src="dyform_item.js"></script>
    <div id="app">
        <el-row :gutter="20">
            <el-col :span="12">
                <el-form ref="form">
                    <template v-for="item in ExtForm">
                        <form-item :key="item.key" @change="changeData" :ref="item.code" :widget="item"></form-item>
                    </template>
                </el-form>
            </el-col>
            <el-col :span="10">
                 <el-table
                    :data="ExtFormData"
                    border
                    style="width: 100%">
                   
                    <el-table-column
                      prop="name"
                      label="名称"
                      width="180">
                    </el-table-column>
                    <el-table-column
                      prop="value"
                      label="值">
                    </el-table-column>
                  </el-table>
            </el-col>
        </el-row>
    </div>
    <script>
    var cp = {
        data: function() {
            return {
                ExtForm: {},
                ExtFormData: []
            }
        },
        components: {
            'form-item': dyform_item
        },
        methods: {
            // 动态加载数据到右侧表格.
            getDataShow:function() {
                this.ExtFormData=Object.entries(this.ExtForm).map(data=>{
                    return {
                        name:data[1].name,
                        value:data[1].value
                    }
                })
            },
            // 子组件动态修改数据调用.
            changeData: function(itemCode, value) {
                console.log(`[${this.ExtForm[itemCode].name}]更新前的值:${this.ExtForm[itemCode].value}`);
                this.ExtForm[itemCode].value = value;
                console.log(`[${this.ExtForm[itemCode].name}]更新后的值:${this.ExtForm[itemCode].value}`);
                this.getDataShow();
            },
            // 动态初始化表单
            initExtForm: function() {
                var _this = this;
                var items = [
                   
                    {  "XMLID": "940bfdfdsaf2218911c81330d",  "ITEMCODE": "sCheckBox", "ITEMNAME": "渠道1", "ITEMTYPE": "checkbox", "NOTNULL": "", "DEFDATA": "[{\"label\":\"网络\",\"value\":\"1\"},{\"label\":\"线下\",\"value\":\"2\"},{\"label\":\"活动\",\"value\":\"3\"}]", "ACTIVE_STATE": "Y", "ITEMNO": "5", "DEFTYPE": "json" },
                 {  "XMLID": "940bfdsafd2c226c8303911c81330d",  "ITEMCODE": "sName", "ITEMNAME": "姓名2", "ITEMTYPE": "", "NOTNULL": "", "DEFDATA": "", "ACTIVE_STATE": "Y", "ITEMNO": "1", "DEFTYPE": null },
{ "XMLID":"4b0f67616dfdsafdas8106fecbbb1d7e",  "ITEMCODE": "sSex", "ITEMNAME": "性别3", "ITEMTYPE": "select", "NOTNULL": "", "DEFDATA": "[{\"label\":\"男性\",\"value\":\"1\"},{\"label\":\"女性\",\"value\":\"2\"},{\"label\":\"未知\",\"value\":\"3\"}]", "ACTIVE_STATE": "Y", "ITEMNO": "2", "DEFTYPE": "json" }, 
  {  "XMLID": "940bfdsafasd6c8308911c81330d",  "ITEMCODE": "sPhone", "ITEMNAME": "电话4", "ITEMTYPE": "number", "NOTNULL": "", "DEFDATA": "", "ACTIVE_STATE": "Y", "ITEMNO": "3", "DEFTYPE": null }, 
  {  "XMLID": "940bfdfdsoiwoekskkzdcf08911c81330d",  "ITEMCODE": "sRadio", "ITEMNAME": "是否启用5", "ITEMTYPE": "radio", "NOTNULL": "", "DEFDATA": "[{\"label\":\"启用\",\"value\":\"1\"},{\"label\":\"停用\",\"value\":\"2\"},{\"label\":\"未知\",\"value\":\"3\"}]", "ACTIVE_STATE": "Y", "ITEMNO": "3", "DEFTYPE": "json" }, 
 
{  "XMLID": "2040356f36f9dsaze29fc98da3c",  "ITEMCODE": "sZipCode", "ITEMNAME": "邮编6", "ITEMTYPE": "textarea", "NOTNULL": "", "DEFDATA": "", "ACTIVE_STATE": "Y", "ITEMNO": "4", "DEFTYPE": null },
{  "XMLID": "2040356f36f9232daewewda3c",  "ITEMCODE": "sRegTime", "ITEMNAME": "注册时间7", "ITEMTYPE": "time", "NOTNULL": "", "DEFDATA": "", "ACTIVE_STATE": "Y", "ITEMNO": "4", "DEFTYPE": null },
{  "XMLID": "204fdsafdsa2iqkjs98da3c",  "ITEMCODE": "sRegDate", "ITEMNAME": "注册日期8", "ITEMTYPE": "date","ITEMSUBTYPE":"date", "NOTNULL": "", "DEFDATA": "", "ACTIVE_STATE": "Y", "ITEMNO": "4", "DEFTYPE": null },
{  "XMLID": "204fdsafdsawewzjs98da3c",  "ITEMCODE": "sRegDateTo", "ITEMNAME": "有效区间9", "ITEMTYPE": "date","ITEMSUBTYPE":"daterange", "NOTNULL": "", "DEFDATA": "", "ACTIVE_STATE": "Y", "ITEMNO": "4", "DEFTYPE": null },
{  "XMLID": "204fd28iueiww98da3c",  "ITEMCODE": "sRegRate", "ITEMNAME": "服务评分10", "ITEMTYPE": "rate","ITEMSUBTYPE":"", "NOTNULL": "", "DEFDATA": "", "ACTIVE_STATE": "Y", "ITEMNO": "4", "DEFTYPE": null },
{  "XMLID": "2041298siuiqs",  "ITEMCODE": "sColorSelect", "ITEMNAME": "颜色选择11", "ITEMTYPE": "color","ITEMSUBTYPE":"", "NOTNULL": "", "DEFDATA": "", "ACTIVE_STATE": "Y", "ITEMNO": "4", "DEFTYPE": null },
{  "XMLID": "2041298siuiqs",  "ITEMCODE": "sSwitch", "ITEMNAME": "滑块选择12", "ITEMTYPE": "switch","ITEMSUBTYPE":"", "NOTNULL": "", "DEFDATA": "", "ACTIVE_STATE": "Y", "ITEMNO": "4", "DEFTYPE": null }

];
                for (var i in items) {
                    var row = items[i];
                    var optionsSelect = '';
                    switch (row.DEFTYPE) {
                        case 'json':
                            optionsSelect = JSON.parse(row.DEFDATA);
                            console.log(optionsSelect);
                            break;
                    }
                    var value = _this.ExtFormData[row.ITEMCODE] || "";
                    switch (row.ITEMTYPE) {
                        case 'select':
                        case 'radio':
                        case 'checkbox':
                            _this.$set(_this.ExtForm, row.ITEMCODE, { notnull: row.NOTNULL, type: row.ITEMTYPE, model: _this.ExtForm[row.ITEMCODE], code: row.ITEMCODE, name: row.ITEMNAME, value: value, options: { options: optionsSelect, showLabel: true } });
                            break;
                        case 'inputnumber':
                        case 'inputfloat':
                        case 'inputint':
                        _this.$set(_this.ExtForm, row.ITEMCODE, { notnull: row.NOTNULL, type: 'input', model: '0', code: row.ITEMCODE, name: row.ITEMNAME, value: value, options:  { dataType: row.ITEMTYPE.replace('input',''), placeholder: '' } });
                        break;
                        case 'number':
                         _this.$set(_this.ExtForm, row.ITEMCODE, { notnull: row.NOTNULL, type: 'number', model: '0', code: row.ITEMCODE, name: row.ITEMNAME, value: value});
                        break;
                        case 'time':
                         _this.$set(_this.ExtForm, row.ITEMCODE, { notnull: row.NOTNULL, type: 'time', model: _this.ExtForm[row.ITEMCODE],options:{format:'HH:mm:ss'}, code: row.ITEMCODE, name: row.ITEMNAME, value: value});
                        break;
                        case 'date':
                        let optionsType=(row.ITEMSUBTYPE)?row.ITEMSUBTYPE:'date';
                        _this.$set(_this.ExtForm, row.ITEMCODE, { notnull: row.NOTNULL, type: 'date', model: _this.ExtForm[row.ITEMCODE],options:{type:optionsType,format:'yyyy-MM-dd'}, code: row.ITEMCODE, name: row.ITEMNAME, value: value});
                        break;
                        case 'textarea':
                        case 'rate':
                        case 'color':
                        case 'switch':
                        _this.$set(_this.ExtForm, row.ITEMCODE, { notnull: row.NOTNULL, type: row.ITEMTYPE, model: _this.ExtForm[row.ITEMCODE], code: row.ITEMCODE, name: row.ITEMNAME, value: value});
                        break;
                        default:
                            _this.$set(_this.ExtForm, row.ITEMCODE, { notnull: row.NOTNULL, type: 'input', model: _this.ExtForm[row.ITEMCODE], code: row.ITEMCODE, name: row.ITEMNAME, value: value, options: { options: { dataType: 'text', placeholder: '' } } });
                            break;
                    }


                }
            }
        },
        created: function() {

        },
        mounted: function() {
            this.initExtForm();
        }
    }

    var config = Vue.extend(cp);
    var c = new config().$mount('#app');
    </script>
</body>

</html>

总结

  1. 定义通用模板(子组件);
  2. 在HTML中引用(父组件),并初始化;
  3. 子组件通过$emit的方式更新父组件中的值,这里使用了一个方法changeData.可以在父组件中自定义需要更新及验证的数据.
  4. 将dyform_item.js和index.html保存成文件,放在同一个目录.之后用浏览器访问index.html即可看到效果.

Vue:切换class模拟实现tab

实现个切换

其实就是翻译了jQuery的removeClass和addClass,只是在Vue里面使用了变量去切换.

vuejs logo

运行效果

vuejs tab 运行效果

源码

源码比较简单,引用了bootcss的cdn服务.

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8" />
    <title>首页</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script src="https://cdn.bootcss.com/vue/2.4.2/vue.min.js"></script>
    <style>
       

        .Grid {
            display: flex;
        }

        .Grid-cell {
            flex: 1;
            text-align: center;
        }

   

        .bottomActive{
            background-color: #00ff00;
        }

        .Grid .Grid-cell {
            padding:5px;
            cursor: pointer;
        }
    </style>
</head>

<body>
<div id="app">
    
            <div class="Grid">
                <div :class="[[{'bottomActive':1==bottomactive},'Grid-cell']]" @click="bottomMenuClick(1)"> 
                    <div>第一个</div>
                </div>
                <div :class="[{'bottomActive':2==bottomactive},'Grid-cell']" @click="bottomMenuClick(2)">
                    <div>第二个</div>
                </div>
                <div :class="[{'bottomActive':3==bottomactive},'Grid-cell']" @click="bottomMenuClick(3)">
                    <div>第三个</div>
                </div>
            </div>


    </div>
    <script>
    var vue = new Vue({
        el:"#app",
        data:{
            bottomactive:1
        },
        methods:{
            bottomMenuClick:function(index){
                console.log("index:"+index);
                this.bottomactive=index;
            }
        }
    })

    </script>
</body>

</html>

Element-UI:缓存Tabs打开的页签

竟然不缓存?

在使用Element ui 的时候,发现Tabs挺好用,于是做了这样一个页面:左侧是菜单,右侧顶部是Tabs,右侧下方是iframe.一切就绪,问题出现,就是每次点菜单的时候,Tabs就会新增一个页签,这导致iframe增加不少.于是想了个办法,来解决这个问题.

  1. 在menu菜单中,index直接为页面的名称;
  2. 在Select事件中,拿到index就可以请求页面了;
  3. 请求页面的时候,先判断页面是否已经存在于缓存中.如果存在则直接激活该页签,不存在就先添加到Tabs,然后激活,并放入缓存中.
  4. over……

但这样,存在另外一个问题:如果有几十个菜单,会堆叠几十个iframe.侧面一想,如果共用一个iframe,那么会出现另外一个问题,就是可能在上一个页面只输入了一部分文本,还没处理就切换页签,会丢失上个页面的数据.

于是,在菜单量不多的情况下,还是每个页签一个单独的iframe.便于处理.

html

源码

只有main.html是有实质内容的,其余html都是为了演示数据.

代码可以直接在本地运行,可以保存成文件,进行运行.

main.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <title>系统管理</title>
    <link href="https://cdn.bootcss.com/element-ui/1.4.0/theme-default/index.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/vue/2.4.2/vue.min.js"></script>
    <script src="https://cdn.bootcss.com/element-ui/1.4.0/index.js"></script>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        .left-menu {
            width: 200px;
        }
    </style>
</head>

<body>
    <h1 class="title">系统管理</h1>
    <div id="app">
        <template>
<el-row>
  <el-col :span="4"><div class="grid-content bg-purple">
<el-menu default-active="userList" class="left-menu" @select="menuSelect" :collapse="isCollapse">
  <el-submenu index="1">
    <template slot="title">
      <span slot="title">用户</span>
    </template>
        <el-menu-item-group>
            <el-menu-item index="userList">用户列表</el-menu-item>
            <el-menu-item index="userAdd">用户添加</el-menu-item>
        </el-menu-item-group>
        </el-submenu>
        <el-submenu index="2">
            <template slot="title">
      <span slot="title">权限</span>
    </template>
            <el-menu-item-group>
                <el-menu-item index="roleList">权限列表</el-menu-item>
                <el-menu-item index="roleAdd">权限添加</el-menu-item>
            </el-menu-item-group>
        </el-submenu>
        </el-menu>
    </div>
    </el-col>
    <el-col :span="20">
        <div class="grid-content bg-purple-light">
            <el-tabs v-model="editableTabsValue" closable @tab-remove="removeTab" type="card">
                <el-tab-pane :key="item.name" v-for="(item, index) in editableTabs"  :label="item.title" :name="item.name">
                    <iframe class="iframepage" :src="item.content" width="100%" onload="changeFrameHeight()" style="border: none;" height="100%"></iframe>
                </el-tab-pane>
            </el-tabs>
        </div>
    </el-col>
    </el-row>

    </template>
    </div>
    <script>
        Vue.config.devtools = true;
        var Main = {
            data() {
                return {
                    isCollapse: false,
                    editableTabsValue: '1',
                    editableTabs: [{
                        title: '用户列表',
                        name: '1',
                        content: 'userList.html',
                        closable: false
                    }],
                    tabInfoObj: {}, // 缓存tabs打开的页签.
                    tabIndex: 1
                }
            },
            methods: { //给自定义的组件绑定事件
                selectMenu(key) {
                    var title = '';
                    switch (key) {
                        case 'userList':
                            title = '用户列表';
                            break;
                        case 'userAdd':
                            title = '用户添加';
                            break;
                        case 'roleList':
                            title = '权限列表';
                            break;
                        case 'roleAdd':
                            title = '权限添加';
                            break;
                    }
                    return title;
                },
                menuSelect(key, keyPath) {
                    if (this.tabInfoObj[key]) { // 判断,如果页签已经打开,则直接切换.
                        this.editableTabsValue = this.tabInfoObj[key] + '';
                    } else { // 如果页面未打开,则直接打开并添加到缓存对象中.
                        this.tabIndex += 1;
                        this.tabInfoObj[key] = this.tabIndex;
                        let newTabName = this.tabIndex + '';
                        this.editableTabs.push({
                            title: this.selectMenu(key),
                            name: newTabName,
                            content: key + '.html'
                        });
                        this.editableTabsValue = newTabName;
                    }
                },removeTab(targetName) {
                                // 关闭页签的时候,同时将其从缓存中删除(否则将导致后面不能再次添加).
                                for (var a in this.tabInfoObj) {
                		    if(this.tabInfoObj[a]==targetName){
                			delete this.tabInfoObj[a];
                			break;
                		   }
                	        };
			        let tabs = this.editableTabs;
			        let activeName = this.editableTabsValue;
			        if (activeName === targetName) {
			          tabs.forEach((tab, index) => {
			            if (tab.name === targetName) {
			              let nextTab = tabs[index + 1] || tabs[index - 1];
			              if (nextTab) {
			                activeName = nextTab.name;
			              }
			            }
			          });
			        }
			        this.editableTabsValue = activeName;
			        this.editableTabs = tabs.filter(tab => tab.name !== targetName);
			      }  
            },
            created: function() {
            	// 把第一个页面添加到缓存中.
                this.tabInfoObj.userList = '1';
            }
        };
        var Ctor = Vue.extend(Main)
        new Ctor().$mount('#app')


        // 设置iframe高度自适应.
        function changeFrameHeight() {
            var ifm = document.getElementsByClassName("iframepage");
            for (var i = ifm.length - 1; i >= 0; i--) {
                ifm[i].height = document.documentElement.clientHeight;
            };
        }
        window.onresize = function() {
            changeFrameHeight();
        }
        // 设置iframe高度自适应 end.
    </script>
</body>

</html>

roleList.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <title>权限列表</title>
</head>
<body>
    权限列表
</body>
</html>

userAdd.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <title>用户添加</title>
</head>
<body>
    用户添加
</body>
</html>

userList.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <title>用户列表</title>
</head>
<body>
    用户列表
</body>
</html>

roleAdd.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <title>权限添加</title>
</head>
<body>
    权限添加
</body>
</html>