Browse Source

no message

feature/v1.6
xpz2018 4 years ago
parent
commit
c08da91d54
32 changed files with 2144 additions and 1 deletions
  1. 383
      components/animation-group/index.js
  2. 3
      components/animation-group/index.json
  3. 5
      components/animation-group/index.wxml
  4. 206
      components/animation-group/index.wxss
  5. 62
      components/backdrop/index.js
  6. 6
      components/backdrop/index.json
  7. 1
      components/backdrop/index.wxml
  8. 15
      components/backdrop/index.wxss
  9. 24
      components/helpers/arrayTreeFilter.js
  10. 77
      components/helpers/baseComponent.js
  11. 29
      components/helpers/checkIPhoneX.js
  12. 39
      components/helpers/classNames.js
  13. 27
      components/helpers/colors.js
  14. 27
      components/helpers/compareVersion.js
  15. 48
      components/helpers/computedBehavior.js
  16. 75
      components/helpers/createFieldsStore.js
  17. 56
      components/helpers/debounce.js
  18. 53
      components/helpers/eventsMixin.js
  19. 97
      components/helpers/funcBehavior.js
  20. 50
      components/helpers/gestures.js
  21. 19
      components/helpers/isEmpty.js
  22. 17
      components/helpers/mergeOptionsToData.js
  23. 67
      components/helpers/relationsBehavior.js
  24. 46
      components/helpers/safeAreaBehavior.js
  25. 57
      components/helpers/safeSetDataBehavior.js
  26. 65
      components/helpers/shallowEqual.js
  27. 138
      components/helpers/styleToCssString.js
  28. 234
      components/popover/index.js
  29. 7
      components/popover/index.json
  30. 17
      components/popover/index.wxml
  31. 193
      components/popover/index.wxss
  32. 2
      pages/storage/order-info/index.js

383
components/animation-group/index.js

@ -0,0 +1,383 @@
import baseComponent from '../helpers/baseComponent'
import styleToCssString from '../helpers/styleToCssString'
const ENTER = 'enter'
const ENTERING = 'entering'
const ENTERED = 'entered'
const EXIT = 'exit'
const EXITING = 'exiting'
const EXITED = 'exited'
const UNMOUNTED = 'unmounted'
const TRANSITION = 'transition'
const ANIMATION = 'animation'
const TIMEOUT = 1000 / 60
const defaultClassNames = {
enter: '', // 进入过渡的开始状态,在过渡过程完成之后移除
enterActive: '', // 进入过渡的结束状态,在过渡过程完成之后移除
enterDone: '', // 进入过渡的完成状态
exit: '', // 离开过渡的开始状态,在过渡过程完成之后移除
exitActive: '', // 离开过渡的结束状态,在过渡过程完成之后移除
exitDone: '', // 离开过渡的完成状态
}
baseComponent({
properties: {
// 触发组件进入或离开过渡的状态
in: {
type: Boolean,
value: false,
observer(newVal) {
if (this.data.isMounting) {
this.updated(newVal)
}
},
},
// 过渡的类名
classNames: {
type: null,
value: defaultClassNames,
},
// 过渡持续时间
duration: {
type: null,
value: null,
},
// 过渡动效的类型
type: {
type: String,
value: TRANSITION,
},
// 首次挂载时是否触发进入过渡
appear: {
type: Boolean,
value: false,
},
// 是否启用进入过渡
enter: {
type: Boolean,
value: true,
},
// 是否启用离开过渡
exit: {
type: Boolean,
value: true,
},
// 首次进入过渡时是否懒挂载组件
mountOnEnter: {
type: Boolean,
value: true,
},
// 离开过渡完成时是否卸载组件
unmountOnExit: {
type: Boolean,
value: true,
},
// 自定义类名
wrapCls: {
type: String,
value: '',
},
// 自定义样式
wrapStyle: {
type: [String, Object],
value: '',
observer(newVal) {
this.setData({
extStyle: styleToCssString(newVal),
})
},
},
disableScroll: {
type: Boolean,
value: false,
},
},
data: {
animateCss: '', // 动画样式
animateStatus: EXITED, // 动画状态,可选值 entering、entered、exiting、exited
isMounting: false, // 是否首次挂载
extStyle: '', // 组件样式
},
methods: {
/**
* 监听过渡或动画的回调函数
*/
addEventListener() {
const { animateStatus } = this.data
const { enter, exit } = this.getTimeouts()
if (animateStatus === ENTERING && !enter && this.data.enter) {
this.performEntered()
}
if (animateStatus === EXITING && !exit && this.data.exit) {
this.performExited()
}
},
/**
* 会在 WXSS transition wx.createAnimation 动画结束后触发
*/
onTransitionEnd() {
if (this.data.type === TRANSITION) {
this.addEventListener()
}
},
/**
* 会在一个 WXSS animation 动画完成时触发
*/
onAnimationEnd() {
if (this.data.type === ANIMATION) {
this.addEventListener()
}
},
/**
* 更新组件状态
* @param {String} nextStatus 下一状态ENTERING EXITING
* @param {Boolean} mounting 是否首次挂载
*/
updateStatus(nextStatus, mounting = false) {
if (nextStatus !== null) {
this.cancelNextCallback()
this.isAppearing = mounting
if (nextStatus === ENTERING) {
this.performEnter()
} else {
this.performExit()
}
}
},
/**
* 进入过渡
*/
performEnter() {
const { className, activeClassName } = this.getClassNames(ENTER)
const { enter } = this.getTimeouts()
const enterParams = {
animateStatus: ENTER,
animateCss: className,
}
const enteringParams = {
animateStatus: ENTERING,
animateCss: `${className} ${activeClassName}`,
}
// 若已禁用进入过渡,则更新状态至 ENTERED
if (!this.isAppearing && !this.data.enter) {
return this.performEntered()
}
// 第一阶段:设置进入过渡的开始状态,并触发 ENTER 事件
// 第二阶段:延迟一帧后,设置进入过渡的结束状态,并触发 ENTERING 事件
// 第三阶段:若已设置过渡的持续时间,则延迟指定时间后触发进入过渡完成 performEntered,否则等待触发 onTransitionEnd 或 onAnimationEnd
this.safeSetData(enterParams, () => {
this.triggerEvent('change', { animateStatus: ENTER })
this.triggerEvent(ENTER, { isAppearing: this.isAppearing })
// 由于有些时候不能正确的触发动画完成的回调,具体原因未知
// 所以采用延迟一帧的方式来确保可以触发回调
this.delayHandler(TIMEOUT, () => {
this.safeSetData(enteringParams, () => {
this.triggerEvent('change', { animateStatus: ENTERING })
this.triggerEvent(ENTERING, { isAppearing: this.isAppearing })
if (enter) {
this.delayHandler(enter, this.performEntered)
}
})
})
})
},
/**
* 进入过渡完成
*/
performEntered() {
const { doneClassName } = this.getClassNames(ENTER)
const enteredParams = {
animateStatus: ENTERED,
animateCss: doneClassName,
}
// 第三阶段:设置进入过渡的完成状态,并触发 ENTERED 事件
this.safeSetData(enteredParams, () => {
this.triggerEvent('change', { animateStatus: ENTERED })
this.triggerEvent(ENTERED, { isAppearing: this.isAppearing })
})
},
/**
* 离开过渡
*/
performExit() {
const { className, activeClassName } = this.getClassNames(EXIT)
const { exit } = this.getTimeouts()
const exitParams = {
animateStatus: EXIT,
animateCss: className,
}
const exitingParams = {
animateStatus: EXITING,
animateCss: `${className} ${activeClassName}`,
}
// 若已禁用离开过渡,则更新状态至 EXITED
if (!this.data.exit) {
return this.performExited()
}
// 第一阶段:设置离开过渡的开始状态,并触发 EXIT 事件
// 第二阶段:延迟一帧后,设置离开过渡的结束状态,并触发 EXITING 事件
// 第三阶段:若已设置过渡的持续时间,则延迟指定时间后触发离开过渡完成 performExited,否则等待触发 onTransitionEnd 或 onAnimationEnd
this.safeSetData(exitParams, () => {
this.triggerEvent('change', { animateStatus: EXIT })
this.triggerEvent(EXIT)
this.delayHandler(TIMEOUT, () => {
this.safeSetData(exitingParams, () => {
this.triggerEvent('change', { animateStatus: EXITING })
this.triggerEvent(EXITING)
if (exit) {
this.delayHandler(exit, this.performExited)
}
})
})
})
},
/**
* 离开过渡完成
*/
performExited() {
const { doneClassName } = this.getClassNames(EXIT)
const exitedParams = {
animateStatus: EXITED,
animateCss: doneClassName,
}
// 第三阶段:设置离开过渡的完成状态,并触发 EXITED 事件
this.safeSetData(exitedParams, () => {
this.triggerEvent('change', { animateStatus: EXITED })
this.triggerEvent(EXITED)
// 判断离开过渡完成时是否卸载组件
if (this.data.unmountOnExit) {
this.setData({ animateStatus: UNMOUNTED }, () => {
this.triggerEvent('change', { animateStatus: UNMOUNTED })
})
}
})
},
/**
* 获取指定状态下的类名
* @param {String} type 过渡类型enter exit
*/
getClassNames(type) {
const { classNames } = this.data
const className = typeof classNames !== 'string' ? classNames[type] : `${classNames}-${type}`
const activeClassName = typeof classNames !== 'string' ? classNames[`${type}Active`] : `${classNames}-${type}-active`
const doneClassName = typeof classNames !== 'string' ? classNames[`${type}Done`] : `${classNames}-${type}-done`
return {
className,
activeClassName,
doneClassName,
}
},
/**
* 获取过渡持续时间
*/
getTimeouts() {
const { duration } = this.data
if (duration !== null && typeof duration === 'object') {
return {
enter: duration.enter,
exit: duration.exit,
}
} else if (typeof duration === 'number') {
return {
enter: duration,
exit: duration,
}
}
return {}
},
/**
* 属性值 in 被更改时的响应函数
* @param {Boolean} newVal 触发组件进入或离开过渡的状态
*/
updated(newVal) {
let { animateStatus } = this.pendingData || this.data
let nextStatus = null
if (newVal) {
if (animateStatus === UNMOUNTED) {
animateStatus = EXITED
this.setData({ animateStatus: EXITED }, () => {
this.triggerEvent('change', { animateStatus: EXITED })
})
}
if (animateStatus !== ENTER && animateStatus !== ENTERING && animateStatus !== ENTERED) {
nextStatus = ENTERING
}
} else {
if (animateStatus === ENTER || animateStatus === ENTERING || animateStatus === ENTERED) {
nextStatus = EXITING
}
}
this.updateStatus(nextStatus)
},
/**
* 延迟一段时间触发回调
* @param {Number} timeout 延迟时间
* @param {Function} handler 回调函数
*/
delayHandler(timeout, handler) {
if (timeout) {
this.setNextCallback(handler)
setTimeout(this.nextCallback, timeout)
}
},
/**
* 点击事件
*/
onTap() {
this.triggerEvent('click')
},
/**
* 阻止移动触摸
*/
noop() {
},
},
attached() {
let animateStatus = null
let appearStatus = null
if (this.data.in) {
if (this.data.appear) {
animateStatus = EXITED
appearStatus = ENTERING
} else {
animateStatus = ENTERED
}
} else {
if (this.data.unmountOnExit || this.data.mountOnEnter) {
animateStatus = UNMOUNTED
} else {
animateStatus = EXITED
}
}
// 由于小程序组件首次挂载时 observer 事件总是优先于 attached 事件
// 所以使用 isMounting 来强制优先触发 attached 事件
this.safeSetData({ animateStatus, isMounting: true }, () => {
this.triggerEvent('change', { animateStatus })
this.updateStatus(appearStatus, true)
})
},
})

