知識(shí)
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價(jià)值,我們?cè)谧非笃湟曈X表現(xiàn)的同時(shí),更側(cè)重于功能的便捷,營(yíng)銷的便利,運(yùn)營(yíng)的高效,讓網(wǎng)站成為營(yíng)銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏?jí)提供便捷的支持!
您當(dāng)前位置>首頁(yè) » 新聞資訊 » 小程序相關(guān) >
小程序picker地區(qū)級(jí)聯(lián)選擇的問題及解決方案
發(fā)表時(shí)間:2021-4-30
發(fā)布人:葵宇科技
瀏覽次數(shù):108
一、造輪子的原因
1.1 數(shù)據(jù)要自定義
微信官方的picker的region模式使用的是標(biāo)準(zhǔn)的國(guó)家行政區(qū)域數(shù)據(jù),而我們的場(chǎng)景有一些自設(shè)的區(qū)域要加入;也不可以自定久選擇級(jí)數(shù),只能選到縣/區(qū)級(jí)。
1.2 picker的兼容性并不好。
uni-app的picker組件,在小程序模式是使用各自的picker,H5則是uni-app自的picker組件。所以在各平臺(tái)中還是有差異的,在我們測(cè)試中微信的picker的mulitSelector模式,在列級(jí)聯(lián)滑動(dòng)中如果出現(xiàn)兩次列數(shù)組值length不一致時(shí),后綁定的選定索引時(shí)會(huì)無效,會(huì)自動(dòng)致為0,且后續(xù)觸發(fā)的change事件則仍是綁定索引,而在H5時(shí)不會(huì)。
1.3 picker是不適合異步加載數(shù)據(jù)
級(jí)聯(lián)就是要簡(jiǎn)便的控制后續(xù)列的變化,如1.2所示,綁定索引bug。而如果數(shù)據(jù)是異步加載,則更難于控制加載狀態(tài),特別是滑動(dòng)過快網(wǎng)絡(luò)不佳時(shí),很容易出現(xiàn)數(shù)據(jù)混亂。
1.4 picker作級(jí)聯(lián),不如京東級(jí)聯(lián)模式的體驗(yàn)好效率高。
如圖所示
二、上代碼
使用的了tui-drawer 、tui-loadmore等tui-xxx為uni-app第三方組件,具本使用參考官方文檔,或使用別的組件替代。regionApi為行政區(qū)域節(jié)點(diǎn)異步加載封裝,可根自己數(shù)據(jù)自行封裝。
<!--
* 行政區(qū)域選擇器
*
* @alphaiar
* 20210408 created.
-->
<template>
<view class="region-picker">
<input v-if="!visibleInputer" placeholder-class="placeholder" :placeholder="placeholder" :value="https://www.wxapp-union.com/selectorPath"
disabled @tap="onPopupToggle" />
<view v-else @tap="onPopupToggle">
<slot name="inputer"></slot>
</view>
<view v-if="errorMessage" class="messager">{{errorMessage}}</view>
<tui-drawer :visible="visibled" mode="bottom" @close="onPopupToggle">
<view class="header">
<text class="cancel" @tap="onPopupToggle">取消</text>
<text class="confirm" @tap="onConfirm">確認(rèn)</text>
</view>
<view class="tab-wrapper">
<template v-for="(lab,idx) in labels">
<label v-if="idx!==labelIndex" :key="idx" @tap="onLabelChange({index:idx})">
{{lab}}
</label>
<template v-else>
<label class="active">
{{lab}}
</label>
<iconfont class="indicator" name="arrow-down" />
</template>
</template>
</view>
<tui-loadmore v-if="loading" :index="3" type="primary" text="加載中..." />
<view v-else class="region-view">
<template v-for="(n,idx) in regions">
<label v-if="idx !== selectorIndexs[labelIndex]" @tap="onSelector(idx)" :key="idx">{{n}}</label>
<label v-else :key="idx">
<span class="selected">{{n}}</span>
</label>
</template>
</view>
<view v-if="errorTips" class="error-tips">
{{errorTips}}
</view>
</tui-drawer>
</view>
</template>
<script>
import utils from "../utils/utils.js";
import regionApi from "../apis/region.js";
export default {
name: 'regionPicker',
props: {
/**
* 選擇器區(qū)級(jí)
* 0-省
* 1-地市
* 2-縣區(qū)
* 3-鄉(xiāng)鎮(zhèn)
*/
selectorLevel: {
type: Number,
default: 1,
validator(val) {
return [0, 1, 2, 3].some(x => x === val);
}
},
/**
* 當(dāng)前選擇值
*/
value: {
type: Array,
default: null
},
/**
* 沒有值時(shí)的占位符
*/
placeholder: {
type: String,
default: '請(qǐng)選擇地區(qū)'
},
/**
* 表單驗(yàn)證錯(cuò)誤提示消息
*/
errorMessage: {
type: String,
default: null
},
/**
* 啟用自定義輸入框
*/
visibleInputer: {
type: Boolean,
default: false
}
},
watch: {
selectorLevel(val) {
this.$emit('input', null);
this.initialize();
},
value(val) {
this.initialize();
}
},
data() {
return {
visibled: false,
loading: false,
labels: ['請(qǐng)選擇'],
labelIndex: 0,
regions: [],
selectorIndexs: [],
selectorNodes: [],
errorTips: null
};
},
computed: {
selectorPath() {
let nodes = this.selectorNodes;
if (!nodes || nodes.length < 1)
return null;
let paths = nodes.map(x => x.name);
let path = paths.join(' / ');
return path;
}
},
mounted() {
const self = this;
regionApi.getNodes({
params: {
endCategory: 1
},
loading: false,
onLoading(ld) {
self.loading = ld;
},
showError: true,
callback(fkb) {
if (!fkb.success)
return;
let nodes = fkb.result;
self.__rawRegions = nodes;
if (!self.value || self.value.length < 1)
self.bindViews(nodes);
else
self.initialize();
}
});
},
methods: {
/**
* 初始化選擇器
*/
initialize() {
//初始化數(shù)據(jù)沒有執(zhí)行完成
if (!this.__rawRegions)
return;
this.labels = ['請(qǐng)選擇'];
this.labelIndex = 0;
this.selectorIndexs = [];
this.selectorNodes = [];
this.bindViews(this.__rawRegions);
//設(shè)定初始值
let values = this.value;
if (!values || values.length < 1)
return;
const self = this;
let prevs = this.__rawRegions;
let setValue = https://www.wxapp-union.com/function(idx) {
let nd = values[idx];
let about = false;
let exists = prevs.some((x, i) => {
if (nd.name !== x.name && nd.code !== x.code)
return false;
prevs = x.children || prevs;
//如果還有下級(jí),但又未加載子節(jié)點(diǎn),則先加載再來設(shè)定
if (!x.children && idx + 1 < values.length) {
self.getNextRegions(x, () => {
setValue(idx);
});
about = true;
return true;
}
self.selectorNodes.push({
category: x.category,
code: x.code,
name: x.name
});
self.onSelector(i);
return true;
});
if (about)
return;
if (exists && idx + 1 < values.length)
setValue(idx + 1);
};
setValue(0);
},
/**
* 將待選節(jié)點(diǎn)綁定至待選視圖
*
* @param {Array} nodes 要綁定的原始節(jié)點(diǎn)
*/
bindViews(nodes) {
this.regions = nodes.map(x => x.name);
},
/**
* 獲取下級(jí)節(jié)點(diǎn)
*
* @param {Object} prevNode 上級(jí)選中的節(jié)點(diǎn)
* @param {function} cb 加載完成后回調(diào)
*/
getNextRegions(prevNode, cb) {
const self = this;
regionApi.getChildren({
params: {
category: prevNode.category + 1,
prevCode: prevNode.code
},
loading: false,
onLoading(ld) {
self.loading = ld;
},
showError: true,
callback(fkb) {
if (!fkb.success)
return;
prevNode.children = fkb.result;
if (!cb)
self.bindViews(fkb.result);
else
cb();
}
});
},
/**
* 獲取指定列選擇的節(jié)點(diǎn)
*
* @param {Object} level 地區(qū)級(jí)別0-3
*/
getSelectorNode(level) {
let prevs = this.__rawRegions;
for (let i = 0; i < level; i++) {
let sidx = this.selectorIndexs[i];
if (!sidx)
return null;
prevs = prevs[sidx].children;
if (!prevs)
return null;
}
let cval = this.selectorIndexs[level];
let node = prevs[cval];
return node;
},
/**
* 切下至下一級(jí)區(qū)域選擇
*
* @param {Object} current 當(dāng)前選中級(jí)別0-3
*/
moveNextLevel(current) {
let node = this.getSelectorNode(current);
if (node == null)
return;
if (node.children)
this.bindViews(node.children);
else
this.getNextRegions(node);
},
onPopupToggle(e) {
this.visibled = !this.visibled;
},
onConfirm(e) {
if (this.selectorLevel + 1 > this.selectorIndexs.length) {
this.errorTips ='*請(qǐng)將地區(qū)選擇完整。';
return;
}
let nodes = [];
for (let i = 0; i < this.selectorIndexs.length; i++) {
let node = this.getSelectorNode(i);
nodes.push({
category: node.category,
code: node.code,
name: node.name
});
}
this.selectorNodes = nodes;
this.onPopupToggle();
this.$emit('input', nodes);
this.$emit('change', nodes);
},
onLabelChange(e) {
//加載中,禁止切換
if (this.loading)
return;
let idx = e.index;
this.labelIndex = idx;
if (idx > 0)
this.moveNextLevel(idx - 1);
else
this.bindViews(this.__rawRegions);
},
onSelector(idx) {
this.errorTips = null;
let labIdx = this.labelIndex;
//由于uni 對(duì)于數(shù)組的值監(jiān)聽不完善,只有復(fù)制數(shù)組更新才生效
let labs = utils.clone(this.labels);
labs[labIdx] = this.regions[idx];
this.labels = labs;
//原因上同
let idexs = utils.clone(this.selectorIndexs);
if (idexs.length <= labIdx)
idexs.push(idx);
else
idexs[labIdx] = idx;
this.selectorIndexs = idexs;
//有下級(jí),全清空
if (labIdx >= this.selectorLevel)
return;
this.selectorIndexs.splice(labIdx + 1, 4); //最大只有4級(jí)
this.labels.splice(labIdx + 1, 4); //最大只有4級(jí)
this.labels.push('請(qǐng)選擇');
this.labelIndex = labIdx + 1;
this.moveNextLevel(labIdx);
}
}
}
</script>
<style lang="scss">
.region-picker {
.header {
width: 100%;
box-sizing: border-box;
margin: 7.2463rpx 0;
line-height: $uni-font-size-base+ 7.2463rpx;
.cancel {
padding: 0 18.1159rpx;
float: left;
//color: $uni-text-color-grey;
}
.confirm {
padding: 0 18.1159rpx;
float: right;
color: $uni-color-primary;
}
text:hover {
background-color: $uni-bg-color-hover;
}
}
.tab-wrapper {
width: 100%;
margin-bottom: 28.9855rpx;
display: flex;
justify-content: center;
box-sizing: border-box;
label {
margin: 7.2463rpx 28.9855rpx;
padding: 7.2463rpx 0;
color: $uni-text-color;
border-bottom: solid 3.6231rpx transparent;
}
.active {
color: $uni-color-primary;
border-color: $uni-color-primary;
}
.indicator {
margin-left: -10px;
margin-top: 6px;
color: $uni-color-primary;
}
}
.region-view {
width: 100%;
display: flex;
flex-wrap: wrap;
padding: 7.2463rpx 14.4927rpx 28.9855rpx 14.4927rpx;
box-sizing: border-box;
label {
margin: 7.2463rpx 0;
width: 33%;
text-align: center;
color: $uni-text-color-grey;
text-overflow: ellipsis;
overflow: hidden;
}
.selected {
padding: 3.6231rpx 14.4927rpx;
background-color: $uni-color-light-primary;
color: #FFF;
border-radius: 10.8695rpx;
}
}
.error-tips {
width: 100%;
height: auto;
padding-bottom: 21.7391rpx;
text-align: center;
color: $uni-color-error;
font-size: $uni-font-size-sm;
}
}
</style>
行政區(qū)化節(jié)點(diǎn)數(shù)據(jù),來源國(guó)家統(tǒng)計(jì)局,到縣區(qū)級(jí)。
https://files.cnblogs.com/fil...
最終效果
相關(guān)案例查看更多
相關(guān)閱讀
- 區(qū)塊鏈
- 軟件定制公司
- 云南省建設(shè)廳網(wǎng)站官網(wǎng)
- 網(wǎng)站建設(shè)方案 doc
- 曲靖小程序開發(fā)
- 云南網(wǎng)站建設(shè)招商
- 報(bào)廢車管理系統(tǒng)
- 云南企業(yè)網(wǎng)站
- 網(wǎng)站優(yōu)化
- 云南etc小程序
- 汽車報(bào)廢軟件
- 旅游網(wǎng)站建設(shè)
- 網(wǎng)站建設(shè)哪家強(qiáng)
- 昆明小程序公司
- 高端網(wǎng)站建設(shè)公司
- 保山小程序開發(fā)
- flex
- 云南網(wǎng)站建設(shè)公司地址
- 模版消息
- 云南網(wǎng)站建設(shè)首頁(yè)
- web學(xué)習(xí)路線
- 云南小程序開發(fā)制作
- 網(wǎng)站優(yōu)化哪家好
- 云南網(wǎng)站建設(shè)
- 搜索引擎優(yōu)化
- 網(wǎng)站建設(shè)報(bào)價(jià)
- 網(wǎng)絡(luò)公司哪家好
- 云南網(wǎng)站建設(shè) 網(wǎng)絡(luò)服務(wù)
- 昆明網(wǎng)站開發(fā)
- 云南網(wǎng)絡(luò)公司