• 封装你的第一个vue组件


    故事背景:

    结束了四年的大学生活,阿花成功信心满满的投递出自己的简历,最终加入了A公司成为A公司的一员。

    今天是阿花第一天入职,在简单熟悉了一下项目代码后,项目经理给阿花分了一个利用Element UI 实现一个支持固定列、标签列、内容超出省略、内容操作等功能的表格

    整体ui展示图例

    在这里插入图片描述

    一、表格功能实现

    阿花接到需求后,当即利用Element UI着手实现了完整功能的table组件

    <template>
      <div>
        <el-table
        v-if="tableData.length != 0"
        :data="tableData"
        border
        stripe
        class="active_table"
        style="width:100%">
        <el-table-column
          fixed
          prop="active_name"
          label="活动名称"
          width="100">
        </el-table-column>
        <el-table-column
          prop="active_duty"
          label="活动负责人"
          width="100">
        </el-table-column>
        <el-table-column
          prop="club_name"
          label="申请社团"
          width="100">
        </el-table-column>
        <el-table-column
          prop="active_tem"
          label="负责人联系方式"
          width="100">
        </el-table-column>
        <el-table-column
          prop="active_date"
          label="活动时间"
          width="100">
        </el-table-column>
        <el-table-column
          prop="active_region"
          label="活动地点"
          width="100">
        </el-table-column>
        <el-table-column
          prop="active_num"
          label="活动人数"
          width="100">
        </el-table-column>
        <el-table-column
          prop="state"
          label="活动状态"
          width="100">
          <template slot-scope="scope">
            <el-tag
              :type="judge(scope.row.state)"
              disable-transitions>{{scope.row.state}}</el-tag>
          </template>
        </el-table-column>
        <el-table-column
          prop="active_desc"
          show-overflow-tooltip	
          label="活动简介"
          width="300">
        </el-table-column>
        <el-table-column
          fixed="right"
          label="操作"
          width="80">
          <template slot-scope="scope">
            <el-button @click="handleActive(scope.row.id)" type="text" size="small">查看详情</el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-empty description="暂无活动申请" v-else></el-empty>
      </div>
    
    
    </template>
    
    <script>
      export default {
        methods: {
          handleActive(id) {
             this.$router.push({ name: 'CheckActive', params: { id: id }})
          },
          // 初始化
          loadActiveApply () {
            this.$api.active
            .findAllActives(this.$store.state.userStore.userType)
            .then((result) => {
                if (result.msg == "ok") {
                  for(let item in result.data){
                    result.data[item].active_date = result.data[item].active_date.split("T")[0]
                  }
                  this.tableData = result.data
                }
            })
          },
          judge(state){
            if(state == '审核通过'){
              return 'success'
            }else if(state == '审核不通过'){
              return 'danger'
            }else {
              return 'paimary'
            }
          }
        },
        mounted(){
          this.loadActiveApply()
        },
        data() {
          return {
            tableData: []
          }
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114

    于是准备着手将代码提交到代码仓库,这时一位同事乔木看到阿花准备提交的代码,摇摇头说:“阿花,稍等一下,先不要提交”

    “怎么了?”

    “你为什么不把这个表格封装成一个组件呢”

    “需求上的功能我已经实现了阿”

    “假如之后再需要使用类似的table表格你每次都重新再写一份吗?这样代码太冗余了”

    阿花瞬间不知所措起来,乔木说的有道理,但是封装组件他并不是很熟悉。。。。

    组件封装的优势

    乔木见状连忙拍拍阿花的肩膀笑道:“那我给你讲讲在项目开发中组件封装的优越性吧”

    乔木道: “ 我们把⼀个功能的模板(template)封装 在⼀个.vue 文件中。把每个组件的逻辑和样式,也就是把HTML、JavaScript 和 CSS 封装在⼀起,这样在项目中任何地方都可以再次复用整个组件的代码,减少代码冗余”

    “ 可是,我这个代码的.vue文件如果其他地方需要复用,引入不也是可以的吗…”

    “是的,你上面写出的代码在功能上没有问题,在需求完全一致时也能复用,但是如果新的需求时不需要表格中有标签列或者列定位呢?”

    “ 那,那可能需要重新写功能匹配的表格代码了 ”

    “ 所以你就需要将其封装成一个组件,根据业务需求对组件进行定制 ”

    功能拆分

    “ 可是我该怎么做呢?” 阿花迫不及待的问道

    “ 那我就简单的给你分析一下怎么进行组件封装 ”

    “ 既然要根据业务需求对组件进行定制,那么在实现所有功能之后,肯定是将不同业务对应的功能抽离出来 ”

    实现上面的表格需求我们可以将单元格拆分为 :
    ① 常规列
    ② 溢出省略功能列
    ③ 标签功能列
    ④ 左表格固定列
    ⑤ 表格内容操作列

    “ 然后根据不同的需求展示不同列中的内容,那你知道怎么实现单元格内容按需求展示吗? ”

    “ v-if,v-if 可以条件性地渲染一块内容,判断指令返回truthy值时进行渲染。

    数据传递

    “ 是的,但是指令和渲染的数据从哪里来呢? ”

    “ 当然是从父组件那里传过去了,父组件根据需求给子组件传递对应的数据,子组件对收到的数据进行个性化展示。 ”,阿花激动道

    “ 那我考考你,你还记得父子组件之间传递数据有什么方法吗? ” ,乔木问

    “ 记得记得 ,父传子用prop、子传父用emit、多组件共享数据用全局事件总线、消息的订阅与发布、vuex状态管理库”

    “ 是的,那这种情况下我们使用什么方法传递数据比较合适? ”

    “ prop,组件的封装然后复用 数据流是父 ==> 子,在子组件指定接收的验证需求,然后父组件传递符合验证需求的数据 ”

    “ 没错,如果需要父子组件交互还会使用到emit自定义事件,但是我们这个需求不需要了,那现在你看看能不能优化一下你上面的代码 ”

    于是阿花拍了一下脑袋,在乔木的指教下写出了如下代码

    子组件

    <template>
      <div>
        <el-table
          :v-loading="loading"
          v-if="tableData.length != 0"
          :data="tableData"
          border
          stripe
          class="active_table"
          style="width: 100%"
          max-height="600"
        >
          <!-- 左侧固定列名 -->
          <el-table-column
            v-if="fixedLeft"
            :prop="fixedLeftName"
            :label="fixedLeftLable"
            fixed
            min-width="100"
          ></el-table-column>
          <!-- 正常列 -->
          <el-table-column
            v-for="(value, name) in lableObject"
            :key="name"
            :prop="name"
            :label="value"
            fit="true"
            min-width="100"
          ></el-table-column>
          <!-- 标签列 -->
          <el-table-column v-if="tableTag" :prop="tagName" :label="tagLable" min-width="100" >
            <template slot-scope="scope">
              <el-tag :type="judge(scope.row[tagName])" disable-transitions>
                {{ scope.row[tagName] }}
              </el-tag>
            </template>
          </el-table-column>
    
          <!-- 超出省略列 -->
          <el-table-column
            v-if="overflowTooltip"
            :prop="overflowTooltipName"
            :label="overflowTooltipLable"
            :width="overflowTooltipWidth"
            show-overflow-tooltip
          ></el-table-column>
    
          <!-- 操作表格列 -->
          <el-table-column v-if="operateTable" fixed="right" :label="operateTable.lable" min-width="100">
            <template slot-scope="scope">
              <el-button @click="handleActive(scope.row.id)" type="text" size="small"
                >{{operateTable.value}}</el-button
              >
            </template>
          </el-table-column>
    
        </el-table>
        <el-empty :description="emptyTable" v-else></el-empty>
      </div>
    </template>
    
    <script>
    export default {
      props: {
        // 常规表格数据
        tableData: {
          type: Array,
          required: true,
        },
        // 表格数据对应的lable
        lableObject: {
          type: Object,
          required: true,
        },
        // 有表格操作时使用
        operateTable: {
          type: Object,
        },
        // 数据为空时展示的结果
        emptyTable: {
          type: String,
          default: "数据为空",
        },
        // 溢出省略列
        overflowTooltip: {
          type: Object,
        },
        // 左定位列
        fixedLeft: {
          type: Object,
        },
        // 标签列
        tableTag: {
          type: Object,
        }
      },
      methods: {
        judge(state) {
          if (state == `${this.tableTag.success}`) {
            return "success";
          } else if (state == `${this.tableTag.danger}`) {
            return "danger";
          } else {
            return "paimary";
          }
        },
        handleActive(id) {
          this.$router.push({ name: this.operateTable.url, params: { id: id }})
          this.loading = !this.loading
        },
        loadTable() {
          // 初始化标签列
          if(this.tableTag){
            this.tagName = Object.keys(this.tableTag)[0];
            this.tagLable = this.tableTag[this.tagName];
          }
          // 初始化fixed定位列
          if(this.fixedLeft){
            this.fixedLeftName = Object.keys(this.fixedLeft)[0];
            this.fixedLeftLable = this.fixedLeft[this.fixedLeftName];
          }
          // 初始化省略行列
          if(this.overflowTooltip){
              this.overflowTooltipName = Object.keys(this.overflowTooltip)[0];
              this.overflowTooltipLable = this.overflowTooltip[this.overflowTooltipName];
              this.overflowTooltipWidth = this.overflowTooltip.width
          }
        }
      },
      data() {
        return {
          tagName: "",
          tagLable: "",
          fixedLeftName: "",
          fixedLeftLable: "",
          overflowTooltipName:'',
          overflowTooltipLable:'',
          overflowTooltipWidth: '',
          loading: true
        }
      },
      mounted() {
        this.loadTable()
      }
    };
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146

    父组件

    <template>
      <div>
        <v-Table
          :tableData="tableData"
          :lableObject="lableObject"
          :operateTable="operateTable"
          :tableTag="tableTag"
          :fixedLeft="fixedLeft"
          :overflowTooltip="overflowTooltip"
          emptyTable="暂无活动申请"
        ></v-Table>
      </div>
    </template>
    
    <script>
    import vTable from "@/components/vTable.vue";
    
    export default {
      components: {
        vTable,
      },
      methods: {
      // 从后端获取表格信息的ajax请求
        loadActiveApply() {
          this.$api.active
            .findAllActives()
            .then((result) => {
              if (result.msg == "ok") {
                this.tableData = result.data;
              }
            });
        },
      },
      mounted() {
        this.loadActiveApply();
      },
      data() {
        return {
          tableData: [],
          // 表数据是通过后端返回的,如果返回的格式中lable和value的对应关系明确则 不需要lableObject的定义
          // 表数据(key)和表头(value)对应关系
          lableObject: {
            active_duty: "活动负责人",
            club_name: "申请社团",
            active_tem: "联系方式",
            active_date: "活动时间",
            active_region: "活动地点",
            active_num: "活动人数",
          },
          // 标签列的表头和标签状态
          tableTag: {
            state: "活动状态",
            success: "审核通过",
            danger: "审核不通过"
          },
          // 左fixed的列
          fixedLeft: {
            active_name: "活动名称",
          },
          // 溢出省略的列
          overflowTooltip: {
            active_desc: "活动简介",
            width: '300'
          },
          // 可以跳转的列
          operateTable: {
            lable: "操作",
            value: "查看详情",
            // 跳转路径
            url: 'CheckActive'
          },
    
        };
      },
    };
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    自此阿花的第一个组件封装算是基本完成了,接下来他的求职路上又会出现什么问题呢,让我们拭目以待吧。

  • 相关阅读:
    CRM帮助企业解决客户关系管理难题
    时隔一年后,35岁生日,恰逢720
    macbook 上的 redis.conf 在哪里
    【STM32】入门(九):HAL库学习
    C语言 总结const的用法
    windows配置FTP服务
    CF487C Prefix Product Sequence 题解
    [DOM]获取元素:根据ID、标签名、HTML5新增的方法、特殊元素获取
    rust打包编译为mac或者linux可执行文件,发送到别的电脑不能运行
    Qt5开发从入门到精通——第四篇(消息对话框类)
  • 原文地址:https://blog.csdn.net/aaahuahua/article/details/125134720