diff --git a/src/handlers/handleAutoFillBeisen.ts b/src/handlers/handleAutoFillBeisen.ts index f88d164..f3a6b5f 100644 --- a/src/handlers/handleAutoFillBeisen.ts +++ b/src/handlers/handleAutoFillBeisen.ts @@ -444,8 +444,15 @@ export async function handleAutoFillBeisen(params: AutoFillBeisenParams): Promis // 【独立步骤】注释下面这段即可禁用阶段B2 { const resumeName = currentResumeData?.main?.resumeName || currentResumeData?.main?.name || "" + // 构建阶段B中简历无值未填写的非经历字段映射(标签文字→input元素),传给B2作为 fallback 定位 + const unfilledMainFieldMap = new Map() + for (const f of mainFields) { + if (!f.fillValue && f.inputElement && !usedInputs.has(f.inputElement)) { + unfilledMainFieldMap.set(f.labelText, f.inputElement) + } + } const b2Result = await handleFillCachedData({ - lang, resumeName, usedInputs, expandedResults, sectionResults, + lang, resumeName, usedInputs, expandedResults, sectionResults, unfilledMainFieldMap, }) result.success += b2Result.success result.failed += b2Result.failed @@ -1130,7 +1137,7 @@ export async function handleAutoFillBeisen(params: AutoFillBeisenParams): Promis let cacheData: any = null if (existing && existing.resumeName === resumeName && existing.unfilledFormData) { - // 同一份简历,用新数据替换同名 section(保留其他平台的 section) + // 同一份简历,按字段级合并(非空值才更新,保留旧缓存中已有的非空值不被覆盖) const oldSections = existing.unfilledFormData as any[] const newSections = unfilledFormData as any[] @@ -1139,7 +1146,43 @@ export async function handleAutoFillBeisen(params: AutoFillBeisenParams): Promis if (oldSecIdx < 0) { oldSections.push(newSec) } else { - oldSections[oldSecIdx] = newSec + const oldSec = oldSections[oldSecIdx] + if (newSec.isExperience && oldSec.isExperience) { + // 经历类型:按段合并,保留旧字段 + const oldSegments = oldSec.formItems as any[][] + const newSegments = newSec.formItems as any[][] + for (let sIdx = 0; sIdx < newSegments.length; sIdx++) { + if (sIdx >= oldSegments.length) { + oldSegments.push(newSegments[sIdx]) + } else { + const oldFields = oldSegments[sIdx] + const newFields = newSegments[sIdx] + for (const nf of newFields) { + const of_ = oldFields.find((f: any) => f.label === nf.label) + if (of_) { + if (nf.value) of_.value = nf.value + } else { + oldFields.push(nf) + } + } + } + } + } else if (!newSec.isExperience && !oldSec.isExperience) { + // 非经历类型:按字段合并,只有非空值才更新,旧值不丢失 + const oldFields = oldSec.formItems as any[] + const newFields = newSec.formItems as any[] + for (const nf of newFields) { + const of_ = oldFields.find((f: any) => f.label === nf.label) + if (of_) { + if (nf.value) of_.value = nf.value + } else { + oldFields.push(nf) + } + } + } else { + // 类型变化(经历↔非经历),直接替换 + oldSections[oldSecIdx] = newSec + } } } @@ -1197,6 +1240,8 @@ interface FillCachedDataParams { usedInputs: Set expandedResults: ExperienceSectionLocateResult[] sectionResults: ExperienceSectionLocateResult[] + /** 阶段B中简历无值未填写的非经历字段(标签→input映射),B2用缓存值填写这些字段 */ + unfilledMainFieldMap?: Map } /** 阶段B2 结果 */ @@ -1466,7 +1511,7 @@ async function b2FillBeisenRadioGroup( * 阶段B2 主流程:从缓存读取 unfilledFormData,填写有值的字段 */ async function handleFillCachedData(params: FillCachedDataParams): Promise { - const { lang, resumeName, usedInputs, expandedResults, sectionResults } = params + const { lang, resumeName, usedInputs, expandedResults, sectionResults, unfilledMainFieldMap } = params const result: FillCachedDataResult = { success: 0, failed: 0, skipped: 0 } const cacheData = await storageGet("offerpie_unfilled_form") @@ -1618,7 +1663,15 @@ async function handleFillCachedData(params: FillCachedDataParams): Promise() + for (const f of mainFields) { + if (!f.fillValue && f.inputElement && !usedInputs.has(f.inputElement)) { + unfilledMainFieldMap.set(f.labelText, f.inputElement) + } + } const b2Result = await handleFillCachedData({ - lang, resumeName, usedInputs, expandedResults, sectionResults, + lang, resumeName, usedInputs, expandedResults, sectionResults, unfilledMainFieldMap, }) result.success += b2Result.success result.failed += b2Result.failed @@ -1065,7 +1072,7 @@ export async function handleAutoFillCommon(params: AutoFillCommonParams): Promis let cacheData: any = null if (existing && existing.resumeName === resumeName && existing.unfilledFormData) { - // 同一份简历,用新数据替换同名 section(保留其他平台的 section) + // 同一份简历,按字段级合并(非空值才更新,保留旧缓存中已有的非空值不被覆盖) const oldSections = existing.unfilledFormData as any[] const newSections = unfilledFormData as any[] @@ -1074,7 +1081,43 @@ export async function handleAutoFillCommon(params: AutoFillCommonParams): Promis if (oldSecIdx < 0) { oldSections.push(newSec) } else { - oldSections[oldSecIdx] = newSec + const oldSec = oldSections[oldSecIdx] + if (newSec.isExperience && oldSec.isExperience) { + // 经历类型:按段合并,保留旧字段 + const oldSegments = oldSec.formItems as any[][] + const newSegments = newSec.formItems as any[][] + for (let sIdx = 0; sIdx < newSegments.length; sIdx++) { + if (sIdx >= oldSegments.length) { + oldSegments.push(newSegments[sIdx]) + } else { + const oldFields = oldSegments[sIdx] + const newFields = newSegments[sIdx] + for (const nf of newFields) { + const of_ = oldFields.find((f: any) => f.label === nf.label) + if (of_) { + if (nf.value) of_.value = nf.value + } else { + oldFields.push(nf) + } + } + } + } + } else if (!newSec.isExperience && !oldSec.isExperience) { + // 非经历类型:按字段合并,只有非空值才更新,旧值不丢失 + const oldFields = oldSec.formItems as any[] + const newFields = newSec.formItems as any[] + for (const nf of newFields) { + const of_ = oldFields.find((f: any) => f.label === nf.label) + if (of_) { + if (nf.value) of_.value = nf.value + } else { + oldFields.push(nf) + } + } + } else { + // 类型变化(经历↔非经历),直接替换 + oldSections[oldSecIdx] = newSec + } } } @@ -1132,6 +1175,8 @@ interface FillCachedDataParams { usedInputs: Set expandedResults: ExperienceSectionLocateResult[] sectionResults: ExperienceSectionLocateResult[] + /** 阶段B中简历无值未填写的非经历字段(标签→input映射),B2用缓存值填写这些字段 */ + unfilledMainFieldMap?: Map } /** 阶段B2 结果 */ @@ -1310,7 +1355,7 @@ async function b2FillDateTimeField( * 阶段B2 主流程:从缓存读取 unfilledFormData,填写有值的字段 */ async function handleFillCachedData(params: FillCachedDataParams): Promise { - const { lang, resumeName, usedInputs, expandedResults, sectionResults } = params + const { lang, resumeName, usedInputs, expandedResults, sectionResults, unfilledMainFieldMap } = params const result: FillCachedDataResult = { success: 0, failed: 0, skipped: 0 } const cacheData = await storageGet("offerpie_unfilled_form") @@ -1454,7 +1499,15 @@ async function handleFillCachedData(params: FillCachedDataParams): Promise = { - low: 10, // <100ms 场景:微等待,点击后极短暂停 - mid: 50, // 100~300ms 场景:等待 DOM 更新、弹出层渲染 - high: 500, // >300ms 场景:等待接口返回、搜索结果、动画完成 + low: 10, // 原<100ms 场景:微等待,点击后极短暂停 + mid: 30, // 原100~300ms 场景:等待 DOM 更新、弹出层渲染 + high: 500, // 原>300ms 场景:等待接口返回、搜索结果、动画完成 max: 2000, // >2000ms 场景:特殊超长延时(谨慎使用) }