表單表格組件的封裝


一、開發背景

  2年前做了一個基於element-ui的layout組件發布到npm package上去,廣受大家的歡迎,下載量每周頗升。這個組件的好處在於開發者不用寫太多html代碼和邏輯,只要通過配置json的方式就能馬上生成后台,包括菜單欄和導航面包屑等一鍵生成,還能根據不同的需求做定制化后台界面,就像拼積木一樣,讓用戶只專注於內容核心代碼的開發。現在還沒做大范圍的推廣,如果我覺得它做得足夠好,我一定會放到npm上推薦給各位讀者使用哈。當初為了解答網友的疑問,我還專門建立了該組件的官網。logo看起來有沒有很熟悉?哈哈哈,它其實就是element-ui的logo進行改造的,意義就是告訴使用者,這是element-ui功能的組合加強版。

 

 

  為了迎合公司的OKR,我提出了對表單表格組件的封裝。為什么我會有這個想法?其實這個組件我很早就想做了,只是以前做的是基於UI層面的,近期我接手了公司的一個項目叫童畫,每天做的事情感覺就是復制粘貼,修改部分不同的功能和字段名稱。組件的意義在於可以在遇到同一類設計場景時,可以復用,從而減少設計的時間和形成產品的統一性。傳統的搜索表單不就是這樣嗎?上面是表單搜索字段,中間是搜索結果的表格,下面是搜索結果的分頁。把表單和表格組合起來的好處在於很多搜索字段都是基於表格組件的某些字段,那我根據search字段進行篩選不就可以了么,很久之前,我們總是在吐槽產品經理總是喜歡截圖現有功能,然后做字段修改,搞成原型扔給前端。如果前端也能像他這樣簡單,那該多好呀,類似這樣的想法油然而生。剛開始我只是為了方便我的工作,沒想到領導卻重視了起來,想把這個組件推廣給公司其他十幾個前端同事使用,於是乎,我便認真開搞了起來。有理論還不行,得有場景實踐,剛好公司的項目童畫有很多場景,我根據它里面的場景,做了很多功能的封裝和兼容。

  寫到這里,有人會說了,這不就是CRUD組件嗎?有這想法的話,說明你還太年輕和小看這個組件的功能了。傳統的CRUD組件靈活性不是很高,這個組件的好處是配置即可用,不用考慮其他搜索,翻頁,清空等各種邏輯,讓組件達到高度復用,封裝了場景的插槽類型,但為了防止翻車,我還是預留了變態需求的插槽。字段的使用更多采用elementUI的命名方式,讓使用者減少學習成本。這樣做的好處是什么呢?首先,前端再也不用寫頁面了,其次,對於比較規矩的搜索表單頁面,完全可以通過請求接口的形式交給后端來配置呈現頁面即可,根本沒有前端什么事了,前端的工作可以解放出來做更復雜的功能開發。

二、部分原理

  1. 搜索字段通過search=true進行篩選;
  2. 分頁通過請求的total總數進行分頁;
  3. Vue.prototype.$query接入請求。

三、組件實踐

為了使用方便,我把它做成了組件並放到了公司的私服上,接下來的工作就是寫文檔啦,以下是部分文檔的編寫,因為時間問題,沒來得及好好檢查,各位看官將就看一下就行啦。

3.1 版本升級

  • [0.0.0] 項目初始化
  • [0.0.1] 文檔編寫
  • [0.0.2] 屬性字段的修改

3.2 如何使用

確保安裝好elementUI

npm install element-ui --save

3.3 安裝

  • 先切回到公司內部npm源
    nrm use xxx
  • Windows系統安裝
    npm install search-table --save
  • Mac系統安裝
    sudo npm install search-table --save

3.4 更新

npm install search-table@latest

3.5 組件列表

   search-table 布局組件