3
components/animation-group/index.json

@ -0,0 +1,3 @@
{
"component": true
}

5
components/animation-group/index.wxml

@ -0,0 +1,5 @@
<view class="wux-class {{ wrapCls }} {{ animateCss }}" catchtap="onTap"
catchtouchmove="{{ disableScroll ? 'onTap' : '' }}" bindtransitionend="onTransitionEnd"
bindanimationend="onAnimationEnd" wx:if="{{ animateStatus !== 'unmounted' }}" style="{{ extStyle }}">
<slot></slot>
</view>

206
components/animation-group/index.wxss

@ -0,0 +1,206 @@
.wux-animate--fadeIn-enter {
transition: opacity .3s;
opacity: 0
}
.wux-animate--fadeIn-enter-active,
.wux-animate--fadeIn-enter-done {
opacity: 1
}
.wux-animate--fadeIn-exit {
transition: opacity .3s;
opacity: 1
}
.wux-animate--fadeIn-exit-active,
.wux-animate--fadeIn-exit-done {
opacity: 0
}
.wux-animate--fadeInDown-enter {
transition: opacity .3s,transform .3s;
opacity: 0;
transform: translate3d(0,-100%,0)
}
.wux-animate--fadeInDown-enter-active,
.wux-animate--fadeInDown-enter-done {
opacity: 1;
transform: none
}
.wux-animate--fadeInDown-exit {
transition: opacity .3s,transform .3s;
opacity: 1;
transform: none
}
.wux-animate--fadeInDown-exit-active,
.wux-animate--fadeInDown-exit-done {
opacity: 0;
transform: translate3d(0,-100%,0)
}
.wux-animate--fadeInLeft-enter {
transition: opacity .3s,transform .3s;
opacity: 0;
transform: translate3d(-100%,0,0)
}
.wux-animate--fadeInLeft-enter-active,
.wux-animate--fadeInLeft-enter-done {
opacity: 1;
transform: none
}
.wux-animate--fadeInLeft-exit {
transition: opacity .3s,transform .3s;
opacity: 1;
transform: none
}
.wux-animate--fadeInLeft-exit-active,
.wux-animate--fadeInLeft-exit-done {
opacity: 0;
transform: translate3d(-100%,0,0)
}
.wux-animate--fadeInRight-enter {
transition: opacity .3s,transform .3s;
opacity: 0;
transform: translate3d(100%,0,0)
}
.wux-animate--fadeInRight-enter-active,
.wux-animate--fadeInRight-enter-done {
opacity: 1;
transform: none
}
.wux-animate--fadeInRight-exit {
transition: opacity .3s,transform .3s;
opacity: 1;
transform: none
}
.wux-animate--fadeInRight-exit-active,
.wux-animate--fadeInRight-exit-done {
opacity: 0;
transform: translate3d(100%,0,0)
}
.wux-animate--fadeInUp-enter {
transition: opacity .3s,transform .3s;
opacity: 0;
transform: translate3d(0,100%,0)
}
.wux-animate--fadeInUp-enter-active,
.wux-animate--fadeInUp-enter-done {
opacity: 1;
transform: none
}
.wux-animate--fadeInUp-exit {
transition: opacity .3s,transform .3s;
opacity: 1;
transform: none
}
.wux-animate--fadeInUp-exit-active,
.wux-animate--fadeInUp-exit-done {
opacity: 0;
transform: translate3d(0,100%,0)
}
.wux-animate--slideInUp-enter {
transition: transform .3s;
transform: translate3d(0,100%,0);
visibility: visible
}
.wux-animate--slideInUp-enter-active,
.wux-animate--slideInUp-enter-done {
transform: translateZ(0)
}
.wux-animate--slideInUp-exit {
transition: transform .3s;
transform: translateZ(0)
}
.wux-animate--slideInUp-exit-active,
.wux-animate--slideInUp-exit-done {
transform: translate3d(0,100%,0);
visibility: visible
}
.wux-animate--slideInDown-enter {
transition: transform .3s;
transform: translate3d(0,-100%,0);
visibility: visible
}
.wux-animate--slideInDown-enter-active,
.wux-animate--slideInDown-enter-done {
transform: translateZ(0)
}
.wux-animate--slideInDown-exit {
transition: transform .3s;
transform: translateZ(0)
}
.wux-animate--slideInDown-exit-active,
.wux-animate--slideInDown-exit-done {
transform: translate3d(0,-100%,0);
visibility: visible
}
.wux-animate--slideInLeft-enter {
transition: transform .3s;
transform: translate3d(-100%,0,0);
visibility: visible
}
.wux-animate--slideInLeft-enter-active,
.wux-animate--slideInLeft-enter-done {
transform: translateZ(0)
}
.wux-animate--slideInLeft-exit {
transition: transform .3s;
transform: translateZ(0)
}
.wux-animate--slideInLeft-exit-active,
.wux-animate--slideInLeft-exit-done {
transform: translate3d(-100%,0,0);
visibility: visible
}
.wux-animate--slideInRight-enter {
transition: transform .3s;
transform: translate3d(100%,0,0);
visibility: visible
}
.wux-animate--slideInRight-enter-active,
.wux-animate--slideInRight-enter-done {
transform: none
}
.wux-animate--slideInRight-exit {
transition: transform .3s;
transform: none
}
.wux-animate--slideInRight-exit-active,
.wux-animate--slideInRight-exit-done {
transform: translate3d(100%,0,0);
visibility: visible
}
.wux-animate--zoom-enter {
transition: all .3s cubic-bezier(.215,.61,.355,1);
opacity: .01;
transform: scale(.75)
}
.wux-animate--zoom-enter-active,
.wux-animate--zoom-enter-done {
opacity: 1;
transform: none
}
.wux-animate--zoom-exit {
transition: all .25s linear;
transform: none
}
.wux-animate--zoom-exit-active,
.wux-animate--zoom-exit-done {
opacity: .01;
transform: scale(.75)
}
.wux-animate--punch-enter {
transition: all .3s cubic-bezier(.215,.61,.355,1);
opacity: .01;
transform: scale(1.35)
}
.wux-animate--punch-enter-active,
.wux-animate--punch-enter-done {
opacity: 1;
transform: none
}
.wux-animate--punch-exit {
transition: all .25s linear;
transform: none
}
.wux-animate--punch-exit-active,
.wux-animate--punch-exit-done {
opacity: .01;
transform: scale(1.35)
}

