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

Vue相信玩前端的小伙伴一定不會(huì)陌生。使用Vue最大的好處就是組件化啼肩,通過組件的復(fù)用可以大大減小我們?nèi)粘i_發(fā)的工作量妇斤。最近在工作中遇到這樣的情況骆撇,于是便趁著周末正好將自己寫的組件整理一下来吩,做成更加通用的組件哆键。

先介紹一下背景囊卜。這段時(shí)間因?yàn)楣ぷ魃系男枰M龊笈_(tái)管理系統(tǒng)塞赂,這類系統(tǒng)相信很多小伙伴也很熟悉泪勒。主要以表單的填寫以及表格的展示、編輯為主宴猾。前端這一部分采用Vue全家桶圆存,UI組件采用的是iview。UI組件大大提高了開發(fā)效率以及降低了開發(fā)的復(fù)雜度仇哆,但是組件也不是萬能的沦辙,在面對實(shí)際的業(yè)務(wù)時(shí),還是需要靠我們自己來制作組件讹剔。而這一次要制作的組件就是可以根據(jù)傳入的數(shù)據(jù)不同油讯,自動(dòng)生成不同樣式的表單以及表格了。(當(dāng)然延欠,利用的組件還是基于iview的)

制作思路

組件的本質(zhì)就是拼裝陌兑。通過拼裝一個(gè)個(gè)原子組件,形成一個(gè)大組件最后來滿足我們的實(shí)際業(yè)務(wù)需求由捎。而在我這一次的需求中兔综,原子組件已經(jīng)有iview提供了。我要做的就是怎樣來制作出想要的表單以及表格了。Vue提倡的思想就是數(shù)據(jù)驅(qū)動(dòng)软驰,所以在與其說是拼裝組件涧窒,更不如說是設(shè)計(jì)一個(gè)合理的數(shù)據(jù)結(jié)構(gòu)。

首先是表單碌宴,通過v-for對數(shù)據(jù)進(jìn)行循環(huán)杀狡,然后判斷數(shù)據(jù)對應(yīng)的表單組件類型就可以了。因此我們需要的數(shù)據(jù)結(jié)構(gòu)就需要組件的類型贰镣、綁定的值呜象、值對應(yīng)的變量名(類似username:'用戶'),而其他的屬性就可以根據(jù)需要再做追加碑隆。

再是表格恭陡,同樣也是通過v-for對數(shù)據(jù)進(jìn)行循環(huán)展示。在編輯時(shí)也需要切換到對應(yīng)的組件上煤。因此與表單的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)相同休玩,我們需要組件的類型、綁定的值劫狠、值對應(yīng)的變量名(類似username:'用戶')拴疤。

而其中的難點(diǎn),無非就是在于行數(shù)和列數(shù)的計(jì)算上了独泞。怎么靈活地變換行數(shù)呐矾,還是需要利用到v-for循環(huán)中的index來進(jìn)行幫忙。相信這些懦砂,只要看過一眼代碼就馬上可以理解蜒犯。

// 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>
          <!-- 級(jí)聯(lián)菜單 -->
          <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>
          <!-- 日期時(shí)間選擇器 -->
          <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>
          <!-- 日期時(shí)間圍選擇器 -->
          <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>
          <!-- 時(shí)間范圍選擇器 -->
          <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>
              <!-- 日期時(shí)間選擇器 -->
              <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>
              <!-- 日期時(shí)間圍選擇器 -->
              <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>
              <!-- 時(shí)間范圍選擇器 -->
              <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>
              <!-- 級(jí)聯(lián)菜單 -->
              <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>

項(xiàng)目地址:my-components 項(xiàng)目主頁上也有相關(guān)的說明。這里就不做贅述了荞膘。

說完了思路那么還是一如既往罚随,先來看看效果直接感受一下吧。

表單組件 commonForm

