334 lines
8.8 KiB
Plaintext
334 lines
8.8 KiB
Plaintext
<script lang="ts">
|
|
// import pickerItem from './picker-item.uvue'
|
|
import { PropType } from "vue"
|
|
import { PICKER_ITEM_INFO, X_PICKER_X_ITEM } from "../../interface.uts"
|
|
|
|
/**
|
|
* @name 选择器容器 xPickerView
|
|
* @description 这是非官方pickerView封装,由于我是自行开发的,
|
|
因此比较好的能控制动画,回弹,阻尼,禁用项等
|
|
如果你不喜欢这个样式可以修改源码,比官方的更好更改样式。
|
|
组件采用数组id式选择,非索引。考虑到实际实用中多以id为交互提交数据。因此摒弃了传统的索引选项
|
|
* @page /pages/index/picker-view
|
|
* @category 表单组件
|
|
* @constant 平台兼容
|
|
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
|
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
|
|
*/
|
|
export default {
|
|
// components:{
|
|
// pickerItem
|
|
// },
|
|
data() {
|
|
return {
|
|
wrapTotalWidth: 0,
|
|
nowValue: [] as string[],
|
|
_modelValueIndex: [] as number[],
|
|
okNodeInfo: false,
|
|
tid: 0,
|
|
parentIndex: 0,
|
|
resizeTid: 0
|
|
}
|
|
},
|
|
emits: [
|
|
/**
|
|
* 选项变化时触发
|
|
* @param {string[]} ids - 当前选中项的id
|
|
*/
|
|
'change',
|
|
/**
|
|
* 等同v-model:model-str
|
|
* 只对外输出当前回选区的选中项的文本,不要外部改变此值。
|
|
*/
|
|
'update:modelStr',
|
|
|
|
'update:modelValue'
|
|
],
|
|
props: {
|
|
|
|
/**
|
|
* 数据项
|
|
* 格式类型为:PICKER_ITEM_INFO
|
|
*/
|
|
list: {
|
|
type: Array as PropType<PICKER_ITEM_INFO[]>,
|
|
default: () : PICKER_ITEM_INFO[] => [] as PICKER_ITEM_INFO[]
|
|
},
|
|
/**
|
|
* 数据项,这是内部使用的以提高数据的转换性能。如果你确定你提供的是这个类型,可以赋值此值,避免转换时间。
|
|
* 格式类型为:X_PICKER_X_ITEM
|
|
* 数据如果超过1mb不要去转换,最后直接从后台提供string然后直接JSON.getArray<X_PICKER_X_ITEM>(data)赋值到值。
|
|
* 这样性能在安卓上可以提高到好多。
|
|
*/
|
|
listPro: {
|
|
type: Array as PropType<X_PICKER_X_ITEM[]>,
|
|
default: () : X_PICKER_X_ITEM[] => [] as X_PICKER_X_ITEM[]
|
|
},
|
|
/**
|
|
* 当前选中项的id值
|
|
*/
|
|
modelValue: {
|
|
type: Array as PropType<string[]>,
|
|
default: () : string[] => [] as string[]
|
|
},
|
|
/**
|
|
* 当前选中项的标题文本组
|
|
*/
|
|
modelStr: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
/**
|
|
* 显示在顶部的单位名称
|
|
*/
|
|
cellUnits: {
|
|
type: Array as PropType<string[]>,
|
|
default: () : string[] => [] as string[]
|
|
},
|
|
unitsFontSize:{
|
|
type:String,
|
|
default:'12'
|
|
},
|
|
/**
|
|
* 项目的字体号大小
|
|
*/
|
|
fontSize: {
|
|
type: String,
|
|
default: "16"
|
|
},
|
|
// 滑动项目时停止的动画时间
|
|
duration: {
|
|
type: Number,
|
|
default: 300
|
|
},
|
|
// 摩擦系数,就是指当你用力滑动时,滑动的缓动距离效果
|
|
// 为了解决动画不停止无法选中的原生问题,我这个是重写的
|
|
// 动画只是展示效果,事实上是结果是先知的。
|
|
threshold: {
|
|
type: Number,
|
|
default: 0.9
|
|
},
|
|
/**
|
|
* 自动同步modelstr拼接时的符号.
|
|
*/
|
|
modelStrJoin:{
|
|
type:String,
|
|
default:","
|
|
},
|
|
},
|
|
|
|
|
|
mounted() {
|
|
this.oninitFun();
|
|
uni.$on('onResize',this.onResize)
|
|
},
|
|
watch: {
|
|
modelValue(newValue : string[]) {
|
|
if (newValue.join('') == this.nowValue.join('')) return;
|
|
this.nowValue = newValue;
|
|
this._modelValueIndex = this.chuliIndex(this.getIdeByindex());
|
|
this.setChangeStrvmodel()
|
|
}
|
|
},
|
|
beforeUnmount() {
|
|
clearTimeout(this.tid);
|
|
clearTimeout(this.resizeTid);
|
|
uni.$off('onResize',this.onResize)
|
|
},
|
|
computed: {
|
|
|
|
_cellUnits() : string[] {
|
|
return this.cellUnits as string[]
|
|
},
|
|
_list() : X_PICKER_X_ITEM[] {
|
|
if (this.listPro.length > 0) return this.listPro;
|
|
return this.normalizeList(this.list as PICKER_ITEM_INFO[])
|
|
},
|
|
_maxDeep() : number {
|
|
if (this._list.length == 0) return 0;
|
|
return this.getDepth(this._list);
|
|
},
|
|
|
|
_deepWidth() : number {
|
|
if (this._list.length == 0) return this.wrapTotalWidth;
|
|
const depth = this.getDepth(this._list);
|
|
return this.wrapTotalWidth / Math.max(1, depth)
|
|
},
|
|
|
|
},
|
|
methods: {
|
|
normalizeList(tree : PICKER_ITEM_INFO[]) : X_PICKER_X_ITEM[] {
|
|
let nowlist = [] as X_PICKER_X_ITEM[]
|
|
for (let i = 0; i < tree.length; i++) {
|
|
const node = tree[i];
|
|
const disabled = (node.disabled == null ? false : (node.disabled! as boolean));
|
|
const id = (node.id == null ? i.toString() : (node.id! as string));
|
|
const children = (node.children == null ? ([] as PICKER_ITEM_INFO[]) : (node.children! as PICKER_ITEM_INFO[]));
|
|
let item = {
|
|
id: id,
|
|
title: node.title,
|
|
disabled: disabled,
|
|
children: [] as X_PICKER_X_ITEM[]
|
|
} as X_PICKER_X_ITEM
|
|
if (children.length > 0) {
|
|
item.children = this.normalizeList(children)
|
|
}
|
|
nowlist.push(item)
|
|
}
|
|
return nowlist
|
|
},
|
|
getDepth(list : X_PICKER_X_ITEM[]) : number {
|
|
let deepIndex = 1;
|
|
const node = list[0];
|
|
if (node.children.length > 0) {
|
|
deepIndex += this.getDepth(node.children);
|
|
}
|
|
return deepIndex;
|
|
},
|
|
setChangeStrvmodel(){
|
|
let listitem = this.getModelStr()
|
|
let idvalue = [] as string[]
|
|
let strs = [] as string[]
|
|
listitem.forEach((el) => {
|
|
idvalue.push(el.id)
|
|
strs.push(el.title)
|
|
})
|
|
|
|
this.$emit('update:modelStr', strs.join(this.modelStrJoin))
|
|
},
|
|
oninitFun(){
|
|
this.nowValue = this.modelValue.slice(0)
|
|
this._modelValueIndex = this.chuliIndex(this.getIdeByindex());
|
|
this.setChangeStrvmodel()
|
|
this.getNodeInfo();
|
|
},
|
|
onResize(){
|
|
this.getNodeInfoDebounced()
|
|
},
|
|
change(ids : number[]) {
|
|
|
|
this._modelValueIndex = ids;
|
|
let listitem = this.getModelStr()
|
|
|
|
let idvalue = [] as string[]
|
|
let strs = [] as string[]
|
|
listitem.forEach((el) => {
|
|
idvalue.push(el.id)
|
|
strs.push(el.title)
|
|
})
|
|
|
|
/**
|
|
* 等同v-model
|
|
*/
|
|
this.$emit('update:modelValue', idvalue)
|
|
|
|
this.$emit('update:modelStr', strs.join(this.modelStrJoin))
|
|
if (idvalue.join('') == this.modelValue.join('')) return;
|
|
let t = this;
|
|
clearTimeout(this.tid);
|
|
t.tid = setTimeout(function () {
|
|
/**
|
|
* 选项变化时触发
|
|
* @param {string[]} ids 当前选中项的id
|
|
*/
|
|
t.$emit('change', idvalue)
|
|
|
|
}, 30);
|
|
},
|
|
chuliIndex(indexs : number[]) : number[] {
|
|
let ids = indexs.slice(0)
|
|
let val = this.nowValue.slice(0)
|
|
for (let i = 0; i < this._maxDeep; i++) {
|
|
if (i >= val.length) {
|
|
ids.push(0)
|
|
}
|
|
}
|
|
|
|
return ids;
|
|
},
|
|
getIdeByindex() : number[] {
|
|
let index = 0;
|
|
let val = this.nowValue.slice(0)
|
|
let indexs = [] as number[]
|
|
function getIndex(nodes : X_PICKER_X_ITEM[]) {
|
|
if (val.length <= index || val.length == 0) return;
|
|
let id = val[index]
|
|
let sindex = 0
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
let item = nodes[i]
|
|
if (item.id == id) {
|
|
sindex = i;
|
|
indexs.push(sindex)
|
|
if (item.children.length > 0) {
|
|
index += 1
|
|
getIndex(item.children)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
getIndex(this._list)
|
|
return indexs;
|
|
},
|
|
getModelStr() : X_PICKER_X_ITEM[] {
|
|
let ids = this._modelValueIndex.slice(0)
|
|
let index = 0;
|
|
let _this = this;
|
|
let selectedList = [] as X_PICKER_X_ITEM[]
|
|
|
|
function getStr(nodes : X_PICKER_X_ITEM[]) {
|
|
if (ids.length <= index || ids.length == 0) return;
|
|
let idx = ids[index]
|
|
idx = Math.max(0, Math.min(nodes.length - 1, idx));
|
|
let item = nodes[idx];
|
|
selectedList.push(item)
|
|
if (item.children.length > 0) {
|
|
index += 1
|
|
getStr(item.children)
|
|
}
|
|
}
|
|
getStr(this._list)
|
|
return selectedList
|
|
},
|
|
conutChangeEnd(n : number) {
|
|
|
|
},
|
|
getNodeInfo() {
|
|
uni.createSelectorQuery().in(this)
|
|
.select(".xPickerView")
|
|
.boundingClientRect().exec((ret) => {
|
|
let nodeinfo = ret[0] as NodeInfo
|
|
let w = nodeinfo.width!
|
|
if (w != this.wrapTotalWidth) {
|
|
this.wrapTotalWidth = w
|
|
}
|
|
if (!this.okNodeInfo) {
|
|
this.okNodeInfo = true;
|
|
}
|
|
})
|
|
}
|
|
,
|
|
getNodeInfoDebounced(){
|
|
let t = this;
|
|
clearTimeout(this.resizeTid)
|
|
this.resizeTid = setTimeout(function(){
|
|
t.getNodeInfo()
|
|
},100)
|
|
}
|
|
},
|
|
}
|
|
</script>
|
|
<template>
|
|
<view class="xPickerView" ref="xPickerView">
|
|
|
|
<x-picker-item :unitsFontSize="unitsFontSize" @countChange="conutChangeEnd" :duration="duration" :threshold="threshold" :font-size="fontSize"
|
|
:cellUnits="_cellUnits" v-if="okNodeInfo" @changeDeep="change" :selectedIndex="_modelValueIndex"
|
|
:parentIndex="parentIndex" :wrapWight="_deepWidth" :list="_list" class="xPickerViewItem"></x-picker-item>
|
|
</view>
|
|
</template>
|
|
<style scoped>
|
|
.xPickerView {
|
|
/* display: flex; */
|
|
|
|
}
|
|
</style> |