379 lines
11 KiB
Smarty
379 lines
11 KiB
Smarty
<template>
|
||
<!-- [[$.ModuleComment]]新增/编辑弹窗(树形结构) -->
|
||
<el-dialog
|
||
v-model="props.visible"
|
||
:title="props.[[$.PrimaryPk]] ? '编辑' : '新增'"
|
||
width="600"
|
||
:close-on-click-modal="false"
|
||
:before-close="dialogClose"
|
||
>
|
||
<el-form ref="formRef" :model="formData" label-width="80px">
|
||
[[- range .FormFields]]
|
||
[[- if eq .CamelName $.ParentIdCamel]]
|
||
<!-- [[.Label]](上级选择) -->
|
||
<el-form-item
|
||
label="[[.Label]]"
|
||
prop="[[.CamelName]]"
|
||
[[- if .Required]]
|
||
:rules="{ required: true, message: '请选择[[.Label]]', trigger: 'change' }"
|
||
[[- end]]
|
||
>
|
||
<el-tree-select
|
||
class="flex-1"
|
||
v-model="formData.[[.CamelName]]"
|
||
:data="treeOptions"
|
||
clearable
|
||
node-key="[[$.PrimaryPk]]"
|
||
:props="{
|
||
label: '[[$.DisplayFieldCamel]]',
|
||
}"
|
||
:default-expand-all="true"
|
||
placeholder="请选择[[.Label]]"
|
||
check-strictly
|
||
/>
|
||
</el-form-item>
|
||
[[- else if .IsImage]]
|
||
<!-- [[.Label]](图片上传) -->
|
||
<el-form-item
|
||
label="[[.Label]]"
|
||
prop="[[.CamelName]]"
|
||
[[- if .Required]]
|
||
:rules="{ required: true, message: '请上传[[.Label]]', 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 and (eq .Type "CharField") (.IsTextarea)]]
|
||
<!-- [[.Label]](多行文本) -->
|
||
<el-form-item
|
||
label="[[.Label]]"
|
||
prop="[[.CamelName]]"
|
||
[[- if .Required]]
|
||
:rules="{ required: true, message: '请输入[[.Label]]', trigger: 'blur' }"
|
||
[[- end]]
|
||
>
|
||
<el-input
|
||
class="flex-1"
|
||
v-model="formData.[[.CamelName]]"
|
||
type="textarea"
|
||
placeholder="请输入[[.Label]]"
|
||
:rows="3"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
[[- else if eq .Type "CharField"]]
|
||
<!-- [[.Label]](单行文本) -->
|
||
<el-form-item
|
||
label="[[.Label]]"
|
||
prop="[[.CamelName]]"
|
||
[[- if .Required]]
|
||
:rules="{ required: true, message: '请输入[[.Label]]', trigger: 'blur' }"
|
||
[[- end]]
|
||
>
|
||
<el-input v-model="formData.[[.CamelName]]" placeholder="请输入[[.Label]]" clearable />
|
||
</el-form-item>
|
||
[[- else if and (eq .Type "IntegerField") (.Choices)]]
|
||
<!-- [[.Label]]([[if le (len .Choices) 3]]单选[[else]]下拉选择[[end]]) -->
|
||
<el-form-item
|
||
label="[[.Label]]"
|
||
prop="[[.CamelName]]"
|
||
[[- if .Required]]
|
||
:rules="{ required: true, message: '请选择[[.Label]]', 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="请选择[[.Label]]"
|
||
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") (eq .CamelName "sort")]]
|
||
<!-- [[.Label]](排序) -->
|
||
<el-form-item
|
||
label="[[.Label]]"
|
||
prop="[[.CamelName]]"
|
||
[[- if .Required]]
|
||
:rules="{ required: true, message: '请输入[[.Label]]', trigger: 'blur' }"
|
||
[[- end]]
|
||
>
|
||
<div>
|
||
<el-input-number v-model="formData.[[.CamelName]]" :min="[[.MinValue]]" :max="[[.MaxValue]]" placeholder="请输入[[.Label]]" />
|
||
<div class="form-tips">数值越小越排前</div>
|
||
</div>
|
||
</el-form-item>
|
||
[[- else if and (eq .Type "IntegerField") (not .Choices)]]
|
||
<!-- [[.Label]](数字输入) -->
|
||
<el-form-item
|
||
label="[[.Label]]"
|
||
prop="[[.CamelName]]"
|
||
[[- if .Required]]
|
||
:rules="{ required: true, message: '请输入[[.Label]]', trigger: 'blur' }"
|
||
[[- end]]
|
||
>
|
||
<el-input-number v-model="formData.[[.CamelName]]" :min="[[.MinValue]]" :max="[[.MaxValue]]" />
|
||
</el-form-item>
|
||
[[- else if eq .Type "BooleanField"]]
|
||
<!-- [[.Label]](开关) -->
|
||
<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"]]
|
||
<!-- [[.Label]](日期选择) -->
|
||
<el-form-item
|
||
label="[[.Label]]"
|
||
prop="[[.CamelName]]"
|
||
[[- if .Required]]
|
||
:rules="{ required: true, message: '请选择[[.Label]]', trigger: 'change' }"
|
||
[[- end]]
|
||
>
|
||
<el-date-picker
|
||
v-model="formData.[[.CamelName]]"
|
||
type="date"
|
||
placeholder="请选择[[.Label]]"
|
||
format="YYYY-MM-DD"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
</el-form-item>
|
||
[[- else if eq .Type "DateTimeField"]]
|
||
<!-- [[.Label]](日期时间选择) -->
|
||
<el-form-item
|
||
label="[[.Label]]"
|
||
prop="[[.CamelName]]"
|
||
[[- if .Required]]
|
||
:rules="{ required: true, message: '请选择[[.Label]]', trigger: 'change' }"
|
||
[[- end]]
|
||
>
|
||
<el-date-picker
|
||
v-model="formData.[[.CamelName]]"
|
||
type="datetime"
|
||
placeholder="请选择[[.Label]]"
|
||
format="YYYY-MM-DD HH:mm:ss"
|
||
value-format="YYYY-MM-DD HH:mm:ss"
|
||
/>
|
||
</el-form-item>
|
||
[[- else if eq .Type "TextField"]]
|
||
<!-- [[.Label]](文本域) -->
|
||
<el-form-item
|
||
label="[[.Label]]"
|
||
prop="[[.CamelName]]"
|
||
[[- if .Required]]
|
||
:rules="{ required: true, message: '请输入[[.Label]]', trigger: 'blur' }"
|
||
[[- end]]
|
||
>
|
||
<el-input class="flex-1" type="textarea" v-model="formData.[[.CamelName]]" placeholder="请输入[[.Label]]" :rows="3" />
|
||
</el-form-item>
|
||
[[- else if eq .Type "DecimalField"]]
|
||
<!-- [[.Label]](小数输入) -->
|
||
<el-form-item
|
||
label="[[.Label]]"
|
||
prop="[[.CamelName]]"
|
||
[[- if .Required]]
|
||
:rules="{ required: true, message: '请输入[[.Label]]', 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 {
|
||
[[$.ModuleCamel]]Add,
|
||
[[$.ModuleCamel]]Update,
|
||
get[[$.ModelClass]]List,
|
||
get[[$.ModelClass]]Detail,
|
||
} from '@/api/[[$.ApiPath]]';
|
||
import { onMounted, reactive, ref, shallowRef, watch } from 'vue';
|
||
import { message, buildTree } from '@/utils/auth';
|
||
import { useLockFn } from '@/utils/useLockFn';
|
||
[[- if $.HasImageField]]
|
||
import UploadImg from '@/components/Upload/Image.vue';
|
||
[[- end]]
|
||
|
||
const emit = defineEmits(['success', 'update:visible']);
|
||
const formRef = shallowRef<FormInstance>();
|
||
|
||
// 树形选项数据,供上级选择器使用
|
||
const treeOptions = ref<any[]>([]);
|
||
|
||
/**
|
||
* 下拉选项数据
|
||
*
|
||
* 仅当选项数超过 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]]
|
||
});
|
||
|
||
/**
|
||
* 表单数据
|
||
*/
|
||
const formData = reactive({
|
||
id: '',
|
||
[[- 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: '',
|
||
[[- end]]
|
||
[[- end]]
|
||
});
|
||
|
||
/**
|
||
* 定义接收的参数
|
||
*/
|
||
const props = defineProps({
|
||
visible: {
|
||
type: Boolean,
|
||
required: true,
|
||
default: false,
|
||
},
|
||
[[$.PrimaryPk]]: {
|
||
type: Number,
|
||
required: true,
|
||
default: 0,
|
||
},
|
||
parentId: {
|
||
type: Number,
|
||
default: 0,
|
||
},
|
||
});
|
||
|
||
const dialogClose = () => {
|
||
emit('update:visible', false);
|
||
};
|
||
|
||
/**
|
||
* 加载树形选项数据
|
||
*
|
||
* 请求全量列表后通过 buildTree 组装为树,
|
||
* 并在根节点前置一个"顶级"虚拟节点(id=0),
|
||
* 使新增时可将父级设为顶级。
|
||
*/
|
||
const get[[$.ModelClass]]Tree = async () => {
|
||
const data: any = await get[[$.ModelClass]]List();
|
||
const tree: any = [{ id: 0, [[$.DisplayFieldCamel]]: '顶级', children: [] }];
|
||
const lists = buildTree(data);
|
||
tree[0].children.push(...lists);
|
||
treeOptions.value = tree;
|
||
};
|
||
|
||
/**
|
||
* 提交表单
|
||
*/
|
||
const handleSubmit = async () => {
|
||
await formRef.value?.validate();
|
||
props.[[$.PrimaryPk]] ? await [[$.ModuleCamel]]Update(formData) : await [[$.ModuleCamel]]Add(formData);
|
||
message('操作成功');
|
||
emit('update:visible', false);
|
||
emit('success');
|
||
};
|
||
|
||
const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit);
|
||
|
||
/**
|
||
* 设置表单数据
|
||
*/
|
||
const setFormData = (data: Record<any, any>) => {
|
||
for (const key in formData) {
|
||
if (data[key] != null && data[key] != undefined) {
|
||
formData[key] = data[key];
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 获取详情
|
||
*/
|
||
const getDetail = async () => {
|
||
const data = await get[[$.ModelClass]]Detail(props.[[$.PrimaryPk]]);
|
||
setFormData(data);
|
||
};
|
||
|
||
/**
|
||
* 重置表单
|
||
*/
|
||
const resetForm = () => {
|
||
if (formRef.value) {
|
||
formRef.value.resetFields();
|
||
}
|
||
formData.[[$.ParentIdCamel]] = props.parentId || 0;
|
||
};
|
||
|
||
onMounted(() => {
|
||
get[[$.ModelClass]]Tree();
|
||
if (props.[[$.PrimaryPk]]) {
|
||
getDetail();
|
||
} else {
|
||
resetForm();
|
||
}
|
||
});
|
||
|
||
watch(
|
||
() => props.visible,
|
||
(newVal) => {
|
||
if (newVal && props.[[$.PrimaryPk]]) {
|
||
getDetail();
|
||
} else if (newVal && !props.[[$.PrimaryPk]]) {
|
||
resetForm();
|
||
get[[$.ModelClass]]Tree();
|
||
}
|
||
},
|
||
);
|
||
</script>
|
||
|
||
<style lang="scss" scoped></style>
|