mirror of
https://github.com/VickScarlet/lifeRestart.git
synced 2026-04-16 21:02:42 +08:00
增加微信小程序版
This commit is contained in:
311
liferestartWX/utils/wux/cascader/index.js
Normal file
311
liferestartWX/utils/wux/cascader/index.js
Normal 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)
|
||||
},
|
||||
})
|
||||
8
liferestartWX/utils/wux/cascader/index.json
Normal file
8
liferestartWX/utils/wux/cascader/index.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"wux-popup": "../popup/index",
|
||||
"wux-icon": "../icon/index",
|
||||
"wux-button": "../button/index"
|
||||
}
|
||||
}
|
||||
42
liferestartWX/utils/wux/cascader/index.wxml
Normal file
42
liferestartWX/utils/wux/cascader/index.wxml
Normal 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>
|
||||
144
liferestartWX/utils/wux/cascader/index.wxss
Normal file
144
liferestartWX/utils/wux/cascader/index.wxss
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user