3.6 引用

  • main.js

    import Vue from "vue"
    import ElementUI from 'element-ui'
    import 'element-ui/lib/theme-chalk/index.css'
    Vue.use(ElementUI)
    import SearchTable from "search-table"
    Vue.use(SearchTable)
    import axios from 'axios'
    const http = axios.create({
      baseURL: '/'
    })
    http.interceptors.request.use(config => {
      let userInfo = getCookie(xxx)
      if (userInfo) {
          userInfo = JSON.parse(decodeURIComponent(userInfo))
          config.headers['Access-Token'] = userInfo.accessToken
      }
      return config
    })
    Vue.prototype.$query = http
  • [page].vue

    ...
    <template>
      <SearchTable :options="options" ref="SearchTable" @on-operation="handleOperation" @on-selection="handleSelect">
      </SearchTable>
    </template>
    ...
    import options from './options'
    export default {
    ...
    data () {
      return {
        ...options
      }
    },
    methods: {
      showDialog ({id}) {
        this.$confirm('確認已聯系過本用戶嗎?', '提示', {
          confirmButtonText: '確定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(async () => {
          let res = await auditionStatus({id, status: 2})
          this.notifyMX(res, '處理成功')
          this.$refs.SearchTable.getTableData()
        }).catch(() => {})
      },
      handleOperation (val, ...arg) {
        switch (val) {
          case 'handle':
            this.showDialog(...arg)
            break
          default:
            break
        }
      }
    }
    ...
    }

    3.7 使用示例 【與[page].vue同級建立options.js文件】

    export default {
    options: {
      request: {
        api: '/student/web/student/enroll/list',
        method: 'GET',
        paramMap: {
          index: 'pageIndex',
          limit: 'pageSize'
        },
        resultMapping: {
          total: 'total',
          data: 'pageData'
        }
      },
      size: '',  // medium/mini/small, 默認medium
      labelWidth: 90,
      submitBtn: true, // 搜索按鈕,默認true,非必填
      submitText: '查詢', // 搜索按鈕的文字,默認查詢,非必填
      clearBtn: true, // 清除按鈕,默認true
      clearSize: 'mini', // medium/mini/small, 默認medium
      clearText: '清除', // 清除按鈕的文字,默認清除,非必填
      column: [
        {
           slotType: 'selection'
        },
        {
          prop: 'keyword',
          label: '學員/家長',
          search: true,
          hide: true
        },
        {
          prop: 'studentName',
          label: '學員'
        },
        {
          prop: 'telephone',
          label: '家長手機號',
          width: 130
        },
        {
          prop: 'type',
          label: '報名類型',
          width: 90,
          search: true,
          type: 'select',
          slot: true,
          slotType: 'text',
          slotArray: [{
            label: '新報',
            value: 1
          },
          {
            label: '續報',
            value: 2
          }],
          dicData: [{
            label: '新報',
            value: 1
          },
          {
            label: '續報',
            value: 2
          }]
        },
        {
          prop: 'courseFee',
          label: '繳費金額',
          slot: true,
          slotType: 'regEx',
          regEx: '&yen;{{courseFee/100}}'
        },
        {
          prop: 'payType',
          label: '支付方式',
          search: true,
          type: 'select',
          width: 100,
          dicData: [
            {
              label: '微信',
              value: '微信'
            },
            {
              label: '支付寶',
              value: '支付寶'
            },
            {
              label: '銀行卡轉賬',
              value: '銀行卡轉賬'
            },
            {
              label: '其它',
              value: '其它'
            }
          ]
        },
        {
          prop: 'courseCount',
          label: '報名課時'
        },
        {
          prop: 'followTeacher',
          label: '跟進人'
        },
        {
          prop: 'createTime',
          label: '報名時間',
          param: 'beginTime,endTime',
          format: 'yyyy 年 MM 月 dd 日',
          valueFormat: 'timestamp',
          search: true,
          width: 160,
          type: 'daterange'
        },
        {
          prop: 'auditor',
          label: '報名老師',
          search: true,
          param: 'teacherId', // 修正請求參數名
          type: 'select',
          dicData: [],
          dicUrl: '/org/web/org/user/list/teacher',
          dicMap: {
            label: 'userName',
            value: 'id'
          }
        },
        {
          prop: 'status',
          label: '報名狀態',
          slot: true,
          slotType: 'tag',
          width: 110,
          slotArray: [{
            type: 'warning',
            label: '待審核',
            value: 1
          },
          {
            type: 'danger',
            label: '審核不通過',
            value: 3
          },
          {
            type: 'success',
            label: '審核通過',
            value: 2
          }],
          dicUrl: ''
        },
        {
          prop: 'operation',
          label: '操作',
          width: 80,
          slot: true,
          slotType: 'operation',
          slotArray: [
            {
              label: '去審核',
              value: 'handle',
              filter: ({status}) => {
                return status === 1
              }
            }
          ]
        }
      ]
    }
    }

