popup 弹窗 picker-view 滚动选择器

popup 弹窗 picker-view 滚动选择器

组件链接: https://ext.dcloud.net.cn/plugin?id=7960

<m-picker mode="bottom" :show.sync="show" :range="list" @confirm="confirm" />

一、自定义popup类型的picker-view滚动选择器

  1. 组件是基于uniapp开发的,过度动画使用了uniapp的<uni-transition></uni-transition>组件

Mark 遮罩层

<uni-transition v-if="mark" ref="aniMark" custom-class="transition" mode-class="fade" :styles="stylesMark" :show="show" @click="clickMark" />

popup内容

<uni-transition ref="ani" custom-class="transition" :mode-class="modeClass" :styles="styles" :show="show">
	<view class="m-picker">
		<view class="m-picker__case" :style="{
			background: bgColor,
			borderTopLeftRadius: radius[0] + 'rpx',
			borderTopRightRadius: radius[1] + 'rpx',
			borderBottomLeftRadius: radius[2] + 'rpx',
			borderBottomRightRadius: radius[3] + 'rpx',
			paddingBottom: model == 'bottom' ? 'calc(constant(safe-area-inset-bottom) + 30rpx)' : '30rpx',
			paddingBottom: model == 'bottom' ? 'calc(env(safe-area-inset-bottom) + 30rpx)' : '30rpx'
		}">
			<!-- 确认/取消 按钮 -->
			<slot v-if="model == 'bottom'" name="handle">
				<view class="handle">
					<view class="button cancel" :style="cancelStyle" @click="cancel">{{cancelText}}</view>
					<view class="button confirm" :style="confirmStyle" @click="confirm">{{confirmText}}</view>
				</view>
			</slot>
			<!-- 内容 -->
			<slot>
				
			</slot>
			<!-- 确认/取消 按钮 -->
			<slot v-if="model != 'bottom'" name="handle">
				<view class="handle">
					<view class="button cancel" :style="cancelStyle" @click="cancel">{{cancelText}}</view>
					<view class="button confirm" :style="confirmStyle" @click="confirm">{{confirmText}}</view>
				</view>
			</slot>
		</view>
	</view>
</uni-transition>

picker-view 组件

<view class="content" :style="{height: `${height}rpx`}">
	<picker-view class="picker-view" :indicator-class="indicatorClass" :indicator-style="indicatorStyle" :value="pickerValue" @change="bindChange" @pickstart="pickstart" @pickend="pickend">
		<picker-view-column v-for="(rangeItem, rangeIndex) in range" :key="rangeIndex">
			<view class="picker-view__item" v-for="(item, index) in rangeItem" :key="index">
				{{ item[rangeKey] }}
			</view>
		</picker-view-column>
	</picker-view>
</view>

props.js

const props = {
	// 是否显示遮罩
	mark: {
		type: Boolean,
		default() {
			return true
		}
	},
	// 点击遮罩是否关闭
	markClose: {
		type: Boolean,
		default() {
			return true
		}
	},
	// 点击按钮是否关闭
	btnClose: {
		type: Boolean,
		default() {
			return true
		}
	},
	// 是否显示弹窗
	show: {
		type: Boolean,
		default() {
			return false
		}
	},
	// 数据
	range: {
		type: Array,
		default: []
	},
	// picker-item显示内容的key
	rangeKey: {
		type: String,
		default: 'label'
	},
	// 设置选择器中间选中框的样式
	indicatorStyle: {
		type: String,
		default: 'height: 50px;'
	},
	// 设置选择器中间选中框的类名,注意页面或组件的style中写了scoped时,需要在类名前写/deep/
	indicatorClass: {
		type: String,
		default: ''
	},
	// cancel文字
	cancelText: {
		type: String,
		default: '取消'
	},
	// confirm文字
	confirmText: {
		type: String,
		default: '确定'
	},
	// cancel样式style
	cancelStyle: {
		type: Object,
		default: {}
	},
	// confirm样式style
	confirmStyle: {
		type: Object,
		default: {}
	},
	// 内容高度 rpx
	height: {
		type: Number,
		default: 500
	},
	// 圆角 rpx
	borderRadius: {
		type: Number,
		default: 16
	},
	// 背景颜色
	bgColor: {
		type: String,
		default: '#FFFFFF'
	},
	// mark 背景颜色
	markBgColor: {
		type: String,
		default: '#00000080'
	},
	// 方向 top/bottom/center
	mode: {
		type: String,
		default: 'bottom'
	}
}

