567 lines
21 KiB
TypeScript
567 lines
21 KiB
TypeScript
import type { CommonList, ModuleField } from '../models/common';
|
||
import { FieldTypeEnum } from '../enums/formDesignEnum';
|
||
import type { FormCreateField, FormDetail } from '@cordys/web/src/components/business/crm-form-create/types';
|
||
import { formatTimeValue, getCityPath, getIndustryPath } from './index';
|
||
import { useI18n } from '../hooks/useI18n';
|
||
|
||
export const linkAllAcceptTypes = [FieldTypeEnum.INPUT, FieldTypeEnum.TEXTAREA];
|
||
export const dataSourceTypes = [FieldTypeEnum.DATA_SOURCE, FieldTypeEnum.DATA_SOURCE_MULTIPLE];
|
||
export const hiddenTypes = [
|
||
FieldTypeEnum.DIVIDER,
|
||
FieldTypeEnum.PICTURE,
|
||
FieldTypeEnum.ATTACHMENT,
|
||
FieldTypeEnum.LINK,
|
||
FieldTypeEnum.SUB_PRICE,
|
||
FieldTypeEnum.SUB_PRODUCT,
|
||
];
|
||
export const needSameTypes = [
|
||
FieldTypeEnum.PHONE,
|
||
FieldTypeEnum.LOCATION,
|
||
FieldTypeEnum.DATE_TIME,
|
||
FieldTypeEnum.INPUT_NUMBER,
|
||
FieldTypeEnum.INDUSTRY,
|
||
FieldTypeEnum.SUB_PRICE,
|
||
FieldTypeEnum.SUB_PRODUCT,
|
||
];
|
||
export const multipleTypes = [FieldTypeEnum.CHECKBOX, FieldTypeEnum.SELECT_MULTIPLE, FieldTypeEnum.INPUT_MULTIPLE];
|
||
export const memberTypes = [FieldTypeEnum.MEMBER, FieldTypeEnum.MEMBER_MULTIPLE];
|
||
export const departmentTypes = [FieldTypeEnum.DEPARTMENT, FieldTypeEnum.DEPARTMENT_MULTIPLE];
|
||
export const singleTypes = [FieldTypeEnum.RADIO, FieldTypeEnum.SELECT];
|
||
export const specialBusinessKeyMap: Record<string, string> = {
|
||
customerId: 'customerName',
|
||
contactId: 'contactName',
|
||
clueId: 'clueName',
|
||
businessId: 'businessName',
|
||
contractId: 'contractName',
|
||
owner: 'ownerName',
|
||
opportunityId: 'opportunityName',
|
||
paymentPlanId: 'paymentPlanName',
|
||
businessTitleId: 'businessTitleName',
|
||
};
|
||
|
||
export function getRuleType(item: FormCreateField) {
|
||
if (
|
||
item.type === FieldTypeEnum.SELECT_MULTIPLE ||
|
||
item.type === FieldTypeEnum.CHECKBOX ||
|
||
item.type === FieldTypeEnum.INPUT_MULTIPLE ||
|
||
item.type === FieldTypeEnum.MEMBER_MULTIPLE ||
|
||
item.type === FieldTypeEnum.DEPARTMENT_MULTIPLE ||
|
||
item.type === FieldTypeEnum.DATA_SOURCE ||
|
||
item.type === FieldTypeEnum.DATA_SOURCE_MULTIPLE ||
|
||
item.type === FieldTypeEnum.PICTURE ||
|
||
item.type === FieldTypeEnum.ATTACHMENT
|
||
) {
|
||
return 'array';
|
||
}
|
||
if (item.type === FieldTypeEnum.DATE_TIME) {
|
||
return 'date';
|
||
}
|
||
if ([FieldTypeEnum.INPUT_NUMBER, FieldTypeEnum.FORMULA].includes(item.type)) {
|
||
return 'number';
|
||
}
|
||
return 'string';
|
||
}
|
||
|
||
export function getNormalFieldValue(item: FormCreateField, value: any) {
|
||
if (item.type === FieldTypeEnum.DATA_SOURCE && !value) {
|
||
return '';
|
||
}
|
||
if (
|
||
[
|
||
FieldTypeEnum.SELECT_MULTIPLE,
|
||
FieldTypeEnum.MEMBER_MULTIPLE,
|
||
FieldTypeEnum.DEPARTMENT_MULTIPLE,
|
||
FieldTypeEnum.DATA_SOURCE_MULTIPLE,
|
||
FieldTypeEnum.INPUT_MULTIPLE,
|
||
].includes(item.type) &&
|
||
!value
|
||
) {
|
||
return [];
|
||
}
|
||
if (item.type === FieldTypeEnum.INPUT_MULTIPLE && !value) {
|
||
return [];
|
||
}
|
||
if (item.multiple && !value) {
|
||
return [];
|
||
}
|
||
return value;
|
||
}
|
||
|
||
/**
|
||
* 格式化数字
|
||
* @param value 数字
|
||
* @param item
|
||
*/
|
||
export function formatNumberValue(value: string | number, item: FormCreateField) {
|
||
if (value !== undefined && value !== null && value !== '') {
|
||
if (item.numberFormat === 'percent') {
|
||
return item.precision ? `${Number(value).toFixed(item.precision)}%` : `${value}%`;
|
||
}
|
||
if (item.showThousandsSeparator) {
|
||
return (item.precision ? Number(Number(value).toFixed(item.precision)) : Number(value)).toLocaleString('en-US');
|
||
}
|
||
return item.precision ? Number(value).toFixed(item.precision) : value.toString();
|
||
}
|
||
return '-';
|
||
}
|
||
|
||
/**
|
||
* 格式化数字显示为字符串
|
||
* @param value 数字
|
||
* @param item
|
||
*/
|
||
export function formatNumberValueToString(value: number, item: FormCreateField) {
|
||
if (value !== undefined && value !== null) {
|
||
if (item.numberFormat === 'percent') {
|
||
return item.precision ? `${Number(value).toFixed(item.precision)}%` : `${value}%`;
|
||
}
|
||
if (item.showThousandsSeparator) {
|
||
if (typeof value === 'string') {
|
||
return value;
|
||
}
|
||
return item.precision
|
||
? `${value.toLocaleString('en-US').split('.')[0]}.${value.toFixed?.(item.precision).split('.')[1]}`
|
||
: value.toLocaleString('en-US');
|
||
}
|
||
return item.precision ? Number(value).toFixed(item.precision) : value.toString();
|
||
}
|
||
return '-';
|
||
}
|
||
|
||
export function initFieldValue(field: FormCreateField, value: string | number | (string | number)[]) {
|
||
if (
|
||
[FieldTypeEnum.DATA_SOURCE, FieldTypeEnum.DATA_SOURCE_MULTIPLE].includes(field.type) &&
|
||
typeof value === 'string'
|
||
) {
|
||
return value ? [value] : [];
|
||
}
|
||
return value;
|
||
}
|
||
|
||
export function getFieldItemId(field: FormCreateField) {
|
||
if (field.resourceFieldId) {
|
||
return field.id.split('_ref_')[1]; // 处理数据源显示字段
|
||
}
|
||
return field.id;
|
||
}
|
||
/**
|
||
*
|
||
* @param field
|
||
* @param fieldValue
|
||
* @returns 获取系统字段的值展示
|
||
*/
|
||
export function getDisplayFieldText(field: FormCreateField, fieldValue: any) {
|
||
const { t } = useI18n();
|
||
const fieldKey = field.businessKey || getFieldItemId(field);
|
||
|
||
if (fieldKey === 'invalid') {
|
||
if (fieldValue === true || fieldValue === 'true') {
|
||
return t('common.voided');
|
||
}
|
||
if (fieldValue === false || fieldValue === 'false') {
|
||
return t('common.normal');
|
||
}
|
||
}
|
||
|
||
const currentOption = field.options?.find((option: any) => {
|
||
if (option.value === fieldValue) {
|
||
return true;
|
||
}
|
||
if (typeof option.value === 'boolean' && typeof fieldValue === 'string') {
|
||
return String(option.value) === fieldValue;
|
||
}
|
||
return false;
|
||
});
|
||
|
||
return currentOption ? currentOption.label : fieldValue;
|
||
}
|
||
|
||
export function parseModuleFieldValue(item: FormCreateField, fieldValue: string | string[], options?: any[]) {
|
||
if (fieldValue === undefined || fieldValue === null || fieldValue === '') {
|
||
return '-';
|
||
}
|
||
const { t } = useI18n();
|
||
let value: string | string[] = fieldValue;
|
||
if (options) {
|
||
// 若字段值是选项值,则取选项值的name
|
||
if (Array.isArray(fieldValue)) {
|
||
value = fieldValue.map((e) => {
|
||
const option = options.find((opt) => opt.id === e);
|
||
if (option) {
|
||
return option.name || t('common.optionNotExist');
|
||
}
|
||
return t('common.optionNotExist');
|
||
});
|
||
} else {
|
||
value = options.find((e) => e.id === fieldValue)?.name || t('common.optionNotExist');
|
||
}
|
||
} else if (
|
||
[
|
||
FieldTypeEnum.DATA_SOURCE,
|
||
FieldTypeEnum.DATA_SOURCE_MULTIPLE,
|
||
FieldTypeEnum.MEMBER,
|
||
FieldTypeEnum.MEMBER_MULTIPLE,
|
||
FieldTypeEnum.DEPARTMENT,
|
||
FieldTypeEnum.DEPARTMENT_MULTIPLE,
|
||
].includes(item.type)
|
||
) {
|
||
// 数据源/成员/部门类型字段,且没有匹配到 options,则显示不存在
|
||
if (Array.isArray(fieldValue)) {
|
||
value = fieldValue.map(() => t('common.optionNotExist'));
|
||
} else {
|
||
value = t('common.optionNotExist');
|
||
}
|
||
} else if (item.type === FieldTypeEnum.LOCATION) {
|
||
const addressArr: string[] = (fieldValue as string)?.split('-')?.filter(Boolean) || [];
|
||
if (!addressArr.length) {
|
||
value = '-';
|
||
} else {
|
||
const country = addressArr[0];
|
||
const rest = addressArr.filter((e, i) => i > 0).join('-');
|
||
value = rest ? `${getCityPath(country, item.scope)}-${rest}` : getCityPath(country, item.scope);
|
||
}
|
||
} else if (item.type === FieldTypeEnum.INDUSTRY) {
|
||
value = fieldValue ? getIndustryPath(fieldValue as string) : '-';
|
||
} else if (item.type === FieldTypeEnum.INPUT_NUMBER) {
|
||
value = formatNumberValueToString(fieldValue as unknown as number, item);
|
||
if (value.includes('NaN') || value.includes('%%')) {
|
||
value = fieldValue.toString();
|
||
}
|
||
} else if (item.type === FieldTypeEnum.DATE_TIME) {
|
||
value = formatTimeValue(fieldValue as string, item.dateType);
|
||
}
|
||
if (Array.isArray(value) && item.resourceFieldId) {
|
||
value = value.join(',');
|
||
}
|
||
return value;
|
||
}
|
||
|
||
export function parseFormDetailValue(item: FormCreateField, form: FormDetail, sourceName?: Ref<string>) {
|
||
const { t } = useI18n();
|
||
if (item.businessKey && !item.resourceFieldId) {
|
||
// 引用数据源字段使用 id 读取数据,而不是 businessKey
|
||
const options = form.optionMap?.[item.businessKey];
|
||
// 业务标准字段读取最外层,读取form[item.businessKey]取到 id 值,然后去 options 里取 name
|
||
let name: string | string[] = '';
|
||
const value = form[item.businessKey];
|
||
// 若字段值是选项值,则取选项值的name
|
||
if (options) {
|
||
if (Array.isArray(value)) {
|
||
name = value.map((e) => {
|
||
const option = options.find((opt) => opt.id === e);
|
||
if (option) {
|
||
return option.name || t('common.optionNotExist');
|
||
}
|
||
return t('common.optionNotExist');
|
||
});
|
||
} else if (value) {
|
||
name = options.find((e) => e.id === value)?.name || t('common.optionNotExist');
|
||
}
|
||
}
|
||
if (item.type === FieldTypeEnum.DATE_TIME) {
|
||
return formatTimeValue(name || form[item.businessKey], item.dateType);
|
||
}
|
||
if (item.type === FieldTypeEnum.INPUT_NUMBER) {
|
||
return formatNumberValueToString(name || form[item.businessKey], item);
|
||
}
|
||
if (item.type === FieldTypeEnum.ATTACHMENT) {
|
||
return form.attachmentMap?.[item.businessKey] || [];
|
||
}
|
||
if (item.businessKey === 'name' && sourceName) {
|
||
sourceName.value = name || form[item.businessKey];
|
||
}
|
||
return name || form[item.businessKey];
|
||
}
|
||
const options = form.optionMap?.[item.id];
|
||
// 其他的字段读取moduleFields
|
||
const field = form.moduleFields?.find((moduleField: ModuleField) => moduleField.fieldId === item.id);
|
||
if (item.type === FieldTypeEnum.ATTACHMENT) {
|
||
return form.attachmentMap?.[item.id] || [];
|
||
}
|
||
if (field) {
|
||
return parseModuleFieldValue(item, field.fieldValue, options);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 表单配置表格回显数据
|
||
*/
|
||
export function transformData({
|
||
item,
|
||
fields,
|
||
originalData,
|
||
excludeFieldIds,
|
||
needParseSubTable = false,
|
||
}: {
|
||
fields: FormCreateField[];
|
||
item: any;
|
||
originalData?: CommonList<any>;
|
||
excludeFieldIds?: string[];
|
||
needParseSubTable?: boolean;
|
||
}) {
|
||
const { t } = useI18n();
|
||
const businessFieldAttr: Record<string, any> = {};
|
||
const customFieldAttr: Record<string, any> = {};
|
||
const addressFieldIds: string[] = [];
|
||
const industryFieldIds: string[] = [];
|
||
const dataSourceFieldIds: string[] = [];
|
||
const memberFieldIds: string[] = [];
|
||
const departmentFieldIds: string[] = [];
|
||
const timeFieldIds: string[] = [];
|
||
const fieldOptionMap: Record<string, any[]> = {};
|
||
|
||
fields.forEach((field) => {
|
||
const fieldId = field.resourceFieldId ? field.id : field.businessKey || field.id;
|
||
if (field.type === FieldTypeEnum.LOCATION) {
|
||
addressFieldIds.push(fieldId);
|
||
} else if (field.type === FieldTypeEnum.INDUSTRY) {
|
||
industryFieldIds.push(fieldId);
|
||
} else if (field.type === FieldTypeEnum.DATA_SOURCE || field.type === FieldTypeEnum.DATA_SOURCE_MULTIPLE) {
|
||
dataSourceFieldIds.push(fieldId);
|
||
} else if (field.type === FieldTypeEnum.MEMBER || field.type === FieldTypeEnum.MEMBER_MULTIPLE) {
|
||
memberFieldIds.push(fieldId);
|
||
} else if (field.type === FieldTypeEnum.DEPARTMENT || field.type === FieldTypeEnum.DEPARTMENT_MULTIPLE) {
|
||
departmentFieldIds.push(fieldId);
|
||
} else if (field.type === FieldTypeEnum.DATE_TIME) {
|
||
timeFieldIds.push(fieldId);
|
||
} else if ([FieldTypeEnum.SUB_PRICE, FieldTypeEnum.SUB_PRODUCT].includes(field.type) && needParseSubTable) {
|
||
field.subFields?.forEach((subField) => {
|
||
const subFieldData = (
|
||
item[fieldId] || item.moduleFields?.find((mf: any) => mf.fieldId === fieldId)?.fieldValue
|
||
)?.map((subItem: Record<string, any>) => {
|
||
if (subField.resourceFieldId) {
|
||
subItem[`${subField.id}_original`] = subItem[field.id]; // 备份原始值以供编辑时填充数据源
|
||
subItem[subField.id] = parseModuleFieldValue(
|
||
subField,
|
||
subItem[subField.id],
|
||
// 数据源显示字段不使用业务 key,直接使用字段 id 去取值
|
||
originalData?.optionMap?.[subField.id]
|
||
);
|
||
fieldOptionMap[subField.id] = originalData?.optionMap?.[subField.id] || [];
|
||
} else {
|
||
subItem[`${subField.id}_original`] = subItem[subField.businessKey || subField.id]; // 备份原始值以供编辑时填充数据源
|
||
subItem[subField.id] = parseModuleFieldValue(
|
||
subField,
|
||
subItem[subField.businessKey || subField.id],
|
||
originalData?.optionMap?.[subField.businessKey || subField.id]
|
||
);
|
||
fieldOptionMap[subField.businessKey || subField.id] =
|
||
originalData?.optionMap?.[subField.businessKey || subField.id] || [];
|
||
}
|
||
return subItem;
|
||
});
|
||
item[fieldId] = subFieldData;
|
||
if (fieldId === field.businessKey) {
|
||
// 子表格字段可能会被设置为数据源的显示字段,而数据源显示字段都通过 id 读取,所以这里需要用 id 备份一份数据以供数据源显示字段场景读取
|
||
item[field.id] = subFieldData;
|
||
}
|
||
});
|
||
}
|
||
if (field.businessKey && !field.resourceFieldId) {
|
||
const fieldId = field.businessKey;
|
||
const options = originalData?.optionMap?.[fieldId]?.map((e: any) => ({
|
||
...e,
|
||
name: e.name || t('common.optionNotExist'),
|
||
}));
|
||
fieldOptionMap[fieldId] = options || [];
|
||
if (addressFieldIds.includes(fieldId)) {
|
||
// 地址类型字段,解析代码替换成省市区
|
||
const addressArr: string[] = item[fieldId]?.split('-')?.filter(Boolean) || [];
|
||
let value = '';
|
||
if (!addressArr.length) {
|
||
value = '-';
|
||
} else {
|
||
const country = addressArr[0];
|
||
const rest = addressArr.filter((e, i) => i > 0).join('-');
|
||
value = rest ? `${getCityPath(country)}-${rest}` : getCityPath(country);
|
||
}
|
||
businessFieldAttr[fieldId] = value;
|
||
} else if (industryFieldIds.includes(fieldId)) {
|
||
// 行业类型字段,解析代码替换成行业名称
|
||
businessFieldAttr[fieldId] = item[fieldId] ? getIndustryPath(item[fieldId] as string) : '-';
|
||
} else if (timeFieldIds.includes(fieldId)) {
|
||
// 时间类型字段,格式化时间显示
|
||
businessFieldAttr[fieldId] = formatTimeValue(item[fieldId], field.dateType);
|
||
} else if (options && options.length > 0) {
|
||
let name: string | string[] = '';
|
||
if (item[fieldId] === '' || item[fieldId] === null) {
|
||
name = '-';
|
||
} else if (dataSourceFieldIds.includes(fieldId)) {
|
||
// 处理数据源字段,需要赋值为数组
|
||
if (typeof item[fieldId] === 'string' || typeof item[fieldId] === 'number') {
|
||
// 单选
|
||
name = options?.find((e) => e.id === item[fieldId])?.name || t('common.optionNotExist');
|
||
} else {
|
||
// 多选
|
||
name = options?.filter((e) => item[fieldId]?.includes(e.id)).map((e) => e.name) || [
|
||
t('common.optionNotExist'),
|
||
];
|
||
}
|
||
} else if (typeof item[fieldId] === 'string' || typeof item[fieldId] === 'number') {
|
||
// 若值是单个字符串/数字
|
||
name = options?.find((e) => e.id === item[fieldId])?.name || t('common.optionNotExist');
|
||
} else {
|
||
// 若值是数组
|
||
name = options?.filter((e) => item[fieldId]?.includes(e.id)).map((e) => e.name) || [
|
||
t('common.optionNotExist'),
|
||
];
|
||
if (Array.isArray(name) && name.length === 0) {
|
||
name = [t('common.optionNotExist')];
|
||
}
|
||
}
|
||
if (!excludeFieldIds?.includes(field.businessKey)) {
|
||
if (specialBusinessKeyMap[fieldId]) {
|
||
// 处理特殊业务 key 映射关系
|
||
businessFieldAttr[specialBusinessKeyMap[fieldId]] = name || t('common.optionNotExist');
|
||
} else {
|
||
businessFieldAttr[fieldId] = name || t('common.optionNotExist');
|
||
}
|
||
}
|
||
if (fieldId === 'owner') {
|
||
businessFieldAttr.ownerId = item.owner;
|
||
}
|
||
} else if (specialBusinessKeyMap[fieldId]) {
|
||
// 处理特殊业务 key 映射关系
|
||
businessFieldAttr[specialBusinessKeyMap[fieldId]] = item[specialBusinessKeyMap[fieldId]];
|
||
}
|
||
businessFieldAttr[field.id] = businessFieldAttr[fieldId] || item[fieldId];
|
||
}
|
||
});
|
||
|
||
item.moduleFields?.forEach((field: ModuleField) => {
|
||
const options = originalData?.optionMap?.[field.fieldId]?.map((e) => ({
|
||
...e,
|
||
name: e.name || t('common.optionNotExist'),
|
||
}));
|
||
fieldOptionMap[field.fieldId] = options || [];
|
||
if (addressFieldIds.includes(field.fieldId)) {
|
||
// 地址类型字段,解析代码替换成省市区
|
||
const addressArr: string[] = (field?.fieldValue as string)?.split('-')?.filter(Boolean) || [];
|
||
let value = '';
|
||
if (!addressArr.length) {
|
||
value = '-';
|
||
} else {
|
||
const country = addressArr[0];
|
||
const scope = fields.find((f) => f.id === field.fieldId)?.scope;
|
||
const rest = addressArr.filter((e, i) => i > 0).join('-');
|
||
value = rest ? `${getCityPath(country, scope)}-${rest}` : getCityPath(country, scope);
|
||
}
|
||
customFieldAttr[field.fieldId] = value;
|
||
} else if (industryFieldIds.includes(field.fieldId)) {
|
||
// 行业类型字段,解析代码替换成行业名称
|
||
customFieldAttr[field.fieldId] = field.fieldValue ? getIndustryPath(field.fieldValue as string) : '-';
|
||
} else if (timeFieldIds.includes(field.fieldId)) {
|
||
// 时间类型字段,格式化时间显示
|
||
customFieldAttr[field.fieldId] = formatTimeValue(
|
||
field.fieldValue as string,
|
||
fields.find((f) => f.id === field.fieldId)?.dateType
|
||
);
|
||
} else if (options && options.length > 0) {
|
||
let name: string | string[] = '';
|
||
if (dataSourceFieldIds.includes(field.fieldId)) {
|
||
// 处理数据源字段,需要赋值为数组
|
||
if (typeof field.fieldValue === 'string' || typeof field.fieldValue === 'number') {
|
||
// 单选
|
||
name = [options.find((e) => e.id === field.fieldValue)?.name || t('common.optionNotExist')];
|
||
} else {
|
||
// 多选
|
||
name = field.fieldValue?.map((e) => options.find((o) => o.id === e)?.name || t('common.optionNotExist'));
|
||
}
|
||
} else if (typeof field.fieldValue === 'string' || typeof field.fieldValue === 'number') {
|
||
// 若值是单个字符串/数字
|
||
name = options.find((e) => e.id === field.fieldValue)?.name || t('common.optionNotExist');
|
||
} else {
|
||
// 若值是数组
|
||
name = field.fieldValue?.map((fv) => options.find((e) => e.id === fv)?.name || t('common.optionNotExist'));
|
||
if (Array.isArray(name) && name.length === 0) {
|
||
name = [t('common.optionNotExist')];
|
||
}
|
||
}
|
||
customFieldAttr[field.fieldId] = name || [t('common.optionNotExist')];
|
||
} else if (
|
||
[...dataSourceFieldIds, ...memberFieldIds, ...departmentFieldIds].includes(field.fieldId) &&
|
||
(!options || options.length === 0)
|
||
) {
|
||
// 处理匹配不到 optionsMap 的数据源/成员/部门字段
|
||
if (typeof field.fieldValue === 'string' || typeof field.fieldValue === 'number') {
|
||
// 单选
|
||
customFieldAttr[field.fieldId] = field.fieldValue !== '' ? [t('common.optionNotExist')] : ['-'];
|
||
} else {
|
||
// 避免这里返回 [['选项不存在']] 这样的嵌套数组
|
||
customFieldAttr[field.fieldId] = field.fieldValue?.map((e) => (e !== '' ? t('common.optionNotExist') : '-'));
|
||
}
|
||
} else {
|
||
// 其他类型字段,直接赋值
|
||
customFieldAttr[field.fieldId] = field.fieldValue;
|
||
}
|
||
});
|
||
// 根据 moduleFields 集合判断 fields 完整字段集合中是否有自定义字段无值,因为无值后台不会在 moduleFields 里返回该字段,需要手动置空
|
||
fields.forEach((field) => {
|
||
if (!field.resourceFieldId && !field.businessKey) {
|
||
const fieldId = field.id;
|
||
// 避免将 0 有效计算结果误判为空
|
||
if (customFieldAttr[fieldId] === undefined || customFieldAttr[fieldId] === null) {
|
||
customFieldAttr[fieldId] = undefined;
|
||
}
|
||
}
|
||
});
|
||
|
||
return {
|
||
...item,
|
||
...customFieldAttr,
|
||
...businessFieldAttr,
|
||
optionMap: fieldOptionMap,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 表单子表单计算汇总数值转换
|
||
*/
|
||
export function normalizeNumber(val: unknown): number {
|
||
if (val === null || val === undefined || val === '') return 0;
|
||
if (typeof val === 'number') {
|
||
return Number.isFinite(val) ? val : 0;
|
||
}
|
||
if (typeof val === 'string') {
|
||
let str = val.trim();
|
||
if (!str) return 0;
|
||
// 是否是百分比
|
||
const isPercent = str.endsWith('%');
|
||
// 去掉百分号
|
||
if (isPercent) {
|
||
str = str.slice(0, -1);
|
||
}
|
||
// 去掉千分位
|
||
str = str.replace(/,/g, '');
|
||
const num = Number(str);
|
||
return Number.isNaN(num) ? 0 : num;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* 合并初始选项和追加选项,去重后返回新的初始选项数组
|
||
* @param sumInitialOptions
|
||
* @param appendOptions
|
||
* @returns
|
||
*/
|
||
export function mergeUniqueOptions(sumInitialOptions: Record<string, any>[], appendOptions: Record<string, any>[]) {
|
||
const optionMap = new Map<any, Record<string, any>>();
|
||
[...sumInitialOptions, ...appendOptions].forEach((option) => {
|
||
if (!option) {
|
||
return;
|
||
}
|
||
const optionKey = option.id ?? option.value;
|
||
if (optionKey !== undefined) {
|
||
if (optionMap.has(optionKey)) {
|
||
Object.assign(optionMap.get(optionKey) || {}, option);
|
||
} else {
|
||
optionMap.set(optionKey, option);
|
||
}
|
||
}
|
||
});
|
||
sumInitialOptions = Array.from(optionMap.values());
|
||
return sumInitialOptions;
|
||
}
|