index.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. <script setup lang="ts">
  2. import {
  3. type EventType,
  4. type ButtonProps,
  5. type DialogOptions,
  6. closeDialog,
  7. dialogStore
  8. } from "./index";
  9. import { ref, computed } from "vue";
  10. import { isFunction } from "@pureadmin/utils";
  11. import Fullscreen from "~icons/ri/fullscreen-fill";
  12. import ExitFullscreen from "~icons/ri/fullscreen-exit-fill";
  13. defineOptions({
  14. name: "ReDialog"
  15. });
  16. const sureBtnMap = ref({});
  17. const fullscreen = ref(false);
  18. const footerButtons = computed(() => {
  19. return (options: DialogOptions) => {
  20. return options?.footerButtons?.length > 0
  21. ? options.footerButtons
  22. : ([
  23. {
  24. label: "取消",
  25. text: true,
  26. bg: true,
  27. btnClick: ({ dialog: { options, index } }) => {
  28. const done = () =>
  29. closeDialog(options, index, { command: "cancel" });
  30. if (options?.beforeCancel && isFunction(options?.beforeCancel)) {
  31. options.beforeCancel(done, { options, index });
  32. } else {
  33. done();
  34. }
  35. }
  36. },
  37. {
  38. label: "确定",
  39. type: "primary",
  40. text: true,
  41. bg: true,
  42. popconfirm: options?.popconfirm,
  43. btnClick: ({ dialog: { options, index } }) => {
  44. if (options?.sureBtnLoading) {
  45. sureBtnMap.value[index] = Object.assign(
  46. {},
  47. sureBtnMap.value[index],
  48. {
  49. loading: true
  50. }
  51. );
  52. }
  53. const closeLoading = () => {
  54. if (options?.sureBtnLoading) {
  55. sureBtnMap.value[index].loading = false;
  56. }
  57. };
  58. const done = () => {
  59. closeLoading();
  60. closeDialog(options, index, { command: "sure" });
  61. };
  62. if (options?.beforeSure && isFunction(options?.beforeSure)) {
  63. options.beforeSure(done, { options, index, closeLoading });
  64. } else {
  65. done();
  66. }
  67. }
  68. }
  69. ] as Array<ButtonProps>);
  70. };
  71. });
  72. const fullscreenClass = computed(() => {
  73. return [
  74. "el-icon",
  75. "el-dialog__close",
  76. "-translate-x-2",
  77. "cursor-pointer",
  78. "hover:text-[red]!"
  79. ];
  80. });
  81. function eventsCallBack(
  82. event: EventType,
  83. options: DialogOptions,
  84. index: number,
  85. isClickFullScreen = false
  86. ) {
  87. if (!isClickFullScreen) fullscreen.value = options?.fullscreen ?? false;
  88. if (options?.[event] && isFunction(options?.[event])) {
  89. return options?.[event]({ options, index });
  90. }
  91. }
  92. function handleClose(
  93. options: DialogOptions,
  94. index: number,
  95. args = { command: "close" }
  96. ) {
  97. closeDialog(options, index, args);
  98. eventsCallBack("close", options, index);
  99. }
  100. </script>
  101. <template>
  102. <el-dialog
  103. v-for="(options, index) in dialogStore"
  104. :key="index"
  105. v-bind="options"
  106. v-model="options.visible"
  107. class="pure-dialog"
  108. :fullscreen="fullscreen ? true : options?.fullscreen ? true : false"
  109. @closed="handleClose(options, index)"
  110. @opened="eventsCallBack('open', options, index)"
  111. @openAutoFocus="eventsCallBack('openAutoFocus', options, index)"
  112. @closeAutoFocus="eventsCallBack('closeAutoFocus', options, index)"
  113. >
  114. <!-- header -->
  115. <template
  116. v-if="options?.fullscreenIcon || options?.headerRenderer"
  117. #header="{ close, titleId, titleClass }"
  118. >
  119. <div
  120. v-if="options?.fullscreenIcon"
  121. class="flex items-center justify-between"
  122. >
  123. <span :id="titleId" :class="titleClass">{{ options?.title }}</span>
  124. <i
  125. v-if="!options?.fullscreen"
  126. :class="fullscreenClass"
  127. @click="
  128. () => {
  129. fullscreen = !fullscreen;
  130. eventsCallBack(
  131. 'fullscreenCallBack',
  132. { ...options, fullscreen },
  133. index,
  134. true
  135. );
  136. }
  137. "
  138. >
  139. <IconifyIconOffline
  140. class="pure-dialog-svg"
  141. :icon="
  142. options?.fullscreen
  143. ? ExitFullscreen
  144. : fullscreen
  145. ? ExitFullscreen
  146. : Fullscreen
  147. "
  148. />
  149. </i>
  150. </div>
  151. <component
  152. :is="options?.headerRenderer({ close, titleId, titleClass })"
  153. v-else
  154. />
  155. </template>
  156. <component
  157. v-bind="options?.props"
  158. :is="options.contentRenderer({ options, index })"
  159. @close="args => handleClose(options, index, args)"
  160. />
  161. <!-- footer -->
  162. <template v-if="!options?.hideFooter" #footer>
  163. <template v-if="options?.footerRenderer">
  164. <component :is="options?.footerRenderer({ options, index })" />
  165. </template>
  166. <span v-else>
  167. <template v-for="(btn, key) in footerButtons(options)" :key="key">
  168. <el-popconfirm
  169. v-if="btn.popconfirm"
  170. v-bind="btn.popconfirm"
  171. @confirm="
  172. btn.btnClick({
  173. dialog: { options, index },
  174. button: { btn, index: key }
  175. })
  176. "
  177. >
  178. <template #reference>
  179. <el-button v-bind="btn">{{ btn?.label }}</el-button>
  180. </template>
  181. </el-popconfirm>
  182. <el-button
  183. v-else
  184. v-bind="btn"
  185. :loading="key === 1 && sureBtnMap[index]?.loading"
  186. @click="
  187. btn.btnClick({
  188. dialog: { options, index },
  189. button: { btn, index: key }
  190. })
  191. "
  192. >
  193. {{ btn?.label }}
  194. </el-button>
  195. </template>
  196. </span>
  197. </template>
  198. </el-dialog>
  199. </template>