Files
miniprogram-picker/src/index.js
qimengjie efc8fbd388 fix: #2
2019-07-26 10:29:51 +08:00

387 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 参考文档: https://github.com/IceApriler/miniprogram-picker */
function isExist(field) {
return field !== null && field !== undefined
}
Component({
externalClasses: ['picker-class'],
/**
* 组件的属性列表
*/
properties: {
// 初始化时,是否需要自动返回结果给开发者
autoSelect: {
type: Boolean,
value: false
},
// 源数组sourceData有几维Picker就可以有几阶
sourceData: {
type: Array,
value: [],
observer: 'sourceDataChange'
},
// 阶数
steps: {
type: Number,
value: 1
},
// 展示数据的字段名称
shownFieldName: {
type: String,
value: 'name'
},
// 子节点名称,
subsetFieldName: {
type: String,
value: 'subset'
},
// 其他需要返回的字段
otherNeedFieldsName: {
type: Array,
value: []
},
// 选择了第n列后是否将大于n的列的选择值自动初始化为0
initColumnSelectedIndex: {
type: Boolean,
value: false,
},
// 默认选中项的下标数组
defaultIndex: {
type: Array,
value: []
},
// 默认选中项的值数组
defaultValue: {
type: Array,
value: []
},
// 默认选中项的值数组的唯一字段,用来和源数组进行比对
defaultValueUniqueField: {
type: String,
value: ''
},
// 是否禁用
disabled: {
type: Boolean,
value: false,
}
},
/**
* 组件的初始数据
*/
data: {
// Picker当前所选择的索引数组 => 比如:[0, 0, 2]表示第一列选择第0项第二列选择第0项第三列选择第2项。
multiIndex: {
type: Array,
value: [],
},
// Picker当前所展示的数组 => 比如:[['河北', '山东'], ['石家庄', '保定'], ['桥西区', '裕华区', '长安区']]。
multiArray: {
type: Array,
value: [],
},
// 用户点击确定后,所选择的值数组 => 比如:
// [{name: '河北', id: '3110'}, {name: '石家庄', id: '3110xx'}, {name: '长安区', id: '3110xxx'}]。
selectedArray: {
type: Array,
value: [],
},
},
/**
* 组件的方法列表
*/
methods: {
/**
* 监听源数组更新变化
*
* @param {Array} newSourceData 源数组newSourceData有几维Picker就可以有几阶。
*/
sourceDataChange(newSourceData) {
const { shownFieldName, subsetFieldName, steps } = this.data
// 源数组更新则需要更新multiIndex、multiArray
const multiIndex = []
const multiArray = []
newSourceData = this.checkSourceData(newSourceData)
console.warn(newSourceData)
const defaultIndex = this.getDefaultIndex(newSourceData)
const handle = (source = [], columnIndex = 0) => {
// 当前遍历Picker的第columnIndex列
// 当columnIndex = 0时source表示sourceData其它则表示子集subset
const _multiArrayColumn0 = []
source.forEach((item, index) => {
if (columnIndex === 0) {
// newSourceData的第0维要单独处理最后unshift到multiArray中
_multiArrayColumn0.push(item[shownFieldName])
}
if (isExist(item[shownFieldName]) && index === (defaultIndex[columnIndex] || 0)) {
// 选中的索引和值默认取每列的第0个
multiIndex.push(index)
if (columnIndex < steps - 1) {
if (isExist(item[subsetFieldName])) {
// 开始处理下一维的数据
const _subsetArr = item[subsetFieldName].map(sub => sub[shownFieldName])
multiArray.push(_subsetArr)
handle(item[subsetFieldName], columnIndex + 1)
}
}
}
})
if (columnIndex === 0) {
multiArray.unshift(_multiArrayColumn0)
}
}
handle(newSourceData)
this.setData({
multiIndex,
multiArray
})
if (this.data.autoSelect) {
this.processData()
}
},
/**
* 获取默认值index
* @param {Array} newSourceData 源数组
*/
getDefaultIndex(newSourceData) {
const {
defaultIndex,
defaultValue,
defaultValueUniqueField,
steps,
subsetFieldName
} = this.data
if (defaultIndex.length) {
return defaultIndex
} else if (defaultValue.length) {
if (defaultValue.length !== steps) {
this.consoleError(new Error('你设置的"defaultValue"字段阶数与"steps"不符,请修改后再试。'))
return []
} else if (!defaultValueUniqueField) {
this.consoleError(new Error('你设置了"defaultValue"字段, 但是没有设置defaultValueUniqueField这将无法识别默认选项请补充后再试。'))
return []
} else {
defaultValue.forEach((def, key) => {
if (!def[defaultValueUniqueField]) {
this.consoleError(def, new Error(`"defaultValue"中第${key}项(从0开始计算)的对象中缺少"${defaultValueUniqueField}"字段`))
}
})
const _defaultIndex = []
const handle = (source = [], columnIndex = 0) => {
// 默认值
_defaultIndex[columnIndex] = 0
source.forEach((item, index) => {
if (!item[defaultValueUniqueField]) {
this.consoleError(item, new Error(`源数组第${columnIndex}维(从0开始计算)的对象中缺少"${defaultValueUniqueField}"字段`))
} else if (
(defaultValue[columnIndex][defaultValueUniqueField]) ===
(item[defaultValueUniqueField])
) {
_defaultIndex[columnIndex] = index
if (columnIndex < steps - 1) {
if (item[subsetFieldName]) {
// 开始处理下一维的数据
handle(item[subsetFieldName], columnIndex + 1)
}
}
}
})
}
handle(newSourceData)
return _defaultIndex
}
} else {
return []
}
},
/**
* 校验源数组是否正确
*
* @param {Array} sourceData 源数组
*/
checkSourceData(sourceData) {
const { shownFieldName, subsetFieldName, steps } = this.data
const handle = (source = [], columnIndex = 0) => {
// 当前遍历Picker的第columnIndex列
// 当columnIndex = 0时source表示sourceData其它则表示子集subset
if (!source.length) {
const temp = {}
temp[shownFieldName] = ''
temp[subsetFieldName] = []
source.push(temp)
}
return source.map((item) => {
if (!isExist(item[shownFieldName])) {
this.consoleError(item, new Error(`源数组第${columnIndex}维(从0开始计算)的对象中缺少"${shownFieldName}"字段`))
item[shownFieldName] = ''
}
// 有shownFieldName字段才会去遍历subsetFieldName字段
if (columnIndex < steps - 1) {
if (!isExist(item[subsetFieldName])) {
this.consoleError(item, new Error(`源数组第${columnIndex}维(从0开始计算)的对象中缺少"${subsetFieldName}"字段`))
}
// 开始处理下一维的数据
item[subsetFieldName] = handle(item[subsetFieldName], columnIndex + 1)
}
return item
})
}
return handle(sourceData)
},
/**
* 用户点击了确认。
*
* @param {Object} e 事件对象具体参考微信小程序api。
* @param {Array} e.detail.value 用户选择的下标数组。
*/
pickerChange(e) {
// console.log('picker发送选择改变携带值为', e.detail.value)
this.setData({
multiIndex: e.detail.value
})
this.processData()
},
/**
* 处理最终数据,将返回给开发者。
*
*/
processData() {
const {
sourceData,
shownFieldName,
subsetFieldName,
otherNeedFieldsName,
multiIndex
} = this.data
const selectedArray = []
const handle = (source = [], columnIndex = 0) => {
source.forEach((item, index) => {
if (index === multiIndex[columnIndex]) {
const selectedItem = {}
selectedItem[shownFieldName] = item[shownFieldName]
otherNeedFieldsName.forEach(key => {
selectedItem[key] = item[key]
})
selectedArray.push(selectedItem)
if (columnIndex < this.data.steps - 1) {
handle(item[subsetFieldName], columnIndex + 1)
}
}
})
}
handle(sourceData)
this.setData({
selectedArray
})
const detail = {
selectedIndex: this.data.multiIndex,
selectedArray: this.data.selectedArray
}
this.triggerEvent('change', detail)
},
/**
* 用户滚动了某一列。
*
* @param {Object} e 事件对象具体参考微信小程序api。
*/
pickerColumnChange(e) {
const {
shownFieldName,
subsetFieldName,
multiArray,
sourceData,
steps,
initColumnSelectedIndex
} = this.data
let { multiIndex } = this.data
const { column, value: changeIndex } = e.detail
// console.log(`修改了Picker的第${column}列(从0开始计算),选中了第${changeIndex}个值(从0开始计算)`)
// multiIndex变化了所以也要同步更新multiArray
multiIndex[column] = changeIndex
if (initColumnSelectedIndex) {
// 每次重置之后的index为0但是有bug待定。 => 经检查,是编辑器的问题,真机上是没有问题的。
const _multiIndex = multiIndex.map((item, index) => {
if (column >= index) {
return item
} else {
return 0
}
})
multiIndex = _multiIndex
}
const handle = (source = [], columnIndex = 0) => {
// 当前遍历第 columnIndex 列
source.forEach((item, index) => {
if (index === multiIndex[columnIndex]) {
if (columnIndex < steps - 1) {
if (!item[subsetFieldName]) {
item[subsetFieldName] = []
}
const multiArrayItem = item[subsetFieldName].map((sub) => sub[shownFieldName])
// 从第1列开始才有可能变化
multiArray[columnIndex + 1] = multiArrayItem
handle(item[subsetFieldName], columnIndex + 1)
}
}
})
}
handle(sourceData)
this.setData({
multiArray,
multiIndex,
})
this.triggerEvent('columnchange', e)
},
/**
* 用户点击了取消触发
* @param {Object} e 事件对象
*/
pickerCancel(e) {
this.triggerEvent('cancel', e)
},
/**
* 绑定console.error
* @param {...any} arg 打印参数
*/
consoleError(...arg) {
console.error(...arg)
console.warn('参考文档https://github.com/IceApriler/miniprogram-picker')
}
},
attached() {
if (this.data.autoSelect) {
this.processData()
}
}
})