半自动填写插件

半自动填写插件,匹配并尝试填写网页,填写失败则写入剪贴板。可用于在线网页填写简历等等

当前为 2023-08-26 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         半自动填写插件
// @namespace    https://github.com/balala8/semi_autofill_plugin/
// @version      0.1.5
// @description  半自动填写插件,匹配并尝试填写网页,填写失败则写入剪贴板。可用于在线网页填写简历等等
// @author       obalala
// @match        *://*/*
// @icon         https://raw.githubusercontent.com/balala8/semi_autofill_plugin/main/logo-256.ico
// @run-at       document-end
// @license MIT
// ==/UserScript==


let information = {
    "姓名": ["张三"],
    "性别": ["男"],
    "身高": ["180"],
    "体重": ["70"],
    "籍贯": ["北京"],
    "民族": ["汉族"],
    "婚姻状况": ["未婚"],
    "职业": ["学生"],
    "微信号": ["test"],
    "邮箱": ["[email protected]"],
    "手机电话号码": ["12345678901"],
    "证件类型": ["身份证"],
    "证件号码": ["123456789012345678"],
    "当前地址": ["北京市海淀区"],
    "户口所在地": ["北京市海淀区"],
    "外语分数": ["750"],
    "紧急联系人姓名": ["李四"],
    "紧急联系人电话": ["12345678901"],
    "学历": ["本科", "硕士"],
    "毕业学校": ["北京大学", "清华大学"],
    "院系": ["计算机系", "软件工程系"],
    "专业": ["计算机", "软件工程"],
    "导师": ["张三", "李四"],
    "实验室": ["张三实验室", "李四实验室"],
    "研究方向": ["计算机视觉", "机器学习"],
    "论文": ["论文1", "论文2"],
    "获奖": ["奖项1", "奖项2"],
    "竞赛": ["竞赛1", "竞赛2"],
    "任职公司": ["公司1", "公司2"],
    "职位": ["职位1", "职位2"],
    "任职描述": ["描述1", "描述2"],
    "项目名称": ["项目1", "项目2"],
    "项目描述": ["描述1", "描述2"],
    "项目职责": ["职责1", "职责2"],
    "项目角色": ["技术1", "技术2"],
}

const popupWindow = document.createElement('div');
popupWindow.className = 'popup-window';
popupWindow.style.display = 'none';
popupWindow.style.position = 'fixed';
popupWindow.style.backgroundColor = 'rgb(46, 196, 182)';
popupWindow.style.zIndex = '9999';
document.body.appendChild(popupWindow);

var alertBox = document.createElement("div");
alertBox.textContent = "这是一个提示框";
alertBox.style.position = "fixed";
alertBox.style.top = "50%";
alertBox.style.left = "50%";
alertBox.style.transform = "translate(-50%, -50%)";
alertBox.style.backgroundColor = 'rgb(46, 196, 182)';
alertBox.style.padding = "10px";
alertBox.style.border = "1px solid rgb(46, 196, 182)";
alertBox.style.borderRadius = "5px";
alertBox.style.transition = "opacity 0.5s";
alertBox.style.display = "none";
alertBox.style.zIndex = '9999';
document.body.appendChild(alertBox);

let uponElement = null;

// 在页面加载后3秒执行
setTimeout(function() {
    // const inputElements = document.querySelectorAll('input[type="text"], textarea');
    // inputElements.forEach(input => {
    //     input.addEventListener('click', popupWindowClick);
    // });

    document.addEventListener('click', function(event) {
        if (event.target.tagName === 'INPUT' && event.target.type==='text' || event.target.tagName === 'TEXTAREA') {
            popupWindowClick(event);
        }
    });
    

    // 监听页面滚动事件
    window.addEventListener('scroll', function() {
        popupWindow.style.display = 'none';
    });
}, 3000);


