代码片段
片段 1
enterprises.value = res.data
.map((item: IEnterprise) => {
const { authorizationType, accessScope, workName, id } = item; // 解构赋值
if (
authorizationType === 1 ||
(authorizationType === 2 && accessScope.includes("企微客服"))
) {
return {
label: workName,
value: id,
authorizationType,
};
}
return null;
})
.filter(Boolean); // 过滤掉 null 值使用解构赋值,减少变量名
使用 Boolean 转换(去除 falsy 值,包括 null、undefined、''、0、false、NaN)
var arr = ["1", null, "2", "", undefined, "3.jpg", " ", "0"];只去除 null 和 undefined
var newArr = arr.filter((v) => v != null); // 等价于 v !== null && v !== undefined
console.log(newArr); // ['1', '2', '', '3.jpg', ' ', '0']使用 Boolean(去除所有 falsy 值)
var newArr = arr.filter(Boolean);
console.log(newArr); // ['1', '2', '3.jpg', ' ', '0']filter(Boolean) 会移除以下值:
false,0,""(空字符串),null,undefined,NaN
但不会移除 " "(空格字符串),因为它是 truthy。
去除 null、undefined、空字符串(包括纯空格)
var newArr = arr.filter((v) => v && v.trim() !== "");
console.log(newArr); // ['1', '2', '3.jpg', '0']自定义函数封装
function cleanArray(arr) {
return arr.filter((v) => v != null && String(v).trim() !== "");
}
console.log(cleanArray(arr)); // ['1', '2', '3.jpg', '0']注重性能(当前的 reduce 方案),但可读性较差
enterprises.value = res.data.reduce((acc: IEnterprise[], item: IEnterprise) => {
const { authorizationType, accessScope, workName, id } = item;
if (
authorizationType === 1 ||
(authorizationType === 2 && accessScope.includes("企微客服"))
) {
acc.push({
label: workName,
value: id,
authorizationType,
});
}
return acc;
}, []);片段 2
三元表达式处理接口
const params: IContactPerson = buildXXXParams(); // 构建参数
const res = await(isEdit.value ? updateContactPerson : addContactPerson)(
params
); // 三元表达式处理添加/编辑操作// 多个接口使用对象映射,不过具体看场景,复杂场景建议单一接口,遵循单一职责原则
const operation = {
add: addContactPerson,
edit: updateContactPerson,
// ...
};
const res = await operation[isEdit.value ? "edit" : "add"](params); // 三元表达式处理添加/编辑操作三元表达式嵌套处理空格
原本的写法:
<template>
<div v-for="item in list" :key="item.id">
{{ item.value ? (item.value === " " ? "false" : "true") : "false" }}
<!-- 在vue模板中处理空格,使用嵌套三元表达式 -->
</div>
</template>最佳的写法:
<!-- 数据源处理(最佳) -->
<script setup lang="ts">
interface ListItem {
id: string;
value: string;
displayText: string; // 预处理的显示文本
}
const list = ref<ListItem[]>([]);
// 在获取数据时处理,避免在模板中处理
const processListData = (rawData: ListItem[]) => {
return rawData.map((item) => ({
...item,
displayText: getDisplayText(item.value),
}));
};
// 处理空格
const getDisplayText = (value: string) => {
if (!value || value === " ") return "false";
return "true";
};
</script>
<template>
<div v-for="item in list" :key="item.id">
{{ item.displayText }}
</div>
</template>片段 3
嵌套循环的判断优化
const checkSkipLink = (): boolean => {
let hasWarning = false;
if (pageData.value.type === "2") {
formData.value.generateData.forEach((item: IWidgetComponents) => {
if (item.type === "button" && item.options.skipLink) {
if (!item.options.skipLink.startsWith("https://")) {
ElMessage.error("跳转企微链接需是完整链接,包含https://");
hasWarning = true;
} else if (!item.options.skipLink.includes("qq.com")) {
hasWarning = true;
ElMessage.error("跳转企微链接必须包含域名qq.com");
}
} else if (item.type === "group") {
item.cols.forEach((arrItem: IWidgetComponents[]) => {
arrItem.forEach((item: IWidgetComponents) => {
if (item.type === "button" && item.options.skipLink) {
if (!item.options.skipLink.startsWith("https://")) {
ElMessage.error("跳转企微链接需是完整链接,包含https://");
hasWarning = true;
} else if (!item.options.skipLink.includes("qq.com")) {
hasWarning = true;
ElMessage.error("跳转企微链接必须包含域名qq.com");
}
}
});
});
} else if (item.type === "groupBox") {
const widget = item as IGroupBoxWidget;
widget.childList.forEach((item: IWidgetComponents) => {
if (item.type === "button" && item.options.skipLink) {
if (!item.options.skipLink.startsWith("https://")) {
ElMessage.error("跳转企微链接需是完整链接,包含https://");
hasWarning = true;
} else if (!item.options.skipLink.includes("qq.com")) {
hasWarning = true;
ElMessage.error("跳转企微链接必须包含域名qq.com");
}
}
});
}
});
}
return !hasWarning;
};优化后:
const checkSkipLink = (): boolean => {
// 将配置提取到方法内部
const VALIDATION_CONFIG = {
REQUIRED_PROTOCOL: "https://",
REQUIRED_DOMAIN: "qq.com",
TARGET_PAGE_TYPE: "2",
} as const;
// 将错误消息提取
const ERROR_MESSAGES = {
INVALID_PROTOCOL: "跳转企微链接需是完整链接,包含https://",
INVALID_DOMAIN: "跳转企微链接必须包含域名qq.com",
} as const;
let isSuspend = false;
// 链接验证逻辑,职责更单一
const validateSkipLink = (link: string): boolean => {
if (!link.startsWith(VALIDATION_CONFIG.REQUIRED_PROTOCOL)) {
ElMessage.error(ERROR_MESSAGES.INVALID_PROTOCOL);
return true;
}
if (!link.includes(VALIDATION_CONFIG.REQUIRED_DOMAIN)) {
ElMessage.error(ERROR_MESSAGES.INVALID_DOMAIN);
return true;
}
return false;
};
// 处理单个组件的验证
const processItem = (item: IWidgetComponents) => {
if (item.type === "button" && item.options.skipLink) {
isSuspend = validateSkipLink(item.options.skipLink) || isSuspend;
}
};
// 递归处理组合型组件
const processGroupItem = (item: IWidgetComponents) => {
if (item.type === "group") {
item.cols.forEach((arrItem: IWidgetComponents[]) => {
arrItem.forEach(processItem);
});
} else if (item.type === "groupBox") {
const widget = item as IGroupBoxWidget;
widget.childList.forEach(processItem);
} else {
processItem(item);
}
};
// 主逻辑:只有特定页面类型才需要检查
if (pageData.value.type === VALIDATION_CONFIG.TARGET_PAGE_TYPE) {
formData.value.generateData.forEach(processGroupItem);
}
return isSuspend;
};片段 4
接口拆分
当前端数据属性与接口需要传入属性不一致的(很多的)情况,使用映射关系进行处理
/**
* 设置ai组件数据
* @param data 数据,为低代码组件list,包含aiUtils、groupBox等组件
* @param aiCustomerServices ai组件数据,为接口返回数据(多个ai组件数据)
*/
const setAiComponentData = (
data: IWidgetComponents[],
aiCustomerServices: IAiConversationParams[]
) => {
data.forEach((item) => {
if (item.type === "aiUtils") {
const aiUtils = item as IAiUtilsWidget;
aiCustomerServices.find((aiCustomerService) => {
if (
aiCustomerService.componentKey === aiUtils.key &&
aiCustomerService.type === "aiConversation"
) {
aiUtils.options.customerDesc =
aiCustomerService.accurateCustomerDescription;
aiUtils.options.unCustomerDesc =
aiCustomerService.nonTargetCustomerDescription;
aiUtils.options.roleDefinition = aiCustomerService.roleCustomization;
aiUtils.options.welcomeContent = aiCustomerService.welcomeMessage;
aiUtils.options.knowledgeBase =
aiCustomerService.relatedKnowledgeBase;
return true;
} else if (
aiCustomerService.componentKey === aiUtils.key &&
aiCustomerService.type === "aiAssistant"
) {
aiUtils.options.assistantName = aiCustomerService.one;
aiUtils.options.greeting = aiCustomerService.two;
aiUtils.options.capabilities = aiCustomerService.three;
return true;
}
// 等等等...
});
} else if (item.type === "groupBox") {
const groupBox = item as IGroupBoxWidget;
setAiComponentData(groupBox.childList, aiCustomerServices);
}
});
};优化后:
/**
* 设置ai组件数据
* @param data 数据
* @param aiCustomerServices ai组件数据
*/
const setAiComponentData = (
data: IWidgetComponents[],
aiCustomerServices: IAiConversationParams[]
) => {
const serviceMap = new Map(
aiCustomerServices.map((service) => [service.componentKey, service])
);
// 配置不同类型的属性映射
const AI_SERVICE_HANDLERS = {
// 添加 aiConversation 的映射配置
aiConversation: {
mappings: {
accurateCustomerDescription: "customerDesc",
nonTargetCustomerDescription: "unCustomerDesc",
roleCustomization: "roleDefinition",
welcomeMessage: "welcomeContent",
relatedKnowledgeBase: "knowledgeBase",
},
},
// 后续的扩展,aiAssistant 的映射配置
// aiAssistant: {
// mappings: {
// roleCustomization: "assistantName",
// welcomeMessage: "greeting",
// relatedKnowledgeBase: "capabilities"
// },
// }
} as const;
const processAiComponent = (component: IAiUtilsWidget) => {
const service = serviceMap.get(component.key);
if (!service) {
// 找不到数据源问题 serviceMap 生成有误
console.error(`AI组件缺少服务配置: componentKey="${component.key}"`, {
availableServices: Array.from(serviceMap.keys()),
component,
possibleCauses: [
"后端未返回此组件的服务配置",
"组件key与服务componentKey不匹配",
"aiCustomerServices数据缺失",
],
});
return;
}
const handler =
AI_SERVICE_HANDLERS[service.type as keyof typeof AI_SERVICE_HANDLERS];
if (!handler) {
// 找不到映射配置问题 AI_SERVICE_HANDLERS 生成有误
console.error(`AI服务类型缺少映射配置: serviceType="${service.type}"`, {
availableHandlers: Object.keys(AI_SERVICE_HANDLERS),
service,
solution: `请在 AI_SERVICE_HANDLERS 中添加 "${service.type}" 类型的映射配置`,
});
return;
}
// 正常的映射逻辑
const updates = Object.entries(handler.mappings).reduce(
(acc, [sourceKey, targetKey]) => {
const value = service[sourceKey as keyof IAiConversationParams];
if (value !== undefined) {
acc[targetKey] = value;
}
return acc;
},
{} as any
);
Object.assign(component.options, updates);
};
const processComponents = (components: IWidgetComponents[]) => {
components.forEach((item) => {
if (item.type === "aiUtils") {
processAiComponent(item as IAiUtilsWidget);
} else if (item.type === "groupBox") {
const groupBox = item as IGroupBoxWidget;
processComponents(groupBox.childList);
}
});
};
processComponents(data);
};执行流程:
setAiComponentData 函数完整执行流程详解
// 假设传入的数据
const data = [
{ type: "aiUtils", key: "ai_001", options: {} },
{ type: "button", key: "btn_001" },
{
type: "groupBox",
key: "group_001",
childList: [
{ type: "aiUtils", key: "ai_002", options: {} },
{ type: "text", key: "txt_001" },
],
},
];
const aiCustomerServices = [
{
componentKey: "ai_001",
type: "aiConversation",
accurateCustomerDescription: "精准客户描述内容",
nonTargetCustomerDescription: "非目标客户描述内容",
roleCustomization: "AI角色定制内容",
welcomeMessage: "欢迎语内容",
relatedKnowledgeBase: "相关知识库内容",
},
{
componentKey: "ai_002",
type: "aiConversation",
// ... 其他属性
},
];第 1 步:创建服务映射表 - Map 工作原理
const serviceMap = new Map(aiCustomerServices.map((service) => [service.componentKey, service]))
// Map 构造过程详解:
// 1. .map() 生成二维数组:
const pairs = [
["ai_001", { componentKey: "ai_001", type: "aiConversation", accurateCustomerDescription: "精准客户描述内容", ... }],
["ai_002", { componentKey: "ai_002", type: "aiConversation", ... }]
]
// 2. new Map() 将二维数组转换为Map对象:
// Map {
// "ai_001" => { componentKey: "ai_001", type: "aiConversation", ... },
// "ai_002" => { componentKey: "ai_002", type: "aiConversation", ... }
// }
// 重要:Map 不是普通对象 {},必须用 .get(key) 访问值
const service = serviceMap.get("ai_001") // 返回完整的service对象第 2 步:定义映射配置
const AI_SERVICE_HANDLERS = {
aiConversation: {
mappings: {
accurateCustomerDescription: "customerDesc",
nonTargetCustomerDescription: "unCustomerDesc",
roleCustomization: "roleDefinition",
welcomeMessage: "welcomeContent",
relatedKnowledgeBase: "knowledgeBase",
},
},
};第 3 步:开始处理组件
processComponents(data); // 传入整个数据数组第 4 步:遍历顶层组件
data.forEach((item) => {
// item1: { type: "aiUtils", key: "ai_001", options: {} }
// item2: { type: "button", key: "btn_001" }
// item3: { type: "groupBox", key: "group_001", childList: [...] }
});第 5 步:处理第一个 aiUtils 组件
// item.type === "aiUtils" 为 true
processAiComponent(item as IAiUtilsWidget)
// 在processAiComponent中:
const service = serviceMap.get("ai_001") // 找到完整的服务对象
const handler = AI_SERVICE_HANDLERS["aiConversation"] // 获取映射配置第 6 步:属性映射核心逻辑
6.1 Object.entries() 转换映射配置
// Object.entries(handler.mappings)
const entries = [
["accurateCustomerDescription", "customerDesc"],
["nonTargetCustomerDescription", "unCustomerDesc"],
["roleCustomization", "roleDefinition"],
["welcomeMessage", "welcomeContent"],
["relatedKnowledgeBase", "knowledgeBase"],
];6.2 reduce() 逐项处理映射
// .reduce((acc, [sourceKey, targetKey]) => {...}, {} as any)
// 初始值:acc = {}
// === 第1次迭代 ===
// [sourceKey, targetKey] = ["accurateCustomerDescription", "customerDesc"]
const value = service["accurateCustomerDescription"]; // "精准客户描述内容"
if (value !== undefined) {
// true
acc["customerDesc"] = "精准客户描述内容";
}
// acc 现在是:{ customerDesc: "精准客户描述内容" }
// === 第2次迭代 ===
// [sourceKey, targetKey] = ["nonTargetCustomerDescription", "unCustomerDesc"]
const value = service["nonTargetCustomerDescription"]; // "非目标客户描述内容"
if (value !== undefined) {
// true
acc["unCustomerDesc"] = "非目标客户描述内容";
}
// ... 依次处理所有映射规则
// 最终 reduce 返回的 updates:
const updates = {
customerDesc: "精准客户描述内容",
unCustomerDesc: "非目标客户描述内容",
roleDefinition: "AI角色定制内容",
welcomeContent: "欢迎语内容",
knowledgeBase: "相关知识库内容",
};6.3 添加组件类型并批量更新
// 添加组件类型
updates.componentsType = "aiConversation";
// 批量更新组件属性
// 假设 component.options 原来是:{ someExistingProp: "原有属性" }
Object.assign(component.options, updates);
// 执行后 component.options 变成:
// {
// someExistingProp: "原有属性", // 保留原有属性
// customerDesc: "精准客户描述内容", // 新增
// unCustomerDesc: "非目标客户描述内容", // 新增
// roleDefinition: "AI角色定制内容", // 新增
// welcomeContent: "欢迎语内容", // 新增
// knowledgeBase: "相关知识库内容", // 新增
// componentsType: "aiConversation" // 新增
// }第 8 步:处理 groupBox 组件(递归)
// item.type === "groupBox" 为 true
const groupBox = item as IGroupBoxWidget
processComponents(groupBox.childList) // 递归调用!
// 递归处理 groupBox.childList,重复上述流程:
// - 遇到 ai_002,调用 processAiComponent 处理
// - 遇到 text,跳过关键技术点总结
1. Map 数据结构的优势
创建:
new Map([[key1, value1], [key2, value2]])从二维数组创建查找:
map.get(key)时间复杂度 O(1),比数组查找 O(n) 更高效返回值:
.get(key)返回完整的 value 对象
2. 配置化映射机制
Object.entries(): 将对象转为
[key, value]数组数组解构:
[sourceKey, targetKey]同时获取源属性名和目标属性名动态属性访问:
service[sourceKey]用变量作为属性名访问reduce 累积器: 从空对象
{}开始,逐步构建最终的 updates 对象
3. 批量更新策略
Object.assign(): 一次性复制所有属性,比逐个赋值更高效
属性合并: 保留原有属性,覆盖同名属性,添加新属性
4. 递归遍历设计
深度优先: 遇到嵌套结构(groupBox)时递归处理
类型判断: 通过
item.type决定处理方式无限层级: 可以处理任意深度的组件嵌套
整体设计优势
高性能: Map 查找 O(1) + 批量更新
高扩展性: 配置化映射,新增 AI 类型只需添加配置
类型安全: TypeScript 类型约束确保代码健壮性
递归处理: 优雅处理复杂的嵌套组件结构
代码简洁: 函数式编程思维,逻辑清晰易懂