export default props

接收样式参数

import props from './props.js'
export default {
	name:"m-picker",
	props,
	computed: {
		model() {
			if (this.mode == 'top') return 'top';
			else if (this.mode == 'bottom') return 'bottom';
			else if (this.mode == 'center') return 'center';
			else return 'bottom';
		},
		stylesMark() {
			return {
				position: 'fixed',
				top: 0,
				left: 0,
				right: 0,
				bottom: 0,
				zIndex: 99,
				backgroundColor: this.markBgColor,
			}
		},
		styles() {
			const top = {
				position: 'fixed',
				left: 0,
				right: 0,
				top: 0,
				zIndex: 100
			}
			const bottom = {
				position: 'fixed',
				left: 0,
				right: 0,
				bottom: 0,
				zIndex: 100
			}
			const center = {
				position: 'fixed',
				left: '50%',
				top: '50%',
				width: '90vw',
				transform: 'translate(-50%, -50%)',
				zIndex: 100
			}
			if (this.model == 'top') return top;
			else if (this.model == 'bottom') return bottom;
			else if (this.model == 'center') return center;
			else return bottom;
		},
		radius() {
			const borderRadius = this.borderRadius;
			if (this.model == 'top') return [0, 0, borderRadius, borderRadius];
			else if (this.model == 'bottom') return [borderRadius, borderRadius, 0, 0];
			else if (this.model == 'center') return [borderRadius, borderRadius, borderRadius, borderRadius];
			else return [0, 0, 0, 0];
		},
		modeClass() {
			if (this.model == 'top') return ['fade', 'slide-top'];
			else if (this.model == 'bottom') return ['fade', 'slide-bottom'];
			else if (this.model == 'center') return 'fade';
			else return ['fade', 'slide-bottom'];
		}
	},
}

二、组件说明

Methods

方法称名说明参数
confirm点击右侧按钮[]
cancel点击左侧按钮

字段

字段类型默认值说明
markBooleantrue是否显示遮罩
markCloseBooleantrue点击遮罩是否关闭
btnCloseBooleantrue点击按钮是否关闭
showBooleanfale是否显示弹窗
rangeArray[]数据
rangeKeyString‘label’picker-item显示内容的key
indicatorStyleString‘height: 50px;’设置选择器中间选中框的样式
indicatorClassString’ ’设置选择器中间选中框的类名,注意页面或组件的style中写了scoped时,需要在类名前写/deep/
cancelTextString取消cancel文字
confirmTextString确定confirm文字
cancelStyleObject{}cancel样式style
confirmlStyleObject{}confirm样式style
heightNumber500内容高度 rpx
borderRadiusNumber16圆角 rpx
bgColorString‘#FFFFFF’背景颜色
modeStringbottom方向 top/bottom/center

Slots

称名说明
default会覆盖默认的picker-view
handle按钮部分

三、 图片演示

bottom

mode=bottom

top

mode=top

center

mode=center

四、完整代码

<template>
	<view class="pricke">
		<uni-transition v-if="mark" ref="aniMark" custom-class="transition" mode-class="fade" :styles="stylesMark" :show="show" @click="clickMark"></uni-transition>
		<uni-transition ref="ani" custom-class="transition" :mode-class="modeClass" :styles="styles" :show="show">
			<view class="m-picker">
				<view class="m-picker__case" :style="{
					background: bgColor,
					borderTopLeftRadius: radius[0] + 'rpx',
					borderTopRightRadius: radius[1] + 'rpx',
					borderBottomLeftRadius: radius[2] + 'rpx',
					borderBottomRightRadius: radius[3] + 'rpx',
					paddingBottom: model == 'bottom' ? 'calc(constant(safe-area-inset-bottom) + 30rpx)' : '30rpx',
					paddingBottom: model == 'bottom' ? 'calc(env(safe-area-inset-bottom) + 30rpx)' : '30rpx'
				}">
					<slot v-if="model == 'bottom'" name="handle">
						<view class="handle">
							<view class="button cancel" :style="cancelStyle" @click="cancel">{{cancelText}}</view>
							<view class="button confirm" :style="confirmStyle" @click="confirm">{{confirmText}}</view>
						</view>
					</slot>
					<slot>
						<view class="content" :style="{height: `${height}rpx`}">
							<picker-view class="picker-view" :indicator-class="indicatorClass" :indicator-style="indicatorStyle" :value="pickerValue" @change="bindChange" @pickstart="pickstart" @pickend="pickend">
								<picker-view-column v-for="(rangeItem, rangeIndex) in range" :key="rangeIndex">
									<view class="picker-view__item" v-for="(item, index) in rangeItem" :key="index">
										{{ item[rangeKey] }}
									</view>
								</picker-view-column>
							</picker-view>
						</view>
					</slot>
					<slot v-if="model != 'bottom'" name="handle">
						<view class="handle">
							<view class="button cancel" :style="cancelStyle" @click="cancel">{{cancelText}}</view>
							<view class="button confirm" :style="confirmStyle" @click="confirm">{{confirmText}}</view>
						</view>
					</slot>
				</view>
			</view>
		</uni-transition>
	</view>