62
components/backdrop/index.js

@ -0,0 +1,62 @@
import baseComponent from '../helpers/baseComponent'
baseComponent({
properties: {
prefixCls: {
type: String,
value: 'wux-backdrop',
},
transparent: {
type: Boolean,
value: false,
},
zIndex: {
type: Number,
value: 1000,
},
classNames: {
type: null,
value: 'wux-animate--fadeIn',
},
},
computed: {
classes: ['prefixCls, transparent', function(prefixCls, transparent) {
const wrap = transparent ? `${prefixCls}--transparent` : prefixCls
return {
wrap,
}
}],
},
methods: {
/**
* 保持锁定
*/
retain() {
if (typeof this.backdropHolds !== 'number' || !this.backdropHolds) {
this.backdropHolds = 0
}
this.backdropHolds = this.backdropHolds + 1
if (this.backdropHolds === 1) {
this.setData({ in: true })
}
},
/**
* 释放锁定
*/
release() {
if (this.backdropHolds === 1) {
this.setData({ in: false })
}
this.backdropHolds = Math.max(0, this.backdropHolds - 1)
},
/**
* 点击事件
*/
onClick() {
this.triggerEvent('click')
},
},
})

6
components/backdrop/index.json

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"wux-animation-group": "../animation-group/index"
}
}

1
components/backdrop/index.wxml

@ -0,0 +1 @@
<wux-animation-group wux-class="{{ classes.wrap }}" in="{{ in }}" classNames="{{ classNames }}" bind:click="onClick" wrapStyle="{{ { zIndex } }}" disableScroll />

15
components/backdrop/index.wxss

@ -0,0 +1,15 @@
.wux-backdrop {
background: rgba(0,0,0,.4)
}
.wux-backdrop,
.wux-backdrop--transparent {
position: fixed;
z-index: 1000;
top: 0;
right: 0;
left: 0;
bottom: 0
}
.wux-backdrop--transparent {
background: 0 0
}

24
components/helpers/arrayTreeFilter.js

@ -0,0 +1,24 @@
/**
* https://github.com/afc163/array-tree-filter
*/
function arrayTreeFilter(data, filterFn, options) {
options = options || {}
options.childrenKeyName = options.childrenKeyName || 'children'
let children = data || []
const result = []
let level = 0
do {
const foundItem = children.filter(function(item) {
return filterFn(item, level)
})[0]
if (!foundItem) {
break
}
result.push(foundItem)
children = foundItem[options.childrenKeyName] || []
level += 1
} while (children.length > 0)
return result
}
export default arrayTreeFilter

77
components/helpers/baseComponent.js

