modalApi.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import { defineComponent, h, onMounted, render, ref, type VNode } from 'vue';
  2. import Modal from './Modal.vue';
  3. import Button from './Button.vue';
  4. import Icon from '@/components/Icon.vue';
  5. export type ModalConfirmType = 'info' | 'success' | 'warning' | 'error' | 'confirm';
  6. export interface ModalConfirmOptions {
  7. title?: string;
  8. content?: string | VNode;
  9. okText?: string;
  10. cancelText?: string;
  11. width?: string | number;
  12. maskClosable?: boolean;
  13. showCancel?: boolean;
  14. onOk?: () => void | Promise<void>;
  15. onCancel?: () => void;
  16. }
  17. const typeConfig: Record<ModalConfirmType, { icon: string; color: string }> = {
  18. info: { icon: 'icon-info-fill', color: '#1677ff' },
  19. success: { icon: 'icon-round-check-fill', color: '#00c49f' },
  20. warning: { icon: 'icon-warn-fill', color: '#ff8b2c' },
  21. error: { icon: 'icon-round-close-fill', color: '#ff4d4f' },
  22. confirm: { icon: 'icon-question-fill', color: '#ff8b2c' },
  23. };
  24. const ModalHolder = defineComponent({
  25. name: 'ModalConfirmHolder',
  26. props: {
  27. type: { type: String, required: true },
  28. options: { type: Object, required: true },
  29. onDestroy: { type: Function, required: true },
  30. },
  31. setup(props) {
  32. const show = ref(false);
  33. const okLoading = ref(false);
  34. const opt = props.options as ModalConfirmOptions;
  35. const cfg = typeConfig[props.type as ModalConfirmType];
  36. onMounted(() => {
  37. show.value = true;
  38. });
  39. function destroy() {
  40. (props.onDestroy as () => void)();
  41. }
  42. function handleClose() {
  43. show.value = false;
  44. }
  45. function handleUpdateShow(value: boolean) {
  46. show.value = value;
  47. if (!value) {
  48. setTimeout(destroy, 200);
  49. }
  50. }
  51. function handleOk() {
  52. const result = opt.onOk?.();
  53. if (result && typeof (result as Promise<void>).then === 'function') {
  54. okLoading.value = true;
  55. Promise.resolve(result)
  56. .then(handleClose)
  57. .catch(() => {})
  58. .finally(() => {
  59. okLoading.value = false;
  60. });
  61. } else {
  62. handleClose();
  63. }
  64. }
  65. function handleCancel() {
  66. opt.onCancel?.();
  67. handleClose();
  68. }
  69. return () =>
  70. h(
  71. Modal,
  72. {
  73. show: show.value,
  74. 'onUpdate:show': handleUpdateShow,
  75. title: opt.title,
  76. width: opt.width,
  77. maskClosable: opt.maskClosable,
  78. },
  79. {
  80. default: () =>
  81. h('div', { class: 'flex items-start gap-3' }, [
  82. h(Icon, { name: cfg.icon, size: '1.5rem', color: cfg.color, class: 'flex-shrink-0 mt-0.5' }),
  83. opt.content
  84. ? h(
  85. 'div',
  86. { class: 'flex-1 text-sm whitespace-pre-wrap break-words' },
  87. typeof opt.content === 'string' ? opt.content : opt.content,
  88. )
  89. : null,
  90. ]),
  91. footer: () =>
  92. h('div', { class: 'flex flex-row justify-end items-center gap-2' }, [
  93. opt.showCancel
  94. ? h(Button, { onClick: handleCancel }, { default: () => opt.cancelText ?? '取消' })
  95. : null,
  96. h(
  97. Button,
  98. { type: 'primary', loading: okLoading.value, onClick: handleOk },
  99. { default: () => opt.okText ?? '确定' },
  100. ),
  101. ]),
  102. },
  103. );
  104. },
  105. });
  106. function openConfirm(type: ModalConfirmType, options: ModalConfirmOptions = {}) {
  107. const container = document.createElement('div');
  108. document.body.appendChild(container);
  109. const destroy = () => {
  110. render(null, container);
  111. container.remove();
  112. };
  113. const vm = h(ModalHolder, { type, options, onDestroy: destroy });
  114. render(vm, container);
  115. }
  116. export const ModalApi = {
  117. info: (options?: ModalConfirmOptions) => openConfirm('info', { showCancel: false, ...options }),
  118. success: (options?: ModalConfirmOptions) => openConfirm('success', { showCancel: false, ...options }),
  119. warning: (options?: ModalConfirmOptions) => openConfirm('warning', { showCancel: false, ...options }),
  120. error: (options?: ModalConfirmOptions) => openConfirm('error', { showCancel: false, ...options }),
  121. confirm: (options?: ModalConfirmOptions) => openConfirm('confirm', { showCancel: true, ...options }),
  122. };