feat: 优化Toast、Message和Notify组件的函数式调用方案 (#696)

解决当前页面存在多次使用useXX时仅最后一次生效的问题
This commit is contained in:
不如摸鱼去 2024-11-06 10:07:35 +08:00 committed by GitHub
parent deeb45d8cb
commit 9f318bdeb3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 72 additions and 64 deletions

View File

@ -1,22 +1,19 @@
/* /*
* @Author: weisheng * @Author: weisheng
* @Date: 2022-12-14 17:33:21 * @Date: 2022-12-14 17:33:21
* @LastEditTime: 2024-08-17 18:18:16 * @LastEditTime: 2024-11-05 23:15:31
* @LastEditors: weisheng * @LastEditors: weisheng
* @Description: * @Description:
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-message-box/index.ts * @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-message-box\index.ts
* *
*/ */
import { provide, ref } from 'vue' import { inject, provide, ref } from 'vue'
import type { Message, MessageOptions, MessageResult, MessageType } from './types' import type { Message, MessageOptions, MessageResult, MessageType } from './types'
import { deepMerge } from '../common/util' import { deepMerge } from '../common/util'
/** const messageDefaultOptionKey = '__MESSAGE_OPTION__'
* useMessage key
* const None = Symbol('None')
* @internal
*/
export const messageDefaultOptionKey = '__MESSAGE_OPTION__'
// 默认模板 // 默认模板
export const defaultOptions: MessageOptions = { export const defaultOptions: MessageOptions = {
@ -36,9 +33,12 @@ export const defaultOptions: MessageOptions = {
} }
export function useMessage(selector: string = ''): Message { export function useMessage(selector: string = ''): Message {
const messageOption = ref<MessageOptions>(defaultOptions) // Message选项
const messageOptionKey = selector ? messageDefaultOptionKey + selector : messageDefaultOptionKey const messageOptionKey = selector ? messageDefaultOptionKey + selector : messageDefaultOptionKey
provide(messageOptionKey, messageOption) const messageOption = inject(messageOptionKey, ref<MessageOptions | typeof None>(None)) // Message选项
if (messageOption.value === None) {
messageOption.value = defaultOptions
provide(messageOptionKey, messageOption)
}
const createMethod = (type: MessageType) => { const createMethod = (type: MessageType) => {
// 优先级options->MessageOptions->defaultOptions // 优先级options->MessageOptions->defaultOptions
@ -77,7 +77,9 @@ export function useMessage(selector: string = ''): Message {
const prompt = createMethod('prompt') const prompt = createMethod('prompt')
const close = () => { const close = () => {
messageOption.value.show = false if (messageOption.value !== None) {
messageOption.value.show = false
}
} }
return { return {
show, show,
@ -87,3 +89,7 @@ export function useMessage(selector: string = ''): Message {
close close
} }
} }
export const getMessageDefaultOptionKey = (selector: string) => {
return selector ? `${messageDefaultOptionKey}${selector}` : messageDefaultOptionKey
}

View File

@ -1,13 +1,13 @@
/* /*
* @Author: weisheng * @Author: weisheng
* @Date: 2024-04-08 22:34:01 * @Date: 2024-04-08 22:34:01
* @LastEditTime: 2024-09-23 15:59:04 * @LastEditTime: 2024-11-05 23:17:06
* @LastEditors: weisheng * @LastEditors: weisheng
* @Description: * @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-message-box\types.ts * @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-message-box\types.ts
* *
*/ */
import { baseProps } from '../common/props' import { baseProps, makeStringProp } from '../common/props'
import { type InputType } from '../wd-input/types' import { type InputType } from '../wd-input/types'
export type MessageType = 'alert' | 'confirm' | 'prompt' export type MessageType = 'alert' | 'confirm' | 'prompt'
@ -113,5 +113,8 @@ export interface Message {
export const messageBoxProps = { export const messageBoxProps = {
...baseProps, ...baseProps,
selector: String /**
*
*/
selector: makeStringProp('')
} }

View File

@ -61,7 +61,7 @@ import {
type MessageResult, type MessageResult,
type MessageType type MessageType
} from './types' } from './types'
import { defaultOptions, messageDefaultOptionKey } from '.' import { defaultOptions, getMessageDefaultOptionKey } from '.'
import { isDef, isFunction } from '../common/util' import { isDef, isFunction } from '../common/util'
import { useTranslate } from '../composables/useTranslate' import { useTranslate } from '../composables/useTranslate'
import { type InputType } from '../wd-input/types' import { type InputType } from '../wd-input/types'
@ -78,7 +78,7 @@ const bodyClass = computed(() => {
return `wd-message-box__body ${!title.value ? 'is-no-title' : ''} ${type.value === 'prompt' ? 'is-prompt' : ''}` return `wd-message-box__body ${!title.value ? 'is-no-title' : ''} ${type.value === 'prompt' ? 'is-prompt' : ''}`
}) })
const messageOptionKey = props.selector ? messageDefaultOptionKey + props.selector : messageDefaultOptionKey const messageOptionKey = getMessageDefaultOptionKey(props.selector)
const messageOption = inject(messageOptionKey, ref<MessageOptions>(defaultOptions)) // message const messageOption = inject(messageOptionKey, ref<MessageOptions>(defaultOptions)) // message
/** /**

View File

@ -1,10 +1,11 @@
import { provide, reactive } from 'vue' import { inject, provide, reactive, ref } from 'vue'
import type { NotifyProps } from './types' import type { NotifyProps } from './types'
import { deepMerge, isString } from '../common/util' import { deepMerge, isString } from '../common/util'
let timer: ReturnType<typeof setTimeout> let timer: ReturnType<typeof setTimeout>
let currentOptions = getDefaultOptions() let currentOptions = getDefaultOptions()
const notifyDefaultOptionKey = '__NOTIFY_OPTION__' const notifyDefaultOptionKey = '__NOTIFY_OPTION__'
const None = Symbol('None')
export const setNotifyDefaultOptions = (options: NotifyProps) => { export const setNotifyDefaultOptions = (options: NotifyProps) => {
currentOptions = deepMerge(currentOptions, options) as NotifyProps currentOptions = deepMerge(currentOptions, options) as NotifyProps
} }
@ -12,24 +13,28 @@ export const resetNotifyDefaultOptions = () => {
currentOptions = getDefaultOptions() currentOptions = getDefaultOptions()
} }
export const useNotify = (selector: string = '') => { export const useNotify = (selector: string = '') => {
const notifyOption = reactive<NotifyProps>(currentOptions) const notifyOptionKey = getNotifyOptionKey(selector)
const notifyOption = inject(notifyOptionKey, ref<NotifyProps | typeof None>(None))
if (notifyOption.value === None) {
notifyOption.value = currentOptions
provide(notifyOptionKey, notifyOption)
}
const showNotify = (option: NotifyProps | string) => { const showNotify = (option: NotifyProps | string) => {
const options = deepMerge(currentOptions, isString(option) ? { message: option } : option) as NotifyProps const options = deepMerge(currentOptions, isString(option) ? { message: option } : option) as NotifyProps
notifyOption.value = deepMerge(options, { visible: true })
Object.assign(notifyOption, options, { visible: true }) if (notifyOption.value.duration && notifyOption.value.duration > 0) {
if (notifyOption.duration && notifyOption.duration > 0) {
timer && clearTimeout(timer) timer && clearTimeout(timer)
timer = setTimeout(() => closeNotify(), options.duration) timer = setTimeout(() => closeNotify(), options.duration)
} }
} }
const closeNotify = () => { const closeNotify = () => {
timer && clearTimeout(timer) timer && clearTimeout(timer)
notifyOption.visible = false if (notifyOption.value !== None) {
notifyOption.value.visible = false
}
} }
// provide
provide(getNotifyOptionKey(selector), notifyOption)
return { return {
showNotify, showNotify,
closeNotify closeNotify

View File

@ -16,16 +16,18 @@
</template> </template>
<script lang="ts"> <script lang="ts">
export default { export default {
// #ifdef H5
name: 'wd-notify', name: 'wd-notify',
// #endif options: {
options: { virtualHost: true, addGlobalClass: true, styleIsolation: 'shared' } virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
} }
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import wdPopup from '../wd-popup/wd-popup.vue' import wdPopup from '../wd-popup/wd-popup.vue'
import { inject, computed, watch } from 'vue' import { inject, computed, watch, ref } from 'vue'
import { notifyProps, type NotifyProps } from './types' import { notifyProps, type NotifyProps } from './types'
import { getNotifyOptionKey } from '.' import { getNotifyOptionKey } from '.'
import { addUnit, isFunction } from '../common/util' import { addUnit, isFunction } from '../common/util'
@ -37,10 +39,10 @@ const emits = defineEmits<{
(e: 'closed'): void (e: 'closed'): void
(e: 'opened'): void (e: 'opened'): void
}>() }>()
const state = inject<NotifyProps>(getNotifyOptionKey(props.selector), props) const state = inject(getNotifyOptionKey(props.selector), ref<NotifyProps>(props))
const customStyle = computed(() => { const customStyle = computed(() => {
const { safeHeight, position } = state const { safeHeight, position } = state.value
let customStyle: string = '' let customStyle: string = ''
switch (position) { switch (position) {
case 'top': case 'top':
@ -56,21 +58,24 @@ const customStyle = computed(() => {
}) })
const onClick = (event: MouseEvent) => { const onClick = (event: MouseEvent) => {
if (isFunction(state.onClick)) return state.onClick(event) if (isFunction(state.value.onClick)) return state.value.onClick(event)
emits('click', event) emits('click', event)
} }
const onClosed = () => { const onClosed = () => {
if (isFunction(state.onClosed)) return state.onClosed() if (isFunction(state.value.onClosed)) return state.value.onClosed()
emits('closed') emits('closed')
} }
const onOpened = () => { const onOpened = () => {
if (isFunction(state.onOpened)) return state.onOpened() if (isFunction(state.value.onOpened)) return state.value.onOpened()
emits('opened') emits('opened')
} }
watch( watch(
() => state.visible, () => state.value.visible,
(visible) => emits('update:visible', visible as boolean) (visible) => {
emits('update:visible', visible as boolean)
},
{ deep: true }
) )
</script> </script>

View File

@ -1,22 +1,11 @@
/* import { inject, provide, ref } from 'vue'
* @Author: weisheng
* @Date: 2024-03-29 13:29:57
* @LastEditTime: 2024-07-18 23:16:16
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-toast/index.ts
*
*/
import { provide, ref } from 'vue'
import type { Toast, ToastOptions } from './types' import type { Toast, ToastOptions } from './types'
import { deepMerge } from '../common/util' import { deepMerge } from '../common/util'
/** /**
* useToast key * useToast key
*
* @internal
*/ */
export const toastDefaultOptionKey = '__TOAST_OPTION__' const toastDefaultOptionKey = '__TOAST_OPTION__'
// 默认模板 // 默认模板
export const defaultOptions: ToastOptions = { export const defaultOptions: ToastOptions = {
@ -30,11 +19,16 @@ export const defaultOptions: ToastOptions = {
zIndex: 100 zIndex: 100
} }
const None = Symbol('None')
export function useToast(selector: string = ''): Toast { export function useToast(selector: string = ''): Toast {
const toastOptionKey = getToastOptionKey(selector)
const toastOption = inject(toastOptionKey, ref<ToastOptions | typeof None>(None)) // toast选项
if (toastOption.value === None) {
toastOption.value = defaultOptions
provide(toastOptionKey, toastOption)
}
let timer: ReturnType<typeof setTimeout> | null = null let timer: ReturnType<typeof setTimeout> | null = null
const toastOption = ref<ToastOptions>(defaultOptions) // Toast选项
const toastOptionKey = selector ? toastDefaultOptionKey + selector : toastDefaultOptionKey
provide(toastOptionKey, toastOption)
const createMethod = (toastOptions: ToastOptions) => { const createMethod = (toastOptions: ToastOptions) => {
// 优先级options->toastOptions->defaultOptions // 优先级options->toastOptions->defaultOptions
@ -85,6 +79,10 @@ export function useToast(selector: string = ''): Toast {
} }
} }
export const getToastOptionKey = (selector: string) => {
return selector ? `${toastDefaultOptionKey}${selector}` : toastDefaultOptionKey
}
export const toastIcon = { export const toastIcon = {
success() { success() {
return '<svg width="42px" height="42px" viewBox="0 0 42 42" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>成功</title><desc>Created with Sketch.</desc><defs><filter x="-63.2%" y="-80.0%" width="226.3%" height="260.0%" filterUnits="objectBoundingBox" id="filter-1"><feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset><feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur><feColorMatrix values="0 0 0 0 0.122733141 0 0 0 0 0.710852582 0 0 0 0 0.514812768 0 0 0 1 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix><feMerge><feMergeNode in="shadowMatrixOuter1"></feMergeNode><feMergeNode in="SourceGraphic"></feMergeNode></feMerge></filter><rect id="path-2" x="3.4176226" y="5.81442199" width="3" height="8.5" rx="1.5"></rect><linearGradient x1="50%" y1="0.126649064%" x2="50%" y2="100%" id="linearGradient-4"><stop stop-color="#ACFFBD" stop-opacity="0.208123907" offset="0%"></stop><stop stop-color="#10B87C" offset="100%"></stop></linearGradient></defs><g id="规范" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="反馈-轻提示" transform="translate(-388.000000, -538.000000)"><g id="成功" transform="translate(388.000000, 538.000000)"><circle id="Oval" fill="#34D19D" opacity="0.400000006" cx="21" cy="21" r="20"></circle><circle id="Oval" fill="#34D19D" cx="21" cy="21" r="16"></circle><g id="Group-6" filter="url(#filter-1)" transform="translate(11.500000, 14.000000)"><mask id="mask-3" fill="white"><use xlink:href="#path-2"></use></mask><use id="Rectangle-Copy-24" fill="#C4FFEB" transform="translate(4.917623, 10.064422) rotate(-45.000000) translate(-4.917623, -10.064422) " xlink:href="#path-2"></use><rect id="Rectangle" fill="url(#linearGradient-4)" mask="url(#mask-3)" transform="translate(6.215869, 11.372277) rotate(-45.000000) translate(-6.215869, -11.372277) " x="4.71586891" y="9.52269089" width="3" height="3.69917136"></rect><rect id="Rectangle" fill="#FFFFFF" transform="translate(11.636236, 7.232744) scale(1, -1) rotate(-45.000000) translate(-11.636236, -7.232744) " x="10.1362361" y="-1.02185365" width="3" height="16.5091951" rx="1.5"></rect></g></g></g></g></svg>' return '<svg width="42px" height="42px" viewBox="0 0 42 42" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>成功</title><desc>Created with Sketch.</desc><defs><filter x="-63.2%" y="-80.0%" width="226.3%" height="260.0%" filterUnits="objectBoundingBox" id="filter-1"><feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset><feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur><feColorMatrix values="0 0 0 0 0.122733141 0 0 0 0 0.710852582 0 0 0 0 0.514812768 0 0 0 1 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix><feMerge><feMergeNode in="shadowMatrixOuter1"></feMergeNode><feMergeNode in="SourceGraphic"></feMergeNode></feMerge></filter><rect id="path-2" x="3.4176226" y="5.81442199" width="3" height="8.5" rx="1.5"></rect><linearGradient x1="50%" y1="0.126649064%" x2="50%" y2="100%" id="linearGradient-4"><stop stop-color="#ACFFBD" stop-opacity="0.208123907" offset="0%"></stop><stop stop-color="#10B87C" offset="100%"></stop></linearGradient></defs><g id="规范" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="反馈-轻提示" transform="translate(-388.000000, -538.000000)"><g id="成功" transform="translate(388.000000, 538.000000)"><circle id="Oval" fill="#34D19D" opacity="0.400000006" cx="21" cy="21" r="20"></circle><circle id="Oval" fill="#34D19D" cx="21" cy="21" r="16"></circle><g id="Group-6" filter="url(#filter-1)" transform="translate(11.500000, 14.000000)"><mask id="mask-3" fill="white"><use xlink:href="#path-2"></use></mask><use id="Rectangle-Copy-24" fill="#C4FFEB" transform="translate(4.917623, 10.064422) rotate(-45.000000) translate(-4.917623, -10.064422) " xlink:href="#path-2"></use><rect id="Rectangle" fill="url(#linearGradient-4)" mask="url(#mask-3)" transform="translate(6.215869, 11.372277) rotate(-45.000000) translate(-6.215869, -11.372277) " x="4.71586891" y="9.52269089" width="3" height="3.69917136"></rect><rect id="Rectangle" fill="#FFFFFF" transform="translate(11.636236, 7.232744) scale(1, -1) rotate(-45.000000) translate(-11.636236, -7.232744) " x="10.1362361" y="-1.02185365" width="3" height="16.5091951" rx="1.5"></rect></g></g></g></g></svg>'

View File

@ -38,12 +38,11 @@ import wdTransition from '../wd-transition/wd-transition.vue'
import { computed, inject, onBeforeMount, ref, watch, type CSSProperties } from 'vue' import { computed, inject, onBeforeMount, ref, watch, type CSSProperties } from 'vue'
import base64 from '../common/base64' import base64 from '../common/base64'
import { defaultOptions, toastDefaultOptionKey, toastIcon } from '.' import { defaultOptions, getToastOptionKey, toastIcon } from '.'
import { toastProps, type ToastLoadingType, type ToastOptions } from './types' import { toastProps, type ToastLoadingType, type ToastOptions } from './types'
import { addUnit, isDef, isFunction, objToStyle } from '../common/util' import { addUnit, isDef, isFunction, objToStyle } from '../common/util'
const props = defineProps(toastProps) const props = defineProps(toastProps)
const iconName = ref<string>('') // const iconName = ref<string>('') //
const msg = ref<string>('') // const msg = ref<string>('') //
const position = ref<string>('middle') const position = ref<string>('middle')
@ -62,7 +61,7 @@ let opened: (() => void) | null = null
let closed: (() => void) | null = null let closed: (() => void) | null = null
const toastOptionKey = props.selector ? toastDefaultOptionKey + props.selector : toastDefaultOptionKey const toastOptionKey = getToastOptionKey(props.selector)
const toastOption = inject(toastOptionKey, ref<ToastOptions>(defaultOptions)) // toast const toastOption = inject(toastOptionKey, ref<ToastOptions>(defaultOptions)) // toast
// options // options

View File

@ -8,23 +8,15 @@
* *
*/ */
// Toast
export { useToast } from './components/wd-toast' export { useToast } from './components/wd-toast'
// Messageb
export { useMessage } from './components/wd-message-box' export { useMessage } from './components/wd-message-box'
// useQueue
export { useQueue } from './components/composables/useQueue' export { useQueue } from './components/composables/useQueue'
// Notify
export * from './components/wd-notify' export * from './components/wd-notify'
export { dayjs } from './components/common/dayjs' export { dayjs } from './components/common/dayjs'
export * as CommonUtil from './components/common/util' export * as CommonUtil from './components/common/util'
export * as clickOut from './components/common/clickoutside' export * as clickOut from './components/common/clickoutside'
export * from './locale' export * from './locale'
export type { ConfigProviderThemeVars } from './components/wd-config-provider/types' export type { ConfigProviderThemeVars } from './components/wd-config-provider/types'