@ -0,0 +1,77 @@
import computedBehavior from './computedBehavior'
import relationsBehavior from './relationsBehavior'
import safeAreaBehavior from './safeAreaBehavior'
import safeSetDataBehavior from './safeSetDataBehavior'
import funcBehavior from './funcBehavior'
import compareVersion from './compareVersion'
const { platform, SDKVersion } = wx.getSystemInfoSync()
const libVersion = '2.6.6'
// check SDKVersion
if (platform === 'devtools' && compareVersion(SDKVersion, libVersion) < 0) {
if (wx && wx.showModal) {
wx.showModal({
title: '提示',
content: `当前基础库版本(${SDKVersion})过低,无法使用 Wux Weapp 组件库,请更新基础库版本 >=${libVersion} 后重试。`,
})
}
}
const baseComponent = (options = {}) => {
// add default externalClasses
options.externalClasses = [
'wux-class',
'wux-hover-class',
...(options.externalClasses = options.externalClasses || []),
]
// add default behaviors
options.behaviors = [
relationsBehavior,
safeSetDataBehavior,
...(options.behaviors = options.behaviors || []),
computedBehavior, // make sure it's triggered
]
// use safeArea
if (options.useSafeArea) {
options.behaviors = [...options.behaviors, safeAreaBehavior]
delete options.useSafeArea
}
// use func
if (options.useFunc) {
options.behaviors = [...options.behaviors, funcBehavior]
delete options.useFunc
}
// use field
if (options.useField) {
options.behaviors = [...options.behaviors, 'wx://form-field']
delete options.useField
}
// use export
if (options.useExport) {
options.behaviors = [...options.behaviors, 'wx://component-export']
options.methods = {
export () {
return this
},
...options.methods,
}
delete options.useExport
}
// add default options
options.options = {
multipleSlots: true,
addGlobalClass: true,
...options.options,
}
return Component(options)
}
export default baseComponent

29
components/helpers/checkIPhoneX.js

@ -0,0 +1,29 @@
/**
* 获取系统信息
*/
let systemInfo = null
export const getSystemInfo = (isForce) => {
if (!systemInfo || isForce) {
try {
systemInfo = wx.getSystemInfoSync()
} catch(e) { /* Ignore */ }
}
return systemInfo
}
// iPhoneX 竖屏安全区域
export const safeAreaInset = {
top: 88, // StatusBar & NavBar
left: 0,
right: 0,
bottom: 34, // Home Indicator
}
const isIPhoneX = ({ model, platform }) => {
return /iPhone X/.test(model) && platform === 'ios'
}
export const checkIPhoneX = (isForce) => isIPhoneX(getSystemInfo(isForce))

39
components/helpers/classNames.js

@ -0,0 +1,39 @@
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/* global define */
'use strict';
var hasOwn = {}.hasOwnProperty;
function classNames() {
var classes = [];
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (!arg) continue;
var argType = typeof arg;
if (argType === 'string' || argType === 'number') {
classes.push(arg);
} else if (Array.isArray(arg) && arg.length) {
var inner = classNames.apply(null, arg);
if (inner) {
classes.push(inner);
}
} else if (argType === 'object') {
for (var key in arg) {
if (hasOwn.call(arg, key) && arg[key]) {
classes.push(key);
}
}
}
}
return classes.join(' ');
}
export default classNames

27
components/helpers/colors.js

@ -0,0 +1,27 @@
/**
* Don't modify this file!
* Colors generated by themes!
*/
/* eslint-disable */
export const colors = {
'light': '#ddd',
'stable': '#b2b2b2',
'positive': '#387ef5',
'calm': '#11c1f3',
'balanced': '#33cd5f',
'energized': '#ffc900',
'assertive': '#ef473a',
'royal': '#886aea',
'dark': '#444',
}
export const isPresetColor = (color) => {
if (!color) {
return false
}
return colors[color] ? colors[color] : color
}
/* eslint-enable */

27
components/helpers/compareVersion.js

@ -0,0 +1,27 @@
function compareVersion(v1, v2) {
const $v1 = v1.split('.')
const $v2 = v2.split('.')
const len = Math.max($v1.length, $v2.length)
while ($v1.length < len) {
$v1.push('0')
}
while ($v2.length < len) {
$v2.push('0')
}
for (let i = 0; i < len; i++) {
const num1 = parseInt($v1[i])
const num2 = parseInt($v2[i])
if (num1 > num2) {
return 1
} else if (num1 < num2) {
return -1
}
}
return 0
}
export default compareVersion

48
components/helpers/computedBehavior.js

@ -0,0 +1,48 @@
import isEmpty from './isEmpty'
import shallowEqual from './shallowEqual'
const ALL_DATA_KEY = '**'
const trim = (str = '') => str.replace(/\s/g, '')
export default Behavior({
lifetimes: {
attached() {
this.initComputed()
},
},
definitionFilter(defFields) {
const { computed = {} } = defFields
const observers = Object.keys(computed).reduce((acc, name) => {
const [field, getter] = Array.isArray(computed[name]) ? computed[name] : [ALL_DATA_KEY, computed[name]]
return {
...acc,
[field]: function(...args) {
if (typeof getter === 'function') {
const newValue = getter.apply(this, args)
const oldValue = this.data[name]
if (!isEmpty(newValue) && !shallowEqual(newValue, oldValue)) {
this.setData({ [name]: newValue })
}
}
},
}
}, {})
Object.assign(defFields.observers = (defFields.observers || {}), observers)
Object.assign(defFields.methods = (defFields.methods || {}), {
initComputed: function(data = {}, isForce = false) {
if (!this.runInitComputed || isForce) {
this.runInitComputed = false
const context = this
const result = { ...this.data, ...data }
Object.keys(observers).forEach((key) => {
const values = trim(key).split(',').reduce((acc, name) => ([...acc, result[name]]), [])
observers[key].apply(context, values)
})
this.runInitComputed = true
}
},
})
},
})

75
components/helpers/createFieldsStore.js

@ -0,0 +1,75 @@
class FieldsStore {
constructor(fields = {}) {
this.fields = fields
}
setFields(fields) {
Object.assign(this.fields, fields)
}
updateFields(fields) {
this.fields = fields
}
clearField(name) {
delete this.fields[name]
}
getValueFromFields(name, fields) {
const field = fields[name]
if (field && 'value' in field) {
return field.value
}
return field.initialValue
}
getAllFieldsName() {
const { fields } = this
return fields ? Object.keys(fields) : []
}
getField(name) {
return {
...this.fields[name],
name,
}
}
getFieldValuePropValue(fieldOption) {
const { name, valuePropName } = fieldOption
const field = this.getField(name)
const fieldValue = 'value' in field ? field.value : field.initialValue
return {
[valuePropName]: fieldValue,
}
}
getFieldValue(name) {
return this.getValueFromFields(name, this.fields)
}
getFieldsValue(names) {
const fields = names || this.getAllFieldsName()
return fields.reduce((acc, name) => {
acc[name] = this.getFieldValue(name)
return acc
}, {})
}
resetFields(ns) {
const { fields } = this
const names = ns || this.getAllFieldsName()
return names.reduce((acc, name) => {
const field = fields[name]
if (field) {
acc[name] = field.initialValue
}
return acc
}, {})
}
}
export default function createFieldsStore(fields) {
return new FieldsStore(fields)
}

