缓存字段和简历字段冲突时的逻辑优化
This commit is contained in:
@@ -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,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<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 下查找
|
||||
|
||||
@@ -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,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<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
@@ -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 场景:特殊超长延时(谨慎使用)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user