Izyz-Helper

Help you to use izyz easier!

当前为 2025-01-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Izyz-Helper
  3. // @namespace https://greasyfork.org/users/1417526
  4. // @version 0.1
  5. // @description Help you to use izyz easier!
  6. // @author Weichenleeeee
  7. // @match https://www.gdzyz.cn/*
  8. // @icon https://www.gdzyz.cn/assets/weblogo.1b6eba63.svg
  9. // @grant GM_registerMenuCommand
  10. // @grant GM_addStyle
  11. // @grant GM_openInTab
  12. // @grant GM_setValue
  13. // @grant GM_getValue
  14. // @grant GM_xmlhttpRequest
  15. // @grant GM_getResourceText
  16. // @require https://unpkg.com/xlsx/dist/xlsx.full.min.js
  17. // ==/UserScript==
  18.  
  19. (function() {
  20. 'use strict';
  21.  
  22. var names = []; // 用实际的名字替换
  23. var nextButtonEnabled = false; // 是否已点击“添加补录”
  24. var skipButtonEnabled = false; // 是否点击了跳过按钮
  25. var volunteersSkipped = [];
  26.  
  27. // 检测XLSX库是否加载成功
  28. setTimeout(function() {
  29. if (typeof XLSX === "undefined") {
  30. console.error("XLSX 库加载失败!");
  31. alert("无法加载 XLSX 库,功能无法使用!");
  32. return;
  33. } else {
  34. console.log("XLSX 库加载成功!");
  35. }
  36. }, 1000); // 延迟1秒检查是否加载成功
  37.  
  38. // 添加图片上传按钮
  39. function createFileInput() {
  40. var input = document.createElement('input');
  41. input.type = 'file';
  42. input.accept = 'image/*'; // 限制为图片文件
  43. input.style.position = 'fixed';
  44. input.style.bottom = '10px';
  45. input.style.left = '10px';
  46. input.style.zIndex = 9999;
  47. input.style.padding = '5px';
  48. input.style.borderRadius = '5px';
  49. input.style.backgroundColor = '#4CAF50';
  50. input.style.color = 'white';
  51. input.style.border = 'none';
  52. input.style.cursor = 'pointer';
  53. input.addEventListener('change', handleFileSelect);
  54. document.body.appendChild(input);
  55. }
  56. // 添加跳过按钮
  57. function createSkipButton() {
  58. var skipButton = document.createElement('button');
  59. skipButton.textContent = '跳过当前志愿者';
  60. skipButton.style.position = 'fixed';
  61. skipButton.style.bottom = '50px'; // 放在“上传文件”按钮的上方
  62. skipButton.style.left = '10px';
  63. skipButton.style.zIndex = 9999;
  64. skipButton.style.padding = '10px';
  65. skipButton.style.borderRadius = '5px';
  66. skipButton.style.backgroundColor = '#f44336'; // 红色按钮
  67. skipButton.style.color = 'white';
  68. skipButton.style.border = 'none';
  69. skipButton.style.cursor = 'pointer';
  70. skipButton.style.fontSize = '14px';
  71. skipButton.addEventListener('click', function() {
  72. skipButtonEnabled = true; // 设置跳过标志
  73. console.log('用户点击了跳过按钮1');
  74. console.log('skipButtonEnabled is ',skipButtonEnabled);
  75. });
  76. document.body.appendChild(skipButton);
  77. }
  78.  
  79. // 百度云OCR配置
  80. const BAIDU_API_KEY = '4rHGTojlMzTYGov3tBHugdI6';
  81. const BAIDU_SECRET_KEY = 'g4dhvSh3KHXfDoljUPRFceVczdVrGued';
  82. let accessToken = '';
  83.  
  84. // 获取百度云access_token
  85. function getAccessToken() {
  86. return new Promise((resolve, reject) => {
  87. const url = `https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=${BAIDU_API_KEY}&client_secret=${BAIDU_SECRET_KEY}`;
  88. GM_xmlhttpRequest({
  89. method: 'GET',
  90. url: url,
  91. onload: function(response) {
  92. try {
  93. const data = JSON.parse(response.responseText);
  94. if (data.access_token) {
  95. accessToken = data.access_token;
  96. console.log('百度云access_token获取成功');
  97. resolve();
  98. } else {
  99. throw new Error('获取access_token失败');
  100. }
  101. } catch (error) {
  102. reject(error);
  103. }
  104. },
  105. onerror: function(error) {
  106. reject(error);
  107. }
  108. });
  109. });
  110. }
  111.  
  112. // 将图片文件转换为base64
  113. function fileToBase64(file) {
  114. return new Promise((resolve, reject) => {
  115. const reader = new FileReader();
  116. reader.onload = () => {
  117. const base64 = reader.result.split(',')[1];
  118. resolve(base64);
  119. };
  120. reader.onerror = reject;
  121. reader.readAsDataURL(file);
  122. });
  123. }
  124.  
  125. // 处理图片选择
  126. async function handleFileSelect(event) {
  127. const file = event.target.files[0];
  128. if (!file || !file.type.startsWith('image/')) {
  129. alert('请上传有效的图片文件');
  130. return;
  131. }
  132.  
  133. try {
  134. // 获取access_token
  135. await getAccessToken();
  136. // 将图片转换为base64
  137. const imageBase64 = await fileToBase64(file);
  138. // 调用百度云OCR API
  139. const ocrUrl = `https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token=${accessToken}`;
  140. return new Promise((resolve, reject) => {
  141. GM_xmlhttpRequest({
  142. method: 'POST',
  143. url: ocrUrl,
  144. headers: {
  145. 'Content-Type': 'application/x-www-form-urlencoded'
  146. },
  147. data: `image=${encodeURIComponent(imageBase64)}&language_type=CHN_ENG`,
  148. onload: function(response) {
  149. try {
  150. const result = JSON.parse(response.responseText);
  151. if (result.words_result) {
  152. names = result.words_result
  153. .map(item => item.words.trim())
  154. .filter(line => line.length > 0)
  155. .map(name => {
  156. // 移除姓名前的数字
  157. name = name.replace(/^\d+/, '').trim();
  158. // 匹配2-4个中文字符的姓名
  159. const chineseNamePattern = /^[\u4e00-\u9fa5]{2,4}$/;
  160. if (chineseNamePattern.test(name)) {
  161. return name;
  162. }
  163. return null;
  164. })
  165. .filter(name => name !== null); // 过滤掉不符合条件的项
  166.  
  167. console.log('从图片中识别出的姓名:', names);
  168. alert('图片已成功识别,姓名已提取!');
  169. resolve();
  170. } else {
  171. throw new Error('OCR识别失败');
  172. }
  173. } catch (error) {
  174. reject(error);
  175. }
  176. },
  177. onerror: function(error) {
  178. reject(error);
  179. }
  180. });
  181. });
  182. } catch (error) {
  183. console.error('图片识别失败:', error);
  184. alert('图片识别失败,请确保图片清晰且包含中文文本');
  185. }
  186. }
  187.  
  188.  
  189. // 创建并显示进度条
  190. function createProgressBar() {
  191. var progressContainer = document.createElement('div');
  192. progressContainer.style.position = 'fixed';
  193. progressContainer.style.bottom = '90px';
  194. progressContainer.style.left = '10px';
  195. progressContainer.style.zIndex = 9999;
  196. progressContainer.style.width = '300px';
  197. progressContainer.style.height = '30px';
  198. progressContainer.style.backgroundColor = '#e0e0e0';
  199. progressContainer.style.borderRadius = '5px';
  200. var progressBar = document.createElement('div');
  201. progressBar.style.height = '100%';
  202. progressBar.style.width = '0%';
  203. progressBar.style.backgroundColor = '#4CAF50';
  204. progressBar.style.borderRadius = '5px';
  205. progressContainer.appendChild(progressBar);
  206. var progressText = document.createElement('span');
  207. progressText.style.position = 'absolute';
  208. progressText.style.top = '50%';
  209. progressText.style.left = '50%';
  210. progressText.style.transform = 'translate(-50%, -50%)';
  211. progressText.style.color = 'white';
  212. progressText.style.fontSize = '14px';
  213. progressContainer.appendChild(progressText);
  214. var currentVolunteerText = document.createElement('span');
  215. currentVolunteerText.style.position = 'fixed';
  216. currentVolunteerText.style.bottom = '130px';
  217. currentVolunteerText.style.left = '10px';
  218. currentVolunteerText.style.zIndex = 9999;
  219. currentVolunteerText.style.fontSize = '14px';
  220. currentVolunteerText.style.color = '#4CAF50';
  221. currentVolunteerText.style.fontWeight = 'bold';
  222. currentVolunteerText.textContent = '当前录入:第 0 个志愿者';
  223. document.body.appendChild(currentVolunteerText);
  224. document.body.appendChild(progressContainer); // 确保进度条在页面中显示
  225. return { progressBar, progressText, currentVolunteerText };
  226. }
  227.  
  228. // 更新进度条
  229. function updateProgressBar(progressBar, progressText, current, total, currentVolunteerText) {
  230. var percentage = Math.round((current / total) * 100);
  231. progressBar.style.width = percentage + '%';
  232. progressText.textContent = `进度:${percentage}%`;
  233. currentVolunteerText.textContent = `当前录入:第 ${current} 个志愿者`;
  234. }
  235.  
  236.  
  237. // 定义一个函数来模拟点击按钮事件
  238. async function clickButton(selector, delay) {
  239. await new Promise(resolve => setTimeout(resolve, delay));
  240. var button = document.querySelector(selector);
  241. if (button) {
  242. button.click();
  243. console.log(`${selector} 按钮已点击`);
  244. } else {
  245. console.log(`${selector} 按钮未找到`);
  246. }
  247. }
  248.  
  249. // 定义一个函数来输入姓名
  250. async function inputName(name, delay) { // 将 inputText 作为参数传递
  251. await new Promise(resolve => setTimeout(resolve, delay));
  252. let input = document.querySelectorAll('.el-input__inner')[0];
  253. if (input) {
  254. input.value = name; // 使用参数 name 而不是全局变量 inputText
  255. var event = document.createEvent('HTMLEvents');
  256. event.initEvent("input", true, true);
  257. event.eventType = 'message';
  258. input.dispatchEvent(event);
  259. console.log('姓名已输入');
  260. } else {
  261. console.log('文本框未找到');
  262. }
  263. }
  264.  
  265. async function waitForUserAction() {
  266. return new Promise(resolve => {
  267. let resolved = false;
  268. const handleClick = (event) => {
  269. if (resolved) return;
  270. // 处理跳过按钮点击
  271. const skipButton = event.target.closest('button');
  272. if (skipButton && skipButton.textContent.trim() === '跳过当前志愿者') {
  273. console.log('用户点击了跳过按钮');
  274. resolved = true;
  275. resolve('SKIP');
  276. return;
  277. }
  278.  
  279. // 处理添加补录按钮点击
  280. const nextButton = event.target.closest('button.el-button.el-button--primary');
  281. if (nextButton && nextButton.querySelector('span')?.textContent.trim() === '添加补录') {
  282. console.log('用户点击了“添加补录”按钮');
  283. resolved = true;
  284. resolve('CONTINUE');
  285. return;
  286. }
  287. };
  288.  
  289. // 使用捕获阶段监听,确保能捕获到动态创建的按钮
  290. document.addEventListener('click', handleClick, { capture: true });
  291.  
  292. // 添加超时检查
  293. const timeout = setTimeout(() => {
  294. if (!resolved) {
  295. console.log('等待用户操作超时');
  296. resolved = true;
  297. resolve('TIMEOUT');
  298. }
  299. }, 30000); // 30秒超时
  300.  
  301. // 清理函数
  302. return () => {
  303. document.removeEventListener('click', handleClick, { capture: true });
  304. clearTimeout(timeout);
  305. };
  306. });
  307. }
  308.  
  309. // 定义一个函数来勾选单选框并点击“添加补录”
  310. async function checkCheckbox(delay) {
  311. await new Promise(resolve => setTimeout(resolve, delay));
  312. var checkboxes = document.querySelectorAll('.el-checkbox__inner');
  313. if (checkboxes.length > 4) {
  314. console.log('超过4个单选框被找到,等待用户操作');
  315. alert('出现重名,请手动勾选志愿者,并点击“添加补录”');
  316. await waitForUserAction(); // 等待用户点击“添加补录”
  317. }
  318. else if(checkboxes.length < 4){
  319. // 查无此人的情况,需要手动勾选
  320. console.log('查无此人,提供跳过或手动选择的选项');
  321. alert('查无此人,请手动勾选志愿者并点击“添加补录”,或点击“跳过”按钮');
  322. const result = await waitForUserAction(); // 等待用户点击“添加补录”或“跳过”
  323. console.log(result);
  324. if (result === 'SKIP') {
  325. return 'SKIP'; // 跳过当前志愿者
  326. }else if (result === 'CONTINUE') {
  327. return 'CONTINUE';
  328. }
  329. }else{
  330. // 正常情况
  331. checkboxes.forEach((checkbox) => checkbox.click());
  332. console.log('单选框已勾选');
  333. await clickButton('.el-button.el-button--primary[style*="margin-bottom: 20px;"]', 500); // 点击“添加补录”按钮
  334. nextButtonEnabled = true; // 标志已完成“添加补录”
  335. }
  336. }
  337.  
  338. // 主处理函数
  339. async function processNames(names){
  340. const { progressBar, progressText, currentVolunteerText } = createProgressBar(); // 创建进度条
  341. for (let i = 0; i < names.length; i++) {
  342. console.log(`正在处理志愿者:${names[i]}`);
  343. await clickButton('.el-button.el-button--primary', 1000); // 打开输入页面
  344. await inputName(names[i], 1000); // 输入志愿者姓名
  345. await clickButton('.queryOrgBtn', 500); // 点击查询按钮
  346. const result = await checkCheckbox(500);
  347. if (result === 'SKIP') {
  348. console.log(`跳过志愿者:${names[i]}`);
  349. // 记录当前志愿者的名字
  350. volunteersSkipped.push(names[i]);
  351. console.log(`记录志愿者:${names[i]}`);
  352. continue; // 跳过当前志愿者,进入下一个
  353. }else if (result === 'CONTINUE') {
  354. console.log(`继续处理志愿者:${names[i]}`);
  355. }
  356. // 更新进度条
  357. updateProgressBar(progressBar, progressText, i + 1, names.length, currentVolunteerText);
  358. // 等待用户完成“添加补录”操作
  359. await clickButton('.el-button.el-button--primary[style*="display: block; margin: 0px auto;"]', 500); // 点击“下一步”按钮
  360. console.log('“下一步”已被点击');
  361. nextButtonEnabled = false; // 重置标志
  362. };
  363. // 在最后显示被跳过的志愿者名字
  364. if (volunteersSkipped.length > 0) {
  365. alert(`录用完成,被跳过的的志愿者:${volunteersSkipped.join(', ')}`);
  366. }
  367. }
  368.  
  369. // 创建按钮
  370. createFileInput();
  371. createSkipButton();
  372.  
  373. // 定义菜单命令:开始
  374. let menu1 = GM_registerMenuCommand('开始', function () {
  375. if (names.length === 0) {
  376. alert('请先上传并加载 Excel 文件!');
  377. return;
  378. }
  379. processNames(names);
  380. }, 'o');
  381.  
  382. })();