epmet 工作端 小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

320 lines
11 KiB

import baseComponent from '../helpers/baseComponent'
import classNames from '../helpers/classNames'
import styleToCssString from '../helpers/styleToCssString'
import arrayTreeFilter from '../helpers/arrayTreeFilter'
const WUX_CASCADER_VIEW = 'wux-cascader-view'
const defaultFieldNames = {
label: 'label',
value: 'value',
children: 'children',
disabled: 'disabled',
}
baseComponent({
externalClasses: ['wux-scroll-view-class'],
properties: {
prefixCls: {
type: String,
value: 'wux-cascader-view',
},
defaultValue: {
type: Array,
value: [],
},
value: {
type: Array,
value: [],
},
controlled: {
type: Boolean,
value: false,
},
options: {
type: Array,
value: [],
},
full: {
type: Boolean,
value: false,
},
placeholder: {
type: String,
value: '请选择',
},
height: {
type: [String, Number],
value: 'auto',
},
defaultFieldNames: {
type: Object,
value: defaultFieldNames,
},
skipAnimation: {
type: Boolean,
value: false,
},
},
data: {
activeOptions: [],
activeIndex: 0,
bodyStyle: '',
activeValue: [],
showOptions: [],
fieldNames: undefined,
scrollViewStyle: '',
},
computed: {
classes: ['prefixCls, full', function(prefixCls, full) {
const wrap = classNames(prefixCls)
const hd = `${prefixCls}__hd`
const bd = `${prefixCls}__bd`
const innerScroll = classNames(`${prefixCls}__inner-scroll`, {
[`${prefixCls}__inner-scroll--full`]: full,
})
const scrollView = `${prefixCls}__scroll-view`
const ft = `${prefixCls}__ft`
return {
wrap,
hd,
bd,
innerScroll,
scrollView,
ft,
}
}],
},
observers: {
value(newVal) {
if (this.data.controlled) {
this.setData({ activeValue: newVal })
this.getCurrentOptions(newVal)
}
},
options() {
this.getCurrentOptions(this.data.activeValue)
},
height(newVal) {
this.updateStyle(newVal)
},
},
methods: {
getActiveOptions(activeValue) {
const { options } = this.data
const value = this.getFieldName('value')
const childrenKeyName = this.getFieldName('children')
return arrayTreeFilter(options, (option, level) => option[value] === activeValue[level], { childrenKeyName })
},
getShowOptions(activeValue) {
const { options } = this.data
const children = this.getFieldName('children')
const result = this.getActiveOptions(activeValue).map((activeOption) => activeOption[children]).filter((activeOption) => !!activeOption)
return [options, ...result]
},
getMenus(activeValue = [], hasChildren) {
const { placeholder } = this.data
const activeOptions = this.getActiveOptions(activeValue)
if (hasChildren) {
const value = this.getFieldName('value')
const label = this.getFieldName('label')
activeOptions.push({
[value]: WUX_CASCADER_VIEW,
[label]: placeholder,
})
}
return activeOptions
},
getNextActiveValue(value, optionIndex) {
let { activeValue } = this.data
activeValue = activeValue.slice(0, optionIndex + 1)
activeValue[optionIndex] = value
return activeValue
},
updated(currentOptions, optionIndex, condition, callback) {
const value = this.getFieldName('value')
const children = this.getFieldName('children')
const hasChildren = currentOptions && currentOptions[children] && currentOptions[children].length > 0
const activeValue = this.getNextActiveValue(currentOptions[value], optionIndex)
const activeOptions = this.getMenus(activeValue, hasChildren)
const activeIndex = activeOptions.length - 1
const showOptions = this.getShowOptions(activeValue)
const props = {
activeValue,
activeOptions,
activeIndex,
showOptions,
}
// 判断 hasChildren 计算需要更新的数据
if (hasChildren || (activeValue.length === showOptions.length && (optionIndex = Math.max(0, optionIndex - 1)))) {
props.bodyStyle = this.getTransform(optionIndex + 1)
props.showOptions = showOptions
}
// 判断是否需要 setData 更新数据
if (condition) {
this.setCascaderView(props)
}
// 回调函数
if (typeof callback === 'function') {
callback.call(this, currentOptions, activeValue)
}
},
/**
* 更新级联数据
* @param {Array} activeValue 当前选中值
*/
getCurrentOptions(activeValue = this.data.activeValue) {
const optionIndex = Math.max(0, activeValue.length - 1)
const activeOptions = this.getActiveOptions(activeValue)
const currentOptions = activeOptions[optionIndex]
if (currentOptions) {
this.updated(currentOptions, optionIndex, true)
} else {
const value = this.getFieldName('value')
const label = this.getFieldName('label')
activeOptions.push({
[value]: WUX_CASCADER_VIEW,
[label]: this.data.placeholder,
})
const showOptions = this.getShowOptions(activeValue)
const activeIndex = activeOptions.length - 1
const props = {
showOptions,
activeOptions,
activeIndex,
bodyStyle: '',
}
this.setCascaderView(props)
}
},
setCascaderView(props) {
const { activeOptions, ...restProps } = props
this.setData({ activeOptions }, () => {
if (this.data.activeIndex !== restProps.activeIndex) {
this.triggerEvent('tabsChange', { index: restProps.activeIndex })
}
this.setData(restProps)
})
},
getTransform(index, animating = !this.data.skipAnimation) {
const pt = this.data.full ? 2 : 1
const i = this.data.full ? index : index - 1
const bodyStyle = styleToCssString({
transition: animating ? 'transform .3s' : 'none',
transform: `translate(${-50 * pt * Math.max(0, i)}%)`,
})
return bodyStyle
},
/**
* 点击菜单时的回调函数
*/
onTabsChange(e) {
const activeIndex = parseInt(e.detail.key)
const bodyStyle = this.getTransform(activeIndex)
if (
this.data.bodyStyle !== bodyStyle ||
this.data.activeIndex !== activeIndex
) {
this.setData({
bodyStyle,
activeIndex,
})
this.triggerEvent('tabsChange', { index: activeIndex })
}
},
/**
* 点击选项时的回调函数
*/
onItemSelect(e) {
const { optionIndex } = e.currentTarget.dataset
const { index } = e.detail
const { showOptions } = this.data
const item = showOptions[optionIndex][index]
// updated
this.updated(item, optionIndex, !this.data.controlled, this.onChange)
},
/**
* 选择完成时的回调函数
*/
onChange(currentOptions = {}, activeValue = []) {
const values = this.getValue(activeValue)
// 判断是否异步加载
if (currentOptions && currentOptions.isLeaf === false && !currentOptions.children) {
this.triggerEvent('change', { ...values })
this.triggerEvent('load', { value: values.value, options: values.options })
return
}
// 正常加载
this.triggerEvent('change', { ...values })
},
getValue(activeValue = this.data.activeValue) {
const optionIndex = Math.max(0, activeValue.length - 1)
const activeOptions = this.getActiveOptions(activeValue)
const currentOptions = activeOptions[optionIndex]
const valueKeyName = this.getFieldName('value')
const childrenKeyName = this.getFieldName('children')
const hasChildren = currentOptions && currentOptions[childrenKeyName] && currentOptions[childrenKeyName].length > 0
const options = activeOptions.filter((n) => n[valueKeyName] !== WUX_CASCADER_VIEW)
const value = options.map((n) => n[valueKeyName])
if (currentOptions && currentOptions.isLeaf === false && !currentOptions.children) {
return {
value,
options,
done: false,
}
}
return {
value,
options,
done: !hasChildren,
}
},
getFieldName(name) {
const { defaultFieldNames, fieldNames } = this.data
return typeof fieldNames !== 'undefined'
? fieldNames[name]
: defaultFieldNames[name]
},
updateStyle(height) {
const scrollViewStyle = styleToCssString({
height,
minHeight: height,
})
if (this.data.scrollViewStyle !== scrollViewStyle) {
this.setData({
scrollViewStyle,
})
}
},
},
attached() {
const { defaultValue, value, controlled, height } = this.data
const activeValue = controlled ? value : defaultValue
const fieldNames = Object.assign({}, defaultFieldNames, this.data.defaultFieldNames)
this.setData({ activeValue, fieldNames })
this.getCurrentOptions(activeValue)
this.updateStyle(height)
},
})