缓存表单填写阶段去掉类名限制用findLabelForInput规则
This commit is contained in:
@@ -926,12 +926,7 @@ export async function handleAutoFillBeisen(params: AutoFillBeisenParams): Promis
|
||||
|
||||
if (allTitles.length > 0) {
|
||||
const INPUT_SEL_JSON = "input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='checkbox']):not([type='radio']):not([type='file']):not([disabled]), textarea:not([disabled])"
|
||||
const FORM_ITEM_SELS_JSON = [
|
||||
".form-item", ".form-group", ".form-field",
|
||||
".el-form-item", ".ant-form-item", ".ant-row",
|
||||
".arco-form-item", ".t-form-item", ".n-form-item",
|
||||
"[class*='form-item']", "[class*='form-group']", "[class*='formItem']",
|
||||
]
|
||||
|
||||
const JSON_EXCLUDE_LABELS = ["请输入", "输入", ":", ":", "请选择", "选择", "*", "必填"]
|
||||
|
||||
for (let tIdx = 0; tIdx < allTitles.length; tIdx++) {
|
||||
|
||||
@@ -910,12 +910,7 @@ export async function handleAutoFillCommon(params: AutoFillCommonParams): Promis
|
||||
|
||||
if (allTitles.length > 0) {
|
||||
const INPUT_SEL_JSON = "input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='checkbox']):not([type='radio']):not([type='file']):not([disabled]), textarea:not([disabled])"
|
||||
const FORM_ITEM_SELS_JSON = [
|
||||
".form-item", ".form-group", ".form-field",
|
||||
".el-form-item", ".ant-form-item", ".ant-row",
|
||||
".arco-form-item", ".t-form-item", ".n-form-item",
|
||||
"[class*='form-item']", "[class*='form-group']", "[class*='formItem']",
|
||||
]
|
||||
|
||||
const JSON_EXCLUDE_LABELS = ["请输入", "输入", ":", ":", "请选择", "选择", "*", "必填"]
|
||||
|
||||
for (let tIdx = 0; tIdx < allTitles.length; tIdx++) {
|
||||
@@ -1151,13 +1146,7 @@ export async function handleAutoFillCommon(params: AutoFillCommonParams): Promis
|
||||
// 【独立模块】可以通过注释 handleAutoFillCommon 中的调用行来禁用
|
||||
// ============================================================
|
||||
|
||||
/** 阶段B2 表单项容器选择器 */
|
||||
const B2_FORM_ITEM_SELS = [
|
||||
".form-item", ".form-group", ".form-field",
|
||||
".el-form-item", ".ant-form-item", ".ant-row",
|
||||
".arco-form-item", ".t-form-item", ".n-form-item",
|
||||
"[class*='form-item']", "[class*='form-group']", "[class*='formItem']",
|
||||
]
|
||||
|
||||
|
||||
/** 阶段B2 input 选择器 */
|
||||
const B2_INPUT_SEL = "input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='checkbox']):not([type='radio']):not([type='file']):not([disabled]), textarea:not([disabled])"
|
||||
@@ -1188,6 +1177,7 @@ interface FillCachedDataResult {
|
||||
|
||||
/**
|
||||
* 阶段B2:在指定大标题范围内,通过标签文字查找对应的 input 元素
|
||||
* 使用 findLabelForInput 统一标签定位(和缓存收集时一致),不依赖容器类名
|
||||
*/
|
||||
function b2FindInputByLabel(
|
||||
labelText: string,
|
||||
@@ -1202,27 +1192,10 @@ function b2FindInputByLabel(
|
||||
const beforeNext = !nextTitleEl || !!(nextTitleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_PRECEDING)
|
||||
if (!afterTitle || !beforeNext) continue
|
||||
|
||||
let container: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) {
|
||||
container = inp.closest(sel)
|
||||
if (container) break
|
||||
}
|
||||
if (container) {
|
||||
const labelEls = container.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const directText = Array.from(el.childNodes)
|
||||
.filter((n) => n.nodeType === Node.TEXT_NODE)
|
||||
.map((n) => n.textContent?.trim())
|
||||
.filter(Boolean)
|
||||
.join("")
|
||||
if (!directText || directText.length > 30) continue
|
||||
if (B2_EXCLUDE_LABELS.some((ex) => directText === ex)) continue
|
||||
// 标签文字必须严格全名匹配,不走 includes
|
||||
if (directText === labelText &&
|
||||
(el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
// 用 findLabelForInput 获取该 input 对应的标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() === labelText) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -1230,6 +1203,7 @@ function b2FindInputByLabel(
|
||||
|
||||
/**
|
||||
* 阶段B2:在指定容器内,通过标签文字查找对应的 input 元素
|
||||
* 使用 findLabelForInput 统一标签定位(和缓存收集时一致),不依赖容器类名
|
||||
*/
|
||||
function b2FindInputByLabelInContainer(
|
||||
labelText: string,
|
||||
@@ -1239,27 +1213,10 @@ function b2FindInputByLabelInContainer(
|
||||
const allInputs = containerEl.querySelectorAll(B2_INPUT_SEL)
|
||||
for (const inp of Array.from(allInputs)) {
|
||||
if (usedInputs.has(inp)) continue
|
||||
let formItem: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) {
|
||||
formItem = inp.closest(sel)
|
||||
if (formItem) break
|
||||
}
|
||||
if (formItem) {
|
||||
const labelEls = formItem.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const directText = Array.from(el.childNodes)
|
||||
.filter((n) => n.nodeType === Node.TEXT_NODE)
|
||||
.map((n) => n.textContent?.trim())
|
||||
.filter(Boolean)
|
||||
.join("")
|
||||
if (!directText || directText.length > 30) continue
|
||||
if (B2_EXCLUDE_LABELS.some((ex) => directText === ex)) continue
|
||||
// 标签文字必须严格全名匹配,不走 includes
|
||||
if (directText === labelText &&
|
||||
(el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
// 用 findLabelForInput 获取该 input 对应的标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() === labelText) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -1428,23 +1385,17 @@ async function handleFillCachedData(params: FillCachedDataParams): Promise<FillC
|
||||
} else {
|
||||
console.log(` [B2] "${sectionData.title}" (其他经历) 缓存${segments.length}段`)
|
||||
|
||||
// 检测当前页面已有几段
|
||||
// 检测当前页面已有几段(通过统计同名标签出现次数判断段数)
|
||||
const allInputsInRange = document.body.querySelectorAll(B2_INPUT_SEL)
|
||||
const labelsInRange: string[] = []
|
||||
for (const inp of Array.from(allInputsInRange)) {
|
||||
const afterTitle = !!(titleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)
|
||||
const beforeNext = !nextTitleEl || !!(nextTitleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_PRECEDING)
|
||||
if (!afterTitle || !beforeNext) continue
|
||||
let container: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) { container = inp.closest(sel); if (container) break }
|
||||
if (!container) continue
|
||||
const labelEls = container.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const t = Array.from(el.childNodes).filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => n.textContent?.trim()).filter(Boolean).join("")
|
||||
if (t && t.length <= 30 && !B2_EXCLUDE_LABELS.some((ex) => t === ex) && (el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
labelsInRange.push(t)
|
||||
break
|
||||
}
|
||||
// 用 findLabelForInput 获取标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() && !B2_EXCLUDE_LABELS.some((ex) => detectedLabel.trim() === ex)) {
|
||||
labelsInRange.push(detectedLabel.trim())
|
||||
}
|
||||
}
|
||||
const labelCounts = new Map<string, number>()
|
||||
|
||||
@@ -916,12 +916,7 @@ export async function handleAutoFillFeishu(params: AutoFillFeishuParams): Promis
|
||||
|
||||
if (allTitles.length > 0) {
|
||||
const INPUT_SEL_JSON = "input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='checkbox']):not([type='radio']):not([type='file']):not([disabled]), textarea:not([disabled])"
|
||||
const FORM_ITEM_SELS_JSON = [
|
||||
".form-item", ".form-group", ".form-field",
|
||||
".el-form-item", ".ant-form-item", ".ant-row",
|
||||
".arco-form-item", ".t-form-item", ".n-form-item",
|
||||
"[class*='form-item']", "[class*='form-group']", "[class*='formItem']",
|
||||
]
|
||||
|
||||
const JSON_EXCLUDE_LABELS = ["请输入", "输入", ":", ":", "请选择", "选择", "*", "必填"]
|
||||
|
||||
for (let tIdx = 0; tIdx < allTitles.length; tIdx++) {
|
||||
@@ -1157,13 +1152,7 @@ export async function handleAutoFillFeishu(params: AutoFillFeishuParams): Promis
|
||||
// 【独立模块】可以通过注释 handleAutoFillFeishu 中的调用行来禁用
|
||||
// ============================================================
|
||||
|
||||
/** 阶段B2 表单项容器选择器 */
|
||||
const B2_FORM_ITEM_SELS = [
|
||||
".form-item", ".form-group", ".form-field",
|
||||
".el-form-item", ".ant-form-item", ".ant-row",
|
||||
".arco-form-item", ".t-form-item", ".n-form-item",
|
||||
"[class*='form-item']", "[class*='form-group']", "[class*='formItem']",
|
||||
]
|
||||
|
||||
|
||||
/** 阶段B2 input 选择器 */
|
||||
const B2_INPUT_SEL = "input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='checkbox']):not([type='radio']):not([type='file']):not([disabled]), textarea:not([disabled])"
|
||||
@@ -1194,6 +1183,7 @@ interface FillCachedDataResult {
|
||||
|
||||
/**
|
||||
* 阶段B2:在指定大标题范围内,通过标签文字查找对应的 input 元素
|
||||
* 使用 findLabelForInput 统一标签定位(和缓存收集时一致),不依赖容器类名
|
||||
*/
|
||||
function b2FindInputByLabel(
|
||||
labelText: string,
|
||||
@@ -1208,27 +1198,10 @@ function b2FindInputByLabel(
|
||||
const beforeNext = !nextTitleEl || !!(nextTitleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_PRECEDING)
|
||||
if (!afterTitle || !beforeNext) continue
|
||||
|
||||
let container: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) {
|
||||
container = inp.closest(sel)
|
||||
if (container) break
|
||||
}
|
||||
if (container) {
|
||||
const labelEls = container.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const directText = Array.from(el.childNodes)
|
||||
.filter((n) => n.nodeType === Node.TEXT_NODE)
|
||||
.map((n) => n.textContent?.trim())
|
||||
.filter(Boolean)
|
||||
.join("")
|
||||
if (!directText || directText.length > 30) continue
|
||||
if (B2_EXCLUDE_LABELS.some((ex) => directText === ex)) continue
|
||||
// 标签文字必须严格全名匹配,不走 includes
|
||||
if (directText === labelText &&
|
||||
(el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
// 用 findLabelForInput 获取该 input 对应的标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() === labelText) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -1236,6 +1209,7 @@ function b2FindInputByLabel(
|
||||
|
||||
/**
|
||||
* 阶段B2:在指定容器内,通过标签文字查找对应的 input 元素
|
||||
* 使用 findLabelForInput 统一标签定位(和缓存收集时一致),不依赖容器类名
|
||||
*/
|
||||
function b2FindInputByLabelInContainer(
|
||||
labelText: string,
|
||||
@@ -1245,27 +1219,10 @@ function b2FindInputByLabelInContainer(
|
||||
const allInputs = containerEl.querySelectorAll(B2_INPUT_SEL)
|
||||
for (const inp of Array.from(allInputs)) {
|
||||
if (usedInputs.has(inp)) continue
|
||||
let formItem: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) {
|
||||
formItem = inp.closest(sel)
|
||||
if (formItem) break
|
||||
}
|
||||
if (formItem) {
|
||||
const labelEls = formItem.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const directText = Array.from(el.childNodes)
|
||||
.filter((n) => n.nodeType === Node.TEXT_NODE)
|
||||
.map((n) => n.textContent?.trim())
|
||||
.filter(Boolean)
|
||||
.join("")
|
||||
if (!directText || directText.length > 30) continue
|
||||
if (B2_EXCLUDE_LABELS.some((ex) => directText === ex)) continue
|
||||
// 标签文字必须严格全名匹配,不走 includes
|
||||
if (directText === labelText &&
|
||||
(el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
// 用 findLabelForInput 获取该 input 对应的标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() === labelText) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -1434,23 +1391,17 @@ async function handleFillCachedData(params: FillCachedDataParams): Promise<FillC
|
||||
} else {
|
||||
console.log(` [B2] "${sectionData.title}" (其他经历) 缓存${segments.length}段`)
|
||||
|
||||
// 检测当前页面已有几段
|
||||
// 检测当前页面已有几段(通过统计同名标签出现次数判断段数)
|
||||
const allInputsInRange = document.body.querySelectorAll(B2_INPUT_SEL)
|
||||
const labelsInRange: string[] = []
|
||||
for (const inp of Array.from(allInputsInRange)) {
|
||||
const afterTitle = !!(titleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)
|
||||
const beforeNext = !nextTitleEl || !!(nextTitleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_PRECEDING)
|
||||
if (!afterTitle || !beforeNext) continue
|
||||
let container: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) { container = inp.closest(sel); if (container) break }
|
||||
if (!container) continue
|
||||
const labelEls = container.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const t = Array.from(el.childNodes).filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => n.textContent?.trim()).filter(Boolean).join("")
|
||||
if (t && t.length <= 30 && !B2_EXCLUDE_LABELS.some((ex) => t === ex) && (el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
labelsInRange.push(t)
|
||||
break
|
||||
}
|
||||
// 用 findLabelForInput 获取标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() && !B2_EXCLUDE_LABELS.some((ex) => detectedLabel.trim() === ex)) {
|
||||
labelsInRange.push(detectedLabel.trim())
|
||||
}
|
||||
}
|
||||
const labelCounts = new Map<string, number>()
|
||||
|
||||
@@ -916,12 +916,7 @@ export async function handleAutoFillHotjob(params: AutoFillHotjobParams): Promis
|
||||
|
||||
if (allTitles.length > 0) {
|
||||
const INPUT_SEL_JSON = "input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='checkbox']):not([type='radio']):not([type='file']):not([disabled]), textarea:not([disabled])"
|
||||
const FORM_ITEM_SELS_JSON = [
|
||||
".form-item", ".form-group", ".form-field",
|
||||
".el-form-item", ".ant-form-item", ".ant-row",
|
||||
".arco-form-item", ".t-form-item", ".n-form-item",
|
||||
"[class*='form-item']", "[class*='form-group']", "[class*='formItem']",
|
||||
]
|
||||
|
||||
const JSON_EXCLUDE_LABELS = ["请输入", "输入", ":", ":", "请选择", "选择", "*", "必填"]
|
||||
|
||||
for (let tIdx = 0; tIdx < allTitles.length; tIdx++) {
|
||||
@@ -1157,13 +1152,7 @@ export async function handleAutoFillHotjob(params: AutoFillHotjobParams): Promis
|
||||
// 【独立模块】可以通过注释 handleAutoFillHotjob 中的调用行来禁用
|
||||
// ============================================================
|
||||
|
||||
/** 阶段B2 表单项容器选择器 */
|
||||
const B2_FORM_ITEM_SELS = [
|
||||
".form-item", ".form-group", ".form-field",
|
||||
".el-form-item", ".ant-form-item", ".ant-row",
|
||||
".arco-form-item", ".t-form-item", ".n-form-item",
|
||||
"[class*='form-item']", "[class*='form-group']", "[class*='formItem']",
|
||||
]
|
||||
|
||||
|
||||
/** 阶段B2 input 选择器 */
|
||||
const B2_INPUT_SEL = "input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='checkbox']):not([type='radio']):not([type='file']):not([disabled]), textarea:not([disabled])"
|
||||
@@ -1194,6 +1183,7 @@ interface FillCachedDataResult {
|
||||
|
||||
/**
|
||||
* 阶段B2:在指定大标题范围内,通过标签文字查找对应的 input 元素
|
||||
* 使用 findLabelForInput 统一标签定位(和缓存收集时一致),不依赖容器类名
|
||||
*/
|
||||
function b2FindInputByLabel(
|
||||
labelText: string,
|
||||
@@ -1208,27 +1198,10 @@ function b2FindInputByLabel(
|
||||
const beforeNext = !nextTitleEl || !!(nextTitleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_PRECEDING)
|
||||
if (!afterTitle || !beforeNext) continue
|
||||
|
||||
let container: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) {
|
||||
container = inp.closest(sel)
|
||||
if (container) break
|
||||
}
|
||||
if (container) {
|
||||
const labelEls = container.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const directText = Array.from(el.childNodes)
|
||||
.filter((n) => n.nodeType === Node.TEXT_NODE)
|
||||
.map((n) => n.textContent?.trim())
|
||||
.filter(Boolean)
|
||||
.join("")
|
||||
if (!directText || directText.length > 30) continue
|
||||
if (B2_EXCLUDE_LABELS.some((ex) => directText === ex)) continue
|
||||
// 标签文字必须严格全名匹配,不走 includes
|
||||
if (directText === labelText &&
|
||||
(el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
// 用 findLabelForInput 获取该 input 对应的标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() === labelText) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -1236,6 +1209,7 @@ function b2FindInputByLabel(
|
||||
|
||||
/**
|
||||
* 阶段B2:在指定容器内,通过标签文字查找对应的 input 元素
|
||||
* 使用 findLabelForInput 统一标签定位(和缓存收集时一致),不依赖容器类名
|
||||
*/
|
||||
function b2FindInputByLabelInContainer(
|
||||
labelText: string,
|
||||
@@ -1245,27 +1219,10 @@ function b2FindInputByLabelInContainer(
|
||||
const allInputs = containerEl.querySelectorAll(B2_INPUT_SEL)
|
||||
for (const inp of Array.from(allInputs)) {
|
||||
if (usedInputs.has(inp)) continue
|
||||
let formItem: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) {
|
||||
formItem = inp.closest(sel)
|
||||
if (formItem) break
|
||||
}
|
||||
if (formItem) {
|
||||
const labelEls = formItem.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const directText = Array.from(el.childNodes)
|
||||
.filter((n) => n.nodeType === Node.TEXT_NODE)
|
||||
.map((n) => n.textContent?.trim())
|
||||
.filter(Boolean)
|
||||
.join("")
|
||||
if (!directText || directText.length > 30) continue
|
||||
if (B2_EXCLUDE_LABELS.some((ex) => directText === ex)) continue
|
||||
// 标签文字必须严格全名匹配,不走 includes
|
||||
if (directText === labelText &&
|
||||
(el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
// 用 findLabelForInput 获取该 input 对应的标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() === labelText) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -1434,23 +1391,17 @@ async function handleFillCachedData(params: FillCachedDataParams): Promise<FillC
|
||||
} else {
|
||||
console.log(` [B2] "${sectionData.title}" (其他经历) 缓存${segments.length}段`)
|
||||
|
||||
// 检测当前页面已有几段
|
||||
// 检测当前页面已有几段(通过统计同名标签出现次数判断段数)
|
||||
const allInputsInRange = document.body.querySelectorAll(B2_INPUT_SEL)
|
||||
const labelsInRange: string[] = []
|
||||
for (const inp of Array.from(allInputsInRange)) {
|
||||
const afterTitle = !!(titleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)
|
||||
const beforeNext = !nextTitleEl || !!(nextTitleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_PRECEDING)
|
||||
if (!afterTitle || !beforeNext) continue
|
||||
let container: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) { container = inp.closest(sel); if (container) break }
|
||||
if (!container) continue
|
||||
const labelEls = container.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const t = Array.from(el.childNodes).filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => n.textContent?.trim()).filter(Boolean).join("")
|
||||
if (t && t.length <= 30 && !B2_EXCLUDE_LABELS.some((ex) => t === ex) && (el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
labelsInRange.push(t)
|
||||
break
|
||||
}
|
||||
// 用 findLabelForInput 获取标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() && !B2_EXCLUDE_LABELS.some((ex) => detectedLabel.trim() === ex)) {
|
||||
labelsInRange.push(detectedLabel.trim())
|
||||
}
|
||||
}
|
||||
const labelCounts = new Map<string, number>()
|
||||
|
||||
@@ -916,12 +916,7 @@ export async function handleAutoFillMoka(params: AutoFillMokaParams): Promise<Au
|
||||
|
||||
if (allTitles.length > 0) {
|
||||
const INPUT_SEL_JSON = "input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='checkbox']):not([type='radio']):not([type='file']):not([disabled]), textarea:not([disabled])"
|
||||
const FORM_ITEM_SELS_JSON = [
|
||||
".form-item", ".form-group", ".form-field",
|
||||
".el-form-item", ".ant-form-item", ".ant-row",
|
||||
".arco-form-item", ".t-form-item", ".n-form-item",
|
||||
"[class*='form-item']", "[class*='form-group']", "[class*='formItem']",
|
||||
]
|
||||
|
||||
const JSON_EXCLUDE_LABELS = ["请输入", "输入", ":", ":", "请选择", "选择", "*", "必填"]
|
||||
|
||||
for (let tIdx = 0; tIdx < allTitles.length; tIdx++) {
|
||||
@@ -1157,13 +1152,7 @@ export async function handleAutoFillMoka(params: AutoFillMokaParams): Promise<Au
|
||||
// 【独立模块】可以通过注释 handleAutoFillMoka 中的调用行来禁用
|
||||
// ============================================================
|
||||
|
||||
/** 阶段B2 表单项容器选择器 */
|
||||
const B2_FORM_ITEM_SELS = [
|
||||
".form-item", ".form-group", ".form-field",
|
||||
".el-form-item", ".ant-form-item", ".ant-row",
|
||||
".arco-form-item", ".t-form-item", ".n-form-item",
|
||||
"[class*='form-item']", "[class*='form-group']", "[class*='formItem']",
|
||||
]
|
||||
|
||||
|
||||
/** 阶段B2 input 选择器 */
|
||||
const B2_INPUT_SEL = "input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='checkbox']):not([type='radio']):not([type='file']):not([disabled]), textarea:not([disabled])"
|
||||
@@ -1194,6 +1183,7 @@ interface FillCachedDataResult {
|
||||
|
||||
/**
|
||||
* 阶段B2:在指定大标题范围内,通过标签文字查找对应的 input 元素
|
||||
* 使用 findLabelForInput 统一标签定位(和缓存收集时一致),不依赖容器类名
|
||||
*/
|
||||
function b2FindInputByLabel(
|
||||
labelText: string,
|
||||
@@ -1208,27 +1198,10 @@ function b2FindInputByLabel(
|
||||
const beforeNext = !nextTitleEl || !!(nextTitleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_PRECEDING)
|
||||
if (!afterTitle || !beforeNext) continue
|
||||
|
||||
let container: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) {
|
||||
container = inp.closest(sel)
|
||||
if (container) break
|
||||
}
|
||||
if (container) {
|
||||
const labelEls = container.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const directText = Array.from(el.childNodes)
|
||||
.filter((n) => n.nodeType === Node.TEXT_NODE)
|
||||
.map((n) => n.textContent?.trim())
|
||||
.filter(Boolean)
|
||||
.join("")
|
||||
if (!directText || directText.length > 30) continue
|
||||
if (B2_EXCLUDE_LABELS.some((ex) => directText === ex)) continue
|
||||
// 标签文字必须严格全名匹配,不走 includes
|
||||
if (directText === labelText &&
|
||||
(el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
// 用 findLabelForInput 获取该 input 对应的标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() === labelText) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -1236,6 +1209,7 @@ function b2FindInputByLabel(
|
||||
|
||||
/**
|
||||
* 阶段B2:在指定容器内,通过标签文字查找对应的 input 元素
|
||||
* 使用 findLabelForInput 统一标签定位(和缓存收集时一致),不依赖容器类名
|
||||
*/
|
||||
function b2FindInputByLabelInContainer(
|
||||
labelText: string,
|
||||
@@ -1245,27 +1219,10 @@ function b2FindInputByLabelInContainer(
|
||||
const allInputs = containerEl.querySelectorAll(B2_INPUT_SEL)
|
||||
for (const inp of Array.from(allInputs)) {
|
||||
if (usedInputs.has(inp)) continue
|
||||
let formItem: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) {
|
||||
formItem = inp.closest(sel)
|
||||
if (formItem) break
|
||||
}
|
||||
if (formItem) {
|
||||
const labelEls = formItem.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const directText = Array.from(el.childNodes)
|
||||
.filter((n) => n.nodeType === Node.TEXT_NODE)
|
||||
.map((n) => n.textContent?.trim())
|
||||
.filter(Boolean)
|
||||
.join("")
|
||||
if (!directText || directText.length > 30) continue
|
||||
if (B2_EXCLUDE_LABELS.some((ex) => directText === ex)) continue
|
||||
// 标签文字必须严格全名匹配,不走 includes
|
||||
if (directText === labelText &&
|
||||
(el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
// 用 findLabelForInput 获取该 input 对应的标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() === labelText) {
|
||||
return inp as HTMLInputElement | HTMLTextAreaElement
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -1434,23 +1391,17 @@ async function handleFillCachedData(params: FillCachedDataParams): Promise<FillC
|
||||
} else {
|
||||
console.log(` [B2] "${sectionData.title}" (其他经历) 缓存${segments.length}段`)
|
||||
|
||||
// 检测当前页面已有几段
|
||||
// 检测当前页面已有几段(通过统计同名标签出现次数判断段数)
|
||||
const allInputsInRange = document.body.querySelectorAll(B2_INPUT_SEL)
|
||||
const labelsInRange: string[] = []
|
||||
for (const inp of Array.from(allInputsInRange)) {
|
||||
const afterTitle = !!(titleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)
|
||||
const beforeNext = !nextTitleEl || !!(nextTitleEl.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_PRECEDING)
|
||||
if (!afterTitle || !beforeNext) continue
|
||||
let container: Element | null = null
|
||||
for (const sel of B2_FORM_ITEM_SELS) { container = inp.closest(sel); if (container) break }
|
||||
if (!container) continue
|
||||
const labelEls = container.querySelectorAll("label, span, div, td, th, p, legend, dt")
|
||||
for (const el of Array.from(labelEls)) {
|
||||
const t = Array.from(el.childNodes).filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => n.textContent?.trim()).filter(Boolean).join("")
|
||||
if (t && t.length <= 30 && !B2_EXCLUDE_LABELS.some((ex) => t === ex) && (el.compareDocumentPosition(inp) & Node.DOCUMENT_POSITION_FOLLOWING)) {
|
||||
labelsInRange.push(t)
|
||||
break
|
||||
}
|
||||
// 用 findLabelForInput 获取标签文字,和缓存收集时逻辑一致
|
||||
const { labelText: detectedLabel } = findLabelForInput(inp)
|
||||
if (detectedLabel && detectedLabel.trim() && !B2_EXCLUDE_LABELS.some((ex) => detectedLabel.trim() === ex)) {
|
||||
labelsInRange.push(detectedLabel.trim())
|
||||
}
|
||||
}
|
||||
const labelCounts = new Map<string, number>()
|
||||
|
||||
Reference in New Issue
Block a user