// ==UserScript==
// @name Izyz-Helper
// @namespace https://greasyfork.org/users/1417526
// @version 0.1.3
// @description Help you to use izyz easier!
// @author Weichenleeeee
// @match https://www.gdzyz.cn/*
// @icon https://www.gdzyz.cn/assets/weblogo.1b6eba63.svg
// @grant GM_registerMenuCommand
// @grant GM_addStyle
// @grant GM_openInTab
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @grant GM_getResourceText
// @require https://unpkg.com/xlsx/dist/xlsx.full.min.js
// ==/UserScript==
(function() {
'use strict';
var names = []; // 用实际的名字替换
var nextButtonEnabled = false; // 是否已点击“添加补录”
var skipButtonEnabled = false; // 是否点击了跳过按钮
var volunteersSkipped = [];
// 检测XLSX库是否加载成功
setTimeout(function() {
if (typeof XLSX === "undefined") {
console.error("XLSX 库加载失败!");
alert("无法加载 XLSX 库,功能无法使用!");
return;
} else {
console.log("XLSX 库加载成功!");
}
}, 1000); // 延迟1秒检查是否加载成功
// 添加图片上传按钮
function createImageInput() {
var container = document.createElement('div');
container.style.position = 'fixed';
container.style.bottom = '130px';
container.style.left = '10px';
container.style.zIndex = 9999;
var label = document.createElement('span');
label.textContent = '上传志愿者照片';
label.style.color = '#4CAF50';
label.style.marginRight = '10px';
label.style.fontSize = '14px';
container.appendChild(label);
var input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*'; // 限制为图片文件
input.title = '上传志愿者照片(支持JPG/PNG格式)';
input.style.padding = '5px';
input.style.borderRadius = '5px';
input.style.backgroundColor = '#4CAF50';
input.style.color = 'white';
input.style.border = 'none';
input.style.cursor = 'pointer';
input.addEventListener('change', handleImageSelect);
container.appendChild(input);
document.body.appendChild(container);
}
// 添加Excel上传按钮
function createExcelInput() {
var container = document.createElement('div');
container.style.position = 'fixed';
container.style.bottom = '90px';
container.style.left = '10px';
container.style.zIndex = 9999;
var label = document.createElement('span');
label.textContent = '上传志愿者名单';
label.style.color = '#2196F3';
label.style.marginRight = '10px';
label.style.fontSize = '14px';
container.appendChild(label);
var input = document.createElement('input');
input.type = 'file';
input.accept = '.xlsx,.xls'; // 限制为Excel文件
input.title = '上传志愿者名单(支持Excel格式)';
input.style.padding = '5px';
input.style.borderRadius = '5px';
input.style.backgroundColor = '#2196F3';
input.style.color = 'white';
input.style.border = 'none';
input.style.cursor = 'pointer';
input.addEventListener('change', handleExcelSelect);
container.appendChild(input);
document.body.appendChild(container);
}
// 处理Excel文件选择
function handleExcelSelect(event) {
var file = event.target.files[0];
if (file && (file.name.endsWith('.xlsx') || file.name.endsWith('.xls'))) {
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
var workbook = XLSX.read(data, { type: 'binary' });
var sheet = workbook.Sheets[workbook.SheetNames[0]]; // 默认取第一个工作表
// 将工作表转换为二维数组,raw: true 确保读取原始数据而不进行格式化
var json = XLSX.utils.sheet_to_json(sheet, { header: 1, raw: true });
// 打印原始数据以查看第一行
console.log('读取到的数据:', json);
// 在整个表格中搜索"姓名"列
var nameColumnIndex = -1;
// 首先检查表头
var header = json[0];
nameColumnIndex = header.findIndex(col => col.trim() === "姓名");
// 如果表头中没有找到,遍历所有行查找
if (nameColumnIndex === -1) {
for (let i = 0; i < json.length; i++) {
nameColumnIndex = json[i].findIndex(col => col.trim() === "姓名");
if (nameColumnIndex !== -1) {
console.log('在第', i + 1, '行找到"姓名"列');
break;
}
}
}
if (nameColumnIndex === -1) {
alert('未找到“姓名”列,请确保Excel中有“姓名”列');
console.error('未找到姓名列');
return;
}
// 提取姓名列数据,并移除姓名前的数字,跳过表头
names = json.slice(1).map(row => {
let name = row[nameColumnIndex];
if (name) {
// 使用正则表达式移除姓名前的数字
name = name.replace(/^\d+/, '').trim();
}
return name;
}).filter(name => name && name !== "姓名"); // 过滤掉空值和"姓名"字符串
console.log('已加载姓名:', names);
alert('Excel 文件已成功加载,姓名已提取!');
};
reader.onerror = function(ex) {
console.log(ex);
};
reader.readAsBinaryString(file);
} else {
alert('请上传有效的 Excel 文件');
}
}
// 添加跳过按钮
function createSkipButton() {
var skipButton = document.createElement('button');
skipButton.textContent = '跳过当前志愿者';
skipButton.style.position = 'fixed';
skipButton.style.bottom = '50px'; // 放在“上传文件”按钮的上方
skipButton.style.left = '10px';
skipButton.style.zIndex = 9999;
skipButton.style.padding = '10px';
skipButton.style.borderRadius = '5px';
skipButton.style.backgroundColor = '#f44336'; // 红色按钮
skipButton.style.color = 'white';
skipButton.style.border = 'none';
skipButton.style.cursor = 'pointer';
skipButton.style.fontSize = '14px';
skipButton.addEventListener('click', function() {
skipButtonEnabled = true; // 设置跳过标志
console.log('用户点击了跳过按钮1');
console.log('skipButtonEnabled is ',skipButtonEnabled);
});
document.body.appendChild(skipButton);
}
// 百度云OCR配置
const BAIDU_API_KEY = '4rHGTojlMzTYGov3tBHugdI6';
const BAIDU_SECRET_KEY = 'g4dhvSh3KHXfDoljUPRFceVczdVrGued';
let accessToken = '';
// 获取百度云access_token
function getAccessToken() {
return new Promise((resolve, reject) => {
const url = `https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=${BAIDU_API_KEY}&client_secret=${BAIDU_SECRET_KEY}`;
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
try {
const data = JSON.parse(response.responseText);
if (data.access_token) {
accessToken = data.access_token;
console.log('百度云access_token获取成功');
resolve();
} else {
throw new Error('获取access_token失败');
}
} catch (error) {
reject(error);
}
},
onerror: function(error) {
reject(error);
}
});
});
}
// 将图片文件转换为base64
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const base64 = reader.result.split(',')[1];
resolve(base64);
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
// 处理图片选择
async function handleImageSelect(event) {
const file = event.target.files[0];
if (!file || !file.type.startsWith('image/')) {
alert('请上传有效的图片文件');
return;
}
try {
// 获取access_token
await getAccessToken();
// 将图片转换为base64
const imageBase64 = await fileToBase64(file);
// 调用百度云OCR API
const ocrUrl = `https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token=${accessToken}`;
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'POST',
url: ocrUrl,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: `image=${encodeURIComponent(imageBase64)}&language_type=CHN_ENG`,
onload: function(response) {
try {
const result = JSON.parse(response.responseText);
if (result.words_result) {
names = result.words_result
.map(item => item.words.trim())
.filter(line => line.length > 0)
.map(name => {
// 移除姓名前的数字
name = name.replace(/^\d+/, '').trim();
// 匹配2-4个中文字符的姓名
const chineseNamePattern = /^[\u4e00-\u9fa5]{2,4}$/;
if (chineseNamePattern.test(name)) {
return name;
}
return null;
})
.filter(name => name !== null); // 过滤掉不符合条件的项
// 创建选择界面
const modal = document.createElement('div');
modal.style.position = 'fixed';
modal.style.top = '0';
modal.style.left = '0';
modal.style.width = '100%';
modal.style.height = '100%';
modal.style.backgroundColor = 'rgba(0,0,0,0.5)';
modal.style.zIndex = 10000;
const content = document.createElement('div');
content.style.position = 'absolute';
content.style.top = '50%';
content.style.left = '50%';
content.style.transform = 'translate(-50%, -50%)';
content.style.backgroundColor = 'white';
content.style.padding = '20px';
content.style.borderRadius = '5px';
content.style.width = '400px';
const title = document.createElement('h3');
title.textContent = '请选择要添加的姓名';
title.style.marginBottom = '15px';
content.appendChild(title);
const list = document.createElement('div');
list.style.maxHeight = '300px';
list.style.overflowY = 'auto';
list.style.marginBottom = '15px';
names.forEach((name, index) => {
const item = document.createElement('div');
item.style.display = 'flex';
item.style.alignItems = 'center';
item.style.marginBottom = '10px';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = true;
checkbox.style.marginRight = '10px';
const input = document.createElement('input');
input.type = 'text';
input.value = name;
input.style.flex = '1';
input.style.padding = '5px';
item.appendChild(checkbox);
item.appendChild(input);
list.appendChild(item);
});
content.appendChild(list);
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex';
buttonContainer.style.justifyContent = 'flex-end';
const confirmButton = document.createElement('button');
confirmButton.textContent = '确认';
confirmButton.style.padding = '8px 16px';
confirmButton.style.backgroundColor = '#4CAF50';
confirmButton.style.color = 'white';
confirmButton.style.border = 'none';
confirmButton.style.borderRadius = '4px';
confirmButton.style.cursor = 'pointer';
confirmButton.onclick = () => {
const selectedNames = [];
list.querySelectorAll('div').forEach(item => {
const checkbox = item.querySelector('input[type="checkbox"]');
const input = item.querySelector('input[type="text"]');
if (checkbox.checked) {
selectedNames.push(input.value.trim());
}
});
names = selectedNames.filter(name => name.length > 0);
document.body.removeChild(modal);
console.log('用户选择的姓名:', names);
alert('姓名选择完成!');
resolve();
};
const cancelButton = document.createElement('button');
cancelButton.textContent = '取消';
cancelButton.style.padding = '8px 16px';
cancelButton.style.marginRight = '10px';
cancelButton.style.backgroundColor = '#f44336';
cancelButton.style.color = 'white';
cancelButton.style.border = 'none';
cancelButton.style.borderRadius = '4px';
cancelButton.style.cursor = 'pointer';
cancelButton.onclick = () => {
document.body.removeChild(modal);
resolve();
};
buttonContainer.appendChild(cancelButton);
buttonContainer.appendChild(confirmButton);
content.appendChild(buttonContainer);
modal.appendChild(content);
document.body.appendChild(modal);
} else {
throw new Error('OCR识别失败');
}
} catch (error) {
reject(error);
}
},
onerror: function(error) {
reject(error);
}
});
});
} catch (error) {
console.error('图片识别失败:', error);
alert('图片识别失败,请确保图片清晰且包含中文文本');
}
}
// 创建并显示进度条
function createProgressBar() {
var progressContainer = document.createElement('div');
progressContainer.style.position = 'fixed';
progressContainer.style.bottom = '170px'; // 下移80px
progressContainer.style.left = '10px';
progressContainer.style.zIndex = 9999;
progressContainer.style.width = '300px';
progressContainer.style.height = '30px';
progressContainer.style.backgroundColor = '#e0e0e0';
progressContainer.style.borderRadius = '5px';
var progressBar = document.createElement('div');
progressBar.style.height = '100%';
progressBar.style.width = '0%';
progressBar.style.backgroundColor = '#4CAF50';
progressBar.style.borderRadius = '5px';
progressContainer.appendChild(progressBar);
var progressText = document.createElement('span');
progressText.style.position = 'absolute';
progressText.style.top = '50%';
progressText.style.left = '50%';
progressText.style.transform = 'translate(-50%, -50%)';
progressText.style.color = 'white';
progressText.style.fontSize = '14px';
progressContainer.appendChild(progressText);
var currentVolunteerText = document.createElement('span');
currentVolunteerText.style.position = 'fixed';
currentVolunteerText.style.bottom = '210px'; // 下移80px
currentVolunteerText.style.left = '10px';
currentVolunteerText.style.zIndex = 9999;
currentVolunteerText.style.fontSize = '14px';
currentVolunteerText.style.color = '#4CAF50';
currentVolunteerText.style.fontWeight = 'bold';
currentVolunteerText.textContent = '当前录入:第 0 个志愿者';
document.body.appendChild(currentVolunteerText);
document.body.appendChild(progressContainer); // 确保进度条在页面中显示
return { progressBar, progressText, currentVolunteerText };
}
// 更新进度条
function updateProgressBar(progressBar, progressText, current, total, currentVolunteerText) {
var percentage = Math.round((current / total) * 100);
progressBar.style.width = percentage + '%';
progressText.textContent = `进度:${percentage}%`;
currentVolunteerText.textContent = `当前录入:第 ${current} 个志愿者`;
}
// 定义一个函数来模拟点击按钮事件
async function clickButton(selector, delay) {
await new Promise(resolve => setTimeout(resolve, delay));
var button = document.querySelector(selector);
if (button) {
button.click();
console.log(`${selector} 按钮已点击`);
} else {
console.log(`${selector} 按钮未找到`);
}
}
// 定义一个函数来输入姓名
async function inputName(name, delay) { // 将 inputText 作为参数传递
await new Promise(resolve => setTimeout(resolve, delay));
let input = document.querySelectorAll('.el-input__inner')[0];
if (input) {
input.value = name; // 使用参数 name 而不是全局变量 inputText
var event = document.createEvent('HTMLEvents');
event.initEvent("input", true, true);
event.eventType = 'message';
input.dispatchEvent(event);
console.log('姓名已输入');
} else {
console.log('文本框未找到');
}
}
async function waitForUserAction() {
return new Promise(resolve => {
let resolved = false;
const handleClick = (event) => {
if (resolved) return;
// 处理跳过按钮点击
const skipButton = event.target.closest('button');
if (skipButton && skipButton.textContent.trim() === '跳过当前志愿者') {
console.log('用户点击了跳过按钮');
resolved = true;
resolve('SKIP');
return;
}
// 处理添加补录按钮点击
const nextButton = event.target.closest('button.el-button.el-button--primary');
if (nextButton && nextButton.querySelector('span')?.textContent.trim() === '添加补录') {
console.log('用户点击了“添加补录”按钮');
resolved = true;
resolve('CONTINUE');
return;
}
};
// 使用捕获阶段监听,确保能捕获到动态创建的按钮
document.addEventListener('click', handleClick, { capture: true });
// 添加超时检查
const timeout = setTimeout(() => {
if (!resolved) {
console.log('等待用户操作超时');
resolved = true;
resolve('TIMEOUT');
}
}, 30000); // 30秒超时
// 清理函数
return () => {
document.removeEventListener('click', handleClick, { capture: true });
clearTimeout(timeout);
};
});
}
// 定义一个函数来勾选单选框并点击“添加补录”
async function checkCheckbox(delay) {
await new Promise(resolve => setTimeout(resolve, delay));
var checkboxes = document.querySelectorAll('.el-checkbox__inner');
if (checkboxes.length > 4) {
console.log('超过4个单选框被找到,等待用户操作');
alert('出现重名,请手动勾选志愿者,并点击“添加补录”');
await waitForUserAction(); // 等待用户点击“添加补录”
}
else if(checkboxes.length < 4){
// 查无此人的情况,需要手动勾选
console.log('查无此人,提供跳过或手动选择的选项');
alert('查无此人,请手动勾选志愿者并点击“添加补录”,或点击“跳过”按钮');
const result = await waitForUserAction(); // 等待用户点击“添加补录”或“跳过”
console.log(result);
if (result === 'SKIP') {
return 'SKIP'; // 跳过当前志愿者
}else if (result === 'CONTINUE') {
return 'CONTINUE';
}
}else{
// 正常情况
checkboxes.forEach((checkbox) => checkbox.click());
console.log('单选框已勾选');
await clickButton('.el-button.el-button--primary[style*="margin-bottom: 20px;"]', 500); // 点击“添加补录”按钮
nextButtonEnabled = true; // 标志已完成“添加补录”
}
}
// 主处理函数
async function processNames(names){
const { progressBar, progressText, currentVolunteerText } = createProgressBar(); // 创建进度条
for (let i = 0; i < names.length; i++) {
console.log(`正在处理志愿者:${names[i]}`);
await clickButton('.el-button.el-button--primary', 1000); // 打开输入页面
await inputName(names[i], 1000); // 输入志愿者姓名
await clickButton('.queryOrgBtn', 500); // 点击查询按钮
const result = await checkCheckbox(500);
if (result === 'SKIP') {
console.log(`跳过志愿者:${names[i]}`);
// 记录当前志愿者的名字
volunteersSkipped.push(names[i]);
console.log(`记录志愿者:${names[i]}`);
continue; // 跳过当前志愿者,进入下一个
}else if (result === 'CONTINUE') {
console.log(`继续处理志愿者:${names[i]}`);
}
// 更新进度条
updateProgressBar(progressBar, progressText, i + 1, names.length, currentVolunteerText);
// 等待用户完成“添加补录”操作
await clickButton('.el-button.el-button--primary[style*="display: block; margin: 0px auto;"]', 500); // 点击“下一步”按钮
console.log('“下一步”已被点击');
nextButtonEnabled = false; // 重置标志
};
// 在最后显示被跳过的志愿者名字
if (volunteersSkipped.length > 0) {
alert(`录用完成,被跳过的的志愿者:${volunteersSkipped.join(', ')}`);
}
}
// 创建按钮
createImageInput();
createExcelInput();
createSkipButton();
// 定义菜单命令:开始
let menu1 = GM_registerMenuCommand('开始', function () {
if (names.length === 0) {
alert('请先上传并加载 Excel 文件!');
return;
}
processNames(names);
}, 'o');
})();