| @ -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 | |||