一、代码
<template>
<!--其他表单信息-->
<div v-loading="loading" class="otherFrom">
<div v-for="(item, index) in formList" :key="index">
<div class="title">
{{ item.name }}
<i v-if="item.ifExpand === 1" class="el-icon-circle-plus-outline addBtn" @click="addRow(item)"></i>
</div>
<el-form
ref="infoFormRefList"
:model="item.columnObj"
label-width="150px"
class="form"
style="width: 920px; margin: 24px auto 0"
>
<el-row v-for="(rowNumItem, rowNumIndex) in item.rowNum" :key="rowNumIndex">
<el-col v-for="(it, idx) in item.attributetemplateList" :key="idx" :span="12">
<el-form-item
v-if="it.dictionarytypeId === 0"
:label="it.name"
:prop="`${it.code}${rowNumIndex}`"
:rules="{
required: it.notnull,
message: `${it.name}不能为空`,
trigger: 'blur',
}"
>
<el-input
v-if="it.dataTypeName === '字符'"
v-model.trim="item.columnObj[`${it.code}${rowNumIndex}`]"
:type="it.type === 1111 ? 'textarea' : 'input'"
:disabled="it.disabled"
:placeholder="it.placeholder"
></el-input>
<el-input-number
v-if="it.dataTypeName === '小数'"
v-model.trim="item.columnObj[`${it.code}${rowNumIndex}`]"
:precision="2"
:step="0.1"
></el-input-number>
<el-input-number
v-if="it.dataTypeName === '整数' || it.dataTypeName === '长整数'"
v-model.trim="item.columnObj[`${it.code}${rowNumIndex}`]"
:step="1"
step-strictly
></el-input-number>
<el-date-picker
v-if="it.dataTypeName === '日期'"
v-model.trim="item.columnObj[`${it.code}${rowNumIndex}`]"
type="date"
:placeholder="it.placeholder"
value-format="yyyy-MM-dd"
>
</el-date-picker>
<el-date-picker
v-if="it.dataTypeName === '日期时间'"
v-model.trim="item.columnObj[`${it.code}${rowNumIndex}`]"
type="datetime"
:placeholder="it.placeholder"
value-format="yyyy-MM-dd HH:mm:ss"
>
</el-date-picker>
</el-form-item>
<el-form-item
v-else
:label="it.name"
:prop="`${it.code}${rowNumIndex}`"
:rules="{
required: it.notnull,
message: `${it.name}不能为空`,
trigger: 'change',
}"
>
<el-select v-model.trim="item.columnObj[`${it.code}${rowNumIndex}`]" filterable clearable>
<el-option
v-for="(dictItem, dictIndex) in it.dictList"
:key="dictIndex"
:label="dictItem.name"
:value="dictItem.value"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col v-if="item.rowNum > 1">
<el-divider></el-divider>
</el-col>
</el-row>
</el-form>
</div>
<el-divider></el-divider>
</div>
</template>
<script lang="ts" setup>
import { useRoute } from 'vue-router/composables';
import { getItemAttribute, findDictTypeListByTypeIds, getFormInfo, createOrUpdateForm } from '@/apis/handle';
import { ref, onMounted } from 'vue';
import { Message } from 'element-ui';
const route = useRoute();
const loading = ref<boolean>(false);
const props = withDefaults(
defineProps<{
catalogCode?: string;
projectNo?: string;
}>(),
{
catalogCode: '',
projectNo: '',
},
);
// const dataTypeList = ref<any>([
// // 表单项数据类型
// { name: '字符', id: 'varchar' }, // 输入框
// { name: '整数', id: 'int' }, // 数字输入框(不能输入小数)
// { name: '小数', id: 'double' }, // 数字输入框
// { name: '日期', id: 'date' }, // 日期选择框
// { name: '日期时间', id: 'datetime' }, // 日期选择框
// { name: '长整数', id: 'bigint' }, // 数字输入框(不能输入小数)
// ]);
const infoFormRefList = ref<any>(null); // form表单数组
const onSave = async () => {
let flag = true;
let list: any = [];
formList.value.forEach((item: any, index: number) => {
infoFormRefList.value[index].validate(async (valid: any) => {
if (valid) {
// item中有表单输入的值
let columnList: any = [];
for (let i = 0; i < item.rowNum; i++) {
let obj: any = {};
Object.keys(item.columnObj).forEach((key) => {
// 取key的最后一位字符
if (key.slice(-1) === String(i)) {
let objKey = key.slice(0, key.length - 1); // 去掉key最后一位字符
obj[objKey] = item.columnObj[key];
}
});
columnList.push(obj);
}
let paramsObj: any = {};
paramsObj.projectNo = route.query.projectNo || props.projectNo;
paramsObj.pageId = item.pageId;
paramsObj.columnList = columnList;
list.push(paramsObj);
} else {
Message({
message: `表单${formList.value[index].name}未通过校验,请检查`,
type: 'warning',
});
flag = false;
}
});
});
// 传参格式如下
// {
// "formList": [
// {
// "projectNo": "123123",
// "pageId": 111,
// "columnList": [{
// "id": 123,
// "fill_user_name":111
// ...
// }]
// }
// ]
// }
if (flag) {
let params = { formList: list };
await createOrUpdateForm(params);
asyncGetFormInfo();
return true;
} else {
return false;
}
};
// 查询其他所有动态表单列表
const formList = ref<any>([]);
const getOtherInfo = async () => {
const params = {
projectNo: route.query.projectNo || props.projectNo,
catalogCode: route.query.catalogCode || props.catalogCode,
};
const { data } = await getItemAttribute(params);
formList.value = data.map((item: any) => {
item = Object.assign({}, item, { columnObj: {} }); //(响应式赋值)
item.rowNum = 1;
return item;
});
getDictTypeListAndFormInfo();
};
// 查询其他所有动态表单内容以便反显
const asyncGetFormInfo = async () => {
let list: any = [];
formList.value.forEach((item: any) => {
let obj: any = {};
obj.projectNo = route.query.projectNo || props.projectNo;
obj.pageId = item.pageId;
list.push(obj);
});
const params = { formList: list };
const { data } = await getFormInfo(params);
formList.value = formList.value.map((item1: any) => {
data.forEach((item2: any) => {
if (item1.pageId === item2.pageId) {
if (item2.formData?.length) {
item1.rowNum = item2.formData.length;
item2.formData.forEach((item3: any, index3: number) => {
Object.keys(item3).forEach((key) => {
item1.columnObj = Object.assign({}, item1.columnObj, { [`${key}${index3}`]: item3[key] }); //(响应式赋值)
});
});
}
}
});
return item1;
});
loading.value = false;
};
const getDictTypeListAndFormInfo = async () => {
// 获取所有不为0的dictionarytypeId列表
let idList: any = [];
formList.value.forEach((item: any) => {
if (item.attributetemplateList?.length) {
idList.push(...item.attributetemplateList.map((it: any) => it.dictionarytypeId).filter((id: any) => id !== 0));
}
});
let params = { ids: idList };
const { data } = await findDictTypeListByTypeIds(params);
formList.value.forEach((item1: any) => {
if (item1.attributetemplateList?.length) {
item1.attributetemplateList.forEach((item2: any) => {
if (idList.indexOf(item2.dictionarytypeId) > -1) {
// 在下拉框项时,把该下拉框的下拉内容赋值上去 // 工作组件的上线版本管理的预览有另一种方式
item2.dictList = data.find((item3: any) => item3.dictType.id === item2.dictionarytypeId)?.dictList || [];
}
});
}
});
asyncGetFormInfo();
};
const addRow = (item: any) => {
item.rowNum++;
};
onMounted(() => {
loading.value = true;
getOtherInfo();
});
defineExpose({
onSave,
});
</script>
<style lang="scss" scoped>
.otherFrom {
display: flex;
flex-direction: column;
padding-top: 30px;
background: #fff;
}
.title {
font-size: 20px;
font-weight: 500;
color: #1c1c1c;
line-height: 20px;
.addBtn {
font-size: 24px;
margin-left: 10px;
color: #333;
}
}
</style>
二、遇到的问题与解决
1.循环的表单的modal要设置为动态的:
:model="item.columnObj"
绑定的值与当前的item有关。可以直接是item,但是后面的表单项的 v-model 的值必须与 :modal 的值只差一级。详见第二点。
2. 表单项的 v-model 的值必须与 :modal 的值要只差一级。
如果后者是item,则前者必须是item.xxxx。不能是item.xxx.xxx。否则 v-model 的值不能存进form的modal中,则必填校验无法完成。
3.表单相关对象的赋值时必须用 Object.assign
这样可以使得新增的对象具有响应式 见vue响应式赋值
item = Object.assign({}, item, { columnObj: {} }); //(响应式赋值)
4. 省市区下拉框的v-modal的值是个对象时,要给初始值
省市区下拉框的v-modal的值是个对象时,要给初始值,否则选择时不能正确反显。