56
components/helpers/debounce.js

@ -0,0 +1,56 @@
export default function debounce(func, wait, immediate) {
let timeout,
args,
context,
timestamp,
result
function later() {
const last = +(new Date()) - timestamp
if (last < wait && last >= 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = undefined
if (!immediate) {
result = func.apply(context, args)
if (!timeout) {
context = undefined
args = undefined
}
}
}
}
function debounced() {
context = this
args = arguments
timestamp = +(new Date())
const callNow = immediate && !timeout
if (!timeout) {
timeout = setTimeout(later, wait)
}
if (callNow) {
result = func.apply(context, args)
context = undefined
args = undefined
}
return result
}
function cancel() {
if (timeout !== undefined) {
clearTimeout(timeout)
timeout = undefined
}
context = undefined
args = undefined
}
debounced.cancel = cancel
return debounced
}

53
components/helpers/eventsMixin.js

@ -0,0 +1,53 @@
const defaultEvents = {
onChange() {},
}
export default function eventsMixin(params = { defaultEvents }) {
return Behavior({
lifetimes: {
created () {
this._oriTriggerEvent = this.triggerEvent
this.triggerEvent = this._triggerEvent
},
},
properties: {
events: {
type: Object,
value: defaultEvents,
},
},
data: {
inputEvents: defaultEvents,
},
definitionFilter(defFields) {
// set default data
Object.assign(defFields.data = (defFields.data || {}), {
inputEvents: Object.assign({}, defaultEvents, defFields.inputEvents),
})
// set default methods
Object.assign(defFields.methods = (defFields.methods || {}), {
_triggerEvent(name, params, runCallbacks = true, option) {
const { inputEvents } = this.data
const method = `on${name[0].toUpperCase()}${name.slice(1)}`
const func = inputEvents[method]
if (runCallbacks && typeof func === 'function') {
func.call(this, params)
}
this._oriTriggerEvent(name, params, option)
},
})
// set default observers
Object.assign(defFields.observers = (defFields.observers || {}), {
events(newVal) {
this.setData({
inputEvents: Object.assign({}, defaultEvents, this.data.inputEvents, newVal),
})
},
})
},
})
}

97
components/helpers/funcBehavior.js

@ -0,0 +1,97 @@
/**
* 过滤对象的函数属性
* @param {Object} opts
*/
const mergeOptionsToData = (opts = {}) => {
const options = Object.assign({}, opts)
for (const key in options) {
if (options.hasOwnProperty(key) && typeof options[key] === 'function') {
delete options[key]
}
}
return options
}
/**
* Simple bind, faster than native
*
* @param {Function} fn
* @param {Object} ctx
* @return {Function}
*/
const bind = (fn, ctx) => {
return (...args) => {
return args.length ? fn.apply(ctx, args) : fn.call(ctx)
}
}
/**
* Object assign
*/
const assign = (...args) => Object.assign({}, ...args)
export default Behavior({
definitionFilter(defFields) {
defFields.data = mergeOptionsToData(defFields.data)
defFields.data.in = false
defFields.data.visible = false
},
methods: {
/**
* 过滤对象的函数属性
* @param {Object} opts
*/
$$mergeOptionsToData: mergeOptionsToData,
/**
* 合并参数并绑定方法
*
* @param {Object} opts 参数对象
* @param {Object} fns 方法挂载的属性
*/
$$mergeOptionsAndBindMethods (opts = {}, fns = this.fns) {
const options = Object.assign({}, opts)
for (const key in options) {
if (options.hasOwnProperty(key) && typeof options[key] === 'function') {
fns[key] = bind(options[key], this)
delete options[key]
}
}
return options
},
/**
* Promise setData
* @param {Array} args 参数对象
*/
$$setData (...args) {
const params = assign({}, ...args)
return new Promise((resolve) => {
this.setData(params, resolve)
})
},
/**
* 延迟指定时间执行回调函数
* @param {Function} callback 回调函数
* @param {Number} timeout 延迟时间
*/
$$requestAnimationFrame (callback = () => {}, timeout = 1000 / 60) {
return new Promise((resolve) => setTimeout(resolve, timeout)).then(callback)
},
},
/**
* 组件生命周期函数在组件实例进入页面节点树时执行
*/
created () {
this.fns = {}
},
/**
* 组件生命周期函数在组件实例被从页面节点树移除时执行
*/
detached () {
this.fns = {}
},
})

50
components/helpers/gestures.js

@ -0,0 +1,50 @@
/**
* 获取触摸点位置信息
*/
export const getTouchPoints = (nativeEvent, index = 0) => {
const touches = nativeEvent.touches
const changedTouches = nativeEvent.changedTouches
const hasTouches = touches && touches.length > 0
const hasChangedTouches = changedTouches && changedTouches.length > 0
const points = !hasTouches && hasChangedTouches ? changedTouches[index] : hasTouches ? touches[index] : nativeEvent
return {
x: points.pageX,
y: points.pageY,
}
}
/**
* 获取触摸点个数
*/
export const getPointsNumber = (e) => e.touches && e.touches.length || e.changedTouches && e.changedTouches.length
/**
* 判断是否为同一点
*/
export const isEqualPoints = (p1, p2) => p1.x === p2.x && p1.y === p2.y
/**
* 判断是否为相近的两点
*/
export const isNearbyPoints = (p1, p2, DOUBLE_TAP_RADIUS = 25) => {
const xMove = Math.abs(p1.x - p2.x)
const yMove = Math.abs(p1.y - p2.y)
return xMove < DOUBLE_TAP_RADIUS & yMove < DOUBLE_TAP_RADIUS
}
/**
* 获取两点之间的距离
*/
export const getPointsDistance = (p1, p2) => {
const xMove = Math.abs(p1.x - p2.x)
const yMove = Math.abs(p1.y - p2.y)
return Math.sqrt(xMove * xMove + yMove * yMove)
}
/**
* 获取触摸移动方向
*/
export const getSwipeDirection = (x1, x2, y1, y2) => {
return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
}

19
components/helpers/isEmpty.js

@ -0,0 +1,19 @@
/**
* Checks if a value is empty.
*/
function isEmpty(value) {
if (Array.isArray(value)) {
return value.length === 0
} else if (typeof value === 'object') {
if (value) {
for (const _ in value) {
return false
}
}
return true
} else {
return !value
}
}
export default isEmpty

17
components/helpers/mergeOptionsToData.js

@ -0,0 +1,17 @@
/**
* 过滤对象的函数属性
* @param {Object} opts
*/
const mergeOptionsToData = (opts = {}) => {
const options = Object.assign({}, opts)
for (const key in options) {
if (options.hasOwnProperty(key) && typeof options[key] === 'function') {
delete options[key]
}
}
return options
}
export default mergeOptionsToData

