You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
5.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<style lang="scss">
.hqs-popup {
.qs-con {
background-color: rgb(245,247,251);;
}
.qs-header {
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
}
.qs-title {
font-size: 16px;
font-weight: bold;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.hidden { visibility: hidden; }
.qs-side {
min-width: 60px;
padding: 15px 20px;
color: #888;
flex-shrink: 0;
&:active { opacity: .8; }
}
.qs-h-scroll {
height: 100vh;
// #ifdef H5
margin-top: 44px;
height: calc(100vh - 44px);
// #endif
}
.ta-r { text-align: right; }
}
</style>
<template>
<uni-popup ref="popup"
:mask-click="maskClick" @change="onChange"
:type="from" class="hqs-popup">
<view class="qs-con"
:style="conStyle"
@mousedown="onTouch" @mousemove="onTouch" @mouseup="onTouch"
@touchstart="onTouch" @touchmove="onTouch" @touchend="onTouch">
<block v-if="isVertical">
<view class="qs-header" v-if="from == 'bottom' && showHeader">
<view class="qs-side" :class="{'hidden': !showBack && !$slots.back}"
@click="onBack">
<slot name="back">
<text>返回</text>
</slot>
</view>
<slot name="title">
<text class="qs-title">{{ title }}</text>
</slot>
<view class="qs-side ta-r" :class="{'hidden': !showClose}"
@click="close">
<slot name="close">
<u-icon :name="closeIcon" :size="32" v-if="closeIcon"></u-icon>
<text v-else>关闭</text>
</slot>
</view>
</view>
<slot name="sub-header"></slot>
<scroll-view scroll-y :style="{ height }"
@scroll="onScroll">
<view>
<slot></slot>
</view>
</scroll-view>
<slot name="bottom"></slot>
</block>
<block v-else-if="!isVertical">
<scroll-view scroll-y class="qs-h-scroll" :style="{ width }">
<slot></slot>
</scroll-view>
</block>
</view>
</uni-popup>
</template>
<script>
import UniPopup from './uni-popup.vue'
export default {
components: {
UniPopup,
},
props: {
// 弹窗显示可通过v-model控制
value: Boolean,
// 弹窗打开开始方向
from: {
type: String,
default: 'bottom',
},
// 内容区边缘圆角大小
round: {
type: Number,
default: 10,
},
// 弹窗内容宽度(当from=left或right时起作用)
width: {
type: String,
default: '60vw',
},
// 弹窗内容高度(当from=top或bottom时起作用)
height: {
type: String,
default: '50vh',
},
// 显示默认头部标题栏(仅在底部弹出时有)
showHeader: {
type: Boolean,
default: true,
},
// 弹窗标题
title: String,
// 显示左侧返回按钮如果v-slot:back存在时也会显示此按钮
showBack: Boolean,
// 显示关闭按钮如果是字符串将作为uview需另外引入的u-icon组件的name如showClose="close"会显示close图标
showClose: {
type: [Boolean, String],
default: true,
},
// 是否可点击模态背景关闭弹窗
maskClick: {
type: Boolean,
default: true,
},
},
data() {
return {
scrollTop: 0,
panStyle: '',
showPopup: false,
}
},
computed: {
closeIcon() {
if(typeof this.showClose == 'string') return this.showClose
return false
},
isVertical() {
return ['bottom', 'top'].includes(this.from)
},
conStyle() {
let style = this.panStyle || ''
let r = this.round
if(r > 0) {
r += 'px'
if(this.from == 'bottom') r = [r, r, 0, 0]
else if(this.from == 'left') r = [0, r, r, 0]
else if(this.from == 'right') r = [r, 0, 0, r]
else r = [0, 0, r, r]
style += `border-radius: ${r.join(' ')};`
}
return style
},
},
watch: {
value(val) {
if(val == this.showPopup) return
if(val) this.open()
else this.close()
},
showPopup(val) {
this.$emit('input', val)
},
},
mounted() {
if(this.value) this.open()
},
methods: {
onScroll(e) {
this.scrollTop = e.detail.scrollTop
},
onTouch(ev) {
// if(!this.maskClick) return
const { pageX, pageY } = ev.changedTouches[0] || ev
if(['touchstart', 'mousedown'].includes(ev.type)) {
this.startX = pageX
this.startY = pageY
this.startTime = ev.timeStamp
} else {
if(!this.startTime) return
const orien = this.isVertical ? 'Y' : 'X'
let moveDis = pageY - this.startY
if(this.from == 'left') moveDis = this.startX - pageX
else if(this.from == 'right') moveDis = pageX - this.startX
else if(this.from == 'top') moveDis = -moveDis
if(!this.maskClick) moveDis /= 3
const duration = (ev.timeStamp - this.startTime)
const speed = moveDis/duration
if(['touchend', 'mouseup'].includes(ev.type)) {
if(this.panStyle) {
if(this.maskClick && (moveDis > 120 || speed > 0.25)) {
this.close()
}
else {
this.panStyle = `transform: translate${orien}(0); transition: all ease 200ms;`
}
setTimeout(() => {
this.panStyle = ''
}, 300)
}
// conScrollTop = 0
this.startTime = 0
return
}
// if(this.scrollTop > 0) return
if(moveDis > 0) {
if(this.from == 'left' || this.from == 'top') moveDis *= -1
this.panStyle = `transform: translate${orien}(${moveDis}px); transition: none;`
}
}
},
onChange({ show }) {
this.showPopup = show
},
open() {
this.$refs.popup.open()
this.showPopup = true
},
close() {
this.$refs.popup.close()
this.showPopup = false
},
onBack() {
this.$emit('back')
},
}
}
</script>