index.vue 15 KB

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