mirror of
https://gitee.com/yiming_chang/vue-pure-admin.git
synced 2024-12-01 18:57:39 +08:00
feat: 新增函数式抽屉组件 (#1183)
* feat: 函数式抽屉组件 * feat: 添加ReDrawer demo * fix: 组件ReDrawer 增加按钮loading等功能
This commit is contained in:
parent
384c789fc0
commit
5032a75221
@ -83,6 +83,7 @@ menus:
|
|||||||
pureFive: "500"
|
pureFive: "500"
|
||||||
pureComponents: Components
|
pureComponents: Components
|
||||||
pureDialog: Dialog
|
pureDialog: Dialog
|
||||||
|
pureDrawer: Drawer
|
||||||
pureMessage: Message Tips
|
pureMessage: Message Tips
|
||||||
pureVideo: Video
|
pureVideo: Video
|
||||||
pureSegmented: Segmented
|
pureSegmented: Segmented
|
||||||
|
@ -83,6 +83,7 @@ menus:
|
|||||||
pureFive: "500"
|
pureFive: "500"
|
||||||
pureComponents: 组件
|
pureComponents: 组件
|
||||||
pureDialog: 函数式弹框
|
pureDialog: 函数式弹框
|
||||||
|
pureDrawer: 函数式抽屉
|
||||||
pureMessage: 消息提示
|
pureMessage: 消息提示
|
||||||
pureVideo: 视频
|
pureVideo: 视频
|
||||||
pureSegmented: 分段控制器
|
pureSegmented: 分段控制器
|
||||||
@ -233,4 +234,4 @@ login:
|
|||||||
purePassWordRuleReg: 密码格式应为8-18位数字、字母、符号的任意两种组合
|
purePassWordRuleReg: 密码格式应为8-18位数字、字母、符号的任意两种组合
|
||||||
purePassWordSureReg: 请输入确认密码
|
purePassWordSureReg: 请输入确认密码
|
||||||
purePassWordDifferentReg: 两次密码不一致!
|
purePassWordDifferentReg: 两次密码不一致!
|
||||||
purePassWordUpdateReg: 修改密码成功
|
purePassWordUpdateReg: 修改密码成功
|
@ -2,6 +2,7 @@
|
|||||||
<el-config-provider :locale="currentLocale">
|
<el-config-provider :locale="currentLocale">
|
||||||
<router-view />
|
<router-view />
|
||||||
<ReDialog />
|
<ReDialog />
|
||||||
|
<ReDrawer />
|
||||||
</el-config-provider>
|
</el-config-provider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -10,6 +11,7 @@ import { defineComponent } from "vue";
|
|||||||
import { checkVersion } from "version-rocket";
|
import { checkVersion } from "version-rocket";
|
||||||
import { ElConfigProvider } from "element-plus";
|
import { ElConfigProvider } from "element-plus";
|
||||||
import { ReDialog } from "@/components/ReDialog";
|
import { ReDialog } from "@/components/ReDialog";
|
||||||
|
import { ReDrawer } from "@/components/ReDrawer";
|
||||||
import en from "element-plus/es/locale/lang/en";
|
import en from "element-plus/es/locale/lang/en";
|
||||||
import zhCn from "element-plus/es/locale/lang/zh-cn";
|
import zhCn from "element-plus/es/locale/lang/zh-cn";
|
||||||
import plusEn from "plus-pro-components/es/locale/lang/en";
|
import plusEn from "plus-pro-components/es/locale/lang/en";
|
||||||
@ -19,7 +21,8 @@ export default defineComponent({
|
|||||||
name: "app",
|
name: "app",
|
||||||
components: {
|
components: {
|
||||||
[ElConfigProvider.name]: ElConfigProvider,
|
[ElConfigProvider.name]: ElConfigProvider,
|
||||||
ReDialog
|
ReDialog,
|
||||||
|
ReDrawer
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
currentLocale() {
|
currentLocale() {
|
||||||
|
64
src/components/ReDrawer/index.ts
Normal file
64
src/components/ReDrawer/index.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { ref } from "vue";
|
||||||
|
import reDrawer from "./index.vue";
|
||||||
|
import { useTimeoutFn } from "@vueuse/core";
|
||||||
|
import { withInstall } from "@pureadmin/utils";
|
||||||
|
import type {
|
||||||
|
EventType,
|
||||||
|
ArgsType,
|
||||||
|
DrawerProps,
|
||||||
|
DrawerOptions,
|
||||||
|
ButtonProps
|
||||||
|
} from "./type";
|
||||||
|
|
||||||
|
const drawerStore = ref<Array<DrawerOptions>>([]);
|
||||||
|
|
||||||
|
/** 打开抽屉 */
|
||||||
|
const addDrawer = (options: DrawerOptions) => {
|
||||||
|
const open = () =>
|
||||||
|
drawerStore.value.push(Object.assign(options, { visible: true }));
|
||||||
|
if (options?.openDelay) {
|
||||||
|
useTimeoutFn(() => {
|
||||||
|
open();
|
||||||
|
}, options.openDelay);
|
||||||
|
} else {
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 关闭抽屉 */
|
||||||
|
const closeDrawer = (options: DrawerOptions, index: number, args?: any) => {
|
||||||
|
drawerStore.value[index].visible = false;
|
||||||
|
options.closeCallBack && options.closeCallBack({ options, index, args });
|
||||||
|
|
||||||
|
const closeDelay = options?.closeDelay ?? 200;
|
||||||
|
useTimeoutFn(() => {
|
||||||
|
drawerStore.value.splice(index, 1);
|
||||||
|
}, closeDelay);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 更改抽屉自身属性值
|
||||||
|
* @param value 属性值
|
||||||
|
* @param key 属性,默认`title`
|
||||||
|
* @param index 弹框索引(默认`0`,代表只有一个弹框,对于嵌套弹框要改哪个弹框的属性值就把该弹框索引赋给`index`)
|
||||||
|
*/
|
||||||
|
const updateDrawer = (value: any, key = "title", index = 0) => {
|
||||||
|
drawerStore.value[index][key] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 关闭所有弹框 */
|
||||||
|
const closeAllDrawer = () => {
|
||||||
|
drawerStore.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const ReDrawer = withInstall(reDrawer);
|
||||||
|
|
||||||
|
export type { EventType, ArgsType, DrawerOptions, DrawerProps, ButtonProps };
|
||||||
|
export {
|
||||||
|
ReDrawer,
|
||||||
|
drawerStore,
|
||||||
|
addDrawer,
|
||||||
|
closeDrawer,
|
||||||
|
updateDrawer,
|
||||||
|
closeAllDrawer
|
||||||
|
};
|
169
src/components/ReDrawer/index.vue
Normal file
169
src/components/ReDrawer/index.vue
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
type EventType,
|
||||||
|
type ButtonProps,
|
||||||
|
type DrawerOptions,
|
||||||
|
closeDrawer,
|
||||||
|
drawerStore
|
||||||
|
} from "./index";
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
import { isFunction } from "@pureadmin/utils";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "ReDrawer"
|
||||||
|
});
|
||||||
|
|
||||||
|
const sureBtnMap = ref({});
|
||||||
|
|
||||||
|
const footerButtons = computed(() => {
|
||||||
|
return (options: DrawerOptions) => {
|
||||||
|
return options?.footerButtons?.length > 0
|
||||||
|
? options.footerButtons
|
||||||
|
: ([
|
||||||
|
{
|
||||||
|
label: "取消",
|
||||||
|
text: true,
|
||||||
|
bg: true,
|
||||||
|
btnClick: ({ drawer: { options, index } }) => {
|
||||||
|
const done = () =>
|
||||||
|
closeDrawer(options, index, { command: "cancel" });
|
||||||
|
if (options?.beforeCancel && isFunction(options?.beforeCancel)) {
|
||||||
|
options.beforeCancel(done, { options, index });
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "确定",
|
||||||
|
type: "primary",
|
||||||
|
text: true,
|
||||||
|
bg: true,
|
||||||
|
popConfirm: options?.popConfirm,
|
||||||
|
btnClick: ({ drawer: { options, index } }) => {
|
||||||
|
if (options?.sureBtnLoading) {
|
||||||
|
sureBtnMap.value[index] = Object.assign(
|
||||||
|
{},
|
||||||
|
sureBtnMap.value[index],
|
||||||
|
{
|
||||||
|
loading: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const closeLoading = () => {
|
||||||
|
if (options?.sureBtnLoading) {
|
||||||
|
sureBtnMap.value[index].loading = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const done = () => {
|
||||||
|
closeLoading();
|
||||||
|
closeDrawer(options, index, { command: "sure" });
|
||||||
|
};
|
||||||
|
if (options?.beforeSure && isFunction(options?.beforeSure)) {
|
||||||
|
options.beforeSure(done, { options, index, closeLoading });
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] as Array<ButtonProps>);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function eventsCallBack(
|
||||||
|
event: EventType,
|
||||||
|
options: DrawerOptions,
|
||||||
|
index: number
|
||||||
|
) {
|
||||||
|
if (options?.[event] && isFunction(options?.[event])) {
|
||||||
|
return options?.[event]({ options, index });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {DrawerOptions} options - 包含抽屉相关配置的对象
|
||||||
|
* @param {number} index - 抽屉的索引
|
||||||
|
* @param {Object} args - 传递给关闭抽屉操作的参数对象,默认为 { command: 'close' }
|
||||||
|
* @returns {void} 这个函数不返回任何值
|
||||||
|
*/
|
||||||
|
function handleClose(
|
||||||
|
options: DrawerOptions,
|
||||||
|
index: number,
|
||||||
|
args = { command: "close" }
|
||||||
|
) {
|
||||||
|
closeDrawer(options, index, args);
|
||||||
|
eventsCallBack("close", options, index);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-drawer
|
||||||
|
v-for="(options, index) in drawerStore"
|
||||||
|
:key="index"
|
||||||
|
v-bind="options"
|
||||||
|
v-model="options.visible"
|
||||||
|
class="pure-drawer"
|
||||||
|
:append-to-body="!!options?.appendToBody"
|
||||||
|
:append-to="options?.appendTo ? options.appendTo : 'body'"
|
||||||
|
:destroy-on-close="!!options?.destroyOnClose"
|
||||||
|
:lock-scroll="!!options?.lockScroll"
|
||||||
|
@closed="handleClose(options, index)"
|
||||||
|
@opened="eventsCallBack('open', options, index)"
|
||||||
|
@open-auto-focus="eventsCallBack('openAutoFocus', options, index)"
|
||||||
|
@close-auto-focus="eventsCallBack('closeAutoFocus', options, index)"
|
||||||
|
>
|
||||||
|
<!-- header -->
|
||||||
|
<template
|
||||||
|
v-if="options?.headerRenderer"
|
||||||
|
#header="{ close, titleId, titleClass }"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="options?.headerRenderer({ close, titleId, titleClass })"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<!-- body -->
|
||||||
|
<component
|
||||||
|
v-bind="options?.props"
|
||||||
|
:is="options.contentRenderer({ options, index })"
|
||||||
|
@close="args => handleClose(options, index, args)"
|
||||||
|
/>
|
||||||
|
<!-- footer -->
|
||||||
|
<template v-if="!options?.hideFooter" #footer>
|
||||||
|
<template v-if="options?.footerRenderer">
|
||||||
|
<component :is="options?.footerRenderer({ options, index })" />
|
||||||
|
</template>
|
||||||
|
<span v-else>
|
||||||
|
<template v-for="(btn, key) in footerButtons(options)" :key="key">
|
||||||
|
<el-popconfirm
|
||||||
|
v-if="btn.popConfirm"
|
||||||
|
v-bind="btn.popConfirm"
|
||||||
|
@confirm="
|
||||||
|
btn.btnClick({
|
||||||
|
drawer: { options, index },
|
||||||
|
button: { btn, index: key }
|
||||||
|
})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<el-button v-bind="btn">{{ btn?.label }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
v-bind="btn"
|
||||||
|
:loading="key === 1 && sureBtnMap[index]?.loading"
|
||||||
|
@click="
|
||||||
|
btn.btnClick({
|
||||||
|
drawer: { options, index },
|
||||||
|
button: { btn, index: key }
|
||||||
|
})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ btn?.label }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
262
src/components/ReDrawer/type.ts
Normal file
262
src/components/ReDrawer/type.ts
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
import type { CSSProperties, VNode, Component } from "vue";
|
||||||
|
|
||||||
|
type DoneFn = (cancel?: boolean) => void;
|
||||||
|
type EventType = "open" | "close" | "openAutoFocus" | "closeAutoFocus";
|
||||||
|
type ArgsType = {
|
||||||
|
/** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了 `esc` 键 */
|
||||||
|
command: "cancel" | "sure" | "close";
|
||||||
|
};
|
||||||
|
|
||||||
|
type ButtonType =
|
||||||
|
| "primary"
|
||||||
|
| "success"
|
||||||
|
| "warning"
|
||||||
|
| "danger"
|
||||||
|
| "info"
|
||||||
|
| "text";
|
||||||
|
|
||||||
|
type DrawerProps = {
|
||||||
|
/** `Drawer` 的显示与隐藏 */
|
||||||
|
visible?: boolean;
|
||||||
|
/** `Drawer` 自身是否插入至 `body` 元素上。嵌套的 `Drawer` 必须指定该属性并赋值为 `true`,默认 `false` */
|
||||||
|
appendToBody?: boolean;
|
||||||
|
/** 挂载到哪个 `DOM` 元素 将覆盖 `appendToBody` */
|
||||||
|
appendTo?: string;
|
||||||
|
/** 是否在 `Drawer` 出现时将 `body` 滚动锁定,默认 `true` */
|
||||||
|
lockScroll?: boolean;
|
||||||
|
/** 关闭前的回调,会暂停 `Drawer` 的关闭 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */
|
||||||
|
beforeClose?: (done: DoneFn) => void;
|
||||||
|
/** 是否可以通过点击 `modal` 关闭 `Drawer` ,默认 `true` */
|
||||||
|
closeOnClickModal?: boolean;
|
||||||
|
/** 是否可以通过按下 `ESC` 关闭 `Drawer` ,默认 `true` */
|
||||||
|
closeOnPressEscape?: boolean;
|
||||||
|
/** 是否显示关闭按钮,默认 `true` */
|
||||||
|
showClose?: boolean;
|
||||||
|
/** `Drawer` 打开的延时时间,单位毫秒,默认 `0` */
|
||||||
|
openDelay?: number;
|
||||||
|
/** `Drawer` 关闭的延时时间,单位毫秒,默认 `0` */
|
||||||
|
closeDelay?: number;
|
||||||
|
/** `Drawer` 自定义类名 */
|
||||||
|
class?: string;
|
||||||
|
/** `Drawer` 的自定义样式 */
|
||||||
|
style?: CSSProperties;
|
||||||
|
/** 控制是否在关闭 `Drawer` 之后将子元素全部销毁,默认 `false` */
|
||||||
|
destroyOnClose?: boolean;
|
||||||
|
/** 是否需要遮罩层,默认 `true` */
|
||||||
|
modal?: boolean;
|
||||||
|
/** `Drawer` 打开的方向,默认 `rtl` */
|
||||||
|
direction?: "rtl" | "ltr" | "ttb" | "btt";
|
||||||
|
/** `Drawer` 窗体的大小, 当使用 `number` 类型时, 以像素为单位, 当使用 `string` 类型时, 请传入 `'x%'`, 否则便会以 `number` 类型解释 */
|
||||||
|
size?: string | number;
|
||||||
|
/** `Drawer` 的标题 */
|
||||||
|
title?: string;
|
||||||
|
/** 控制是否显示 `header` 栏, 默认为 `true`, 当此项为 `false` 时, `title attribute` 和 `title slot` 均不生效 */
|
||||||
|
withHeader?: boolean;
|
||||||
|
/** 遮罩层的自定义类名 */
|
||||||
|
modalClass?: string;
|
||||||
|
/** 设置 `z-index` */
|
||||||
|
zIndex?: number;
|
||||||
|
/** `header` 的 `aria-level` 属性,默认 `2` */
|
||||||
|
headerAriaLevel?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
//element-plus.org/zh-CN/component/popConfirm.html#attributes
|
||||||
|
type PopConfirm = {
|
||||||
|
/** 标题 */
|
||||||
|
title?: string;
|
||||||
|
/** 确认按钮文字 */
|
||||||
|
confirmButtonText?: string;
|
||||||
|
/** 取消按钮文字 */
|
||||||
|
cancelButtonText?: string;
|
||||||
|
/** 确认按钮类型,默认 `primary` */
|
||||||
|
confirmButtonType?: ButtonType;
|
||||||
|
/** 取消按钮类型,默认 `text` */
|
||||||
|
cancelButtonType?: ButtonType;
|
||||||
|
/** 自定义图标,默认 `QuestionFilled` */
|
||||||
|
icon?: string | Component;
|
||||||
|
/** `Icon` 颜色,默认 `#f90` */
|
||||||
|
iconColor?: string;
|
||||||
|
/** 是否隐藏 `Icon`,默认 `false` */
|
||||||
|
hideIcon?: boolean;
|
||||||
|
/** 关闭时的延迟,默认 `200` */
|
||||||
|
hideAfter?: number;
|
||||||
|
/** 是否将 `popover` 的下拉列表插入至 `body` 元素,默认 `true` */
|
||||||
|
teleported?: boolean;
|
||||||
|
/** 当 `popover` 组件长时间不触发且 `persistent` 属性设置为 `false` 时, `popover` 将会被删除,默认 `false` */
|
||||||
|
persistent?: boolean;
|
||||||
|
/** 弹层宽度,最小宽度 `150px`,默认 `150` */
|
||||||
|
width?: string | number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BtnClickDrawer = {
|
||||||
|
options?: DrawerOptions;
|
||||||
|
index?: number;
|
||||||
|
};
|
||||||
|
type BtnClickButton = {
|
||||||
|
btn?: ButtonProps;
|
||||||
|
index?: number;
|
||||||
|
};
|
||||||
|
/** https://element-plus.org/zh-CN/component/button.html#button-attributes */
|
||||||
|
type ButtonProps = {
|
||||||
|
/** 按钮文字 */
|
||||||
|
label: string;
|
||||||
|
/** 按钮尺寸 */
|
||||||
|
size?: "large" | "default" | "small";
|
||||||
|
/** 按钮类型 */
|
||||||
|
type?: "primary" | "success" | "warning" | "danger" | "info";
|
||||||
|
/** 是否为朴素按钮,默认 `false` */
|
||||||
|
plain?: boolean;
|
||||||
|
/** 是否为文字按钮,默认 `false` */
|
||||||
|
text?: boolean;
|
||||||
|
/** 是否显示文字按钮背景颜色,默认 `false` */
|
||||||
|
bg?: boolean;
|
||||||
|
/** 是否为链接按钮,默认 `false` */
|
||||||
|
link?: boolean;
|
||||||
|
/** 是否为圆角按钮,默认 `false` */
|
||||||
|
round?: boolean;
|
||||||
|
/** 是否为圆形按钮,默认 `false` */
|
||||||
|
circle?: boolean;
|
||||||
|
/** 确认按钮的 `PopConfirm` 气泡确认框相关配置 */
|
||||||
|
popConfirm?: PopConfirm;
|
||||||
|
/** 是否为加载中状态,默认 `false` */
|
||||||
|
loading?: boolean;
|
||||||
|
/** 自定义加载中状态图标组件 */
|
||||||
|
loadingIcon?: string | Component;
|
||||||
|
/** 按钮是否为禁用状态,默认 `false` */
|
||||||
|
disabled?: boolean;
|
||||||
|
/** 图标组件 */
|
||||||
|
icon?: string | Component;
|
||||||
|
/** 是否开启原生 `autofocus` 属性,默认 `false` */
|
||||||
|
autofocus?: boolean;
|
||||||
|
/** 原生 `type` 属性,默认 `button` */
|
||||||
|
nativeType?: "button" | "submit" | "reset";
|
||||||
|
/** 自动在两个中文字符之间插入空格 */
|
||||||
|
autoInsertSpace?: boolean;
|
||||||
|
/** 自定义按钮颜色, 并自动计算 `hover` 和 `active` 触发后的颜色 */
|
||||||
|
color?: string;
|
||||||
|
/** `dark` 模式, 意味着自动设置 `color` 为 `dark` 模式的颜色,默认 `false` */
|
||||||
|
dark?: boolean;
|
||||||
|
/** 自定义元素标签 */
|
||||||
|
tag?: string | Component;
|
||||||
|
/** 点击按钮后触发的回调 */
|
||||||
|
btnClick?: ({
|
||||||
|
drawer,
|
||||||
|
button
|
||||||
|
}: {
|
||||||
|
/** 当前 `Drawer` 信息 */
|
||||||
|
drawer: BtnClickDrawer;
|
||||||
|
/** 当前 `button` 信息 */
|
||||||
|
button: BtnClickButton;
|
||||||
|
}) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface DrawerOptions extends DrawerProps {
|
||||||
|
/** 内容区组件的 `props`,可通过 `defineProps` 接收 */
|
||||||
|
props?: any;
|
||||||
|
/** 是否隐藏 `Drawer` 按钮操作区的内容 */
|
||||||
|
hideFooter?: boolean;
|
||||||
|
/** 确认按钮的 `PopConfirm` 气泡确认框相关配置 */
|
||||||
|
popConfirm?: PopConfirm;
|
||||||
|
/** 点击确定按钮后是否开启 `loading` 加载动画 */
|
||||||
|
sureBtnLoading?: boolean;
|
||||||
|
/**
|
||||||
|
* @description 自定义抽屉标题的内容渲染器
|
||||||
|
* @see {@link https://element-plus.org/zh-CN/component/drawer.html#%E6%8F%92%E6%A7%BD}
|
||||||
|
*/
|
||||||
|
headerRenderer?: ({
|
||||||
|
close,
|
||||||
|
titleId,
|
||||||
|
titleClass
|
||||||
|
}: {
|
||||||
|
close: Function;
|
||||||
|
titleId: string;
|
||||||
|
titleClass: string;
|
||||||
|
}) => VNode | Component;
|
||||||
|
/** 自定义内容渲染器 */
|
||||||
|
contentRenderer?: ({
|
||||||
|
options,
|
||||||
|
index
|
||||||
|
}: {
|
||||||
|
options: DrawerOptions;
|
||||||
|
index: number;
|
||||||
|
}) => VNode | Component;
|
||||||
|
/** 自定义按钮操作区的内容渲染器,会覆盖`footerButtons`以及默认的 `取消` 和 `确定` 按钮 */
|
||||||
|
footerRenderer?: ({
|
||||||
|
options,
|
||||||
|
index
|
||||||
|
}: {
|
||||||
|
options: DrawerOptions;
|
||||||
|
index: number;
|
||||||
|
}) => VNode | Component;
|
||||||
|
/** 自定义底部按钮操作 */
|
||||||
|
footerButtons?: Array<ButtonProps>;
|
||||||
|
/** `Drawer` 打开后的回调 */
|
||||||
|
open?: ({
|
||||||
|
options,
|
||||||
|
index
|
||||||
|
}: {
|
||||||
|
options: DrawerOptions;
|
||||||
|
index: number;
|
||||||
|
}) => void;
|
||||||
|
/** `Drawer` 关闭后的回调(只有点击右上角关闭按钮或空白页或按下了esc键关闭页面时才会触发) */
|
||||||
|
close?: ({
|
||||||
|
options,
|
||||||
|
index
|
||||||
|
}: {
|
||||||
|
options: DrawerOptions;
|
||||||
|
index: number;
|
||||||
|
}) => void;
|
||||||
|
/** `Drawer` 关闭后的回调。 `args` 返回的 `command` 值解析:`cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键 */
|
||||||
|
closeCallBack?: ({
|
||||||
|
options,
|
||||||
|
index,
|
||||||
|
args
|
||||||
|
}: {
|
||||||
|
options: DrawerOptions;
|
||||||
|
index: number;
|
||||||
|
args: any;
|
||||||
|
}) => void;
|
||||||
|
/** 输入焦点聚焦在 `Drawer` 内容时的回调 */
|
||||||
|
openAutoFocus?: ({
|
||||||
|
options,
|
||||||
|
index
|
||||||
|
}: {
|
||||||
|
options: DrawerOptions;
|
||||||
|
index: number;
|
||||||
|
}) => void;
|
||||||
|
/** 输入焦点从 `Drawer` 内容失焦时的回调 */
|
||||||
|
closeAutoFocus?: ({
|
||||||
|
options,
|
||||||
|
index
|
||||||
|
}: {
|
||||||
|
options: DrawerOptions;
|
||||||
|
index: number;
|
||||||
|
}) => void;
|
||||||
|
|
||||||
|
/** 点击底部取消按钮的回调,会暂停 `Drawer` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */
|
||||||
|
beforeCancel?: (
|
||||||
|
done: Function,
|
||||||
|
{
|
||||||
|
options,
|
||||||
|
index
|
||||||
|
}: {
|
||||||
|
options: DrawerOptions;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
) => void;
|
||||||
|
/** 点击底部确定按钮的回调,会暂停 `Drawer` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */
|
||||||
|
beforeSure?: (
|
||||||
|
done: Function,
|
||||||
|
{
|
||||||
|
options,
|
||||||
|
index,
|
||||||
|
closeLoading
|
||||||
|
}: {
|
||||||
|
options: DrawerOptions;
|
||||||
|
index: number;
|
||||||
|
closeLoading: Function;
|
||||||
|
}
|
||||||
|
) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { ButtonProps, DrawerOptions, ArgsType, DrawerProps, EventType };
|
@ -18,6 +18,14 @@ export default {
|
|||||||
title: $t("menus.pureDialog")
|
title: $t("menus.pureDialog")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/components/drawer",
|
||||||
|
name: "DrawerPage",
|
||||||
|
component: () => import("@/views/components/drawer/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: $t("menus.pureDrawer")
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/components/message",
|
path: "/components/message",
|
||||||
name: "Message",
|
name: "Message",
|
||||||
|
@ -280,11 +280,11 @@ function onUpdateClick() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// popconfirm 确认框
|
// Popconfirm 确认框
|
||||||
function onPopconfirmClick() {
|
function onPopconfirmClick() {
|
||||||
addDialog({
|
addDialog({
|
||||||
width: "30%",
|
width: "30%",
|
||||||
title: "popconfirm确认框示例",
|
title: "Popconfirm确认框示例",
|
||||||
popconfirm: { title: "是否确认修改当前数据" },
|
popconfirm: { title: "是否确认修改当前数据" },
|
||||||
contentRenderer: () => <p>点击右下方确定按钮看看效果吧</p>
|
contentRenderer: () => <p>点击右下方确定按钮看看效果吧</p>
|
||||||
});
|
});
|
||||||
@ -519,7 +519,7 @@ function onSureBtnLoading() {
|
|||||||
<el-button @click="onCloseCallBackClick"> 关闭后的回调 </el-button>
|
<el-button @click="onCloseCallBackClick"> 关闭后的回调 </el-button>
|
||||||
<el-button @click="onNestingClick"> 嵌套的弹框 </el-button>
|
<el-button @click="onNestingClick"> 嵌套的弹框 </el-button>
|
||||||
<el-button @click="onUpdateClick"> 更改弹框自身属性值 </el-button>
|
<el-button @click="onUpdateClick"> 更改弹框自身属性值 </el-button>
|
||||||
<el-button @click="onPopconfirmClick">popconfirm确认框</el-button>
|
<el-button @click="onPopconfirmClick">Popconfirm确认框</el-button>
|
||||||
</el-space>
|
</el-space>
|
||||||
<el-divider />
|
<el-divider />
|
||||||
<el-space wrap>
|
<el-space wrap>
|
||||||
|
47
src/views/components/drawer/form.vue
Normal file
47
src/views/components/drawer/form.vue
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
// 声明 props 类型
|
||||||
|
export interface FormProps {
|
||||||
|
formInline: {
|
||||||
|
user: string;
|
||||||
|
region: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 声明 props 默认值
|
||||||
|
// 推荐阅读:https://cn.vuejs.org/guide/typescript/composition-api.html#typing-component-props
|
||||||
|
const props = withDefaults(defineProps<FormProps>(), {
|
||||||
|
formInline: () => ({ user: "", region: "" })
|
||||||
|
});
|
||||||
|
|
||||||
|
// vue 规定所有的 prop 都遵循着单向绑定原则,直接修改 prop 时,Vue 会抛出警告。此处的写法仅仅是为了消除警告。
|
||||||
|
// 因为对一个 reactive 对象执行 ref,返回 Ref 对象的 value 值仍为传入的 reactive 对象,
|
||||||
|
// 即 newFormInline === props.formInline 为 true,所以此处代码的实际效果,仍是直接修改 props.formInline。
|
||||||
|
// 但该写法仅适用于 props.formInline 是一个对象类型的情况,原始类型需抛出事件
|
||||||
|
// 推荐阅读:https://cn.vuejs.org/guide/components/props.html#one-way-data-flow
|
||||||
|
const newFormInline = ref(props.formInline);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-form :model="newFormInline">
|
||||||
|
<el-form-item label="姓名">
|
||||||
|
<el-input
|
||||||
|
v-model="newFormInline.user"
|
||||||
|
class="!w-[220px]"
|
||||||
|
placeholder="请输入姓名"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="城市">
|
||||||
|
<el-select
|
||||||
|
v-model="newFormInline.region"
|
||||||
|
class="!w-[220px]"
|
||||||
|
placeholder="请选择城市"
|
||||||
|
>
|
||||||
|
<el-option label="上海" value="上海" />
|
||||||
|
<el-option label="浙江" value="浙江" />
|
||||||
|
<el-option label="深圳" value="深圳" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
22
src/views/components/drawer/formPrimitive.vue
Normal file
22
src/views/components/drawer/formPrimitive.vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useVModel } from "@vueuse/core";
|
||||||
|
|
||||||
|
// 声明 props 类型
|
||||||
|
export interface FormProps {
|
||||||
|
data: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 声明 props 默认值
|
||||||
|
// 推荐阅读:https://cn.vuejs.org/guide/typescript/composition-api.html#typing-component-props
|
||||||
|
const props = withDefaults(defineProps<FormProps>(), {
|
||||||
|
data: () => ""
|
||||||
|
});
|
||||||
|
|
||||||
|
// 使用 vueuse 的双向绑定工具
|
||||||
|
const emit = defineEmits(["update:data"]);
|
||||||
|
const data = useVModel(props, "data", emit);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-input v-model="data" class="!w-[220px]" placeholder="请输入内容" />
|
||||||
|
</template>
|
502
src/views/components/drawer/index.vue
Normal file
502
src/views/components/drawer/index.vue
Normal file
@ -0,0 +1,502 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import {
|
||||||
|
addDrawer,
|
||||||
|
closeDrawer,
|
||||||
|
closeAllDrawer,
|
||||||
|
updateDrawer
|
||||||
|
} from "@/components/ReDrawer/index";
|
||||||
|
import { cloneDeep, debounce } from "@pureadmin/utils";
|
||||||
|
import { message } from "@/utils/message";
|
||||||
|
import { createVNode, h, ref } from "vue";
|
||||||
|
import formPrimitive from "./formPrimitive.vue";
|
||||||
|
import forms, { type FormProps } from "./form.vue";
|
||||||
|
|
||||||
|
function onBaseClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "基础用法",
|
||||||
|
contentRenderer: () => <p>抽屉内容-基础用法</p> // jsx 语法 (注意在.vue文件启用jsx语法,需要在script开启lang="tsx")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onModalClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "无背景遮罩层",
|
||||||
|
modal: false,
|
||||||
|
contentRenderer: () => <p>抽屉内容-无背景遮罩层</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加 600ms 防抖
|
||||||
|
const onoOpenDelayClick = debounce(
|
||||||
|
() =>
|
||||||
|
addDrawer({
|
||||||
|
title: "延时2秒打开抽屉",
|
||||||
|
openDelay: 2000 - 600,
|
||||||
|
contentRenderer: () => <p>抽屉内容-延时2秒打开抽屉</p>
|
||||||
|
}),
|
||||||
|
600
|
||||||
|
);
|
||||||
|
|
||||||
|
function onCloseDelayClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "延时2秒关闭抽屉",
|
||||||
|
closeDelay: 2000,
|
||||||
|
contentRenderer: () => <p>抽屉内容-延时2秒关闭抽屉</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onShowCloseClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "不显示右上角关闭按钮图标",
|
||||||
|
showClose: false,
|
||||||
|
contentRenderer: () => <p>抽屉内容-不显示右上角关闭按钮图标</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBeforeCloseClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "禁止通过键盘ESC关闭",
|
||||||
|
closeOnPressEscape: false,
|
||||||
|
contentRenderer: () => <p>抽屉内容-禁止通过键盘ESC关闭</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCloseOnClickModalClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "禁止通过点击modal关闭",
|
||||||
|
closeOnClickModal: false,
|
||||||
|
contentRenderer: () => <p>抽屉内容-禁止通过点击modal关闭</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onHideFooterClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "隐藏底部取消、确定按钮",
|
||||||
|
hideFooter: true,
|
||||||
|
contentRenderer: () => <p>抽屉内容-隐藏底部取消、确定按钮</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onHeaderRendererClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "自定义头部",
|
||||||
|
showClose: false,
|
||||||
|
headerRenderer: ({ close, titleId, titleClass }) => (
|
||||||
|
// jsx 语法
|
||||||
|
<div class="flex flex-row justify-between">
|
||||||
|
<h4 id={titleId} class={titleClass}>
|
||||||
|
自定义头部
|
||||||
|
</h4>
|
||||||
|
<el-button type="danger" onClick={close}>
|
||||||
|
关闭
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
contentRenderer: () => <p>抽屉内容-自定义头部</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFooterRendererClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "自定义底部",
|
||||||
|
footerRenderer: ({ options, index }) => (
|
||||||
|
<el-button onClick={() => closeDrawer(options, index)}>
|
||||||
|
{options.title}-{index}
|
||||||
|
</el-button>
|
||||||
|
),
|
||||||
|
contentRenderer: () => <p>抽屉内容-自定义底部</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFooterButtonsClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "自定义底部按钮",
|
||||||
|
footerButtons: [
|
||||||
|
{
|
||||||
|
label: "按钮1",
|
||||||
|
size: "small",
|
||||||
|
type: "success",
|
||||||
|
btnClick: ({ drawer: { options, index }, button }) => {
|
||||||
|
console.log(options, index, button);
|
||||||
|
closeDrawer(options, index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "按钮2",
|
||||||
|
text: true,
|
||||||
|
bg: true,
|
||||||
|
btnClick: ({ drawer: { options, index }, button }) => {
|
||||||
|
console.log(options, index, button);
|
||||||
|
closeDrawer(options, index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "按钮3",
|
||||||
|
size: "large",
|
||||||
|
type: "warning",
|
||||||
|
btnClick: ({ drawer: { options, index }, button }) => {
|
||||||
|
console.log(options, index, button);
|
||||||
|
closeDrawer(options, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
contentRenderer: () => <p>抽屉内容-自定义底部按钮</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOpenClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "打开后的回调",
|
||||||
|
open: ({ options, index }) => message({ options, index } as any),
|
||||||
|
contentRenderer: () => <p>抽屉内容-打开后的回调</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCloseCallBackClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "关闭后的回调",
|
||||||
|
closeCallBack: ({ options, index, args }) => {
|
||||||
|
console.log(options, index, args);
|
||||||
|
let text = "";
|
||||||
|
if (args?.command === "cancel") {
|
||||||
|
text = "您点击了取消按钮";
|
||||||
|
} else if (args?.command === "sure") {
|
||||||
|
text = "您点击了确定按钮";
|
||||||
|
} else {
|
||||||
|
text = "您点击了右上角关闭按钮或空白页或按下了esc键";
|
||||||
|
}
|
||||||
|
message(text);
|
||||||
|
},
|
||||||
|
contentRenderer: () => <p>抽屉内容-关闭后的回调</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这里为了演示方便,使用了嵌套写法,实际情况下最好把 addDrawer 函数抽出来 套娃不可取
|
||||||
|
function onNestingClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "嵌套的抽屉",
|
||||||
|
size: "50%",
|
||||||
|
contentRenderer: ({ index }) => (
|
||||||
|
<el-button
|
||||||
|
onClick={() =>
|
||||||
|
addDrawer({
|
||||||
|
title: `第${index + 1}个子抽屉`,
|
||||||
|
size: "40%",
|
||||||
|
contentRenderer: ({ index }) => (
|
||||||
|
<el-button
|
||||||
|
onClick={() =>
|
||||||
|
addDrawer({
|
||||||
|
title: `第${index + 1}个子抽屉`,
|
||||||
|
size: "30%",
|
||||||
|
contentRenderer: () => (
|
||||||
|
<>
|
||||||
|
<el-button round onClick={() => closeAllDrawer()}>
|
||||||
|
哎呦,你干嘛,赶快关闭所有抽屉
|
||||||
|
</el-button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
点击打开第{index + 1}个子抽屉
|
||||||
|
</el-button>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
点击打开第{index + 1}个子抽屉
|
||||||
|
</el-button>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 满足在 contentRenderer 内容区更改抽屉自身属性值的场景
|
||||||
|
function onUpdateClick() {
|
||||||
|
const curPage = ref(1);
|
||||||
|
addDrawer({
|
||||||
|
title: `第${curPage.value}页`,
|
||||||
|
contentRenderer: () => (
|
||||||
|
<>
|
||||||
|
<el-button
|
||||||
|
disabled={curPage.value <= 1}
|
||||||
|
onClick={() => {
|
||||||
|
curPage.value -= 1;
|
||||||
|
updateDrawer(`第${curPage.value}页`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
上一页
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
onClick={() => {
|
||||||
|
curPage.value += 1;
|
||||||
|
updateDrawer(`第${curPage.value}页`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
下一页
|
||||||
|
</el-button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Popconfirm 确认框
|
||||||
|
function onPopConfirmClick() {
|
||||||
|
addDrawer({
|
||||||
|
size: "30%",
|
||||||
|
title: "Popconfirm确认框示例",
|
||||||
|
popConfirm: { title: "是否确认修改当前数据" },
|
||||||
|
contentRenderer: () => <p>点击右下方确定按钮看看效果吧</p>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结合Form表单(第一种方式,抽屉关闭立刻恢复初始值)通过 props 属性接收子组件的 prop 并赋值
|
||||||
|
function onFormOneClick() {
|
||||||
|
addDrawer({
|
||||||
|
size: "30%",
|
||||||
|
title: "结合Form表单(第一种方式)",
|
||||||
|
contentRenderer: () => forms,
|
||||||
|
props: {
|
||||||
|
// 赋默认值
|
||||||
|
formInline: {
|
||||||
|
user: "菜虚鲲",
|
||||||
|
region: "浙江"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
closeCallBack: ({ options, args }) => {
|
||||||
|
// options.props 是响应式的
|
||||||
|
const { formInline } = options.props as FormProps;
|
||||||
|
const text = `姓名:${formInline.user} 城市:${formInline.region}`;
|
||||||
|
if (args?.command === "cancel") {
|
||||||
|
// 您点击了取消按钮
|
||||||
|
message(`您点击了取消按钮,当前表单数据为 ${text}`);
|
||||||
|
} else if (args?.command === "sure") {
|
||||||
|
message(`您点击了确定按钮,当前表单数据为 ${text}`);
|
||||||
|
} else {
|
||||||
|
message(
|
||||||
|
`您点击了右上角关闭按钮或空白页或按下了esc键,当前表单数据为 ${text}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结合Form表单(第二种方式)h 渲染函数 https://cn.vuejs.org/api/render-function.html#h
|
||||||
|
const formInline = ref({
|
||||||
|
user: "菜虚鲲",
|
||||||
|
region: "浙江"
|
||||||
|
});
|
||||||
|
const resetFormInline = cloneDeep(formInline.value);
|
||||||
|
function onFormTwoClick() {
|
||||||
|
addDrawer({
|
||||||
|
size: "30%",
|
||||||
|
title: "结合Form表单(第二种方式)",
|
||||||
|
contentRenderer: () =>
|
||||||
|
h(forms, {
|
||||||
|
formInline: formInline.value
|
||||||
|
}),
|
||||||
|
closeCallBack: () => {
|
||||||
|
message(
|
||||||
|
`当前表单数据为 姓名:${formInline.value.user} 城市:${formInline.value.region}`
|
||||||
|
);
|
||||||
|
// 重置表单数据
|
||||||
|
formInline.value = cloneDeep(resetFormInline);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结合Form表单(第三种方式)createVNode 渲染函数 https://cn.vuejs.org/guide/extras/render-function.html#creating-vnodes
|
||||||
|
const formThreeInline = ref({
|
||||||
|
user: "菜虚鲲",
|
||||||
|
region: "浙江"
|
||||||
|
});
|
||||||
|
const resetFormThreeInline = cloneDeep(formThreeInline.value);
|
||||||
|
function onFormThreeClick() {
|
||||||
|
addDrawer({
|
||||||
|
size: "30%",
|
||||||
|
title: "结合Form表单(第三种方式)",
|
||||||
|
contentRenderer: () =>
|
||||||
|
createVNode(forms, {
|
||||||
|
formInline: formThreeInline.value
|
||||||
|
}),
|
||||||
|
closeCallBack: () => {
|
||||||
|
message(
|
||||||
|
`当前表单数据为 姓名:${formThreeInline.value.user} 城市:${formThreeInline.value.region}`
|
||||||
|
);
|
||||||
|
// 重置表单数据
|
||||||
|
formThreeInline.value = cloneDeep(resetFormThreeInline);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结合Form表单(第四种方式)使用jsx语法
|
||||||
|
// 需要注意的是如果 forms 没注册,这里 forms 注册了是因为上面 contentRenderer: () => forms、h(forms) 、createVNode(createVNode) 间接给注册了
|
||||||
|
// 如果只使用了jsx语法,如下 `contentRenderer: () => <forms formInline={formFourInline.value} />` 是不会给 forms 组件进行注册的,需要在 `script` 中任意位置(最好是末尾)写上 forms 即可
|
||||||
|
// 同理如果在 tsx 文件中,这么使用 `contentRenderer: () => <forms formInline={formFourInline.value} />`,也是不会给 forms 组件进行注册,需要在 return 中写上 forms
|
||||||
|
const formFourInline = ref({
|
||||||
|
user: "菜虚鲲",
|
||||||
|
region: "浙江"
|
||||||
|
});
|
||||||
|
const resetFormFourInline = cloneDeep(formFourInline.value);
|
||||||
|
function onFormFourClick() {
|
||||||
|
addDrawer({
|
||||||
|
size: "30%",
|
||||||
|
title: "结合Form表单(第四种方式)",
|
||||||
|
contentRenderer: () => <forms formInline={formFourInline.value} />,
|
||||||
|
closeCallBack: () => {
|
||||||
|
message(
|
||||||
|
`当前表单数据为 姓名:${formFourInline.value.user} 城市:${formFourInline.value.region}`
|
||||||
|
);
|
||||||
|
// 重置表单数据
|
||||||
|
formFourInline.value = cloneDeep(resetFormFourInline);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子组件 prop 为 primitive 类型的 demo
|
||||||
|
const formPrimitiveParam = ref("Hello World");
|
||||||
|
const resetFormPrimitiveParam = ref(formPrimitiveParam.value);
|
||||||
|
function onFormPrimitiveFormClick() {
|
||||||
|
addDrawer({
|
||||||
|
size: "30%",
|
||||||
|
title: "子组件 prop 为 primitive 类型 demo",
|
||||||
|
contentRenderer: () =>
|
||||||
|
h(formPrimitive, {
|
||||||
|
data: formPrimitiveParam.value,
|
||||||
|
"onUpdate:data": val => (formPrimitiveParam.value = val)
|
||||||
|
}),
|
||||||
|
closeCallBack: () => {
|
||||||
|
message(`当前表单内容:${formPrimitiveParam.value}`);
|
||||||
|
// 重置表单数据
|
||||||
|
formPrimitiveParam.value = resetFormPrimitiveParam.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBeforeCancelClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "点击底部取消按钮的回调",
|
||||||
|
contentRenderer: () => (
|
||||||
|
<p>抽屉内容-点击底部取消按钮的回调(会暂停抽屉的关闭)</p>
|
||||||
|
),
|
||||||
|
beforeCancel: (done, { options, index }) => {
|
||||||
|
console.log(
|
||||||
|
"%coptions, index===>>>: ",
|
||||||
|
"color: MidnightBlue; background: Aquamarine; font-size: 20px;",
|
||||||
|
options,
|
||||||
|
index
|
||||||
|
);
|
||||||
|
// done(); // 需要关闭把注释解开即可
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBeforeSureClick() {
|
||||||
|
addDrawer({
|
||||||
|
title: "点击底部确定按钮的回调",
|
||||||
|
contentRenderer: () => (
|
||||||
|
<p>
|
||||||
|
抽屉内容-点击底部确定按钮的回调(会暂停抽屉的关闭,经常用于新增、修改抽屉内容后调用接口)
|
||||||
|
</p>
|
||||||
|
),
|
||||||
|
beforeSure: (done, { options, index }) => {
|
||||||
|
console.log(
|
||||||
|
"%coptions, index===>>>: ",
|
||||||
|
"color: MidnightBlue; background: Aquamarine; font-size: 20px;",
|
||||||
|
options,
|
||||||
|
index
|
||||||
|
);
|
||||||
|
// done(); // 需要关闭把注释解开即可
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSureBtnLoading() {
|
||||||
|
addDrawer({
|
||||||
|
sureBtnLoading: true,
|
||||||
|
title: "点击底部确定按钮可开启按钮动画",
|
||||||
|
contentRenderer: () => <p>抽屉内容-点击底部确定按钮可开启按钮动画</p>,
|
||||||
|
beforeSure: (done, { closeLoading }) => {
|
||||||
|
// closeLoading(); // 关闭确定按钮动画,不关闭抽屉
|
||||||
|
// done() // 关闭确定按钮动画并关闭抽屉
|
||||||
|
setTimeout(() => done(), 800);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="font-medium">
|
||||||
|
二次封装 Element Plus 的
|
||||||
|
<el-link
|
||||||
|
href="https://element-plus.org/zh-CN/component/drawer.html"
|
||||||
|
target="_blank"
|
||||||
|
style="margin: 0 4px 5px; font-size: 16px"
|
||||||
|
>
|
||||||
|
Drawer
|
||||||
|
</el-link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<el-link
|
||||||
|
href="https://github.com/pure-admin/vue-pure-admin/tree/main/src/views/components/drawer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
代码位置 src/views/components/drawer
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
<el-space wrap>
|
||||||
|
<el-button @click="onBaseClick">基础用法</el-button>
|
||||||
|
<el-button @click="onModalClick"> 无背景遮罩层 </el-button>
|
||||||
|
<el-button @click="onoOpenDelayClick"> 延时2秒打开抽屉 </el-button>
|
||||||
|
<el-button @click="onCloseDelayClick"> 延时2秒关闭抽屉 </el-button>
|
||||||
|
<el-button @click="onShowCloseClick">
|
||||||
|
不显示右上角关闭按钮图标
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="onBeforeCloseClick"> 禁止通过键盘ESC关闭 </el-button>
|
||||||
|
<el-button @click="onCloseOnClickModalClick">
|
||||||
|
禁止通过点击modal关闭
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="onHideFooterClick"> 隐藏底部取消、确定按钮 </el-button>
|
||||||
|
<el-button @click="onHeaderRendererClick"> 自定义头部 </el-button>
|
||||||
|
<el-button @click="onFooterRendererClick"> 自定义底部 </el-button>
|
||||||
|
<el-button @click="onFooterButtonsClick"> 自定义底部按钮 </el-button>
|
||||||
|
<el-button @click="onOpenClick"> 打开后的回调 </el-button>
|
||||||
|
<el-button @click="onCloseCallBackClick"> 关闭后的回调 </el-button>
|
||||||
|
<el-button @click="onNestingClick"> 嵌套的抽屉 </el-button>
|
||||||
|
<el-button @click="onUpdateClick"> 更改抽屉自身属性值 </el-button>
|
||||||
|
<el-button @click="onPopConfirmClick">Popconfirm确认框</el-button>
|
||||||
|
</el-space>
|
||||||
|
<el-divider />
|
||||||
|
<el-space wrap>
|
||||||
|
<el-button @click="onFormOneClick">
|
||||||
|
结合Form表单(第一种方式)
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="onFormTwoClick">
|
||||||
|
结合Form表单(第二种方式)
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="onFormThreeClick">
|
||||||
|
结合Form表单(第三种方式)
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="onFormFourClick">
|
||||||
|
结合Form表单(第四种方式)
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="onFormPrimitiveFormClick">
|
||||||
|
子组件 prop 为 primitive 类型
|
||||||
|
</el-button>
|
||||||
|
</el-space>
|
||||||
|
<el-divider />
|
||||||
|
<el-space wrap>
|
||||||
|
<el-button @click="onBeforeCancelClick">
|
||||||
|
点击底部取消按钮的回调(会暂停抽屉的关闭)
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="onBeforeSureClick">
|
||||||
|
点击底部确定按钮的回调(会暂停抽屉的关闭,经常用于新增、修改抽屉内容后调用接口)
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="onSureBtnLoading">
|
||||||
|
点击底部确定按钮可开启按钮动画
|
||||||
|
</el-button>
|
||||||
|
</el-space>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
Loading…
Reference in New Issue
Block a user