Merge pull request #66 from Chanzhaoyu/dialog-components
perf: 优化 dilog 下组件
This commit is contained in:
commit
9da5f780db
@ -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>
|
||||
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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() {
|
||||
//如果是外部改变modelValue值的话,这里会没有ref
|
||||
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>
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user