反诈中心

所有网站-反诈中心

Ekde 2024/11/16. Vidu La ĝisdata versio.

  1. // ==UserScript==
  2. // @name 反诈中心
  3. // @namespace none
  4. // @version 0.0.2
  5. // @description 所有网站-反诈中心
  6. // @author yangrenrui & _s_z_y_ & Murasame
  7. // @match *://*/*
  8. // @match *://*
  9. // @icon https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://llong.tech&size=64
  10. // @license MIT
  11. // @grant none
  12. // ==/UserScript==
  13. (function() {
  14. 'use strict';
  15.  
  16. // 获取目标关键词列表和正则表达式列表
  17. let targetStrings = JSON.parse(localStorage.getItem('targetStrings')) || ['florr','bilibili','mihoyo','hornex'];
  18. let targetRegexes = JSON.parse(localStorage.getItem('targetRegexes')) || ['/^https?:\/\/([a-zA-Z0-9-]+\.)?bilibili\.(com|cn)\/.*/','/^https?:\/\/([a-zA-Z0-9-]+\.)?mihoyo\.(com|cn)\/.*/', '/^https?:\\/\\/malicious\\.site\\//','/^https?:\/\/([a-zA-Z0-9-]+\.)?florr\.io\/.*/','/^https?:\/\/([a-zA-Z0-9-]+\.)?hornex\.pro\/.*/'];
  19.  
  20. // 解析关键词列表,支持正则表达式
  21. let targetPatterns = [];
  22. function updatePatterns() {
  23. targetPatterns = targetStrings.map(str => str).concat(targetRegexes.map(str => {
  24. if (str.startsWith('/') && str.endsWith('/')) {
  25. try {
  26. let patternBody = str.slice(1, -1);
  27. return new RegExp(patternBody);
  28. } catch (e) {
  29. console.error(`无效的正则表达式: ${str}`);
  30. return null;
  31. }
  32. } else {
  33. console.warn(`正则表达式模式中的项应以斜杠包裹: ${str}`);
  34. return null;
  35. }
  36. }).filter(item => item !== null));
  37. }
  38. updatePatterns();
  39.  
  40. // 创建提示弹窗
  41. function createPopup(message, type = 'info') {
  42. const popup = document.createElement('div');
  43. popup.className = `custom-popup ${type}`;
  44. popup.innerText = message;
  45. document.body.appendChild(popup);
  46.  
  47. // 显示动画
  48. setTimeout(() => {
  49. popup.classList.add('show');
  50. }, 10);
  51.  
  52. // 自动隐藏
  53. setTimeout(() => {
  54. popup.classList.remove('show');
  55. // 移除元素
  56. setTimeout(() => {
  57. document.body.removeChild(popup);
  58. }, 300);
  59. }, 500);
  60. }
  61.  
  62. // 注入自定义CSS
  63. const style = document.createElement('style');
  64. style.innerHTML = `
  65. /* 提示弹窗样式 */
  66. .custom-popup {
  67. position: fixed;
  68. top: 20px;
  69. right: 20px;
  70. background-color: rgba(50, 50, 50, 0.9);
  71. color: #fff;
  72. padding: 10px 20px;
  73. border-radius: 5px;
  74. opacity: 0;
  75. transition: opacity 0.3s, transform 0.3s;
  76. z-index: 10005;
  77. font-family: Arial, sans-serif;
  78. pointer-events: none;
  79. }
  80. .custom-popup.show {
  81. opacity: 1;
  82. transform: translateY(0);
  83. }
  84. .custom-popup.info {
  85. background-color: #e74c3c;
  86. }
  87. .custom-popup.success {
  88. background-color: rgba(76, 175, 80, 0.9);
  89. }
  90. .custom-popup.error {
  91. background-color: rgba(244, 67, 54, 0.9);
  92. }
  93. .custom-popup.warning {
  94. background-color: rgba(255, 152, 0, 0.9);
  95. }
  96.  
  97. /* 编辑界面样式 */
  98. #editUI {
  99. position: fixed;
  100. top: 50%;
  101. left: 50%;
  102. transform: translate(-50%, -50%);
  103. z-index: 10002;
  104. background-color: #fff;
  105. border: 2px solid #333;
  106. padding: 25px;
  107. border-radius: 10px;
  108. box-shadow: 0 4px 16px rgba(0,0,0,0.3);
  109. width: 700px;
  110. max-height: 80vh;
  111. overflow-y: auto;
  112. font-family: Arial, sans-serif;
  113. }
  114. #editUI h2 {
  115. text-align: center;
  116. margin-top: 0;
  117. }
  118. #editUI h3 {
  119. margin-bottom: 10px;
  120. }
  121. #editUI button {
  122. font-size: 14px;
  123. }
  124. #editUI .section {
  125. margin-bottom: 30px;
  126. }
  127. #editUI .item-block {
  128. border: 1px solid #ccc;
  129. padding: 10px;
  130. border-radius: 5px;
  131. background-color: #f5f5f5;
  132. display: flex;
  133. align-items: center;
  134. justify-content: space-between;
  135. position: relative;
  136. }
  137. #editUI .item-block code {
  138. font-family: Consolas, monospace;
  139. }
  140. #editUI .item-buttons {
  141. display: flex;
  142. gap: 5px;
  143. }
  144. #editUI .item-buttons button {
  145. background: none;
  146. border: none;
  147. cursor: pointer;
  148. font-size: 18px;
  149. }
  150. #editUI .add-button {
  151. padding: 8px 16px;
  152. background-color: #2196F3;
  153. color: #fff;
  154. border: none;
  155. border-radius: 4px;
  156. cursor: pointer;
  157. font-size: 14px;
  158. }
  159. #editUI .save-cancel-buttons {
  160. display: flex;
  161. justify-content: flex-end;
  162. gap: 10px;
  163. margin-top: 15px;
  164. }
  165. #editUI .save-cancel-buttons button {
  166. padding: 10px 20px;
  167. border: none;
  168. border-radius: 4px;
  169. cursor: pointer;
  170. font-size: 14px;
  171. }
  172. #editUI .save-button {
  173. background-color: #4CAF50;
  174. color: #fff;
  175. }
  176. #editUI .cancel-button {
  177. background-color: #f44336;
  178. color: #fff;
  179. }
  180.  
  181. /* 提示框样式 */
  182. .tooltip-custom {
  183. position: absolute;
  184. background-color: rgba(0, 0, 0, 0.8);
  185. color: #fff;
  186. padding: 8px 12px;
  187. border-radius: 6px;
  188. display: none;
  189. font-size: 14px;
  190. box-shadow: 0 2px 8px rgba(0,0,0,0.3);
  191. z-index: 10000;
  192. pointer-events: none;
  193. transition: opacity 0.3s;
  194. }
  195. .tooltip-custom.show {
  196. display: block;
  197. opacity: 1;
  198. }
  199. `;
  200. document.head.appendChild(style);
  201.  
  202. // 创建提示框
  203. const tooltip = document.createElement('div');
  204. tooltip.className = 'tooltip-custom';
  205. document.body.appendChild(tooltip);
  206.  
  207. // 创建确认窗口
  208. const confirmDialog = document.createElement('div');
  209. confirmDialog.style.position = 'absolute';
  210. confirmDialog.style.backgroundColor = '#fff';
  211. confirmDialog.style.color = '#000';
  212. confirmDialog.style.padding = '15px 20px';
  213. confirmDialog.style.borderRadius = '8px';
  214. confirmDialog.style.display = 'none';
  215. confirmDialog.style.fontSize = '14px';
  216. confirmDialog.style.boxShadow = '0 4px 12px rgba(0,0,0,0.4)';
  217. confirmDialog.style.zIndex = '10001';
  218. confirmDialog.style.width = '300px';
  219. confirmDialog.style.boxSizing = 'border-box';
  220. document.body.appendChild(confirmDialog);
  221.  
  222. // 确认内容
  223. const confirmText = document.createElement('p');
  224. confirmText.innerText = '您即将打开一个疑似诈骗链接:';
  225. confirmText.style.margin = '0 0 10px 0';
  226. confirmDialog.appendChild(confirmText);
  227.  
  228. const urlText = document.createElement('p');
  229. urlText.style.wordBreak = 'break-all';
  230. urlText.style.margin = '0 0 10px 0';
  231. confirmDialog.appendChild(urlText);
  232.  
  233. // 按钮容器
  234. const buttonContainer = document.createElement('div');
  235. buttonContainer.style.textAlign = 'right';
  236. buttonContainer.style.display = 'flex';
  237. buttonContainer.style.justifyContent = 'flex-end';
  238. buttonContainer.style.gap = '10px';
  239. confirmDialog.appendChild(buttonContainer);
  240.  
  241. const confirmButton = document.createElement('button');
  242. confirmButton.innerText = '确定 (Enter)';
  243. confirmButton.style.padding = '6px 12px';
  244. confirmButton.style.backgroundColor = '#4CAF50';
  245. confirmButton.style.color = '#fff';
  246. confirmButton.style.border = 'none';
  247. confirmButton.style.borderRadius = '4px';
  248. confirmButton.style.cursor = 'pointer';
  249. confirmButton.style.fontSize = '14px';
  250. confirmButton.onclick = () => {
  251. window.open(confirmButton.linkHref, '_blank');
  252. hideConfirmDialog();
  253. createPopup('已打开链接', 'success');
  254. };
  255. buttonContainer.appendChild(confirmButton);
  256.  
  257. const cancelButton = document.createElement('button');
  258. cancelButton.innerText = '取消 (Esc)';
  259. cancelButton.style.padding = '6px 12px';
  260. cancelButton.style.backgroundColor = '#f44336';
  261. cancelButton.style.color = '#fff';
  262. cancelButton.style.border = 'none';
  263. cancelButton.style.borderRadius = '4px';
  264. cancelButton.style.cursor = 'pointer';
  265. cancelButton.style.fontSize = '14px';
  266. cancelButton.onclick = () => {
  267. hideConfirmDialog();
  268. createPopup('已取消', 'info');
  269. };
  270. buttonContainer.appendChild(cancelButton);
  271.  
  272. let currentLinkHref = '';
  273. let mouseX = 0;
  274. let mouseY = 0;
  275.  
  276. // 显示确认窗口
  277. function showConfirmDialog(x, y, href) {
  278. currentLinkHref = href;
  279. urlText.innerText = href;
  280. confirmDialog.style.left = `${x + 10}px`;
  281. confirmDialog.style.top = `${y + 10}px`;
  282. confirmDialog.style.display = 'block';
  283. confirmButton.linkHref = href;
  284. confirmButton.focus();
  285. }
  286.  
  287. // 隐藏确认窗口
  288. function hideConfirmDialog() {
  289. confirmDialog.style.display = 'none';
  290. currentLinkHref = '';
  291. }
  292.  
  293. // 监听鼠标移动以记录位置
  294. document.addEventListener('mousemove', function(event) {
  295. mouseX = event.pageX;
  296. mouseY = event.pageY;
  297. });
  298.  
  299. // 处理鼠标点击事件
  300. document.addEventListener('click', function(event) {
  301. const link = event.target.closest('a');
  302. if (link && link.href) {
  303. for (let pattern of targetPatterns) {
  304. if (typeof pattern === 'string') {
  305. if (link.href.includes(pattern)) {
  306. event.preventDefault();
  307. showConfirmDialog(mouseX, mouseY, link.href);
  308. return;
  309. }
  310. } else if (pattern instanceof RegExp) {
  311. if (pattern.test(link.href)) {
  312. event.preventDefault();
  313. showConfirmDialog(mouseX, mouseY, link.href);
  314. return;
  315. }
  316. }
  317. }
  318. }
  319. });
  320.  
  321. // 处理键盘事件
  322. document.addEventListener('keydown', function(e) {
  323. if (confirmDialog.style.display === 'block') {
  324. if (e.key === 'Enter') {
  325. e.preventDefault();
  326. window.open(confirmButton.linkHref, '_blank');
  327. hideConfirmDialog();
  328. createPopup('已打开链接', 'success');
  329. } else if (e.key === 'Escape') {
  330. e.preventDefault();
  331. hideConfirmDialog();
  332. createPopup('已取消', 'info');
  333. }
  334. }
  335.  
  336. // Ctrl + M 打开编辑界面
  337. if (e.ctrlKey && (e.key === 'm' || e.key === 'M')) {
  338. e.preventDefault();
  339. showEditUI();
  340. }
  341. });
  342.  
  343. // 监听鼠标悬停显示提示框
  344. function handleMouseOver(event) {
  345. const link = event.target.closest('a');
  346. if (link && link.href) {
  347. for (let pattern of targetPatterns) {
  348. if (typeof pattern === 'string') {
  349. if (link.href.includes(pattern)) {
  350. tooltip.innerText = `疑似诈骗链接 (关键字匹配): ${pattern}`;
  351. tooltip.style.left = `${event.pageX + 10}px`;
  352. tooltip.style.top = `${event.pageY + 10}px`;
  353. tooltip.classList.add('show');
  354. return;
  355. }
  356. } else if (pattern instanceof RegExp) {
  357. if (pattern.test(link.href)) {
  358. tooltip.innerText = `疑似诈骗链接 (正则匹配): ${pattern}`;
  359. tooltip.style.left = `${event.pageX + 10}px`;
  360. tooltip.style.top = `${event.pageY + 10}px`;
  361. tooltip.classList.add('show');
  362. return;
  363. }
  364. }
  365. }
  366. tooltip.classList.remove('show');
  367. }
  368. }
  369.  
  370. // 监听鼠标移出隐藏提示框
  371. function handleMouseOut() {
  372. tooltip.classList.remove('show');
  373. }
  374.  
  375. document.addEventListener('mousemove', handleMouseOver);
  376. document.addEventListener('mouseout', handleMouseOut);
  377.  
  378. // 创建编辑关键词和正则表达式的UI
  379. function showEditUI() {
  380. if (document.getElementById('editUI')) return;
  381. const editUI = document.createElement('div');
  382. editUI.id = 'editUI';
  383.  
  384. // 匹配关键字模式部分
  385. const keywordSection = document.createElement('div');
  386. keywordSection.className = 'section';
  387.  
  388. const keywordTitle = document.createElement('h3');
  389. keywordTitle.innerText = '匹配关键字模式';
  390. keywordSection.appendChild(keywordTitle);
  391.  
  392. const keywordList = document.createElement('div');
  393. keywordList.id = 'keywordList';
  394. keywordSection.appendChild(keywordList);
  395.  
  396. // 渲染关键词列表
  397. function renderKeywordList() {
  398. keywordList.innerHTML = '';
  399. targetStrings.forEach((keyword, index) => {
  400. const keywordBlock = document.createElement('div');
  401. keywordBlock.className = 'item-block';
  402.  
  403. const keywordDisplay = document.createElement('div');
  404. keywordDisplay.innerHTML = `<code>${keyword}</code>`;
  405. keywordBlock.appendChild(keywordDisplay);
  406.  
  407. const buttonGroup = document.createElement('div');
  408. buttonGroup.className = 'item-buttons';
  409.  
  410. const editButton = document.createElement('button');
  411. editButton.innerHTML = '🖊';
  412. editButton.title = '编辑';
  413. editButton.onclick = () => {
  414. enterEditMode(keywordBlock, 'keyword', index, keyword);
  415. };
  416. buttonGroup.appendChild(editButton);
  417.  
  418. const deleteButton = document.createElement('button');
  419. deleteButton.innerHTML = '×';
  420. deleteButton.title = '删除';
  421. deleteButton.onclick = () => {
  422. createCustomConfirm(`确定要删除关键词 "${keyword}" 吗?`, () => {
  423. targetStrings.splice(index, 1);
  424. updatePatterns();
  425. renderKeywordList();
  426. renderRegexList();
  427. createPopup('已删除', 'success');
  428. });
  429. };
  430. buttonGroup.appendChild(deleteButton);
  431.  
  432. keywordBlock.appendChild(buttonGroup);
  433. keywordList.appendChild(keywordBlock);
  434. });
  435. }
  436.  
  437. renderKeywordList();
  438.  
  439. const addKeywordButton = document.createElement('button');
  440. addKeywordButton.innerText = '+ 添加关键词';
  441. addKeywordButton.className = 'add-button';
  442. addKeywordButton.onclick = () => {
  443. enterAddMode('keyword');
  444. };
  445. keywordSection.appendChild(addKeywordButton);
  446.  
  447. editUI.appendChild(keywordSection);
  448.  
  449. // 高级模式部分
  450. const regexSection = document.createElement('div');
  451. regexSection.className = 'section';
  452.  
  453. const regexTitle = document.createElement('h3');
  454. regexTitle.innerText = '高级模式(正则表达式)';
  455. regexSection.appendChild(regexTitle);
  456.  
  457. const regexList = document.createElement('div');
  458. regexList.id = 'regexList';
  459. regexSection.appendChild(regexList);
  460.  
  461. // 渲染正则表达式列表
  462. function renderRegexList() {
  463. regexList.innerHTML = '';
  464. targetRegexes.forEach((regex, index) => {
  465. const regexBlock = document.createElement('div');
  466. regexBlock.className = 'item-block';
  467.  
  468. const regexDisplay = document.createElement('div');
  469. regexDisplay.innerHTML = `<code>${regex}</code>`;
  470. regexBlock.appendChild(regexDisplay);
  471.  
  472. const buttonGroup = document.createElement('div');
  473. buttonGroup.className = 'item-buttons';
  474.  
  475. const editButton = document.createElement('button');
  476. editButton.innerHTML = '🖊';
  477. editButton.title = '编辑';
  478. editButton.onclick = () => {
  479. enterEditMode(regexBlock, 'regex', index, regex);
  480. };
  481. buttonGroup.appendChild(editButton);
  482.  
  483. const deleteButton = document.createElement('button');
  484. deleteButton.innerHTML = '×';
  485. deleteButton.title = '删除';
  486. deleteButton.onclick = () => {
  487. createCustomConfirm(`确定要删除正则表达式 "${regex}" 吗?`, () => {
  488. targetRegexes.splice(index, 1);
  489. updatePatterns();
  490. renderKeywordList();
  491. renderRegexList();
  492. createPopup('已删除', 'success');
  493. });
  494. };
  495. buttonGroup.appendChild(deleteButton);
  496.  
  497. regexBlock.appendChild(buttonGroup);
  498. regexList.appendChild(regexBlock);
  499. });
  500. }
  501.  
  502. renderRegexList();
  503.  
  504. const addRegexButton = document.createElement('button');
  505. addRegexButton.innerText = '+ 添加正则表达式';
  506. addRegexButton.className = 'add-button';
  507. addRegexButton.onclick = () => {
  508. enterAddMode('regex');
  509. };
  510. regexSection.appendChild(addRegexButton);
  511.  
  512. editUI.appendChild(regexSection);
  513.  
  514. // 按钮容器
  515. const buttonContainerEdit = document.createElement('div');
  516. buttonContainerEdit.className = 'save-cancel-buttons';
  517.  
  518. const saveButtonEdit = document.createElement('button');
  519. saveButtonEdit.innerText = '保存';
  520. saveButtonEdit.className = 'save-button';
  521. saveButtonEdit.onclick = () => {
  522. try {
  523. // 验证所有正则表达式
  524. targetRegexes.forEach(regex => {
  525. if (regex.startsWith('/') && regex.endsWith('/')) {
  526. new RegExp(regex.slice(1, -1));
  527. } else {
  528. throw new Error(`正则表达式必须以斜杠 "/" 包裹: ${regex}`);
  529. }
  530. });
  531.  
  532. // 更新存储
  533. localStorage.setItem('targetStrings', JSON.stringify(targetStrings));
  534. localStorage.setItem('targetRegexes', JSON.stringify(targetRegexes));
  535.  
  536. createPopup('已自动保存', 'success');
  537. document.body.removeChild(editUI);
  538. } catch (error) {
  539. createPopup(`保存失败: ${error.message}`, 'error');
  540. }
  541. };
  542. buttonContainerEdit.appendChild(saveButtonEdit);
  543.  
  544. const cancelButtonEdit = document.createElement('button');
  545. cancelButtonEdit.innerText = '取消';
  546. cancelButtonEdit.className = 'cancel-button';
  547. cancelButtonEdit.onclick = () => {
  548. createPopup('已取消', 'info');
  549. document.body.removeChild(editUI);
  550. };
  551. buttonContainerEdit.appendChild(cancelButtonEdit);
  552.  
  553. editUI.appendChild(buttonContainerEdit);
  554. document.body.appendChild(editUI);
  555. }
  556.  
  557. // 进入编辑模式
  558. function enterEditMode(block, type, index, currentValue) {
  559. block.innerHTML = '';
  560.  
  561. const inputContainer = document.createElement('div');
  562. inputContainer.style.display = 'flex';
  563. inputContainer.style.alignItems = 'center';
  564. inputContainer.style.gap = '10px';
  565. inputContainer.style.width = '100%';
  566.  
  567. const input = document.createElement('input');
  568. input.type = 'text';
  569. input.value = currentValue;
  570. input.style.flexGrow = '1';
  571. input.style.fontFamily = 'Consolas, monospace';
  572. input.style.padding = '5px';
  573. input.style.border = '1px solid #ccc';
  574. input.style.borderRadius = '4px';
  575. inputContainer.appendChild(input);
  576.  
  577. const okButton = document.createElement('button');
  578. okButton.innerText = 'OK (请在编辑后到最下方保存)';
  579. okButton.style.padding = '5px 10px';
  580. okButton.style.backgroundColor = '#4CAF50';
  581. okButton.style.color = '#fff';
  582. okButton.style.border = 'none';
  583. okButton.style.borderRadius = '4px';
  584. okButton.style.cursor = 'pointer';
  585. okButton.style.fontSize = '14px';
  586. okButton.onclick = () => {
  587. saveEdit(block, type, index, input.value.trim());
  588. };
  589. inputContainer.appendChild(okButton);
  590.  
  591. block.appendChild(inputContainer);
  592.  
  593. // 监听 Enter 键
  594. input.addEventListener('keydown', function(e) {
  595. if (e.key === 'Enter') {
  596. e.preventDefault();
  597. saveEdit(block, type, index, input.value.trim());
  598. }
  599. });
  600.  
  601. // 自动聚焦输入框
  602. input.focus();
  603. }
  604.  
  605. // 进入添加模式
  606. function enterAddMode(type) {
  607. let sectionId = type === 'keyword' ? 'keywordList' : 'regexList';
  608. let list = document.getElementById(sectionId);
  609.  
  610. const addBlock = document.createElement('div');
  611. addBlock.className = 'item-block';
  612.  
  613. const inputContainer = document.createElement('div');
  614. inputContainer.style.display = 'flex';
  615. inputContainer.style.alignItems = 'center';
  616. inputContainer.style.gap = '10px';
  617. inputContainer.style.width = '100%';
  618.  
  619. const input = document.createElement('input');
  620. input.type = 'text';
  621. input.placeholder = type === 'keyword' ? '输入新的关键词' : '输入新的正则表达式(需用斜杠包裹)';
  622. input.style.flexGrow = '1';
  623. input.style.fontFamily = 'Consolas, monospace';
  624. input.style.padding = '5px';
  625. input.style.border = '1px solid #ccc';
  626. input.style.borderRadius = '4px';
  627. inputContainer.appendChild(input);
  628.  
  629. const okButton = document.createElement('button');
  630. okButton.innerText = 'OK';
  631. okButton.style.padding = '5px 10px';
  632. okButton.style.backgroundColor = '#4CAF50';
  633. okButton.style.color = '#fff';
  634. okButton.style.border = 'none';
  635. okButton.style.borderRadius = '4px';
  636. okButton.style.cursor = 'pointer';
  637. okButton.style.fontSize = '14px';
  638. okButton.onclick = () => {
  639. const newValue = input.value.trim();
  640. if (newValue === '') {
  641. createPopup('输入不能为空', 'error');
  642. return;
  643. }
  644. if (type === 'regex') {
  645. if (!newValue.startsWith('/') || !newValue.endsWith('/')) {
  646. createPopup('正则表达式必须以斜杠 "/" 包裹。', 'error');
  647. return;
  648. }
  649. try {
  650. new RegExp(newValue.slice(1, -1));
  651. } catch (e) {
  652. createPopup('无效的正则表达式。', 'error');
  653. return;
  654. }
  655. targetRegexes.push(newValue);
  656. } else {
  657. targetStrings.push(newValue);
  658. }
  659. updatePatterns();
  660. renderEditUI();
  661. createPopup('已自动保存', 'success');
  662. };
  663. inputContainer.appendChild(okButton);
  664.  
  665. // 监听 Enter 键
  666. input.addEventListener('keydown', function(e) {
  667. if (e.key === 'Enter') {
  668. e.preventDefault();
  669. okButton.click();
  670. }
  671. });
  672.  
  673. addBlock.appendChild(inputContainer);
  674. list.appendChild(addBlock);
  675.  
  676. // 自动聚焦输入框
  677. input.focus();
  678. }
  679.  
  680. // 保存编辑
  681. function saveEdit(block, type, index, newValue) {
  682. if (type === 'keyword') {
  683. if (newValue === '') {
  684. createPopup('关键词不能为空。', 'error');
  685. return;
  686. }
  687. targetStrings[index] = newValue;
  688. } else if (type === 'regex') {
  689. if (!newValue.startsWith('/') || !newValue.endsWith('/')) {
  690. createPopup('正则表达式必须以斜杠 "/" 包裹。', 'error');
  691. return;
  692. }
  693. try {
  694. new RegExp(newValue.slice(1, -1));
  695. targetRegexes[index] = newValue;
  696. } catch (e) {
  697. createPopup('无效的正则表达式。', 'error');
  698. return;
  699. }
  700. }
  701.  
  702. updatePatterns();
  703. renderEditUI();
  704. createPopup('已自动保存', 'success');
  705. }
  706.  
  707. // 重新渲染编辑界面
  708. function renderEditUI() {
  709. const editUI = document.getElementById('editUI');
  710. if (editUI) {
  711. document.body.removeChild(editUI);
  712. showEditUI();
  713. }
  714. }
  715.  
  716. // 自定义确认弹窗
  717. function createCustomConfirm(message, onConfirm) {
  718. // 创建遮罩
  719. const overlay = document.createElement('div');
  720. overlay.className = 'custom-confirm-overlay';
  721. document.body.appendChild(overlay);
  722.  
  723. // 创建弹窗
  724. const confirmBox = document.createElement('div');
  725. confirmBox.className = 'custom-confirm-box';
  726.  
  727. const msg = document.createElement('p');
  728. msg.innerText = message;
  729. confirmBox.appendChild(msg);
  730.  
  731. const buttons = document.createElement('div');
  732. buttons.style.display = 'flex';
  733. buttons.style.justifyContent = 'flex-end';
  734. buttons.style.gap = '10px';
  735.  
  736. const yesButton = document.createElement('button');
  737. yesButton.innerText = '确定';
  738. yesButton.style.padding = '6px 12px';
  739. yesButton.style.backgroundColor = '#4CAF50';
  740. yesButton.style.color = '#fff';
  741. yesButton.style.border = 'none';
  742. yesButton.style.borderRadius = '4px';
  743. yesButton.style.cursor = 'pointer';
  744. yesButton.onclick = () => {
  745. onConfirm();
  746. document.body.removeChild(overlay);
  747. document.body.removeChild(confirmBox);
  748. };
  749. buttons.appendChild(yesButton);
  750.  
  751. const noButton = document.createElement('button');
  752. noButton.innerText = '取消';
  753. noButton.style.padding = '6px 12px';
  754. noButton.style.backgroundColor = '#f44336';
  755. noButton.style.color = '#fff';
  756. noButton.style.border = 'none';
  757. noButton.style.borderRadius = '4px';
  758. noButton.style.cursor = 'pointer';
  759. noButton.onclick = () => {
  760. document.body.removeChild(overlay);
  761. document.body.removeChild(confirmBox);
  762. };
  763. buttons.appendChild(noButton);
  764.  
  765. confirmBox.appendChild(buttons);
  766. document.body.appendChild(confirmBox);
  767.  
  768. // CSS for custom confirm
  769. const confirmStyle = document.createElement('style');
  770. confirmStyle.innerHTML = `
  771. .custom-confirm-overlay {
  772. position: fixed;
  773. top: 0;
  774. left: 0;
  775. width: 100%;
  776. height: 100%;
  777. background-color: rgba(0,0,0,0.5);
  778. z-index: 10003;
  779. }
  780. .custom-confirm-box {
  781. position: fixed;
  782. top: 50%;
  783. left: 50%;
  784. transform: translate(-50%, -50%);
  785. background-color: #fff;
  786. padding: 20px 30px;
  787. border-radius: 8px;
  788. box-shadow: 0 4px 16px rgba(0,0,0,0.3);
  789. z-index: 10004;
  790. font-family: Arial, sans-serif;
  791. width: 300px;
  792. }
  793. .custom-confirm-box p {
  794. margin-bottom: 20px;
  795. font-size: 14px;
  796. }
  797. .custom-confirm-box button {
  798. font-size: 14px;
  799. }
  800. `;
  801. document.head.appendChild(confirmStyle);
  802. }
  803. })();