# md **Repository Path**: geng-v/md ## Basic Information - **Project Name**: md - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-11-07 - **Last Updated**: 2025-12-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 云评估功能详细文档 本文档详细介绍 `useDetectionProtocol.js` 中云评估(人人估)相关的数据结构、方法和 JavaScript/Vue 知识点。 --- ## 目录 1. [数据结构](#数据结构) 2. [核心方法详解](#核心方法详解) 3. [数据流转流程](#数据流转流程) 4. [JavaScript/Vue 知识点](#javascriptvue-知识点) --- ## 数据结构 ### 1. 响应式变量 #### `cloudActiveChildIndex` (ref) ```javascript const cloudActiveChildIndex = ref(null) ``` - **类型**: `Ref` - **作用**: 存储当前选中的云评估子级 tab 索引值 - **Vue 知识点**: - `ref()`: Vue 3 的响应式 API,用于创建基本类型的响应式引用 - 访问值需要使用 `.value`,模板中自动解包 #### `cloudTabData` (ref) ```javascript const cloudTabData = ref({}) ``` - **类型**: `Ref>` - **作用**: 存储所有 tab 的数据,以检测方位(detectBearing)为 key - **数据结构示例**: ```javascript { '0': { // detectBearing 值 left: [ // 左侧已选选项 { label: '评估项名称', value: 123, position: 'left', productEvaluationItemId: 123, partsId: 0, restrictedUndetectableToItem: 0, // ... 其他字段 } ], right: [] // 右侧待选选项(动态计算,不直接存储) }, '1': { left: [], right: [] } } ``` #### `cloudChildTabs` (computed) ```javascript const cloudChildTabs = computed(() => cloud_orientation.value) ``` - **类型**: `ComputedRef` - **作用**: 计算属性,返回云评估子级 tab 选项列表(来自字典 `cloud_orientation`) - **Vue 知识点**: - `computed()`: 创建计算属性,自动追踪依赖,缓存结果 - 当 `cloud_orientation.value` 变化时,`cloudChildTabs` 自动更新 #### `cloudBaseOptions` (ref) ```javascript const cloudBaseOptions = ref([]) ``` - **类型**: `Ref>` - **作用**: 存储从 API 获取的所有评估项候选数据(完整列表) - **数据结构**: ```javascript [ { label: '评估项名称', value: 123, // productEvaluationItemId position: 'right', operatingInstructions: '操作说明', restrictedUndetectableStitch: 1, noSuchPartStitch: 1, buttonStitch: 1, evaluationItemName: '评估项名称' } ] ``` #### `cloudLeftOptions` (ref) ```javascript const cloudLeftOptions = ref([]) ``` - **类型**: `Ref>` - **作用**: 当前活跃 tab 的左侧已选选项列表(用于穿梭框左侧显示) #### `cloudRightOptions` (ref) ```javascript const cloudRightOptions = ref([]) ``` - **类型**: `Ref>` - **作用**: 当前活跃 tab 的右侧待选选项列表(用于穿梭框右侧显示) - **注意**: 此列表是动态计算的,排除所有 tab 中已选的项 #### `cloudLeftSelected` / `cloudRightSelected` (ref) ```javascript const cloudLeftSelected = ref([]) // 左侧选中项(用于右移) const cloudRightSelected = ref([]) // 右侧选中项(用于左移) ``` - **类型**: `Ref>` - **作用**: 存储用户在穿梭框中选中的选项的 `value` 数组 #### `cloudTransferLoading` (ref) ```javascript const cloudTransferLoading = ref(false) ``` - **类型**: `Ref` - **作用**: 控制穿梭框加载状态,用于显示 loading 效果 --- ## 核心方法详解 ### 1. `getCloudPartsListData()` - 获取云评估零部件列表 ```javascript const getCloudPartsListData = async () => { cloudTransferLoading.value = true try { const res = await getEvaluationItemListAll() const list = res?.data || [] cloudBaseOptions.value = list.map((item) => ({ label: item.evaluationItemName || '', value: item.productEvaluationItemId, position: 'right', operatingInstructions: item.operatingInstructions, restrictedUndetectableStitch: item.restrictedUndetectableStitch, noSuchPartStitch: item.noSuchPartStitch, buttonStitch: item.buttonStitch, evaluationItemName: item.evaluationItemName || '' })) } finally { cloudTransferLoading.value = false } } ``` **作用**: 从 API 获取所有评估项数据,并转换为标准格式存储到 `cloudBaseOptions` **JavaScript 知识点**: - **`async/await`**: 异步函数语法,`await` 等待 Promise 完成 - **可选链操作符 `?.`**: `res?.data` 安全访问属性,如果 `res` 为 `null/undefined` 返回 `undefined` - **逻辑或 `||`**: `res?.data || []` 提供默认值,如果左侧为假值则使用右侧 - **`Array.map()`**: 数组映射方法,将每个元素转换为新格式 - **对象展开**: `{ ...item, newProp: value }` 创建新对象并添加/覆盖属性 - **`try...finally`**: 无论成功或失败都会执行 `finally` 中的代码,确保 loading 状态被重置 **执行流程**: 1. 设置 loading 为 `true` 2. 调用 API 获取数据 3. 使用 `map` 转换数据格式 4. 存储到 `cloudBaseOptions.value` 5. 无论成功失败,都设置 loading 为 `false` --- ### 2. `getUsedValuesAllTabs()` - 获取所有 tab 已选值的集合 ```javascript const getUsedValuesAllTabs = (data = cloudTabData.value) => { return Array.from( new Set( Object.values(data) .flatMap((val) => val?.left || []) .map((x) => x.value) ) ) } ``` **作用**: 收集所有 tab 中左侧已选选项的 `value`,返回去重后的数组 **JavaScript 知识点**: - **默认参数**: `data = cloudTabData.value` 提供默认值 - **`Object.values()`**: 获取对象所有值的数组 - **`Array.flatMap()`**: 扁平化映射,相当于 `map().flat()` ```javascript // flatMap 等价于 Object.values(data) .map((val) => val?.left || []) .flat() .map((x) => x.value) ``` - **可选链 `?.`**: `val?.left` 安全访问,如果 `val` 为 `null/undefined` 返回 `undefined` - **`Array.map()`**: 提取每个选项的 `value` - **`Set`**: ES6 集合,自动去重 - **`Array.from()`**: 将 Set 转换为数组 **示例**: ```javascript // 输入 cloudTabData.value = { '0': { left: [{ value: 1 }, { value: 2 }] }, '1': { left: [{ value: 2 }, { value: 3 }] } } // 执行过程 Object.values(data) // [{ left: [...] }, { left: [...] }] .flatMap((val) => val?.left || []) // [{ value: 1 }, { value: 2 }, { value: 2 }, { value: 3 }] .map((x) => x.value) // [1, 2, 2, 3] new Set(...) // Set {1, 2, 3} Array.from(...) // [1, 2, 3] ``` --- ### 3. `buildRightOptions()` - 构建右侧选项列表 ```javascript const buildRightOptions = (usedAll, base = cloudBaseOptions.value || []) => { return base.filter((opt) => !usedAll.includes(opt.value)).map((x) => ({ ...x })) } ``` **作用**: 从基础选项列表中排除已使用的选项,生成右侧待选列表 **JavaScript 知识点**: - **默认参数**: `base = cloudBaseOptions.value || []` 提供默认值 - **`Array.filter()`**: 过滤数组,返回满足条件的元素 - **`Array.includes()`**: 检查数组是否包含某个值 - **逻辑非 `!`**: `!usedAll.includes(opt.value)` 表示不在已用列表中 - **对象展开 `{ ...x }`**: 创建新对象副本,避免引用问题 **执行逻辑**: 1. 遍历 `base` 数组 2. 过滤出 `value` 不在 `usedAll` 中的选项 3. 使用展开运算符创建新对象(浅拷贝) --- ### 4. `resetRightOptionsForAllTabs()` - 重置所有 tab 的右侧选项 ```javascript const resetRightOptionsForAllTabs = () => { const base = cloudBaseOptions.value || [] const entries = Object.entries(cloudTabData.value) const usedAll = getUsedValuesAllTabs() if (!entries.length) { const firstKey = cloudChildTabs.value?.[0]?.value cloudTabData.value = firstKey ? { [firstKey]: { left: [], right: buildRightOptions(usedAll, base) } } : {} } else { cloudTabData.value = entries.reduce((acc, [key, val]) => { acc[key] = { left: val.left || [], right: buildRightOptions(usedAll, base) } return acc }, {}) } } ``` **作用**: 重新计算所有 tab 的右侧选项列表,确保右侧不显示任何 tab 已选的项 **JavaScript 知识点**: - **`Object.entries()`**: 将对象转换为 `[key, value]` 数组 ```javascript { a: 1, b: 2 } => [['a', 1], ['b', 2]] ``` - **可选链和数组访问**: `cloudChildTabs.value?.[0]?.value` - `?.` 安全访问,如果 `cloudChildTabs.value` 为 `null/undefined` 返回 `undefined` - `[0]` 访问数组第一个元素 - **计算属性名**: `{ [firstKey]: ... }` ES6 语法,动态设置对象 key - **三元运算符**: `condition ? value1 : value2` 条件表达式 - **`Array.reduce()`**: 数组归约方法,将数组累积为单个值 ```javascript entries.reduce((acc, [key, val]) => { // acc: 累积器(初始值为 {}) // [key, val]: 当前项的解构 acc[key] = { ... } return acc // 返回累积器,作为下一次的 acc }, {}) ``` **执行流程**: 1. 获取基础选项和已用值列表 2. 如果 `cloudTabData` 为空,初始化第一个 tab 3. 否则遍历所有 tab,重新计算每个 tab 的右侧选项 --- ### 5. `loadActiveTabData()` - 加载当前活跃 tab 的数据 ```javascript const loadActiveTabData = (detectBearing) => { const base = cloudBaseOptions.value || [] if (!cloudTabData.value[detectBearing]) { cloudTabData.value[detectBearing] = { left: [], right: [] } } const current = cloudTabData.value[detectBearing] const usedAll = getUsedValuesAllTabs() cloudLeftOptions.value = current.left || [] cloudRightOptions.value = buildRightOptions(usedAll, base) cloudLeftSelected.value = [] cloudRightSelected.value = [] } ``` **作用**: 将指定 tab 的数据加载到当前显示的变量中(`cloudLeftOptions`、`cloudRightOptions`) **JavaScript 知识点**: - **参数**: `detectBearing` 检测方位值(如 '0', '1') - **对象属性访问**: `cloudTabData.value[detectBearing]` 动态访问对象属性 - **逻辑或默认值**: `current.left || []` 如果 `left` 为假值则使用空数组 **执行流程**: 1. 如果该 tab 不存在,初始化空数据 2. 获取当前 tab 的数据 3. 计算所有已用值 4. 设置左侧选项(直接使用存储的值) 5. 设置右侧选项(动态计算,排除所有已用值) 6. 清空选中状态 --- ### 6. `persistActiveTabData()` - 持久化当前活跃 tab 的数据 ```javascript const persistActiveTabData = () => { const key = cloudActiveChildIndex.value if (!key) return cloudTabData.value[key] = { left: cloudLeftOptions.value || [], right: cloudRightOptions.value || [] } } ``` **作用**: 将当前显示的选项(`cloudLeftOptions`、`cloudRightOptions`)保存回 `cloudTabData` **JavaScript 知识点**: - **早期返回**: `if (!key) return` 如果 key 不存在则提前退出 - **对象属性赋值**: `cloudTabData.value[key] = ...` 动态设置对象属性 **使用场景**: 在切换 tab 或移动选项后调用,确保数据同步 --- ### 7. `handleCloudTabChange()` - 处理云评估 tab 切换 ```javascript const handleCloudTabChange = (val) => { if (val !== undefined && val !== null) { cloudActiveChildIndex.value = val } loadActiveTabData(cloudActiveChildIndex.value) } ``` **作用**: 切换云评估子级 tab 时调用,更新当前 tab 并加载对应数据 **JavaScript 知识点**: - **严格相等检查**: `val !== undefined && val !== null` 检查值是否有效 - **条件更新**: 只有当 `val` 有效时才更新 `cloudActiveChildIndex` **执行流程**: 1. 如果传入值有效,更新当前 tab 索引 2. 加载对应 tab 的数据 --- ### 8. `moveRightToLeft()` - 从右侧移动到左侧 ```javascript const moveRightToLeft = () => { if (!cloudRightSelected.value.length) return const toMove = cloudRightOptions.value.filter((opt) => cloudRightSelected.value.includes(opt.value) ) const remainedRight = cloudRightOptions.value.filter( (opt) => !cloudRightSelected.value.includes(opt.value) ) const newLeft = [ ...cloudLeftOptions.value, ...toMove.map((x) => ({ ...x, position: 'left' })) ] cloudLeftOptions.value = newLeft cloudRightOptions.value = remainedRight cloudLeftSelected.value = [] cloudRightSelected.value = [] persistActiveTabData() resetRightOptionsForAllTabs() loadActiveTabData(cloudActiveChildIndex.value) } ``` **作用**: 将右侧选中的选项移动到左侧(添加到已选列表) **JavaScript 知识点**: - **早期返回**: `if (!cloudRightSelected.value.length) return` 如果没有选中项则退出 - **`Array.filter()`**: 过滤出需要移动的选项和剩余的选项 - **`Array.includes()`**: 检查选中列表是否包含某个值 - **展开运算符 `...`**: - `...cloudLeftOptions.value` 展开数组元素 - `[...arr1, ...arr2]` 合并数组 - **`Array.map()`**: 将移动的选项的 `position` 改为 `'left'` - **对象展开**: `{ ...x, position: 'left' }` 创建新对象并覆盖 `position` **执行流程**: 1. 检查是否有选中项 2. 分离出要移动的选项和剩余的右侧选项 3. 将移动的选项添加到左侧,并更新 `position` 4. 更新 `cloudLeftOptions` 和 `cloudRightOptions` 5. 清空选中状态 6. 持久化数据 7. 重置所有 tab 的右侧选项(因为已用列表变化) 8. 重新加载当前 tab 数据 --- ### 9. `moveLeftToRight()` - 从左侧移动到右侧 ```javascript const moveLeftToRight = () => { if (!cloudLeftSelected.value.length) return const toMove = cloudLeftOptions.value.filter((opt) => cloudLeftSelected.value.includes(opt.value) ) const remainedLeft = cloudLeftOptions.value.filter( (opt) => !cloudLeftSelected.value.includes(opt.value) ) const newRight = [ ...cloudRightOptions.value, ...toMove.map((x) => ({ ...x, position: 'right' })) ] cloudLeftOptions.value = remainedLeft cloudRightOptions.value = newRight cloudLeftSelected.value = [] cloudRightSelected.value = [] persistActiveTabData() resetRightOptionsForAllTabs() loadActiveTabData(cloudActiveChildIndex.value) } ``` **作用**: 将左侧选中的选项移回右侧(从已选列表移除) **执行逻辑**: 与 `moveRightToLeft()` 类似,方向相反 --- ### 10. `handleCloudActionClick()` - 处理按钮操作点击 ```javascript const handleCloudActionClick = ({ item, action, value }) => { const fieldMap = { '是': 'yesButtonToItem', '否': 'noButtonToItem', '下一项': 'nextButtonSwitchToItem', '受限': 'restrictedUndetectableToItem', '无部件': 'noSuchPartToItem' } const fieldName = fieldMap[action] if (!fieldName) return const index = cloudLeftOptions.value.findIndex((opt) => opt.value === item.value) if (index !== -1) { cloudLeftOptions.value[index] = { ...cloudLeftOptions.value[index], [fieldName]: (value && value !== null && value !== undefined) ? value : 0 } persistActiveTabData() } } ``` **作用**: 处理用户点击操作按钮(如"是"、"否"等),更新对应选项的字段值 **JavaScript 知识点**: - **对象解构**: `{ item, action, value }` 从参数对象中解构属性 - **对象映射**: `fieldMap` 将中文操作名映射到字段名 - **`Object[key]` 访问**: `fieldMap[action]` 动态访问对象属性 - **`Array.findIndex()`**: 查找数组中满足条件的元素的索引,未找到返回 `-1` - **计算属性名**: `[fieldName]: value` 动态设置对象属性名 - **三元运算符**: `condition ? value : 0` 条件赋值 - **逻辑与**: `value && value !== null && value !== undefined` 检查值是否有效 - **对象展开**: `{ ...cloudLeftOptions.value[index], [fieldName]: ... }` 创建新对象并更新字段 **执行流程**: 1. 根据 `action` 查找对应的字段名 2. 如果字段名不存在则退出 3. 在 `cloudLeftOptions` 中查找对应选项 4. 如果找到,更新字段值(有效值使用原值,否则设为 0) 5. 持久化数据 --- ### 11. `handleCloudSave()` - 保存云评估数据 ```javascript const handleCloudSave = async () => { persistActiveTabData() const evaluationItemGroupList = Object.entries(cloudTabData.value).map( ([detectBearing, val]) => { const options = val.left || [] const evaluationItemList = options.map((x) => ({ productEvaluationItemId: x.value || 0, partsId: x.partsId ?? 0, restrictedUndetectableToItem: x.restrictedUndetectableToItem ?? 0, noSuchPartToItem: x.noSuchPartToItem ?? 0, nextButtonSwitchToItem: x.nextButtonSwitchToItem ?? 0, yesButtonToItem: x.yesButtonToItem ?? 0, noButtonToItem: x.noButtonToItem ?? 0, coatingThicknessMax: x.coatingThicknessMax ?? 0, coatingThicknessMin: x.coatingThicknessMin ?? 0, evaluationItemName: x.evaluationItemName || '' })) return { detectBearing, evaluationItemName: options.length > 0 ? options[0].evaluationItemName || '' : '', evaluationItemList } } ) try { proxy.$modal.loading('保存中...') const apiMethod = isEdit ? editDetectionProtocol : addDetectionProtocol await apiMethod({ ...form.value, evaluationItemGroupList }) proxy.$message.success('保存成功') proxy.$tab.closeOpenPage({ name: 'Detection' }) } catch (err) { console.error(err) } finally { proxy.$modal.closeLoading() } } ``` **作用**: 将云评估数据转换为 API 所需格式并保存 **JavaScript 知识点**: - **`Object.entries()`**: 将对象转换为 `[key, value]` 数组 - **数组解构**: `[detectBearing, val]` 解构数组元素 - **空值合并运算符 `??`**: `x.partsId ?? 0` 如果左侧为 `null` 或 `undefined` 则使用右侧 - 与 `||` 的区别:`??` 只对 `null/undefined` 生效,`||` 对所有假值生效 - **逻辑或 `||`**: `x.value || 0` 如果为假值则使用 0 - **条件表达式**: `options.length > 0 ? ... : ''` 根据条件返回不同值 - **对象展开**: `{ ...form.value, evaluationItemGroupList }` 合并对象 - **三元运算符**: `isEdit ? editDetectionProtocol : addDetectionProtocol` 根据条件选择函数 - **`try...catch...finally`**: 错误处理和资源清理 **数据转换**: ```javascript // 输入 cloudTabData.value { '0': { left: [ { value: 123, partsId: 456, yesButtonToItem: 789, ... } ] } } // 输出 evaluationItemGroupList [ { detectBearing: '0', evaluationItemName: '评估项名称', evaluationItemList: [ { productEvaluationItemId: 123, partsId: 456, yesButtonToItem: 789, ... } ] } ] ``` --- ### 12. `initCloud()` - 初始化云评估 ```javascript const initCloud = async () => { proxy.$modal.loading('加载中···') try { if (!cloudBaseOptions.value.length) { await getCloudPartsListData() } const [first] = cloud_orientation.value || [] cloudActiveChildIndex.value = first?.value || '0' loadActiveTabData(cloudActiveChildIndex.value) } catch (error) { console.log(error) } finally { proxy.$modal.closeLoading() } } ``` **作用**: 初始化云评估功能,加载数据并设置默认 tab **JavaScript 知识点**: - **数组解构**: `const [first] = array` 获取数组第一个元素 - **可选链**: `first?.value` 安全访问属性 - **条件加载**: `if (!cloudBaseOptions.value.length)` 只在数据为空时加载 **执行流程**: 1. 显示 loading 2. 如果基础选项为空,加载数据 3. 获取第一个 tab 的值 4. 设置当前 tab 并加载数据 5. 无论成功失败,关闭 loading --- ### 13. `getDetailData()` 中的云评估数据处理 在 `getDetailData()` 方法中(第 545-590 行),处理编辑模式下的云评估数据: ```javascript //云评估数据 const everyoneEvalGroupList = data.evaluationItemGroupList || [] cloudTabData.value = {} if (Array.isArray(everyoneEvalGroupList) && everyoneEvalGroupList.length) { everyoneEvalGroupList.forEach((group, groupIndex) => { const detectBearing = group.detectBearing const itemList = group['evaluationItemList'] || [] const left = itemList.map((item) => { const matchId = item.productEvaluationItemId const base = cloudBaseOptions.value.find((x) => String(x.value) === String(matchId)) || {} return { label: base.label || '', value: base.value, position: 'left', // ... 合并 base 和 item 的字段 productEvaluationItemId: item.productEvaluationItemId || base.value, partsId: item.partsId ?? 0, // ... 其他字段 } }) cloudTabData.value[detectBearing] = { left: left, right: [] } }) } resetRightOptionsForAllTabs() const firstKey = cloudChildTabs.value?.[0]?.value ?? '0' cloudActiveChildIndex.value = firstKey loadActiveTabData(firstKey) ``` **作用**: 将 API 返回的云评估数据转换为内部数据结构 **JavaScript 知识点**: - **`Array.isArray()`**: 检查是否为数组 - **逻辑与 `&&`**: `Array.isArray(...) && array.length` 确保是数组且有元素 - **`Array.forEach()`**: 遍历数组 - **对象属性访问**: `group['evaluationItemList']` 使用字符串访问属性 - **`Array.find()`**: 查找数组中满足条件的第一个元素,未找到返回 `undefined` - **类型转换**: `String(x.value) === String(matchId)` 转换为字符串比较 - **空值合并**: `item.partsId ?? 0` 如果为 `null/undefined` 则使用 0 - **逻辑或**: `item.productEvaluationItemId || base.value` 如果为假值则使用默认值 --- ## 数据流转流程 ### 1. 初始化流程 ``` initCloud() ↓ getCloudPartsListData() → cloudBaseOptions ↓ 设置默认 tab → cloudActiveChildIndex ↓ loadActiveTabData() → cloudLeftOptions, cloudRightOptions ``` ### 2. Tab 切换流程 ``` handleCloudTabChange(val) ↓ 更新 cloudActiveChildIndex ↓ loadActiveTabData() ↓ 从 cloudTabData 读取数据 ↓ 计算右侧选项(排除所有已用项) ↓ 更新 cloudLeftOptions, cloudRightOptions ``` ### 3. 选项移动流程(右→左) ``` 用户选择右侧选项 → cloudRightSelected ↓ moveRightToLeft() ↓ 过滤出要移动的选项 ↓ 添加到 cloudLeftOptions ↓ 从 cloudRightOptions 移除 ↓ persistActiveTabData() → 保存到 cloudTabData ↓ resetRightOptionsForAllTabs() → 重新计算所有右侧选项 ↓ loadActiveTabData() → 刷新当前 tab 显示 ``` ### 4. 保存流程 ``` handleCloudSave() ↓ persistActiveTabData() → 确保数据最新 ↓ Object.entries(cloudTabData) → 遍历所有 tab ↓ 转换为 API 格式 → evaluationItemGroupList ↓ 调用 API 保存 ``` --- ## JavaScript/Vue 知识点 ### Vue 3 Composition API #### 1. `ref()` - 响应式引用 ```javascript const count = ref(0) // 访问: count.value // 模板中自动解包: {{ count }} ``` #### 2. `computed()` - 计算属性 ```javascript const doubled = computed(() => count.value * 2) // 自动追踪依赖,缓存结果 ``` #### 3. `watch()` - 监听器 ```javascript watch(() => count.value, (newVal, oldVal) => { // 响应变化 }) ``` ### ES6+ JavaScript 特性 #### 1. 解构赋值 ```javascript // 数组解构 const [first, second] = array const [first] = array // 只要第一个 // 对象解构 const { name, age } = obj const { name: userName } = obj // 重命名 ``` #### 2. 展开运算符 ```javascript // 数组展开 const newArr = [...arr1, ...arr2] // 对象展开 const newObj = { ...obj1, ...obj2, newProp: value } ``` #### 3. 可选链 `?.` ```javascript obj?.prop // 如果 obj 为 null/undefined 返回 undefined arr?.[0] // 安全访问数组 func?.() // 安全调用函数 ``` #### 4. 空值合并 `??` ```javascript value ?? defaultValue // 只在 null/undefined 时使用默认值 // 与 || 的区别 0 || 10 // 10 (0 是假值) 0 ?? 10 // 0 (0 不是 null/undefined) ``` #### 5. 数组方法 **`map()`** - 映射 ```javascript [1, 2, 3].map(x => x * 2) // [2, 4, 6] ``` **`filter()`** - 过滤 ```javascript [1, 2, 3].filter(x => x > 1) // [2, 3] ``` **`find()`** - 查找 ```javascript [1, 2, 3].find(x => x > 1) // 2 ``` **`findIndex()`** - 查找索引 ```javascript [1, 2, 3].findIndex(x => x > 1) // 1 ``` **`flatMap()`** - 扁平化映射 ```javascript [[1, 2], [3, 4]].flatMap(x => x) // [1, 2, 3, 4] ``` **`reduce()`** - 归约 ```javascript [1, 2, 3].reduce((acc, x) => acc + x, 0) // 6 ``` **`includes()`** - 包含检查 ```javascript [1, 2, 3].includes(2) // true ``` #### 6. 对象方法 **`Object.values()`** - 获取所有值 ```javascript Object.values({ a: 1, b: 2 }) // [1, 2] ``` **`Object.entries()`** - 获取键值对数组 ```javascript Object.entries({ a: 1, b: 2 }) // [['a', 1], ['b', 2]] ``` **`Object.assign()`** - 合并对象 ```javascript Object.assign({}, obj1, obj2) // 合并到新对象 ``` #### 7. Set 和 Array.from ```javascript const unique = Array.from(new Set([1, 2, 2, 3])) // [1, 2, 3] ``` #### 8. 异步处理 **`async/await`** ```javascript async function fetchData() { const res = await api.getData() return res.data } ``` **`try...catch...finally`** ```javascript try { // 可能出错的代码 } catch (error) { // 错误处理 } finally { // 无论成功失败都执行 } ``` #### 9. 条件表达式 ```javascript // 三元运算符 const result = condition ? value1 : value2 // 逻辑与(短路) condition && doSomething() // 逻辑或(默认值) value || defaultValue ``` #### 10. 类型转换 ```javascript String(value) // 转字符串 Number(value) // 转数字 Boolean(value) // 转布尔值 ``` --- ## 总结 云评估功能的核心设计思路: 1. **数据分离**: `cloudTabData` 存储所有 tab 的数据,`cloudLeftOptions/cloudRightOptions` 只存储当前显示 tab 的数据 2. **动态计算**: 右侧选项列表动态计算,排除所有 tab 已选的项,确保不重复 3. **数据同步**: 通过 `persistActiveTabData()` 和 `loadActiveTabData()` 保持数据同步 4. **响应式更新**: 使用 Vue 3 的响应式 API,数据变化自动更新 UI 关键方法调用链: - **初始化**: `initCloud()` → `getCloudPartsListData()` → `loadActiveTabData()` - **切换 tab**: `handleCloudTabChange()` → `loadActiveTabData()` - **移动选项**: `moveRightToLeft()` / `moveLeftToRight()` → `persistActiveTabData()` → `resetRightOptionsForAllTabs()` → `loadActiveTabData()` - **保存**: `handleCloudSave()` → 数据转换 → API 调用