67
components/helpers/relationsBehavior.js

@ -0,0 +1,67 @@
import isEmpty from './isEmpty'
import debounce from './debounce'
/**
* bind func to obj
*/
function bindFunc(obj, method, observer) {
const oldFn = obj[method]
obj[method] = function(target) {
if (observer) {
observer.call(this, target, {
[method]: true,
})
}
if (oldFn) {
oldFn.call(this, target)
}
}
}
// default methods
const methods = ['linked', 'linkChanged', 'unlinked']
// extra props
const extProps = ['observer']
export default Behavior({
lifetimes: {
created() {
this._debounce = null
},
detached() {
if (this._debounce && this._debounce.cancel) {
this._debounce.cancel()
}
},
},
definitionFilter(defFields) {
const { relations } = defFields
if (!isEmpty(relations)) {
for (const key in relations) {
const relation = relations[key]
// bind func
methods.forEach((method) => bindFunc(relation, method, relation.observer))
// delete extProps
extProps.forEach((prop) => delete relation[prop])
}
}
Object.assign(defFields.methods = (defFields.methods || {}), {
getRelationsName: function(types = ['parent', 'child', 'ancestor', 'descendant']) {
return Object.keys(relations || {}).map((key) => {
if (relations[key] && types.includes(relations[key].type)) {
return key
}
return null
}).filter((v) => !!v)
},
debounce: function(func, wait = 0, immediate = false) {
return (this._debounce = this._debounce || debounce(func.bind(this), wait, immediate)).call(this)
},
})
},
})

46
components/helpers/safeAreaBehavior.js

@ -0,0 +1,46 @@
import { getSystemInfo, checkIPhoneX } from './checkIPhoneX'
const defaultSafeArea = {
top: false,
bottom: false,
}
const setSafeArea = (params) => {
if (typeof params === 'boolean') {
return Object.assign({}, defaultSafeArea, {
top: params,
bottom: params,
})
} else if (params !== null && typeof params === 'object') {
return Object.assign({}, defaultSafeArea)
} else if (typeof params === 'string') {
return Object.assign({}, defaultSafeArea, {
[params]: true,
})
}
return defaultSafeArea
}
export default Behavior({
properties: {
safeArea: {
type: [Boolean, String, Object],
value: false,
},
},
observers: {
safeArea(newVal) {
this.setData({ safeAreaConfig: setSafeArea(newVal) })
},
},
definitionFilter(defFields) {
const { statusBarHeight } = getSystemInfo() || {}
const isIPhoneX = checkIPhoneX()
Object.assign(defFields.data = (defFields.data || {}), {
safeAreaConfig: defaultSafeArea,
statusBarHeight,
isIPhoneX,
})
},
})

57
components/helpers/safeSetDataBehavior.js

@ -0,0 +1,57 @@
export default Behavior({
lifetimes: {
created() {
this.nextCallback = null
},
detached() {
this.cancelNextCallback()
},
},
methods: {
/**
* safeSetData
* @param {Object} nextData 数据对象
* @param {Function} callback 回调函数
*/
safeSetData(nextData, callback) {
this.pendingData = Object.assign({}, this.data, nextData)
callback = this.setNextCallback(callback)
this.setData(nextData, () => {
this.pendingData = null
callback()
})
},
/**
* 设置下一回调函数
* @param {Function} callback 回调函数
*/
setNextCallback(callback) {
let active = true
this.nextCallback = (event) => {
if (active) {
active = false
this.nextCallback = null
callback.call(this, event)
}
}
this.nextCallback.cancel = () => {
active = false
}
return this.nextCallback
},
/**
* 取消下一回调函数
*/
cancelNextCallback() {
if (this.nextCallback !== null) {
this.nextCallback.cancel()
this.nextCallback = null
}
},
},
})

65
components/helpers/shallowEqual.js

