增加微信小程序版

This commit is contained in:
uiiang
2021-09-08 16:04:03 +08:00
parent e420fdf8fd
commit 1c29c3a737
207 changed files with 5936 additions and 0 deletions

View File

@@ -0,0 +1,311 @@
import baseComponent from '../helpers/baseComponent'
import classNames from '../helpers/classNames'
import arrayTreeFilter from '../helpers/arrayTreeFilter'
const WUX_CASCADER = 'wux-cascader'
const defaultFieldNames = {
label: 'label',
value: 'value',
children: 'children',
}
baseComponent({
externalClasses: ['wux-scroll-view-class'],
properties: {
prefixCls: {
type: String,
value: 'wux-cascader',
},
defaultValue: {
type: Array,
value: [],
},
value: {
type: Array,
value: [],
},
controlled: {
type: Boolean,
value: false,
},
title: {
type: String,
value: '',
},
options: {
type: Array,
value: [],
},
chooseTitle: {
type: String,
value: '请选择',
},
visible: {
type: Boolean,
value: false,
},
defaultFieldNames: {
type: Object,
value: defaultFieldNames,
},
},
data: {
activeOptions: [],
activeIndex: 0,
bodyStyle: '',
activeValue: [],
showOptions: [],
fieldNames: {},
},
computed: {
classes: ['prefixCls', function(prefixCls) {
const wrap = classNames(prefixCls)
const hd = `${prefixCls}__hd`
const title = `${prefixCls}__title`
const menus = `${prefixCls}__menus`
const menu = `${prefixCls}__menu`
const bd = `${prefixCls}__bd`
const inner = `${prefixCls}__inner`
const scrollView = `${prefixCls}__scroll-view`
const option = `${prefixCls}__option`
const item = `${prefixCls}__item`
const icon = `${prefixCls}__icon`
const ft = `${prefixCls}__ft`
return {
wrap,
hd,
title,
menus,
menu,
bd,
inner,
scrollView,
option,
item,
icon,
ft,
}
}],
},
observers: {
value(newVal) {
if (this.data.controlled) {
this.setData({ activeValue: newVal })
this.getCurrentOptions(newVal)
}
},
options() {
this.getCurrentOptions(this.data.activeValue)
},
},
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 { options, chooseTitle } = this.data
const activeOptions = this.getActiveOptions(activeValue)
if (hasChildren) {
const value = this.getFieldName('value')
const label = this.getFieldName('label')
activeOptions.push({
[value]: WUX_CASCADER,
[label]: chooseTitle,
})
}
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[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 params = {
activeValue,
activeOptions,
activeIndex,
showOptions,
}
// 判断 hasChildren 计算需要更新的数据
if (hasChildren || (activeValue.length === showOptions.length && (optionIndex = Math.max(0, optionIndex - 1)))) {
params.bodyStyle = `transform: translate(${-50 * optionIndex}%)`
params.showOptions = showOptions
}
// 判断是否需要 setData 更新数据
if (condition) {
this.setData(params)
}
// 回调函数
if (typeof callback === 'function') {
callback.call(this, currentOptions, activeOptions, !hasChildren)
}
},
/**
* 更新级联数据
* @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,
[label]: this.data.chooseTitle,
})
const showOptions = this.getShowOptions(activeValue)
const activeIndex = activeOptions.length - 1
const params = {
showOptions,
activeOptions,
activeIndex,
bodyStyle: '',
}
this.setData(params)
}
},
/**
* 点击菜单时的回调函数
*/
onMenuClick(e) {
const { menuIndex } = e.currentTarget.dataset
const index = menuIndex > 1 ? menuIndex - 1 : 0
const bodyStyle = `transform: translate(${-50 * index}%)`
this.setData({
bodyStyle,
activeIndex: menuIndex,
})
},
onItemDelete(e){
this.triggerEvent('close')
this.triggerEvent('delete')
},
/**
* 点击选项中的确认按钮的函数
*/
onItemCheck(e){
const { item, optionIndex } = e.currentTarget.dataset
// 判断是否禁用
if (!item || item.disabled) return
// updated
this.updated(item, optionIndex
, !this.data.controlled, this.onConfrim)
},
/**
* 点击选项时的回调函数
*/
onItemSelect(e) {
const { item, optionIndex } = e.currentTarget.dataset
// 判断是否禁用
if (!item || item.disabled) return
// updated
this.updated(item, optionIndex, !this.data.controlled, this.onChange)
},
/**
* 组件关闭时的回调函数
*/
onPopupClose() {
this.triggerEvent('close')
},
/**
* 选择完成时的回调函数
*/
onConfrim(currentOptions = {}, activeOptions = [], done = false) {
const options = activeOptions.filter((n) => n[this.getFieldName('value')] !== WUX_CASCADER)
const value = options.map((n) => n[this.getFieldName('value')])
// 判断是否异步加载
if (currentOptions.isLeaf === false && !currentOptions.children) {
this.emitEventClose({ value, options, done: false })
this.triggerEvent('load', { value, options })
return
}
// 正常加载
this.emitEventClose({ value, options, done })
},
/**
* 选择完成时的回调函数
*/
onChange(currentOptions = {}, activeOptions = [], done = false) {
const options = activeOptions.filter((n) => n[this.getFieldName('value')] !== WUX_CASCADER)
const value = options.map((n) => n[this.getFieldName('value')])
// 判断是否异步加载
if (currentOptions.isLeaf === false && !currentOptions.children) {
this.emitEvent({ value, options, done: false })
this.triggerEvent('load', { value, options })
return
}
// 正常加载
this.emitEvent({ value, options, done })
},
emitEvent(params = {}) {
// console.log('emitEvent change')
this.triggerEvent('change', params)
},
emitEventClose(params = {}) {
// console.log('emitEvent close')
this.triggerEvent('confrim', params)
// 当选择完成时关闭组件
if (params.done) {
this.onPopupClose()
}
},
getFieldName(name) {
return this.data.fieldNames[name]
},
},
attached() {
const { defaultValue, value, controlled } = this.data
const activeValue = controlled ? value : defaultValue
const fieldNames = Object.assign({}, defaultFieldNames, this.data.defaultFieldNames)
this.setData({ activeValue, fieldNames })
this.getCurrentOptions(activeValue)
},
})

