Files
2026-10-20 19:08:49 +08:00

410 lines
12 KiB
Smarty
Raw Permalink 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>
<!-- [[$.ModuleComment]]新增/编辑弹窗 -->
<el-dialog
v-model="props.visible"
:title="props.[[$.PrimaryPk]] ? '编辑[[$.ModuleComment]]' : '新增[[$.ModuleComment]]'"
[[- if $.HasRichTextField]]
width="850"
[[- else]]
width="500"
[[- end]]
:close-on-click-modal="false"
:before-close="dialogClose"
>
<!-- 表单 -->
<el-form class="ls-form" ref="formRef" :model="formData" label-width="80px">
[[- range .FormFields]]
[[- if .IsImage]]
<!-- [[.Comment]](图片上传) -->
<el-form-item
label="[[.Label]]"
prop="[[.CamelName]]"
[[- if .Required]]
:rules="{ required: true, message: '请上传[[.Comment]]', trigger: 'change' }"
[[- end]]
>
<UploadImg
@changeFileName="(name) => (formData.[[.CamelName]]ImgName = name)"
:fileType="['image/jpeg', 'image/png', 'image/jpg', 'image/gif', 'image/webp']"
name="[[$.AppName]]"
:fileSize="20"
v-model:image-url="formData.[[.CamelName]]"
>
<template #tip>支持扩展名: jpg png jpeg gif webp; 文件大小不超过20M</template>
</UploadImg>
</el-form-item>
[[- else if .IsRichText]]
<!-- [[.Comment]](富文本编辑器) -->
<el-form-item
label="[[.Label]]"
prop="[[.CamelName]]"
[[- if .Required]]
:rules="{ required: true, message: '请输入[[.Comment]]', trigger: 'blur' }"
[[- end]]
>
<div class="flex">
<el-form-item class="flex-1" label-width="0">
<Editor
ref="[[.CamelName]]EditorRef"
:height="editorHeight"
class="flex-1"
name="[[$.AppName]]"
/>
</el-form-item>
</div>
</el-form-item>
[[- else if and (eq .Type "CharField") ((containsAny .Comment "备注" "介绍" "描述" "说明"))]]
<!-- [[.Comment]](多行文本) -->
<el-form-item
label="[[.Label]]"
prop="[[.CamelName]]"
[[- if .Required]]
:rules="{ required: true, message: '请输入[[.Comment]]', trigger: 'blur' }"
[[- end]]
>
<el-input type="textarea" v-model="formData.[[.CamelName]]" placeholder="请输入[[.Comment]]" :rows="3" clearable />
</el-form-item>
[[- else if eq .Type "CharField"]]
<!-- [[.Comment]](单行文本) -->
<el-form-item
label="[[.Label]]"
prop="[[.CamelName]]"
[[- if .Required]]
:rules="{ required: true, message: '请输入[[.Comment]]', trigger: 'blur' }"
[[- end]]
>
<el-input class="ls-input" v-model="formData.[[.CamelName]]" placeholder="请输入[[.Comment]]" clearable />
</el-form-item>
[[- else if and (eq .Type "IntegerField") (.Choices)]]
<!-- [[.Comment]]([[if le (len .Choices) 3]]单选[[else]]下拉选择[[end]]) -->
<el-form-item
label="[[.Label]]"
prop="[[.CamelName]]"
[[- if .Required]]
:rules="{ required: true, message: '请选择[[.Comment]]', trigger: 'change' }"
[[- end]]
>
[[- if le (len .Choices) 3]]
<el-radio-group v-model="formData.[[.CamelName]]">
[[- range .Choices]]
<el-radio :value="[[.Value]]">[[.Label]]</el-radio>
[[- end]]
</el-radio-group>
[[- else]]
<el-select
v-model="formData.[[.CamelName]]"
placeholder="请选择[[.Comment]]"
clearable
filterable
>
<el-option
v-for="item in optionsData.[[.CamelName]]List"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
[[- end]]
</el-form-item>
[[- else if and (eq .Type "IntegerField") (not .Choices)]]
<!-- [[.Comment]](数字输入) -->
<el-form-item
label="[[.Label]]"
prop="[[.CamelName]]"
[[- if .Required]]
:rules="{ required: true, message: '请输入[[.Comment]]', trigger: 'blur' }"
[[- end]]
>
<el-input-number v-model="formData.[[.CamelName]]" :min="[[.MinValue]]" :max="[[.MaxValue]]" />
</el-form-item>
[[- else if eq .Type "BooleanField"]]
<!-- [[.Comment]](开关) -->
<el-form-item label="[[.Label]]" prop="[[.CamelName]]">
<el-switch v-model="formData.[[.CamelName]]" :active-value="1" :inactive-value="0" />
</el-form-item>
[[- else if eq .Type "DateField"]]
<!-- [[.Comment]](日期选择) -->
<el-form-item
label="[[.Label]]"
prop="[[.CamelName]]"
[[- if .Required]]
:rules="{ required: true, message: '请选择[[.Comment]]', trigger: 'change' }"
[[- end]]
>
<el-date-picker
v-model="formData.[[.CamelName]]"
type="date"
placeholder="请选择[[.Comment]]"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
[[- else if eq .Type "DateTimeField"]]
<!-- [[.Comment]](日期时间选择) -->
<el-form-item
label="[[.Label]]"
prop="[[.CamelName]]"
[[- if .Required]]
:rules="{ required: true, message: '请选择[[.Comment]]', trigger: 'change' }"
[[- end]]
>
<el-date-picker
v-model="formData.[[.CamelName]]"
type="datetime"
placeholder="请选择[[.Comment]]"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
[[- else if eq .Type "TextField"]]
<!-- [[.Comment]](文本域) -->
<el-form-item
label="[[.Label]]"
prop="[[.CamelName]]"
[[- if .Required]]
:rules="{ required: true, message: '请输入[[.Comment]]', trigger: 'blur' }"
[[- end]]
>
<el-input type="textarea" v-model="formData.[[.CamelName]]" placeholder="请输入[[.Comment]]" :rows="3" />
</el-form-item>
[[- else if or (eq .Type "FloatField") (eq .Type "DecimalField")]]
<!-- [[.Comment]](小数输入) -->
<el-form-item
label="[[.Label]]"
prop="[[.CamelName]]"
[[- if .Required]]
:rules="{ required: true, message: '请输入[[.Comment]]', trigger: 'blur' }"
[[- end]]
>
<el-input-number
v-model="formData.[[.CamelName]]"
:min="[[.MinValue]]"
:max="[[.MaxValue]]"
:precision="2"
:step="0.1"
/>
</el-form-item>
[[- end]]
[[- end]]
</el-form>
<!-- 底部按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogClose">取消</el-button>
<el-button :loading="subLoading" type="primary" @click="submit">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import type { FormInstance } from 'element-plus';
import { get[[$.ModelClass]]Detail, [[$.ModuleCamel]]Add, [[$.ModuleCamel]]Update } from '@/api/[[$.ApiPath]]';
import { onMounted, reactive, shallowRef, watch } from 'vue';
import { message } from '@/utils/auth';
import { useLockFn } from '@/utils/useLockFn';
[[- if $.HasImageField]]
import UploadImg from '@/components/Upload/Image.vue';
[[- end]]
[[- if $.HasRichTextField]]
import Editor from '@/components/Editor/tinymce.vue';
[[- end]]
// 定义事件
const emit = defineEmits(['success', 'update:visible']);
// 表单引用
const formRef = shallowRef<FormInstance>();
[[- if $.HasRichTextField]]
// 富文本编辑器高度,按视口高度动态计算
const editorHeight = document.body.clientHeight - 400;
// 富文本编辑器引用
[[- range .FormFields]]
[[- if .IsRichText]]
const [[.CamelName]]EditorRef = ref();
[[- end]]
[[- end]]
[[- end]]
/**
* 下拉选项数据
*
* 仅当选项数超过 3 个时生成,供 el-select 使用;
* 选项不超过 3 个的字段在模板中直接用 el-radio 渲染,不在此维护。
*/
const optionsData = reactive({
[[- range .FormFields]]
[[- if and (eq .Type "IntegerField") (.Choices) (gt (len .Choices) 3)]]
[[.CamelName]]List: [
[[- range .Choices]]
{
id: [[if eq .ValueType "string"]]'[[.Value]]'[[else]][[.Value]][[end]],
name: '[[.Label]]',
},
[[- end]]
],
[[- end]]
[[- end]]
});
/**
* 表单数据
*
* 字段与 form_fields 一一对应,按类型初始化默认值;
* 图片字段额外维护一个 {字段名}ImgName,用于回显原始文件名。
*/
const formData = reactive({
id: '', // ID字段,新增时为0
[[- range .FormFields]]
[[.CamelName]]: [[if or (eq .Type "IntegerField") (eq .Type "BooleanField") (eq .Type "FloatField") (eq .Type "DecimalField")]][[.Default]][[else]]''[[end]],
[[- end]]
[[- range .FormFields]]
[[- if .IsImage]]
[[.CamelName]]ImgName: '', // [[.Comment]]文件名
[[- end]]
[[- end]]
});
/**
* 组件接收的参数
*
* @property visible 弹窗是否显示,配合 update:visible 双向绑定
* @property [[$.PrimaryPk]] [[$.ModuleComment]]ID,为 0 时表示新增
*/
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
[[$.PrimaryPk]]: {
type: Number,
required: true,
default: 0,
},
});
/**
* 提交表单
*
* 处理流程:
* 1. 同步富文本编辑器内容到 formData;
* 2. 触发表单校验,未通过则中断;
* 3. 按主键是否存在决定调用新增或更新接口;
* 4. 成功后提示并通知父组件刷新列表、关闭弹窗。
*/
const handleSubmit = async () => {
// 获取富文本编辑器内容
[[- range .FormFields]]
[[- if .IsRichText]]
if ([[.CamelName]]EditorRef.value) {
formData.[[.CamelName]] = [[.CamelName]]EditorRef.value.myValue;
}
[[- end]]
[[- end]]
// 表单验证
await formRef.value?.validate();
// 调用接口
props.[[$.PrimaryPk]] ? await [[$.ModuleCamel]]Update(formData) : await [[$.ModuleCamel]]Add(formData);
message('操作成功');
emit('update:visible', false);
emit('success');
};
/**
* 关闭弹窗
*
* 通过 update:visible 通知父组件隐藏。
*/
const dialogClose = () => {
emit('update:visible', false);
};
// 防抖处理,防止重复提交
const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit);
/**
* 加载详情数据
*
* 按主键请求详情,逐字段回填到 formData;
* 同时把富文本内容同步到对应编辑器实例。
* 请求失败时提示用户并输出控制台错误。
*/
const setFormData = async () => {
try {
const data = await get[[$.ModelClass]]Detail(props.[[$.PrimaryPk]]);
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
// @ts-ignore
formData[key] = data[key];
}
}
// 设置富文本编辑器内容
[[- range .FormFields]]
[[- if .IsRichText]]
if ([[.CamelName]]EditorRef.value && formData.[[.CamelName]]) {
[[.CamelName]]EditorRef.value.myValue = formData.[[.CamelName]];
}
[[- end]]
[[- end]]
} catch (error) {
console.error('获取[[$.ModuleComment]]详情失败', error);
message('获取数据失败', 'error');
}
};
/**
* 重置表单
*
* 调用 el-form 的 resetFields 清空校验状态与字段值,
* 并同步清空富文本编辑器内容。
*/
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields();
}
// 清空富文本编辑器内容
[[- range .FormFields]]
[[- if .IsRichText]]
if ([[.CamelName]]EditorRef.value) {
[[.CamelName]]EditorRef.value.myValue = '';
}
[[- end]]
[[- end]]
};
/**
* 组件挂载后处理
*
* 编辑场景加载详情,新增场景重置表单。
*/
onMounted(() => {
if (props.[[$.PrimaryPk]]) {
setFormData();
} else {
resetForm();
}
});
/**
* 监听弹窗显示状态
*
* 显示时:编辑场景加载详情,新增场景重置表单。
*/
watch(
() => props.visible,
(newVal) => {
if (newVal && props.[[$.PrimaryPk]]) {
setFormData();
} else if (newVal && !props.[[$.PrimaryPk]]) {
resetForm();
}
},
);
</script>