useDialog.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import {
  2. ref,
  3. defineAsyncComponent,
  4. getCurrentInstance,
  5. computed,
  6. h,
  7. createApp,
  8. unref,
  9. provide,
  10. reactive
  11. } from 'vue'
  12. import type { Component, App, VNode, ComputedRef } from 'vue'
  13. import type { ElDialog } from 'element-plus'
  14. import Dialog from '@/components/Dialog/index.vue'
  15. import { DialogProps, DialogModalProps } from './props'
  16. const defaultModalProps: Omit<DialogModalProps, 'content'> = {
  17. title:'',
  18. showOk: true,
  19. showClose: false,
  20. hideCancel: false,
  21. okText: '确定',
  22. cancelText: '取消',
  23. footer: true,
  24. header: true,
  25. width: '60%'
  26. }
  27. /**
  28. * 获取组件实例的 provides
  29. * @param instance Vue 组件实例
  30. * @returns 组件实例及其父级的 provides 对象
  31. */
  32. function getProvides(instance: any): Record<string, any> {
  33. let provides = instance?.provides || {}
  34. if (instance.parent) {
  35. provides = { ...provides, ...getProvides(instance.parent) }
  36. }
  37. return provides
  38. }
  39. const useDialog = (initialOptions: DialogProps) => {
  40. const modalRef = ref<InstanceType<typeof ElDialog> | null>(null)
  41. const currentInstance = getCurrentInstance() as any
  42. const provides = getProvides(currentInstance)
  43. // 用于保存对话框应用实例和可见性状态
  44. let dialogApp: App | null = null
  45. const visible = ref(false)
  46. let container: HTMLElement | null = null
  47. // 使用reactive存储options,以便能够响应式更新
  48. const options = reactive<DialogProps>({
  49. ...initialOptions
  50. })
  51. const mergeModalOptions: ComputedRef<DialogModalProps> = computed(() => {
  52. return {
  53. ...defaultModalProps,
  54. ...options.modalProps
  55. }
  56. })
  57. const openModal = () => {
  58. // 判断content类型
  59. const isAsync = typeof options.content === 'function'
  60. const isString = typeof options.content === 'string'
  61. const modalComponent = isAsync
  62. ? defineAsyncComponent(options.content as () => Promise<Component>)
  63. : isString
  64. ? h('div', options.content as string)
  65. : options.content
  66. container = document.createElement('div')
  67. document.body.appendChild(container)
  68. // 状态管理
  69. visible.value = true
  70. dialogApp = createApp({
  71. setup() {
  72. const closed = () => {
  73. if (dialogApp) {
  74. dialogApp.unmount()
  75. dialogApp = null
  76. }
  77. if (container) {
  78. container.remove()
  79. container = null
  80. }
  81. }
  82. const instance = getCurrentInstance() as any
  83. if (instance) {
  84. instance.provides = { ...instance.provides, ...provides }
  85. // 使用provide提供上下文
  86. provide('parentInstance', currentInstance)
  87. provide('dialogInstance', instance)
  88. provide('dialogProvides', provides)
  89. }
  90. const onOk = async () => {
  91. // 调用props中传递的onOk回调函数
  92. try {
  93. await unref(options.modalProps)?.onOk?.();
  94. // 如果需要,可以在这里添加默认的关闭行为
  95. } catch (error) {
  96. console.error('Dialog onOk error:', error);
  97. }
  98. }
  99. return () => {
  100. return h(
  101. Dialog,
  102. {
  103. ...unref(mergeModalOptions),
  104. modelValue: visible.value,
  105. 'onUpdate:modelValue': (val: boolean) => {
  106. visible.value = val
  107. },
  108. onOk: onOk,
  109. onClosed: () => {
  110. //如果需要回调就调用回调
  111. unref(options.contentProps)?.onClosed?.()
  112. closed()
  113. }
  114. },
  115. () =>
  116. h(modalComponent, {
  117. ...options.contentProps,
  118. // 将当前实例上下文传递给子组件
  119. instance: instance,
  120. // 传递provides
  121. provides: provides,
  122. // 如果需要,还可以传递其他上下文信息
  123. parentContext: currentInstance
  124. })
  125. )
  126. }
  127. }
  128. })
  129. // 挂载应用
  130. dialogApp.mount(container)
  131. }
  132. const closeModal = () => {
  133. // 设置可见性为false,触发对话框关闭
  134. if (unref(visible)) {
  135. visible.value = false
  136. }
  137. // 如果dialogApp存在,手动卸载
  138. if (dialogApp) {
  139. setTimeout(() => {
  140. dialogApp?.unmount()
  141. dialogApp = null
  142. if (container) {
  143. container.remove()
  144. container = null
  145. }
  146. }, 300) // 给动画效果一些时间
  147. }
  148. }
  149. //更新属性
  150. const updateModalProps = (props: Partial<DialogProps>) => {
  151. if (!dialogApp) {
  152. return false
  153. }
  154. // 更新modalProps
  155. if (props.modalProps) {
  156. options.modalProps = {
  157. ...options.modalProps,
  158. ...props.modalProps
  159. }
  160. }
  161. // 更新contentProps
  162. if (props.contentProps) {
  163. options.contentProps = {
  164. ...options.contentProps,
  165. ...props.contentProps
  166. }
  167. }
  168. // 如果提供了新的content,需要重新打开对话框
  169. if (props.content) {
  170. options.content = props.content
  171. closeModal()
  172. setTimeout(() => {
  173. openModal()
  174. }, 300)
  175. }
  176. return true
  177. }
  178. return {
  179. modalRef: modalRef,
  180. openModal: openModal,
  181. closeModal: closeModal,
  182. updateModalProps: updateModalProps
  183. }
  184. }
  185. export default useDialog