The associated form in the form is filled with the associated form.
This case is from Jing, a three-party developer 」
1. Usage scenarios
YIDA currently, the associated form component cannot be filled with the associated form component. In this example, we can discuss how to implement the corresponding functions.
Equivalent:
Currently, only the scenarios in which the associated form component is used in the main table are supported. Currently, the scenarios in subforms are not supported.
If data filling is included, the associated form must be filled with the new version of data. The old version of data filling cannot be triggered. For more information, see the following update announcement.
For more information about how to upgrade the new version of data filling, see Directory [5. Upgrade the new version of data filling] (the associated form configured after 2023.10.30 does not need to be operated).
2. Implement functions
2.1. Configure associated form data table 1
Used to associate data with subsequent forms.
2.2. Configure associated form data table 2
An associated form association Table 1 is placed in the associated form table 2.
2.3. Does not include the data filling of the associated form to be filled.
2.3.1. Configuration page
2.3.2. Configure associated forms
Associate the form configured in 2.2 and set display fields.
Associate the form configured in 2.1 and set display fields.
2.3.3. Copy the following code to Page JS
No modification is required.
/**
* 关联表单填充关联表单
* @param sourceAssociationFormFieldId 关联表单主表字段-关联表单组件唯一标识
* @param fillAssociationFormFieldId 当前表单字段-关联表单组件唯一标识
* @param value 关联表单实例数据
*/
export function associationFormFillAssociationForm(sourceAssociationFormFieldId = '', fillAssociationFormFieldId = '', value = []) {
if (sourceAssociationFormFieldId && fillAssociationFormFieldId) {
if (value && value.length) {
const { rawData = {} } = value[0];
const { instValue = [] } = rawData;
const sourceAssociationFormFieldData = instValue.filter((item) => {
return item.fieldId === sourceAssociationFormFieldId;
});
if (sourceAssociationFormFieldData.length) {
const fillData = (((sourceAssociationFormFieldData[0] || {}).fieldData || {}).value || []);
this.$(fillAssociationFormFieldId).setValue(fillData);
}
} else {
this.$(fillAssociationFormFieldId).reset();
}
}
}
2.3.4. Call functions in onChange
Note that the unique identifier in parentheses is modified.
The value of the primary table Field (sourceAssociationFormFieldId) of the associated form is filled into the current form field (fillAssociationFormFieldId).
/**
* 关联表单-数据表2 onChange
* @param value 当前值
*/
export function onChange({ value }) {
this.associationFormFillAssociationForm('associationFormField_lrolefps', 'associationFormField_lrolnvkw', value);
}
2.4. Data filling that contains the associated form to be filled
2.4.1. Configuration page
2.4.2. Configure associated forms
Associate the form configured in 2.2 and set display fields.
Associate the form configured in 2.1 and set display fields.
2.4.3. Configure Data filling
Configure Data filling on demand.
2.4.4. Copy the following code to Page JS
No modification is required.
/**
* 关联表单填充关联表单
* @param sourceAssociationFormFieldId 关联表单主表字段-关联表单组件唯一标识
* @param fillAssociationFormFieldId 当前表单字段-关联表单组件唯一标识
* @param value 关联表单实例数据
*/
export function associationFormFillAssociationForm(sourceAssociationFormFieldId = '', fillAssociationFormFieldId = '', value = []) {
if (sourceAssociationFormFieldId && fillAssociationFormFieldId) {
if (value && value.length) {
const { rawData = {} } = value[0];
const { instValue = [] } = rawData;
const sourceAssociationFormFieldData = instValue.filter((item) => {
return item.fieldId === sourceAssociationFormFieldId;
});
if (sourceAssociationFormFieldData.length) {
const fillData = (((sourceAssociationFormFieldData[0] || {}).fieldData || {}).value || []);
this.$(fillAssociationFormFieldId).setValue(fillData);
}
} else {
this.$(fillAssociationFormFieldId).reset();
}
}
}
/**
* 触发关联表单数据填充
* @param associationFormFieldId 需要数据填充的关联表单组件唯一标识
*/
export function associationFormDataFilling(associationFormFieldId = '') {
if (associationFormFieldId) {
const associationFormValue = this.$(associationFormFieldId).getValue();
if (associationFormValue && associationFormValue.length) {
const { appType = '', formType = '', formUuid = '', instanceId = '' } = associationFormValue[0] || {};
if (appType && formType && formUuid && instanceId) {
const dataFillingRules = this.$(associationFormFieldId).get('dataFillingRules') || {}; // 关联表单填充条件
const { mainRules = [], tableRules = [] } = dataFillingRules;
if (mainRules.length) {
// 主表字段数据填充
if (formType === 'receipt') {
// 表单
fetch(`/${appType}/v1/form/getFormDataById.json?_csrf_token=${this.getCsrfToken()}&formInstId=${instanceId}`, {
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
})
.then((data) => {
return data.json();
}).then(({ content }) => {
const { formData = {} } = (content || {});
this.mainDataFilling(formData, mainRules);
}).catch(() => {
this.utils.toast({
title: '数据获取失败',
type: 'error',
});
});
}
if (formType === 'process') {
// 流程
fetch(`/${appType}/v1/process/getInstanceById.json?_csrf_token=${this.getCsrfToken()}&processInstanceId=${instanceId}`, {
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
})
.then((data) => {
return data.json();
}).then(({ content }) => {
const { data = {} } = (content || {});
this.mainDataFilling(data, mainRules);
}).catch(() => {
this.utils.toast({
title: '数据获取失败',
type: 'error',
});
});
}
}
if (tableRules.length) {
// 子表字段数据填充
const body = new URLSearchParams();
const searchBody = tableRules.map((item) => {
return {
page: 1,
limit: 500,
fieldId: item.tableId,
resultGroupKey: item.tableId,
searchField: (((item.filters || {}).rules) || []).map((i) => {
return {
key: i.id,
value: i.extValue === 'value' ? i.value : this.$(i.value).getValue(),
type: this.getType(i.componentType),
componentName: i.componentType,
operator: i.op,
};
}),
logicOperator: (item.filters || {}).condition,
}
});
body.append('_csrf_token', this.getCsrfToken());
body.append('formUuid', formUuid);
body.append('formInstId', instanceId);
body.append('instSubTableDataParamListStr', JSON.stringify(searchBody));
fetch(`/${appType}/query/formProcInstData/getInstMultiSubTableDatas.json`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body,
})
.then((data) => {
return data.json();
}).then(({ content }) => {
// this.utils.__getFlowMaps().tableFieldIdMap 为非官方提供标准 API,有失效风险,谨慎使用
const targetTableFieldIdMap = this.utils.__getFlowMaps().tableFieldIdMap; // 获取当前页面子表单字段映射
const childDataFillingRules = {}; // 子表字段填充规则
tableRules.forEach((item) => {
if (!childDataFillingRules[item.tableId]) {
childDataFillingRules[item.tableId] = [];
}
const childRules = (item.rules || []).map((i) => {
return {
...i,
sourceTableField: item.tableId, // 目标子表单
targetTableField: targetTableFieldIdMap[i.target], // 当前页面子表单
};
});
childDataFillingRules[item.tableId] = childRules;
});
this.childDataFilling(content, childDataFillingRules);
}).catch(() => {
this.utils.toast({
title: '数据获取失败',
type: 'error',
});
});
}
}
}
}
}
/**
* 主表字段数据填充处理函数
* @param dataMap 源数据
* @param mainRules 主表字段填充规则
*/
export function mainDataFilling(dataMap = {}, mainRules = []) {
try {
mainRules.forEach((item) => {
this.$(item.target).setValue(this.mainDataSolve(dataMap, item.source, item.sourceType));
});
} catch (error) {
console.error(error);
this.utils.toast({
title: '主表字段数据填充错误',
type: 'error',
});
}
}
/**
* 主表数据处理函数
* @param dataMap 源数据
* @param source 源字段唯一标识
* @param sourceType 源字段类型
*/
export function mainDataSolve(dataMap = {}, source = '', sourceType = '') {
try {
if (dataMap[source]) {
switch (sourceType) {
case 'AttachmentField': // 附件
case 'ImageField': // 图片
case 'AddressField': // 地址
return dataMap[source] ? JSON.parse(dataMap[source]) : [];
case 'RadioField': // 单选
case 'CheckboxField': // 复选
case 'SelectField': // 下拉单选
case 'MultiSelectField': // 下拉复选
return dataMap[`${source}_id`];
case 'CascadeSelectField': // 级联选择
return (dataMap[`${source}_id`] || []).at(-1);
case 'EmployeeField': // 成员
return (dataMap[source] || []).map((item, index) => {
return {
label: item,
value: dataMap[`${source}_id`][index],
};
});
case 'DepartmentSelectField': // 部门
case 'CountrySelectField': // 国家 / 地区
return (dataMap[source] || []).map((item, index) => {
return {
text: item,
value: dataMap[`${source}_id`][index],
};
});
case 'DateField': // 日期
return Number(dataMap[source]);
case 'CascadeDateField': // 日期区间
return [Number(dataMap[source][0]), Number(dataMap[source][1])];
default:
return dataMap[source];
}
}
} catch (error) {
console.error(error);
this.utils.toast({
title: '数据填充处理错误',
type: 'error',
});
}
}
/**
* 子表字段数据填充处理函数
* @param dataMap 源数据
* @param childDataFillingRules 子表字段填充规则
*/
export function childDataFilling(dataMap = {}, childDataFillingRules = {}) {
try {
const tableDataResult = {};
Object.keys(dataMap).forEach((tableKey) => {
const items = [];
if (!tableDataResult[tableKey]) {
tableDataResult[tableKey] = [];
}
const tableDataItem = (dataMap[tableKey].values || []).map((item) => {
return item.instValue ? JSON.parse(JSON.parse(item.instValue)) : [];
});
tableDataItem.forEach((i) => {
const objItem = {};
i.forEach((j) => {
objItem[j.fieldId] = this.childDataSolve(j.fieldData, j.componentName);
});
items.push(objItem);
});
tableDataResult[tableKey] = items;
});
Object.keys(childDataFillingRules).forEach((key) => {
if (key && childDataFillingRules[key] && childDataFillingRules[key].length) {
const result = []; // 数据处理结果
tableDataResult[key].forEach((i) => {
const objItem = {};
childDataFillingRules[key].forEach((j) => {
objItem[j.target] = i[j.source];
});
result.push(objItem);
});
this.$(childDataFillingRules[key][0].targetTableField).setValue(result);
}
});
} catch (error) {
console.error(error);
this.utils.toast({
title: '子表单数据填充错误',
type: 'error',
});
}
}
/**
* 子表数据处理函数
* @param fieldData 源数据
* @param componentName 源字段类型
*/
export function childDataSolve(fieldData = {}, componentName = '') {
try {
if (componentName) {
switch (componentName) {
case 'CascadeSelectField': // 级联选择
return ((fieldData.value || []).at(-1) || {}).value;
default:
return fieldData.value;
}
}
} catch (error) {
console.error(error);
this.utils.toast({
title: '数据填充处理错误',
type: 'error',
});
}
}
/**
* 获取数据筛选的类型
* @param componentName 字段类型
*/
export function getType(componentName = '') {
if (componentName) {
switch (componentName) {
case 'RadioField': // 单选
case 'CheckboxField': // 复选
case 'SelectField': // 下拉单选
case 'MultiSelectField': // 下拉复选
case 'DepartmentSelectField': // 部门
case 'CountrySelectField': // 国家地区
return 'ARRAY';
case 'NumberField': // 数值
case 'RateField': // 评分
case 'DateField': // 日期
case 'CascadeDateField': // 日期区间
return 'DOUBLE';
default:
return 'TEXT';
}
}
}
/**
* 获取 csrfToken
*/
export function getCsrfToken() {
const tokenDom = document.getElementsByName('_csrf_token');
const csrfToken = tokenDom && tokenDom.length ? (tokenDom[0] || {}).value : '';
return csrfToken;
}
2.4.5. Call functions in onChange
The value of the primary table Field (sourceAssociationFormFieldId) of the associated form is filled into the current form field (fillAssociationFormFieldId).
The unique identifier (associationFormFieldId) of the associated form component that requires data filling.
/**
* 关联表单-数据表2 onChange
* @param value 当前值
*/
export function onChange({ value }) {
this.associationFormFillAssociationForm('associationFormField_lrolefps', 'associationFormField_lrolnvkw', value);
this.associationFormDataFilling('associationFormField_lrolnvkw');
}
3. Effect
3.1. Does not include the data filling of the associated form to be filled.
3.2. Data filling that contains the associated form to be filled
4. Try it online
5. Upgrade the new version of data filling
Save the fill conditions of the associated form again to upgrade to the new version of the data fill (the page needs to be saved again after the following operations).