index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. <script setup lang="tsx">
  2. import { useRouter } from "vue-router";
  3. import { h, createVNode, ref } from "vue";
  4. import { message } from "@/utils/message";
  5. import formPrimitive from "./formPrimitive.vue";
  6. import forms, { type FormProps } from "./form.vue";
  7. import { cloneDeep, debounce } from "@pureadmin/utils";
  8. import {
  9. addDialog,
  10. closeDialog,
  11. updateDialog,
  12. closeAllDialog
  13. } from "@/components/ReDialog";
  14. defineOptions({
  15. name: "DialogPage"
  16. });
  17. const router = useRouter();
  18. function onBaseClick() {
  19. addDialog({
  20. title: "基础用法",
  21. contentRenderer: () => <p>弹框内容-基础用法</p> // jsx 语法 (注意在.vue文件启用jsx语法,需要在script开启lang="tsx")
  22. });
  23. }
  24. function onDraggableClick() {
  25. addDialog({
  26. title: "可拖拽",
  27. draggable: true,
  28. contentRenderer: () => h("p", "弹框内容-可拖拽") // h 渲染函数 https://cn.vuejs.org/api/render-function.html#h
  29. });
  30. }
  31. function onFullscreenClick() {
  32. addDialog({
  33. title: "全屏",
  34. fullscreen: true,
  35. contentRenderer: () => createVNode("p", null, "弹框内容-全屏") // createVNode 渲染函数 https://cn.vuejs.org/guide/extras/render-function.html#creating-vnodes
  36. });
  37. }
  38. function onFullscreenIconClick() {
  39. addDialog({
  40. title: "全屏按钮和全屏事件",
  41. fullscreenIcon: true,
  42. fullscreenCallBack: ({ options, index }) =>
  43. message(options.fullscreen ? "全屏" : "非全屏"),
  44. contentRenderer: () => <p>弹框内容-全屏按钮和全屏事件</p>
  45. });
  46. }
  47. function onModalClick() {
  48. addDialog({
  49. title: "无背景遮罩层",
  50. modal: false,
  51. contentRenderer: () => <p>弹框内容-无背景遮罩层</p>
  52. });
  53. }
  54. function onStyleClick() {
  55. addDialog({
  56. title: "自定义弹出位置",
  57. top: "60vh",
  58. style: { marginRight: "20px" },
  59. contentRenderer: () => <p>弹框内容-自定义弹出位置</p>
  60. });
  61. }
  62. // 添加 600ms 防抖
  63. const onoOpenDelayClick = debounce(
  64. () =>
  65. addDialog({
  66. title: "延时2秒打开弹框",
  67. openDelay: 2000 - 600,
  68. contentRenderer: () => <p>弹框内容-延时2秒打开弹框</p>
  69. }),
  70. 600
  71. );
  72. function onCloseDelayClick() {
  73. addDialog({
  74. title: "延时2秒关闭弹框",
  75. closeDelay: 2000,
  76. contentRenderer: () => <p>弹框内容-延时2秒关闭弹框</p>
  77. });
  78. }
  79. function onShowCloseClick() {
  80. addDialog({
  81. title: "不显示右上角关闭按钮图标",
  82. showClose: false,
  83. contentRenderer: () => <p>弹框内容-不显示右上角关闭按钮图标</p>
  84. });
  85. }
  86. function onBeforeCloseClick() {
  87. addDialog({
  88. title: "禁止通过键盘ESC关闭",
  89. closeOnPressEscape: false,
  90. contentRenderer: () => <p>弹框内容-禁止通过键盘ESC关闭</p>
  91. });
  92. }
  93. function onCloseOnClickModalClick() {
  94. addDialog({
  95. title: "禁止通过点击modal关闭",
  96. closeOnClickModal: false,
  97. contentRenderer: () => <p>弹框内容-禁止通过点击modal关闭</p>
  98. });
  99. }
  100. function onHideFooterClick() {
  101. addDialog({
  102. title: "隐藏底部取消、确定按钮",
  103. hideFooter: true,
  104. contentRenderer: () => <p>弹框内容-隐藏底部取消、确定按钮</p>
  105. });
  106. }
  107. function onHeaderRendererClick() {
  108. addDialog({
  109. title: "自定义头部",
  110. showClose: false,
  111. headerRenderer: ({ close, titleId, titleClass }) => (
  112. // jsx 语法
  113. <div class="flex flex-row justify-between">
  114. <h4 id={titleId} class={titleClass}>
  115. 自定义头部
  116. </h4>
  117. <el-button type="danger" onClick={close}>
  118. 关闭
  119. </el-button>
  120. </div>
  121. ),
  122. contentRenderer: () => <p>弹框内容-自定义头部</p>
  123. });
  124. }
  125. function onFooterRendererClick() {
  126. addDialog({
  127. title: "自定义底部",
  128. footerRenderer: ({ options, index }) => (
  129. <el-button onClick={() => closeDialog(options, index)}>
  130. {options.title}-{index}
  131. </el-button>
  132. ),
  133. contentRenderer: () => <p>弹框内容-自定义底部</p>
  134. });
  135. }
  136. function onFooterButtonsClick() {
  137. addDialog({
  138. title: "自定义底部按钮",
  139. footerButtons: [
  140. {
  141. label: "按钮1",
  142. size: "small",
  143. type: "success",
  144. btnClick: ({ dialog: { options, index }, button }) => {
  145. console.log(options, index, button);
  146. closeDialog(options, index);
  147. }
  148. },
  149. {
  150. label: "按钮2",
  151. text: true,
  152. bg: true,
  153. btnClick: ({ dialog: { options, index }, button }) => {
  154. console.log(options, index, button);
  155. closeDialog(options, index);
  156. }
  157. },
  158. {
  159. label: "按钮3",
  160. size: "large",
  161. type: "warning",
  162. btnClick: ({ dialog: { options, index }, button }) => {
  163. console.log(options, index, button);
  164. closeDialog(options, index);
  165. }
  166. }
  167. ],
  168. contentRenderer: () => <p>弹框内容-自定义底部按钮</p>
  169. });
  170. }
  171. function onOpenClick() {
  172. addDialog({
  173. title: "打开后的回调",
  174. open: ({ options, index }) => message({ options, index } as any),
  175. contentRenderer: () => <p>弹框内容-打开后的回调</p>
  176. });
  177. }
  178. function onCloseCallBackClick() {
  179. addDialog({
  180. title: "关闭后的回调",
  181. closeCallBack: ({ options, index, args }) => {
  182. console.log(options, index, args);
  183. let text = "";
  184. if (args?.command === "cancel") {
  185. text = "您点击了取消按钮";
  186. } else if (args?.command === "sure") {
  187. text = "您点击了确定按钮";
  188. } else {
  189. text = "您点击了右上角关闭按钮或空白页或按下了esc键";
  190. }
  191. message(text);
  192. },
  193. contentRenderer: () => <p>弹框内容-关闭后的回调</p>
  194. });
  195. }
  196. // 这里为了演示方便,使用了嵌套写法,实际情况下最好把 addDialog 函数抽出来 套娃不可取
  197. function onNestingClick() {
  198. addDialog({
  199. title: "嵌套的弹框",
  200. contentRenderer: ({ index }) => (
  201. <el-button
  202. onClick={() =>
  203. addDialog({
  204. title: `第${index + 1}个子弹框`,
  205. width: "40%",
  206. contentRenderer: ({ index }) => (
  207. <el-button
  208. onClick={() =>
  209. addDialog({
  210. title: `第${index + 1}个子弹框`,
  211. width: "30%",
  212. contentRenderer: () => (
  213. <>
  214. <el-button round onClick={() => closeAllDialog()}>
  215. 哎呦,你干嘛,赶快关闭所有弹框
  216. </el-button>
  217. </>
  218. )
  219. })
  220. }
  221. >
  222. 点击打开第{index + 1}个子弹框
  223. </el-button>
  224. )
  225. })
  226. }
  227. >
  228. 点击打开第{index + 1}个子弹框
  229. </el-button>
  230. )
  231. });
  232. }
  233. // 满足在 contentRenderer 内容区更改弹框自身属性值的场景
  234. function onUpdateClick() {
  235. const curPage = ref(1);
  236. addDialog({
  237. title: `第${curPage.value}页`,
  238. contentRenderer: () => (
  239. <>
  240. <el-button
  241. disabled={curPage.value > 1 ? false : true}
  242. onClick={() => {
  243. curPage.value -= 1;
  244. updateDialog(`第${curPage.value}页`);
  245. }}
  246. >
  247. 上一页
  248. </el-button>
  249. <el-button
  250. onClick={() => {
  251. curPage.value += 1;
  252. updateDialog(`第${curPage.value}页`);
  253. }}
  254. >
  255. 下一页
  256. </el-button>
  257. </>
  258. )
  259. });
  260. }
  261. // Popconfirm 确认框
  262. function onPopconfirmClick() {
  263. addDialog({
  264. width: "30%",
  265. title: "Popconfirm确认框示例",
  266. popconfirm: { title: "是否确认修改当前数据" },
  267. contentRenderer: () => <p>点击右下方确定按钮看看效果吧</p>
  268. });
  269. }
  270. // 结合Form表单(第一种方式,弹框关闭立刻恢复初始值)通过 props 属性接收子组件的 prop 并赋值
  271. function onFormOneClick() {
  272. addDialog({
  273. width: "30%",
  274. title: "结合Form表单(第一种方式)",
  275. contentRenderer: () => forms,
  276. props: {
  277. // 赋默认值
  278. formInline: {
  279. user: "菜虚鲲",
  280. region: "浙江"
  281. }
  282. },
  283. closeCallBack: ({ options, args }) => {
  284. // options.props 是响应式的
  285. const { formInline } = options.props as FormProps;
  286. const text = `姓名:${formInline.user} 城市:${formInline.region}`;
  287. if (args?.command === "cancel") {
  288. // 您点击了取消按钮
  289. message(`您点击了取消按钮,当前表单数据为 ${text}`);
  290. } else if (args?.command === "sure") {
  291. message(`您点击了确定按钮,当前表单数据为 ${text}`);
  292. } else {
  293. message(
  294. `您点击了右上角关闭按钮或空白页或按下了esc键,当前表单数据为 ${text}`
  295. );
  296. }
  297. }
  298. });
  299. }
  300. // 结合Form表单(第二种方式)h 渲染函数 https://cn.vuejs.org/api/render-function.html#h
  301. const formInline = ref({
  302. user: "菜虚鲲",
  303. region: "浙江"
  304. });
  305. const resetFormInline = cloneDeep(formInline.value);
  306. function onFormTwoClick() {
  307. addDialog({
  308. width: "30%",
  309. title: "结合Form表单(第二种方式)",
  310. contentRenderer: () =>
  311. h(forms, {
  312. formInline: formInline.value
  313. }),
  314. closeCallBack: () => {
  315. message(
  316. `当前表单数据为 姓名:${formInline.value.user} 城市:${formInline.value.region}`
  317. );
  318. // 重置表单数据
  319. formInline.value = cloneDeep(resetFormInline);
  320. }
  321. });
  322. }
  323. // 结合Form表单(第三种方式)createVNode 渲染函数 https://cn.vuejs.org/guide/extras/render-function.html#creating-vnodes
  324. const formThreeInline = ref({
  325. user: "菜虚鲲",
  326. region: "浙江"
  327. });
  328. const resetFormThreeInline = cloneDeep(formThreeInline.value);
  329. function onFormThreeClick() {
  330. addDialog({
  331. width: "30%",
  332. title: "结合Form表单(第三种方式)",
  333. contentRenderer: () =>
  334. createVNode(forms, {
  335. formInline: formThreeInline.value
  336. }),
  337. closeCallBack: () => {
  338. message(
  339. `当前表单数据为 姓名:${formThreeInline.value.user} 城市:${formThreeInline.value.region}`
  340. );
  341. // 重置表单数据
  342. formThreeInline.value = cloneDeep(resetFormThreeInline);
  343. }
  344. });
  345. }
  346. // 结合Form表单(第四种方式)使用jsx语法
  347. // 需要注意的是如果 forms 没注册,这里 forms 注册了是因为上面 contentRenderer: () => forms、h(forms) 、createVNode(createVNode) 间接给注册了
  348. // 如果只使用了jsx语法,如下 `contentRenderer: () => <forms formInline={formFourInline.value} />` 是不会给 forms 组件进行注册的,需要在 `script` 中任意位置(最好是末尾)写上 forms 即可
  349. // 同理如果在 tsx 文件中,这么使用 `contentRenderer: () => <forms formInline={formFourInline.value} />`,也是不会给 forms 组件进行注册,需要在 return 中写上 forms
  350. const formFourInline = ref({
  351. user: "菜虚鲲",
  352. region: "浙江"
  353. });
  354. const resetFormFourInline = cloneDeep(formFourInline.value);
  355. function onFormFourClick() {
  356. addDialog({
  357. width: "30%",
  358. title: "结合Form表单(第四种方式)",
  359. contentRenderer: () => <forms formInline={formFourInline.value} />,
  360. closeCallBack: () => {
  361. message(
  362. `当前表单数据为 姓名:${formFourInline.value.user} 城市:${formFourInline.value.region}`
  363. );
  364. // 重置表单数据
  365. formFourInline.value = cloneDeep(resetFormFourInline);
  366. }
  367. });
  368. }
  369. // 子组件 prop 为 primitive 类型的 demo
  370. const formPrimitiveParam = ref("Hello World");
  371. const resetFormPrimitiveParam = ref(formPrimitiveParam.value);
  372. function onFormPrimitiveFormClick() {
  373. addDialog({
  374. width: "30%",
  375. title: "子组件 prop 为 primitive 类型 demo",
  376. contentRenderer: () =>
  377. h(formPrimitive, {
  378. data: formPrimitiveParam.value,
  379. "onUpdate:data": val => (formPrimitiveParam.value = val)
  380. }),
  381. closeCallBack: () => {
  382. message(`当前表单内容:${formPrimitiveParam.value}`);
  383. // 重置表单数据
  384. formPrimitiveParam.value = resetFormPrimitiveParam.value;
  385. }
  386. });
  387. }
  388. function onBeforeCancelClick() {
  389. addDialog({
  390. title: "点击底部取消按钮的回调",
  391. contentRenderer: () => (
  392. <p>弹框内容-点击底部取消按钮的回调(会暂停弹框的关闭)</p>
  393. ),
  394. beforeCancel: (done, { options, index }) => {
  395. console.log(
  396. "%coptions, index===>>>: ",
  397. "color: MidnightBlue; background: Aquamarine; font-size: 20px;",
  398. options,
  399. index
  400. );
  401. // done(); // 需要关闭把注释解开即可
  402. }
  403. });
  404. }
  405. function onBeforeSureClick() {
  406. addDialog({
  407. title: "点击底部确定按钮的回调",
  408. contentRenderer: () => (
  409. <p>
  410. 弹框内容-点击底部确定按钮的回调(会暂停弹框的关闭,经常用于新增、修改弹框内容后调用接口)
  411. </p>
  412. ),
  413. beforeSure: (done, { options, index }) => {
  414. console.log(
  415. "%coptions, index===>>>: ",
  416. "color: MidnightBlue; background: Aquamarine; font-size: 20px;",
  417. options,
  418. index
  419. );
  420. // done(); // 需要关闭把注释解开即可
  421. }
  422. });
  423. }
  424. function onSureBtnLoading() {
  425. addDialog({
  426. sureBtnLoading: true,
  427. title: "点击底部确定按钮可开启按钮动画",
  428. contentRenderer: () => <p>弹框内容-点击底部确定按钮可开启按钮动画</p>,
  429. beforeSure: (done, { closeLoading }) => {
  430. // closeLoading() // 关闭确定按钮动画,不关闭弹框
  431. // done() // 关闭确定按钮动画并关闭弹框
  432. setTimeout(() => done(), 800);
  433. }
  434. });
  435. }
  436. </script>
  437. <template>
  438. <el-card shadow="never">
  439. <template #header>
  440. <div class="card-header">
  441. <span class="font-medium">
  442. 二次封装 Element Plus 的
  443. <el-link
  444. href="https://element-plus.org/zh-CN/component/dialog.html"
  445. target="_blank"
  446. style="margin: 0 4px 5px; font-size: 16px"
  447. >
  448. Dialog
  449. </el-link>
  450. ,采用函数式调用弹框组件(更多操作实例请参考
  451. <span
  452. class="cursor-pointer text-primary"
  453. @click="router.push({ name: 'SystemDept' })"
  454. >
  455. 系统管理页面
  456. </span>
  457. </span>
  458. </div>
  459. <el-link
  460. href="https://github.com/pure-admin/vue-pure-admin/tree/main/src/views/components/dialog"
  461. target="_blank"
  462. >
  463. 代码位置 src/views/components/dialog
  464. </el-link>
  465. </template>
  466. <el-space wrap>
  467. <el-button @click="onBaseClick"> 基础用法 </el-button>
  468. <el-button @click="onDraggableClick"> 可拖拽 </el-button>
  469. <el-button @click="onFullscreenClick"> 全屏 </el-button>
  470. <el-button @click="onFullscreenIconClick"> 全屏按钮和全屏事件 </el-button>
  471. <el-button @click="onModalClick"> 无背景遮罩层 </el-button>
  472. <el-button @click="onStyleClick"> 自定义弹出位置 </el-button>
  473. <el-button @click="onoOpenDelayClick"> 延时2秒打开弹框 </el-button>
  474. <el-button @click="onCloseDelayClick"> 延时2秒关闭弹框 </el-button>
  475. <el-button @click="onShowCloseClick">
  476. 不显示右上角关闭按钮图标
  477. </el-button>
  478. <el-button @click="onBeforeCloseClick"> 禁止通过键盘ESC关闭 </el-button>
  479. <el-button @click="onCloseOnClickModalClick">
  480. 禁止通过点击modal关闭
  481. </el-button>
  482. <el-button @click="onHideFooterClick"> 隐藏底部取消、确定按钮 </el-button>
  483. <el-button @click="onHeaderRendererClick"> 自定义头部 </el-button>
  484. <el-button @click="onFooterRendererClick"> 自定义底部 </el-button>
  485. <el-button @click="onFooterButtonsClick"> 自定义底部按钮 </el-button>
  486. <el-button @click="onOpenClick"> 打开后的回调 </el-button>
  487. <el-button @click="onCloseCallBackClick"> 关闭后的回调 </el-button>
  488. <el-button @click="onNestingClick"> 嵌套的弹框 </el-button>
  489. <el-button @click="onUpdateClick"> 更改弹框自身属性值 </el-button>
  490. <el-button @click="onPopconfirmClick">Popconfirm确认框</el-button>
  491. </el-space>
  492. <el-divider />
  493. <el-space wrap>
  494. <el-button @click="onFormOneClick">
  495. 结合Form表单(第一种方式)
  496. </el-button>
  497. <el-button @click="onFormTwoClick">
  498. 结合Form表单(第二种方式)
  499. </el-button>
  500. <el-button @click="onFormThreeClick">
  501. 结合Form表单(第三种方式)
  502. </el-button>
  503. <el-button @click="onFormFourClick">
  504. 结合Form表单(第四种方式)
  505. </el-button>
  506. <el-button @click="onFormPrimitiveFormClick">
  507. 子组件 prop 为 primitive 类型
  508. </el-button>
  509. </el-space>
  510. <el-divider />
  511. <el-space wrap>
  512. <el-button @click="onBeforeCancelClick">
  513. 点击底部取消按钮的回调(会暂停弹框的关闭)
  514. </el-button>
  515. <el-button @click="onBeforeSureClick">
  516. 点击底部确定按钮的回调(会暂停弹框的关闭,经常用于新增、修改弹框内容后调用接口)
  517. </el-button>
  518. <el-button @click="onSureBtnLoading">
  519. 点击底部确定按钮可开启按钮动画
  520. </el-button>
  521. </el-space>
  522. </el-card>
  523. </template>