feat: ✨ pageModal组件,添加二级弹窗与使用案例
This commit is contained in:
@@ -503,19 +503,24 @@ function handleDelete(id?: number | string) {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(function () {
|
||||
if (props.contentConfig.deleteAction) {
|
||||
props.contentConfig.deleteAction(ids).then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
removeIds.value = [];
|
||||
//清空选中项
|
||||
tableRef.value?.clearSelection();
|
||||
handleRefresh(true);
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("未配置deleteAction");
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
if (props.contentConfig.deleteAction) {
|
||||
props.contentConfig
|
||||
.deleteAction(ids)
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
removeIds.value = [];
|
||||
//清空选中项
|
||||
tableRef.value?.clearSelection();
|
||||
handleRefresh(true);
|
||||
})
|
||||
.catch(() => {});
|
||||
} else {
|
||||
ElMessage.error("未配置deleteAction");
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
// 导出表单
|
||||
@@ -777,7 +782,14 @@ function handleToolbar(name: string) {
|
||||
|
||||
// 操作列
|
||||
function handleOperate(data: IOperateData) {
|
||||
emit("operateClick", data);
|
||||
switch (data.name) {
|
||||
case "delete":
|
||||
props.contentConfig?.deleteAction && handleDelete(data.row[pk]);
|
||||
break;
|
||||
default:
|
||||
emit("operateClick", data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 属性修改
|
||||
|
||||
@@ -1,147 +1,151 @@
|
||||
<template>
|
||||
<!-- drawer -->
|
||||
<template v-if="modalConfig.component === 'drawer'">
|
||||
<el-drawer
|
||||
v-model="modalVisible"
|
||||
v-bind="{ destroyOnClose: true, ...modalConfig.drawer }"
|
||||
@close="handleClose"
|
||||
>
|
||||
<!-- 表单 -->
|
||||
<el-form ref="formRef" v-bind="modalConfig.form" :model="formData" :rules="formRules">
|
||||
<el-row :gutter="20">
|
||||
<template v-for="item in formItems" :key="item.prop">
|
||||
<el-col v-show="!item.hidden" v-bind="item.col">
|
||||
<el-form-item :label="item.label" :prop="item.prop">
|
||||
<!-- Label -->
|
||||
<template #label>
|
||||
<span>
|
||||
{{ item?.label || "" }}
|
||||
<el-tooltip v-if="item?.tips" v-bind="getTooltipProps(item.tips)">
|
||||
<QuestionFilled class="w-4 h-4 mx-1" />
|
||||
</el-tooltip>
|
||||
<span v-if="modalConfig.colon" class="ml-0.5">:</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<!-- components -->
|
||||
<template v-if="item.type === 'custom'">
|
||||
<slot
|
||||
:name="item.slotName ?? item.prop"
|
||||
:prop="item.prop"
|
||||
:form-data="formData"
|
||||
:attrs="item.attrs"
|
||||
style="width: 100%"
|
||||
></slot>
|
||||
</template>
|
||||
<component
|
||||
:is="componentMap.get(item.type)"
|
||||
v-else
|
||||
v-model.trim="formData[item.prop]"
|
||||
v-bind="{ style: { width: '100%' }, ...item.attrs }"
|
||||
>
|
||||
<template v-if="['select', 'radio', 'checkbox'].includes(item.type)">
|
||||
<component
|
||||
:is="childrenMap.get(item.type)"
|
||||
v-for="opt in item.options"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
></component>
|
||||
<div>
|
||||
<!-- drawer -->
|
||||
<template v-if="modalConfig.component === 'drawer'">
|
||||
<el-drawer
|
||||
v-model="modalVisible"
|
||||
v-bind="{ destroyOnClose: true, ...modalConfig.drawer }"
|
||||
@close="handleClose"
|
||||
>
|
||||
<!-- 表单 -->
|
||||
<el-form ref="formRef" v-bind="modalConfig.form" :model="formData" :rules="formRules">
|
||||
<el-row :gutter="20">
|
||||
<template v-for="item in formItems" :key="item.prop">
|
||||
<el-col v-show="!item.hidden" v-bind="item.col">
|
||||
<el-form-item :label="item.label" :prop="item.prop">
|
||||
<!-- Label -->
|
||||
<template #label>
|
||||
<span>
|
||||
{{ item?.label || "" }}
|
||||
<el-tooltip v-if="item?.tips" v-bind="getTooltipProps(item.tips)">
|
||||
<QuestionFilled class="w-4 h-4 mx-1" />
|
||||
</el-tooltip>
|
||||
<span v-if="modalConfig.colon" class="ml-0.5">:</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template v-if="item?.slotName && $slots[item.slotName]" #[item.slotName]>
|
||||
<slot :name="item.slotName" />
|
||||
<!-- components -->
|
||||
<template v-if="item.type === 'custom'">
|
||||
<slot
|
||||
:name="item.slotName ?? item.prop"
|
||||
:prop="item.prop"
|
||||
:form-data="formData"
|
||||
:attrs="item.attrs"
|
||||
></slot>
|
||||
</template>
|
||||
</component>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button v-if="!formDisable" type="primary" @click="handleSubmit">确 定</el-button>
|
||||
<el-button @click="handleClose">{{ !formDisable ? "取 消" : "关闭" }}</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
<!-- dialog -->
|
||||
<template v-else>
|
||||
<el-dialog
|
||||
v-model="modalVisible"
|
||||
v-bind="{ destroyOnClose: true, appendToBody: true, ...modalConfig.dialog }"
|
||||
@close="handleClose"
|
||||
>
|
||||
<!-- 表单 -->
|
||||
<el-form ref="formRef" v-bind="modalConfig.form" :model="formData" :rules="formRules">
|
||||
<el-row :gutter="20">
|
||||
<template v-for="item in formItems" :key="item.prop">
|
||||
<el-col v-show="!item.hidden" v-bind="item.col">
|
||||
<el-form-item :label="item.label" :prop="item.prop">
|
||||
<!-- Label -->
|
||||
<template #label>
|
||||
<span class="flex-y-center">
|
||||
{{ item?.label || "" }}
|
||||
<el-tooltip v-if="item?.tips">
|
||||
<QuestionFilled class="w-4 h-4 mx-1" />
|
||||
</el-tooltip>
|
||||
<span v-if="modalConfig.colon" class="ml-0.5">:</span>
|
||||
</span>
|
||||
</template>
|
||||
<component
|
||||
:is="componentMap.get(item.type)"
|
||||
v-else
|
||||
v-model.trim="formData[item.prop]"
|
||||
v-bind="{ style: { width: '100%' }, ...item.attrs }"
|
||||
>
|
||||
<template v-if="['select', 'radio', 'checkbox'].includes(item.type)">
|
||||
<component
|
||||
:is="childrenMap.get(item.type)"
|
||||
v-for="opt in item.options"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
></component>
|
||||
</template>
|
||||
|
||||
<!-- components -->
|
||||
<template v-if="item.type === 'custom'">
|
||||
<slot
|
||||
:name="item.slotName ?? item.prop"
|
||||
:prop="item.prop"
|
||||
:form-data="formData"
|
||||
:attrs="item.attrs"
|
||||
></slot>
|
||||
</template>
|
||||
<component
|
||||
:is="componentMap.get(item.type)"
|
||||
v-else
|
||||
v-model.trim="formData[item.prop]"
|
||||
v-bind="{ style: { width: '100%' }, ...item.attrs }"
|
||||
>
|
||||
<template v-if="['select', 'radio', 'checkbox'].includes(item.type)">
|
||||
<component
|
||||
:is="childrenMap.get(item.type)"
|
||||
v-for="opt in item.options"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
>
|
||||
<slot v-if="item.slotName" :name="item.slotName"></slot>
|
||||
</component>
|
||||
<template v-if="item?.slotName && $slots[item.slotName]" #[item.slotName]>
|
||||
<slot :name="item.slotName" />
|
||||
</template>
|
||||
</component>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button v-if="!formDisable" type="primary" @click="handleSubmit">确 定</el-button>
|
||||
<el-button @click="handleClose">{{ !formDisable ? "取 消" : "关闭" }}</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
<!-- dialog -->
|
||||
<template v-else>
|
||||
<el-dialog
|
||||
v-model="modalVisible"
|
||||
v-bind="{ destroyOnClose: true, ...modalConfig.dialog }"
|
||||
@close="handleClose"
|
||||
>
|
||||
<!-- 表单 -->
|
||||
<el-form ref="formRef" v-bind="modalConfig.form" :model="formData" :rules="formRules">
|
||||
<el-row :gutter="20">
|
||||
<template v-for="item in formItems" :key="item.prop">
|
||||
<el-col v-show="!item.hidden" v-bind="item.col">
|
||||
<el-form-item :label="item.label" :prop="item.prop">
|
||||
<!-- Label -->
|
||||
<template #label>
|
||||
<span>
|
||||
{{ item?.label || "" }}
|
||||
<el-tooltip v-if="item?.tips" v-bind="getTooltipProps(item.tips)">
|
||||
<QuestionFilled class="w-4 h-4 mx-1" />
|
||||
</el-tooltip>
|
||||
<span v-if="modalConfig.colon" class="ml-0.5">:</span>
|
||||
</span>
|
||||
</template>
|
||||
</component>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button v-if="!formDisable" type="primary" @click="handleSubmit">确 定</el-button>
|
||||
<el-button @click="handleClose">{{ !formDisable ? "取 消" : "关闭" }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<!-- components -->
|
||||
<template v-if="item.type === 'custom'">
|
||||
<slot
|
||||
:name="item.slotName ?? item.prop"
|
||||
:prop="item.prop"
|
||||
:form-data="formData"
|
||||
:attrs="item.attrs"
|
||||
></slot>
|
||||
</template>
|
||||
<component
|
||||
:is="componentMap.get(item.type)"
|
||||
v-else
|
||||
v-model.trim="formData[item.prop]"
|
||||
v-bind="{ style: { width: '100%' }, ...item.attrs }"
|
||||
>
|
||||
<template v-if="['select', 'radio', 'checkbox'].includes(item.type)">
|
||||
<component
|
||||
:is="childrenMap.get(item.type)"
|
||||
v-for="opt in item.options"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
></component>
|
||||
</template>
|
||||
|
||||
<template v-if="item?.slotName && $slots[item.slotName]" #[item.slotName]>
|
||||
<slot :name="item.slotName" />
|
||||
</template>
|
||||
</component>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button v-if="!formDisable" type="primary" @click="handleSubmit">确 定</el-button>
|
||||
<el-button @click="handleClose">{{ !formDisable ? "取 消" : "关闭" }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useThrottleFn } from "@vueuse/core";
|
||||
import type { FormInstance, FormRules } from "element-plus";
|
||||
import type { IModalConfig, IObject } from "./types";
|
||||
import type { IComponentType, IModalConfig, IObject } from "./types";
|
||||
import InputTag from "@/components/InputTag/index.vue";
|
||||
import IconSelect from "@/components/IconSelect/index.vue";
|
||||
|
||||
defineSlots<{ [key: string]: (_args: any) => any }>();
|
||||
// 定义接收的属性
|
||||
const props = defineProps<{ modalConfig: IModalConfig }>();
|
||||
// 自定义事件
|
||||
const emit = defineEmits<{ submitClick: [] }>();
|
||||
const emit = defineEmits<{ submitClick: []; customSubmit: [queryParams: IObject] }>();
|
||||
// 组件映射表
|
||||
/* eslint-disable */
|
||||
const componentMap = new Map([
|
||||
const componentMap = new Map<IComponentType, any>([
|
||||
// @ts-ignore
|
||||
["input", markRaw(ElInput)], // @ts-ignore
|
||||
["select", markRaw(ElSelect)], // @ts-ignore
|
||||
@@ -160,7 +164,7 @@ const componentMap = new Map([
|
||||
["icon-select", markRaw(IconSelect)], // @ts-ignore"
|
||||
["custom", ""],
|
||||
]);
|
||||
const childrenMap = new Map([
|
||||
const childrenMap = new Map<IComponentType, any>([
|
||||
// @ts-ignore
|
||||
["select", markRaw(ElOption)], // @ts-ignore
|
||||
["radio", markRaw(ElRadio)], // @ts-ignore"
|
||||
@@ -168,13 +172,13 @@ const childrenMap = new Map([
|
||||
]);
|
||||
/* eslint-enable */
|
||||
|
||||
const pk = props.modalConfig.pk ?? "id";
|
||||
const modalVisible = ref(false);
|
||||
const formRef = ref<FormInstance>();
|
||||
const formItems = reactive(props.modalConfig.formItems ?? []);
|
||||
const formData = reactive<IObject>({});
|
||||
const formRules: FormRules = {};
|
||||
const formDisable = ref(false);
|
||||
const pk = props.modalConfig.pk ?? "id"; // 主键名,用于表单数据处理
|
||||
const modalVisible = ref(false); // 弹窗显示状态
|
||||
const formRef = ref<FormInstance>(); // 表单实例
|
||||
const formItems = reactive(props.modalConfig.formItems ?? []); // 表单配置项
|
||||
const formData = reactive<IObject>({}); // 表单数据
|
||||
const formRules: FormRules = {}; // 表单验证规则
|
||||
const formDisable = ref(false); // 表单禁用状态
|
||||
|
||||
// 获取tooltip提示框属性
|
||||
const getTooltipProps = (tips: string | IObject) => {
|
||||
@@ -204,17 +208,16 @@ const handleSubmit = useThrottleFn(() => {
|
||||
props.modalConfig.beforeSubmit(formData);
|
||||
}
|
||||
if (!props.modalConfig?.formAction) {
|
||||
ElMessage.error("未配置exportAction");
|
||||
emit("customSubmit", formData);
|
||||
handleClose();
|
||||
return;
|
||||
}
|
||||
props.modalConfig.formAction(formData).then(() => {
|
||||
let msg = "操作成功";
|
||||
if (props.modalConfig.component === "drawer") {
|
||||
msg = `${props.modalConfig.drawer?.title}成功`;
|
||||
ElMessage.success(`${props.modalConfig.drawer?.title}成功`);
|
||||
} else {
|
||||
msg = `${props.modalConfig.dialog?.title}成功`;
|
||||
ElMessage.success(`${props.modalConfig.dialog?.title}成功`);
|
||||
}
|
||||
ElMessage.success(msg);
|
||||
emit("submitClick");
|
||||
handleClose();
|
||||
});
|
||||
|
||||
@@ -13,11 +13,11 @@ export type PageFormInstance = InstanceType<typeof PageForm>;
|
||||
|
||||
export type IObject = Record<string, any>;
|
||||
|
||||
type DataComponent = "date-picker" | "time-picker" | "time-select" | "custom-tag" | "input-tag";
|
||||
type DateComponent = "date-picker" | "time-picker" | "time-select" | "custom-tag" | "input-tag";
|
||||
type InputComponent = "input" | "select" | "input-number" | "cascader" | "tree-select";
|
||||
type OtherComponent = "text" | "radio" | "checkbox" | "switch" | "icon-select" | "custom";
|
||||
export type ISearchComponent = DataComponent | InputComponent;
|
||||
export type IComponentType = DataComponent | InputComponent | OtherComponent;
|
||||
export type ISearchComponent = DateComponent | InputComponent;
|
||||
export type IComponentType = DateComponent | InputComponent | OtherComponent;
|
||||
|
||||
type ToolbarLeft = "add" | "delete" | "import" | "export";
|
||||
type ToolbarRight = "refresh" | "filter" | "imports" | "exports" | "search";
|
||||
@@ -44,26 +44,7 @@ export interface ISearchConfig {
|
||||
// 标签冒号(默认:false)
|
||||
colon?: boolean;
|
||||
// 表单项(默认:[])
|
||||
formItems?: Array<{
|
||||
// 组件类型(如input,select等)
|
||||
type?: ISearchComponent;
|
||||
// 标签文本
|
||||
label?: string;
|
||||
// 标签提示
|
||||
tips?: string | IObject;
|
||||
// 键名
|
||||
prop: string;
|
||||
// 组件属性(input-tag组件支持join,btnText,size属性)
|
||||
attrs?: IObject;
|
||||
// 初始值
|
||||
initialValue?: any;
|
||||
// 可选项(适用于select组件)
|
||||
options?: Array<{ label: string; value: any }>;
|
||||
// 组件事件
|
||||
events?: Record<string, (...args: any) => void>;
|
||||
// 初始化数据函数扩展
|
||||
initFn?: (formItem: IObject) => void;
|
||||
}>;
|
||||
formItems?: IFormItems;
|
||||
// 是否开启展开和收缩(默认:true)
|
||||
isExpandable?: boolean;
|
||||
// 默认展示的表单项数量(默认:3)
|
||||
@@ -199,36 +180,36 @@ export interface IModalConfig<T = any> {
|
||||
// 提交之前处理
|
||||
beforeSubmit?: (data: T) => void;
|
||||
// 提交的网络请求函数(需返回promise)
|
||||
formAction: (data: T) => Promise<any>;
|
||||
formAction?: (data: T) => Promise<any>;
|
||||
}
|
||||
|
||||
export type IForm = Partial<Omit<FormProps, "model" | "rules">>;
|
||||
|
||||
// 表单项
|
||||
export type IFormItems = Array<{
|
||||
// 组件类型(如input,select,radio,custom等,默认input)
|
||||
// 组件类型(如input,select,radio,custom等)
|
||||
type: IComponentType;
|
||||
// 标签提示
|
||||
tips?: string | IObject;
|
||||
// 标签文本
|
||||
label: string;
|
||||
// 键名
|
||||
prop: string;
|
||||
// 组件属性
|
||||
attrs?: IObject;
|
||||
// 组件可选项(适用于select,radio,checkbox组件)
|
||||
// 组件可选项(只适用于select,radio,checkbox组件)
|
||||
options?: Array<{
|
||||
label: string;
|
||||
value: any;
|
||||
disabled?: boolean;
|
||||
[key: string]: any;
|
||||
}>;
|
||||
// 插槽名(适用于组件类型为custom)
|
||||
slotName?: string;
|
||||
// 标签文本
|
||||
label: string;
|
||||
// 标签提示
|
||||
tips?: string | IObject;
|
||||
// 键名
|
||||
prop: string;
|
||||
// 验证规则
|
||||
rules?: FormItemRule[];
|
||||
// 初始值
|
||||
initialValue?: any;
|
||||
// 插槽名(适用于自定义组件,设置类型为custom)
|
||||
slotName?: string;
|
||||
// 是否隐藏
|
||||
hidden?: boolean;
|
||||
// layout组件Col属性
|
||||
|
||||
@@ -18,23 +18,50 @@ function usePage() {
|
||||
contentRef.value?.fetchPageData({ ...queryParams, ...filterParams }, true);
|
||||
}
|
||||
// 新增
|
||||
function handleAddClick() {
|
||||
//显示添加表单
|
||||
addModalRef.value?.setModalVisible();
|
||||
function handleAddClick(RefImpl?: Ref<PageContentInstance>) {
|
||||
if (RefImpl) {
|
||||
RefImpl?.value.setModalVisible();
|
||||
RefImpl?.value.handleDisabled(false);
|
||||
} else {
|
||||
addModalRef.value?.setModalVisible();
|
||||
addModalRef.value?.handleDisabled(false);
|
||||
}
|
||||
}
|
||||
// 编辑
|
||||
async function handleEditClick(row: IObject, callback?: (result?: IObject) => IObject) {
|
||||
editModalRef.value?.setModalVisible();
|
||||
editModalRef.value?.handleDisabled(false);
|
||||
let from = await (callback?.(row) ?? Promise.resolve(row));
|
||||
editModalRef.value?.setFormData(from ? from : row);
|
||||
async function handleEditClick(
|
||||
row: IObject,
|
||||
callback?: (result?: IObject) => IObject,
|
||||
RefImpl?: Ref<PageContentInstance>
|
||||
) {
|
||||
if (RefImpl) {
|
||||
RefImpl.value?.setModalVisible();
|
||||
RefImpl.value?.handleDisabled(false);
|
||||
let from = await (callback?.(row) ?? Promise.resolve(row));
|
||||
RefImpl.value?.setFormData(from ? from : row);
|
||||
} else {
|
||||
editModalRef.value?.setModalVisible();
|
||||
editModalRef.value?.handleDisabled(false);
|
||||
let from = await (callback?.(row) ?? Promise.resolve(row));
|
||||
editModalRef.value?.setFormData(from ? from : row);
|
||||
}
|
||||
}
|
||||
// 编辑
|
||||
async function handleViewClick(row: IObject, callback?: (result?: IObject) => IObject) {
|
||||
editModalRef.value?.setModalVisible();
|
||||
editModalRef.value?.handleDisabled(true);
|
||||
let from = await (callback?.(row) ?? Promise.resolve(row));
|
||||
editModalRef.value?.setFormData(from ? from : row);
|
||||
// 查看
|
||||
async function handleViewClick(
|
||||
row: IObject,
|
||||
callback?: (result?: IObject) => IObject,
|
||||
RefImpl?: Ref<PageContentInstance>
|
||||
) {
|
||||
if (RefImpl) {
|
||||
RefImpl.value?.setModalVisible();
|
||||
RefImpl.value?.handleDisabled(true);
|
||||
let from = await (callback?.(row) ?? Promise.resolve(row));
|
||||
RefImpl.value?.setFormData(from ? from : row);
|
||||
} else {
|
||||
editModalRef.value?.setModalVisible();
|
||||
editModalRef.value?.handleDisabled(true);
|
||||
let from = await (callback?.(row) ?? Promise.resolve(row));
|
||||
editModalRef.value?.setFormData(from ? from : row);
|
||||
}
|
||||
}
|
||||
// 表单提交
|
||||
function handleSubmitClick() {
|
||||
|
||||
Reference in New Issue
Block a user