Files
shrimp-admin/src/components/ShrimpEditDrawer.vue
T

127 lines
3.9 KiB
Vue

<template>
<el-drawer class="shrimp-drawer" :model-value="visible" @update:model-value="$emit('update:visible', $event)" :title="title" size="400px">
<el-form ref="formRef" :model="formData" :rules="formRules" @submit.prevent>
<template v-for="field in fields" :key="field.key">
<el-form-item :label="vueAdmin.t(field.label)" :prop="field.key" :required="field.required">
<slot :name="`form-${field.key}`" :field="field" :data="formData" :isEdit="isEdit">
<component :is="vueAdmin.getFieldComponent(moduleKey, field.type, field.key)" :field="field"
:model-value="formData[field.key]"
@update:model-value="onFieldChange(field.key, $event)"
mode="edit"
:disabled="isEdit && (field.editable === 'readonly' || field.editable === 'addOnly')" />
</slot>
</el-form-item>
</template>
</el-form>
<template #footer>
<el-button @click="$emit('update:visible', false)">{{ $t('base.cancel') }}</el-button>
<el-button type="primary" :disabled="!canSave" @click="$emit('save')">{{ $t('base.confirm') }}</el-button>
</template>
</el-drawer>
</template>
<script lang="ts" setup>
import { provide, computed, ref } from 'vue'
import { IFieldConfig, IFieldRule } from '../IShrimpType'
import { vueAdmin } from '../VueAdmin'
import type { FormInstance, FormItemRule } from 'element-plus'
interface Props {
visible: boolean
title: string
moduleKey: string
fields: IFieldConfig[]
formData: Record<string, any>
isEdit: boolean
canSave?: boolean
}
const props = defineProps<Props>()
provide('shrimpModuleKey', props.moduleKey)
const emit = defineEmits<{
'update:visible': [value: boolean]
'update:formData': [value: Record<string, any>]
save: []
}>()
const formRef = ref<FormInstance>()
function buildRules(field: IFieldConfig): FormItemRule[] {
const rules: FormItemRule[] = []
if (field.required) {
rules.push({
required: true,
message: vueAdmin.t({ t: 'shrimp.fieldRequired', params: { field: field.label } }),
trigger: 'change',
...(field.type === 'string' || field.type === 'textarea' || field.type === 'password' ? { trigger: 'blur' } : {}),
})
}
if (field.rules) {
for (const r of field.rules) {
const rule: FormItemRule = { trigger: 'change' }
if (r.min !== undefined) rule.min = r.min
if (r.max !== undefined) rule.max = r.max
if (r.pattern) rule.pattern = new RegExp(r.pattern)
if (r.message) rule.message = vueAdmin.t(r.message)
rules.push(rule)
}
}
return rules
}
const formRules = computed(() => {
const rules: Record<string, FormItemRule[]> = {}
for (const field of props.fields) {
const list = buildRules(field)
if (list.length > 0) {
rules[field.key] = list
}
}
return rules
})
function onFieldChange(key: string, value: any) {
emit('update:formData', { ...props.formData, [key]: value })
}
async function validate(): Promise<boolean> {
if (!formRef.value) return true
try {
await formRef.value.validate()
return true
} catch {
return false
}
}
defineExpose({ validate })
</script>
<style>
.shrimp-drawer .el-drawer__header {
margin-bottom: 0;
padding: 16px 20px;
border-bottom: 1px solid var(--el-border-color-lighter);
}
.shrimp-drawer .el-drawer__body {
padding: 20px;
}
.shrimp-drawer .el-drawer__footer {
padding: 12px 20px;
border-top: 1px solid var(--el-border-color-lighter);
display: flex;
justify-content: flex-end;
gap: 10px;
}
.shrimp-drawer .el-drawer__body .el-form-item {
margin-bottom: 20px;
}
</style>