</template>

<script>
	import props from './props.js'
	export default {
		name:"m-picker",
		props,
		data() {
			return {
				pickerValue: [],
				pickMove: false,
			};
		},
		computed: {
			model() {
				if (this.mode == 'top') return 'top';
				else if (this.mode == 'bottom') return 'bottom';
				else if (this.mode == 'center') return 'center';
				else return 'bottom';
			},
			stylesMark() {
				return {
					position: 'fixed',
					top: 0,
					left: 0,
					right: 0,
					bottom: 0,
					zIndex: 99,
					backgroundColor: this.markBgColor,
				}
			},
			styles() {
				const top = {
					position: 'fixed',
					left: 0,
					right: 0,
					top: 0,
					zIndex: 100
				}
				const bottom = {
					position: 'fixed',
					left: 0,
					right: 0,
					bottom: 0,
					zIndex: 100
				}
				const center = {
					position: 'fixed',
					left: '50%',
					top: '50%',
					width: '90vw',
					transform: 'translate(-50%, -50%)',
					zIndex: 100
				}
				if (this.model == 'top') return top;
				else if (this.model == 'bottom') return bottom;
				else if (this.model == 'center') return center;
				else return bottom;
			},
			radius() {
				const borderRadius = this.borderRadius;
				if (this.model == 'top') return [0, 0, borderRadius, borderRadius];
				else if (this.model == 'bottom') return [borderRadius, borderRadius, 0, 0];
				else if (this.model == 'center') return [borderRadius, borderRadius, borderRadius, borderRadius];
				else return [0, 0, 0, 0];
			},
			modeClass() {
				if (this.model == 'top') return ['fade', 'slide-top'];
				else if (this.model == 'bottom') return ['fade', 'slide-bottom'];
				else if (this.model == 'center') return 'fade';
				else return ['fade', 'slide-bottom'];
			}
		},
		watch: {
			range(val) {
				if (val.length) {
					let arr = []
					for (let index in this.range) {
						arr.push(0);
					}
					this.pickerValue = arr;
				}
			}
		},
		created() {
			if (this.range && this.range.length) {
				this.pickerValue = [];
				for (let index in this.range) {
					this.pickerValue.push(0)
				}
			}
		},
		methods: {
			// 关闭
			close() {
				if (!this.pickMove) this.$emit('update:show', false)
			},
			// 取消
			cancel() {
				this.$emit('cancel')
				if (this.btnClose) this.close()
			},
			// 确定
			confirm() {
				this.$emit('confirm', this.pickerValue);
				if (this.btnClose) this.close()
			},
			// 点击遮罩
			clickMark() {
				if (this.markClose) this.close()
			},
			bindChange (e) {
				const val = e.detail.value;
				this.pickerValue = val;
			},
			pickstart() {
				this.pickMove = true;
			},
			pickend() {
				this.pickMove = false;
			}
		}
	}
</script>

<style lang="scss">
.m-picker {
	width: 100%;
	height: 100%;
	
	&__case {
		padding-left: 56rpx;
		padding-right: 56rpx;
		padding-top: 30rpx;
		padding-bottom: 30rpx;
		
		.handle {
			width: 100%;
			display: flex;
			align-items: center;
			justify-content: space-between;
			
			.button {
				font-size: 48rpx;
				font-family: PingFang SC;
				font-weight: 500;
				color: #000000;
				
				&.cancel {
					
				}
				
				&.confirm {
					color: #FF8833;
				}
			}
		}
		
		.content {
			width: 100%;
			height: 400rpx;
		}
		
	}
}

.picker-view {
	width: 100%;
	height: 100%;
	
	&__item {
		display: flex;
		align-items: center;
		justify-content: center;
		font-size: 48rpx;
	}
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DevilAngelia

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值