@ -0,0 +1,65 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @typechecks
*
*/
/*eslint-disable no-self-compare */
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x, y) {
// SameValue algorithm
if (x === y) {
// Steps 1-5, 7-10
// Steps 6.b-6.e: +0 != -0
// Added the nonzero y check to make Flow happy, but it is redundant
return x !== 0 || y !== 0 || 1 / x === 1 / y;
} else {
// Step 6.a: NaN == NaN
return x !== x && y !== y;
}
}
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA, objB) {
if (is(objA, objB)) {
return true;
}
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
return false;
}
var keysA = Object.keys(objA);
var keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (var i = 0; i < keysA.length; i++) {
if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
export default shallowEqual

138
components/helpers/styleToCssString.js

@ -0,0 +1,138 @@
'use strict';
/**
* CSS properties which accept numbers but are not in units of "px".
*/
var isUnitlessNumber = {
boxFlex: true,
boxFlexGroup: true,
columnCount: true,
flex: true,
flexGrow: true,
flexPositive: true,
flexShrink: true,
flexNegative: true,
fontWeight: true,
lineClamp: true,
lineHeight: true,
opacity: true,
order: true,
orphans: true,
widows: true,
zIndex: true,
zoom: true,
// SVG-related properties
fillOpacity: true,
strokeDashoffset: true,
strokeOpacity: true,
strokeWidth: true
};
/**
* @param {string} prefix vendor-specific prefix, eg: Webkit
* @param {string} key style name, eg: transitionDuration
* @return {string} style name prefixed with `prefix`, properly camelCased, eg:
* WebkitTransitionDuration
*/
function prefixKey(prefix, key) {
return prefix + key.charAt(0).toUpperCase() + key.substring(1);
}
/**
* Support style names that may come passed in prefixed by adding permutations
* of vendor prefixes.
*/
var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
// infinite loop, because it iterates over the newly added props too.
Object.keys(isUnitlessNumber).forEach(function(prop) {
prefixes.forEach(function(prefix) {
isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
});
});
var msPattern = /^ms-/;
var _uppercasePattern = /([A-Z])/g;
/**
* Hyphenates a camelcased string, for example:
*
* > hyphenate('backgroundColor')
* < "background-color"
*
* For CSS style names, use `hyphenateStyleName` instead which works properly
* with all vendor prefixes, including `ms`.
*
* @param {string} string
* @return {string}
*/
function hyphenate(string) {
return string.replace(_uppercasePattern, '-$1').toLowerCase();
}
/**
* Hyphenates a camelcased CSS property name, for example:
*
* > hyphenateStyleName('backgroundColor')
* < "background-color"
* > hyphenateStyleName('MozTransition')
* < "-moz-transition"
* > hyphenateStyleName('msTransition')
* < "-ms-transition"
*
* As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix
* is converted to `-ms-`.
*
* @param {string} string
* @return {string}
*/
function hyphenateStyleName(string) {
return hyphenate(string).replace(msPattern, '-ms-');
}
var isArray = Array.isArray;
var keys = Object.keys;
var counter = 1;
// Follows syntax at https://developer.mozilla.org/en-US/docs/Web/CSS/content,
// including multiple space separated values.
var unquotedContentValueRegex = /^(normal|none|(\b(url\([^)]*\)|chapter_counter|attr\([^)]*\)|(no-)?(open|close)-quote|inherit)((\b\s*)|$|\s+))+)$/;
function buildRule(key, value) {
if (!isUnitlessNumber[key] && typeof value === 'number') {
value = '' + value + 'px';
} else if (key === 'content' && !unquotedContentValueRegex.test(value)) {
value = "'" + value.replace(/'/g, "\\'") + "'";
}
return hyphenateStyleName(key) + ': ' + value + '; ';
}
function styleToCssString(rules) {
var result = ''
if (typeof rules === 'string') {
return rules
}
if (!rules || keys(rules).length === 0) {
return result;
}
var styleKeys = keys(rules);
for (var j = 0, l = styleKeys.length; j < l; j++) {
var styleKey = styleKeys[j];
var value = rules[styleKey];
if (isArray(value)) {
for (var i = 0, len = value.length; i < len; i++) {
result += buildRule(styleKey, value[i]);
}
} else {
result += buildRule(styleKey, value);
}
}
return result;
}
export default styleToCssString

234
components/popover/index.js

@ -0,0 +1,234 @@
import baseComponent from '../helpers/baseComponent'
import classNames from '../helpers/classNames'
import styleToCssString from '../helpers/styleToCssString'
const getPlacements = ([a, s, b] = rects, placement = 'top') => {
switch (placement) {
case 'topLeft':
return {
top: s.scrollTop + a.top - b.height - 4,
left: s.scrollLeft + a.left,
}
case 'top':
return {
top: s.scrollTop + a.top - b.height - 4,
left: s.scrollLeft + a.left + (a.width - b.width) / 2,
}
case 'topRight':
return {
top: s.scrollTop + a.top - b.height - 4,
left: s.scrollLeft + a.left + a.width - b.width,
}
case 'rightTop':
return {
top: s.scrollTop + a.top,
left: s.scrollLeft + a.left + a.width + 4,
}
case 'right':
return {
top: s.scrollTop + a.top + (a.height - b.height) / 2,
left: s.scrollLeft + a.left + a.width + 4,
}
case 'rightBottom':
return {
top: s.scrollTop + a.top + a.height - b.height,
left: s.scrollLeft + a.left + a.width + 4,
}
case 'bottomRight':
return {
top: s.scrollTop + a.top + a.height + 4,
left: s.scrollLeft + a.left + a.width - b.width,
}
case 'bottom':
return {
top: s.scrollTop + a.top + a.height + 4,
left: s.scrollLeft + a.left + (a.width - b.width) / 2,
}
case 'bottomLeft':
return {
top: s.scrollTop + a.top + a.height + 4,
left: s.scrollLeft + a.left,
}
case 'leftBottom':
return {
top: s.scrollTop + a.top + a.height - b.height,
left: s.scrollLeft + a.left - b.width - 4,
}
case 'left':
return {
top: s.scrollTop + a.top + (a.height - b.height) / 2,
left: s.scrollLeft + a.left - b.width - 4,
}
case 'leftTop':
return {
top: s.scrollTop + a.top,
left: s.scrollLeft + a.left - b.width - 4,
}
default:
return {
left: 0,
top: 0,
}
}
}
baseComponent({
properties: {
prefixCls: {
type: String,
value: 'wux-popover',
},
classNames: {
type: null,
value: 'wux-animate--fadeIn',
},
theme: {
type: String,
value: 'light',
},
title: {
type: String,
value: '',
},
content: {
type: String,
value: '',
},
placement: {
type: String,
value: 'top',
},
trigger: {
type: String,
value: 'click',
},
bodyStyle: {
type: [String, Object],
value: '',
observer(newVal) {
this.setData({
extStyle: styleToCssString(newVal),
})
},
},
defaultVisible: {
type: Boolean,
value: false,
},
visible: {
type: Boolean,
value: false,
observer(newVal) {
if (this.data.controlled) {
this.updated(newVal)
}
},
},
controlled: {
type: Boolean,
value: false,
},
mask: {
type: Boolean,
value: false,
},
maskClosable: {
type: Boolean,
value: true,
},
},
data: {
extStyle: '',
popoverStyle: '',
popoverVisible: false,
},
computed: {
classes: ['prefixCls, theme, placement', function(prefixCls, theme, placement) {
const wrap = classNames(prefixCls, {
[`${prefixCls}--theme-${theme}`]: theme,
[`${prefixCls}--placement-${placement}`]: placement,
})
const content = `${prefixCls}__content`
const arrow = `${prefixCls}__arrow`
const inner = `${prefixCls}__inner`
const title = `${prefixCls}__title`
const innerContent = `${prefixCls}__inner-content`
const element = `${prefixCls}__element`
return {
wrap,
content,
arrow,
inner,
title,
innerContent,
element,
}
}],
},
methods: {
updated(popoverVisible) {
if (this.data.popoverVisible !== popoverVisible) {
this.setData({ popoverVisible })
this.setBackdropVisible(popoverVisible)
}
},
getPopoverStyle() {
const { prefixCls, placement } = this.data
const query = wx.createSelectorQuery().in(this)
query.select(`.${prefixCls}__element`).boundingClientRect()
query.selectViewport().scrollOffset()
query.select(`.${prefixCls}`).boundingClientRect()
query.exec((rects) => {
if (rects.filter((n) => !n).length) return
const placements = getPlacements(rects, placement)
const popoverStyle = styleToCssString(placements)
this.setData({
popoverStyle,
})
})
},
/**
* 当组件进入过渡的开始状态时设置气泡框位置信息
*/
onEnter() {
this.getPopoverStyle()
},
onChange() {
const { popoverVisible, controlled } = this.data
const nextVisible = !popoverVisible
if (!controlled) {
this.updated(nextVisible)
}
this.triggerEvent('change', { visible: nextVisible })
},
onClick() {
if (this.data.trigger === 'click') {
this.onChange()
}
},
setBackdropVisible(visible) {
if (this.data.mask && this.wuxBackdrop) {
this.wuxBackdrop[visible ? 'retain' : 'release']()
}
},
onMaskClick() {
const { maskClosable, popoverVisible } = this.data
if (maskClosable && popoverVisible) {
this.onChange()
}
},
},
ready() {
const { defaultVisible, visible, controlled } = this.data
const popoverVisible = controlled ? visible : defaultVisible
if (this.data.mask) {
this.wuxBackdrop = this.selectComponent('#wux-backdrop')
}
this.updated(popoverVisible)
},
})

7
components/popover/index.json

@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"wux-animation-group": "../animation-group/index",
"wux-backdrop": "../backdrop/index"
}
}

17
components/popover/index.wxml