// 悬浮窗口添加点击事件监听器
function popupWindowClick(event) {
    const clicked = event.target;
    uponElement = clicked;
    const inputRect = clicked.getBoundingClientRect();
    let contents = "-1"
    // 尝试从placehollder中获取关键字
    if (uponElement.placeholder !== '') {
        let keyword = uponElement.placeholder;
        contents = keyword2index(keyword);
    }
    // 从placeholder中获取失败,从dom树中获取关系上最近且排版上最近的元素
    if (contents == '-1') {
        contents = getNearLabelElement(uponElement)
    }
    if (contents == '-1') {
        showText("未找到匹配的信息");
        return;
    }

    while (popupWindow.firstChild){
        popupWindow.removeChild(popupWindow.firstChild);
    }
    for (const line of contents) {
        const div = document.createElement('div');
        div.style.backgroundColor = 'white';
        div.style.padding = '10px';
        div.style.margin = '5px';
        div.style.border = '1px solid white';
        div.style.cursor = 'pointer';
        div.style.color = 'rgb(46, 196, 182)';
        div.textContent = line;
        div.addEventListener('click', answerClick);
        popupWindow.appendChild(div);
    }
    popupWindow.style.position = 'absolute';
    popupWindow.style.top = (event.clientY + window.scrollY) + 'px';
    popupWindow.style.left = (event.clientX + window.scrollX) + 'px';
    popupWindow.style.display = 'flex'
}

// 答案item被点击事件监听器,点击选择答案并隐藏popupWindow
function answerClick(event) {
    const clickedDiv = event.target;
    const text = clickedDiv.textContent;
    console.log(`Clicked: ${text}`);
    if (text !== "") {
        uponElement.value = text;
    }
    uponElement.focus();
    popupWindow.style.display = 'none';

    var clipboardItem = new ClipboardItem({ "text/plain": new Blob([text], { type: "text/plain" }) });
    navigator.clipboard.write([clipboardItem]).then(function () {
        showText("已成功复制到剪贴板");
    }).catch(function (err) {
        console.error("复制到剪贴板失败: ", err);
        showText("复制到剪贴板失败");
    });

}

// 根据关键字获取自身信息
function keyword2index(keyword) {
    keyword = trimKeyword(keyword);
    if (keyword === false) {
        return "-1";
    }
    let maxSimilarity = 0;
    let mostSimilarKey = null;
    for (const key in information) {
        const similarity = calculateJaccardSimilarity(keyword, key);
        if (similarity > maxSimilarity) {
            maxSimilarity = similarity;
            mostSimilarKey = key;
        }
    }
    if (maxSimilarity == 0) {
        return "-1";
    }
    console.log("最匹配的 key 是:", mostSimilarKey, "相似度:", maxSimilarity, "值:", information[mostSimilarKey]);
    return information[mostSimilarKey];
}

function getNearLabelElement(element) {
    const siblings = Array.from(element.parentNode.children);
    siblings.sort((a, b) => {
        const distanceToA = Math.abs(a.getBoundingClientRect().top - element.getBoundingClientRect().top);
        const distanceToB = Math.abs(b.getBoundingClientRect().top - element.getBoundingClientRect().top);
        return distanceToA - distanceToB;
    });

    for (let i = 0; i < siblings.length; i++) {
        let html = siblings[i].innerHTML;
        let htmlText = html.replace(/<[^>]+>/g, '');
        if (htmlText == null || htmlText == undefined || htmlText == "") {
            continue;
        }
        console.log("htmlText:", htmlText);
        let res = keyword2index(htmlText);
        console.log("res:", res);
        if (res != "-1") {
            return res;
        }
    }
    return getNearLabelElement(element.parentNode);
}

function trimKeyword(keyword) {
    keyword = keyword.replace(/请|输入|\s/g, '');
    if (keyword === null || keyword === undefined || keyword === '') {
        return false;
    }
    return keyword;
}

// 计算 Jaccard 相似度
function calculateJaccardSimilarity(str1, str2) {
    const set1 = new Set(str1.split(''));
    const set2 = new Set(str2.split(''));
    const intersection = new Set([...set1].filter(x => set2.has(x)));
    const union = new Set([...set1, ...set2]);
    return intersection.size / union.size;
}

function showText(text) {
    alertBox.textContent = text;
    alertBox.style.display = "flex";
    alertBox.style.opacity = "1";
    setTimeout(function () {
        alertBox.style.opacity = "0";
        setTimeout(function () {
            alertBox.style.display = "none";
        }, 500);
    }, 500);
}