缓存字段和简历字段冲突时的逻辑优化

This commit is contained in:
2026-07-31 19:38:47 +08:00
parent 0c8193d230
commit 07ba286d34
3 changed files with 120 additions and 14 deletions
+58 -5
View File
@@ -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<string, HTMLInputElement | HTMLTextAreaElement>()
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,9 +1146,45 @@ export async function handleAutoFillBeisen(params: AutoFillBeisenParams): Promis
if (oldSecIdx < 0) {
oldSections.push(newSec)
} else {
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
}
}
}
existing.unfilledFormData = oldSections
existing.timestamp = Date.now()
@@ -1197,6 +1240,8 @@ interface FillCachedDataParams {
usedInputs: Set<Element>
expandedResults: ExperienceSectionLocateResult[]
sectionResults: ExperienceSectionLocateResult[]
/** 阶段B中简历无值未填写的非经历字段(标签→input映射),B2用缓存值填写这些字段 */
unfilledMainFieldMap?: Map<string, HTMLInputElement | HTMLTextAreaElement>
}
/** 阶段B2 结果 */
@@ -1466,7 +1511,7 @@ async function b2FillBeisenRadioGroup(
* 阶段B2 主流程:从缓存读取 unfilledFormData,填写有值的字段
*/
async function handleFillCachedData(params: FillCachedDataParams): Promise<FillCachedDataResult> {
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<any>("offerpie_unfilled_form")
@@ -1618,7 +1663,15 @@ async function handleFillCachedData(params: FillCachedDataParams): Promise<FillC
console.log(` [B2] "${sectionData.title}" (非经历) 缓存${fields.length}个字段`)
for (const field of fields) {
if (!field.value) { result.skipped++; continue }
const inputEl = b2FindInputByLabel(field.label, titleEl, nextTitleEl, usedInputs)
let inputEl = b2FindInputByLabel(field.label, titleEl, nextTitleEl, usedInputs)
// fallback:如果 b2FindInputByLabel 找不到,从阶段B传入的未填简历字段映射中查找
if (!inputEl && unfilledMainFieldMap) {
const fallbackEl = unfilledMainFieldMap.get(field.label)
if (fallbackEl && !usedInputs.has(fallbackEl)) {
inputEl = fallbackEl
console.log(` [B2] "${field.label}" 通过 unfilledMainFieldMap fallback 定位到 input`)
}
}
if (inputEl) {
// 时间/日期字段走阶段A同款流程:直接点击 input → fillDatePicker,不经过 detectPickerField
if (b2IsDateTimeLabel(field.label)) {
@@ -1742,7 +1795,7 @@ export async function fillBeisenCascadePicker(
forceSetValue(searchInput, fillValue)
searchInput.dispatchEvent(new Event("input", { bubbles: true }))
searchInput.dispatchEvent(new Event("change", { bubbles: true }))
await delay("mid") // 等待搜索结果刷新(太短会导致选项列表未更新,选到错误选项
await delay("high") // 等待搜索结果刷新(地区类级联数据量大,需要更长等待时间
// 6. 检测选项集合位置(两种情况)
// 在 left-container 下查找
+57 -4
View File
@@ -428,8 +428,15 @@ export async function handleAutoFillCommon(params: AutoFillCommonParams): Promis
// 【独立步骤】注释下面这段即可禁用阶段B2
{
const resumeName = currentResumeData?.main?.resumeName || currentResumeData?.main?.name || ""
// 构建阶段B中简历无值未填写的非经历字段映射(标签文字→input元素),传给B2作为 fallback 定位
const unfilledMainFieldMap = new Map<string, HTMLInputElement | HTMLTextAreaElement>()
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,9 +1081,45 @@ export async function handleAutoFillCommon(params: AutoFillCommonParams): Promis
if (oldSecIdx < 0) {
oldSections.push(newSec)
} else {
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
}
}
}
existing.unfilledFormData = oldSections
existing.timestamp = Date.now()
@@ -1132,6 +1175,8 @@ interface FillCachedDataParams {
usedInputs: Set<Element>
expandedResults: ExperienceSectionLocateResult[]
sectionResults: ExperienceSectionLocateResult[]
/** 阶段B中简历无值未填写的非经历字段(标签→input映射),B2用缓存值填写这些字段 */
unfilledMainFieldMap?: Map<string, HTMLInputElement | HTMLTextAreaElement>
}
/** 阶段B2 结果 */
@@ -1310,7 +1355,7 @@ async function b2FillDateTimeField(
* 阶段B2 主流程:从缓存读取 unfilledFormData,填写有值的字段
*/
async function handleFillCachedData(params: FillCachedDataParams): Promise<FillCachedDataResult> {
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<any>("offerpie_unfilled_form")
@@ -1454,7 +1499,15 @@ async function handleFillCachedData(params: FillCachedDataParams): Promise<FillC
console.log(` [B2] "${sectionData.title}" (非经历) 缓存${fields.length}个字段`)
for (const field of fields) {
if (!field.value) { result.skipped++; continue }
const inputEl = b2FindInputByLabel(field.label, titleEl, nextTitleEl, usedInputs)
let inputEl = b2FindInputByLabel(field.label, titleEl, nextTitleEl, usedInputs)
// fallback:如果 b2FindInputByLabel 找不到,从阶段B传入的未填简历字段映射中查找
if (!inputEl && unfilledMainFieldMap) {
const fallbackEl = unfilledMainFieldMap.get(field.label)
if (fallbackEl && !usedInputs.has(fallbackEl)) {
inputEl = fallbackEl
console.log(` [B2] "${field.label}" 通过 unfilledMainFieldMap fallback 定位到 input`)
}
}
if (!inputEl) { result.skipped++; continue }
// 时间/日期字段走阶段A同款流程:直接点击 input → fillDatePicker,不经过 detectPickerField
if (b2IsDateTimeLabel(field.label)) {
+3 -3
View File
@@ -8,9 +8,9 @@ export type DelayLevel = "low" | "mid" | "high" | "max"
/** 各等级对应的毫秒数(统一在此处调整) */
const DELAY_MS: Record<DelayLevel, number> = {
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 场景:特殊超长延时(谨慎使用)
}