Files
vue3-element-admin/src/components/PageModal/index.vue
2024-05-03 10:40:02 +08:00

234 lines
6.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<el-dialog
v-model="dialogVisible"
v-bind="modalConfig.dialog"
@close="closeDialog"
>
<!-- 表单 -->
<el-form
ref="formRef"
label-width="80px"
v-bind="modalConfig.form"
:model="formData"
:rules="formRules"
>
<template v-for="item in modalConfig.formItems" :key="item.prop">
<el-form-item :label="item.label" :prop="item.prop">
<!-- Input 输入框 -->
<template v-if="item.type === 'input'">
<template v-if="item.attrs?.type === 'number'">
<el-input
v-model.number="formData[item.prop]"
v-bind="item.attrs"
/>
</template>
<template v-else>
<el-input v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
</template>
<!-- Select 选择器 -->
<template v-else-if="item.type === 'select'">
<el-select v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-option v-bind="option" />
</template>
</el-select>
</template>
<!-- Radio 单选框 -->
<template v-else-if="item.type === 'radio'">
<el-radio-group v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-radio v-bind="option" />
</template>
</el-radio-group>
</template>
<!-- Checkbox 多选框 -->
<template v-else-if="item.type === 'checkbox'">
<el-checkbox-group
v-model="formData[item.prop]"
v-bind="item.attrs"
>
<template v-for="option in item.options" :key="option.value">
<el-checkbox v-bind="option" />
</template>
</el-checkbox-group>
</template>
<!-- Input Number 数字输入框 -->
<template v-else-if="item.type === 'input-number'">
<el-input-number
v-model="formData[item.prop]"
v-bind="item.attrs"
/>
</template>
<!-- TreeSelect 树形选择 -->
<template v-else-if="item.type === 'tree-select'">
<el-tree-select v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- DatePicker 日期选择器 -->
<template v-else-if="item.type === 'date-picker'">
<el-date-picker v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- 自定义 -->
<template v-else-if="item.type === 'custom'">
<slot
:name="item.slotName ?? item.prop"
:prop="item.prop"
:formData="formData"
v-bind="item.attrs"
></slot>
</template>
<!-- Input 输入框 -->
<template v-else>
<template v-if="item.attrs?.type === 'number'">
<el-input
v-model.number="formData[item.prop]"
v-bind="item.attrs"
/>
</template>
<template v-else>
<el-input v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
</template>
</el-form-item>
</template>
</el-form>
<!-- 弹窗底部操作按钮 -->
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleSubmit"> </el-button>
<el-button @click="closeDialog">取消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, watch } from "vue";
import { useThrottleFn } from "@vueuse/core";
import type {
DialogProps,
FormProps,
FormRules,
FormItemRule,
} from "element-plus";
// 对象类型
type IObject = Record<string, any>;
// 定义接收的属性
export interface IModalConfig<T = any> {
// 页面名称
pageName?: string;
// 主键名(主要用于编辑数据,默认为id)
pk?: string;
// dialog组件属性
dialog?: Partial<Omit<DialogProps, "modelValue">>;
// form组件属性
form?: Partial<Omit<FormProps, "model" | "rules">>;
// 提交的网络请求函数(需返回promise)
formAction: (data: T) => Promise<any>;
// 提交之前处理
beforeSubmit?: (data: T) => void;
// 表单项
formItems: Array<{
// 组件类型(如input,select,radio,custom等默认input)
type?:
| "input"
| "select"
| "radio"
| "checkbox"
| "tree-select"
| "date-picker"
| "input-number"
| "custom";
// 组件属性
attrs?: IObject;
// 组件可选项(适用于select,radio,checkbox组件)
options?: Array<{
label: string;
value: any;
disabled?: boolean;
[key: string]: any;
}>;
// 插槽名(适用于组件类型为custom)
slotName?: string;
// 标签文本
label: string;
// 键名
prop: string;
// 验证规则
rules?: FormItemRule[];
// 初始值
initialValue?: any;
// 监听函数
watch?: (newValue: any, oldValue: any, data: T) => void;
}>;
}
const props = defineProps<{
modalConfig: IModalConfig;
}>();
// 自定义事件
const emit = defineEmits<{
submitClick: [];
}>();
// 暴露的属性和方法
defineExpose({ setModalVisible });
const pk = props.modalConfig.pk ?? "id";
const dialogVisible = ref(false);
const formRef = ref<InstanceType<typeof ElForm>>();
const formData = reactive<IObject>({});
const formRules: FormRules = {};
// 初始化
for (const item of props.modalConfig.formItems) {
formData[item.prop] = item.initialValue ?? "";
formRules[item.prop] = item.rules ?? [];
if (item.watch !== undefined) {
watch(
() => formData[item.prop],
(newValue, oldValue) => {
item.watch?.(newValue, oldValue, formData);
}
);
}
}
// 显示dialog
function setModalVisible(initData: IObject = {}) {
dialogVisible.value = true;
for (const key in formData) {
if (Object.hasOwn(formData, key) && key in initData) {
formData[key] = initData[key];
}
}
if (Object.hasOwn(initData, pk)) {
formData[pk] = initData[pk];
}
}
// 表单提交
const handleSubmit = useThrottleFn(() => {
formRef.value?.validate((valid: boolean) => {
if (valid) {
if (typeof props.modalConfig.beforeSubmit === "function") {
props.modalConfig.beforeSubmit(formData);
}
props.modalConfig.formAction(formData).then(() => {
ElMessage.success(
props.modalConfig.dialog?.title
? `${props.modalConfig.dialog.title}成功`
: "操作成功"
);
emit("submitClick");
closeDialog();
});
}
});
}, 3000);
// 关闭弹窗
function closeDialog() {
dialogVisible.value = false;
formRef.value?.resetFields();
formRef.value?.clearValidate();
}
</script>
<style lang="scss" scoped></style>