使用场景
后台管理系统中,会经常性的出现各种新增和编辑表单的业务,这时如果简单的封装必然会提高开发效率。
表单弹窗封装
表单弹窗的封装应方便业务使用,针对含有form表单的新增和编辑的业务,入参包括各种表单元素、校验规则rules、标题、表单初始值等。
PageModal.vue
<template>
<el-dialog v-model="dialogFormVisible" :title="title" width="500" @close="closeModal(ruleFormRef)">
<el-form :rules="props.modalConfig.rules" :model="formData" ref="ruleFormRef">
<template v-for="item in modalConfig.formItems" :key="item.prop">
<el-form-item :label="item.label" :prop="item.prop">
<template v-if="item.type === 'date-picker'">
<el-date-picker
type="daterange"
v-model="formData[item.prop]"
range-separator="-"
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
</template>
<template v-else-if="item.type === 'select'">
<el-select
:placeholder="item.placeholder"
v-model="formData[item.prop]"
style="width: 100%"
>
<template v-for="op in item.options" :key="op.value">
<el-option :label="op.label" :value="op.value"></el-option>
</template>
</el-select>
</template>
<template v-else-if="item.type === 'custom'">
<slot :name="item.slotName"></slot>
</template>
<template v-else>
<el-input :placeholder="item.placeholder" v-model="formData[item.prop]"></el-input>
</template>
</el-form-item>
</template>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeModal(ruleFormRef)">
取消
</el-button>
<el-button type="primary" @click="handleConfirmClick(ruleFormRef)">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { reactive, ref,nextTick } from 'vue'
import type { IModalProps } from './type'
import type { FormInstance } from 'element-plus'
const props = defineProps<IModalProps>();
const emits = defineEmits(['submit'])
const title = ref();
const dialogFormVisible = ref(false);
const ruleFormRef = ref<FormInstance>()
const initialForm: any = {}
// 初始化formData 因为modal一开始就是false的 不渲染初始值
for (const item of props.modalConfig.formItems) {
initialForm[item.prop] = item.initialValue ?? ''
}
const formData = reactive<any>(initialForm);
const setModalVisible = (isNew: boolean = true, itemData?: any) => {
dialogFormVisible.value = true;
if (!isNew && itemData) {//编辑
nextTick(() => {
for (const key in formData) {
formData[key] = itemData[key];
title.value = props.modalConfig.header?.editTitle;
}
});
} else {
title.value = props.modalConfig.header?.newTitle;
}
};
const handleConfirmClick = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
let infoData = { ...formData }
if (props.otherInfo) {
infoData = { ...formData, ...props.otherInfo }
}
dialogFormVisible.value = false;
emits('submit',title.value,infoData);
}
})
}
const closeModal = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
dialogFormVisible.value = false;
}
defineExpose({
setModalVisible,
closeModal
})
</script>
封装的组件使用
在页面中,先引用PageModal组件, modalClick()打开弹窗,closeModal()关闭弹窗
const { modalRef, modalClick, editClick } = usePageModal();
<template>
<div class='systemdepartment'>
<PageSearch
:config='searchConfig.formItems'
:form='form'
></PageSearch>
<PageContent
:columns='tableColumns'
:data='data'
:onChange='onChange'
:pagination='pagination'
>
<!-- <template v-slot:name><el-button type='primary'>22</el-button></template>-->
</PageContent>
<PageModal ref='modalRef' :modalConfig='modalConfigRef' @submit='submitForm'></PageModal>
</div>
</template>
<script setup lang='ts' name='systemdepartment'>
import { computed, h } from 'vue'
import PageModal from '@/components/PageMain/PageModal/PageModal.vue'
import PageSearch from '@/components/BaseSearch/index.vue'
import PageContent from '@/components/PageMain/PageContent/PageContent.vue'
import searchConfig from '@/views/SystemMain/SystemDepartment/config/search.config'
import useMainStore from '@/store/main/main'
import { usePageModal } from '@/hooks/usePageModal'
import { storeToRefs } from 'pinia'
import modalConfig from './config/modal.config'
import { useRouter } from 'vue-router'
//引入
import FormTableMixin from '@/mixins/form-table.d'
import { postListDataRequest } from '@/service/modules/main/main'
import {ElButton} from "element-plus";
const { modalRef, modalClick, closeModal, editClick } = usePageModal();
const router = useRouter();
const mainStore = useMainStore();
const { entriesDepartments } = storeToRefs(mainStore);
const submitForm = (titleType:string, formObject:object) => {}
const goDetail = (a:object, b:object) => {
//router.push('/login');
modalClick()
}
const tableColumns = [
{
label: '序号',
type: 'index',
width: 80
},
{
prop: 'name',
label: '部门名称'
},
{
label: '部门领导',
prop: 'leader'
},
{
label: '上级领导',
prop: 'parentId'
},
{
label: '创建时间',
prop: 'createAt'
},
{
label: '操作',
render: (row:object) => {
// 返回一个渲染函数
return h(
'div',
{},
[
h(ElButton, {
type: 'primary',
size: 'small',
onClick: (event) => goDetail(event, row)
}, '查看'),
h(ElButton, {
size: 'small'
}, '删除')
]
)
}
}
];
//使用
const service = async (params:object, _:any) => {
const { data } = await postListDataRequest('department', params)
return {
data: data.list,
total: data.totalCount
}
}
const { form, data, pagination, onChange, params } = FormTableMixin(service, {});
const modalConfigRef = computed(() => {
modalConfig.formItems.forEach((item) => {
if (item.prop === 'parentId') {
const option = entriesDepartments.value.map((item) => {
return { label: item.name, value: item.id }
})
item.options.push(...option)
}
})
return modalConfig
});
</script>
<style scoped>
.systemdepartment {
border-radius: 7px;
overflow: hidden;
}
</style>
modal.config.ts附件
import { reactive } from 'vue';
import type { IModalConfig } from '@/components/PageMain/PageModal/type'
const rules = reactive<any>({
name: [
{ required: true, message: '请输入部门名称', trigger: 'blur' },
{ min: 1, max: 12, message: '部门名称不能超过12位', trigger: 'blur' },
],
leader: [
{ required: true, message: '请输入领导名称', trigger: 'blur' },
{ min: 1, max: 48, message: '领导名称不能超过4位', trigger: 'blur' },
],
parentId: [
{ required: true, message: '请选择部门', trigger: 'change' }
]
});
const modalConfig: IModalConfig = {
pageName: 'department',
header: {
newTitle: '新建部门',
editTitle: '编辑部门'
},
formItems: [
{
type: 'input',
prop: 'name',
label: '部门名称',
placeholder: '请输入部门名称',
initialValue: ''
},
{
type: 'input',
prop: 'leader',
label: '部门领导',
placeholder: '请输入领导名称',
initialValue: ''
},
{
type: 'select',
prop: 'parentId',
label: '选择部门',
placeholder: '请选择部门',
options: [],
initialValue: ''
}
],
rules: rules
}
export default modalConfig
type.ts附件
export interface IModalConfig {
pageName: string
header?: {
newTitle?: string
editTitle?: string
},
formItems: any[],
rules: []
}
export interface IModalProps {
modalConfig: IModalConfig
otherInfo?: any
}
usePageModal.ts附件
import { ref } from 'vue'
import type PageModal from '@/components/PageMain/PageModal/PageModal.vue'
type EditCallBack = (data: any) => void
type NEWCallBack = (data?: any) => void
export const usePageModal = (newCallBack?: NEWCallBack, editCallBack?: EditCallBack) => {
const modalRef = ref<InstanceType<typeof PageModal>>()
const modalClick = () => {
(modalRef.value as any)?.setModalVisible()
if (newCallBack) {
newCallBack()
}
}
const closeModal = () => {
(modalRef.value as any)?.closeModal()
}
const editClick = (itemData: any) => {
(modalRef.value as any)?.setModalVisible(false, itemData)
console.log({ ...itemData })
if (editCallBack) {
editCallBack(itemData)
}
}
return {
modalRef,
modalClick,
editClick,
closeModal
}
}