美文网首页web 前端程序员Web前端之路
Vue踩坑之制作自己的组件工具

Vue踩坑之制作自己的组件工具

作者: 吃土的小此方 | 来源:发表于2017-12-03 23:02 被阅读644次

    Vue相信玩前端的小伙伴一定不会陌生。使用Vue最大的好处就是组件化,通过组件的复用可以大大减小我们日常开发的工作量。最近在工作中遇到这样的情况,于是便趁着周末正好将自己写的组件整理一下,做成更加通用的组件。

    先介绍一下背景。这段时间因为工作上的需要,要做后台管理系统,这类系统相信很多小伙伴也很熟悉。主要以表单的填写以及表格的展示、编辑为主。前端这一部分采用Vue全家桶,UI组件采用的是iview。UI组件大大提高了开发效率以及降低了开发的复杂度,但是组件也不是万能的,在面对实际的业务时,还是需要靠我们自己来制作组件。而这一次要制作的组件就是可以根据传入的数据不同,自动生成不同样式的表单以及表格了。(当然,利用的组件还是基于iview的)

    制作思路

    组件的本质就是拼装。通过拼装一个个原子组件,形成一个大组件最后来满足我们的实际业务需求。而在我这一次的需求中,原子组件已经有iview提供了。我要做的就是怎样来制作出想要的表单以及表格了。Vue提倡的思想就是数据驱动,所以在与其说是拼装组件,更不如说是设计一个合理的数据结构。

    首先是表单,通过v-for对数据进行循环,然后判断数据对应的表单组件类型就可以了。因此我们需要的数据结构就需要组件的类型、绑定的值、值对应的变量名(类似username:'用户'),而其他的属性就可以根据需要再做追加。

    再是表格,同样也是通过v-for对数据进行循环展示。在编辑时也需要切换到对应的组件。因此与表单的数据结构设计相同,我们需要组件的类型、绑定的值、值对应的变量名(类似username:'用户')。

    而其中的难点,无非就是在于行数和列数的计算上了。怎么灵活地变换行数,还是需要利用到v-for循环中的index来进行帮忙。相信这些,只要看过一眼代码就马上可以理解。

    // commonForm 代码
    <template>
      <div class="wrapper">
        <Form ref="commonForm" :label-width="100">
          <Row v-for="(rowNum, rowIndex) in _formRow" :key="rowIndex" :gutter="10">
            <Col v-for="(colNum, colIndex) in _colNum" :span="_formSpan" :key="colIndex">
            <FormItem v-if="(rowNum - 1) * _colNum + colIndex < _formLength" :label="_formLayout[(rowNum - 1) * _colNum + colIndex].title">
              <!-- 输入框 input -->
              <Input v-if="_formLayout[(rowNum - 1) * _colNum + colIndex].type === 'text'" type="text" v-model="_formLayout[(rowNum - 1) * _colNum + colIndex].value" :placeholder="_formLayout[(rowNum - 1) * _colNum + colIndex].placeholder"></Input>
              <!-- 输入框 textarea -->
              <Input v-else-if="_formLayout[(rowNum - 1) * _colNum + colIndex].type === 'textarea'" v-model="_formLayout[(rowNum - 1) * _colNum + colIndex].value" type="textarea" :placeholder="_formLayout[(rowNum - 1) * _colNum + colIndex].placeholder"></Input>
              <!-- 级联菜单 -->
              <Cascader v-else-if="_formLayout[(rowNum - 1) * _colNum + colIndex].type === 'cascader'" :data="_formLayout[(rowNum - 1) * _colNum + colIndex].data" v-model="_formLayout[(rowNum - 1) * _colNum + colIndex].value" :placeholder="_formLayout[(rowNum - 1) * _colNum + colIndex].placeholder"></Cascader>
              <!-- 下拉菜单 -->
              <Select v-else-if="_formLayout[(rowNum - 1) * _colNum + colIndex].type === 'selection'" v-model="_formLayout[(rowNum - 1) * _colNum + colIndex].value" :placeholder="_formLayout[(rowNum - 1) * _colNum + colIndex].placeholder">
                  <Option v-for="(opt, optIndex) in _formLayout[(rowNum - 1) * _colNum + colIndex].selection" :key="optIndex" :value="opt.value">{{opt.title}}</Option>
                </Select>
              <!-- 日期时间选择器 -->
              <DatePicker v-else-if="_formLayout[(rowNum - 1) * _colNum + colIndex].type==='datetime'" type="datetime" :placeholder="_formLayout[(rowNum - 1) * _colNum + colIndex].placeholder" :value="_formLayout[(rowNum - 1) * _colNum + colIndex].value" @on-change="(datetime)=>{changeDateTime(datetime, _formLayout[(rowNum - 1) * _colNum + colIndex] )}"></DatePicker>
              <!-- 日期时间围选择器 -->
              <DatePicker v-else-if="_formLayout[(rowNum - 1) * _colNum + colIndex].type==='datetime-range'" type="datetimerange" :placeholder="_formLayout[(rowNum - 1) * _colNum + colIndex].placeholder" :value="_formLayout[(rowNum - 1) * _colNum + colIndex].value" @on-change="(datetime)=>{changeDateTime(datetime, _formLayout[(rowNum - 1) * _colNum + colIndex] )}"></DatePicker>
              <!-- 日期范围选择器 -->
              <DatePicker v-else-if="_formLayout[(rowNum - 1) * _colNum + colIndex].type==='date-range'" type="daterange" placement="bottom-end" :placeholder="_formLayout[(rowNum - 1) * _colNum + colIndex].placeholder" :value="_formLayout[(rowNum - 1) * _colNum + colIndex].value" @on-change="(datetime)=>{changeDateTime(datetime, _formLayout[(rowNum - 1) * _colNum + colIndex] )}"></DatePicker>
              <!-- 时间范围选择器 -->
              <TimePicker v-else-if="_formLayout[(rowNum - 1) * _colNum + colIndex].type==='time-range'" format="HH’mm’ss" type="timerange" :placeholder="_formLayout[(rowNum - 1) * _colNum + colIndex].placeholder" :value="_formLayout[(rowNum - 1) * _colNum + colIndex].value" @on-change="(datetime)=>{changeDateTime(datetime, _formLayout[(rowNum - 1) * _colNum + colIndex] )}"></TimePicker>
            </FormItem>
            </Col>
          </Row>
          <Row>
            <slot name="btn-area" :form-content="_formLayout"></slot>
          </Row>
        </Form>
      </div>
    </template>
    
    // commonTable 代码
    <template>
      <div class="table-wrapper">
        <table class="content-table">
          <thead class="table-header" v-if="_detialData.tableHeader">
            <td :colspan="_colNum">
              <h3>{{_detialData.tableHeader}}</h3>
            </td>
          </thead>
          <tr class="table-row" v-for="(rownum, rowIndex) in _rowNum" :key="rowIndex">
            <td :class="['table-cell', {'table-bg-gray': (colnum % 2 !== 0)}]" v-for="(colnum, colIndex) in _colNum " :key="colIndex" v-if="">
              <h4 v-if="(colnum % 2 !== 0)">
                {{_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].title}}
              </h4>
              <div v-else>
                <p v-if="viewMode" class="content-text">
                  {{_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].value}}
                </p>
                <div v-else>
                  <!-- 输入框 -->
                  <Input v-if="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].edit_type === 'text'" v-model="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].value"></Input>
                  <!-- textarea -->
                  <Input v-else-if="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].edit_type === 'textarea'" type="textarea" v-model="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].value"></Input>
                  <!-- 日期时间选择器 -->
                  <Date-picker v-else-if="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].edit_type === 'datetime'" type="datetime" format="yyyy-MM-dd HH:mm" placement="top" placeholder="请选择日期..." :value="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].value" @on-change="(datetime)=>{changeDateTime(datetime, _dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)])}"></Date-picker>
                  <!-- 日期时间围选择器 -->
                  <DatePicker v-else-if="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].edit_type==='datetime-range'" type="datetimerange" :value="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].value" @on-change="(datetime)=>{changeDateTime(datetime, _dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)])}"></DatePicker>
                  <!-- 日期范围选择器 -->
                  <DatePicker v-else-if="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].edit_type==='date-range'" type="daterange" placement="bottom-end" :value="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].value" @on-change="(datetime)=>{changeDateTime(datetime, _dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)])}"></DatePicker>
                  <!-- 时间范围选择器 -->
                  <TimePicker v-else-if="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].edit_type==='time-range'" format="HH’mm’ss" type="timerange" :value="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].value" @on-change="(datetime)=>{changeDateTime(datetime, _dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)])}"></TimePicker>
                  <!-- 级联菜单 -->
                  <Cascader v-else-if="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].edit_type === 'cascader'" :data="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].data" v-model="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].value" :placeholder="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].placeholder"></Cascader>
                  <!-- 下拉菜单 -->
                  <Select v-else-if="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].edit_type === 'selection'" v-model="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].value" :placeholder="_dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].placeholder">
                    <Option v-for="(opt, optIndex) in _dataList[Math.floor(((rownum - 1) * _colNum + colIndex) / 2)].selection" :key="optIndex" :value="opt.value">{{opt.title}}</Option>
                  </Select>
                </div>
              </div>
            </td>
          </tr>
        </table>
        <slot name="btn-area" :detials="_detialData" :view-mode="_viewMode"></slot>
      </div>
    </template>
    

    项目地址:my-components 项目主页上也有相关的说明。这里就不做赘述了。

    说完了思路那么还是一如既往,先来看看效果直接感受一下吧。

    表单组件 commonForm

    当我们传入如下数据格式的数据后,就会生成这样的表单。

      mockForm: [
        { title: '输入框', name: 'text', type: "text", placeholder: '请输入输入框内容', value: '' },
        {
          title: '地区级联菜单', name: 'cascader', type: "cascader", placeholder: '请选择地区', data: [{
            value: 'shanghai', label: '上海', children: [
              { value: 'huangpu', label: '黄浦区' },
              { value: 'huangpu', label: '普陀区' },
              { value: 'huangpu', label: '静安区' },
              { value: 'huangpu', label: '闸北区' }
            ]
          }, {
            value: 'shanghai', label: '上海', children: [
              { value: 'huangpu', label: '黄浦区' },
              { value: 'huangpu', label: '普陀区' },
              { value: 'huangpu', label: '静安区' },
              { value: 'huangpu', label: '闸北区' }
            ]
          }],
          value: []
        },
        { title: '下拉框选项', name: 'select', type: "selection", selection: [{ title: '选项一', value: '选项一' }, { title: '选项二', value: '选项二' }], placeholder: '请选择选项', value: '' },
        { title: '日期时间', name: 'datetime', type: "datetime", placeholder: '请输入日期', value: '' },
        { title: '日期时间范围', name: 'datetimerange', type: "datetime-range", placeholder: '请输入日期', value: '' },
        { title: '日期范围', name: 'daterange', type: "date-range", placeholder: '请输入日期', value: '' },
        { title: '时间范围', name: 'timerange', type: "time-range", placeholder: '请输入时间范围', value: [] },
        { title: '输入框', name: 'textarea', type: "textarea", placeholder: '请输入客户的简介', value: '' },
      ],
    
    表单组件

    表格组件 commonTable

    当我们传入如下数据格式的数据后,就会生成这样的表格。点击编辑后会转为编辑模式。

      mockTable: {
        tableHeader: '表格标题',
        dataList: [
          { title: '输入框', name: 'input', value: '点击编辑后为输入框', edit_type: 'text' },
          {
            title: '级联菜单', name: 'cascader', value: ['shanghai', 'huangpu'], edit_type: 'cascader', data: [{
              value: 'shanghai', label: '上海', children: [
                { value: 'huangpu', label: '黄浦区' },
                { value: 'huangpu', label: '普陀区' },
                { value: 'huangpu', label: '静安区' },
                { value: 'huangpu', label: '闸北区' }
              ]
            }, {
              value: 'beijing', label: '北京', children: [
                { value: 'huangpu', label: '天安门' },
                { value: 'huangpu', label: '故宫' },
                { value: 'huangpu', label: '颐和园' },
                { value: 'huangpu', label: '紫禁城' }
              ]
            }]
          },
          { title: '下拉菜单', name: 'select', value: '是', edit_type: 'selection', selection: [{ title: '是', value: '是' }, { title: '否', value: '否' }] },
          { title: '日期时间', name: 'datetime', value: '2017-01-01 01:30', edit_type: 'datetime' },
          { title: '日期时间范围', name: 'datetimerange', value: '2017-01-01 01:30', edit_type: 'datetime-range' },
          { title: '日期范围', name: 'daterange', value: '2017-01-01 01:30', edit_type: 'date-range' },
          { title: '时间范围', name: 'timerange', value: '2017-01-01 01:30', edit_type: 'time-range' },      
          { title: '块级输入框', name: 'textarea', value: '这是块级输入框', edit_type: 'textarea' }
        ]
      }
    
    表格组件 视图模式 表格组件 编辑模式

    最后,现在这两个组件只是目前在我自己的项目中会比较常用。灵活性以及适用性还不够广泛(比如缺乏校验,表单的格式等),同时由于依赖iview,一些iview上组件的bug也同样会有。如果各位小伙伴有好的建议,也欢迎来交流。

    相关文章

      网友评论

        本文标题:Vue踩坑之制作自己的组件工具

        本文链接:https://www.haomeiwen.com/subject/uwwzbxtx.html