在平常開發中,咱們確定不止一次碰到重複的業務代碼,明明功能類似,但總沒思路去把它封裝成組件。關於封裝組件,但願這篇文章能帶給你們新的思路,去更高效的完成平常開發。(注:例子都是基於ElementUI, 但思路都是同樣的)
示例地址-> https://www.lyh.red/adminnode
代碼地址git
數據驅動是基於數據觸發的,在編寫業務的時候,只須要編寫好組件的dom結構,以後咱們即可以不用再去關心dom層,只須要關心數據就ok。
基於這種思路,那留給咱們的只有兩步,組件設計和數據設計。github
搜索欄配置以及生成效果
// 過濾相關配置 filterInfo: { query: { create_user: '', account: '', name: '' }, list: [ {type: 'input', label: '帳戶', value: 'account'}, {type: 'input', label: '用戶名', value: 'name'}, // {type: 'select', label: '建立人', value: 'create_user'}, // {type: 'date', label: '建立時間', value: 'create_time'}, {type: 'button', label: '搜索', btType: 'primary', icon: 'el-icon-search', event: 'search', show: true}, {type: 'button', label: '添加', btType: 'primary', icon: 'el-icon-plus', event: 'add', show: true} ] }
表格配置以及生成效果
// 表格相關 tableInfo: { refresh: false, initCurpage: false, data: [], fieldList: [ {label: '帳號', value: 'account'}, {label: '用戶名', value: 'name'}, {label: '性別', value: 'sex', width: 80, list: 'sexList'}, {label: '帳號類型', value: 'type', width: 100, list: 'accountTypeList'}, {label: '狀態', value: 'status', width: 90, list: 'statusList'}, {label: '建立人', value: 'create_user'}, {label: '建立時間', value: 'create_time', minWidth: 180}, {label: '更新人', value: 'update_user'}, {label: '更新時間', value: 'update_time', minWidth: 180} ], handle: { fixed: 'right', label: '操做', width: '180', btList: [ {label: '編輯', type: '', icon: 'el-icon-edit', event: 'update', show: true}, {label: '刪除', type: 'danger', icon: 'el-icon-delete', event: 'delete', show: true} ] } }
dom配置和完整頁面
<template> <div class="app-container"> <!-- 條件欄 --> <page-filter :query.sync="filterInfo.query" :filterList="filterInfo.list" :listTypeInfo="listTypeInfo" @handleClickBt="handleClickBt" @handleEvent="handleEvent"> </page-filter> <!-- 表格 --> <page-table :refresh="tableInfo.refresh" :initCurpage="tableInfo.initCurpage" :data.sync="tableInfo.data" :api="getListApi" :query="filterInfo.query" :fieldList="tableInfo.fieldList" :listTypeInfo="listTypeInfo" :handle="tableInfo.handle" @handleClickBt="handleClickBt" @handleEvent="handleEvent"> </page-table> <!-- 彈窗 --> <page-dialog :title="dialogInfo.title[dialogInfo.type]" :visible.sync="dialogInfo.visible" :width="dialogInfo.width" :btLoading="dialogInfo.btLoading" :btList="dialogInfo.btList" @handleClickBt="handleClickBt" @handleEvent="handleEvent"> <!-- form --> <page-form :refObj.sync="formInfo.ref" :data="formInfo.data" :fieldList="formInfo.fieldList" :rules="formInfo.rules" :labelWidth="formInfo.labelWidth" :listTypeInfo="listTypeInfo"> </page-form> </page-dialog> </div> </template>
參數設計
搜索參數query,好比要查詢的參數有帳號,名字。api
dom相關屬性設計
首先要考慮dom的類型,和顯示,這是基本的,還有擴展類型,好比事件能夠設置event屬性,是否顯示設置show屬性,這些是比較通用的。
而基於不一樣類型的dom,若是是input,select,datetime類型的dom,做爲一個承載數據的容器,則須要一個value屬性去和query中的屬性名對上,除此以外不一樣類型的dom還有不一樣的特定屬性,好比select須要提供對應的list,datetime須要相關的pickersOptions去限制時間範圍,若是是按鈕,好比el-button,則能夠設置icon,按鈕相關type。數據結構
最終實現:app
filterInfo: { query: { create_user: '', account: '', name: '' }, list: [ {type: 'input', label: '帳戶', value: 'account'}, {type: 'input', label: '用戶名', value: 'name'}, // {type: 'select', label: '建立人', value: 'create_user'}, // {type: 'date', label: '建立時間', value: 'create_time'}, {type: 'button', label: '搜索', btType: 'primary', icon: 'el-icon-search', event: 'search', show: true}, {type: 'button', label: '添加', btType: 'primary', icon: 'el-icon-plus', event: 'add', show: true} ] }
循環的dom列表dom
先考慮設計的這個dom須要什麼屬性
好比dom是el-input,一個輸入框,能夠設置是否禁止disabled,能夠設置是否可清空clearable,v-model要綁定的數據,設置dom的class名,設置dom綁定的事件。
好比dom是el-select, 除了上面這些屬性,咱們還須要這個元素可循環的list函數
最終dom結構爲:ui
<div class="filter-item" v-for="(item, index) in getConfigList()" :key="index"> <!-- <label class="filter-label" v-if="item.type !== 'button'">{{item.key}}</label> --> <!-- 輸入框 --> <el-input :class="`filter-${item.type}`" v-if="item.type === 'input'" :type="item.type" :disabled="item.disabled" :clearable="item.clearable || true" :placeholder="getPlaceholder(item)" @focus="handleEvent(item.event)" v-model="searchQuery[item.value]"> </el-input> <!-- 選擇框 --> <el-select :class="`filter-${item.type}`" v-if="item.type === 'select'" v-model="searchQuery[item.value]" :disabled="item.disabled" @change="handleEvent(item.even)" :clearable="item.clearable || true" :filterable="item.filterable || true" :placeholder="getPlaceholder(item)"> <el-option v-for="(item ,index) in listTypeInfo[item.list]" :key="index" :label="item.key" :value="item.value"></el-option> </el-select> <!-- 時間選擇框 --> <el-time-select :class="`filter-${item.type}`" v-if="item.type === 'time'" v-model="searchQuery[item.value]" :picker-options="item.TimePickerOptions" :clearable="item.clearable || true" :disabled="item.disabled" :placeholder="getPlaceholder(item)"> </el-time-select> <!-- 日期選擇框 --> <el-date-picker :class="`filter-${item.type}`" v-if="item.type === 'date'" v-model="searchQuery[item.value]" :picker-options="item.datePickerOptions || datePickerOptions" :type="item.dateType" :clearable="item.clearable || true" :disabled="item.disabled" @focus="handleEvent(item.event)" :placeholder="getPlaceholder(item)"> </el-date-picker> <!-- 按鈕 --> <el-button :class="`filter-${item.type}`" v-else-if="item.type === 'button'" v-waves :type="item.btType" :icon="item.icon" @click="handleClickBt(item.event)">{{item.label}}</el-button> </div> </div>
事件怎麼綁定在dom上
綁定事件,能夠在數據結構中給dom設置一個event屬性,好比說是查詢search,在dom結構中咱們能夠設計一箇中間層函數去處理,好比:this
<!-- 按鈕 --> <el-button :class="`filter-${item.type}`" v-else-if="item.type === 'button'" v-waves :type="item.btType" :icon="item.icon" @click="handleClickBt(item.event)">{{item.label}}</el-button>
中間層函數接收事件類型,而後統一處理。
組件中的函數,外部怎麼處理
我以爲組件的話,就承載一個去重複的做用,將因此重複的事情去除就能夠,像若是是表格,表單,功能欄相似這種可能顯示重複可是事件多變性的組件,咱們則能夠考慮將它們的事件派發到業務相關頁面處理,組件保持去除重複的工做,簡單幹淨明瞭就行了。
將事件所有交給父級處理:
// 綁定的相關事件 handleEvent (evnet) { this.$emit('handleEvent', evnet) }, // 派發按鈕點擊事件 handleClickBt (event, data) { this.$emit('handleClickBt', event, data) }
在後臺管理頁面樹狀組件用到次數實在太多了,靜態的樹數據加載,動態的樹數據懶加載,左鍵點擊事件,右鍵點擊事件等等,封裝以後,哼哼,誰用誰知道,一個字,爽。
其實就是將elementui中的大部分用上的tree屬性加上,而後再設計一部分讓組件更加好用的屬性,部分舉個例子。
屬性 | 類型 | 描述 |
---|---|---|
lazy | Boolean | 是否懶加載 |
lazyInfo | Array | 懶加載相關數據 |
loadInfo | Object | 正常相關數據 |
rightClick | Boolean | 是否須要右鍵菜單 |
rightMenuList | Array | 右鍵菜單列表 |
懶加載數據和正常加載數據結構的詳細設計
/** * 懶加載相關數據 * key -> 惟一標識 label -> 顯示 type -> 類型 api -> 接口 params -> 參數 leaf -> 是否葉子節點 */ lazyInfo: { type: Array, default: () => { return [ { key: 'id', label: 'name', type: '', api: () => {}, params: {key: '', value: '', type: 'url'}, // url/query->{data: [{key: '', value: '', default: ''}] type: 'query'} leaf: true } ] } }, /** * 正常加載相關 */ loadInfo: { key: 'id', label: 'name', api: () => {}, params: {key: '', value: '', type: 'url'} // url/query->{data: [{key: '', value: '', default: ''}] type: 'query'} },
事件處理一樣是須要派發到父級處理,以保證組件的可複用性,經過中間件將樹組件的相關事件派發搭到父級。
懶加載樹組件相關數據配置:
// 樹相關信息 treeInfo: { refresh: false, refreshLevel: 0, nodeKey: 'key', lazy: true, type: 0, // 省市區類型 lazyInfo: [ { key: 'id', label: 'name', type: 1, api: getAllApi, params: {key: 'pid', value: 1, type: 'url'} }, { key: 'id', label: 'name', type: 2, api: getAllApi, params: {key: 'pid', value: '', type: 'url'}, leaf: true } ], rightMenuList: [] },
懶加載樹dom結構:
<div class="page-tree" v-loading="treeLoading" @contextmenu.prevent="handleTreeClick"> <el-tree class="tree-component disabled-select" ref="TreeComponent" :show-checkbox="checkBox" :node-key="nodeKey" :data="treeData" :load="handleLoadNode" :lazy="lazy" :draggable="draggable" :allow-drop="handleDrop" :expand-on-click-node="false" :check-strictly="checkStrictly" :filter-node-method="filterNode" :default-checked-keys="defaultChecked" :default-expanded-keys="defaultExpanded" @node-click="handleClickLeft" @node-contextmenu="handleClickRight" @check="handleCheck" @check-change="handleCheck" @current-change="handleCheck" @node-expand="handleCheck" highlight-current :render-content="renderContent" :props="treeProps"> </el-tree> <!-- 右鍵菜單 --> <ul class='contextmenu' v-show="rightMenu.show" :style="{left: rightMenu.left +'px',top: rightMenu.top +'px'}"> <li v-for="(item, index) in rightMenu.list" :key="index" @click="handleRightEvent(item.type, item.data, item.node, item.vm)">{{item.name}}</li> </ul> </div>
實現效果:
本文之後臺管理頁面爲例,通常後臺管理頁面經常使用到的tree, table, form, dialog, 搜索欄已經所有作成了可複用的組件,只須要配置好相關數據,引入組件便可使用。 關於組件的相關邏輯,可能要在文章裏面一次性說清楚,仍是須要費很大的精力,不過但願數據驅動的思想可以讓以前沒有體會到這種開發樂趣的小夥伴們有到新的想法。