如何优雅的绘制一棵省市区三级可选择的树?
開始
總結(jié)一下 開發(fā)過程中的思路想法 各位大佬們看看就好
首先你擁有的數(shù)據(jù)結(jié)構(gòu) 所有省市區(qū)的信息列表 以及已經(jīng)選中的信息 用的是element-ui的 el-tree
const cityStorage = {provinceList:[{id: 1, provinceId: "110000", name: "北京市"}],//所有省cityList:[{id: 1, cityId: "110100", name: "北京市", provinceId: "110000", zipCode: "102600"}],//所有市districtList:[{id: 1, districtId: "110101", name: "東城區(qū)", cityId: "110100"}],//所有區(qū)} const selectList = [{ provinceId: "110000",cityId: "110100",districtId: "110101"} ] // 所有選中的省市區(qū) ID 保存的時候也是這個格式按需渲染
首先 作為有相對要求的開發(fā)人員 不會考慮說 直接的去渲染出整個樹 那整個省市區(qū)加載的速度絕對會是感人的
那么 可行的解決方法是 一開始 只展示 所有省的信息 點擊展開 時再去渲染下一層 數(shù)據(jù)
這個對應關(guān)系 相對還容易找 每次點擊展開能獲得當前的層級和id 根據(jù)層級和id去對應的city和district中過濾就行
這里分享一個小技巧 不通過判斷的方式去對應 而是通過數(shù)據(jù)的方式
//level , id const levelConfig = {1: {idLabel: 'cityId',fetchLabel: 'cityList',perIdLabel: 'provinceId'},2: {idLabel: 'districtId',fetchLabel: 'districtList',perIdLabel: 'cityId'} }// 那么過濾就可以這么寫 cityStorage[levelConfig[level].fetchLabel].filter(item => item[levelConfig[level].perIdLabel] == id)獲取數(shù)據(jù) 然后加載對應下一層 一切到現(xiàn)在為止 都還可以
賦值渲染
再往下 如果我有初始數(shù)據(jù)呢?
在只展示省信息的情況下 結(jié)合前面給的數(shù)據(jù)格式 怎么展示 這個省是 全選 半選(表示省中有選擇的市或者區(qū)但沒選全) 和 不選 ?
第一 你需要設法知道省份滿足全選的條件
第二 你需要設法知道已經(jīng)選擇的情況
所以這個時候 需要做的 是計數(shù) 也就
遍歷一遍 cityStorage.provinceList 和 cityStorage.cityList
往Map中初始化 provinceId cityId 對應的計數(shù)
在遍歷 cityStorage.districtList 過程中往Map 對應provinceId cityId 增加計數(shù)
那么 有沒有什么別的基礎(chǔ)數(shù)據(jù) 是要在這個時候初始化的呢?
例如 只給你一個 districtId 你怎么才能最快的 找到他對應的 cityId 和 provinceId
或者 只給你一個 id 怎么最快找到他 對應的 name 呢
我們可以構(gòu)建一個Map來記錄我們需要的信息
districtId:cityId
cityId:provinceId
那么 我可以通過 Map[districtId] 找到cityId Map[Map[districtId]] 找到 provinceId
id 和 name 的對應關(guān)系 也是如此
而這些 可以在 計數(shù)的 過程進行
接著 通過已經(jīng)選擇地區(qū) 的 列表 獲取provinceId cityId的數(shù)據(jù) 的計數(shù)
兩份數(shù)據(jù)都有了
在渲染 省的時候 判斷 兩份Map中對應的計數(shù) 是否相同來渲染勾選
那半選的狀態(tài)怎么表示呢?el-tree并不支持設置半選的狀態(tài),必須是通過數(shù)據(jù)的形式呢?
通過模擬子節(jié)點的方法 當滿足不全選的情況 模擬兩個子節(jié)點
var children = [{id:provinceId '111',label:name,type:'none'},{id:provinceId '222',label:name,type:'none'} ]然后選中 其中一個 父元素自然就是半選狀態(tài)
保存提交
最后是保存提交時候的數(shù)據(jù)處理
由于模擬了半選狀態(tài) 所以最后獲取到的選中的數(shù)據(jù) 會有兩種
一種常規(guī)的6位 還是一種是模擬的d{6}xxx
而且如果 是出現(xiàn)這種d{6}xxx的數(shù)據(jù) 代表的是它所在的一級有些被選中了 而這些數(shù)據(jù)還沒有出現(xiàn)在 渲染樹中
這是就 需要有一個數(shù)據(jù)結(jié)構(gòu)記錄 這種情況
在已選擇的數(shù)據(jù) 初始化計數(shù)的時候 新構(gòu)建一個Map 存儲 provinceId cityId出現(xiàn)的數(shù)據(jù)的下標(我這邊保存的是districtId)
provinceId:[districtId,districtId,districtId]
cityId:[districtId,districtId,districtId]
至此 我們最后能拿到的 選中的 id 有 [310000,410000111,510100,610101]
此時這份數(shù)據(jù)中 有provinceId cityId districtId 以及 模擬的半選數(shù)據(jù) 怎么盡可能的優(yōu)雅的生成我們需要的格式呢?
首先是分類 可以發(fā)現(xiàn) xx0000表示的是省 xxx000 xxxx00 表示的是市
let _zeo = item.match(/(0 )$/g),ype = _zeo ? _zeo[0] : '0' switch (type) {case '0':districtList.push(item)break;case '00':case '000':cityList.push(item)break;case '0000':provinceList.push(item)break;}而這種410000111 數(shù)據(jù) 可以通過 先前的 Map 將數(shù)據(jù)并入 districtList 中
接著就是凈化數(shù)據(jù)
省選擇了 不需要市的所有id 市選擇了 不需要區(qū)的所有id
總結(jié) 判斷條件
var sub = item.substr(0,2) var re =new RegExp('\^' sub '\\d{4}'); // 省 // var sub = item.substr(0,4) // var re =new RegExp('\^' sub '\\d{2}'); // 市 cityList.filter(code=>!code.match(re))最后 provinceList cityList districtList 都是有效的選中值
遍歷一遍 cityStorage.districtList 將其中在provinceList cityList中存在id的數(shù)據(jù)并入 districtList中
此時 districtList 是最終有效的所有選中的 districtId值
此時 cityId = Map[districtId] province=Map[cityId]
寫這個需求的時候 頭真的很大 考慮的再清楚 寫著寫著 還是會有讓人抓狂的問題
展示合并
等等 你以為就這么完了 還有一個展示需求 數(shù)據(jù)結(jié)構(gòu)還是 保存的那份數(shù)據(jù)結(jié)構(gòu)
希望展示成
當選擇了一個省份全部地區(qū)的時候展示省份名稱
當選擇了一個省份下的部分市時展示市的名稱
當選擇了一個市下的所有地區(qū)時,只展示市的名稱
當選擇了一個市下的部分地區(qū)時,括號內(nèi)展示地區(qū)名稱
首先還是通過計數(shù) 獲取 已經(jīng)選中的有效的 provinceList cityList districtList
數(shù)據(jù)格式{provinceId: "110000",cityId: "110100",districtId: "110101"}
構(gòu)建一個存 selectNameList 用于存放已經(jīng)選中的 name name可以通過前面的Map[id]獲取
provinceList 選擇的省沒問題 直接推入
cityList 遍歷 構(gòu)建新的數(shù)據(jù) 格式 {provinceId: "110000",cityId: "110100",districtId: "1"}
并入 districtList 中
對districtList 根據(jù) cityId 排序
最后 遍歷 districtList 通過標記判斷每次是否是重復的cityId 設置數(shù)組 indeterminateNameList 記錄不是全選的市的name
不重復 將標記記為當前cityId 如果上一個元素的 districtCode 是 1 將 indeterminateNameList 存入 selectNameList 中
selectNameList.push('(' indeterminateNameList.join(',') ')')
districtId == 1 全選 存入 selectNameList
districtId != 1 不是全選 存入 indeterminateNameList = [cityId]
在這個過程中 有需要 還可以記錄 cityId 和 indeterminateNameList的管理關(guān)系
最終 selectNameList.join(',')
結(jié)語
程序猿真的不容易啊 遇到開發(fā)時間緊 雜七雜八事情多 還毫無頭緒的時候 壓力真的是大啊
更多專業(yè)前端知識,請上 【猿2048】www.mk2048.com
總結(jié)
以上是生活随笔為你收集整理的如何优雅的绘制一棵省市区三级可选择的树?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何使用JavaScript控制台改进工
- 下一篇: 不一样的ZTree,权限树.js插件