193 lines
6.3 KiB
Vue
193 lines
6.3 KiB
Vue
<template>
|
|
<wd-overlay v-if="cover" :z-index="zIndex" lock-scroll :show="show" custom-style="background-color: transparent;pointer-events: auto;"></wd-overlay>
|
|
<wd-transition name="fade" :show="show" :custom-style="transitionStyle" @after-enter="handleAfterEnter" @after-leave="handleAfterLeave">
|
|
<view :class="rootClass">
|
|
<!--iconName优先级更高-->
|
|
<wd-loading
|
|
v-if="iconName === 'loading'"
|
|
:type="loadingType"
|
|
:color="loadingColor"
|
|
:size="loadingSize"
|
|
:custom-class="`wd-toast__icon ${direction === 'vertical' ? 'is-vertical' : ''}`"
|
|
/>
|
|
<view
|
|
:class="`wd-toast__iconWrap wd-toast__icon ${direction === 'vertical' ? 'is-vertical' : ''}`"
|
|
v-else-if="iconName === 'success' || iconName === 'warning' || iconName === 'info' || iconName === 'error'"
|
|
>
|
|
<view class="wd-toast__iconBox">
|
|
<view class="wd-toast__iconSvg" :style="svgStyle"></view>
|
|
</view>
|
|
</view>
|
|
<wd-icon
|
|
v-else-if="iconClass"
|
|
:custom-class="`wd-toast__icon ${direction === 'vertical' ? 'is-vertical' : ''}`"
|
|
:size="iconSize"
|
|
:class-prefix="classPrefix"
|
|
:name="iconClass"
|
|
></wd-icon>
|
|
<!--文本-->
|
|
<view v-if="msg" class="wd-toast__msg">{{ msg }}</view>
|
|
</view>
|
|
</wd-transition>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
export default {
|
|
name: 'wd-toast',
|
|
options: {
|
|
addGlobalClass: true,
|
|
virtualHost: true,
|
|
styleIsolation: 'shared'
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<script lang="ts" setup>
|
|
import wdIcon from '../wd-icon/wd-icon.vue'
|
|
import wdLoading from '../wd-loading/wd-loading.vue'
|
|
import wdOverlay from '../wd-overlay/wd-overlay.vue'
|
|
import wdTransition from '../wd-transition/wd-transition.vue'
|
|
|
|
import { computed, inject, onBeforeMount, ref, watch, type CSSProperties } from 'vue'
|
|
import base64 from '../common/base64'
|
|
import { defaultOptions, getToastOptionKey, toastIcon } from '.'
|
|
import { toastProps, type ToastDirection, type ToastLoadingType, type ToastOptions, type ToastProps } from './types'
|
|
import { addUnit, isDef, isFunction, objToStyle } from '../common/util'
|
|
|
|
const props = defineProps(toastProps)
|
|
const iconName = ref<string>('') // 图标类型
|
|
const msg = ref<string>('') // 消息内容
|
|
const position = ref<string>('middle')
|
|
const show = ref<boolean>(false)
|
|
const zIndex = ref<number>(100)
|
|
const loadingType = ref<ToastLoadingType>('outline')
|
|
const loadingColor = ref<string>('#4D80F0')
|
|
const iconSize = ref<string>() // 图标大小
|
|
const loadingSize = ref<string>() // loading大小
|
|
const svgStr = ref<string>('') // 图标
|
|
const cover = ref<boolean>(false) // 是否存在遮罩层
|
|
const classPrefix = ref<string>('wd-icon') // 图标前缀
|
|
const iconClass = ref<string>('') // 图标类名
|
|
const direction = ref<ToastDirection>('horizontal') // toast布局方向
|
|
|
|
let opened: (() => void) | null = null
|
|
|
|
let closed: (() => void) | null = null
|
|
|
|
const toastOptionKey = getToastOptionKey(props.selector)
|
|
const toastOption = inject(toastOptionKey, ref<ToastOptions>(defaultOptions)) // toast选项
|
|
|
|
// 监听options变化展示
|
|
watch(
|
|
() => toastOption.value,
|
|
(newVal: ToastOptions) => {
|
|
reset(newVal)
|
|
},
|
|
{
|
|
deep: true,
|
|
immediate: true
|
|
}
|
|
)
|
|
|
|
// 监听options变化展示
|
|
watch(
|
|
() => iconName.value,
|
|
() => {
|
|
buildSvg()
|
|
},
|
|
{
|
|
deep: true,
|
|
immediate: true
|
|
}
|
|
)
|
|
|
|
/**
|
|
* 动画自定义样式
|
|
*/
|
|
const transitionStyle = computed(() => {
|
|
const style: CSSProperties = {
|
|
'z-index': zIndex.value,
|
|
position: 'fixed',
|
|
top: '50%',
|
|
left: 0,
|
|
width: '100%',
|
|
transform: 'translate(0, -50%)',
|
|
'text-align': 'center',
|
|
'pointer-events': 'none'
|
|
}
|
|
return objToStyle(style)
|
|
})
|
|
|
|
const rootClass = computed(() => {
|
|
return `wd-toast ${props.customClass} wd-toast--${position.value} ${
|
|
(iconName.value !== 'loading' || msg.value) && (iconName.value || iconClass.value) ? 'wd-toast--with-icon' : ''
|
|
} ${iconName.value === 'loading' && !msg.value ? 'wd-toast--loading' : ''} ${direction.value === 'vertical' ? 'is-vertical' : ''}`
|
|
})
|
|
|
|
const svgStyle = computed(() => {
|
|
const style: CSSProperties = {
|
|
backgroundImage: `url(${svgStr.value})`
|
|
}
|
|
if (isDef(iconSize.value)) {
|
|
style.width = iconSize.value
|
|
style.height = iconSize.value
|
|
}
|
|
return objToStyle(style)
|
|
})
|
|
|
|
onBeforeMount(() => {
|
|
buildSvg()
|
|
})
|
|
|
|
function handleAfterEnter() {
|
|
if (isFunction(opened)) {
|
|
opened()
|
|
}
|
|
}
|
|
|
|
function handleAfterLeave() {
|
|
if (isFunction(closed)) {
|
|
closed()
|
|
}
|
|
}
|
|
|
|
function buildSvg() {
|
|
if (iconName.value !== 'success' && iconName.value !== 'warning' && iconName.value !== 'info' && iconName.value !== 'error') return
|
|
const iconSvg = toastIcon[iconName.value]()
|
|
const iconSvgStr = `"data:image/svg+xml;base64,${base64(iconSvg)}"`
|
|
svgStr.value = iconSvgStr
|
|
}
|
|
|
|
/**
|
|
* 重置toast选项值
|
|
* @param option toast选项值
|
|
*/
|
|
function reset(option: ToastOptions) {
|
|
show.value = isDef(option.show) ? option.show : false
|
|
|
|
if (show.value) {
|
|
mergeOptionsWithProps(option, props)
|
|
}
|
|
}
|
|
|
|
function mergeOptionsWithProps(option: ToastOptions, props: ToastProps) {
|
|
iconName.value = isDef(option.iconName!) ? option.iconName! : props.iconName
|
|
iconClass.value = isDef(option.iconClass!) ? option.iconClass! : props.iconClass
|
|
msg.value = isDef(option.msg!) ? option.msg! : props.msg
|
|
position.value = isDef(option.position!) ? option.position! : props.position
|
|
zIndex.value = isDef(option.zIndex!) ? option.zIndex! : props.zIndex
|
|
loadingType.value = isDef(option.loadingType!) ? option.loadingType! : props.loadingType
|
|
loadingColor.value = isDef(option.loadingColor!) ? option.loadingColor! : props.loadingColor
|
|
iconSize.value = isDef(option.iconSize) ? addUnit(option.iconSize) : isDef(props.iconSize) ? addUnit(props.iconSize) : undefined
|
|
loadingSize.value = isDef(option.loadingSize) ? addUnit(option.loadingSize) : isDef(props.loadingSize) ? addUnit(props.loadingSize) : undefined
|
|
cover.value = isDef(option.cover!) ? option.cover! : props.cover
|
|
classPrefix.value = isDef(option.classPrefix) ? option.classPrefix : props.classPrefix
|
|
direction.value = isDef(option.direction) ? option.direction : props.direction
|
|
closed = isFunction(option.closed) ? option.closed : isFunction(props.closed) ? props.closed : null
|
|
opened = isFunction(option.opened) ? option.opened : isFunction(props.opened) ? props.opened : null
|
|
}
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
@import './index.scss';
|
|
</style>
|