- 拷 shrimp-server/src/IShrimpType.ts 到 src/IShrimpType.ts,头改 @syncedFrom shrimp-server contract 1.0.0 - 全部内部 import 'shrimp-server/types' → 本地 './IShrimpType'(各文件按相对路径) - src/index.ts 加上类型契约的 re-export(ShrimpCode/ShrimpError/枚举/全部 interface),消费侧从 'shrimp-admin' 一处拿到 - 删 dependencies.shrimp-server,前端 bundle 不再牵 mongoose/express 为什么不直接 import 'shrimp-server': shrimp-server v0.6.0 收单入口后,入口含 mongoose/express,前端会被 vite 牵进 bundle。 契约副本 + 版本号对照是低频改动场景的更干净选择。
127 lines
3.8 KiB
Vue
127 lines
3.8 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('shrimp.fieldRequired'),
|
|
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>
|