输入框修改颜色微调,弹出层下拉选项数据收集修改关闭弹出层的办法,飞书经历添加按钮添加专属兼容

This commit is contained in:
2026-08-05 17:35:31 +08:00
parent e4e3a2683b
commit 69c77dc7ac
3 changed files with 66 additions and 4 deletions
+40 -1
View File
@@ -16,6 +16,7 @@
import { EXPERIENCE_SECTION_CONFIGS, JOB_FORM_LABELS } from "./constants"
import { delay } from "~utils/delay"
import { simulateClick } from "~utils/domEvent"
import { snapshotElementsInRange, diffSnapshots, findTopLevelNewElements } from "./dom"
import { findNearestInput } from "./formMatcher"
import type { ExperienceSection, ExperienceSectionConfig, ResumeData } from "./types"
@@ -732,12 +733,50 @@ function getDirectText(el: Element): string {
*
* 【注意】验证 fullText 不能包含其他经历类型的标题关键词,防止跨区域误匹配
*/
/**
* 【飞书专用】在大标题到下一大标题范围内查找添加按钮
* 飞书招聘平台的添加按钮使用 ud__button ud__button--text 类名的 button 标签
* 通用 findAddButton 在飞书上第二次定位时会误匹配到包含字段文字的大容器,因此飞书走此独立逻辑
*
* @param titleEl - 当前经历区块的大标题元素
* @param nextTitleEl - 下一个大标题元素(范围边界)
* @returns 找到的添加按钮 button 元素,未找到返回 null
*/
function findFeishuAddButton(titleEl: Element, nextTitleEl: Element | null): Element | null {
// 在范围内查找所有 button.ud__button.ud__button--text
const allButtons = document.querySelectorAll("button.ud__button.ud__button--text")
for (const btn of Array.from(allButtons)) {
// 判断是否在 titleEl 之后
const afterTitle = !!(titleEl.compareDocumentPosition(btn) & Node.DOCUMENT_POSITION_FOLLOWING)
if (!afterTitle) continue
// 判断是否在 nextTitleEl 之前(如果有下一大标题)
if (nextTitleEl) {
const beforeNext = !!(nextTitleEl.compareDocumentPosition(btn) & Node.DOCUMENT_POSITION_PRECEDING)
if (!beforeNext) continue
}
// 确认按钮文字含"添加"关键词(排除删除等其他操作按钮)
const btnText = btn.textContent?.trim() || ""
if (btnText.includes("添加") || btnText.includes("Add")) {
return btn
}
}
return null
}
export function findAddButton(
titleEl: Element,
nextTitleEl: Element | null,
config: ExperienceSectionConfig,
lang: "zh" | "en"
): Element | null {
// 【飞书特有逻辑】飞书招聘平台的添加按钮是 button.ud__button.ud__button--text
// 通用逻辑在飞书上第二次查找会误匹配到整个段容器,因此飞书走独立的定位策略
if (window.location.hostname.includes("feishu.cn")) {
const feishuBtn = findFeishuAddButton(titleEl, nextTitleEl)
if (feishuBtn) return feishuBtn
}
const addKeywords = lang === "zh" ? config.addButtonZh : config.addButtonEn
// 当前经历类型的标题关键词(用于验证按钮确实属于本区块)
const sectionKeywords = lang === "zh" ? config.zh : config.en
@@ -842,7 +881,7 @@ export async function clickAddButton(
// 逐层点击,每层点击后检测 input 数量是否增加
for (const target of clickTargets) {
target.click()
simulateClick(target)
await delay("high")
const afterInputs = findInputsInRange(titleEl, nextTitleEl)
+25 -2
View File
@@ -15,6 +15,7 @@ import type { MatchedFormField, UnmatchedFormField } from "./types"
import { isRequiredField, setFieldHighlight } from "./formStyle"
import { findLabelForInput } from "./labelFinder"
import { delay } from "~utils/delay"
import { simulateClick } from "~utils/domEvent"
// ====================================================================
// 一、类型定义
@@ -1236,12 +1237,34 @@ export async function collectPickerOptionTexts(fieldStats: TitleStat[]): Promise
await delay("midH")
}
// 循环结束后,点击 document.documentElement 关闭最后一个可能残留弹出层
document.documentElement.dispatchEvent(new MouseEvent("click", { bubbles: true, clientX: 0, clientY: 0 }))
// 循环结束后,点击第一个已识别字段的输入框,通过 simulateClick 完整事件链关闭所有残留弹出层
const firstInputEl = findFirstVisibleInput(fieldStats)
if (firstInputEl) {
simulateClick(firstInputEl)
await delay("mid")
}
return optionTexts
}
/**
* 从字段统计列表中找到第一个可见的 input 元素
* 用于点击关闭残留弹出层(simulateClick 触发完整事件链,兼容所有 UI 组件库)
*
* @param fieldStats - scanPageFields 返回的按大标题分组的字段统计数组
* @returns 第一个可见的 input HTMLElement,找不到返回 null
*/
function findFirstVisibleInput(fieldStats: TitleStat[]): HTMLElement | null {
for (const ts of fieldStats) {
for (const f of ts.fields) {
if (!f.inputElement) continue
const el = f.inputElement as HTMLElement
if (el.offsetHeight > 0 && el.offsetWidth > 0) return el
}
}
return null
}
/**
* 递归收集指定元素内所有可见叶子节点的文字
* 只收集有效文字(长度 2-30,至少含1个汉字或2个英文字母),排除纯数字/纯符号
+1 -1
View File
@@ -12,7 +12,7 @@ const COLOR_MAP: Record<HighlightColor, string> = {
green: "#b7ffc5", // 淡绿色
greenTwo: "#b7ffc6", // 淡绿色2
red: "#ffb7b7", // 淡红色
yellow: "#fff", //
yellow: "transparent", // 透明
}
/** 常见的表单项容器选择器(与 formMatcher.ts 保持一致) */