原型上有個權限分配的功能;javascript
仔細翻了下對應的文檔(antd vue
),發現有那麼一個樹形控件,可是沒有上面部分全局控制的功能。html
那麼只能本身動手改造出一個符合業務的了,有興趣的看官能夠瞅瞅。vue
首先先梳理下要實現的功能點java
肯定了功能點就能夠開始搞起了,爲此我實現過三個版本;數組
初版是switch
開關控件來控制, 爲此樹組件和開關組是抽離出兩個獨立組件,發現很難避免一些極端的操做行爲;緩存
第二版用的.sync
+ switch
來實現,發現同樣難避免一些極端的操做行爲;服務器
第三版是改由按鈕組去實現,發現能夠很好的解決極端的狀況,能夠分解成三種狀況去實現。antd
TreePanel.vue
<template>
<div :bordered="false" :bodyStyle="{ padding: 0 }">
<a-row type="flex" justify="space-between" align="middle">
<a-col :span="20">
<a-radio-group :size="size" @change="onCheckedBtnGroupChange" v-model="allChecked" buttonStyle="solid">
<a-radio-button value="2" :disabled="true">局部選中</a-radio-button>
<a-radio-button value="1">全選</a-radio-button>
<a-radio-button value="0">全不選</a-radio-button>
</a-radio-group>
<a-divider type="vertical" />
<a-radio-group :size="size" @change="onExpandedBtnGroupChange" v-model="allExpanded" buttonStyle="solid">
<a-radio-button value="2" :disabled="true">局部展開</a-radio-button>
<a-radio-button value="1">展開全部</a-radio-button>
<a-radio-button value="0">摺疊全部</a-radio-button>
</a-radio-group>
</a-col>
<a-col>
<a-button type="primary" :size="size" href="javascript:;" @click="callBackData">
<span>提交改動</span>
</a-button>
</a-col>
</a-row>
<a-tree :expandedKeys="innerExpandedKeys" :treeData="treeData" checkable :checkedKeys="innerCheckedKeys" @expand="onExpand" @check="onCheck" />
</div>
</template>
<script> import { getTreeKey } from './utils'; export default { props: { size: { // 控件規格 type: String, default: 'small' }, checkedKeys: { // 傳遞選中的key type: Array, default: function() { return []; } }, expandedKeys: { // 傳遞須要展開的key type: Array, default: function() { return []; } }, treeData: { type: Array, default: function() { return [ { title: '0-0', key: '0-0', children: [ { title: '0-0-0', key: '0-0-0', children: [ { title: '0-0-0-0', key: '0-0-0-0' }, { title: '0-0-0-1', key: '0-0-0-1' }, { title: '0-0-0-2', key: '0-0-0-2' } ] }, { title: '0-0-1', key: '0-0-1', children: [ { title: '0-0-1-0', key: '0-0-1-0' }, { title: '0-0-1-1', key: '0-0-1-1' }, { title: '0-0-1-2', key: '0-0-1-2' } ] }, { title: '0-0-2', key: '0-0-2' } ] }, { title: '0-1', key: '0-1', children: [ { title: '0-1-0-0', key: '0-1-0-0' }, { title: '0-1-0-1', key: '0-1-0-1' }, { title: '0-1-0-2', key: '0-1-0-2' } ] }, { title: '0-2', key: '0-2' } ]; } } }, data() { return { allChecked: '', // 全選按鈕組 allExpanded: '', // 所有展開按鈕組 innerCheckedKeys: [], // 選中的值 innerExpandedKeys: [] // 展開的值 }; }, watch: { checkedKeys: { // 複製props immediate: true, deep: true, handler(newValue, oldValue) { if (newValue) { if (newValue.length === this.getTreeAllKey.length) { this.allChecked = '1'; } else if (newValue.length === 0) { this.allChecked = '0'; } else { this.allChecked = '2'; } this.innerCheckedKeys = newValue; } } }, expandedKeys: { // 複製props immediate: true, deep: true, handler(newValue, oldValue) { if (newValue) { if (newValue.length === this.getTreeAllGroupKey.length) { this.allExpanded = '1'; } else if (newValue.length === 0) { this.allExpanded = '0'; } else { this.allExpanded = '2'; } this.innerExpandedKeys = newValue; } } } }, computed: { switchDataSource() { // 勾選數據源 return [ { type: 'ALL_CHECKED', labelText: this.isAllChecked ? '全不選' : '全選', checked: this.isAllChecked }, { type: 'ALL_EXPAND', labelText: this.isAllExpanded ? '摺疊全部' : '展開全部', checked: this.isAllExpanded } ]; }, cacheEmitValue() { // 緩存響應的值 return { checkedKeys: this.innerCheckedKeys, expandedKeys: this.innerExpandedKeys }; }, getTreeAllKey() { // 獲取樹的全部key return getTreeKey(this.treeData); }, getTreeAllGroupKey() { // 獲取樹的全部組key return getTreeKey(this.treeData, true); }, isAllChecked() { // 是否所有勾選 return this.innerCheckedKeys.length === this.getTreeAllKey.length; }, isAllExpanded() { // 是否所有展開 return this.innerExpandedKeys.length === this.getTreeAllGroupKey.length; } }, methods: { onExpandedBtnGroupChange({ target: { value: expanedBtnGroupValue } }) { console.log('expanedBtnGroupValue: ', expanedBtnGroupValue); switch (expanedBtnGroupValue) { case '0': this.innerExpandedKeys = []; break; case '1': this.innerExpandedKeys = this.getTreeAllGroupKey; break; default: break; } }, onCheckedBtnGroupChange({ target: { value: checkedBtnGroupValue } }) { console.log('checkedBtnGroupValue: ', checkedBtnGroupValue); switch (checkedBtnGroupValue) { case '0': this.innerCheckedKeys = []; break; case '1': this.innerCheckedKeys = this.getTreeAllKey; break; default: break; } }, callBackData(emit) { // 響應改動的值 if (emit) { this.$emit('change', this.cacheEmitValue); } return false; }, onExpand(expandedKeys) { if (expandedKeys.length === this.getTreeAllGroupKey.length) { this.allExpanded = '1'; } else { this.allExpanded = '2'; } this.innerExpandedKeys = expandedKeys; }, onCheck(checkedKeys) { if (checkedKeys.length === this.getTreeAllKey.length) { this.allChecked = '1'; } else { this.allChecked = '2'; } this.innerCheckedKeys = checkedKeys; } } }; </script>
複製代碼
utils.js
:遞歸獲取須要的數據/** * @param arr 數組對象 * @param parent 是否只獲取父一層的屬性 * @description 遞歸獲取須要的數據 */
export function getTreeKey(arr, parent = false) {
const dataList = [];
const generateList = data => {
for (let i = 0; i < data.length; i++) {
const { key, title, children } = data[i];
if (!parent) dataList.push({ key, title });
if (Array.isArray(children) && children.length > 0) {
if (parent) dataList.push({ key, title });
generateList(children);
}
}
};
generateList(arr);
return dataList.map(item => item.key);
}
複製代碼
<tree-panel :treeData="treeData" :expandedKeys="treeExpandedKeys" :checkedKeys="treeCheckedKeys" @change="onTreePanelChange" />
複製代碼
props | 類型 | 介紹 |
---|---|---|
treeData |
數組對象 | 整個樹的數據 |
expandedKeys |
數組 | 展開的數組key |
checkedKeys |
數組 | 選中的數組key |
@change |
自定義事件 | 拿到返回值的回調函數 |
至此,符合咱們業務的一個樹組件封裝已經能夠正常使用。ide
有時候思路不通的時候,換個角度切入,發現會更美好;函數
有不對之處請留言,會及時修正,謝謝閱讀。