Merge pull request #66 from Chanzhaoyu/dialog-components

perf: 优化 dilog 下组件
This commit is contained in:
Zyronon 2024-04-29 14:15:57 +08:00 committed by GitHub
commit 9da5f780db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 275 additions and 298 deletions

View File

@ -1,83 +1,65 @@
<template>
<div class="ConfirmDialog" @click="$emit('dismiss')" v-if="visible">
<div class="content" @click.stop="stop">
<div class="ConfirmDialog" @click="onDismiss" v-if="visible">
<div class="content">
<slot name="header"></slot>
<div class="body">
<div class="title" v-if="title">{{ title }}</div>
<div class="subtitle" :class="subtitleColor" v-if="subtitle">
<div :class="['subtitle', subtitleColor]" v-if="subtitle">
{{ subtitle }}
</div>
<slot></slot>
</div>
<div class="footer">
<div class="cancel" :class="cancelTextColor" @click.stop="cancel">
<div :class="['cancel', cancelTextColor]" @click.stop="onCancel">
{{ cancelText }}
</div>
<div class="ok" @click.stop="ok">{{ okText }}</div>
<div class="ok" @click.stop="onOk">{{ okText }}</div>
</div>
</div>
</div>
</template>
<script lang="ts">
/*TODO 单独使用时没有mark*/
export default {
name: 'ConfirmDialog',
props: {
visible: {
type: Boolean,
default: true
},
title: {
type: String,
default() {
return ''
}
},
subtitle: {
type: String,
default() {
return ''
}
},
subtitleColor: {
type: String,
default() {
return 'gray'
}
},
okText: {
type: String,
default() {
return '确定'
}
},
cancelText: {
type: String,
default() {
return '取消'
}
},
cancelTextColor: {
type: String,
default() {
return 'gray'
}
}
},
data() {
return {}
},
methods: {
stop() {},
ok() {
this.$emit('ok')
this.$emit('update:visible', false)
},
cancel() {
this.$emit('cancel')
this.$emit('update:visible', false)
}
}
<script setup lang="ts">
defineOptions({ name: 'ConfirmDialog' })
interface Props {
title?: string
subtitle?: string
subtitleColor?: string
okText?: string
cancelText?: string
cancelTextColor?: string
}
withDefaults(defineProps<Props>(), {
title: '',
subtitle: '',
subtitleColor: 'gray',
okText: '确定',
cancelText: '取消',
cancelTextColor: 'gray'
})
const emit = defineEmits<{
(ev: 'ok'): void
(ev: 'cancel'): void
(ev: 'dismiss'): void
}>()
const visible = defineModel<boolean>('visible', { type: Boolean, default: true })
const onOk = () => {
visible.value = false
emit('ok')
}
const onCancel = () => {
visible.value = false
emit('cancel')
}
const onDismiss = () => {
emit('dismiss')
}
</script>

View File

@ -3,16 +3,9 @@
<slot></slot>
</div>
</template>
<script>
export default {
name: 'FadeDialog',
data() {
return {}
},
computed: {},
created() {},
methods: {}
}
<script setup lang="ts">
defineOptions({ name: 'FadeDialog' })
</script>
<style scoped lang="less">

View File

@ -1,17 +1,15 @@
<template>
<!-- <transition> -->
<Transition name="test">
<div
ref="dialog"
class="FromBottomDialog"
v-if="modelValue"
:class="[mode, showHengGang ? '' : 'no-heng-gang']"
@touchstart="start"
@touchmove="move"
@touchend="end"
:class="['FromBottomDialog', mode, showHengGang ? '' : 'no-heng-gang']"
@touchstart="onStart"
@touchmove="onMove"
@touchend="onEnd"
>
<slot name="header"></slot>
<div class="heng-gang" :class="mode" v-if="showHengGang">
<div :class="['heng-gang', mode]" v-if="showHengGang">
<div class="content"></div>
</div>
<div class="wrapper" ref="wrapper">
@ -20,131 +18,129 @@
</div>
</Transition>
</template>
<script>
<script setup lang="ts">
import { ref, watch } from 'vue'
import Dom, { _css } from '../../utils/dom'
import bus, { EVENT_KEY } from '@/utils/bus'
import { _stopPropagation } from '@/utils'
export default {
name: 'FromBottomDialog',
props: {
modelValue: {
type: Boolean,
default: false
},
mode: {
type: String,
default: 'dark'
// default: 'light'
// default: 'white'
},
maskMode: {
type: String,
default: 'dark'
},
height: {
type: String,
default: 'calc(var(--vh, 1vh) * 70)'
},
showHengGang: {
type: Boolean,
default: true
},
pageId: {
type: String,
default: null,
required: true
},
borderRadius: {
type: String,
default: '15rem 15rem 0 0'
},
tag: {
type: String,
default: ''
}
},
watch: {
modelValue(newVal) {
let page = document.getElementById(this.pageId)
if (!page) return
if (newVal) {
this.pagePosition = _css(page, 'position')
page.style.position = 'absolute'
this.scroll = document.documentElement.scrollTop
document.body.style.position = 'fixed'
document.body.style.top = -this.scroll + 'px'
defineOptions({ name: 'FromBottomDialog' })
let maskTemplate = `<div class="Mask fade-in ${this.maskMode}"></div>`
let mask = new Dom().create(maskTemplate)
setTimeout(() => {
mask.on('click', (e) => {
_stopPropagation(e)
this.hide(false)
})
}, 200)
page.appendChild(mask.els[0])
} else {
page.style.position = this.pagePosition || 'fixed'
document.body.style.position = 'static'
document.documentElement.scrollTop = this.scroll
interface Props {
mode?: 'dark' | 'light' | 'white'
maskMode?: 'dark' | 'light' | 'white'
height?: string
showHengGang?: boolean
pageId: string | null
borderRadius?: string
tag?: string
}
let mask = new Dom('.Mask').replaceClass('fade-in', 'fade-out')
setTimeout(() => {
mask.remove()
}, 250)
}
}
},
data() {
return {
scroll: 0,
startY: 0,
moveY: 0,
startTime: 0,
pagePosition: null
}
},
computed: {},
created() {},
methods: {
hide(val = false) {
this.$emit('update:modelValue', val)
this.$emit('cancel')
},
start(e) {
if (this.$refs.wrapper.scrollTop !== 0) return
this.startY = e.touches[0].pageY
this.startTime = Date.now()
_css(this.$refs.dialog, 'transition-duration', `0ms`)
},
move(e) {
if (this.$refs.wrapper.scrollTop !== 0) return
this.moveY = e.touches[0].pageY - this.startY
if (this.moveY > 0) {
bus.emit(EVENT_KEY.DIALOG_MOVE, {
tag: this.tag,
e: this.moveY
interface Emits {
(ev: 'cancel'): void
}
const props = withDefaults(defineProps<Props>(), {
mode: 'dark',
maskMode: 'dark',
height: 'calc(var(--vh, 1vh) * 70)',
showHengGang: true,
pageId: null,
borderRadius: '15rem 15rem 0 0',
tag: ''
})
const emit = defineEmits<Emits>()
const modelValue = defineModel<boolean>('value', { type: Boolean, default: false })
const dialog = ref<HTMLElement | null>(null)
const wrapper = ref<HTMLElement | null>(null)
const scroll = ref(0)
const startY = ref(0)
const moveY = ref(0)
const startTime = ref(0)
const pagePosition = ref(null)
watch(
() => modelValue.value,
(newVal: boolean) => {
let page = document.getElementById(props.pageId)
if (!page) return
if (newVal) {
pagePosition.value = _css(page, 'position')
page.style.position = 'absolute'
scroll.value = document.documentElement.scrollTop
document.body.style.position = 'fixed'
document.body.style.top = -scroll.value + 'px'
let maskTemplate = `<div class="Mask fade-in ${props.maskMode}"></div>`
let mask = new Dom().create(maskTemplate)
setTimeout(() => {
mask.on('click', (e: Event) => {
_stopPropagation(e)
onHide()
})
_css(this.$refs.dialog, 'transform', `translate3d(0,${this.moveY}px,0)`)
}
},
end() {
//modelValueref
if (!this.$refs.dialog) return
if (Date.now() - this.startTime < 150 && Math.abs(this.moveY) < 30) return
let clientHeight = this.$refs.dialog.clientHeight
_css(this.$refs.dialog, 'transition-duration', `250ms`)
if (Math.abs(this.moveY) > clientHeight / 2) {
_css(this.$refs.dialog, 'transform', `translate3d(0,100%,0)`)
bus.emit(EVENT_KEY.DIALOG_END, { tag: this.tag, isClose: true })
setTimeout(this.hide, 250)
} else {
_css(this.$refs.dialog, 'transform', `translate3d(0,0,0)`)
bus.emit(EVENT_KEY.DIALOG_END, { tag: this.tag, isClose: false })
}
}, 200)
page.appendChild(mask.els[0])
} else {
page.style.position = pagePosition.value || 'fixed'
document.body.style.position = 'static'
document.documentElement.scrollTop = scroll.value
let mask = new Dom('.Mask').replaceClass('fade-in', 'fade-out')
setTimeout(() => {
mask.remove()
}, 250)
}
}
)
const onHide = () => {
modelValue.value = false
emit('cancel')
}
const onStart = (e: TouchEvent) => {
if (wrapper.value?.scrollTop !== 0) return
startY.value = e.touches[0].clientY
startTime.value = Date.now()
_css(dialog.value, 'transition-duration', '0ms')
}
const onMove = (e: TouchEvent) => {
if (wrapper.value?.scrollTop !== 0) return
moveY.value = e.touches[0].pageY - startY.value
if (moveY.value < 0) {
bus.emit(EVENT_KEY.DIALOG_MOVE, {
tag: props.tag,
e: moveY.value
})
_css(dialog.value, 'transform', `translate3d(0, ${moveY.value}px, 0)`)
}
}
const onEnd = () => {
// modelValue ref
if (!dialog.value) return
if (Date.now() - startTime.value < 150 && Math.abs(moveY.value) < 30) return
let clientHeight = dialog.value?.clientHeight
_css(dialog.value, 'transition-duration', `250ms`)
if (Math.abs(moveY.value) > clientHeight / 2) {
_css(dialog.value, 'transform', `translate3d(0,100%,0)`)
bus.emit(EVENT_KEY.DIALOG_END, { tag: props.tag, isClose: true })
setTimeout(onHide, 250)
} else {
_css(dialog.value, 'transform', `translate3d(0,0,0)`)
bus.emit(EVENT_KEY.DIALOG_END, { tag: props.tag, isClose: false })
}
}
</script>

View File

@ -1,57 +1,51 @@
<template>
<div class="NoticeDialog" @click="$emit('dismiss')">
<div class="content" @click.stop="stop">
<div class="NoticeDialog" @click="onDismiss">
<div class="content">
<div class="body">
<div class="title">{{ title }}</div>
<div class="subtitle" :class="subtitleColor" v-if="subtitle">
<div :class="['subtitle', subtitleColor]" v-if="subtitle">
{{ subtitle }}
</div>
</div>
<div class="footer">
<div class="cancel" @click.stop="$emit('cancel')">{{ cancelText }}</div>
<div class="cancel" @click.stop="onCancel">{{ cancelText }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'NoticeDialog',
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default() {
return ''
}
},
subtitle: {
type: String,
default() {
return ''
}
},
subtitleColor: {
type: String,
default() {
return 'gray'
}
},
cancelText: {
type: String,
default() {
return '取消'
}
}
},
data() {
return {}
},
methods: {
stop() {}
}
<script setup lang="ts">
defineOptions({ name: 'NoticeDialog' })
interface Props {
title?: string
subtitle?: string
subtitleColor?: string
cancelText?: string
}
withDefaults(defineProps<Props>(), {
title: '',
subtitle: '',
subtitleColor: 'gray',
cancelText: '取消'
})
const emit = defineEmits<{
(ev: 'ok'): void
(ev: 'cancel'): void
(ev: 'dismiss'): void
}>()
const visible = defineModel<boolean>('visible', { type: Boolean, default: true })
const onCancel = () => {
visible.value = false
emit('cancel')
}
const onDismiss = () => {
emit('dismiss')
}
</script>

View File

@ -1,30 +1,39 @@
<template>
<div class="SelectDialog" @click="$emit('cancel')">
<div class="SelectDialog" @click="onCancel">
<div class="content">
<div class="item" :key="i" v-for="(item, i) in list" @click.stop="$emit('ok', item)">
<div class="item" :key="i" v-for="(item, i) in list" @click.stop="onOk(item)">
{{ item.name }}
</div>
</div>
</div>
</template>
<script>
export default {
name: 'SelectDialog',
props: {
visible: {
type: Boolean,
default: false
},
list: {
type: Array,
default() {
return []
}
}
},
data() {
return {}
}
<script setup lang="ts" generic="T">
defineOptions({ name: 'SelectDialog' })
type Item = { name: string } & T
interface Props {
visible?: boolean
list?: Item[]
}
withDefaults(defineProps<Props>(), {
visible: false,
list: () => []
})
const emit = defineEmits<{
(ev: 'ok', item: Item): void
(ev: 'cancel'): void
}>()
const onOk = (item: Item) => {
emit('ok', item)
}
const onCancel = () => {
emit('cancel')
}
</script>

View File

@ -1,47 +1,50 @@
<template>
<div class="SimpleConfirmDialog" @click="$emit('dismiss')">
<div class="content" @click.stop="stop">
<div class="SimpleConfirmDialog" @click="onDismiss">
<div class="content">
<div class="item">{{ title }}</div>
<div class="footer">
<div class="cancel" @click.stop="$emit('cancel')">{{ cancelText }}</div>
<div class="ok" @click.stop="$emit('ok')">{{ okText }}</div>
<div class="cancel" @click.stop="onCancel">{{ cancelText }}</div>
<div class="ok" @click.stop="onOk">{{ okText }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'SimpleConfirmDialog',
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default() {
return ''
}
},
okText: {
type: String,
default() {
return '保存'
}
},
cancelText: {
type: String,
default() {
return '放弃'
}
}
},
data() {
return {}
},
methods: {
stop() {}
}
<script setup lang="ts">
defineOptions({ name: 'SimpleConfirmDialog' })
interface Props {
title?: string
okText?: string
cancelText?: string
}
withDefaults(defineProps<Props>(), {
title: '',
okText: '确定',
cancelText: '取消'
})
const emit = defineEmits<{
(ev: 'ok'): void
(ev: 'cancel'): void
(ev: 'dismiss'): void
}>()
const visible = defineModel<boolean>('visible', { type: Boolean, default: true })
const onOk = () => {
visible.value = false
emit('ok')
}
const onCancel = () => {
visible.value = false
emit('cancel')
}
const onDismiss = () => {
emit('dismiss')
}
</script>