@ -0,0 +1,251 @@ | |||||
<template> | |||||
<view class="swiper-item" ref="swiper_item" :style="itemStyle"> | |||||
<view class="item-cntent" bubble="true" @click.stop="onClick"><slot></slot></view> | |||||
</view> | |||||
</template> | |||||
<script> | |||||
import { getStyleStr } from '../../utils/style.js'; | |||||
// #ifdef APP-NVUE | |||||
const animation = uni.requireNativePlugin('animation'); | |||||
// #endif | |||||
/** | |||||
* esc-swiper-item | |||||
* @description esc-swiper-item (不支持使用class) | |||||
* @property {Number} index 索引(必填) | |||||
* @property {Boolean} clickAny 可以点击任意项 | |||||
* @event {Function} click 点击事件 | |||||
*/ | |||||
export default { | |||||
name: 'esc-swiper-item', | |||||
inject: ['config'], | |||||
props: { | |||||
index: { | |||||
type: Number, | |||||
default: 0 | |||||
}, | |||||
clickAny: { | |||||
type: Boolean, | |||||
default: false | |||||
} | |||||
}, | |||||
data() { | |||||
return { | |||||
isAnimated: false, | |||||
timingFunction: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', | |||||
duration: 0, | |||||
current: 0, | |||||
position: 0, | |||||
mScale: 0, | |||||
canClick: true | |||||
}; | |||||
}, | |||||
created() {}, | |||||
computed: { | |||||
size() { | |||||
return this.config.size; | |||||
}, | |||||
width() { | |||||
return this.config.width; | |||||
}, | |||||
height() { | |||||
return this.config.height; | |||||
}, | |||||
itemWidth() { | |||||
return this.config.itemWidth; | |||||
}, | |||||
itemHeight() { | |||||
return this.config.itemHeight; | |||||
}, | |||||
space() { | |||||
return this.config.space; | |||||
}, | |||||
itemStyle() { | |||||
if (this.index == this.size - 1) { | |||||
return this.rightSpaceStyle(); | |||||
} else if (this.index == this.size - 2) { | |||||
return this.centerSpaceStyle(); | |||||
} else { | |||||
return this.leftSpaceStyle(); | |||||
} | |||||
}, | |||||
scale() { | |||||
if (!this.config.is3D) { | |||||
return 1; | |||||
} | |||||
if (this.myCurrent == this.current) { | |||||
return this.mScale || this.config.scale; | |||||
} else { | |||||
return this.mScale || 1; | |||||
} | |||||
}, | |||||
// 当前swiper-item所属current索引 | |||||
myCurrent() { | |||||
if (!this.config.isCircular) { | |||||
return this.index; | |||||
} | |||||
const p = this.index; | |||||
const plus = this.config.plus; | |||||
const actSize = this.size - plus * 2; | |||||
let current = 0; | |||||
if (p < plus) { | |||||
current = p + (actSize - plus); | |||||
} else if (p >= this.size - plus) { | |||||
current = p - (actSize + plus); | |||||
} else { | |||||
current = p - plus; | |||||
} | |||||
return current; | |||||
} | |||||
}, | |||||
methods: { | |||||
itemSize() { | |||||
let style = { | |||||
width: this.itemWidth + 'rpx', | |||||
height: (this.itemHeight || this.height) + 'rpx' | |||||
}; | |||||
if (this.config.is3D) { | |||||
// #ifndef APP-NVUE | |||||
if (this.isAnimated) { | |||||
style.transition = 'transform ' + this.duration + 'ms ' + this.timingFunction; | |||||
} else { | |||||
style.transition = ''; | |||||
} | |||||
style.transform = 'scale(' + this.scale + ')'; | |||||
// #endif | |||||
// #ifdef APP-NVUE | |||||
const isIOS = uni.getSystemInfoSync().platform == 'ios'; | |||||
if (isIOS) { | |||||
style.transform = 'scale(' + this.scale + ')'; | |||||
} else { | |||||
if (!this.isAnimated) style.transform = 'scale(' + this.scale + ')'; | |||||
} | |||||
// #endif | |||||
} | |||||
return style; | |||||
}, | |||||
leftSpaceStyle() { | |||||
return getStyleStr({ | |||||
...this.itemSize(), | |||||
marginLeft: this.space + 'rpx' | |||||
}); | |||||
}, | |||||
centerSpaceStyle() { | |||||
return getStyleStr({ | |||||
...this.itemSize(), | |||||
marginLeft: this.space + 'rpx', | |||||
marginRight: this.space + 'rpx' | |||||
}); | |||||
}, | |||||
rightSpaceStyle() { | |||||
return getStyleStr({ | |||||
...this.itemSize(), | |||||
marginRight: this.space + 'rpx' | |||||
}); | |||||
}, | |||||
onClick(e) { | |||||
if (!this.canClick) { | |||||
return; | |||||
} | |||||
// #ifdef APP-NVUE | |||||
e.stopPropagation(); | |||||
// #endif | |||||
// 点击任意项 | |||||
if (this.clickAny) { | |||||
this.$emit('click', e); | |||||
return; | |||||
} | |||||
// 只能点击当前项 | |||||
if (this.myCurrent == this.current) { | |||||
this.$emit('click', e); | |||||
} | |||||
}, | |||||
restoreScale(duration) { | |||||
if (!this.config.is3D) { | |||||
return; | |||||
} | |||||
// #ifndef APP-NVUE | |||||
this.duration = duration; | |||||
this.isAnimated = true; | |||||
this.mScale = 0; | |||||
setTimeout(() => { | |||||
this.duration = 0; | |||||
this.isAnimated = false; | |||||
}, duration); | |||||
// #endif | |||||
// #ifdef APP-NVUE | |||||
this.isAnimated = true; | |||||
this.mScale = 0; | |||||
animation.transition( | |||||
this.$refs['swiper_item'].ref, | |||||
{ | |||||
styles: { | |||||
transform: 'scale(' + this.scale + ')' | |||||
}, | |||||
duration, //ms | |||||
timingFunction: this.timingFunction, | |||||
needLayout: false, | |||||
delay: 0 //ms | |||||
}, | |||||
() => { | |||||
this.isAnimated = false; | |||||
} | |||||
); | |||||
// #endif | |||||
}, | |||||
restoreToScale(scale, duration) { | |||||
if (!this.config.is3D) { | |||||
return; | |||||
} | |||||
// #ifndef APP-NVUE | |||||
this.duration = duration; | |||||
this.isAnimated = true; | |||||
this.mScale = scale; | |||||
setTimeout(() => { | |||||
this.duration = 0; | |||||
this.isAnimated = false; | |||||
this.mScale = 0; | |||||
}, duration); | |||||
// #endif | |||||
// #ifdef APP-NVUE | |||||
this.isAnimated = true; | |||||
animation.transition( | |||||
this.$refs['swiper_item'].ref, | |||||
{ | |||||
styles: { | |||||
transform: 'scale(' + scale + ')' | |||||
}, | |||||
duration, //ms | |||||
timingFunction: this.timingFunction, | |||||
needLayout: false, | |||||
delay: 0 //ms | |||||
}, | |||||
() => { | |||||
this.isAnimated = false; | |||||
this.mScale = 0; | |||||
} | |||||
); | |||||
// #endif | |||||
} | |||||
} | |||||
}; | |||||
</script> | |||||
<style scoped> | |||||
.swiper-item { | |||||
position: relative; | |||||
} | |||||
.item-cntent { | |||||
position: absolute; | |||||
top: 0; | |||||
right: 0; | |||||
bottom: 0; | |||||
left: 0; | |||||
/* #ifndef APP-NVUE */ | |||||
display: flex; | |||||
/* #endif */ | |||||
} | |||||
</style> |
@ -0,0 +1,21 @@ | |||||
/** | |||||
* getSwiperList | |||||
* @description 获取Swiper数据 | |||||
* @param {Array} list 原数据 | |||||
* @param {Object} options 配置 | |||||
* @param {Boolean} options.circular 是否循环 | |||||
* @param {Number} options.plus 左右追加个数(开启循环必填,至少为2) | |||||
* @return {Array} | |||||
*/ | |||||
export function getSwiperList(list, options = { | |||||
circular: true, | |||||
plus: 2 | |||||
}) { | |||||
if (!options.circular) { | |||||
return list | |||||
} | |||||
const plus = options.plus || 2 | |||||
const leftPlusList = [...list].reverse().slice(0, plus).reverse(); | |||||
const rightPlusList = [...list].slice(0, plus); | |||||
return [].concat(leftPlusList).concat(list).concat(rightPlusList); | |||||
} |
@ -0,0 +1,198 @@ | |||||
<template> | |||||
<!-- #ifdef APP-VUE || H5 --> | |||||
<view class="box" :style="boxStyle"> | |||||
<view | |||||
ref="container" | |||||
class="container" | |||||
:change:prop="swipe.changeData" | |||||
:prop="wxsData" | |||||
@touchstart.stop="swipe.touchstart" | |||||
@touchmove.stop="swipe.touchmove" | |||||
@touchend.stop="swipe.touchend" | |||||
:style="containerStyle" | |||||
> | |||||
<slot></slot> | |||||
</view> | |||||
</view> | |||||
<!-- #endif --> | |||||
<!-- #ifdef MP-WEIXIN --> | |||||
<view class="box" :style="boxStyle"> | |||||
<view | |||||
ref="container" | |||||
class="container" | |||||
:change:prop="swipe.changeData" | |||||
:prop="wxsData" | |||||
@touchstart="swipe.touchstart" | |||||
@touchmove="swipe.touchmove" | |||||
@touchend="swipe.touchend" | |||||
:style="containerStyle" | |||||
> | |||||
<slot></slot> | |||||
</view> | |||||
</view> | |||||
<!-- #endif --> | |||||
<!-- #ifdef APP-NVUE --> | |||||
<view class="box" :style="boxStyle"> | |||||
<view ref="container" class="container" @horizontalpan="touchstart" :style="containerStyle"><slot></slot></view> | |||||
</view> | |||||
<!-- #endif --> | |||||
<!-- 其他平台使用 js ,长列表性能可能会有影响--> | |||||
<!-- #ifndef APP-PLUS || MP-WEIXIN || H5 --> | |||||
<view class="box" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend" :style="boxStyle"> | |||||
<view ref="container" class="container" :style="containerStyle"><slot></slot></view> | |||||
</view> | |||||
<!-- #endif --> | |||||
</template> | |||||
<script src="./mixins/index.wxs" module="swipe" lang="wxs"></script> | |||||
<script> | |||||
import SwiperMixin from './mixins/base.mixin.js'; | |||||
// #ifdef APP-VUE || MP-WEIXIN || H5 | |||||
import MpMixin from './mixins/mpwxs.js'; | |||||
// #endif | |||||
// #ifdef APP-NVUE | |||||
import BindingxMixin from './mixins/bindingx.js'; | |||||
// #endif | |||||
// #ifndef APP-PLUS || MP-WEIXIN || H5 | |||||
import OtherMixin from './mixins/mpother.js'; | |||||
// #endif | |||||
const mixins = [ | |||||
// #ifdef APP-VUE || MP-WEIXIN || H5 | |||||
MpMixin, | |||||
// #endif | |||||
// #ifdef APP-NVUE | |||||
BindingxMixin, | |||||
// #endif | |||||
// #ifndef APP-PLUS || MP-WEIXIN || H5 | |||||
OtherMixin, | |||||
// #endif | |||||
SwiperMixin | |||||
]; | |||||
/** | |||||
* esc-swiper | |||||
* @description 自定义swiper (不支持使用class) | |||||
* @property {String} mode = [normal|3d] 模式 | |||||
* @property {Number} scale 3D模式选中项的scale | |||||
* @property {Number} width 宽 | |||||
* @property {Number} height 高 | |||||
* @property {Number} itemWidth 项宽 | |||||
* @property {Number} itemHeight 项高 | |||||
* @property {Number} space 间距 | |||||
* @property {Number} plus 左右追加个数(开启循环必填,至少为2) | |||||
* @property {Number} current 选中项索引 | |||||
* @property {Boolean} autoplay 自动轮播 | |||||
* @property {Boolean} circular 是否循环,如果开启,至少需要3项 | |||||
* @property {Boolean} bounce 阻尼效果 | |||||
* @event {Function} change 索引变化 | |||||
*/ | |||||
export default { | |||||
name: 'esc-swiper', | |||||
mixins, | |||||
props: { | |||||
mode: { | |||||
type: String, | |||||
default: 'normal' | |||||
}, | |||||
scale: Number, | |||||
width: Number, | |||||
height: Number, | |||||
itemWidth: { | |||||
type: Number, | |||||
default: 0 | |||||
}, | |||||
itemHeight: { | |||||
type: Number, | |||||
default: 0 | |||||
}, | |||||
space: { | |||||
type: Number, | |||||
default: 0 | |||||
}, | |||||
plus: { | |||||
type: Number, | |||||
default: 0 | |||||
}, | |||||
autoplay: { | |||||
type: Boolean, | |||||
default: false | |||||
}, | |||||
current: { | |||||
type: Number, | |||||
default: 0 | |||||
}, | |||||
interval: { | |||||
type: Number, | |||||
default: 5000 | |||||
}, | |||||
duration: { | |||||
type: Number, | |||||
default: 500 | |||||
}, | |||||
circular: { | |||||
type: Boolean, | |||||
default: false | |||||
}, | |||||
bounce: { | |||||
type: Boolean, | |||||
default: true | |||||
}, | |||||
size: { | |||||
type: Number, | |||||
default: 0 | |||||
} | |||||
}, | |||||
provide() { | |||||
return { | |||||
config: { | |||||
is3D: this.is3D, | |||||
isCircular: this.circular, | |||||
scale: this.scale, | |||||
size: this._size, | |||||
width: this.width, | |||||
height: this.height, | |||||
itemWidth: this.itemWidth, | |||||
itemHeight: this.itemHeight, | |||||
space: this.space, | |||||
plus: this.plus | |||||
} | |||||
}; | |||||
}, | |||||
mounted() { | |||||
if (this.autoplay) { | |||||
this.autoplayInterval = setInterval(() => { | |||||
this.next(); | |||||
}, this.interval); | |||||
} | |||||
}, | |||||
watch: { | |||||
autoplay(newV) { | |||||
if (!newV) { | |||||
clearInterval(this.autoplayInterval); | |||||
} else { | |||||
this.autoplayInterval = setInterval(() => { | |||||
this.next(); | |||||
}, this.interval); | |||||
} | |||||
} | |||||
}, | |||||
methods: {} | |||||
}; | |||||
</script> | |||||
<style scoped> | |||||
.box { | |||||
position: relative; | |||||
overflow: hidden; | |||||
} | |||||
.container { | |||||
position: absolute; | |||||
top: 0; | |||||
/* #ifndef APP-NVUE */ | |||||
display: flex; | |||||
/* #endif */ | |||||
flex-direction: row; | |||||
align-items: center; | |||||
} | |||||
</style> |
@ -0,0 +1,303 @@ | |||||
import { | |||||
getStyleStr | |||||
} from '../../../utils/style.js'; | |||||
// #ifdef APP-NVUE | |||||
const animation = uni.requireNativePlugin('animation') | |||||
// #endif | |||||
export default { | |||||
data() { | |||||
return { | |||||
timingFunction: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', | |||||
isAnimated: false, | |||||
isScrolling: false, | |||||
customDuration: 0, | |||||
left: 0, | |||||
mCurrent: this.current | |||||
}; | |||||
}, | |||||
created() {}, | |||||
mounted() { | |||||
// #ifdef MP | |||||
this.swiperViews = this.$children | |||||
// #endif | |||||
// #ifdef APP-PLUS || H5 | |||||
this.swiperViews = this.$slots.default.map(it => it.child) | |||||
// #endif | |||||
this._setLeft(); | |||||
this.mCurrent = this.current | |||||
this._notifyCurrentForItems(this.current, this.position) | |||||
}, | |||||
watch: { | |||||
mCurrent() { | |||||
let current = this.mCurrent | |||||
if (this.circular) { | |||||
if (this.position == 1) { | |||||
current = this.actualSize - (this.plus - 1) | |||||
// console.log('最前了', current) | |||||
} else if (this.position == this._size - 2) { | |||||
current = this.plus - 2; | |||||
// console.log('最后了', current) | |||||
} | |||||
if (current < 0) { | |||||
current = this.position + 1 | |||||
} | |||||
current %= this.actualSize | |||||
} | |||||
// console.log('position', this.position, current) | |||||
this.$emit('update:current', current) | |||||
this.$emit('change', current) | |||||
this._notifyCurrentForItems(current, this.position) | |||||
} | |||||
}, | |||||
computed: { | |||||
is3D() { | |||||
return this.mode == '3d' | |||||
}, | |||||
position() { | |||||
return this.circular ? (this.mCurrent + this.plus) : this.mCurrent | |||||
}, | |||||
manualDuration() { | |||||
if (this.customDuration > 0) | |||||
return this.customDuration | |||||
return this.isAnimated ? this.duration : 0 | |||||
}, | |||||
boxStyle() { | |||||
return getStyleStr({ | |||||
width: this.width + 'rpx', | |||||
height: this.height + 'rpx' | |||||
}); | |||||
}, | |||||
containerStyle() { | |||||
const style = { | |||||
height: this.height + 'rpx' | |||||
}; | |||||
// #ifdef APP-NVUE | |||||
// FIXME: 理论isAnimated=false应该不设置transform,但是ios有个奇怪的问题,top不为0导致布局错位 | |||||
const isIOS = uni.getSystemInfoSync().platform == 'ios' | |||||
if (isIOS) { | |||||
style.transform = 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)' | |||||
} else { | |||||
if (this.isAnimated == false) { | |||||
style.transform = 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)' | |||||
} | |||||
} | |||||
// #endif | |||||
// #ifndef APP-NVUE | |||||
style.left = this.left + 'rpx' | |||||
style.transition = 'left ' + this.manualDuration + 'ms ' + this.timingFunction | |||||
// #endif | |||||
return getStyleStr(style); | |||||
}, | |||||
_size() { | |||||
return (this.$slots && this.$slots.default && this.$slots.default.length) || this.size; | |||||
}, | |||||
// plus * 2 | |||||
plusSize() { | |||||
return this.circular ? this.plus * 2 : 0; | |||||
}, | |||||
// 真实长度 | |||||
actualSize() { | |||||
return this._size - this.plusSize; | |||||
} | |||||
}, | |||||
methods: { | |||||
prev() { | |||||
if (this.isAnimated) return; | |||||
if (this.isScrolling) return; | |||||
if (this.mCurrent == 0 && this.circular == false) return | |||||
this.mCurrent--; | |||||
this._run() | |||||
}, | |||||
next() { | |||||
if (this.isAnimated) return; | |||||
if (this.isScrolling) return; | |||||
if (this.circular == true) { | |||||
this.mCurrent++; | |||||
if (this.mCurrent == this._size) { | |||||
this.mCurrent = 0; | |||||
} | |||||
} else { | |||||
if (this.mCurrent == this._size - 1) return | |||||
this.mCurrent++; | |||||
} | |||||
this._run() | |||||
}, | |||||
moveTo(e) { | |||||
if (this.isAnimated) return | |||||
const { | |||||
deltaX, | |||||
left | |||||
} = e | |||||
this.isScrolling = true | |||||
if (!this.circular) { | |||||
if ( | |||||
// 第一项,不能向右滑(上一项) | |||||
(deltaX > 0 && this.mCurrent == 0) || | |||||
// 最后一项,不能向左滑(下一项) | |||||
(deltaX < 0 && this.mCurrent == this._size - 1) | |||||
) { | |||||
if (!this.bounce) return | |||||
// 添加阻尼滑动 | |||||
const _left = this._left || this.wxsData.left | |||||
this.left = _left + (deltaX * (1 - Math.abs(deltaX) * 3 / (this.width * 5))) | |||||
this._set3DScale(deltaX) | |||||
return | |||||
} | |||||
} | |||||
this.left = left | |||||
this._set3DScale(deltaX) | |||||
}, | |||||
moveEnd(e) { | |||||
const { | |||||
velocity, | |||||
deltaX, | |||||
deltaY | |||||
} = e | |||||
this.isScrolling = false | |||||
if (!this.circular) { | |||||
// 第一项,不能向右滑(上一项) | |||||
if (deltaX > 0 && this.mCurrent == 0) { | |||||
this._restoreStartTouch() | |||||
return | |||||
} | |||||
// 最后一项,不能向左滑(下一项) | |||||
if (deltaX < 0 && this.mCurrent == this._size - 1) { | |||||
this._restoreStartTouch() | |||||
return | |||||
} | |||||
} | |||||
const isTurnPage = Math.abs(deltaX) > this.itemWidth / 2 | |||||
if (isTurnPage || velocity > 0.2) { | |||||
if (deltaX < 0) { | |||||
this.customDuration = 350 | |||||
this.next() | |||||
} else if (deltaX > 0) { | |||||
this.customDuration = 350 | |||||
this.prev() | |||||
} | |||||
} else { | |||||
this._restoreStartTouch() | |||||
} | |||||
}, | |||||
_set3DScale(deltaX) { | |||||
if (this.is3D) { | |||||
const min = Math.min | |||||
const maxScale = Math.abs(this.scale - 1) | |||||
const mScale = deltaX * maxScale / this.width | |||||
const mRealScale = min(this.scale, this.scale - Math.abs(mScale)) | |||||
this.swiperViews[this.position].mScale = mRealScale < 1 ? 1 : mRealScale | |||||
if (this.position - 1 > -1) { | |||||
this.swiperViews[this.position - 1].mScale = mScale > 0 ? min(this.scale, 1 + mScale) : min(1, 1 + mScale) | |||||
} | |||||
if (this.position + 1 < this._size) { | |||||
this.swiperViews[this.position + 1].mScale = mScale > 0 ? min(1, 1 - mScale) : min(this.scale, 1 - mScale) | |||||
} | |||||
} | |||||
}, | |||||
_restoreStartTouch() { | |||||
const self = this | |||||
this.customDuration = 300 | |||||
// #ifdef APP-VUE || MP-WEIXIN || H5 | |||||
this.left = this.wxsData.left | |||||
// #endif | |||||
// #ifndef APP-PLUS || MP-WEIXIN || H5 | |||||
this.left = this._left | |||||
// #endif | |||||
this._run(false) | |||||
if (this.is3D) { | |||||
this.swiperViews[this.position].restoreScale(this.manualDuration) | |||||
if (this.position - 1 > -1) { | |||||
this.swiperViews[this.position - 1].restoreScale(this.manualDuration) | |||||
} | |||||
if (this.position + 1 < this._size) { | |||||
this.swiperViews[this.position + 1].restoreScale(this.manualDuration) | |||||
} | |||||
} | |||||
}, | |||||
_notifyCurrentForItems(current, position) { | |||||
this.swiperViews && this.swiperViews.forEach(it => { | |||||
it.current = current | |||||
it.position = position | |||||
}) | |||||
}, | |||||
_run(isSet = true) { | |||||
this.isAnimated = true; | |||||
if (isSet) | |||||
this._setLeft(); | |||||
const self = this; | |||||
if (this.is3D) { | |||||
this.swiperViews[this.position].restoreToScale(this.scale, this.manualDuration) | |||||
if (this.position - 1 > -1) { | |||||
this.swiperViews[this.position - 1].restoreToScale(1, this.manualDuration) | |||||
} | |||||
if (this.position + 1 < this._size) { | |||||
this.swiperViews[this.position + 1].restoreToScale(1, this.manualDuration) | |||||
} | |||||
} | |||||
// #ifdef APP-NVUE | |||||
animation.transition(this.$refs.container, { | |||||
styles: { | |||||
transform: 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)', | |||||
}, | |||||
duration: this.manualDuration, //ms | |||||
timingFunction: this.timingFunction, | |||||
needLayout: false, | |||||
delay: 0 //ms | |||||
}, function() { | |||||
self._reset(); | |||||
}) | |||||
// #endif | |||||
// #ifndef APP-NVUE | |||||
setTimeout(() => { | |||||
this._reset(); | |||||
}, this.manualDuration); | |||||
// #endif | |||||
}, | |||||
_setLeft() { | |||||
if (this.circular == true) { | |||||
const s1 = (this.width - this.itemWidth - this.space * 2) / 2; | |||||
let left = (this.plus + this.mCurrent) * (this.space + this.itemWidth) - s1; | |||||
this.left = -left; | |||||
} else { | |||||
this.left = -(this.itemWidth + this.space) * this.mCurrent | |||||
} | |||||
// #ifdef APP-VUE || MP-WEIXIN || H5 | |||||
this.wxsData = { | |||||
left: this.left, | |||||
bounce: this.bounce | |||||
} | |||||
// #endif | |||||
}, | |||||
_reset() { | |||||
this.isScrolling = false | |||||
this.isAnimated = false | |||||
this.customDuration = 0 | |||||
if (this.circular == true) { | |||||
if (this.position == 1) { | |||||
this.mCurrent = this.actualSize - (this.plus - 1); | |||||
this._setLeft(); | |||||
this._restoreScale() | |||||
} | |||||
// -2:原数组索引-1 + plus数组索引-1 | |||||
if (this.position == this._size - 2) { | |||||
this.mCurrent = this.plus - 2; | |||||
this._setLeft(); | |||||
this._restoreScale() | |||||
} | |||||
} | |||||
}, | |||||
_restoreScale() { | |||||
if (this.is3D) { | |||||
this.swiperViews[this.position].restoreToScale(this.scale, 0) | |||||
if (this.position - 1 > -1) { | |||||
this.swiperViews[this.position - 1].restoreToScale(1, 0) | |||||
} | |||||
if (this.position + 1 < this._size) { | |||||
this.swiperViews[this.position + 1].restoreToScale(1, 0) | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,91 @@ | |||||
const BindingX = uni.requireNativePlugin('bindingx'); | |||||
const animation = uni.requireNativePlugin('animation'); | |||||
export default { | |||||
mounted() { | |||||
this.container = this.getEl(this.$refs['container']) | |||||
}, | |||||
methods: { | |||||
_notifyTouchChangeForItems(isTouchEnd) { | |||||
this.swiperViews && this.swiperViews.forEach(it => { | |||||
it.canClick = isTouchEnd | |||||
}) | |||||
}, | |||||
touchstart(e) { | |||||
if (this.isAnimated) return; | |||||
if (this.stop) return | |||||
this.isScrolling = true | |||||
this._notifyTouchChangeForItems(false) | |||||
this.stop = true | |||||
this.startTime = new Date().getTime() | |||||
const props = [{ | |||||
element: this.container, | |||||
property: 'transform.translateX', | |||||
expression: uni.upx2px(this.left) + '+x' | |||||
}] | |||||
if (this.is3D) { | |||||
const deltaScale = `${this.scale}/${this.width}*x` | |||||
const currentScale = `${this.scale}-abs(x)/${this.width}*1` | |||||
const realScale = `min(${currentScale},${this.scale})` | |||||
const currentCardExp = `${realScale} < 1 ? 1 : ${realScale}`; | |||||
const leftCardExp = `${deltaScale} > 0 ? min(${this.scale},(1 + ${deltaScale})) : min(1,(1 + ${deltaScale}))`; | |||||
const rightCardExp = `${deltaScale} > 0 ? min(1,(1 - ${deltaScale})) : min(${this.scale},(1 - ${deltaScale}))`; | |||||
props.push({ | |||||
element: this.getSwipteItemEl(this.position), | |||||
property: 'transform.scale', | |||||
expression: currentCardExp | |||||
}) | |||||
if (this.position - 1 > -1) { | |||||
props.push({ | |||||
element: this.getSwipteItemEl(this.position - 1), | |||||
property: 'transform.scale', | |||||
expression: leftCardExp | |||||
}) | |||||
} | |||||
if (this.position + 1 > this._size) { | |||||
props.push({ | |||||
element: this.getSwipteItemEl(this.position + 1), | |||||
property: 'transform.scale', | |||||
expression: rightCardExp | |||||
}) | |||||
} | |||||
} | |||||
this.eventpan = BindingX.bind({ | |||||
anchor: this.container, | |||||
eventType: 'pan', | |||||
props | |||||
}, (e) => { | |||||
if (e.state === 'end' || | |||||
e.state === 'exit') { | |||||
setTimeout(() => { | |||||
this._notifyTouchChangeForItems(true) | |||||
}, 10) | |||||
const timing = new Date().getTime() - this.startTime | |||||
const velocity = Math.abs(e.deltaX / timing) | |||||
this.stop = false | |||||
this.isScrolling = false | |||||
if (this.eventpan && this.eventpan.token) { | |||||
BindingX.unbind({ | |||||
token: this.eventpan.token, | |||||
eventType: 'pan' | |||||
}) | |||||
} | |||||
this.moveEnd({ | |||||
velocity, | |||||
deltaX: e.deltaX, | |||||
deltaY: e.deltaY | |||||
}) | |||||
} | |||||
}) | |||||
}, | |||||
getSwipteItemEl(idx) { | |||||
return this.swiperViews[idx].$el.ref | |||||
}, | |||||
/** | |||||
* 获取ref | |||||
* @param {Object} el | |||||
*/ | |||||
getEl(el) { | |||||
return el.ref | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,143 @@ | |||||
var MIN_DISTANCE = 10; | |||||
function changeData(newValue, oldValue, ownerInstance, instance) { | |||||
var state = instance.getState(); | |||||
if (newValue.left != undefined) { | |||||
state.left = newValue.left | |||||
} | |||||
if (newValue.bounce != undefined) { | |||||
state.bounce = newValue.bounce | |||||
} | |||||
// console.log('changeData', JSON.stringify(newValue)) | |||||
} | |||||
/** | |||||
* 开始触摸操作 | |||||
* @param {Object} e | |||||
* @param {Object} ins | |||||
*/ | |||||
function touchstart(e, ins) { | |||||
var instance = e.instance; | |||||
// 计算滑动开始位置 | |||||
stopTouchStart(e, ins) | |||||
} | |||||
/** | |||||
* 开始滑动操作 | |||||
* @param {Object} e | |||||
* @param {Object} ownerInstance | |||||
*/ | |||||
function touchmove(e, ownerInstance) { | |||||
var instance = e.instance; | |||||
// 是否可以滑动页面 | |||||
stopTouchMove(e); | |||||
if (e.preventDefault) { | |||||
// 阻止页面滚动 | |||||
e.preventDefault() | |||||
} | |||||
// var state = instance.getState(); | |||||
// && state.bounce | |||||
move(instance, ownerInstance) | |||||
return false | |||||
} | |||||
/** | |||||
* 结束触摸操作 | |||||
* @param {Object} e | |||||
* @param {Object} ownerInstance | |||||
*/ | |||||
function touchend(e, ownerInstance) { | |||||
var instance = e.instance; | |||||
var state = instance.getState() | |||||
ownerInstance.callMethod('moveEnd', { | |||||
velocity: Math.abs(state.deltaX / state.timing), | |||||
direction: state.direction, | |||||
deltaX: state.deltaX, | |||||
deltaY: state.deltaY | |||||
}) | |||||
} | |||||
/** | |||||
* 设置移动距离 | |||||
* @param {Object} instance | |||||
* @param {Object} ownerInstance | |||||
*/ | |||||
function move(instance, ownerInstance) { | |||||
var state = instance.getState() | |||||
var value = state.deltaX || 0 | |||||
var state = instance.getState() | |||||
if (state.direction == 'horizontal') { | |||||
// instance.requestAnimationFrame(function() { | |||||
// instance.setStyle({ | |||||
// transform: 'translateX(' + value + 'px)', | |||||
// '-webkit-transform': 'translateX(' + value + 'px)' | |||||
// }) | |||||
// }) | |||||
ownerInstance.callMethod('moveTo', { | |||||
deltaX: value, | |||||
deltaY: state.deltaY || 0, | |||||
left: state.left + value | |||||
}) | |||||
} | |||||
} | |||||
/** | |||||
* 滑动中,是否禁止打开 | |||||
* @param {Object} event | |||||
*/ | |||||
function stopTouchMove(event) { | |||||
var instance = event.instance; | |||||
var state = instance.getState(); | |||||
var touch = event.touches[0]; | |||||
state.timing = getDate().getTime() - state.startTime; | |||||
state.deltaX = touch.clientX - state.startX; | |||||
state.deltaY = touch.clientY - state.startY; | |||||
state.offsetX = Math.abs(state.deltaX); | |||||
state.offsetY = Math.abs(state.deltaY); | |||||
state.direction = state.direction || getDirection(state.offsetX, state.offsetY); | |||||
} | |||||
/** | |||||
* 设置滑动开始位置 | |||||
* @param {Object} event | |||||
*/ | |||||
function stopTouchStart(event) { | |||||
var instance = event.instance; | |||||
var state = instance.getState(); | |||||
resetTouchStatus(instance); | |||||
var touch = event.touches[0]; | |||||
state.startTime = getDate().getTime(); | |||||
state.startX = touch.clientX; | |||||
state.startY = touch.clientY; | |||||
} | |||||
function getDirection(x, y) { | |||||
if (x > y && x > MIN_DISTANCE) { | |||||
return 'horizontal'; | |||||
} | |||||
if (y > x && y > MIN_DISTANCE) { | |||||
return 'vertical'; | |||||
} | |||||
return ''; | |||||
} | |||||
/** | |||||
* 重置滑动状态 | |||||
* @param {Object} event | |||||
*/ | |||||
function resetTouchStatus(instance) { | |||||
var state = instance.getState(); | |||||
state.direction = ''; | |||||
state.deltaX = 0; | |||||
state.deltaY = 0; | |||||
state.offsetX = 0; | |||||
state.offsetY = 0; | |||||
} | |||||
module.exports = { | |||||
changeData: changeData, | |||||
touchstart: touchstart, | |||||
touchmove: touchmove, | |||||
touchend: touchend | |||||
} |
@ -0,0 +1,62 @@ | |||||
const MIN_DISTANCE = 10; | |||||
export default { | |||||
methods: { | |||||
touchstart(e) { | |||||
this._left = this.left | |||||
this.stopTouchStart(e) | |||||
}, | |||||
touchmove(e) { | |||||
// 是否可以滑动页面 | |||||
this.stopTouchMove(e); | |||||
if (this.direction == 'horizontal') { | |||||
this.moveTo({ | |||||
deltaX: this.deltaX, | |||||
deltaY: this.deltaY || 0, | |||||
left: this._left + this.deltaX | |||||
}) | |||||
} | |||||
// FIXME: 冒泡 | |||||
return false | |||||
}, | |||||
touchend() { | |||||
this.moveEnd({ | |||||
velocity: Math.abs(this.deltaX / this.timing), | |||||
direction: this.direction, | |||||
deltaX: this.deltaX, | |||||
deltaY: this.deltaY, | |||||
}) | |||||
}, | |||||
stopTouchStart(event) { | |||||
this.resetTouchStatus(); | |||||
const touch = event.touches[0]; | |||||
this.startTime = new Date().getTime(); | |||||
this.startX = touch.clientX; | |||||
this.startY = touch.clientY; | |||||
}, | |||||
stopTouchMove(event) { | |||||
const touch = event.touches[0]; | |||||
this.timing = new Date().getTime() - this.startTime; | |||||
this.deltaX = touch.clientX - this.startX; | |||||
this.deltaY = touch.clientY - this.startY; | |||||
this.offsetX = Math.abs(this.deltaX); | |||||
this.offsetY = Math.abs(this.deltaY); | |||||
this.direction = this.direction || this.getDirection(this.offsetX, this.offsetY); | |||||
}, | |||||
getDirection(x, y) { | |||||
if (x > y && x > MIN_DISTANCE) { | |||||
return 'horizontal'; | |||||
} | |||||
if (y > x && y > MIN_DISTANCE) { | |||||
return 'vertical'; | |||||
} | |||||
return ''; | |||||
}, | |||||
resetTouchStatus() { | |||||
this.direction = ''; | |||||
this.deltaX = 0; | |||||
this.deltaY = 0; | |||||
this.offsetX = 0; | |||||
this.offsetY = 0; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,7 @@ | |||||
export default { | |||||
data() { | |||||
return { | |||||
wxsData: {} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,15 @@ | |||||
const toLine = (name) => { | |||||
return name.replace(/([A-Z])/g, '-$1').toLowerCase(); | |||||
} | |||||
/** | |||||
* style对象转化为style字符串 | |||||
* @return {string} | |||||
*/ | |||||
export const getStyleStr = (styleObject) => { | |||||
let transfrom = ''; | |||||
for (let i in styleObject) { | |||||
let line = toLine(i); | |||||
transfrom += line + ':' + styleObject[i] + ';'; | |||||
} | |||||
return transfrom | |||||
} |
@ -0,0 +1,22 @@ | |||||
## 1.1.7(2024-10-29) | |||||
修复底部露出部分组件bug | |||||
## 1.1.6(2024-08-20) | |||||
更新本地数据源为最新数据源 | |||||
## 1.1.5(2024-06-12) | |||||
使用说明文档优化 | |||||
## 1.1.4(2024-06-12) | |||||
增加问题反馈描述 | |||||
## 1.1.3(2024-02-29) | |||||
更新使用文档 | |||||
## 1.1.2(2024-01-16) | |||||
解决Vue3项目导入导出报错问题 | |||||
## 1.1.1(2023-12-06) | |||||
defaultValue可以传入defaultValue:['河北省','唐山市','丰南区']数组类型以及defaultValue: '420103'地区编码字符串类型 | |||||
## 1.1.0(2023-12-05) | |||||
即默认值传入地区编码,也支持传入中文省市区数组 | |||||
## 1.0.9(2023-12-04) | |||||
优化 | |||||
## 1.0.8(2023-10-24) | |||||
修复东菀市和中山市下各镇的行政编码错误问题 | |||||
## 1.0.4(2023-09-15) | |||||
改为uni_modules规范 |
@ -0,0 +1,281 @@ | |||||
<template> | |||||
<view class="pupop"> | |||||
<view class="popup-box" :animation="animationData"> | |||||
<view class="pupop-btn"> | |||||
<view @tap="cancel">取消</view> | |||||
<view @tap="confirm" style="color: #2979ff;">确定</view> | |||||
</view> | |||||
<picker-view :value="value" :indicator-style="indicatorStyle" @change="bindChange" class="picker-view"> | |||||
<picker-view-column> | |||||
<view class="item" v-for="(item,index) in provinceList" :key="index">{{item.name}}</view> | |||||
</picker-view-column> | |||||
<picker-view-column> | |||||
<view class="item" v-for="(item,index) in cityList" :key="index">{{item.name}}</view> | |||||
</picker-view-column> | |||||
<picker-view-column v-if="column == 3"> | |||||
<view class="item" v-for="(item,index) in areaList" :key="index">{{item.name}}</view> | |||||
</picker-view-column> | |||||
</picker-view> | |||||
</view> | |||||
<view @tap="close" @touchmove.stop.prevent :class="visible ? 'pupop-model' : 'pupop-models'"></view> | |||||
</view> | |||||
</template> | |||||
<script> | |||||
import { | |||||
addressList | |||||
} from './cityData.js' | |||||
export default { | |||||
data() { | |||||
return { | |||||
value: [], | |||||
addressList, | |||||
provinceList: [], | |||||
cityList: [], | |||||
areaList: [], | |||||
indicatorStyle: `height: 50px;`, | |||||
provinceIndex: 0, | |||||
cityIndex: 0, | |||||
areaIndex: 0, | |||||
animationData: {} | |||||
} | |||||
}, | |||||
props: { | |||||
column: { | |||||
type: Number, | |||||
default: 3 | |||||
}, | |||||
defaultValue: { | |||||
default: () => '' | |||||
}, | |||||
visible: { | |||||
type: Boolean, | |||||
default: () => false | |||||
}, | |||||
maskCloseAble: { | |||||
type: Boolean, | |||||
default: () => true | |||||
}, | |||||
}, | |||||
watch: { | |||||
visible (val) { | |||||
this.changeActive() | |||||
}, | |||||
defaultValue() { | |||||
this.init() | |||||
} | |||||
}, | |||||
created() { | |||||
this.init() | |||||
}, | |||||
methods: { | |||||
init () { | |||||
var provinceList = [] | |||||
addressList.filter(item => { | |||||
provinceList.push({ | |||||
code: item.code, | |||||
name: item.name | |||||
}) | |||||
}) | |||||
this.provinceList = [...provinceList] | |||||
if (!this.defaultValue) { | |||||
this.cityList = addressList[0].children | |||||
this.areaList = addressList[0].children[0].children | |||||
} else { | |||||
var city = {11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古",21:"辽宁",22:"吉林",23:"黑龙江 ",31:"上海",32:"江苏",33:"浙江",34:"安徽",35:"福建",36:"江西",37:"山东",41:"河南",42:"湖北 ",43:"湖南",44:"广东",45:"广西",46:"海南",50:"重庆",51:"四川",52:"贵州",53:"云南",54:"西藏 ",61:"陕西",62:"甘肃",63:"青海",64:"宁夏",65:"新疆",71:"台湾",81:"香港",82:"澳门",91:"国外 "} | |||||
if (Array.isArray(this.defaultValue) && this.defaultValue.length >= 2) { | |||||
console.log(this.defaultValue) | |||||
var province = this.defaultValue[0] | |||||
var city = this.defaultValue[1] | |||||
this.provinceIndex = 0 | |||||
this.cityIndex = 0 | |||||
this.areaIndex = 0 | |||||
this.provinceIndex = addressList.findIndex(obj => { | |||||
return obj.name == province | |||||
}) | |||||
this.provinceIndex = this.provinceIndex >= 0 ? this.provinceIndex : 0 | |||||
this.cityList = addressList[this.provinceIndex].children | |||||
this.cityIndex = this.cityList.findIndex(obj => { | |||||
return obj.name == city | |||||
}) | |||||
this.cityIndex = this.cityIndex >= 0 ? this.cityIndex : 0 | |||||
this.areaList = this.cityList[this.cityIndex].children | |||||
if (this.defaultValue.length > 2) { | |||||
this.areaIndex = this.areaList.findIndex(obj => { | |||||
return obj.name == this.defaultValue[2] | |||||
}) | |||||
} | |||||
this.areaIndex = this.areaIndex >= 0 ? this.areaIndex : 0 | |||||
this.$nextTick(() => { | |||||
if (this.column == 3) { | |||||
this.value = JSON.parse(JSON.stringify([this.provinceIndex, this.cityIndex, this.areaIndex])) | |||||
} else if (this.column == 2) { | |||||
this.value = JSON.parse(JSON.stringify([this.provinceIndex, this.cityIndex])) | |||||
} | |||||
}) | |||||
} else if (/^\d{6}$/.test(this.defaultValue)) { | |||||
var province = this.defaultValue.substr(0, 2) | |||||
var city = this.defaultValue.substr(0, 4) | |||||
this.provinceIndex = 0 | |||||
this.cityIndex = 0 | |||||
this.areaIndex = 0 | |||||
this.provinceIndex = addressList.findIndex(obj => { | |||||
return obj.code == province | |||||
}) | |||||
this.provinceIndex = this.provinceIndex >= 0 ? this.provinceIndex : 0 | |||||
this.cityList = addressList[this.provinceIndex].children | |||||
this.cityIndex = this.cityList.findIndex(obj => { | |||||
return obj.code == city | |||||
}) | |||||
this.cityIndex = this.cityIndex >= 0 ? this.cityIndex : 0 | |||||
this.areaList = this.cityList[this.cityIndex].children | |||||
this.areaIndex = this.areaList.findIndex(obj => { | |||||
return obj.code == this.defaultValue | |||||
}) | |||||
this.areaIndex = this.areaIndex >= 0 ? this.areaIndex : 0 | |||||
this.$nextTick(() => { | |||||
if (this.column == 3) { | |||||
this.value = JSON.parse(JSON.stringify([this.provinceIndex, this.cityIndex, this.areaIndex])) | |||||
} else if (this.column == 2) { | |||||
this.value = JSON.parse(JSON.stringify([this.provinceIndex, this.cityIndex])) | |||||
} | |||||
}) | |||||
} else { | |||||
uni.showToast({ | |||||
title: '地区编码格式不正确', | |||||
icon: 'none' | |||||
}) | |||||
console.log('地区编码格式不正确') | |||||
} | |||||
} | |||||
this.changeActive() | |||||
}, | |||||
confirm () { | |||||
if (this.column == 3) { | |||||
this.$emit('confirm', { | |||||
code: addressList[this.provinceIndex].children[this.cityIndex].children[this.areaIndex].code, | |||||
name: addressList[this.provinceIndex].name + addressList[this.provinceIndex].children[this.cityIndex].name + addressList[this.provinceIndex].children[this.cityIndex].children[this.areaIndex].name, | |||||
provinceName: addressList[this.provinceIndex].name, | |||||
cityName: addressList[this.provinceIndex].children[this.cityIndex].name, | |||||
areaName: addressList[this.provinceIndex].children[this.cityIndex].children[this.areaIndex].name | |||||
}) | |||||
} else if (this.column == 2) { | |||||
this.$emit('confirm', { | |||||
code: addressList[this.provinceIndex].children[this.cityIndex].children[this.areaIndex].code.substring(0, 4) + '00', | |||||
name: addressList[this.provinceIndex].name + addressList[this.provinceIndex].children[this.cityIndex].name, | |||||
provinceName: addressList[this.provinceIndex].name, | |||||
cityName: addressList[this.provinceIndex].children[this.cityIndex].name | |||||
}) | |||||
} else { | |||||
uni.showToast({ | |||||
title: '目前column只能传2和3', | |||||
icon: 'none' | |||||
}) | |||||
} | |||||
}, | |||||
cancel () { | |||||
this.$emit('cancel') | |||||
}, | |||||
// 动画 | |||||
changeActive () { | |||||
var active = '-415px' | |||||
if (this.visible) { | |||||
active = 0 | |||||
} | |||||
var animation = uni.createAnimation({ | |||||
duration: 400, | |||||
timingFunction: 'linear' | |||||
}) | |||||
animation.bottom(active).step() | |||||
this.animationData = animation.export() | |||||
}, | |||||
bindChange(e) { | |||||
e.detail.value[0] = e.detail.value[0] || 0 | |||||
e.detail.value[1] = e.detail.value[1] || 0 | |||||
e.detail.value[2] = e.detail.value[2] || 0 | |||||
if (e.detail.value[0] != this.provinceIndex) { | |||||
// console.log('监听第一列') | |||||
this.provinceChange(e.detail.value[0]) | |||||
} else if (e.detail.value[1] != this.cityIndex) { | |||||
// console.log('监听第二列') | |||||
this.cityChange(e.detail.value[1]) | |||||
} else if (e.detail.value[2] != this.areaIndex) { | |||||
// console.log('监听第三列') | |||||
this.areaChange(e.detail.value[2]) | |||||
} | |||||
}, | |||||
// 监听第一列变化 | |||||
provinceChange (e) { | |||||
this.provinceIndex = e | |||||
this.cityIndex = 0 | |||||
this.areaIndex = 0 | |||||
this.value = [...[e, 0, 0]] | |||||
this.cityList = addressList[e].children | |||||
this.areaList = addressList[e].children[0].children | |||||
}, | |||||
// 监听第二列变化 | |||||
cityChange (e) { | |||||
this.cityIndex = e | |||||
this.areaIndex = 0 | |||||
// console.log(this.cityIndex, this.areaIndex) | |||||
this.value = [...[this.provinceIndex, e, 0]] | |||||
this.cityList = addressList[this.provinceIndex].children | |||||
this.areaList = addressList[this.provinceIndex].children[e].children | |||||
}, | |||||
// 监听第三列变化 | |||||
areaChange (e) { | |||||
this.areaIndex = e | |||||
}, | |||||
// 点击模态框 | |||||
close () { | |||||
if (this.maskCloseAble) { | |||||
this.cancel() | |||||
} | |||||
} | |||||
} | |||||
} | |||||
</script> | |||||
<style scoped lang="scss"> | |||||
.pupop { | |||||
.popup-box { | |||||
position: fixed; | |||||
left: 0; | |||||
bottom: -315px; | |||||
z-index: 99999; | |||||
background: #fff; | |||||
padding-bottom: 50px; | |||||
.pupop-btn{ | |||||
height: 40px; | |||||
display: flex; | |||||
align-items: center; | |||||
padding: 0 30upx; | |||||
justify-content: space-between; | |||||
} | |||||
} | |||||
.pupop-model { | |||||
position: fixed; | |||||
left: 0; | |||||
top: 0; | |||||
width: 100%; | |||||
height: 100%; | |||||
z-index: 9999; | |||||
background: rgba(0, 0, 0, .5); | |||||
} | |||||
.pupop-models{ | |||||
display: none; | |||||
} | |||||
.picker-view { | |||||
width: 750rpx; | |||||
height: 225px; | |||||
margin-top: 20rpx; | |||||
} | |||||
.item { | |||||
height: 50px; | |||||
align-items: center; | |||||
justify-content: center; | |||||
text-align: center; | |||||
line-height: 50px; | |||||
} | |||||
} | |||||
</style> |
@ -0,0 +1,17 @@ | |||||
{ | |||||
"id": "piaoyi-cityPicker", | |||||
"name": " data-cityPicker省市区地址选择器", | |||||
"displayName": " data-cityPicker省市区地址选择器(兼容vue3)", | |||||
"version": "1.1.7", | |||||
"description": "常用省市区选择器,可反选,自定义模态框、兼容vue3", | |||||
"keywords": [ | |||||
"省市区", | |||||
"选择器", | |||||
"可反选", | |||||
"自定义模态框和弹框", | |||||
"兼容vue3" | |||||
], | |||||
"dcloudext": { | |||||
"type": "component-vue" | |||||
} | |||||
} |
@ -0,0 +1,106 @@ | |||||
## 1.0.7(2023-09-15) | |||||
改为uni_modules规范 | |||||
### cityPicker 省市区选择器(兼容vue3) | |||||
**使用方法:** | |||||
``` | |||||
<template> | |||||
<view> | |||||
<button @tap="open">打开</button> | |||||
<cityPicker :column="column" :default-value="defaultValue" :mask-close-able="maskCloseAble" @confirm="confirm" @cancel="cancel" :visible="visible"/> | |||||
<view>{{str}}</view> | |||||
</view> | |||||
</template> | |||||
<script> | |||||
import cityPicker from '@/uni_modules/piaoyi-cityPicker/components/piaoyi-cityPicker/piaoyi-cityPicker' | |||||
export default { | |||||
data() { | |||||
return { | |||||
visible: false, | |||||
maskCloseAble: true, | |||||
str: '', | |||||
defaultValue: '420103', | |||||
// defaultValue: ['河北省','唐山市','丰南区'], | |||||
column: 3 | |||||
} | |||||
}, | |||||
components: { | |||||
cityPicker | |||||
}, | |||||
methods: { | |||||
open () { | |||||
this.visible = true | |||||
}, | |||||
confirm (val) { | |||||
console.log(val) | |||||
this.str = JSON.stringify(val) | |||||
this.visible = false | |||||
}, | |||||
cancel () { | |||||
this.visible = false | |||||
} | |||||
}, | |||||
onShareAppMessage(res) { | |||||
if (res.from === 'button') { // 来自页面内分享按钮 | |||||
console.log(res.target) | |||||
} | |||||
return { | |||||
title: 'data-cityPicker省市区地址选择器!', | |||||
path: '/pages/cityPicker/cityPicker' | |||||
} | |||||
}, | |||||
onShareTimeline(res) { | |||||
if (res.from === 'button') { // 来自页面内分享按钮 | |||||
console.log(res.target) | |||||
} | |||||
return { | |||||
title: 'data-cityPicker省市区地址选择器!' | |||||
} | |||||
} | |||||
} | |||||
</script> | |||||
<style lang="scss"> | |||||
</style> | |||||
``` | |||||
### 注:近期收到使用用户反馈,存在以下一个问题(如有好的建议,期待私信,谢谢) | |||||
1、之前只支持默认值传入地区编码,已更新可以支持传入中文省市区数组 | |||||
defaultValue可以传入defaultValue:['河北省','唐山市','丰南区']数组类型以及defaultValue: '420103'地区编码字符串类型 | |||||
可以使用const reg =/([\u4e00-\u9fa5]+省|自治区|[\u4e00-\u9fa5]+市|[\u4e00-\u9fa5]+区)/g;将自己的省市区数据进行处理为数组再传入 | |||||
2、有些用户反馈vue3下watch监听有问题 | |||||
我自己创建一个vue项目,导入插件后,按照示例原封不动进行测试,没有发现问题; 发现有问题的朋友可以提供一下可以复现的demo给我,我这边看看具体什么情况 | |||||
3、有些用户返回无法关闭弹框 | |||||
不要把插件放到scroll-view里面,请务必放到最外层进行使用 | |||||
#### 事件说明 | |||||
| 事件名 | 返回值 | 描述 | | |||||
| :---------: | :----: | :------------: | | |||||
| @confirm | 对象(code,完整地区名称) | 点击确定的回调 | | |||||
| @cancel | 无 | 点击取消的回调 | | |||||
#### Prop | |||||
| 参数名称 | 描述 | | |||||
| -------- | ------------------------------ | | |||||
| visible | 控制选择器显示和隐藏 | | |||||
| column | 可选值2和3,2是省市两列选择;3是省市区三列选择 | | |||||
| maskCloseAble | 点击模态框是否关闭弹框 | | |||||
| defaultValue | 初始地区编码(例:420102或者['河北省','唐山市','丰南区']) | | |||||
### 数据来源:[点击查看省市区数据来源](https://github.com/modood/Administrative-divisions-of-China/blob/master/dist/pca-code.json) | |||||
### 可接定制化组件开发 | |||||
### 右侧有本人代表作小程序二维码,可以扫码体验 | |||||
### 如使用过程中有问题或有一些好的建议,欢迎加QQ群互相学习交流:120594820 |