3.8 參數說明

Variables

參數 說明 類型 可選值 默認值
options 組件配置屬性,詳情見下面options屬性 Object - -

 

 

 

Events

事件名 說明 參數
on-search 點擊搜索后觸發該事件,非必填 -
on-page 切換頁面時觸發該事件,非必填 -
on-selection 當選擇項發生變化時會觸發該事件,非必填 -
on-select 當搜索選擇列表發生變化時會觸發該事件,非必填 -
on-operation 當"操作"(包括'switch-change')發生變化時會觸發該事件 屬性slotArray[{value}]

 

 

 

 

 

 

 

Methods [this.$refs.SearchTable]

方法名 說明 參數
setOptionData 獲取select選項數據接口后塞入的字段 {result: res.data,label: 'name',value: 'id',target: 'courseName'}
getTableData 重新請求該頁面事件 -

 

 

 

 

Options Attributes

參數 說明 類型 可選值/例子 默認值
request.url 請求地址,必填項 String /student/web/audition/list -
request.method 請求方法,可不填 String get/post get
request.paramMap.index 請求映射index參數 String pageIndex pageIndex
request.paramMap.size 請求映射size參數 String pageSize pageSize
request.resultMap.total 請求映射total參數 String total total
request.resultMap.data 請求映射data參數 String pageData pageData
size 表單組件的大小 String medium/mini/small mini
labelWidth label的寬度 String - 70
submitBtn 是否顯示搜索按鈕 Boolean true/false true
submitText 搜索文本 String - 查詢
clearBtn 是否顯示清空按鈕 Boolean true/false true
clearText 清空文本 String - 清除
column 表格配置屬性,詳情見下面Column屬性 Array - [ ]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Column Attributes

參數 說明 類型 可選值 默認值
prop 字段參數 String - -
param 當請求參數與字段不同時,可用該參數修正,多參用逗號隔開,非必填 String 'beginTime,endTime'/'teacherId' -
width th 寬度,非必填 String - -
format type=daterange時,'yyyy 年 MM 月 dd 日' String yyyy 年 MM 月 dd 日 -
valueFormat type=daterange時,值的顯示格式 String timestamp timestamp
label 字段名稱 String - -
hide 隱藏字段,僅搜索表單顯示,表格不顯示的字段 Boolean - -
search 是否是搜索字段 Boolean true/false false
searchType 搜索表單組件類型, 當search=true時才有效 String select/input/daterange input
dicData select選項的值 Array [{label:'',value:''}] []
dicUrl select選項的請求地址 String - -
dicMap.label label字段映射 String - -
dicMap.value value字段映射 String - -
slot table字段是否使用插槽 Boolean true/false false
slotType table插槽類型 String selection/tag/image/text/button -
slotArray 表格插槽顯示字段的各種值,對象中,可加filter字段方法過濾條件顯示 Array[Object] [{type:'success',label:'文本',value:'',filter:()=>{}}] -
searchRight 表格頭部(搜索)右側內容插槽顯示字段的各種值 Array[Object] [{type:'',label:'',value:'',filter:()=>{}}] -

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Scoped Slot

默認插槽, 用以處理復雜字段

參數 說明
列的名稱 列自定義列的內容,參數為{row,label,dic,$index}
searchRight 表格頭部(搜索)右側內容

 

 

 

 

 

 

四、最后感謝

最后,感謝同事世丞在字段命名上給了很多建議,同時也感謝領導給了很多刁難的意見,讓這個組件的功能越發強大,也讓開發者使用更方便,達到配置即可使用的地步。當然了,組件還需要更多場景的訓練才能真正實現各種功能的兼容。后期我希望可以放到layout官網上,供大家使用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM