cascader 动态加载 回显_ElementUI cascader级联动态加载回显和搜索看这个就够了
這一篇是上一次討論cascader級聯(lián)動態(tài)加載回顯問題的延續(xù),文末有鏈接。
以下是思考和開發(fā)的過程,不感興趣可以直接看使用文檔。
https://github.com/zhuss/lazy-cascader?github.com為什么要再寫一篇呢?
當然不是思想覺悟有多高,還不是因為產(chǎn)品提的需求,產(chǎn)品哭哭唧唧的說:“類目太多啦,我要有一個搜索的功能?!?/p>
一開始我是拒絕的,畢竟上一次為了解決回顯問題,耗費了一波本來就不多的頭發(fā),可是后來想一想,都是打工人,打工人和打工人應該是相親相愛的一家人。
既然接下這個鍋,那就想辦法解決吧。
最開始,延續(xù)的原有的思路,既然回顯可以,那搜索應該也不在話下,所以就埋頭去研究官方文檔,然后發(fā)現(xiàn)了 filter-method 和 before-filter 這兩個方法。
嘗試過之后就pass掉了filter-method,這個只是在選擇的時候判斷節(jié)點是否匹配,不適合動態(tài)請求和處理數(shù)據(jù)。
而 before-filter 方法是可以的,而且文檔寫的比較明確。
篩選之前的鉤子,參數(shù)為輸入的值,若返回 false 或者返回 Promise 且被 reject,則停止篩選
如果我們在方法中根據(jù)輸入值去請求后端接口,拿到一組備選項,然后根據(jù)拿到的備選項動態(tài)更新我們的options參數(shù),那么就可以在組件中篩選出我們想要的節(jié)點了吧,至于動態(tài)更新options的方法,可以同上一篇的回顯邏輯類似。
按照這個思路,同事在一番倒騰之后,確實可以實現(xiàn)動態(tài)搜索的需求。
但是,這樣處理是有不足的地方的。
1、產(chǎn)品設計的UI和ElementUI的組件并不一致,交互方式也不一樣。
2、在動態(tài)更新options參數(shù)的時候會動態(tài)請求很多節(jié)點數(shù)據(jù),而這些節(jié)點大部分都是不需要展示的。
所以,為了解決這兩個問題,我又陷入的沉思,甚至想不看ElementUI手動擼一個組件(想想而已)。
那既然原有組件在UI上沒辦滿足產(chǎn)品需求,那我們就自己寫這個UI吧,順著這個思路,那我們就需要用到一個關鍵的東西,級聯(lián)面板。
因為上圖UI的組成部分就這么幾個東西。
- 輸入框
- Popover 彈出框
- 搜索選擇框
- 級聯(lián)選擇面板
級聯(lián)選擇面板的值僅僅是一個數(shù)據(jù),也就是節(jié)點id的路徑數(shù)組,那我們就必須根據(jù)這個值,然后遍歷options得到對應的label的數(shù)組,顯示在輸入框中。
通過搜索拿到的數(shù)據(jù)也可以拿到一個節(jié)點的值。
那么我們只需要根據(jù)值的變化,動態(tài)請求需求展示的節(jié)點,獲取到對應的label即可。
于是就有了最緊要的一段代碼。
html
/**格式化id=>object */async getObject(id) {let options = this.options;let nameArray = [];for (let i = 0; i < id.length; i++) {let index = options.findIndex(item => {return item[this.props.value] == id[i];});nameArray.push(options[index][this.props.label]);if (i < id.length - 1 && options[index].children == undefined) {let list = new Promise(resolve => {this.props.lazyLoad(id[i], list => {resolve(list);});});this.$set(options[index], "children", await list);options = options[index].children;} else {options = options[index].children;}}return { value: id, label: nameArray };}
解決了這個問題,基本上就已經(jīng)實現(xiàn)了回顯了。
至于搜索,直接用的ElementUI的組件autocomplete就可以了,最后在仿照cascader的參數(shù)封裝這個組件的,把需要的參數(shù)暴露出去就可以了。
完整的代碼看下
<template><div class="lazy-cascader" :style="{ width: width }"><!-- 禁用狀態(tài) --><divv-if="disabled"class="el-input__inner lazy-cascader-input lazy-cascader-input-disabled"><span class="lazy-cascader-placeholder" v-show="placeholderVisible">{{ placeholder }}</span><div class="lazy-cascader-tags" v-if="props.multiple"><el-tagclass="lazy-cascader-tag"type="info"disable-transitionsv-for="(item, index) in labelArray":key="index"closable><span> {{ item.label.join(separator) }}</span></el-tag></div><div class="lazy-cascader-label" v-else><el-tooltipplacement="top-start":content="labelObject.label.join(separator)"><span>{{ labelObject.label.join(separator) }}</span></el-tooltip></div></div><!-- 禁用狀態(tài) --><!-- 可選狀態(tài) --><el-popover v-else trigger="click" placement="bottom-start" ref="popover"><!-- 搜索 --><div class="lazy-cascader-search"><el-autocomplete:style="{ width: width }"v-if="filterable"class="inline-input"prefix-icon="el-icon-search"label="name"v-model="keyword":fetch-suggestions="querySearch":trigger-on-focus="false"placeholder="請輸入"@select="handleSelect"><template slot-scope="{ item }"><div class="name">{{ item[props.label].join(separator) }}</div></template></el-autocomplete></div><!-- 搜索 --><!-- 級聯(lián)面板 --><div class="lazy-cascader-panel"><el-cascader-panelref="panel"v-model="current":options="options":props="currentProps"@change="change"></el-cascader-panel></div><!-- 級聯(lián)面板 --><!--內容區(qū)域--><divclass="el-input__inner lazy-cascader-input":class="disabled ? 'lazy-cascader-input-disabled' : ''"slot="reference"><span class="lazy-cascader-placeholder" v-show="placeholderVisible">{{ placeholder }}</span><div class="lazy-cascader-tags" v-if="props.multiple"><el-tagclass="lazy-cascader-tag"type="info"disable-transitionsv-for="(item, index) in labelArray":key="index"closable@close="handleClose(item)"><span> {{ item.label.join(separator) }}</span></el-tag></div><div class="lazy-cascader-label" v-else><el-tooltipplacement="top-start":content="labelObject.label.join(separator)"><span>{{ labelObject.label.join(separator) }}</span></el-tooltip></div></div><!--內容區(qū)域--></el-popover><!-- 可選狀態(tài) --></div>
</template>js
export default {props: {value: {type: Array,default: () => {return [];}},separator: {type: String,default: " > "},placeholder: {type: String,default: "請選擇"},width: {type: String,default: "400px"},filterable: Boolean,disabled: Boolean,props: {type: Object,default: () => {return {};}}},data() {return {keyword: "",options: [],current: [],labelObject: { label: [], value: [] },labelArray: [],currentProps: {multiple: this.props.multiple,checkStrictly: this.props.checkStrictly,value: this.props.value,label: this.props.label,leaf: this.props.leaf,lazy: true,lazyLoad: this.lazyLoad}};},computed: {placeholderVisible() {if (this.current) {return this.current.length == 0;} else {return true;}}},watch: {current() {this.getLabelArray();},value(v) {this.current = v;}},created() {this.initOptions();},methods: {//搜索querySearch(query, callback) {this.props.lazySearch(query, list => {callback(list);});},//選中搜索下拉搜索項handleSelect(item) {if (this.props.multiple) {let index = this.current.findIndex(obj => {return obj.join() == item[this.props.value].join();});if (index == -1) {this.current.push(item[this.props.value]);this.$emit("change", this.current);}} else {//選中下拉選變更值if (this.current == null ||item[this.props.value].join() !== this.current.join()) {this.current = item[this.props.value];this.$emit("change", this.current);}}this.keyword = "";},//初始化數(shù)據(jù)async initOptions() {this.props.lazyLoad(0, list => {this.$set(this, "options", list);if (this.props.multiple) {this.current = [...this.value];} else {this.current = this.value;}});},async getLabelArray() {if (this.props.multiple) {let array = [];for (let i = 0; i < this.current.length; i++) {let obj = await this.getObject(this.current[i]);array.push(obj);}this.labelArray = array;this.$emit("input", this.current);if (!this.disabled) {this.$nextTick(() => {this.$refs.popover.updatePopper();});}} else {this.labelObject = await this.getObject(this.current || []);this.$emit("input", this.current);}},/**格式化id=>object */async getObject(id) {let options = this.options;let nameArray = [];for (let i = 0; i < id.length; i++) {let index = options.findIndex(item => {return item[this.props.value] == id[i];});nameArray.push(options[index][this.props.label]);if (i < id.length - 1 && options[index].children == undefined) {let list = new Promise(resolve => {this.props.lazyLoad(id[i], list => {resolve(list);});});this.$set(options[index], "children", await list);options = options[index].children;} else {options = options[index].children;}}return { value: id, label: nameArray };},//懶加載數(shù)據(jù)async lazyLoad(node, resolve) {let current = this.current;if (this.props.multiple) {current = [...this.current];}if (node.root) {resolve();} else if (node.data[this.props.leaf]) {resolve();} else if (node.data.children) {resolve();} else {this.props.lazyLoad(node.value, list => {node.data.children = list;if (this.props.multiple) {this.current = current;}resolve(list);});}},//刪除多選值/**刪除**/handleClose(item) {let index = this.current.findIndex(obj => {return obj.join() == item.value.join();});if (index > -1) {let node = this.$refs.panel.getCheckedNodes().find(n => {return n.value == this.current[index][this.current[index].length - 1];});if (node) {node.checked = false;}this.current.splice(index, 1);this.$emit("change", this.current);}},change() {this.$emit("change", this.current);}}
};
css
.lazy-cascader {display: inline-block;width: 300px;.lazy-cascader-input {width: 100%;background: #fff;height: auto;min-height: 36px;padding: 5px;line-height: 1;cursor: pointer;> .lazy-cascader-placeholder {padding: 0 2px;line-height: 28px;color: #999;font-size: 14px;}> .lazy-cascader-label {padding: 0 2px;line-height: 28px;color: #606266;font-size: 14px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}}.lazy-cascader-input-disabled {background-color: #f5f7fa;border-color: #e4e7ed;color: #c0c4cc;cursor: not-allowed;> .lazy-cascader-label {color: #c0c4cc;}> .lazy-cascader-placeholder {color: #c0c4cc;}}
}
.lazy-cascader-tag {display: inline-flex;align-items: center;max-width: 100%;margin: 2px;text-overflow: ellipsis;background: #f0f2f5;> span {flex: 1;overflow: hidden;text-overflow: ellipsis;}> .el-icon-close {-webkit-box-flex: 0;-ms-flex: none;flex: none;background-color: #c0c4cc;color: #fff;}
}
.lazy-cascader-panel {margin-top: 10px;display: inline-block;
}其實,在自己封裝組件的時候,也會不自覺的學習或者掌握一些東西,還是比較有趣的。
比如,為了解決多選的時候Popover 彈出框錯位的問題,看了Element 的源碼,發(fā)現(xiàn)Popover組件有一個updatePopper方法。
而且,封裝組件需要考慮的問題比較多,倒不是說復雜,就是盡可能要全面。
好了,這一篇就寫到這里,至于適用性,我也不能保證,至少很好的解決了cascader級聯(lián)動態(tài)加載的不足,而且簡化了動態(tài)加載的方法,很方便的實現(xiàn)了回顯和搜索。
如果這正是你需要的,或者你預感自己會需要,可以先收藏,對于組件的不足之處也可以交流和討論,以方便我去優(yōu)化和改進。
總結
以上是生活随笔為你收集整理的cascader 动态加载 回显_ElementUI cascader级联动态加载回显和搜索看这个就够了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 菜鸟新手写的小说请大神围观!?
- 下一篇: 心音数据库_小V云端数据库 | 2020