View File

@@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"wux-popup": "../popup/index",
"wux-icon": "../icon/index",
"wux-button": "../button/index"
}
}

View File

@@ -0,0 +1,42 @@
<wux-popup position="bottom" visible="{{ visible }}" safeArea="bottom" bind:close="onPopupClose">
<view class="wux-class {{ classes.wrap }}">
<view class="{{ classes.hd }}">
<wux-button block size="small" type="assertive"
catchtap="onItemDelete" class="{{ classes.icon }}" >
删除</wux-button>
<view class="{{ classes.title }}" wx:if="{{ title }}">{{ title }}</view>
<view class="{{ classes.menus }}" wx:if="{{ activeOptions.length }}">
<block wx:for="{{ activeOptions }}" wx:key="">
<view class="{{ classes.menu }} {{ activeIndex === index ? prefixCls + '__menu--active' : '' }}" data-menu-index="{{ index }}" bindtap="onMenuClick">{{ item[fieldNames['label']] }}</view>
</block>
</view>
</view>
<view class="{{ classes.bd }}" style="{{ bodyStyle }}">
<block wx:for="{{ showOptions }}" wx:for-item="option" wx:for-index="optionIndex" wx:key="">
<view class="{{ classes.inner }}">
<scroll-view scroll-y class="wux-scroll-view-class {{ classes.scrollView }}">
<view class="{{ classes.option }}">
<block wx:for="{{ option }}" wx:key="">
<view
class="{{ classes.item }} {{ activeValue[optionIndex] === item[fieldNames['value']] ? prefixCls + '__item--active' : '' }} {{ item.disabled ? prefixCls + '__item--disabled' : '' }}"
data-option-index="{{ optionIndex }}"
data-item="{{ item }}"
bindtap="onItemSelect"
>
<text>{{ item[fieldNames['label']] }}</text>
<!-- <icon class="{{ classes.icon }}" type="success_no_circle" size="16" color="#ef473a" wx:if="{{ activeValue[optionIndex] === item[fieldNames['value']] }}" /> -->
<wux-button block size="small"
outline class="{{ classes.icon }}"
wx:if="{{ item.isLeaf && activeValue[optionIndex] === item[fieldNames['value']] }}"
type="balanced" data-item="{{ item }}"
data-option-index="{{ optionIndex }}"
catchtap="onItemCheck">选择</wux-button>
</view>
</block>
</view>
</scroll-view>
</view>
</block>
</view>
</view>
</wux-popup>

View File

@@ -0,0 +1,144 @@
.wux-cascader__hd {
position: relative;
width: 100%;
font-size: 34rpx;
line-height: 1.5;
color: #444
}
.wux-cascader__hd::after {
content: " ";
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 1PX;
border-bottom: 1PX solid #d9d9d9;
color: #d9d9d9;
transform-origin: 0 100%;
transform: scaleY(.5)
}
.wux-cascader__title {
position: relative;
height: 88rpx;
display: -ms-flexbox;
display: flex;
-ms-flex-align: center;
align-items: center;
-ms-flex-pack: center;
justify-content: center;
text-align: center;
box-sizing: border-box
}
.wux-cascader__title::after {
content: " ";
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 1PX;
border-bottom: 1PX solid #d9d9d9;
color: #d9d9d9;
transform-origin: 0 100%;
transform: scaleY(.5)
}
.wux-cascader__menus {
display: -ms-flexbox;
display: flex;
height: 88rpx;
padding: 0 20rpx;
-ms-flex-align: center;
align-items: center;
box-sizing: border-box
}
.wux-cascader__menu {
font-size: 26rpx;
padding: 0 20rpx;
max-width: 40%;
width: auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal
}
.wux-cascader__menu--active {
color: #ef473a
}
.wux-cascader__bd {
width: 100%;
display: -ms-flexbox;
display: flex;
transition: transform .3s;
background-color: #f5f5f5
}
.wux-cascader__inner {
display: block;
height: inherit;
width: 50%;
-ms-flex: 0 0 50%;
flex: 0 0 50%;
background-color: #fff
}
.wux-cascader__inner:nth-child(2n) {
background-color: #f5f5f5
}
.wux-cascader__scroll-view {
max-height: 540rpx
}
.wux-cascader__option {
width: 100%;
height: inherit;
display: block;
padding-left: 40rpx;
padding-right: 10rpx;
box-sizing: border-box
}
.wux-cascader__item {
position: relative;
z-index: 10;
display: block;
color: rgba(0,0,0,.85);
font-size: 26rpx;
height: 80rpx;
line-height: 80rpx;
text-align: left;
padding-right: 36rpx;
width: auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal
}
.wux-cascader__item::after {
content: " ";
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 1PX;
border-bottom: 1PX solid #d9d9d9;
color: #d9d9d9;
transform-origin: 0 100%;
transform: scaleY(.5)
}
.wux-cascader__item--active {
color: #ef473a
}
.wux-cascader__item--disabled {
opacity: .3
}
.wux-cascader__icon {
position: absolute;
top: 0;
right: 20rpx;
bottom: 50rpx;
z-index: 20;
font-size: 0;
line-height: 1
}
.searchicon {
top: 0;
padding-right: 20rpx;
z-index: 20;
font-size: 0;
line-height: 1
}