當(dāng)我們傳入如下數(shù)據(jù)格式的數(shù)據(jù)后羽资,就會(huì)生成這樣的表單淘菩。

  mockForm: [
    { title: '輸入框', name: 'text', type: "text", placeholder: '請輸入輸入框內(nèi)容', value: '' },
    {
      title: '地區(qū)級(jí)聯(lián)菜單', name: 'cascader', type: "cascader", placeholder: '請選擇地區(qū)', data: [{
        value: 'shanghai', label: '上海', children: [
          { value: 'huangpu', label: '黃浦區(qū)' },
          { value: 'huangpu', label: '普陀區(qū)' },
          { value: 'huangpu', label: '靜安區(qū)' },
          { value: 'huangpu', label: '閘北區(qū)' }
        ]
      }, {
        value: 'shanghai', label: '上海', children: [
          { value: 'huangpu', label: '黃浦區(qū)' },
          { value: 'huangpu', label: '普陀區(qū)' },
          { value: 'huangpu', label: '靜安區(qū)' },
          { value: 'huangpu', label: '閘北區(qū)' }
        ]
      }],
      value: []
    },
    { title: '下拉框選項(xiàng)', name: 'select', type: "selection", selection: [{ title: '選項(xiàng)一', value: '選項(xiàng)一' }, { title: '選項(xiàng)二', value: '選項(xiàng)二' }], placeholder: '請選擇選項(xiàng)', value: '' },
    { title: '日期時(shí)間', name: 'datetime', type: "datetime", placeholder: '請輸入日期', value: '' },
    { title: '日期時(shí)間范圍', name: 'datetimerange', type: "datetime-range", placeholder: '請輸入日期', value: '' },
    { title: '日期范圍', name: 'daterange', type: "date-range", placeholder: '請輸入日期', value: '' },
    { title: '時(shí)間范圍', name: 'timerange', type: "time-range", placeholder: '請輸入時(shí)間范圍', value: [] },
    { title: '輸入框', name: 'textarea', type: "textarea", placeholder: '請輸入客戶的簡介', value: '' },
  ],
表單組件

表格組件 commonTable

當(dāng)我們傳入如下數(shù)據(jù)格式的數(shù)據(jù)后,就會(huì)生成這樣的表格屠升。點(diǎn)擊編輯后會(huì)轉(zhuǎn)為編輯模式瞄勾。

  mockTable: {
    tableHeader: '表格標(biāo)題',
    dataList: [
      { title: '輸入框', name: 'input', value: '點(diǎn)擊編輯后為輸入框', edit_type: 'text' },
      {
        title: '級(jí)聯(lián)菜單', name: 'cascader', value: ['shanghai', 'huangpu'], edit_type: 'cascader', data: [{
          value: 'shanghai', label: '上海', children: [
            { value: 'huangpu', label: '黃浦區(qū)' },
            { value: 'huangpu', label: '普陀區(qū)' },
            { value: 'huangpu', label: '靜安區(qū)' },
            { value: 'huangpu', label: '閘北區(qū)' }
          ]
        }, {
          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: '日期時(shí)間', name: 'datetime', value: '2017-01-01 01:30', edit_type: 'datetime' },
      { title: '日期時(shí)間范圍', 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: '時(shí)間范圍', name: 'timerange', value: '2017-01-01 01:30', edit_type: 'time-range' },      
      { title: '塊級(jí)輸入框', name: 'textarea', value: '這是塊級(jí)輸入框', edit_type: 'textarea' }
    ]
  }
表格組件 視圖模式
表格組件 編輯模式

最后,現(xiàn)在這兩個(gè)組件只是目前在我自己的項(xiàng)目中會(huì)比較常用弥激。靈活性以及適用性還不夠廣泛(比如缺乏校驗(yàn)进陡,表單的格式等),同時(shí)由于依賴iview微服,一些iview上組件的bug也同樣會(huì)有趾疚。如果各位小伙伴有好的建議,也歡迎來交流。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末糙麦,一起剝皮案震驚了整個(gè)濱河市辛孵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赡磅,老刑警劉巖魄缚,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異焚廊,居然都是意外死亡冶匹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門咆瘟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嚼隘,“玉大人,你說我怎么就攤上這事袒餐》捎迹” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵灸眼,是天一觀的道長卧檐。 經(jīng)常有香客問我,道長焰宣,這世上最難降的妖魔是什么霉囚? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮宛徊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逻澳。我一直安慰自己闸天,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布斜做。 她就那樣靜靜地躺著苞氮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瓤逼。 梳的紋絲不亂的頭發(fā)上笼吟,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天,我揣著相機(jī)與錄音霸旗,去河邊找鬼贷帮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛诱告,可吹牛的內(nèi)容都是我干的撵枢。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼锄禽!你這毒婦竟也來了潜必?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤沃但,失蹤者是張志新(化名)和其女友劉穎磁滚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宵晚,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡垂攘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坝疼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搜贤。...
    茶點(diǎn)故事閱讀 38,747評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖钝凶,靈堂內(nèi)的尸體忽然破棺而出仪芒,到底是詐尸還是另有隱情,我是刑警寧澤耕陷,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布掂名,位于F島的核電站,受9級(jí)特大地震影響哟沫,放射性物質(zhì)發(fā)生泄漏饺蔑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一嗜诀、第九天 我趴在偏房一處隱蔽的房頂上張望猾警。 院中可真熱鬧,春花似錦隆敢、人聲如沸发皿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽穴墅。三九已至,卻和暖如春温自,著一層夾襖步出監(jiān)牢的瞬間玄货,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工悼泌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留松捉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓馆里,卻偏偏與公主長得像惩坑,于是被迫代替她去往敵國和親掉盅。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容