@ -0,0 +1,17 @@
<wux-backdrop id="wux-backdrop" wx:if="{{ mask }}" bind:click="onMaskClick" transparent/>
<view class="wux-class {{ classes.wrap }}" style="{{ extStyle + popoverStyle }}">
<wux-animation-group in="{{ popoverVisible }}" classNames="{{ classNames }}" bind:enter="onEnter">
<view class="{{ classes.content }}">
<view class="{{ classes.arrow }}"></view>
<view class="{{ classes.inner }}">
<view class="{{ classes.title }}" wx:if="{{ title }}">{{ title }}</view>
<slot name="title" wx:else></slot>
<view class="{{ classes.innerContent }}" wx:if="{{ content }}">{{ content }}</view>
<slot name="content" wx:else></slot>
</view>
</view>
</wux-animation-group>
</view>
<view class="{{ classes.element }}" catchtap="onClick">
<slot></slot>
</view>

193
components/popover/index.wxss

@ -0,0 +1,193 @@
.wux-popover {
font-family: Monospaced Number,Chinese Quote,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif;
font-size: 28rpx;
line-height: 1.5;
color: rgba(0,0,0,.65);
box-sizing: border-box;
margin: 0;
padding: 0;
list-style: none;
position: absolute;
top: 0;
left: 0;
z-index: 1030;
cursor: auto;
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
white-space: normal;
font-weight: 400;
text-align: left
}
.wux-popover::after {
content: "";
position: absolute;
background: rgba(255,255,255,.01)
}
.wux-popover__container {
position: absolute;
top: 0;
left: 0;
width: 100%
}
.wux-popover__element {
display: inline-block;
line-height: 1
}
.wux-popover--theme-dark .wux-popover__inner {
background-color: #333
}
.wux-popover--theme-dark.wux-popover--placement-top .wux-popover__arrow,
.wux-popover--theme-dark.wux-popover--placement-topLeft .wux-popover__arrow,
.wux-popover--theme-dark.wux-popover--placement-topRight .wux-popover__arrow {
border-top-color: #333
}
.wux-popover--theme-dark.wux-popover--placement-right .wux-popover__arrow,
.wux-popover--theme-dark.wux-popover--placement-rightBottom .wux-popover__arrow,
.wux-popover--theme-dark.wux-popover--placement-rightTop .wux-popover__arrow {
border-right-color: #333
}
.wux-popover--theme-dark.wux-popover--placement-bottom .wux-popover__arrow,
.wux-popover--theme-dark.wux-popover--placement-bottomLeft .wux-popover__arrow,
.wux-popover--theme-dark.wux-popover--placement-bottomRight .wux-popover__arrow {
border-bottom-color: #333
}
.wux-popover--theme-dark.wux-popover--placement-left .wux-popover__arrow,
.wux-popover--theme-dark.wux-popover--placement-leftBottom .wux-popover__arrow,
.wux-popover--theme-dark.wux-popover--placement-leftTop .wux-popover__arrow {
border-left-color: #333
}
.wux-popover--theme-dark .wux-popover__inner,
.wux-popover--theme-dark .wux-popover__title {
color: #fff
}
.wux-popover--placement-top,
.wux-popover--placement-topLeft,
.wux-popover--placement-topRight {
padding-bottom: 10px
}
.wux-popover--placement-right,
.wux-popover--placement-rightBottom,
.wux-popover--placement-rightTop {
padding-left: 10px
}
.wux-popover--placement-bottom,
.wux-popover--placement-bottomLeft,
.wux-popover--placement-bottomRight {
padding-top: 10px
}
.wux-popover--placement-left,
.wux-popover--placement-leftBottom,
.wux-popover--placement-leftTop {
padding-right: 10px
}
.wux-popover__inner {
background-color: #fff;
background-clip: padding-box;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0,0,0,.15);
color: rgba(0,0,0,.65)
}
.wux-popover__title {
position: relative;
min-width: 120px;
margin: 0;
padding: 5px 16px 4px;
min-height: 32px;
box-sizing: border-box;
color: rgba(0,0,0,.85);
font-weight: 500
}
.wux-popover__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-popover__inner-content {
padding: 24rpx 32rpx
}
.wux-popover__arrow {
width: 0;
height: 0;
position: absolute;
display: block;
border-color: transparent;
border-style: solid;
border-width: 16.97056274rpx
}
.wux-popover--placement-top .wux-popover__arrow,
.wux-popover--placement-topLeft .wux-popover__arrow,
.wux-popover--placement-topRight .wux-popover__arrow {
bottom: 8rpx;
border-bottom-width: 0;
border-top-color: #fff
}
.wux-popover--placement-top .wux-popover__arrow {
left: 50%;
transform: translateX(-50%)
}
.wux-popover--placement-topLeft .wux-popover__arrow {
left: 32rpx
}
.wux-popover--placement-topRight .wux-popover__arrow {
right: 32rpx
}
.wux-popover--placement-right .wux-popover__arrow,
.wux-popover--placement-rightBottom .wux-popover__arrow,
.wux-popover--placement-rightTop .wux-popover__arrow {
left: 8rpx;
border-left-width: 0;
border-right-color: #fff
}
.wux-popover--placement-right .wux-popover__arrow {
top: 50%;
transform: translateY(-50%)
}
.wux-popover--placement-rightTop .wux-popover__arrow {
top: 24rpx
}
.wux-popover--placement-rightBottom .wux-popover__arrow {
bottom: 24rpx
}
.wux-popover--placement-bottom .wux-popover__arrow,
.wux-popover--placement-bottomLeft .wux-popover__arrow,
.wux-popover--placement-bottomRight .wux-popover__arrow {
top: 8rpx;
border-top-width: 0;
border-bottom-color: #fff
}
.wux-popover--placement-bottom .wux-popover__arrow {
left: 50%;
transform: translateX(-50%)
}
.wux-popover--placement-bottomLeft .wux-popover__arrow {
left: 32rpx
}
.wux-popover--placement-bottomRight .wux-popover__arrow {
right: 32rpx
}
.wux-popover--placement-left .wux-popover__arrow,
.wux-popover--placement-leftBottom .wux-popover__arrow,
.wux-popover--placement-leftTop .wux-popover__arrow {
right: 8rpx;
border-right-width: 0;
border-left-color: #fff
}
.wux-popover--placement-left .wux-popover__arrow {
top: 50%;
transform: translateY(-50%)
}
.wux-popover--placement-leftTop .wux-popover__arrow {
top: 24rpx
}
.wux-popover--placement-leftBottom .wux-popover__arrow {
bottom: 24rpx
}

2
pages/storage/order-info/index.js

@ -90,7 +90,7 @@ Scene({
}, },
printOrderInfo: function(){ printOrderInfo: function(){
wx.showLoading({ title: '正在处理', mask: true }) wx.showLoading({ title: '正在处理', mask: true })
printOrder({id: this.data.form.id, printType: 3 }).then(result => {
printOrder({id: this.data.orderInfo.id, printType: 3 }).then(result => {
wx.hideLoading() wx.hideLoading()
util.showToast('即将打印,请稍后') util.showToast('即将打印,请稍后')
}).catch(err => { }).catch(err => {

Loading…
Cancel
Save