• [Vue学习] 实现Tab选项卡效果, 动态增删Tab, 内容支持动态组件


    最近学习了一下Vue, 尝试实现一个自定义Tab组件, 效果如下:

    支持动态添加tab项, 内容支持放入动态组件, 模拟支持keep-alive

    效果图:

    目录结构:

    1. 使用vue-cli创建脚手架项目
    2. 在components中创建C1,C2,C3,MyTab四个自定义组件

    package.json

    {
      "name": "hello",
      "version": "0.1.0",
      "private": true,
      "scripts": {
        "serve": "vue-cli-service serve",
        "build": "vue-cli-service build",
        "lint": "vue-cli-service lint"
      },
      "dependencies": {
        "core-js": "^3.6.5",
        "vue": "^2.6.11"
      },
      "devDependencies": {
        "@vue/cli-plugin-babel": "~4.5.0",
        "@vue/cli-plugin-eslint": "~4.5.0",
        "@vue/cli-service": "~4.5.0",
        "babel-eslint": "^10.1.0",
        "eslint": "^6.7.2",
        "eslint-plugin-vue": "^6.2.2",
        "vue-template-compiler": "^2.6.11"
      },
      "eslintConfig": {
        "root": true,
        "env": {
          "node": true
        },
        "extends": [
          "plugin:vue/essential",
          "eslint:recommended"
        ],
        "parserOptions": {
          "parser": "babel-eslint"
        },
        "rules": {}
      },
      "browserslist": [
        "> 1%",
        "last 2 versions",
        "not dead"
      ]
    }
    package.json

    MyTab.vue

    <script>
    export default {
      name: 'MyTab',
      components: {},
      props: {
        tabs: {
          type: Array,
          required: true,
          default: function() {
            return []
          }
        },
        currentTab: {
          type: String,
          default: ''
        }
      },
      data() {
        return {
          current: this.currentTab || ''
        }
      },
      render: function(h) {
        var self = this
        return (
          <div class="tab-container">
            <ol class="title">
              {self.tabs.map(function(t) {
                return (
                  <li
                    class={{ active: self.isActive(t) }}
                    onclick={() => {
                      self.current = t.title
                    }}
                  >
                    {t.title}
                  </li>
                )
              })}
            </ol>

            <ol class="content">
              {self.tabs.map(function(t) {
                if (t.props && t.props.keepAlive) {
                  if (!self.isActive(t) && !t.isInit) {
                    return ''
                  } else {
                    t.isInit = '1'
                    return (
                      <keepAlive>
                        <li class={{ active: self.isActive(t), hide: !self.isActive(t) }}>
                          keep-live
                          <hr />
                          {h(t.com, { props: t.props })}
                        </li>
                      </keepAlive>
                    )
                  }
                } else {
                  return self.isActive(t) ? <li class={{ active: self.isActive(t), hide: !self.isActive(t) }}>{h(t.com, { props: t.props })}</li> : ''
                }
              })}
            </ol>
          </div>
        )
      },
      methods: {
        isActive(tab) {
          var cur = this.current
          if (cur == '') {
            cur = this.tabs[0].title
          }
          return cur == tab.title
        }
      },
      computed: {},
      created() {
        console.info('MyTab:created')
      },
      mounted() {
        console.info('MyTab:mounted')
      },
      updated() {
        console.info('MyTab:updated')
      },
      beforeDestroy() {
        console.info('MyTab:beforeDestroy')
      },
      destroyed() {
        console.info('MyTab:destroyed')
      },
      activated() {
        console.info('MyTab:activated')
      },
      deactivated() {
        console.info('MyTab:deactivated')
      }
    }
    </script>
    <style scoped>
    ol,
    li {
      padding: 0;
      margin: 0;
      list-style: none;
    }
    
    .tab-container {
      border: solid 0px #f60;
    }
    
    .tab-container .title li {
      padding: 5px 10px;
      background-color: cadetblue;
      display: inline-block;
      margin: 0 2px 1px 0;
      cursor: pointer;
    }
    
    .tab-container .title li:hover {
      background-color: rgb(72, 142, 145);
      color: #fff;
    }
    
    .tab-container .title li.active {
      background-color: rgb(43, 105, 107);
      color: #fff;
    }
    
    .tab-container .content li {
      padding: 5px 10px;
      border: solid 2px rgb(51, 141, 148);
    }
    </style>
    

    C1.vue

    <template>
      <p>{{ content }}</p>
    </template>
    <script>
    export default {
      props: {
        content: {
          type: String,
          default: "none",
        },
      },
      data() {
        return {}
      },
    }
    </script>
    <style scoped>
    ol li {
      border: solid 1px #abc;
      padding: 5px 10px;
    }
    </style>
    C1.vue

      

    C2.vue

    <template>
      <ol>
        <li v-for="tr in list" :key="tr">{{ tr }}</li>
      </ol>
    </template>
    <script>
    export default {
      props: ["list"],
      data1() {
        return {
          list: [1, 2, 3, 4, 5, 6],
        }
      },
    }
    </script>
    <style scoped>
    ol li {
      border: solid 1px #c0c0c0;
      margin: 2px;
      padding: 5px 10px;
    }
    </style>
    C2.vue

      

    C3.vue

    <template>
      <table>
        <tr v-for="(tr, idx) in table" :key="tr[0] + '_' + idx">
          <td v-for="(td, idx2) in tr" :key="td + '_' + idx + '_' + idx2">{{ td }}</td>
        </tr>
      </table>
    </template>
    <script>
    export default {
      props: ["table"],
      data1() {
        return {
          table: [
            [1, 2, 3, 4, 5, 6],
            [2, 4, 5, 6, 7, 8],
            [3, 4, 5, 6, 7, 8],
          ],
        }
      },
    }
    </script>
    <style scoped>
    table {
      border: solid 1px #c0c0c0;
    }
    
    table tr td {
      padding: 5px 10px;
      border: solid 1px #c0c0c0;
    }
    </style>
    C3.vue

    App.vue

    <template>
      <div id="app">
        <MyTab :tabs="tabs"></MyTab>
      </div>
    </template>
    
    <script>
    //import HelloWorld from './components/HelloWorld.vue'
    import MyTab from "@/components/MyTab.vue"
    
    export default {
      name: "App",
      components: { MyTab },
      data() {
        return {
          tabs: [
            {
              title: "HelloWorld",
              com: this.$Com("HelloWorld"),
            },
            {
              title: "Tab",
              com: this.$Com("TabExample"),
              props: {
                keepAlive: true,
              },
            },
          ],
        }
      },
      methods: {},
    }
    </script>
    
    <style>
    #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      color: #2c3e50;
      margin: 20px;
      font-size: 14px;
    }
    </style>

    TabExample.vue

    <template>
      <div>
        <div class="opt">
          <button @click="tabAdd">AddTab</button>
          <button @click="tabRemove">RemoveTab</button>
          <button>{{ index }}</button>
        </div>
        <MyTab :tabs="tabs" v-on:tabChange="tabChange"> </MyTab>
      </div>
    </template>
    <script>
    import MyTab from "@/components/MyTab.vue"
    
    export default {
      components: { MyTab },
      data() {
        return {
          index: 3,
          tabs: [
            {
              title: "textTab",
              com: this.$Com("example/C1"),
              props: {
                content: "一段文字而已..",
              },
            },
            {
              title: "listTab",
              com: this.$Com("example/C2"),
              props: {
                list: [1, 2, 3, 4, 5, 6],
                keepAlive: true,
              },
            },
            {
              title: "tableTab",
              com: this.$Com("example/C3"),
              props: {
                table: [
                  [1, 2, 3, 4, 5, 6],
                  [2, 4, 5, 6, 7, 8],
                  [3, 4, 5, 6, 7, 8],
                ],
                keepAlive: true,
              },
            },
          ],
        }
      },
      methods: {
        tabAdd() {
          this.tabs.push({
            title: "tableTab" + this.index++,
            com: this.$Com("example/C3"),
            props: {
              table: [
                [1 + this.index, 2, 3, 4, 5, 6],
                [7 + this.index, 8, 9, 10, 11, 12],
              ],
            },
          })
        },
        tabRemove() {
          if (this.index > 3) {
            this.tabs.splice(this.tabs.length - 1, 1)
            this.index--
            this.currentTab = this.tabs[0].title
          }
        },
        tabChange() {},
      },
      created() {
        console.info("TabExample:created")
      },
      mounted() {
        console.info("TabExample:mounted")
      },
      updated() {
        console.info("TabExample:updated")
      },
      beforeDestroy() {
        console.info("TabExample:beforeDestroy")
      },
      destroyed() {
        console.info("TabExample:destroyed")
      },
      activated() {
        console.info("TabExample:activated")
      },
      deactivated() {
        console.info("TabExample:deactivated")
      },
    }
    </script>
    
    <style scoped>
    .opt {
      margin: 10px 0;
    }
    .opt button {
      margin-right: 5px;
    }
    </style>

    main.js

    import Vue from "vue"
    import App from "./App.vue"
    
    Vue.config.productionTip = false
    
    Vue.prototype.$Com = function(fileName) {
      return Vue.component(fileName, () => import("./components/" + fileName + ".vue"))
    }
    
    new Vue({
      render: (h) => h(App),
    }).$mount("#app")

    参考1: https://cn.vuejs.org/v2/guide/components-dynamic-async.html
    参考2: https://cn.vuejs.org/v2/guide/render-function.html

  • 相关阅读:
    ES6函数剩余参数(Rest Parameters)
    ES6函数默认参数(Default Parameters)
    Mac os下换行符导致发布到npm里的命令行模块不能使用问题
    Mac OSX下卸载Nodejs
    ES6箭头函数(Arrow Functions)
    IE6-9不支持Textarea的maxlength属性
    日期相关的小函数汇总
    IE6-11使用location.href提交时的链接复制到firefox或chrome时出现乱码
    表驱动法
    PlaceHolder的两种实现方式
  • 原文地址:https://www.cnblogs.com/sanshizi/p/14691163.html
Copyright © 2020-2023  润新知