Files
wangziqi 72e2110987
Some checks failed
Synchronize to Gitee / repo-sync (push) Has been cancelled
Typos Checking / Spell Check with Typos (push) Has been cancelled
chore: initial commit
2026-06-23 11:56:23 +08:00

567 lines
21 KiB
TypeScript
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.

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;
}