link工具

点击复制,提取链接

  1. // ==UserScript==
  2. // @name link工具
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.10.4
  5. // @description 点击复制,提取链接
  6. // @author lwj
  7. // @match *://m.linkmcn.com/*
  8. // @match https://m.linkmcn.com/tableCard/redirect*
  9. // @match *://detail.tmall.com/item*
  10. // @match *://item.taobao.com/item*
  11. // @match *://chaoshi.detail.tmall.com/item*
  12. // @match *://traveldetail.fliggy.com/item*
  13. // @match *://detail.tmall.hk/hk/item*
  14. // @license MIT
  15. // @grant GM_setValue
  16. // @grant GM_getValue
  17. // @grant GM_setClipboard
  18. // @grant GM_xmlhttpRequest
  19. // ==/UserScript==
  20. /* global Segmentit */
  21.  
  22. (function () {
  23. // 版本号
  24. var versionTitleTxt = 'Version 0.10.4';
  25.  
  26. // 检查当前页面 URL 是否匹配指定的网址
  27. var isHomeURL = function () {
  28. var currentURL = window.location.href;
  29. return currentURL.indexOf("https://m.linkmcn.com/#/live/plan?select=") !== -1;
  30. };
  31.  
  32. // 从 localStorage 中获取上一次的 URL,如果没有则设置为空字符串
  33. let lastCurrentURL = localStorage.getItem('lastCurrentURL') || '';
  34.  
  35. var isTableCardURL = function () {
  36. return window.location.href.indexOf("https://m.linkmcn.com/#/live/plan/tableCard/") !== -1;
  37. };
  38.  
  39. var isBatchPrintURL = function () {
  40. return window.location.href.indexOf("https://m.linkmcn.com/#/live/plan/batchPrint") !== -1;
  41. };
  42.  
  43. var isTmallItemURL = function () {
  44. var url = window.location.href;
  45.  
  46. // 检查URL是否包含任何指定的域名或路径
  47. return (
  48. url.indexOf("https://detail.tmall.com/item") !== -1 ||
  49. url.indexOf("https://detail.tmall.hk/hk/item") !== -1 ||
  50. url.indexOf("https://item.taobao.com/item") !== -1 ||
  51. url.indexOf("https://chaoshi.detail.tmall.com/item") !== -1 ||
  52. url.indexOf("https://traveldetail.fliggy.com/item") !== -1
  53. );
  54. }
  55.  
  56. var isRedirectUrl = function () {
  57. return window.location.href.indexOf("https://item.taobao.com/item.htm?id=682878335608&link_redirectId=") !== -1;
  58. }
  59.  
  60. var notificationTimer; // 通知计时器
  61. var isHiding = false; // 标志,表示是否正在隐藏通知
  62. let countSort_notificationTimeout = null;// 排序提示定时器
  63.  
  64. let temp_itemId = '';
  65.  
  66. class ToggleButtonComponent {
  67. constructor(localStorageKey, buttonText, dropdownContainer, useSpecialSwitch = 0, defaultState = false) {
  68. this.localStorageKey = localStorageKey;
  69. this.switchState = localStorage.getItem(this.localStorageKey) === null ? defaultState : localStorage.getItem(this.localStorageKey) === 'true';
  70. this.buttonText = buttonText;
  71. this.dropdownContainer = dropdownContainer;
  72. this.useSpecialSwitch = useSpecialSwitch === 1;
  73. this.createComponent();
  74. }
  75.  
  76. createComponent() {
  77. // 创建按钮容器
  78. this.buttonContainer = document.createElement('div');
  79. this.buttonContainer.classList.add('flex', 'items-center', 'dropdown-item');
  80. this.buttonContainer.style.cssText = 'padding: 12px 16px; user-select: none; cursor: pointer; display: flex; justify-content: space-between; align-items: center;';
  81.  
  82. if (this.buttonText === '纯净模式') {
  83. this.buttonContainer.style.paddingTop = '0px';
  84. }
  85. // 创建按钮文本
  86. this.buttonTextElement = document.createElement('span');
  87. this.buttonTextElement.textContent = this.buttonText;
  88. this.buttonTextElement.classList.add('lh-22');
  89.  
  90. this.buttonContainer.appendChild(this.buttonTextElement);
  91.  
  92. if (this.useSpecialSwitch) {
  93. // 创建特殊开关样式按钮
  94. this.createSpecialSwitch();
  95. } else {
  96. // 创建普通按钮
  97. this.createStandardButton();
  98. }
  99.  
  100. // 将按钮容器添加到下拉容器中
  101. this.dropdownContainer.appendChild(this.buttonContainer);
  102.  
  103. // 创建对应的空的子页面并设置样式
  104. this.secondaryContent = document.createElement('div');
  105. this.secondaryContent.id = this.localStorageKey + '-secondary-content';
  106. this.secondaryContent.style.cssText = 'margin: 0px 10px; display: none; padding: 10px 12px; background: #E9E9E9; border: 1px solid #D2D2D2; border-radius: 10px;';
  107.  
  108. // 将子页面添加到按钮容器的下方
  109. this.dropdownContainer.appendChild(this.secondaryContent);
  110. }
  111.  
  112. createStandardButton() {
  113. this.button = document.createElement('button');
  114. this.button.id = 'main_standardFunShowButton';
  115. this.button.style.cssText = 'background: none; border: none; font-size: 16px; cursor: pointer; transition: transform 0.3s; margin: 0px 6px';
  116. this.button.appendChild(createSVGIcon());
  117. this.buttonContainer.appendChild(this.button);
  118.  
  119. // 绑定点击事件
  120. this.button.addEventListener('click', () => this.handleStandardButtonClick());
  121. // 给标题绑定模拟点击开关事件
  122. this.buttonTextElement.addEventListener('click', () => this.titleToSwitchClick());
  123. }
  124.  
  125. handleStandardButtonClick() {
  126. // 切换二级页面显示状态
  127. const secondaryContent = document.getElementById(this.localStorageKey + '-secondary-content');
  128. if (secondaryContent) {
  129. secondaryContent.style.display = secondaryContent.style.display === 'block' ? 'none' : 'block';
  130. } else {
  131. console.log(`${this.buttonText} 二级页面异常`);
  132. }
  133.  
  134. // 旋转按钮图标
  135. const icon = this.button.querySelector('svg');
  136. const rotation = icon.style.transform === 'rotate(90deg)' ? 'rotate(0deg)' : 'rotate(90deg)';
  137. icon.style.transform = rotation;
  138. }
  139.  
  140. createSpecialSwitch() {
  141. this.button = document.createElement('button');
  142. this.button.innerHTML = '<div class="ant-switch-handle"></div><span class="ant-switch-inner"><span class="ant-switch-inner-checked"></span><span class="ant-switch-inner-unchecked"></span></span>';
  143. this.button.setAttribute('type', 'button');
  144. this.button.setAttribute('role', 'switch');
  145. this.button.setAttribute('aria-checked', this.switchState); // 设置开关状态
  146. this.button.classList.add('ant-switch', 'css-9fw9up');
  147. if (this.switchState) {
  148. this.button.classList.add('ant-switch-checked');
  149. }
  150. this.buttonContainer.appendChild(this.button);
  151. this.buttonContainer.style.cursor = 'default';
  152.  
  153. // 添加点击事件监听
  154. this.button.addEventListener('click', () => this.handleSwitchClick());
  155. }
  156.  
  157. handleSwitchClick() {
  158. // 切换开关状态
  159. const newState = this.button.getAttribute('aria-checked') === 'true' ? 'false' : 'true';
  160. this.button.setAttribute('aria-checked', newState);
  161.  
  162. if (newState === 'true') {
  163. this.button.classList.add('ant-switch-checked');
  164. showNotification(`${this.buttonText}:开启`);
  165. } else {
  166. this.button.classList.remove('ant-switch-checked');
  167. showNotification(`${this.buttonText}:关闭`);
  168. }
  169.  
  170. // 更新开关状态
  171. updateSwitchState(this.localStorageKey, newState === 'true');
  172. }
  173.  
  174. titleToSwitchClick() {
  175. // 模拟点击开关按钮
  176. this.button.click();
  177. }
  178.  
  179. remove_titleToSwitchClick() {
  180. // 移除模拟点击开关按钮
  181. this.buttonTextElement.removeEventListener('click', () => this.titleToSwitchClick());
  182. }
  183.  
  184. // 获取开关状态
  185. getSwitchState() {
  186. if (noneDrawColor()) {
  187. return false;
  188. } else {
  189. return this.button.getAttribute('aria-checked') === 'true';
  190. }
  191. }
  192.  
  193. // 获取子页面元素
  194. getSecondaryContent() {
  195. return this.secondaryContent;
  196. }
  197. }
  198.  
  199. class SonToggleButtonComponent extends ToggleButtonComponent {
  200. constructor(localStorageKey, buttonText, dropdownContainer, descriptionText, useSpecialSwitch = false, defaultState = false) {
  201. super(localStorageKey, buttonText, dropdownContainer, useSpecialSwitch, defaultState);
  202. this.buttonText = buttonText;
  203. this.descriptionText = descriptionText;
  204.  
  205. this.createAdditionalContent();
  206. }
  207.  
  208. // 创建附加内容容器
  209. createAdditionalContent() {
  210. // 修改按钮文本样式并去除 class="lh-22"
  211. this.buttonTextElement.style.cssText = 'font-size: 14px; margin: 0;';
  212. this.buttonTextElement.classList.remove('lh-22');
  213.  
  214. // 调整父类的内容容器样式
  215. this.buttonContainer.style.cssText = 'display: flex; user-select: none; justify-content: space-between; align-items: center; padding: 0;';
  216.  
  217. // 新增的说明容器
  218. this.descriptionContainer = document.createElement('div');
  219. this.descriptionContainer.classList.add('description-content');
  220. this.descriptionContainer.style.cssText = 'font-size: 12px; color: #9B9B9B; user-select: none; margin: 5px 0px; padding-bottom: 5px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #D2D2D2; white-space: pre-wrap;';
  221. this.descriptionContainer.textContent = this.descriptionText;
  222.  
  223. if (this.descriptionText === '') this.descriptionContainer.style.paddingBottom = '0px';
  224.  
  225. // 子设置容器
  226. this.childSettingsContainer = document.createElement('div');
  227. this.childSettingsContainer.classList.add('child-settings');
  228. this.childSettingsContainer.style.cssText = 'display: block; justify-content: space-between; align-items: center;';
  229.  
  230. // 初始化子开关容器的样式
  231. this.updateSwitchStyle();
  232.  
  233. // 将说明容器和子设置容器添加到下拉容器中
  234. this.dropdownContainer.appendChild(this.buttonContainer);
  235. this.dropdownContainer.appendChild(this.descriptionContainer);
  236. this.dropdownContainer.appendChild(this.childSettingsContainer);
  237. }
  238.  
  239. // 重写createSpecialSwitch以添加额外的类
  240. createSpecialSwitch() {
  241. // 确保父类方法被正确调用
  242. super.createSpecialSwitch();
  243.  
  244. // 确保 this.button 已初始化
  245. if (!this.button) {
  246. console.error('this.button is not initialized');
  247. return;
  248. }
  249.  
  250. // 将函数定义为对象的方法
  251. this.autoInput_handleSwitchClick = () => {
  252. if (!this.getSwitchState()) {
  253. itemSort_inputConter.showSwitchDiv(true); // 显示输入内容选择框
  254. itemSort_countInputFun.showNumberControlDiv(false); // 隐藏数字控制区
  255. itemSort_countInputFun.toggleDivButtonState(false); // 关闭数字控制按钮
  256. }
  257. };
  258.  
  259. if (this.buttonText === '自动填充') {
  260. // console.log(this.buttonText);
  261. this.button.addEventListener('click', () => this.autoInput_handleSwitchClick());
  262. }
  263.  
  264. // 添加额外的类
  265. this.button.classList.add('ant-switch-small');
  266. }
  267.  
  268. // 独立的方法用于更新开关样式
  269. updateSwitchStyle() {
  270. if (!this.getSwitchState()) {
  271. initializeContainerStyle(this.childSettingsContainer, false); // 使用函数初始化
  272. }
  273. }
  274.  
  275. // 控制单独组件的开启关闭
  276. showSwitchDiv(flag) {
  277. if (flag) {
  278. initializeContainerStyle(this.buttonContainer, true);
  279. initializeContainerStyle(this.descriptionContainer, true);
  280. } else {
  281. initializeContainerStyle(this.buttonContainer, false);
  282. initializeContainerStyle(this.descriptionContainer, false);
  283. }
  284. }
  285.  
  286. createSelectBox(options = ['', '0', '1', '2', '3', '999'], savedKey = this.localStorageKey + '_savedValue', container = this.buttonContainer) {
  287. // 先移除button元素
  288. if (this.button) {
  289. this.buttonContainer.removeChild(this.button);
  290. }
  291.  
  292. // 创建下拉框
  293. const selectBox = document.createElement('select');
  294. selectBox.setAttribute('id', this.localStorageKey + '-select-box');
  295. selectBox.style.cssText = `
  296. font-size: 14px;
  297. margin: 0;
  298. width: 42px;
  299. height: 18px;
  300. border: 1px solid rgb(155, 155, 155);
  301. background-color: rgb(249,249,249);
  302. color: rgb(155, 155, 155);
  303. border-radius: 20px;
  304. padding: 0 2.5%;
  305. -webkit-appearance: none;
  306. -moz-appearance: none;
  307. appearance: none;
  308. position: relative;
  309. cursor: pointer;
  310. `;
  311.  
  312. options.forEach(value => {
  313. const option = document.createElement('option');
  314. option.value = value;
  315. option.style.cssText = `
  316. text-align: center;
  317. padding: 0 5px;
  318. `;
  319. option.textContent = value === '' ? '空' : value;
  320. selectBox.appendChild(option);
  321. });
  322.  
  323. const savedValue = localStorage.getItem(savedKey) || options[1];
  324. selectBox.value = savedValue;
  325.  
  326. selectBox.addEventListener('change', () => {
  327. localStorage.setItem(savedKey, selectBox.value);
  328. updateSwitchState(this.localStorageKey, selectBox.value);
  329.  
  330. // 悬浮时和激活时边框颜色变化
  331. selectBox.style.borderColor = '#ff6200';
  332. });
  333.  
  334. // 悬浮时和激活时边框颜色变化
  335. selectBox.addEventListener('mouseover', function () {
  336. selectBox.style.borderColor = '#ff6200';
  337. });
  338.  
  339. selectBox.addEventListener('mouseout', function () {
  340. if (!selectBox.matches(':focus')) {
  341. selectBox.style.borderColor = 'rgb(155, 155, 155)';
  342. }
  343. });
  344.  
  345. selectBox.addEventListener('focus', function () {
  346. selectBox.style.borderColor = '#ff6200';
  347. });
  348.  
  349. selectBox.addEventListener('blur', function () {
  350. selectBox.style.borderColor = 'rgb(155, 155, 155)';
  351. });
  352.  
  353. container.appendChild(selectBox);
  354. }
  355.  
  356. createDivButton(beforeText, afterText, onClickCallback = null, container = this.buttonContainer) {
  357. // 先移除button元素
  358. if (this.button) {
  359. this.buttonContainer.removeChild(this.button);
  360. }
  361.  
  362. this.beforeText = beforeText;
  363. this.afterText = afterText;
  364. this.onClickCallback = onClickCallback;
  365.  
  366. // 功能开启按钮
  367. this.divButton = document.createElement('div');
  368. this.divButton.setAttribute('id', this.localStorageKey + '_divButton');
  369. this.divButton.textContent = beforeText;
  370. this.divButton.style.cssText = `
  371. font-size: 14px;
  372. margin: 0;
  373. width: 42px;
  374. height: 18px;
  375. border: 1px solid rgb(155, 155, 155);
  376. background-color: rgb(249,249,249);
  377. color: rgb(155, 155, 155);
  378. border-radius: 20px;
  379. padding: 0 2%;
  380. -webkit-appearance: none;
  381. -moz-appearance: none;
  382. appearance: none;
  383. position: relative;
  384. cursor: pointer;
  385. user-select: none;
  386. text-align: center;
  387. `;
  388.  
  389. // 悬浮时和激活时边框颜色变化
  390. this.divButton.addEventListener('mouseover', () => {
  391. if (this.divButton.textContent === beforeText) {
  392. this.divButton.style.borderColor = '#ff6200';
  393. }
  394. else {
  395. this.divButton.style.borderColor = 'rgb(155, 155, 155)';
  396. }
  397. });
  398.  
  399. this.divButton.addEventListener('mouseout', () => {
  400. if (this.divButton.textContent === beforeText) {
  401. this.divButton.style.borderColor = 'rgb(155, 155, 155)';
  402. }
  403. else {
  404. this.divButton.style.borderColor = 'rgb(249, 249, 249)';
  405. }
  406. });
  407.  
  408. // 按钮点击事件
  409. this.divButton.addEventListener('click', () => {
  410. this.toggleDivButtonState();
  411. });
  412.  
  413. container.appendChild(this.divButton);
  414. }
  415.  
  416. // 控制单独组件的开启关闭
  417. toggleDivButtonState(isActivated = null) {
  418. // 如果提供了isActivated参数,则根据参数设置状态,否则根据当前状态切换
  419. const shouldActivate = isActivated !== null ? isActivated : (this.divButton.textContent === this.beforeText);
  420.  
  421. if (shouldActivate) {
  422. this.divButton.textContent = this.afterText;
  423. this.divButton.style.color = '#fff';
  424. this.divButton.style.backgroundColor = '#ff0000';
  425. this.divButton.style.borderColor = '#fff';
  426. showNotification(`${this.buttonText}:开启`);
  427. if (this.onClickCallback) {
  428. this.onClickCallback(true);
  429. }
  430. } else {
  431. this.divButton.textContent = this.beforeText;
  432. this.divButton.style.color = 'rgb(155, 155, 155)';
  433. this.divButton.style.backgroundColor = 'rgb(249,249,249)';
  434. this.divButton.style.borderColor = 'rgb(155, 155, 155)';
  435. showNotification(`${this.buttonText}:关闭`);
  436. if (this.onClickCallback) {
  437. this.onClickCallback(false);
  438. }
  439. }
  440. }
  441.  
  442. // 判断开关开启状态
  443. getDivButtonState() {
  444. if (this.divButton.textContent === this.beforeText) {
  445. return false;// 开关开启状态
  446. } else {
  447. return true;// 开关关闭状态
  448. }
  449. }
  450.  
  451. createNumberControDiv(defaultNumber = 0, single = false, countType = 'none', savedKey = this.localStorageKey + '_savedValue', container = this.buttonContainer) {
  452. if (single) {
  453. //先移除button元素
  454. if (this.button) {
  455. this.buttonContainer.removeChild(this.button);
  456. }
  457. }
  458.  
  459. // 数字文本及SVG控制区
  460. this.numberControDiv = document.createElement('div');
  461. this.numberControDiv.style.cssText = `
  462. font-size: 14px;
  463. display: flex;
  464. align-items: center;
  465. min-width: 42px;
  466. height: 18px;
  467. width: auto;
  468. border: 1px solid rgb(155, 155, 155);
  469. background-color: rgb(249,249,249);
  470. color: rgb(155, 155, 155);
  471. border-radius: 20px;
  472. padding: 0 2.5%;
  473. -webkit-appearance: none;
  474. -moz-appearance: none;
  475. appearance: none;
  476. position: relative;
  477. cursor: default;
  478. `;
  479.  
  480. // 数字文本
  481. this.countText = document.createElement('span');
  482. this.countText.textContent = defaultNumber; // 使用默认数字
  483. this.countText.style.cssText = 'font-size: 14px; margin: 0 auto; cursor: pointer;';
  484. this.numberControDiv.appendChild(this.countText);
  485.  
  486. // 创建函数来设置悬停颜色
  487. function addHoverEffect(svgElement) {
  488. svgElement.addEventListener('mouseover', function () {
  489. var path = svgElement.querySelector('path');
  490. if (path) {
  491. path.setAttribute('data-original-fill', path.getAttribute('fill'));
  492. path.setAttribute('fill', '#ff6200');
  493. }
  494. });
  495.  
  496. svgElement.addEventListener('mouseout', function () {
  497. var path = svgElement.querySelector('path');
  498. if (path) {
  499. path.setAttribute('fill', path.getAttribute('data-original-fill'));
  500. }
  501. });
  502. }
  503.  
  504. // SVG控制区
  505. var svgControlDiv = document.createElement('div');
  506. svgControlDiv.style.cssText = 'display: flex; flex-direction: column; justify-content: space-between; cursor: pointer;';
  507.  
  508. // 上方SVG
  509. var svgUp = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  510. svgUp.setAttribute("width", "6");
  511. svgUp.setAttribute("height", "6");
  512. svgUp.setAttribute("viewBox", "0 0 1024 1024");
  513. svgUp.innerHTML = `
  514. <path d="M854.016 739.328l-313.344-309.248-313.344 309.248q-14.336 14.336-32.768 21.504t-37.376 7.168-36.864-7.168-32.256-21.504q-29.696-28.672-29.696-68.608t29.696-68.608l376.832-373.76q14.336-14.336 34.304-22.528t40.448-9.216 39.424 5.12 31.232 20.48l382.976 379.904q28.672 28.672 28.672 68.608t-28.672 68.608q-14.336 14.336-32.768 21.504t-37.376 7.168-36.864-7.168-32.256-21.504z" fill="#8a8a8a"></path>
  515. `;
  516. addHoverEffect(svgUp);
  517. svgControlDiv.appendChild(svgUp);
  518.  
  519. // 下方SVG
  520. var svgDown = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  521. svgDown.setAttribute("width", "6");
  522. svgDown.setAttribute("height", "6");
  523. svgDown.setAttribute("viewBox", "0 0 1024 1024");
  524. svgDown.innerHTML = `
  525. <path d="M857.088 224.256q28.672-28.672 69.12-28.672t69.12 28.672q29.696 28.672 29.696 68.608t-29.696 68.608l-382.976 380.928q-12.288 14.336-30.72 19.968t-38.912 4.608-40.448-8.704-34.304-22.016l-376.832-374.784q-29.696-28.672-29.696-68.608t29.696-68.608q14.336-14.336 32.256-21.504t36.864-7.168 37.376 7.168 32.768 21.504l313.344 309.248z" fill="#8a8a8a"></path>
  526. `;
  527. addHoverEffect(svgDown);
  528. svgControlDiv.appendChild(svgDown);
  529.  
  530. svgUp.addEventListener('click', () => this.updateCount(1, 'countInput'));
  531. svgDown.addEventListener('click', () => this.updateCount(-1, 'countInput'));
  532.  
  533. this.numberControDiv.appendChild(svgControlDiv);
  534. container.appendChild(this.numberControDiv);
  535.  
  536. // 绑定点击事件以显示弹窗
  537. this.countText.addEventListener('click', () => {
  538. createDropdownModal(dropdownContainer, '输入计数').then((inputValue) => {
  539. // console.log('用户输入:', inputValue);
  540. if (parseInt(inputValue) >= 0 && parseInt(inputValue) <= 999) {
  541. this.countText.textContent = inputValue; // 更新显示的数字
  542. this.countSort_reShowNotification(0); // 更新通知
  543. } else if (parseInt(inputValue) > 999) {
  544. showNotification('输入值超过了合理的范围', 1500);
  545. } else {
  546. showNotification('输入值必须为正整数', 1500);
  547. }
  548. }).catch((error) => {
  549. // console.log('弹窗取消:', error);
  550. });
  551. });
  552. }
  553.  
  554. // 用于更新数字文本的方法
  555. updateCount(increment, notificationType) {
  556. let currentValue = parseInt(this.countText.textContent);
  557. currentValue += increment;
  558. this.setCount(currentValue);
  559.  
  560. if (notificationType === 'countInput') {
  561. if (this.getCount() > 0) {
  562. showNotification(`下一个输入序号:${currentValue}`, 0);
  563. } else {
  564. this.setCount(1);
  565. showNotification('当前已是最小序号', 1500);
  566. this.countSort_reShowNotification();
  567. }
  568. }
  569. }
  570.  
  571. // 获取当前的计数
  572. getCount() {
  573. return this.countText.textContent;
  574. }
  575.  
  576. // 设置当前计数
  577. setCount(count) {
  578. this.countText.textContent = count;
  579. }
  580.  
  581. // 控制numberControDiv显示/隐藏的方法
  582. showNumberControlDiv(visible) {
  583. if (this.numberControDiv) {
  584. this.numberControDiv.style.display = visible ? 'flex' : 'none';
  585. }
  586. }
  587.  
  588. // 常显通知方法
  589. countSort_reShowNotification(show_time = 1500) {
  590. if (countSort_notificationTimeout) {
  591. clearTimeout(countSort_notificationTimeout);
  592. }
  593. countSort_notificationTimeout = setTimeout(() => {
  594. if (this.getDivButtonState()) {
  595. showNotification("下一个输入序号:" + this.getCount(), 0);
  596. }
  597. countSort_notificationTimeout = null;
  598. }, show_time); // 延迟后触发
  599. }
  600.  
  601. // 重写handleSwitchClick以控制子页面的可点击性
  602. handleSwitchClick() {
  603. super.handleSwitchClick();
  604.  
  605. const newState = this.getSwitchState();
  606. initializeContainerStyle(this.childSettingsContainer, newState);
  607. }
  608.  
  609. // 获取子设置容器
  610. getChildSettingsContainer() {
  611. return this.childSettingsContainer;
  612. }
  613.  
  614. getSelectBoxValue() {
  615. const selectBox = this.buttonContainer.querySelector('select');
  616. return selectBox ? selectBox.value : null;
  617. }
  618. }
  619.  
  620. // 创建开关容器元素
  621. var switchesContainer = document.createElement('div');
  622. switchesContainer.classList.add('flex', 'items-center', 'justify-between', 'pb-12');
  623. switchesContainer.style.cssText = 'position: fixed; top: 0px; left: 50%; transform: translateX(-50%); z-index: 9999;';
  624. if (isHomeURL()) {
  625. document.body.appendChild(switchesContainer);
  626. }
  627.  
  628. // 封装函数返回SVG图标
  629. function createSVGIcon() {
  630. var svgIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  631. svgIcon.setAttribute('class', 'icon custom-svg'); // 添加自定义类名
  632. svgIcon.setAttribute('viewBox', '0 0 1024 1024');
  633. svgIcon.setAttribute('width', '20');
  634. svgIcon.setAttribute('height', '20');
  635. svgIcon.setAttribute('fill', '#bbbbbb');
  636. svgIcon.innerHTML = '<path d="M288.791335 65.582671l446.41733 446.417329-446.41733 446.417329z"></path>';
  637. svgIcon.style.cssText = 'vertical-align: middle;'; // 垂直居中样式
  638.  
  639. return svgIcon;
  640. }
  641.  
  642. // 添加事件监听用于下拉箭头
  643. document.addEventListener('mouseenter', function (event) {
  644. if (event.target instanceof Element && event.target.matches('svg.icon.custom-svg')) { // 仅匹配具有自定义类名的SVG
  645. event.target.setAttribute('fill', '#ff6200');
  646. }
  647. }, true);
  648.  
  649. document.addEventListener('mouseleave', function (event) {
  650. if (event.target instanceof Element && event.target.matches('svg.icon.custom-svg')) { // 仅匹配具有自定义类名的SVG
  651. event.target.setAttribute('fill', '#bbbbbb');
  652. }
  653. }, true);
  654.  
  655. var dropdown_style = document.createElement('style');
  656. dropdown_style.textContent = `
  657. @keyframes dropdownContentAnimation {
  658. 0% {
  659. transform: translateX(-50%) translateY(14px) scale(0);
  660. }
  661. 100% {
  662. transform: translateX(-50%) translateY(0) scale(1);
  663. }
  664. }
  665.  
  666. @keyframes dropdownContentExitAnimation {
  667. 0% {
  668. transform: translateX(-50%) translateY(0) scale(1);
  669. }
  670. 100% {
  671. transform: translateX(-50%) translateY(14px) scale(0);
  672. }
  673. }
  674.  
  675. #dropdownContent.show {
  676. animation: dropdownContentAnimation 0.3s ease-out;
  677. }
  678.  
  679. #dropdownContent.hide {
  680. animation: dropdownContentExitAnimation 0.3s ease-out;
  681. }
  682.  
  683. @keyframes dropdownButtonAnimation {
  684. 0% { transform: scale(1); }
  685. 35% { transform: scale(1.15, 1.3); }
  686. 85% { transform: scale(0.94); }
  687. 100% { transform: scale(1); }
  688. }
  689.  
  690. #dropdownButtonAnimate.animate {
  691. animation: dropdownButtonAnimation 0.45s ease-out;
  692. }
  693. `;
  694.  
  695. document.head.appendChild(dropdown_style);
  696.  
  697. var dropdownContainer = document.createElement('div');
  698. dropdownContainer.style.cssText = 'position: relative; display: inline-block;';
  699.  
  700. var dropdownButtonName = '更多功能';
  701.  
  702. var dropdownButton = document.createElement('button');
  703. dropdownButton.id = 'dropdownButtonAnimate'; // 添加 id 以便于应用动画
  704. dropdownButton.textContent = dropdownButtonName;
  705. dropdownButton.style.cssText = 'margin-top: 0px; padding: 0px 10px; cursor: pointer; border-radius: 999px;z-index: 3;';
  706. dropdownButton.classList.add('ant-btn', 'css-9fw9up', 'ant-btn-default', 'primaryButton___N3z1x');
  707.  
  708. var dropdownDeck = document.createElement('div');
  709. dropdownDeck.style.cssText = `
  710. top: 0;
  711. left: 0;
  712. width: 200px;
  713. height: 60px;
  714. box-sizing: border-box;
  715. display: flex;
  716. justify-content: center;
  717. align-items: center;
  718. `;
  719.  
  720. var dropdownContent = document.createElement('div');
  721. dropdownContent.id = 'dropdownContent';
  722. dropdownContent.style.cssText = `
  723. display: none;
  724. position: absolute;
  725. background-color: #f9f9f9;
  726. top: 0px;
  727. left: 50%;
  728. transform: translateX(-50%);
  729. min-width: 200px;
  730. box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  731. z-index: 1;
  732. border-radius: 16px;
  733. max-height: 360px;
  734. overflow-y: auto; /* 使用 auto 可以根据内容是否溢出显示滚动条 */
  735. scrollbar-width: none; /* 隐藏滚动条,适用于 Firefox */
  736. -ms-overflow-style: none; /* 隐藏滚动条,适用于 IE 和 Edge */
  737. transform-origin: top center;
  738. `;
  739.  
  740. // 获取内容的显示状态
  741. function getDropdownContentDisplayState() {
  742. return dropdownContent.style.display !== 'none'; // 返回 true 表示可见,false 表示不可见
  743. }
  744.  
  745. var dropdownMask = document.createElement('div');
  746. dropdownMask.style.cssText = `
  747. position: sticky;
  748. top: 0;
  749. left: 0;
  750. width: 200px;
  751. height: 60px;
  752. backdrop-filter: none;
  753. border-radius: 10px 10px 0 0;
  754. box-sizing: border-box;
  755. border-bottom: 0px solid #DDD;
  756. z-index: 99; /* 确保遮罩在内容上方 */
  757. display: flex;
  758. justify-content: center;
  759. align-items: center;
  760. background-color: rgba(249, 249, 249, 0);
  761. `;
  762.  
  763. dropdownDeck.appendChild(dropdownButton);
  764. dropdownContainer.appendChild(dropdownDeck);
  765. dropdownContent.appendChild(dropdownMask);
  766.  
  767. dropdownContainer.appendChild(dropdownContent);
  768. switchesContainer.appendChild(dropdownContainer);
  769.  
  770. function on_dropdownMask() {
  771. dropdownMask.style.borderBottom = '1px solid #DDD';
  772. dropdownMask.style.backdropFilter = 'blur(8px) brightness(90%)';
  773. dropdownMask.style.backgroundColor = 'rgba(249, 249, 249, 0.5)';
  774. }
  775.  
  776. function off_dropdownMask() {
  777. dropdownMask.style.borderBottom = '0px solid #DDD';
  778. dropdownMask.style.backdropFilter = 'none';
  779. dropdownMask.style.backgroundColor = 'rgba(249, 249, 249, 0)';
  780. }
  781.  
  782. dropdownButton.addEventListener('click', function () {
  783. // 强制重新开始动画
  784. this.classList.remove('animate');
  785.  
  786. // 使用 requestAnimationFrame 确保动画类已被移除
  787. requestAnimationFrame(() => {
  788. this.classList.add('animate');
  789. });
  790.  
  791. this.addEventListener('animationend', function () {
  792. this.classList.remove('animate');
  793. }, { once: true });
  794.  
  795. if (dropdownContent.style.display === 'none' || dropdownContent.classList.contains('hide')) {
  796. dropdownContent.style.display = 'block';
  797. pureMode_infoDisplay(pureSwitch.getSwitchState());
  798. dropdownContent.classList.remove('hide');
  799. dropdownContent.classList.add('show');
  800. if (dropdownContent.scrollTop > 0) {
  801. on_dropdownMask();
  802. }
  803. } else {
  804. dropdownContent.classList.remove('show');
  805. dropdownContent.classList.add('hide');
  806. dropdownContent.addEventListener('animationend', function onAnimationEnd() {
  807. if (dropdownContent.classList.contains('hide')) {
  808. dropdownContent.style.display = 'none';
  809. pureMode_infoDisplay(pureSwitch.getSwitchState());
  810. off_dropdownMask();
  811. }
  812. dropdownContent.removeEventListener('animationend', onAnimationEnd);
  813. });
  814. }
  815. });
  816.  
  817. dropdownContent.addEventListener('scroll', function () {
  818. if (dropdownContent.scrollTop > 0 && dropdownContent.style.display !== 'none') {
  819. on_dropdownMask();
  820. }
  821. else {
  822. off_dropdownMask();
  823. }
  824. });
  825.  
  826. window.addEventListener('click', function (event) {
  827. if (!dropdownContainer.contains(event.target)) {
  828. if (dropdownContent.style.display === 'block') {
  829. dropdownContent.classList.remove('show');
  830. dropdownContent.classList.add('hide');
  831.  
  832. // 强制重新开始动画
  833. dropdownButton.classList.remove('animate');
  834.  
  835. // 使用 requestAnimationFrame 确保动画类已被移除
  836. requestAnimationFrame(() => {
  837. dropdownButton.classList.add('animate');
  838. });
  839.  
  840. dropdownButton.addEventListener('animationend', function () {
  841. this.classList.remove('animate');
  842. }, { once: true });
  843.  
  844. dropdownContent.addEventListener('animationend', function onAnimationEnd() {
  845. if (dropdownContent.classList.contains('hide')) {
  846. dropdownContent.style.display = 'none';
  847. off_dropdownMask();
  848. pureMode_infoDisplay(pureSwitch.getSwitchState());
  849. }
  850. dropdownContent.removeEventListener('animationend', onAnimationEnd);
  851. });
  852. }
  853. }
  854. });
  855.  
  856. class PIPManager {
  857. constructor(itemLink, spanElement, isHorizontal) {
  858. this.itemLink = itemLink;
  859. this.spanElement = spanElement;
  860. this.isHorizontal = isHorizontal;
  861. this.PIP = this.createPIP();
  862. this.ctrlBar = this.createControlBar();
  863. this.bar = this.createBar();
  864. this.witheCtrlBar = this.createWitheCtrlBar();
  865. this.iframe = this.createIframe();
  866. this.spanElement.onclick = this.togglePIP.bind(this);
  867.  
  868. this.collapseButton = null; // 用于存储收起后的矩形按钮
  869.  
  870. // 初始化状态
  871. this.isDragging = false;
  872. this.offsetX = 0;
  873. this.offsetY = 0;
  874. this.isCollapsed = false; // 收起状态
  875.  
  876. // 添加拖动事件监听(只在控制条上)
  877. this.bar.addEventListener('mousedown', this.startDrag.bind(this));
  878. document.addEventListener('mouseup', this.stopDrag.bind(this));
  879. document.addEventListener('mousemove', this.drag.bind(this));
  880.  
  881. // 监听窗口大小变化
  882. window.addEventListener('resize', this.onWindowResize.bind(this));
  883. }
  884.  
  885. countSclaeWidthAndHeight(p) {
  886. const scale = this.getDisplayScale();
  887. const calculatedLength = p / scale;
  888.  
  889. if (calculatedLength > 90) {
  890. return 90;
  891. }
  892. if (calculatedLength < 45) {
  893. return 45;
  894. }
  895.  
  896. return calculatedLength;
  897. }
  898.  
  899. createPIP() {
  900. const PIP = document.createElement('div');
  901. PIP.id = 'mini_PIP';
  902. PIP.style.cssText = `
  903. position: fixed;
  904. bottom: 10px;
  905. background-color: rgba(0, 0, 0, 0.5);
  906. padding: 5px;
  907. padding-top: 3px;
  908. border-radius: 24px;
  909. display: none;
  910. z-index: 1099;
  911. backdrop-filter: blur(10px) brightness(90%);
  912. overflow: hidden;
  913. transform: scale(1);
  914. transition: transform 0.5s ease, opacity 0.5s ease; /* 更新过渡效果时间 */
  915. `;
  916.  
  917. if (sonMain_PIP_defautPosition.getSelectBoxValue() === '左侧') {
  918. PIP.style.left = '10px';
  919. PIP.style.transformOrigin = 'bottom left'; /* 设置缩放的锚点 */
  920.  
  921. } else {
  922. PIP.style.right = '10px';
  923. PIP.style.transformOrigin = 'bottom right'; /* 设置缩放的锚点 */
  924. }
  925. PIP.style.width = this.isHorizontal ? "1300px" : "360px";
  926.  
  927. PIP.style.transform = `scale(${this.getDisplayScale()})`;
  928. PIP.style.height = this.isHorizontal ? this.countSclaeWidthAndHeight(90) + "%" : this.countSclaeWidthAndHeight(90) + "%";
  929.  
  930. return PIP;
  931. }
  932.  
  933. getDisplayScale(isHorizontal = this.isHorizontal) {
  934. const displayWidth = window.innerWidth;
  935. let displayScale = displayWidth / 1530;
  936. if (isHorizontal) {
  937. return displayScale * 0.7;
  938. } else {
  939. return displayScale;
  940. }
  941. }
  942.  
  943. getBarDisplayPX(px, isHorizontal = this.isHorizontal) {
  944. return px / this.getDisplayScale(isHorizontal) + 'px';
  945. }
  946.  
  947. onWindowResize() {
  948. // 调整PIP的缩放比例和尺寸
  949. const newScale = this.getDisplayScale();
  950. this.PIP.style.transform = `scale(${newScale})`;
  951.  
  952. // 重新计算PIP的高度
  953. this.PIP.style.height = this.isHorizontal ? this.countSclaeWidthAndHeight(90) + "%" : this.countSclaeWidthAndHeight(90) + "%";
  954.  
  955. // 调整控制条的高度和其他尺寸
  956. this.ctrlBar.style.height = this.getBarDisplayPX(12);
  957. this.ctrlBar.style.fontSize = this.getBarDisplayPX(10); // 动态调整 ctrlBar 的字体大小
  958. this.bar.style.height = this.getBarDisplayPX(7);
  959. this.bar.style.width = this.getBarDisplayPX(60);
  960. }
  961.  
  962. createControlBar() {
  963. const ctrlBar = document.createElement('div');
  964. ctrlBar.style.cssText = `
  965. height: ${this.getBarDisplayPX(12)};
  966. width: 100%;
  967. background-color: rgba(0, 0, 0, 0);
  968. z-index: 1101;
  969. margin-bottom: 3px;
  970. font-size: ${this.getBarDisplayPX(10)}; /* 初始字体大小 */
  971. `;
  972. return ctrlBar;
  973. }
  974.  
  975. createWitheCtrlBar() {
  976. const witheCtrlBar = document.createElement('div');
  977. witheCtrlBar.style.cssText = `
  978. display: flex;
  979. align-items: center; /* 垂直居中 */
  980. justify-content: space-between; /* 左右对齐 */
  981. height: 100%;
  982. width: calc(100% - 20px);
  983. margin: 0 auto;
  984. position: relative; /* 使子元素可以绝对定位 */
  985. `;
  986.  
  987. const leftButtonsContainer = document.createElement('div');
  988. leftButtonsContainer.style.cssText = `
  989. display: flex;
  990. align-items: center; /* 垂直居中 */
  991. justify-content: flex-start; /* 左对齐 */
  992. height: 100%;
  993. `;
  994. const rightButtonsContainer = document.createElement('div');
  995. rightButtonsContainer.style.cssText = `
  996. display: flex;
  997. align-items: center; /* 垂直居中 */
  998. justify-content: flex-end; /* 右对齐 */
  999. height: 100%;
  1000. `;
  1001.  
  1002. this.returnButton = this.createBarButton("返回", "returnButton", "left");
  1003. this.newWindowButton = this.createBarButton("新窗口", "newWindowButton", "right", "rgb(0, 145, 255)");
  1004. this.closeButton = this.createBarButton("关闭", "closeButton", "right", "rgba(236, 40, 39, 0.5)");
  1005. this.reloadButton = this.createBarButton("刷新", "reloadButton", "left");
  1006. this.collapseButton = this.createBarButton("收起", "collapseButton", "right");
  1007.  
  1008. // 添加按钮点击事件
  1009. this.closeButton.addEventListener('click', this.closePIP.bind(this));
  1010. this.reloadButton.addEventListener('click', this.reloadPIP.bind(this));
  1011. this.returnButton.addEventListener('click', this.returnPIP.bind(this));
  1012. this.newWindowButton.addEventListener('click', this.newWindowPIP.bind(this));
  1013. this.collapseButton.addEventListener('click', this.toggleCollapse.bind(this));
  1014.  
  1015. leftButtonsContainer.appendChild(this.returnButton);
  1016. leftButtonsContainer.appendChild(this.reloadButton);
  1017. rightButtonsContainer.appendChild(this.collapseButton);
  1018. rightButtonsContainer.appendChild(this.newWindowButton);
  1019. rightButtonsContainer.appendChild(this.closeButton);
  1020.  
  1021. // 按钮隐藏
  1022. if (!this.isHorizontal) {
  1023. this.newWindowButton.style.display = 'none';
  1024. }
  1025.  
  1026. // 将按钮容器添加到witheCtrlBar
  1027. witheCtrlBar.appendChild(leftButtonsContainer);
  1028. witheCtrlBar.appendChild(this.bar);
  1029. witheCtrlBar.appendChild(rightButtonsContainer);
  1030.  
  1031. return witheCtrlBar;
  1032. }
  1033.  
  1034. createBar() {
  1035. const bar = document.createElement('div');
  1036. bar.style.cssText = `
  1037. height: ${this.getBarDisplayPX(7)};
  1038. mangrgin-bottom: 1px;
  1039. width: 60px;
  1040. background-color: rgba(255, 255, 255, 0.5);
  1041. border-radius: 999px;
  1042. position: absolute;
  1043. left: 50%;
  1044. transform: translateX(-50%);
  1045. `;
  1046. return bar;
  1047. }
  1048.  
  1049. createBarButton(buttonName = "按钮", buttonId = "mini-PIP-barButton", position = "center", bg_color = "rgba(173, 173, 173, 0.5)") {
  1050. const newButton = document.createElement('button');
  1051. newButton.id = buttonId;
  1052. newButton.style.cssText = `
  1053. font-size: 1em; /* 使用em单位来确保字体大小跟随ctrlBar */
  1054. padding: 0 10px;
  1055. background-color: ${bg_color};
  1056. border: none;
  1057. border-radius: 999px;
  1058. margin: 0 5px;
  1059. color: white;
  1060. cursor: pointer;
  1061. user-select: none;
  1062. `;
  1063. newButton.innerText = buttonName;
  1064.  
  1065. if (position === "left") {
  1066. newButton.style.marginRight = 'auto';
  1067. } else if (position === "right") {
  1068. newButton.style.marginLeft = 'auto';
  1069. }
  1070.  
  1071. return newButton;
  1072. }
  1073.  
  1074. createIframe() {
  1075. const iframe = document.createElement('iframe');
  1076. iframe.style.cssText = `
  1077. display: block;
  1078. width: 100%;
  1079. height: calc(100% - ${this.ctrlBar.style.height} - ${this.ctrlBar.style.marginBottom});
  1080. border: none;
  1081. border-radius: 19px;
  1082. z-index: 1099;
  1083. transition: opacity 0.3s ease;
  1084. `;
  1085. return iframe;
  1086. }
  1087.  
  1088. togglePIP() {
  1089. const pipElement = document.getElementById('mini_PIP');
  1090. const pip_restore_button = document.getElementById('pip_restore_button');
  1091.  
  1092. if (pip_restore_button) {
  1093. pip_restore_button.remove(); // 先移除恢复按钮
  1094. }
  1095. if (pipElement) {
  1096. pipElement.parentElement.removeChild(pipElement);
  1097. }
  1098.  
  1099. // 先添加控制条,再添加iframe
  1100. this.ctrlBar.appendChild(this.witheCtrlBar);
  1101. this.PIP.appendChild(this.ctrlBar);
  1102. this.PIP.appendChild(this.iframe);
  1103.  
  1104. document.body.appendChild(this.PIP);
  1105. this.iframe.src = this.itemLink;
  1106. this.PIP.style.transition = 'transform 0.3s ease, opacity 0.3s ease'; // 过渡效果
  1107.  
  1108. this.PIP.style.display = 'block';
  1109.  
  1110. setTimeout(() => {
  1111. this.PIP.style.opacity = '1'; // 恢复透明度
  1112. }, 0);
  1113.  
  1114. adapterPIP_forTableCardAndBatchPrint(sonMain_PIP_autoAdjust.getSwitchState());
  1115. }
  1116.  
  1117. startDrag(event) {
  1118. // adapterPIP_forTableCardAndBatchPrint(sonMain_PIP_autoAdjust.getSwitchState(), true);
  1119. this.isDragging = true;
  1120. this.offsetX = event.clientX - this.PIP.offsetLeft;
  1121. this.offsetY = event.clientY - this.PIP.offsetTop;
  1122. this.bar.style.cursor = 'grabbing';
  1123. this.iframe.style.opacity = '0'; // 透明
  1124. }
  1125.  
  1126. stopDrag() {
  1127. this.isDragging = false;
  1128. this.bar.style.cursor = 'grab';
  1129. // adapterPIP_forTableCardAndBatchPrint(sonMain_PIP_autoAdjust.getSwitchState());
  1130. this.iframe.style.opacity = '1'; // 取消透明
  1131. }
  1132.  
  1133. drag(event) {
  1134. if (this.isDragging) {
  1135. this.PIP.style.left = `${event.clientX - this.offsetX}px`;
  1136. this.PIP.style.top = `${event.clientY - this.offsetY}px`;
  1137. adapterPIP_forTableCardAndBatchPrint(sonMain_PIP_autoAdjust.getSwitchState());
  1138. this.iframe.style.zIndex = '1'; // 置于底层
  1139. }
  1140. }
  1141.  
  1142. closePIP() {
  1143. // this.PIP.style.display = 'none';
  1144. this.PIP.style.transformOrigin = 'center center'; // 设置缩放的锚点
  1145. this.PIP.style.transform = 'scale(0.1)'; // 缩小
  1146. this.PIP.style.opacity = '0'; // 逐渐透明
  1147. adapterPIP_forTableCardAndBatchPrint(sonMain_PIP_autoAdjust.getSwitchState(), true);
  1148. setTimeout(() => {
  1149. this.PIP.style.display = 'none';
  1150. this.PIP.style.transform = `scale(${this.getDisplayScale()})`; // 恢复
  1151. this.PIP.style.transformOrigin = 'bottom right;'; // 设置缩放
  1152. }, 300); // 等待动画结束后再隐藏
  1153. }
  1154.  
  1155. reloadPIP() {
  1156. this.iframe.src = this.iframe.src;
  1157. }
  1158.  
  1159. returnPIP() {
  1160. window.history.back();
  1161. }
  1162.  
  1163. newWindowPIP() {
  1164. window.open(this.itemLink);
  1165. }
  1166.  
  1167. toggleCollapse() {
  1168. if (this.isCollapsed) {
  1169. this.expandPIP();
  1170. } else {
  1171. this.collapsePIP();
  1172. }
  1173. }
  1174.  
  1175. collapsePIP() {
  1176. this.PIP.style.transformOrigin = ''; // 设置缩放的锚点
  1177. // 获取当前PIP的位置
  1178. const pipRect = this.PIP.getBoundingClientRect();
  1179. const centerX = pipRect.left + pipRect.width / 2;
  1180.  
  1181. // 判断PIP位置,决定收起方向
  1182. const isLeftSide = centerX < window.innerWidth / 2;
  1183.  
  1184. // 计算目标位置的中心点
  1185. const targetX = isLeftSide ? -(window.innerWidth / 2) : (window.innerWidth / 2); // 收起到左侧或右侧
  1186.  
  1187. // 设置PIP收起动画
  1188. this.PIP.style.transition = 'transform 0.5s ease, opacity 0.5s ease'; // 过渡效果
  1189. this.PIP.style.transform = `translate(${targetX}px, 0px) scale(0.1)`; // 缩小并移动到目标位置
  1190. this.PIP.style.opacity = '0'; // 逐渐透明
  1191. adapterPIP_forTableCardAndBatchPrint(sonMain_PIP_autoAdjust.getSwitchState(), true);
  1192.  
  1193. setTimeout(() => {
  1194. setTimeout(() => {
  1195. this.PIP.style.display = 'none';
  1196. }, 200); // 等待动画结束后再隐藏
  1197. this.createRestoreButton(isLeftSide); // 创建恢复按钮
  1198. this.isCollapsed = true; // 设置为已收起状态
  1199. }, 300); // 等待动画结束后再隐藏
  1200. }
  1201.  
  1202. expandPIP() {
  1203. this.PIP.style.transformOrigin = ''; // 设置缩放的锚点
  1204. this.PIP.style.display = 'block';
  1205. this.PIP.style.bottom = '10px'; // 恢复到原位置
  1206. this.PIP.style.right = '10px'; // 恢复到原位置
  1207. adapterPIP_forTableCardAndBatchPrint(sonMain_PIP_autoAdjust.getSwitchState());
  1208.  
  1209. setTimeout(() => {
  1210. this.PIP.style.transform = `scale(${this.getDisplayScale()})`;
  1211. this.PIP.style.opacity = '1'; // 恢复透明度
  1212. this.removeRestoreButton(); // 移除恢复按钮
  1213. this.PIP.style.transformOrigin = 'bottom right'; // 设置缩放的锚点
  1214. }, 0); // 立即开始动画
  1215. this.isCollapsed = false; // 设置为未收起状态
  1216. }
  1217.  
  1218. createRestoreButton(isLeftSide) {
  1219. const restoreButton = document.createElement('div');
  1220. restoreButton.id = 'pip_restore_button';
  1221. restoreButton.style.cssText = `
  1222. position: fixed;
  1223. bottom: 50%; /* 调整到50%高度位置 */
  1224. ${isLeftSide ? 'left' : 'right'}: 0px; /* 根据位置调整 */
  1225. width: 52px;
  1226. height: 50px;
  1227. background-color: rgba(0, 0, 0, 0.5);
  1228. border-radius: 10px;
  1229. ${isLeftSide ? 'border-top-left-radius: 0px;' : 'border-top-right-radius: 0px;'} /* 调整圆角 */
  1230. ${isLeftSide ? 'border-bottom-left-radius: 0px;' : 'border-bottom-right-radius: 0px;'} /* 调整圆角 */
  1231. z-index: 1100;
  1232. cursor: pointer;
  1233. transition: opacity 0.3s ease;
  1234. `;
  1235. restoreButton.addEventListener('click', this.expandPIP.bind(this));
  1236.  
  1237. const iconDiv = document.createElement('div');
  1238. const imgUrl = getTableCardImageUrl();
  1239. iconDiv.style.cssText = `
  1240. width: 40px;
  1241. height: 40px;
  1242. margin: 5px;
  1243. ${isLeftSide ? 'margin-left' : 'margin-right'}: 7px; /* 根据位置调整 */
  1244. ${imgUrl ? 'background-image: url(' + imgUrl + ');' : 'background-image: url("https://www.taobao.com/favicon.ico");'}
  1245. background-size: cover;
  1246. border-radius: 5px;
  1247. `;
  1248.  
  1249. restoreButton.appendChild(iconDiv);
  1250. document.body.appendChild(restoreButton);
  1251. this.restoreButton = restoreButton; // 存储恢复按钮引用
  1252. }
  1253.  
  1254. removeRestoreButton() {
  1255. if (this.restoreButton) {
  1256. document.body.removeChild(this.restoreButton);
  1257. this.restoreButton = null; // 清除引用
  1258. }
  1259. }
  1260. }
  1261.  
  1262. // 版本描述区域
  1263. var versionTitle = document.createElement('p');
  1264. versionTitle.textContent = versionTitleTxt;
  1265. versionTitle.style.cssText = `
  1266. font-size: 12px;
  1267. line-height: 1.8; /* 调整行高 */
  1268. margin: 0 20px;
  1269. justify-content: center; /* 使用 flex 居中对齐 */
  1270. align-items: center;
  1271. border-top: 1px solid #D2D2D2;
  1272. text-align: center; /* 居中文本 */
  1273. color: #9B9B9B;
  1274. display: flex; /* 添加 display: flex 以使用 flexbox 布局 */
  1275. cursor: pointer; /* 鼠标指针 */
  1276. user-select: none;
  1277. `;
  1278.  
  1279. var isExpanded_versionTitle = false; // 用于跟踪当前状态
  1280.  
  1281. // 悬浮时显示“展开所有菜单”或“折叠所有菜单”
  1282. versionTitle.addEventListener('mouseover', function () {
  1283. versionTitle.textContent = isExpanded_versionTitle ? '折叠所有菜单' : '展开所有菜单';
  1284. });
  1285.  
  1286. // 鼠标移开时恢复原始文本
  1287. versionTitle.addEventListener('mouseout', function () {
  1288. versionTitle.textContent = versionTitleTxt;
  1289. });
  1290.  
  1291. // 点击事件处理
  1292. versionTitle.addEventListener('click', function () {
  1293. // 执行点击操作,展开或折叠所有菜单
  1294. document.querySelectorAll('#main_standardFunShowButton').forEach(button => button.click());
  1295.  
  1296. // 切换状态并更新悬浮文本
  1297. isExpanded_versionTitle = !isExpanded_versionTitle;
  1298. versionTitle.textContent = versionTitleTxt; // 恢复原始文本
  1299. });
  1300.  
  1301. // 更新本地存储的开关状态
  1302. var updateSwitchState = function (switchName, newState) {
  1303. localStorage.setItem(switchName, newState.toString());
  1304. };
  1305.  
  1306.  
  1307. // 通用子菜单容器样式的函数
  1308. function initializeContainerStyle(container, state) {
  1309. if (!state) {
  1310. container.style.opacity = '0.4';// 设置透明度使内容变浅
  1311. container.style.pointerEvents = 'none';// 禁止点击操作
  1312. } else {
  1313. container.style.opacity = '';// 恢复透明度
  1314. container.style.pointerEvents = '';// 恢复点击操作
  1315. }
  1316. }
  1317.  
  1318. var notification_style = document.createElement('style');
  1319. notification_style.type = 'text/css';
  1320. notification_style.innerHTML = `
  1321. @keyframes showNotification {
  1322. 0% {
  1323. transform: translateX(-50%) scale(0);
  1324. }
  1325. 40% {
  1326. transform: translateX(-50%) scale(.96);
  1327. }
  1328. 55% {
  1329. transform: translateX(-50%) scale(1.04);
  1330. }
  1331. 100% {
  1332. transform: translateX(-50%) scale(1);
  1333. }
  1334. }
  1335.  
  1336. @keyframes hideNotification {
  1337. 5% {
  1338. transform: translateX(-50%) scale(1);
  1339. }
  1340. 100% {
  1341. opacity: 0;
  1342. transform: translateX(-50%) scale(0.2);
  1343. }
  1344. }
  1345.  
  1346. @keyframes notificationButtonAnimation {
  1347. 0% { transform: translateX(-50%) scale(1); }
  1348. 100% { transform: translateX(-50%) scale(1.15); opacity: 0;}
  1349. }
  1350.  
  1351. .notification {
  1352. cursor: default;
  1353. position: fixed;
  1354. bottom: 60px;
  1355. left: 50%;
  1356. background-color: rgba(0, 0, 0, 0.5);
  1357. color: #fff;
  1358. padding: 10px;
  1359. border-radius: 12px;
  1360. display: none;
  1361. z-index: 9999;
  1362. backdrop-filter: blur(10px) brightness(90%); /* 添加模糊效果 */
  1363. -webkit-backdrop-filter: blur(10px); /* 兼容Safari浏览器 */
  1364. transform-origin: center;
  1365. width: auto; /* 默认宽度 */
  1366. max-width: 68%;
  1367. white-space: nowrap; /* 单行显示 */
  1368. overflow: hidden; /* 超出内容隐藏 */
  1369. text-overflow: ellipsis; /* 溢出省略号 */
  1370. text-align: center; /* 文本居中显示 */
  1371. transform: translateX(-50%); /* 初始水平居中 */
  1372. }
  1373. `;
  1374. document.head.appendChild(notification_style);
  1375.  
  1376. // 创建通知弹窗
  1377. var NotificationContainer = document.createElement('div');
  1378. NotificationContainer.classList.add('notification');
  1379. NotificationContainer.id = 'showNotificationContainer';
  1380. document.body.appendChild(NotificationContainer);
  1381.  
  1382. // 将开关和按钮加入页面
  1383. // 添加纯净模式开关控件
  1384. const pureSwitch = new ToggleButtonComponent('pureSwitch', '纯净模式', dropdownContent, 1);
  1385. // 添加复制开关控件
  1386. const old_copySwitchContainer = new ToggleButtonComponent('copySwitch', '点击店名复制', dropdownContent);
  1387. // 添加提取商品链接控件
  1388. const newonlyItemIdButtonContainer = new ToggleButtonComponent('newmainOnlyItemIdSwitchState', '链接自动提取', dropdownContent);
  1389. // 添加手卡截图控件
  1390. const tableCardPngSwitchContainer = new ToggleButtonComponent('tableCardPngSwitchState', '快捷截图', dropdownContent,);
  1391. // 添加商品排序开关控件
  1392. const itemSortButtonContainer = new ToggleButtonComponent('mainItemSortButtonSwitchState', '商品排序', dropdownContent);
  1393. // 添加挂链工具开关控件
  1394. const newGuaLian_onlyItemIdSwitch = new ToggleButtonComponent('newGuaLian_onlyItemIdSwitch', '挂链工具', dropdownContent);
  1395. // 添加着色与计算器开关控件
  1396. const drawColorAndCalculatorContainer = new ToggleButtonComponent('mainDrawColorAndCalculatorContainer', '着色与计算器(Beta)', dropdownContent);
  1397. // 添加画中画开关控件
  1398. const pictureInPictureSwitchContainer = new ToggleButtonComponent('pictureInPictureSwitchState', '小窗模式', dropdownContent);
  1399. // 添加辅助功能开关控件
  1400. const AuxiliaryFunctionsContainer = new ToggleButtonComponent('AuxiliaryFunctionsContainer', '辅助功能', dropdownContent);
  1401. dropdownContent.appendChild(versionTitle); // 版本说明
  1402.  
  1403. // 复制开关子功能区域
  1404. const main_copySwitch = old_copySwitchContainer.getSecondaryContent();
  1405. const copySwitchContainer = new SonToggleButtonComponent('copySwitchContainer', '点击店名复制', main_copySwitch, "点击店铺名称时,自动复制店铺链接到剪切板", 1);
  1406. const copySwitch_onlyPart = new SonToggleButtonComponent('copySwitch_onlyPart', '部分复制触发方式', copySwitchContainer.getChildSettingsContainer(), "设置复制部分内容的触发方式", 1);
  1407. const copySwitch_onlyPart_clickCount = ['单击', '双击'];
  1408. copySwitch_onlyPart.createSelectBox(copySwitch_onlyPart_clickCount);
  1409. const copySwitch_smartCpoy = new SonToggleButtonComponent('copySwitch_smartCpoy', '复制长度', copySwitchContainer.getChildSettingsContainer(), "设置复制内容的长度", 1);
  1410. const copySwitch_smartCpoy_length = ['默认', '智能'];
  1411. copySwitch_smartCpoy.createSelectBox(copySwitch_smartCpoy_length);
  1412.  
  1413. // 提取链接子功能区域
  1414. const main_onlyItemIdSwitch = newonlyItemIdButtonContainer.getSecondaryContent();
  1415. const sonMain_onlyItemIdSwitch = new SonToggleButtonComponent('mainOnlyItemIdSwitchState', '链接自动提取', main_onlyItemIdSwitch, "当点击商品链接窗口时,会主动提取当前剪切板中的商品链接,并更新你的剪切板", 1);
  1416. const mainOnlyItemId_autoSave = new SonToggleButtonComponent('mainOnlyItemId_autoSave_state', '自动保存', sonMain_onlyItemIdSwitch.getChildSettingsContainer(), '替换链接后自动点击“保存”按钮', 1);
  1417. const mainOnlyItemId_checkTtemLink = new SonToggleButtonComponent('mainOnlyItemId_checkTtemLink_state', '链接检查', sonMain_onlyItemIdSwitch.getChildSettingsContainer(), '链接中不存在12位ID时,使用“警告”替换当前剪切板\n\n开启此功能会引起剪切板冲突', 1);
  1418.  
  1419. // 辅助挂链子功能区域,正式从提取链接子功能区域独立
  1420. const mainGuaLian_onlyItemIdSwitch = newGuaLian_onlyItemIdSwitch.getSecondaryContent();
  1421. const sonMain_onlyItemIdForSearchDiv = new SonToggleButtonComponent('sonMain_onlyItemIdForSearchDiv_state', '搜索框自动提取ID', mainGuaLian_onlyItemIdSwitch, "当点击“全局筛选”搜索框时,会主动提取当前剪切板中的商品id,并更新你的剪切板", 1);
  1422. const onlyItemIdForSearchDiv_onlyNoIntroduce = new SonToggleButtonComponent('mainOnlyItemIdonlyItemIdForSearchDiv_onlyNoIntroduce_state', '仅在挂链中生效', sonMain_onlyItemIdForSearchDiv.getChildSettingsContainer(), '开启后只会在“挂链商品”中生效该功能', 1, true);
  1423. const onlyItemIdForSearchDiv_autoInputSort = new SonToggleButtonComponent('onlyItemIdForSearchDiv_autoInputSort', '自动输入排序', sonMain_onlyItemIdForSearchDiv.getChildSettingsContainer(), '开启后会给上播id正确的商品,自动输入“商品排序”中设定好的内容', 1);
  1424. const sonMain_autoFlagedSwitch = new SonToggleButtonComponent('sonMain_autoFlagedSwitch_state', '批量输入排序', mainGuaLian_onlyItemIdSwitch, "当在“挂链”页面时,“全局筛选”搜索框右侧会出现“批量处理”按钮,可以将当前剪切板中的所有链接,自动标记为“自动填充”的目标内容", 1);
  1425. const sonMain_linkAllCopySwitch = new SonToggleButtonComponent('sonMain_linkAllCopySwitch_state', '完整复制', mainGuaLian_onlyItemIdSwitch, "当在“挂链”页面时,“全局筛选”搜索框右侧会出现“完整复制”按钮,会主动复制当前剪切板中店铺的所有商品链接到剪切板", 1);
  1426.  
  1427. // 快捷截图子功能区域
  1428. const main_tableCardPngSwitch = tableCardPngSwitchContainer.getSecondaryContent();
  1429. const sonMain_tableCardPngSwitch = new SonToggleButtonComponent('main_tableCardPngSwitch', '手卡截图', main_tableCardPngSwitch, "截取当前页面的手卡并添加到剪切板", 1);
  1430. const tableCardPng_resolution = new SonToggleButtonComponent('mainTableCardPng_resolution', '清晰度', sonMain_tableCardPngSwitch.getChildSettingsContainer(), "设置截图的清晰度\n\n注意:更低的清晰度并不能提高截图的响应速度", 1);
  1431. const tableCardPng_resolution_select = ['普通', '高清', '超清'];
  1432. tableCardPng_resolution.createSelectBox(tableCardPng_resolution_select);
  1433. const tableCardPng_checkItemIdRow = new SonToggleButtonComponent('tableCardPng_checkItemIdRow', '上播ID核对', sonMain_tableCardPngSwitch.getChildSettingsContainer(), "截图时在手卡中添一行“上播ID”,以便核对是否正确", 1);
  1434. const tableCardPng_checkItemIdRow_onlyPng = new SonToggleButtonComponent('tableCardPng_checkItemIdRow_onlyPng', '仅截图时显示', tableCardPng_checkItemIdRow.getChildSettingsContainer(), "开启后仅在截图时会含有“上播id核对行”", 1);
  1435.  
  1436. // 自动填充子功能区域
  1437. const main_itemSort = itemSortButtonContainer.getSecondaryContent();
  1438. const sonMain_itemSortSwitch = new SonToggleButtonComponent('main_itemSort_SwitchState_1', '自动填充', main_itemSort, "点击排序时自动自动清空或输入", 1);
  1439. const itemSort_inputConter = new SonToggleButtonComponent('itemSort_inputConter', '输入内容', sonMain_itemSortSwitch.getChildSettingsContainer(), "设置自动输入的默认内容", 1);
  1440. const itemSort_inputConter_input = ['空', '0', '1', '2', '3', '999', '2536'];
  1441. itemSort_inputConter.createSelectBox(itemSort_inputConter_input);
  1442. const itemSort_countInputFun = new SonToggleButtonComponent('itemSort_countInputFun', '计数输入', sonMain_itemSortSwitch.getChildSettingsContainer(), "将“输入内容”更新为当前计数,以方便排序", 1);
  1443. itemSort_countInputFun.createNumberControDiv(10, false, 'countInput');
  1444. itemSort_countInputFun.createDivButton('开始', '退出',
  1445. (isActivated) => {
  1446. if (isActivated) {
  1447. itemSort_countInputFun.showNumberControlDiv(true); // 显示数字控制区
  1448. itemSort_countInputFun.setCount(1);
  1449. itemSort_countInputFun.countSort_reShowNotification();// 计数通知显示恢复
  1450. itemSort_inputConter.showSwitchDiv(false); // 隐藏输入内容选择框
  1451. temp_itemId = '';
  1452. itemSort_countInputFun_rateInput.showSwitchDiv(false); // 隐藏十倍计数输入框
  1453. } else {
  1454. itemSort_countInputFun.showNumberControlDiv(false); // 隐藏数字控制区
  1455. itemSort_inputConter.showSwitchDiv(true); // 显示输入内容选择框
  1456. itemSort_countInputFun_rateInput.showSwitchDiv(true); // 显示十倍计数输入框
  1457.  
  1458. }
  1459. }
  1460. );
  1461. itemSort_countInputFun.showNumberControlDiv(false);
  1462. const itemSort_countInputFun_rateInput = new SonToggleButtonComponent('itemSort_countInputFun_rateInput', '十倍计数输入', sonMain_itemSortSwitch.getChildSettingsContainer(), "计数输入内容为计数的十倍", 1);
  1463. const itemSort_anchorSort = new SonToggleButtonComponent('itemSort_anchorSort', '主播排序', sonMain_itemSortSwitch.getChildSettingsContainer(), "根据输入内容,按照当前商品顺序,进行主播排序", 1);
  1464. itemSort_anchorSort.createDivButton('开始', '退出',
  1465. (isActivated) => {
  1466. if (isActivated) {
  1467. if (itemSort_countInputFun.getDivButtonState()) {
  1468. const button = document.getElementById('itemSort_countInputFun_divButton');
  1469. button.click();
  1470.  
  1471. itemSort_countInputFun.showNumberControlDiv(false); // 隐藏数字控制区
  1472. itemSort_inputConter.showSwitchDiv(true); // 显示输入内容选择框
  1473. }
  1474. processItems();
  1475. }
  1476. else {
  1477. // 检查是否已有弹窗存在,如果有则移除
  1478. const existingModal = document.querySelector('.dropdown-modal');
  1479. if (existingModal) {
  1480. existingModal.style.display = 'none';
  1481. dropdownButton.style.display = ''; // 恢复按钮
  1482. }
  1483. }
  1484. }
  1485. );
  1486. // const itemSort_autoSort = new SonToggleButtonComponent('itemSort_autoSort', '自动排序', sonMain_itemSortSwitch.getChildSettingsContainer(), "根据输入内容,对当前商品自动进行排序", 1);
  1487. // itemSort_autoSort.createDivButton('开始', '退出',
  1488. // async (isActivated) => {
  1489. // if (isActivated) {
  1490. // // 测试函数
  1491. // let AutoSortItemText = await createDropdownModal(dropdownContainer, '自动排序');
  1492. // const autoSort = new AutoSortItem(AutoSortItemText);
  1493. // showNotification('开始排序');
  1494. // autoSort.startSort();
  1495. // }
  1496. // else {
  1497. // // 检查是否已有弹窗存在,如果有则移除
  1498. // const existingModal = document.querySelector('.dropdown-modal');
  1499. // if (existingModal) {
  1500. // existingModal.style.display = 'none';
  1501. // dropdownButton.style.display = ''; // 恢复按钮
  1502. // }
  1503. // }
  1504. // }
  1505. // );
  1506.  
  1507. // 着色与计算器子功能区域
  1508. const main_drawColorAndCalculator = drawColorAndCalculatorContainer.getSecondaryContent();
  1509. const sonMain_drawColor = new SonToggleButtonComponent('main_drawColor', '着色器', main_drawColorAndCalculator, "点击着色按钮后对当前手卡进行着色\n\n注意:着色器功能目前处于测试阶段,可能存在一些问题", 1);
  1510. // 智能替换
  1511. const sonMain_smartReplace = new SonToggleButtonComponent('main_smartReplace', '智能替换', sonMain_drawColor.getChildSettingsContainer(), "1.智能识别手卡中的文字并替换为对应格式\n2.替换违禁词汇\n\n例如:将“99/2箱*6瓶”转写为“平均到手”的形式;\n将“49/2小箱/6瓶”转写为“平均到手”的形式\n例如:将“100%棉”替换为“99.99%棉”", 1);
  1512. // 补贴生成
  1513. const sonMain_subsidyText = new SonToggleButtonComponent('main_subsidyText', '补贴生成', sonMain_drawColor.getChildSettingsContainer(), "点击补贴生成按钮后生成当前手卡的补贴文字", 1);
  1514. // 计算器开关
  1515. const sonMain_calculator = new SonToggleButtonComponent('main_calculator', '计算器', sonMain_drawColor.getChildSettingsContainer(), "点击着色按钮后对当前手卡的算式进行计算\n\n注意:计算器功能目前处于测试阶段,可能存在一些问题;\n\n例如:无法计算形如“(3+9)/2”的算式;如果你想正确计算形如这样的算式,请将算式调整为“(3+9)/(2)”", 1);
  1516.  
  1517. // 画中画子功能区域
  1518. const main_pictureInPictureSwitch = pictureInPictureSwitchContainer.getSecondaryContent();
  1519. const sonMain_pictureInPictureSwitch = new SonToggleButtonComponent('main_pictureInPictureSwitch', '小窗模式', main_pictureInPictureSwitch, "开启后,支持小窗模式的内容将会以小窗的形式呈现", 1);
  1520. const sonMain_PIP_defautPosition = new SonToggleButtonComponent('main_PIP_defautPosition', '默认位置', sonMain_pictureInPictureSwitch.getChildSettingsContainer(), "设置小窗的默认位置", 1);
  1521. const sonMain_PIP_defautPosition_select = ['左侧', '右侧'];
  1522. sonMain_PIP_defautPosition.createSelectBox(sonMain_PIP_defautPosition_select);
  1523. const sonMain_PIP_autoAdjust = new SonToggleButtonComponent('main_PIP_autoAdjust', '手卡位置自适应', sonMain_pictureInPictureSwitch.getChildSettingsContainer(), "开启后,在小窗模式下会自动调整手卡位置", 1, true);
  1524.  
  1525. // 辅助功能子功能区域
  1526. const AuxiliaryFunctions_container = AuxiliaryFunctionsContainer.getSecondaryContent();
  1527. const sAF_backToLastCurrentURL = new SonToggleButtonComponent('sAF_backToLastCurrentURL', '最近浏览', AuxiliaryFunctions_container, "会主动提示上次浏览的场次页面,点击弹出的通知会跳转到上次浏览的场次页面", 1, true);
  1528. const sAF_closeBrowserUpdateDiv = new SonToggleButtonComponent('sAF_closeBrowserUpdateDiv', '关闭浏览器更新提示', AuxiliaryFunctions_container, "关闭浏览器更新提示,防止影响使用\n(已累计关闭" + getCloseCount() + "次)", 1, true);
  1529. const sAF_pastedSeachSwitchConta = new SonToggleButtonComponent('sAF_pastedSeachSwitchConta', '快捷粘贴搜索', AuxiliaryFunctions_container, "进行粘贴操作后,自动点击搜索按钮", 1, false);
  1530. const sAF_AdvanceTableCardTitle = new SonToggleButtonComponent('sAF_AdvanceTableCardTitle', '手卡标题优化', AuxiliaryFunctions_container, "优化手卡标题,使其更易于识别", 1, true);
  1531. const sAF_AdvanceTableCardPage = new SonToggleButtonComponent('sAF_AdvanceTableCardPage', '手卡页面优化', AuxiliaryFunctions_container, "优化手卡页面,隐藏顶栏与禁用词窗口", 1, true);
  1532. const sAF_AdvanceBatchPrint = new SonToggleButtonComponent('sAF_AdvanceBatchPrint', '手卡打印功能优化', AuxiliaryFunctions_container, "优化打印页面默认文件名,允许仅打印手卡的部分区域并添加“上播ID核对”区域,\n添加“编辑手卡”按钮", 1, true);
  1533. const sAF_AdvanceBatchPrint_alwaysDisplay = new SonToggleButtonComponent('sAF_AdvanceBatchPrint_alwaysDisplay', '总是显示ID核对行', sAF_AdvanceBatchPrint.getChildSettingsContainer(), "总是显示ID核对行,不论是否仅打印手卡的部分区域", 1, true);
  1534. const sAF_FristPhotoCopyForTmallAndTaobao = new SonToggleButtonComponent('sAF_FristPhotoCopyForTmallAndTaobao', '天猫&淘宝主图复制', AuxiliaryFunctions_container, "在天猫或淘宝的主图上点击“复制图片”按钮,以快速将图片拷贝到剪切板\n\n注意:此功能会需要多个网页的剪切板访问权限", 1, true);
  1535.  
  1536. // 单击和双击事件的延迟和计数器
  1537. var delay = 200; // 延迟时间,单位毫秒
  1538. var timer = null;
  1539.  
  1540. // 监听鼠标左键点击事件
  1541. document.addEventListener('click', function (event) {
  1542. // 检查是否开启了点击复制功能且在匹配的网址上
  1543. if (copySwitchContainer.getSwitchState() && isHomeURL()) {
  1544. clearTimeout(timer);
  1545.  
  1546. timer = setTimeout(function () {
  1547. handleMouseClick(event, event.detail === 2);
  1548. }, delay);
  1549. }
  1550. });
  1551.  
  1552. let clipboardHistory = ''; // 点击复制历史记录
  1553.  
  1554. // 鼠标点击响应事件
  1555. function handleMouseClick(event, isDoubleClick = false) {
  1556. if (event.target &&
  1557. event.target.classList.contains('link-overflow-tip') &&
  1558. event.target.closest('#MarkHighlight-shopDetail-outShopName')) {
  1559. var text = event.target.textContent;
  1560.  
  1561. // 根据是否处于双击模式及点击类型,决定复制的文本内容
  1562. if (toggleDoubleClickMode()) {
  1563. if (isDoubleClick) {
  1564. if (toggleSmartCopyLength()) {
  1565. copiedText = processText(text);
  1566. } else {
  1567. copiedText = text.substring(0, 3);
  1568. }
  1569. } else {
  1570. copiedText = text.substring();
  1571. }
  1572.  
  1573. } else {
  1574. if (isDoubleClick) {
  1575. copiedText = text.substring();
  1576. } else {
  1577. if (toggleSmartCopyLength()) {
  1578. copiedText = processText(text);
  1579. } else {
  1580. copiedText = text.substring(0, 3);
  1581. }
  1582. }
  1583. }
  1584.  
  1585. clipboardHistory = text;
  1586. GM_setClipboard(copiedText);
  1587.  
  1588. showNotification("复制成功:" + copiedText);
  1589.  
  1590. itemSort_countInputFun.countSort_reShowNotification(); // 计数通知显示恢复
  1591. }
  1592. }
  1593.  
  1594. // 返回单击和双击模式的切换函数
  1595. function toggleDoubleClickMode() {
  1596. const isDoubleClickMode = copySwitch_onlyPart.getSelectBoxValue();
  1597.  
  1598. switch (isDoubleClickMode) {
  1599. case '单击':
  1600. return false;
  1601. case '双击':
  1602. return true;
  1603. default:
  1604. return false;
  1605. }
  1606. }
  1607.  
  1608. // 返回复制长度的切换函数
  1609. function toggleSmartCopyLength() {
  1610. const smartCopyLength = copySwitch_smartCpoy.getSelectBoxValue();
  1611.  
  1612. switch (smartCopyLength) {
  1613. case '默认':
  1614. return false;
  1615. case '智能':
  1616. return true;
  1617. default:
  1618. return false;
  1619. }
  1620. }
  1621.  
  1622. // 生成过滤词汇的正则表达式
  1623. var chineseFilterWords = ["旗舰店", "旗舰", "商品", "海外", "官方", "食品", "化妆品", "生活馆",
  1624. "专营店", "国旅", "数码", "医药", "专卖店", "饮品", "女装", "男装", "企业店", "童装",
  1625. "中国", "集团"];
  1626. var chineseFilterRegex = new RegExp(chineseFilterWords.join('|'), 'g'); // 合并成一个正则表达式
  1627.  
  1628. // 检查文本开头并根据要求处理
  1629. function processText(text) {
  1630. var englishPattern = /^[A-Za-z\s.,!?;:'"-]+/; // 匹配以英文开头的部分,包含常见符号
  1631.  
  1632. // 检查是否以英文开头
  1633. var match = text.match(englishPattern);
  1634. if (match) {
  1635. // 如果以英文开头,返回匹配的英文部分
  1636. return match[0];
  1637. } else {
  1638. // 如果以中文开头,使用正则表达式过滤指定词汇
  1639. return text.replace(chineseFilterRegex, '');
  1640. }
  1641. }
  1642.  
  1643. // 添加鼠标悬浮和移出事件监听器
  1644. NotificationContainer.addEventListener('mouseenter', function () {
  1645. clearTimeout(notificationTimer); // 悬浮时清除计时器
  1646. // console.log('Mouse entered notification'); // 调试日志
  1647. });
  1648. NotificationContainer.addEventListener('mouseleave', function () {
  1649. // console.log('Mouse left notification'); // 调试日志
  1650. var time = 3000;
  1651. if (itemSort_countInputFun.getDivButtonState()) time = 0;
  1652. resetTimer(time); // 移出时重置计时器
  1653. });
  1654.  
  1655. function showNotification(message, duringTime = 3000, showImage = false) {
  1656. // 清除之前的计时器
  1657. if (notificationTimer) {
  1658. clearTimeout(notificationTimer);
  1659. }
  1660.  
  1661. // 重置隐藏标志
  1662. isHiding = false;
  1663.  
  1664. // 重置通知样式
  1665. NotificationContainer.innerHTML = '';
  1666. NotificationContainer.style.width = 'auto';
  1667. NotificationContainer.style.transform = 'translateX(-50%)';
  1668. NotificationContainer.style.animation = 'none';
  1669. NotificationContainer.style.padding = '10px';
  1670.  
  1671. // 短暂移除并重新添加通知元素,强制触发动画
  1672. if (NotificationContainer.parentNode) {
  1673. document.body.removeChild(NotificationContainer);
  1674. }
  1675.  
  1676. setTimeout(() => {
  1677. document.body.appendChild(NotificationContainer);
  1678.  
  1679. // 设置通知文本内容
  1680. NotificationContainer.innerHTML = message;
  1681.  
  1682. // 如果指定了显示图片,则读取剪贴板中的图片并显示
  1683. if (showImage) {
  1684. NotificationContainer.style.padding = '5px';
  1685. navigator.clipboard.read().then(async function (data) {
  1686. for (const item of data) {
  1687. for (const type of item.types) {
  1688. if (type.startsWith('image/')) {
  1689. const blob = await item.getType(type);
  1690. const imageURL = URL.createObjectURL(blob);
  1691.  
  1692. const imageElement = document.createElement('img');
  1693. imageElement.src = imageURL;
  1694. imageElement.style.width = '300px';
  1695. imageElement.style.borderRadius = '8px';
  1696.  
  1697. const imageContainer = document.createElement('div');
  1698. imageContainer.style.paddingTop = '10px';
  1699. imageElement.style.maxWidth = 'auto';
  1700. imageContainer.style.borderRadius = '8px';
  1701. imageContainer.style.margin = 'auto';
  1702. imageContainer.style.display = 'block';
  1703. imageContainer.appendChild(imageElement);
  1704.  
  1705. NotificationContainer.appendChild(imageContainer);
  1706.  
  1707. // 图片加载完成后调整位置并设置消失定时器
  1708. imageElement.onload = function () {
  1709. NotificationContainer.style.left = '50%';
  1710.  
  1711. NotificationContainer.style.display = 'block';
  1712. NotificationContainer.style.animation = 'showNotification 0.5s forwards';
  1713. // 设置消失动画计时器
  1714. resetTimer(duringTime);
  1715. };
  1716.  
  1717. break;
  1718. }
  1719. }
  1720. }
  1721. }).catch(function (error) {
  1722. console.error('Error reading clipboard:', error);
  1723. });
  1724. } else {
  1725. // 显示通知
  1726. NotificationContainer.style.display = 'block';
  1727. NotificationContainer.style.animation = 'showNotification 0.5s forwards';
  1728.  
  1729. // 设置消失动画计时器
  1730. resetTimer(duringTime);
  1731. }
  1732. }, 50); // 确保通知元素短暂移除再添加
  1733. }
  1734.  
  1735. function hideNotification() {
  1736. if (isHiding) return;
  1737. isHiding = true;
  1738.  
  1739. NotificationContainer.style.animation = 'hideNotification 0.5s forwards';
  1740.  
  1741. // 在动画结束后隐藏元素
  1742. notificationTimer = setTimeout(function () {
  1743. NotificationContainer.style.display = 'none';
  1744. isHiding = false;
  1745. }, 500);
  1746. }
  1747.  
  1748. function resetTimer(duringTime = 3000) {
  1749. if (notificationTimer) {
  1750. clearTimeout(notificationTimer);
  1751. // console.log("清除计时器");
  1752. }
  1753. if (duringTime > 0) {
  1754. notificationTimer = setTimeout(function () {
  1755. hideNotification();
  1756. // console.log("设置计时器");
  1757. }, duringTime); // 3秒后自动隐藏通知
  1758. }
  1759. }
  1760.  
  1761. async function clickButton(pastedData = true, delayTime = 0, containerSelector = document, buttonSelector, buttonText = '') {
  1762. // 判断粘贴内容是否为空或为 true
  1763. if (pastedData === true || (typeof pastedData === 'string' && pastedData.trim().length > 0)) {
  1764. // 查找指定容器内的按钮
  1765. var container = containerSelector === document ? document : document.querySelector(containerSelector);
  1766. if (container) {
  1767. // 如果提供了 buttonText,则进一步检查按钮文本内容
  1768. var buttons = container.querySelectorAll(buttonSelector);
  1769. var button = null;
  1770. if (buttonText) {
  1771. buttons.forEach(btn => {
  1772. var span = btn.querySelector('span');
  1773. if (span && span.textContent.trim() === buttonText) {
  1774. button = btn;
  1775. }
  1776. });
  1777. } else {
  1778. button = buttons[0]; // 如果没有提供 buttonText,默认选择第一个匹配的按钮
  1779. }
  1780.  
  1781. // 如果找到符合条件的按钮,延迟点击
  1782. if (button) {
  1783. setTimeout(function () {
  1784. button.click();
  1785. // console.log(`已点击文本内容为“${buttonText || '按钮'}”的按钮`);
  1786. }, delayTime);
  1787. } else {
  1788. console.log(`未找到符合条件的按钮`);
  1789. }
  1790. }
  1791. }
  1792. }
  1793.  
  1794. // 监听粘贴事件
  1795. document.addEventListener('paste', function (event) {
  1796. // 判断是否开启了自动点击功能且在匹配的网址上
  1797. if (sAF_pastedSeachSwitchConta.getSwitchState() && isHomeURL()) {
  1798. // 获取粘贴板中的内容
  1799. var pastedData = (event.clipboardData);
  1800. var className = '.ant-btn.css-9fw9up.ant-btn-primary';
  1801. clickButton(pastedData, 100, undefined, className);
  1802. }
  1803. });
  1804.  
  1805. // 检查文本中是否是天猫链接函数
  1806. function checkForTmallInClipboard(text) {
  1807. const regex = /https:\/\/[^ ]*tmall[^ ]*id=\d{12}/;
  1808. return regex.test(text);
  1809. }
  1810.  
  1811. function checkForChaoshiInClipboard(text) {
  1812. const regex = /https:\/\/[^ ]*chaoshi[^ ]*id=\d{12}/;
  1813. return regex.test(text);
  1814. }
  1815.  
  1816. function checkForFeizhuInClipboard(text) {
  1817. const regex = /https:\/\/[^ ]*fliggy[^ ]*id=\d{12}/;
  1818. return regex.test(text);
  1819. }
  1820.  
  1821. // 转换为移动设备链接(测试)
  1822. function changeLinkToMobilePhone(link) {
  1823. const idMatch = extractIdFromClipboard(link);
  1824. return "https://detail.m.tmall.com/item.htm?id=" + idMatch;
  1825. }
  1826.  
  1827. // 提取链接id函数
  1828. function extractIdFromClipboard(text) {
  1829. // 检查是否只含有11-12位数字
  1830. // const onlyDigitsMatch = text.match(/^\d{11,12}$/);
  1831. const onlyDigitsMatch = text.match(/^\d{11,12}$/);
  1832. if (onlyDigitsMatch) {
  1833. return text; // 如果剪切板仅包含11位或12位数字,直接返回
  1834. }
  1835.  
  1836. // 匹配 id= 后面的11或12位数字
  1837. // const idMatch = text.match(/id=(\d{11,12})/);
  1838. const idMatch = text.match(/id=(\d{11,12})/);
  1839. return idMatch ? idMatch[1] : null;
  1840. }
  1841.  
  1842. // 粘贴功能
  1843. async function simulatePaste(targetElement, clearBeforePaste = true, defaultText = '') {
  1844. try {
  1845. let clipboardText = defaultText;
  1846.  
  1847. if (clipboardText === '') {
  1848. // 读取剪贴板内容或预设内容
  1849. clipboardText = await navigator.clipboard.readText();
  1850. }
  1851. console.log('粘贴内容:' + clipboardText);
  1852.  
  1853. // 检查目标元素是否为可编辑元素
  1854. if (targetElement.isContentEditable || targetElement.tagName === 'INPUT' || targetElement.tagName === 'TEXTAREA') {
  1855. // 如果clearBeforePaste为true,清空目标元素的内容
  1856. if (clearBeforePaste) {
  1857. if (targetElement.isContentEditable) {
  1858. targetElement.innerHTML = '';
  1859. } else {
  1860. targetElement.value = '';
  1861. }
  1862. }
  1863.  
  1864. // 插入剪贴板内容到目标元素
  1865. if (document.execCommand('insertText', false, clipboardText)) {
  1866. // console.log('粘贴成功:' + clipboardText);
  1867. } else {
  1868. targetElement.value += clipboardText;
  1869. }
  1870. } else {
  1871. // alert('目标元素不可编辑');
  1872. }
  1873. } catch (err) {
  1874. console.error('读取剪贴板内容失败:', err);
  1875. showNotification("读取剪贴板内容失败");
  1876. }
  1877. }
  1878.  
  1879. function updateClipboardContent(olnyItemId = false) {
  1880. var tmail = "https://detail.tmall.com/item.htm?id=";
  1881. var chaoshi = "https://chaoshi.detail.tmall.com/item.htm?id=";
  1882. var taobao = "https://item.taobao.com/item.htm?id=";
  1883. var feizhu = "https://traveldetail.fliggy.com/item.htm?id=";
  1884. navigator.clipboard.readText().then((clipText) => {
  1885. const isTmall = checkForTmallInClipboard(clipText);
  1886. const isChaoshi = checkForChaoshiInClipboard(clipText);
  1887. const isFeizhu = checkForFeizhuInClipboard(clipText);
  1888. const itemId = extractIdFromClipboard(clipText);
  1889. // console.log("itemId:" + itemId);
  1890. // console.log("itemId-length:" + itemId.length);
  1891.  
  1892. if (itemId) {
  1893. var newUrl;
  1894. if ((isTmall || isFeizhu) && !isChaoshi) {
  1895. // 转换为天猫链接
  1896. newUrl = tmail + itemId;
  1897. }
  1898. else if (isChaoshi) {
  1899. // 转换为猫超链接
  1900. newUrl = chaoshi + itemId;
  1901. } else {
  1902. if (true) {
  1903. // 转换为淘宝链接
  1904. newUrl = taobao + itemId;
  1905. } else {
  1906. // 转换为天猫链接
  1907. newUrl = tmail + itemId;
  1908. }
  1909. }
  1910. if (olnyItemId) {
  1911. GM_setClipboard(itemId);
  1912. if (!getLinksArrState) {
  1913. showNotification("剪贴板内容已更新为:" + itemId);
  1914. }
  1915. } else {
  1916. GM_setClipboard(newUrl);
  1917. if (!getLinksArrState) {
  1918. showNotification("剪贴板内容已更新为:" + newUrl);
  1919. }
  1920. //console.log('剪贴板内容已更新为:' + newUrl);
  1921. }
  1922. } else {
  1923. if (mainOnlyItemId_checkTtemLink.getSwitchState()) {
  1924. // 防止错误粘贴功能
  1925. GM_setClipboard("12位数字ID不全");
  1926. }
  1927. if (!getLinksArrState) {
  1928. showNotification("剪贴板中没有找到12位数字ID");
  1929. }
  1930. // console.log('剪贴板中没有找到12位数字ID');
  1931. }
  1932. }).catch((err) => {
  1933. console.error('读取剪贴板失败:', err);
  1934. });
  1935. }
  1936.  
  1937. // 查找位于数组中的位置
  1938. function findItemIdForArr(itemId) {
  1939. // 使用 indexOf 查找 itemId 在 upLiveIdArray 中的位置
  1940. let index = upLiveIdArray.indexOf(itemId);
  1941.  
  1942. // 如果 index 是 -1,表示未找到,否则返回 index
  1943. // console.log('index:' + index);
  1944. return index;
  1945. }
  1946.  
  1947. // 返回当前页面的id
  1948. function getPageItemId() {
  1949. const pageIds = document.getElementById('MarkHighlight-upLiveId-upLiveId');
  1950.  
  1951. let itemIds = [];
  1952.  
  1953. for (let i = 0; i < pageIds.length; i++) {
  1954. itemIds.push(pageIds[i].innerText);
  1955. }
  1956.  
  1957. return itemIds;
  1958. }
  1959.  
  1960. function search_clickDownLabel(callback, delayTime = 600) {
  1961. setTimeout(async () => {
  1962. // 寻找 .text-search-components 容器
  1963. const container = document.querySelector('.text-search-components');
  1964.  
  1965. // 检查是否找到了容器
  1966. if (container) {
  1967. // 在容器内寻找含有 aria-label="down" 的 span 标签
  1968. const downSpan = container.querySelector('span[aria-label="down"]');
  1969.  
  1970. // 检查是否找到了该 span
  1971. if (downSpan) {
  1972. // 获取其父 span
  1973. const parentSpan = downSpan.closest('span');
  1974.  
  1975. // 检查父 span 是否在容器内(可选)
  1976. if (parentSpan && container.contains(parentSpan)) {
  1977. await waitForButtonToEnable(); // 等待按钮启用
  1978. // 点击父 span
  1979. parentSpan.click();
  1980.  
  1981. // 调用回调函数
  1982. if (typeof callback === 'function') {
  1983. callback();
  1984. }
  1985. } else {
  1986. console.warn('未找到在 .text-search-components 内的含有 aria-label="down" 的 span 标签的父 span');
  1987. }
  1988. } else {
  1989. console.warn('未在 .text-search-components 内找到含有 aria-label="down" 的 span 标签');
  1990. }
  1991. } else {
  1992. console.warn('未找到 .text-search-components 容器');
  1993. }
  1994. }, delayTime);
  1995. }
  1996.  
  1997. function isDisableButton() {
  1998. const buttons = document.querySelectorAll('.text-search-components-btn');
  1999.  
  2000. let downButton;
  2001. for (let i = 0; i < buttons.length; i++) {
  2002. if (buttons[i].querySelector('span[aria-label="down"]')) {
  2003. downButton = buttons[i];
  2004. break; // 找到后就可以退出循环了
  2005. }
  2006. }
  2007.  
  2008. // 检查 downButton 是否存在,并且是否被禁用
  2009. if (downButton) {
  2010. return downButton.disabled;
  2011. } else {
  2012. // 如果没有找到 downButton,则返回 false 或者根据你的需求返回其他值
  2013. return false;
  2014. }
  2015. }
  2016.  
  2017. async function waitForButtonToEnable(maxWaitTime = 60000, delayTime = 500) {
  2018. return new Promise((resolve, reject) => {
  2019. const startTime = Date.now();
  2020. const interval = setInterval(() => {
  2021. if (!isDisableButton()) {
  2022. clearInterval(interval);
  2023. setTimeout(() => {
  2024. resolve();
  2025. }, delayTime);
  2026. }
  2027.  
  2028. const elapsedTime = Date.now() - startTime;
  2029. if (elapsedTime >= maxWaitTime) {
  2030. clearInterval(interval);
  2031. reject(new Error('Button enabling timed out after 60 seconds'));
  2032. }
  2033. }, 100); // 每500毫秒检查一次
  2034. });
  2035. }
  2036.  
  2037. function click_autoInputForIndex(rowIndex) {
  2038. // 找到带有指定 row-index 的 div
  2039. const targetDiv = document.querySelector(`div[row-index="${rowIndex}"]`);
  2040.  
  2041. // 在 targetDiv 内查找 col-id="weight" 的元素
  2042. if (targetDiv) {
  2043. const weightElement = targetDiv.querySelector('div[col-id="weight"]');
  2044.  
  2045. if (weightElement) {
  2046. const focusElement = weightElement.querySelector('.sortWeights___Kn8mn');
  2047.  
  2048. if (focusElement) {
  2049. focusElement.click(); // 点击来激活输入框
  2050.  
  2051. setTimeout(() => {
  2052. focusElement.click(); // 再次点击来取消输入框
  2053. if (linksArr.length === 0 && !AutoSortState) {
  2054. showNotification("成功找到商品&保存成功");
  2055. }
  2056. }, 100);
  2057. }
  2058. }
  2059. }
  2060. }
  2061.  
  2062. function checkActiveTab_GuaLian() {
  2063. // 查询包含 data-node-key="2" 的 div 元素
  2064. const divElement = document.querySelector('div[data-node-key="2"]');
  2065.  
  2066. // 如果找到了该元素
  2067. if (divElement) {
  2068. // 检查该元素的 classList 是否包含 ant-tabs-tab-active
  2069. const isActive = divElement.classList.contains('ant-tabs-tab-active');
  2070.  
  2071. // console.log('Is the tab active:', isActive);
  2072. return isActive;
  2073. } else {
  2074. // 如果没有找到,输出信息或处理错误
  2075. // console.log('No div element with data-node-key="2" found.');
  2076. return false;
  2077. }
  2078. }
  2079.  
  2080. // 检查当前页面是否在紧急加品页面
  2081. function checkActiveTab_JiaPin() {
  2082. // 查询包含 data-node-key="4" 的 div 元素
  2083. const divElement = document.querySelector('div[data-node-key="4"]');
  2084.  
  2085. // 如果找到了该元素
  2086. if (divElement) {
  2087. // 检查该元素的 classList 是否包含 ant-tabs-tab-active
  2088. const isActive = divElement.classList.contains('ant-tabs-tab-active');
  2089.  
  2090. // console.log('Is the tab active:', isActive);
  2091. return isActive;
  2092. } else {
  2093. // 如果没有找到,输出信息或处理错误
  2094. // console.log('No div element with data-node-key="2" found.');
  2095. return false;
  2096. }
  2097. }
  2098.  
  2099. // 检查当前页面是否在待确认订单页面
  2100. function checkActiveTab_WaitComfirm() {
  2101. // 查询包含 data-node-key="5" 的 div 元素
  2102. const divElement = document.querySelector('div[data-node-key="5"]');
  2103.  
  2104. // 如果找到了该元素
  2105. if (divElement) {
  2106. // 检查该元素的 classList 是否包含 ant-tabs-tab-active
  2107. const isActive = divElement.classList.contains('ant-tabs-tab-active');
  2108.  
  2109. // console.log('Is the tab active:', isActive);
  2110. return isActive;
  2111. } else {
  2112. // 如果没有找到,输出信息或处理错误
  2113. // console.log('No div element with data-node-key="2" found.');
  2114. return false;
  2115. }
  2116. }
  2117.  
  2118. let linksArr = []; // 全局数组,用于存储从剪切板中提取的链接
  2119.  
  2120. function getLinksArrState() {
  2121. return linksArr.length > 0;
  2122. }
  2123.  
  2124. // 从剪切板中提取所有链接并存储到全局数组中
  2125. async function extractLinksFromClipboard() {
  2126. try {
  2127. // 读取剪贴板内容
  2128. const text = await navigator.clipboard.readText();
  2129.  
  2130. linksArr = []; // 清空原有的数组
  2131.  
  2132. // 正则表达式匹配URL
  2133. const urlPattern = /https?:\/\/[^\s]+/g;
  2134. let matches = text.match(urlPattern);
  2135.  
  2136. if (matches) {
  2137. // 将找到的链接添加到全局数组中
  2138. linksArr.push(...matches);
  2139.  
  2140. // console.log('Links extracted and stored:', linksArr);
  2141. } else {
  2142. // console.log('No links found in the clipboard.');
  2143. }
  2144. } catch (err) {
  2145. // console.error('Failed to read clipboard contents: ', err);
  2146. }
  2147. }
  2148.  
  2149.  
  2150. // 等待页面加载结束,最长等待时间为60秒
  2151. async function waitForPageToLoad(maxWaitTime = 60000, delayTime = 500) {
  2152. return new Promise((resolve, reject) => {
  2153. const startTime = Date.now();
  2154. const interval = setInterval(() => {
  2155. if (!isItemPageLoading()) {
  2156. clearInterval(interval);
  2157. setTimeout(() => {
  2158. resolve();
  2159. }, delayTime);
  2160. }
  2161.  
  2162. const elapsedTime = Date.now() - startTime;
  2163. if (elapsedTime >= maxWaitTime) {
  2164. clearInterval(interval);
  2165. reject(new Error('Page loading timed out after 60 seconds'));
  2166. }
  2167. }, 500); // 每500毫秒检查一次
  2168. });
  2169. }
  2170.  
  2171. const MAX_RETRY_TIME = 5; // 最大重试次数
  2172. let retryCount = 0; // 重试次数
  2173.  
  2174. // 遍历 linksArr 并处理每个链接
  2175. async function processAllLinks() {
  2176. await extractLinksFromClipboard(); // 从剪切板中提取链接
  2177. // console.log('linksArr:', linksArr);
  2178.  
  2179. for (let i = 0; i < linksArr.length; i++) {
  2180. let link = linksArr[i];
  2181.  
  2182. // 显示当前处理的链接
  2183. showNotification(`正在处理第 ${i + 1}/ ${linksArr.length} 个`, 0);
  2184.  
  2185. // 模拟将链接放入剪切板
  2186. await navigator.clipboard.writeText(link);
  2187. // console.log('Link copied to clipboard:', link);
  2188.  
  2189. // 点击搜索按钮
  2190. await simulateClickOnAntInputAffixWrapper();
  2191.  
  2192. // 可能需要等待一段时间,以便于上一个操作完成
  2193. await new Promise(resolve => setTimeout(resolve, 2500)); // 根据实际情况调整等待时间
  2194.  
  2195. // 等待页面加载完成
  2196. try {
  2197. await waitForPageToLoad();
  2198. // console.log('Page loaded successfully');
  2199. } catch (error) {
  2200. // console.error('Page loading timed out:', error);
  2201. showNotification('页面加载超时');
  2202. }
  2203. }
  2204.  
  2205. retryCount = 0; // 重置重试次数
  2206. await checkSelfLink();
  2207. }
  2208.  
  2209. async function checkSelfLink() {
  2210. return new Promise(async (resolve, reject) => {
  2211. if (retryCount > 1) {
  2212. let className = '.ant-btn.css-9fw9up.ant-btn-primary';
  2213. clickButton(true, 100, undefined, className);
  2214. await new Promise(resolve => setTimeout(resolve, 3500)); // 根据实际情况调整等待时间
  2215. }
  2216.  
  2217. // 将连接转储为id
  2218. let idArr = linksArr.map(link => extractIdFromClipboard(link));
  2219. // console.log('idArr:', idArr);
  2220.  
  2221. let weight = preGetValue();
  2222. let retryLinkArr = [];
  2223.  
  2224. for (let i = 0; i < idArr.length; i++) {
  2225. let idWeight = goodsUrlCheckArray[idArr[i]].weight;
  2226. if (idWeight != weight) {
  2227. retryLinkArr.push(linksArr[i]);
  2228. }
  2229. }
  2230.  
  2231. if (retryLinkArr.length > 0) {
  2232. retryCount++;
  2233. if (retryCount > MAX_RETRY_TIME) {
  2234. showNotification('重试次数已达最大');
  2235. reject(new Error('重试次数已达最大'));
  2236. return;
  2237. }
  2238. } else {
  2239. showNotification('全部处理完成');
  2240. linksArr = []; // 清空数组
  2241. resolve();
  2242. return;
  2243. }
  2244.  
  2245. showNotification('执行自检流程···', 0);
  2246. // console.log('retryLinkArr:', retryLinkArr);
  2247.  
  2248. for (let i = 0; i < retryLinkArr.length; i++) {
  2249. let link = retryLinkArr[i];
  2250.  
  2251. // 显示当前处理的链接
  2252. showNotification(`[重试 ${retryCount} ] 正在处理第 ${i + 1}/ ${retryLinkArr.length} 个`, 0);
  2253.  
  2254. // 模拟将链接放入剪切板
  2255. await navigator.clipboard.writeText(link);
  2256. // console.log('Link copied to clipboard:', link);
  2257.  
  2258. // 点击搜索按钮
  2259. await simulateClickOnAntInputAffixWrapper();
  2260.  
  2261. // 可能需要等待一段时间,以便于上一个操作完成
  2262. await new Promise(resolve => setTimeout(resolve, 2000)); // 根据实际情况调整等待时间
  2263.  
  2264. // 等待页面加载完成
  2265. try {
  2266. await waitForPageToLoad();
  2267. // console.log('Page loaded successfully');
  2268. } catch (error) {
  2269. // console.error('Page loading timed out:', error);
  2270. showNotification('页面加载超时');
  2271. }
  2272. }
  2273.  
  2274. checkSelfLink();
  2275. });
  2276. }
  2277.  
  2278. function addButtonForHomePage_processAllLinks() {
  2279. var buttonName = '批量处理';
  2280. var buttonId = 'processAllLinksButton';
  2281. const smartCopyButton = createDefaultButton(buttonId, buttonName, () => {
  2282. processAllLinks();
  2283. });
  2284.  
  2285. // 找到搜索栏目
  2286. const searchToolBar = document.querySelector('.ant-pro-table-list-toolbar-left');
  2287. const scButton = document.getElementById(buttonId);
  2288. if (searchToolBar) {
  2289. if (!scButton) {
  2290. searchToolBar.appendChild(smartCopyButton);
  2291. } else {
  2292. if ((onlyItemIdForSearchDiv_onlyNoIntroduce.getSwitchState() ? checkActiveTab_GuaLian() : true) && sonMain_autoFlagedSwitch.getSwitchState()) {
  2293. scButton.style.display = '';
  2294. } else {
  2295. scButton.style.display = 'none';
  2296. }
  2297. }
  2298. }
  2299. }
  2300.  
  2301. // 模拟点击 .ant-input-affix-wrapper 元素
  2302. function simulateClickOnAntInputAffixWrapper() {
  2303. return new Promise((resolve, reject) => {
  2304. const element = document.querySelector('.text-search-components .ant-input-affix-wrapper.css-9fw9up');
  2305.  
  2306. if (element) {
  2307. // // 创建点击事件
  2308. // const clickEvent = new MouseEvent('click', {
  2309. // bubbles: true,
  2310. // cancelable: true,
  2311. // view: window
  2312. // });
  2313.  
  2314. // // 触发点击事件
  2315. // element.dispatchEvent(clickEvent);
  2316. element.click();
  2317.  
  2318. console.log('Simulated click on ant-input-affix-wrapper:', element);
  2319. resolve();
  2320. } else {
  2321. console.error('Element not found');
  2322. reject(new Error('Element not found'));
  2323. }
  2324. });
  2325. }
  2326.  
  2327. // 提取链接id粘贴功能实现
  2328. function handle_onlyItemIdForSearch(event) {
  2329. if (event.target.closest('.text-search-components .ant-input-affix-wrapper.css-9fw9up') &&
  2330. !event.target.closest('.ant-input-suffix')) {
  2331. console.log('点击了 ant-input-affix-wrapper');
  2332.  
  2333. updateClipboardContent(true);
  2334. processItemIdForSearch(document.activeElement);
  2335. }
  2336. }
  2337.  
  2338. // 处理单个链接
  2339. function processItemIdForSearch(targetElement) {
  2340. simulatePaste(targetElement);
  2341.  
  2342. navigator.clipboard.readText().then((itemId) => {
  2343. console.log('itemID:' + itemId);
  2344. const index = findItemIdForArr(itemId);
  2345.  
  2346. if (index !== -1) {
  2347. // itemPageScroll(120 * index, false);
  2348. search_clickDownLabel(() => {
  2349. if (onlyItemIdForSearchDiv_autoInputSort.getSwitchState()) {
  2350. click_autoInputForIndex(index);
  2351. }
  2352. });
  2353. }
  2354. }).catch(err => {
  2355. console.error('读取剪贴板内容失败:', err);
  2356. showNotification("读取剪贴板内容失败");
  2357. });
  2358. }
  2359.  
  2360. // 监听鼠标左键点击事件
  2361. document.addEventListener('click', function (event) {
  2362. // 提取商品链接粘贴功能区域
  2363. if (sonMain_onlyItemIdSwitch.getSwitchState() && isHomeURL()) {
  2364. if (event.target.closest('.ant-form-item.liveLinkFormItem___RPAQZ.css-9fw9up.ant-form-item-has-success')) {
  2365. if (event.target.classList.contains('ant-input-affix-wrapper') ||
  2366. event.target.classList.contains('ant-input-affix-wrapper-status-error') ||
  2367. event.target.classList.contains('css-9fw9up')) {
  2368. // console.log('目标元素被点击');
  2369. updateClipboardContent();
  2370. simulatePaste(document.activeElement);
  2371.  
  2372. // 点击保存按钮
  2373. if (mainOnlyItemId_autoSave.getSwitchState()) {
  2374. var fbutton = '.ant-drawer-footer';
  2375. var buttonName = '.ant-btn.css-9fw9up.ant-btn-primary';
  2376. showNotification("粘贴成功&保存成功");
  2377. clickButton(undefined, 500, fbutton, buttonName);
  2378. }
  2379. }
  2380. }
  2381. }
  2382. // 提取链接id粘贴功能区域
  2383. // 用于控制功能开关
  2384. if (sonMain_onlyItemIdForSearchDiv.getSwitchState() && isHomeURL()) {
  2385. if (onlyItemIdForSearchDiv_onlyNoIntroduce.getSwitchState()) {
  2386. if (checkActiveTab_GuaLian()) {
  2387. handle_onlyItemIdForSearch(event);
  2388. }
  2389. } else {
  2390. handle_onlyItemIdForSearch(event);
  2391. }
  2392. }
  2393.  
  2394. // 自动序号标1功能
  2395. if (sonMain_itemSortSwitch.getSwitchState()) {
  2396. if (event.target.closest('.ant-input-number.ant-input-number-sm.css-1ayq15k')) {
  2397. if (event.target.classList.contains('ant-input-number-input')) {
  2398. // console.log('目标元素被点击');
  2399. if (!itemSort_countInputFun.getDivButtonState()) {
  2400. GM_setClipboard(preGetValue());
  2401. } else {
  2402. if (itemSort_countInputFun_rateInput.getSwitchState()) {
  2403. GM_setClipboard((itemSort_countInputFun.getCount() - 1) * 10);
  2404. } else {
  2405. GM_setClipboard(itemSort_countInputFun.getCount() - 1);
  2406. }
  2407. }
  2408. if (!AutoSortState) {
  2409. simulatePaste(document.activeElement);
  2410. } else {
  2411. simulatePaste(document.activeElement, true, autoSortNum);
  2412. }
  2413. }
  2414. }
  2415. }
  2416. // 自动激活排序输入框
  2417. if (sonMain_itemSortSwitch.getSwitchState()) {
  2418. if (event.target.classList.contains('sortWeights___Kn8mn') ||
  2419. event.target.closest('.sortWeights___Kn8mn')) {
  2420. // console.log('找到sortWeights___Kn8mn');
  2421. activateInputFieldAndSave(event);
  2422. }
  2423. }
  2424. });
  2425.  
  2426. function preGetValue() {
  2427. if (itemSort_inputConter.getSelectBoxValue() === '空') {
  2428. return '';
  2429. } else {
  2430. return itemSort_inputConter.getSelectBoxValue();
  2431. }
  2432. }
  2433.  
  2434.  
  2435. /* 快捷截图功能区 */
  2436. function getTableCardImageUrl() {
  2437. // 查找 class 为 'link-node-img-container' 的 div
  2438. const container = document.querySelector('.link-node-img-container');
  2439.  
  2440. // 检查是否找到了该元素
  2441. if (container) {
  2442. // 查找 div 内的 img 元素
  2443. const imgElement = container.querySelector('img');
  2444.  
  2445. // 检查是否有 img 元素,并获取其 src 属性
  2446. if (imgElement) {
  2447. const imgUrl = imgElement.src; // 获取 img 的 src URL
  2448. console.log('Image URL:', imgUrl); // 输出到控制台
  2449. return imgUrl;
  2450. } else {
  2451. // console.log('No img element found inside the container');
  2452. return null;
  2453. }
  2454. } else {
  2455. // console.log('No div with the class "link-node-img-container" found');
  2456. return null;
  2457. }
  2458. }
  2459.  
  2460. function setTableCardImageUrl(imgUrl) {
  2461. // 查找 class 为 'link-node-img-container' 的 div
  2462. const container = document.querySelector('.link-node-img-container');
  2463.  
  2464. // 检查是否找到了该元素
  2465. if (container) {
  2466. // 查找 div 内的 img 元素
  2467. const imgElement = container.querySelector('img');
  2468.  
  2469. // 检查是否有 img 元素,并获取其 src 属性
  2470. if (imgElement) {
  2471. imgElement.src = imgUrl; // 更新 img 的 src URL
  2472. console.log('Image URL已更新为:', imgUrl); // 输出到控制台
  2473. } else {
  2474. // console.log('No img element found inside the container');
  2475. }
  2476. } else {
  2477. // console.log('No div with the class "link-node-img-container" found');
  2478. }
  2479. }
  2480.  
  2481. // 返回渲染倍数
  2482. function selectedTableCardPngResolution() {
  2483. const resolution = tableCardPng_resolution.getSelectBoxValue();
  2484. // console.log("设定的分辨率:" + resolution);
  2485. switch (resolution) {
  2486. case '普通':
  2487. return 1.5;
  2488. case '高清':
  2489. return 2.5;
  2490. case '超清':
  2491. return 4;
  2492. default:
  2493. return 2.5;
  2494. }
  2495. }
  2496.  
  2497. function loadImageIcon() {
  2498. return '<span class="ant-btn-icon ant-btn-loading-icon" style=""><span role="img" aria-label="loading" class="anticon anticon-loading anticon-spin"><svg viewBox="0 0 1024 1024" focusable="false" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path></svg></span></span>';
  2499. }
  2500.  
  2501. if (sonMain_tableCardPngSwitch.getSwitchState() && isTableCardURL()) {
  2502. // Load html2canvas library
  2503. function loadHtml2Canvas(callback) {
  2504. var script = document.createElement('script');
  2505. script.src = 'https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js';
  2506. script.onload = callback;
  2507. document.head.appendChild(script);
  2508. }
  2509.  
  2510. function createCaptureScreenshotButton() {
  2511. const targetClass = '[class*="ant-space"][class*="css-9fw9up"][class*="ant-space-horizontal"][class*="ant-space-align-center"]';
  2512.  
  2513. const captureScreenshot_observer = new MutationObserver((mutationsList, observer) => {
  2514. for (let mutation of mutationsList) {
  2515. if (mutation.type === 'childList') {
  2516. const targetElement = document.querySelector(targetClass);
  2517. if (targetElement) {
  2518. if (document.querySelector('.captureScreenshot')) {
  2519. captureScreenshot_observer.disconnect();
  2520. return;
  2521. }
  2522.  
  2523. var captureScreenshot = document.createElement('div');
  2524. captureScreenshot.classList.add('ant-space-item');
  2525.  
  2526. var captureScreenshotButton = document.createElement('button');
  2527. captureScreenshotButton.textContent = '快捷截图';
  2528. captureScreenshotButton.classList.add('ant-btn', 'css-9fw9up', 'ant-btn-default', 'captureScreenshot');
  2529. captureScreenshot.appendChild(captureScreenshotButton);
  2530.  
  2531. targetElement.insertBefore(captureScreenshot, targetElement.firstChild);
  2532.  
  2533. captureScreenshotButton.addEventListener('click', captureScreenshotFunction);
  2534.  
  2535. captureScreenshot_observer.disconnect();
  2536. break;
  2537. }
  2538. }
  2539. }
  2540. });
  2541.  
  2542. captureScreenshot_observer.observe(document.body, {
  2543. childList: true,
  2544. subtree: true
  2545. });
  2546. }
  2547.  
  2548. function loadImageAsDataURL(url) {
  2549. return new Promise((resolve, reject) => {
  2550. GM_xmlhttpRequest({
  2551. method: 'GET',
  2552. url: url,
  2553. responseType: 'blob',
  2554. onload: function (response) {
  2555. const blob = response.response;
  2556. const reader = new FileReader();
  2557. reader.onloadend = function () {
  2558. resolve(reader.result);
  2559. };
  2560. reader.onerror = function () {
  2561. reject(new Error('Failed to load image'));
  2562. };
  2563. reader.readAsDataURL(blob);
  2564. },
  2565. onerror: function () {
  2566. reject(new Error('Network error'));
  2567. }
  2568. });
  2569. });
  2570. }
  2571.  
  2572. async function captureScreenshotFunction() {
  2573. const tableElement = document.querySelector('table');
  2574. var displayScale = selectedTableCardPngResolution();
  2575. // console.log("设定的倍率:" + displayScale);
  2576.  
  2577. showNotification("截图中 " + loadImageIcon(), 0);
  2578. if (tableElement) {
  2579. const rows = tableElement.querySelectorAll('tr');
  2580.  
  2581. if (rows.length >= 3) {
  2582. rows[2].cells[0].textContent = '';
  2583.  
  2584. // 隐藏除第二行和第三行外的所有行
  2585. rows.forEach((row, index) => {
  2586. if (index !== 1 && index !== 2) {
  2587. if (index === 3 && isShowTableCheckedRow()) {
  2588. row.style.display = '';
  2589. } else {
  2590. row.style.display = 'none';
  2591. }
  2592. }
  2593. });
  2594.  
  2595. const imgElement = rows[2].cells[2].querySelector('img');
  2596. if (imgElement) {
  2597. try {
  2598. const dataUrl = await loadImageAsDataURL(imgElement.src);
  2599. imgElement.src = dataUrl;
  2600.  
  2601. setTimeout(() => {
  2602. // 使用 html2canvas 捕获截图
  2603. html2canvas(tableElement, { scale: displayScale }).then(canvas => {
  2604. // 恢复所有行的显示状态
  2605. rows.forEach(row => {
  2606. row.style.display = '';
  2607. });
  2608.  
  2609. // 隐藏检查格式行
  2610. if (true) {
  2611. rows.forEach((row, index) => {
  2612. if (index === 3) {
  2613. row.style.display = isCheckItemIdRow_onlyPng();
  2614. }
  2615. });
  2616. }
  2617.  
  2618. canvas.toBlob(async function (blob) {
  2619. try {
  2620. await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]);
  2621. console.log("%cTable Screenshot:", "color: #9147ff", "Screenshot copied to clipboard.");
  2622. showNotification("截图已成功复制到剪贴板", undefined, true);
  2623. } catch (error) {
  2624. console.log("%cTable Screenshot: Screenshot failed to copy to clipboard!", "color: #ff8080");
  2625. showNotification("截图失败!");
  2626. }
  2627. });
  2628. });
  2629. }, 200); // 延迟 200 毫秒等待图片加载完毕
  2630. } catch (error) {
  2631. console.log('Image load error:', error);
  2632. }
  2633. } else {
  2634. console.log('Image element not found');
  2635. }
  2636. } else {
  2637. console.log("Table does not have enough rows");
  2638. }
  2639. } else {
  2640. console.log("Table element not found");
  2641. }
  2642. }
  2643.  
  2644. loadHtml2Canvas(createCaptureScreenshotButton);
  2645. }
  2646.  
  2647. /*
  2648. **************************
  2649. 自动激活排序窗口、点击保存
  2650. **************************
  2651. */
  2652. function activateInputFieldAndSave(event) {
  2653. if (true) {
  2654. const click_itemId = handleCellClick(event);
  2655. if (temp_itemId != click_itemId) {
  2656. if (itemSort_countInputFun.getDivButtonState()) {
  2657. itemSort_countInputFun.updateCount(1, 'countInput');
  2658. }
  2659. temp_itemId = click_itemId;
  2660. }
  2661. // countText.textContent = countNum_Sort + 1;
  2662. // console.log('计数:'+countNum_Sort);
  2663. const popover = document.querySelector('.ant-popover:not(.ant-popover-hidden)');
  2664. const inputField = popover.querySelector('.ant-input-number-input');
  2665. if (inputField) {
  2666. inputField.focus();
  2667. inputField.click();
  2668. } else {
  2669. console.log('未找到输入字段');
  2670. }
  2671. }
  2672. }
  2673.  
  2674. // 查找点击的id
  2675. function handleCellClick(event) {
  2676. // 查找最接近的包含行元素的类
  2677. let rowElement = event.target.closest('.ag-row');
  2678.  
  2679. if (rowElement) {
  2680. // 获取row-index属性
  2681. let rowIndex = rowElement.getAttribute('row-index');
  2682. // console.log('找到的行索引:', rowIndex);
  2683.  
  2684. // 使用row-index属性查找行内的span标签
  2685. let targetSpan = document.querySelector(`.ag-row[row-index="${rowIndex}"] span#MarkHighlight-upLiveId-upLiveId`);
  2686.  
  2687. if (targetSpan) {
  2688. return targetSpan.textContent;
  2689. // 打印span的文本内容
  2690. // console.log('目标span的文本内容:', targetSpan.textContent);
  2691. } else {
  2692. // console.log(`在行索引为${rowIndex}的行中未找到id为"MarkHighlight-upLiveId-upLiveId"的span标签。`);
  2693. }
  2694. } else {
  2695. // console.log('未找到点击单元格对应的行。');
  2696. }
  2697. }
  2698.  
  2699. /*
  2700. =================
  2701. 打印标题优化
  2702. =================
  2703. */
  2704. function titlePrint_extractData() {
  2705. const dateElement = document.querySelector('.isSelect___qbUI1 .ant-space.css-9fw9up.ant-space-horizontal.ant-space-align-center.title___mA8xY .ant-space-item:nth-child(2) div');
  2706. const sessionElement = document.querySelector('.truncate.sessionName___HUMKC.font-ma-semibold');
  2707.  
  2708. if (dateElement && sessionElement) {
  2709. const dateText = dateElement.textContent.trim();
  2710. const sessionText = sessionElement.textContent.trim();
  2711.  
  2712. GM_setValue('titlePrint_extractedDate', dateText);
  2713. GM_setValue('titlePrint_extractedSession', sessionText);
  2714.  
  2715. // console.log('Date extracted and stored:', dateText);
  2716. // console.log('Session name extracted and stored:', sessionText);
  2717. }
  2718. }
  2719.  
  2720. /*
  2721. =================
  2722. 手卡标题优化
  2723. =================
  2724. */
  2725. // 网址id提取
  2726. function url_getSessionGoodsId() {
  2727. const url = window.location.href;
  2728. const match = url.match(/sessionGoodsId=(\d+)/);
  2729. return match ? match[1] : null;
  2730. }
  2731.  
  2732. function modifyTableCardURL_title(itemId) {
  2733. // console.log('itemId:', itemId);
  2734. if (sAF_AdvanceTableCardTitle.getSwitchState()) {
  2735. if (itemId && isTableCardURL()) {
  2736. document.title = '【手卡 ' + itemId[0].toString().slice(-4) + '】' + itemId[1];
  2737. }
  2738. }
  2739.  
  2740. }
  2741.  
  2742. /*
  2743. 手卡附带检查链接功能
  2744. */
  2745. function isShowTableCheckedRow() {
  2746. return tableCardPng_checkItemIdRow.getSwitchState();
  2747. }
  2748.  
  2749. function isCheckItemIdRow_onlyPng() {
  2750. if (tableCardPng_checkItemIdRow_onlyPng.getSwitchState()) {
  2751. return 'none';
  2752. } else {
  2753. return '';
  2754. }
  2755. }
  2756.  
  2757. // 检查行生成函数
  2758. function createTableCheckedRow(itemId, width1 = 0, width2 = 0, item_link = '') {
  2759. // 创建 <tr> 元素
  2760. const tr = document.createElement('tr');
  2761. tr.id = 'checkedItemLink_TableCrad';
  2762. tr.style.cssText = 'min-height: 30px; max-height: 30px;';
  2763. tr.style.display = isCheckItemIdRow_onlyPng();
  2764.  
  2765. // 创建第一个 <td> 元素
  2766. const td1 = document.createElement('td');
  2767. td1.colSpan = '3';
  2768. td1.style.cssText = 'padding: 0px; height: 30px; align-items: center; justify-content: center; display: flex;';
  2769. if (width1 > 0) td1.style.width = width1 + 'px';
  2770.  
  2771. const marioSvg = createMarioSVGIconWrapper('svgCheckedRow', false, 16);
  2772.  
  2773. const superItemLink = document.createElement('a');
  2774. superItemLink.href = item_link;
  2775. superItemLink.target = '_blank';
  2776.  
  2777. // 创建第一个 <div> 元素
  2778. const div1 = document.createElement('div');
  2779. div1.style.cssText = 'display: flex; align-items: center; justify-content: center; height: 100%;';
  2780.  
  2781. // 创建第一个 <span> 元素
  2782. const span1 = document.createElement('span');
  2783. span1.style.cssText = 'color: rgb(236, 40, 39); font-size: 16px; text-align: center; margin-left: 1px;';
  2784. span1.textContent = '上播商品ID核对:';
  2785.  
  2786. // 创建第二个 <span> 元素,放置链接内容
  2787. const span2 = document.createElement('span');
  2788. span2.id = 'cureentItem_upLiveId';
  2789. span2.style.cssText = 'color: rgb(51, 51, 51); font-size: 16px; font-weight: bold; transition: color 0.3s ease;';
  2790. span2.textContent = itemId;
  2791.  
  2792. // 设置悬浮时变颜色
  2793. span2.onmouseover = function () {
  2794. if (span2.style.cursor === 'pointer') {
  2795. span2.style.color = 'rgb(0, 145, 255)'; // 悬浮时的颜色
  2796. }
  2797. };
  2798.  
  2799. // 悬浮离开时恢复原色
  2800. span2.onmouseout = function () {
  2801. if (span2.style.cursor === 'pointer') {
  2802. span2.style.color = 'rgb(51, 51, 51)'; // 恢复默认颜色
  2803. }
  2804. };
  2805.  
  2806. if (item_link !== '') {
  2807. span2.style.cursor = 'pointer';
  2808.  
  2809. if (sonMain_pictureInPictureSwitch.getSwitchState()) {
  2810. // 新功能
  2811. // console.log('item_link:', changeLinkToMobilePhone(item_link));
  2812. const pipManager = new PIPManager(changeLinkToMobilePhone(item_link), span2, false);
  2813. // const pipManager = new PIPManager(item_link, span2, true);
  2814. } else {
  2815. // 原有功能
  2816. span2.onclick = function () {
  2817. window.open(item_link, '_blank'); // 跳转到商品链接
  2818. };
  2819. }
  2820. }
  2821.  
  2822. // 将 span2 放入 span1,然后放入 div1
  2823. span1.appendChild(span2);
  2824. superItemLink.appendChild(marioSvg);
  2825. div1.appendChild(superItemLink);
  2826. div1.appendChild(span1);
  2827.  
  2828. // 将 div1 放入 td1
  2829. td1.appendChild(div1);
  2830.  
  2831. // 创建第二个 <td> 元素
  2832. const td2 = document.createElement('td');
  2833. td2.colSpan = '7';
  2834. td2.style.cssText = 'padding: 0px; align-items: center; justify-content: center; height: 30px;';
  2835. if (width2 > 0) td2.style.width = width2 + 'px';
  2836.  
  2837. // 创建第二个 <div> 元素
  2838. const div2 = document.createElement('div');
  2839. div2.style.cssText = 'display: flex; align-items: center; justify-content: center; height: 100%;';
  2840.  
  2841. // 创建第三个 <span> 元素
  2842. const span3 = document.createElement('span');
  2843. span3.style.cssText = 'color: rgb(51, 51, 51); font-size: 16px; text-align: center;white-space: pre-wrap;';
  2844. span3.textContent = '请回复有误、无误、正确之类的,不要收到、1之类的不明确信息,要让我们知道你核对完了';
  2845. // 使用 innerHTML 设置带有部分上色的内容
  2846. span3.innerHTML = `核对完所有栏目后,请回复<span style="color: rgb(13, 193, 97); font-weight: bold;">有误、无误、正确</span>之类的,不要<span style="color: rgb(236, 40, 39); font-weight: bold;">收到、1</span>之类的不明确信息,要让我们知道你核对完了`;
  2847.  
  2848. // 将 span3 放入 div2,然后放入 td2
  2849. div2.appendChild(span3);
  2850. td2.appendChild(div2);
  2851.  
  2852. // 将两个 td 放入 tr
  2853. tr.appendChild(td1);
  2854. tr.appendChild(td2);
  2855.  
  2856. // 返回生成的 <tr> 元素
  2857. return tr;
  2858. }
  2859.  
  2860. // 用于更新插入的行中的 id
  2861. function changeNewTrItemId(itemId, item_link = '') {
  2862. const idSpan = document.getElementById('cureentItem_upLiveId');
  2863. if (idSpan) {
  2864. idSpan.textContent = itemId;
  2865. }
  2866.  
  2867. const span2 = document.getElementById('cureentItem_upLiveId');
  2868.  
  2869. if (item_link !== '') {
  2870. span2.style.cursor = 'pointer';
  2871.  
  2872. if (sonMain_pictureInPictureSwitch.getSwitchState()) {
  2873. // 新功能
  2874. // console.log('item_link:', changeLinkToMobilePhone(item_link));
  2875. const pipManager = new PIPManager(changeLinkToMobilePhone(item_link), span2, false);
  2876. // const pipManager = new PIPManager(item_link, span2, true);
  2877. } else {
  2878. // 原有功能
  2879. span2.onclick = function () {
  2880. window.open(item_link, '_blank'); // 跳转到商品链接
  2881. };
  2882. }
  2883. }
  2884. }
  2885.  
  2886. function insertCheckedRow(itemId, item_link = '') {
  2887. if (!document.getElementById('checkedItemLink_TableCrad')) {
  2888. const tableElement = document.querySelector('table');
  2889.  
  2890. if (tableElement) {
  2891. const rows = tableElement.querySelectorAll('tr');
  2892.  
  2893. // 确保表格中至少有三个 <tr> 元素
  2894. if (rows.length >= 3) {
  2895. // 获取第三个 <tr> 元素
  2896. const thirdRow = rows[2];
  2897.  
  2898. // 生成新的 <tr> 元素
  2899. const newRow = createTableCheckedRow(itemId, 0, 0, item_link);
  2900.  
  2901. // 在第三个 <tr> 元素的后面插入新行
  2902. thirdRow.parentNode.insertBefore(newRow, thirdRow.nextSibling);
  2903. } else {
  2904. // console.log("表格中没有足够的行来插入新行。");
  2905. }
  2906. } else {
  2907. // console.log("没有找到表格");
  2908. }
  2909. } else {
  2910. changeNewTrItemId(upLiveIdArray[0], upLiveIdArray[2]);
  2911. }
  2912. }
  2913.  
  2914. /*
  2915. 用于打印页面的插入XHR数据获取
  2916. */
  2917. let upLiveIdArray = []; // 上播商品id
  2918. let shopNameArray = []; // 店铺名称
  2919. let goodsUrlArray = []; // 商品链接
  2920. let sessionGoodsId = []; // 编辑页面id
  2921.  
  2922. // 封装提取 upLiveId 的函数
  2923. // 打印页面数据获取
  2924. function extractUpLiveIdsFromResponse(responseText) {
  2925. try {
  2926. const response = JSON.parse(responseText);
  2927.  
  2928. // 初始化 upLiveIdArray, shopNameArray ,goodsUrlArray 数组
  2929. upLiveIdArray = [];
  2930. shopNameArray = [];
  2931. goodsUrlArray = [];
  2932. sessionGoodsId = [];
  2933.  
  2934. if (response.data && Array.isArray(response.data)) {
  2935. // 提取 upLiveId 并过滤掉 undefined
  2936. upLiveIdArray = response.data.map(item => item.upLiveId).filter(id => id !== undefined);
  2937.  
  2938. // 提取 sessionGoodsId 并过滤掉 undefined
  2939. sessionGoodsId = response.data.map(item => item.sessionGoodsId).filter(id => id !== undefined);
  2940.  
  2941. // 提取 shopName 并过滤掉 undefined
  2942. shopNameArray = response.data.map(item => {
  2943. if (item.cardUseDataStyle) {
  2944. const cardUseDataStyle = JSON.parse(item.cardUseDataStyle);
  2945. const shopData = cardUseDataStyle.find(subItem => subItem.useFieldName === 'shopName');
  2946. return shopData ? shopData.useFieldValue : undefined;
  2947. }
  2948. }).filter(shopName => shopName !== undefined); // 过滤掉 undefined 的 shopName 值
  2949.  
  2950. // 提取 goodsUrl 并过滤掉 undefined
  2951. goodsUrlArray = response.data.map(item => item.goodsUrl).filter(goodsUrl => goodsUrl !== undefined);
  2952.  
  2953. } else {
  2954. // console.error("响应中未找到有效的 data 字段");
  2955. }
  2956.  
  2957. // console.log("提取的 upLiveId:", upLiveIdArray);
  2958. // console.log("提取的 shopName:", shopNameArray);
  2959. // console.log("提取的 goodsUrl:", goodsUrlArray);
  2960. // console.log("提取的 sessionGoodsId:", sessionGoodsId);
  2961. } catch (error) {
  2962. console.error("解析响应失败:", error);
  2963. }
  2964. }
  2965.  
  2966. // 手卡页面数据获取
  2967. function extractUpLiveIdFromTableCardResponse(responseText) {
  2968. try {
  2969. const response = JSON.parse(responseText);
  2970.  
  2971. // 初始化 upLiveIdArray 数组
  2972. upLiveIdArray = [];
  2973.  
  2974. // 检查响应数据中的 data 字段
  2975. if (response.data && typeof response.data === 'object') {
  2976. // 提取 data 对象中的 upLiveId
  2977. const upLiveId = response.data.upLiveId;
  2978. if (upLiveId !== undefined) {
  2979. // 将 upLiveId 存储到数组中
  2980. upLiveIdArray[0] = upLiveId;
  2981. } else {
  2982. // console.error("响应的 data 对象中未找到 upLiveId 字段");
  2983. }
  2984. // 解析 cardUseDataStyle 字符串为数组
  2985. if (response.data.cardUseDataStyle) {
  2986. const cardUseDataStyle = JSON.parse(response.data.cardUseDataStyle);
  2987.  
  2988. // 在 cardUseDataStyle 数组中查找 useFieldName 为 'shopName' 的对象
  2989. const shopData = cardUseDataStyle.find(item => item.useFieldName === 'shopName');
  2990. if (shopData && shopData.useFieldValue) {
  2991. upLiveIdArray[1] = shopData.useFieldValue; // 存储 shopName 到数组的第 1 位置
  2992. } else {
  2993. // console.error("未找到 useFieldName 为 'shopName' 的对象或 useFieldValue 为空");
  2994. }
  2995. } else {
  2996. // console.error("响应中未找到有效的 cardUseDataStyle 字符串");
  2997. }
  2998.  
  2999. // 提取 data 对象中的 goodsUrl
  3000. const goodsUrl = response.data.goodsUrl;
  3001. if (goodsUrl !== undefined) {
  3002. // 将 upLiveId 存储到数组中
  3003. upLiveIdArray[2] = goodsUrl;
  3004. }
  3005.  
  3006. // 更新手卡标题
  3007. modifyTableCardURL_title(upLiveIdArray);
  3008. } else {
  3009. // console.error("响应中未找到有效的 data 字段,或 data 不是对象");
  3010. }
  3011. } catch (error) {
  3012. // console.error("解析响应失败:", error);
  3013. }
  3014. }
  3015.  
  3016. // 手卡页面切换数据获取
  3017. function extractUpLiveIdsFromNextTableCardResponse(responseText) {
  3018. try {
  3019. const response = JSON.parse(responseText);
  3020.  
  3021. // 初始化 upLiveIdArray 数组
  3022. upLiveIdArray = [];
  3023.  
  3024. // 检查响应数据中的 data 字段
  3025. if (response.data && typeof response.data === 'object') {
  3026. // 提取 data 对象中的 upLiveId
  3027. const upLiveId = response.data.linkHandCardDetailRespDTO.upLiveId;
  3028. if (upLiveId !== undefined) {
  3029. // 将 upLiveId 存储到数组中
  3030. upLiveIdArray[0] = upLiveId;
  3031. } else {
  3032. // console.error("响应的 data 对象中未找到 upLiveId 字段");
  3033. }
  3034. // 解析 cardUseDataStyle 字符串为数组
  3035. if (response.data.linkHandCardDetailRespDTO.cardUseDataStyle) {
  3036. const cardUseDataStyle = JSON.parse(response.data.linkHandCardDetailRespDTO.cardUseDataStyle);
  3037.  
  3038. // 在 cardUseDataStyle 数组中查找 useFieldName 为 'shopName' 的对象
  3039. const shopData = cardUseDataStyle.find(item => item.useFieldName === 'shopName');
  3040. if (shopData && shopData.useFieldValue) {
  3041. upLiveIdArray[1] = shopData.useFieldValue; // 存储 shopName 到数组的第 1 位置
  3042. } else {
  3043. // console.error("未找到 useFieldName 为 'shopName' 的对象或 useFieldValue 为空");
  3044. }
  3045. } else {
  3046. // console.error("响应中未找到有效的 cardUseDataStyle 字符串");
  3047. }
  3048.  
  3049. // 提取 data 对象中的 goodsUrl
  3050. const goodsUrl = response.data.linkHandCardDetailRespDTO.goodsUrl;
  3051. if (goodsUrl !== undefined) {
  3052. // 将 upLiveId 存储到数组中
  3053. upLiveIdArray[2] = goodsUrl;
  3054. }
  3055.  
  3056. // 更新手卡标题
  3057. modifyTableCardURL_title(upLiveIdArray);
  3058. } else {
  3059. // console.error("响应中未找到有效的 data 字段,或 data 不是对象");
  3060. }
  3061. } catch (error) {
  3062. // console.error("解析响应失败:", error);
  3063. }
  3064. }
  3065.  
  3066. let goodsUrlCheckArray = {}; // shopName, upLiveId, weight, liveLink, sessionGoodsName
  3067.  
  3068. // 提取商品列表中的id
  3069. function extractUpLiveIdsFromGoodsListResponse(responseText) {
  3070. try {
  3071. const response = JSON.parse(responseText);
  3072.  
  3073. // 初始化 upLiveIdArray 数组
  3074. upLiveIdArray = [];
  3075.  
  3076. if (response.data && Array.isArray(response.data)) {
  3077. // 提取 upLiveId 并过滤掉 undefined
  3078. upLiveIdArray = response.data
  3079. .map(item => item.sessionGoods?.upLiveId)
  3080. .filter(id => id !== undefined);
  3081.  
  3082. // 初始化 goodsUrlCheckArray 字典
  3083. goodsUrlCheckArray = {};
  3084.  
  3085. // 填充 goodsUrlCheckArray
  3086. response.data.forEach(item => {
  3087. if (item.sessionGoods && item.sessionGoods.upLiveId !== undefined) {
  3088. const upLiveId = item.sessionGoods.upLiveId;
  3089. // const shopName = item.sessionGoods.shopName || '未知店铺';
  3090. const shopName = item.selectionGoods.shopDetailRespDTO.outShopName || '未知店铺';
  3091. const weight = item.sessionGoods.weight || 0;
  3092. const liveLink = item.sessionGoods.liveLink || '#';
  3093. const sessionGoodsName = item.sessionGoods.sessionGoodsName || '未知商品';
  3094.  
  3095. // 将信息存储到 goodsUrlCheckArray 中
  3096. goodsUrlCheckArray[upLiveId] = {
  3097. shopName: shopName,
  3098. upLiveId: upLiveId,
  3099. weight: weight,
  3100. liveLink: liveLink,
  3101. sessionGoodsName: sessionGoodsName
  3102. };
  3103. }
  3104. });
  3105. } else {
  3106. console.error("响应中未找到有效的 data 字段");
  3107. }
  3108.  
  3109. // console.log("提取的 upLiveId:", upLiveIdArray);
  3110. // console.log("提取的 goodsUrlCheckArray:", goodsUrlCheckArray);
  3111. } catch (error) {
  3112. console.error("解析响应失败:", error);
  3113. }
  3114. }
  3115.  
  3116. // 封装 XMLHttpRequest 的 open 方法重写
  3117. function overrideOpenMethod() {
  3118. const originalOpen = XMLHttpRequest.prototype.open;
  3119.  
  3120. XMLHttpRequest.prototype.open = function (method, url) {
  3121. this._method = method;
  3122. this._url = url;
  3123. originalOpen.apply(this, arguments);
  3124. };
  3125. }
  3126.  
  3127. // 封装 XMLHttpRequest 的 send 方法重写
  3128. function overrideSendMethod() {
  3129. const originalSend = XMLHttpRequest.prototype.send;
  3130.  
  3131. XMLHttpRequest.prototype.send = function (data) {
  3132. const originalOnReadyStateChange = this.onreadystatechange;
  3133.  
  3134. this.onreadystatechange = function () {
  3135. if (this.readyState === 4 && this.status === 200) {
  3136. if (this._method === "POST" && /https:\/\/gw\.linkmcn\.com\/live\/live\/card\/batchDetail.*/.test(this._url)) {
  3137. // console.log("匹配的POST请求URL:", this._url);
  3138.  
  3139. // 调用封装的函数来提取 upLiveId
  3140. extractUpLiveIdsFromResponse(this.responseText);
  3141. // console.log("提取的 upLiveId:", upLiveIdArray);
  3142. }
  3143. if (this._method === "POST" && /https:\/\/gw\.linkmcn\.com\/live\/live\/card\/detail.*/.test(this._url)) {
  3144. // console.log("匹配的POST请求URL:", this._url);
  3145.  
  3146. // 调用封装的函数来提取 upLiveId
  3147. extractUpLiveIdFromTableCardResponse(this.responseText);
  3148. // console.log("提取手卡的 upLiveId:", upLiveIdArray);
  3149. }
  3150. if (this._method === "POST" && /https:\/\/gw\.linkmcn\.com\/live\/live\/card\/nextLiveHandCard.*/.test(this._url)) {
  3151. // console.log("匹配的POST请求URL:", this._url);
  3152.  
  3153. // 调用封装的函数来提取 upLiveId
  3154. extractUpLiveIdsFromNextTableCardResponse(this.responseText);
  3155. // console.log("提取手卡切换的 upLiveId:", upLiveIdArray);
  3156. }
  3157. // 挂链商品id获取
  3158. if (this._method === "POST" && /https:\/\/gw\.linkmcn\.com\/live\/live\/sessionGoods\/liveSessionGoodsList.*/.test(this._url)) {
  3159. extractUpLiveIdsFromGoodsListResponse(this.responseText);
  3160. }
  3161. }
  3162.  
  3163. if (originalOnReadyStateChange) {
  3164. originalOnReadyStateChange.apply(this, arguments);
  3165. }
  3166. };
  3167.  
  3168. originalSend.apply(this, arguments);
  3169. };
  3170. }
  3171.  
  3172. // 判断是否是打印手卡页面,并重写 XMLHttpRequest 的方法
  3173. if (isBatchPrintURL() || isTableCardURL() || isHomeURL()) {
  3174. overrideOpenMethod();
  3175. overrideSendMethod();
  3176. }
  3177.  
  3178. /*
  3179. 系统功能优化
  3180. */
  3181. // 创建一个MutationObserver实例
  3182. // 绑定点击事件的外部函数
  3183. function bindClickEventToNotification(url) {
  3184. // console.log('绑定点击事件, 传入URL:' + url);
  3185. const element = document.getElementById('showNotificationContainer');
  3186. element.style.cursor = 'pointer';
  3187.  
  3188. if (element) {
  3189. // 定义点击事件处理函数
  3190. function handleClick() {
  3191. isHiding = true;
  3192. NotificationContainer.style.animation = 'notificationButtonAnimation 0.5s forwards';
  3193.  
  3194. window.location.href = url;
  3195.  
  3196. setTimeout(function () {
  3197. NotificationContainer.style.display = 'none';
  3198. isHiding = false;
  3199. window.location.reload();
  3200. }, 500);
  3201.  
  3202. // 移除点击事件
  3203. element.removeEventListener('click', handleClick);
  3204. element.style.cursor = 'default';
  3205. }
  3206.  
  3207. // 绑定点击事件
  3208. element.addEventListener('click', handleClick);
  3209. setTimeout(function () {
  3210. // 移除点击事件
  3211. element.removeEventListener('click', handleClick);
  3212. element.style.cursor = 'default';
  3213.  
  3214. hideNotification();
  3215. }, 5000);
  3216. }
  3217. }
  3218.  
  3219. // 设置打印手卡的部分区域
  3220. function setPrintTableCardArea(isActivated) {
  3221. var isShow = isActivated ? 'flex' : 'none';
  3222. var isInsertedShow = isActivated ? 'none' : 'flex'; // 与 isShow 相反的显示结果
  3223. if (sAF_AdvanceBatchPrint_alwaysDisplay.getSwitchState()) isInsertedShow = 'flex'; // 强制显示上播ID核对行
  3224.  
  3225. // 获取所有的table元素
  3226. var tables = document.querySelectorAll('table');
  3227.  
  3228. tables.forEach(function (table, i) {
  3229. // 获取每个table中的tbody
  3230. var tbody = table.querySelector('tbody');
  3231.  
  3232. // 获取tbody中的所有tr行
  3233. var rows = tbody.querySelectorAll('tr');
  3234.  
  3235. // 遍历tr行并隐藏第三行之后的所有行
  3236. rows.forEach(function (row, index) {
  3237. if (index >= 3) { // 从第四行开始(索引为3),设置display为none
  3238. row.style.display = isShow;
  3239. }
  3240. });
  3241.  
  3242. // 检查是否已经插入过具有相同id的tr
  3243. var existingRow = tbody.querySelector('#checkedItemLink_TableCrad');
  3244. if (existingRow) {
  3245. existingRow.style.display = isInsertedShow; // 如果存在则更新显示状态
  3246. } else {
  3247. console.warn("不存在“上播ID核对”行,请检查是否已插入过该行。");
  3248. }
  3249. });
  3250. }
  3251.  
  3252. function insertCheckedRow_forBatchPrint(isActivated) {
  3253. var isInsertedShow = isActivated ? 'flex' : 'none';
  3254.  
  3255. // 获取所有的table元素
  3256. var tables = document.querySelectorAll('table');
  3257.  
  3258. tables.forEach(function (table, i) {
  3259. // 获取每个table中的tbody
  3260. var tbody = table.querySelector('tbody');
  3261.  
  3262. // 获取tbody中的所有tr行
  3263. var rows = tbody.querySelectorAll('tr');
  3264.  
  3265. // 检查是否已经插入过具有相同id的tr
  3266. var existingRow = tbody.querySelector('#checkedItemLink_TableCrad');
  3267. if (!existingRow) {
  3268. // 检查行
  3269. var newRow = createTableCheckedRow(upLiveIdArray[i], 276.8492, 878.8229, goodsUrlArray[i]); // 创建新行
  3270. rows[2].insertAdjacentElement('afterend', newRow); // 插入新行到第三个 <tr> 元素之后
  3271. newRow.style.display = isInsertedShow; // 设置新行的显示状态
  3272. }
  3273.  
  3274. // 插入打印用的edit按钮(默认隐藏)
  3275. var existingEditButton = tbody.querySelector('#editButton_TableCrad_print');
  3276. if (!existingEditButton) {
  3277. // 编辑按钮
  3278. var newEditButton = createEditButton(sessionGoodsId[i], 'print');
  3279. var targetDiv = rows[0].querySelector('#sessionName');
  3280. targetDiv.appendChild(newEditButton);
  3281. }
  3282.  
  3283. // 插入edit按钮
  3284. var existingEditButton = tbody.querySelector('#editButton_TableCrad_PIP');
  3285. if (!existingEditButton) {
  3286. // 编辑按钮
  3287. var newEditButton = createEditButton(sessionGoodsId[i], 'PIP');
  3288. var targetDiv = rows[0].querySelector('#sessionName');
  3289. targetDiv.appendChild(newEditButton);
  3290. }
  3291. });
  3292. }
  3293.  
  3294. // 手卡编辑页面链接
  3295. function getSessionGoodsIdUrl(id) {
  3296. return 'https://m.linkmcn.com/\#/live/plan/tableCard/detail/' + id;
  3297. }
  3298.  
  3299. // 生成重定向链接
  3300. function createRedirectUrl(id) {
  3301. return 'https://item.taobao.com/item.htm?id=682878335608&link_redirectId=' + id;
  3302. }
  3303.  
  3304. function isRedirectToTableCardURL() {
  3305. // 获取当前页面的 URL
  3306. const currentUrl = window.location.href;
  3307.  
  3308. // 检查当前 URL 是否包含指定的前缀
  3309. if (isRedirectUrl()) {
  3310. // 获取 "?" 后面的 id
  3311. const id = currentUrl.split('link_redirectId=')[1];
  3312.  
  3313. // 如果 id 存在,构造新的 URL 并重定向
  3314. if (id) {
  3315. const newUrl = getSessionGoodsIdUrl(id);
  3316. // console.log("Redirecting to: " + newUrl); // 用于调试,查看重定向的 URL
  3317. window.location.href = newUrl; // 重定向到新网址
  3318. } else {
  3319. // console.error('No id found in the URL for redirection');
  3320. }
  3321. }
  3322. }
  3323.  
  3324. function printingTableCard() {
  3325. const loadingText = document.querySelector('.ant-spin-text');
  3326.  
  3327. if (loadingText) {
  3328. // console.log('Loading text 出现在页面中');
  3329. displayEditButtonType(true); // 显示中控按钮
  3330. // displayEditButtonType(false); // 显示制表按钮
  3331. } else {
  3332. // console.log('Loading text 不在页面中');
  3333. displayEditButtonType(false);
  3334. }
  3335. }
  3336.  
  3337. function isPrintingTableCard() {
  3338. const loadingText = document.querySelector('.ant-spin-text');
  3339.  
  3340. if (loadingText) {
  3341. return true;
  3342. } else {
  3343. return false;
  3344. }
  3345. }
  3346.  
  3347. function displayEditButtonType(onPrint) {
  3348. // onPrint 为 true 时显示中控按钮,false 时显示制表按钮
  3349. var print_editButtons = document.querySelectorAll('#editButton_TableCrad_print');
  3350. var PIP_editButtons = document.querySelectorAll('#editButton_TableCrad_PIP');
  3351.  
  3352.  
  3353. if (onPrint && print_editButtons.length > 0 && PIP_editButtons.length > 0) {
  3354. // 显示中控按钮,隐藏制表按钮
  3355. print_editButtons.forEach(function (button) {
  3356. // button.style.display = 'block';
  3357. button.style.opacity = '1';
  3358. });
  3359. PIP_editButtons.forEach(function (button) {
  3360. button.style.display = 'none';
  3361. });
  3362. } else {
  3363. // 显示制表按钮,隐藏中控按钮
  3364. print_editButtons.forEach(function (button) {
  3365. // button.style.display = 'none';
  3366. button.style.opacity = '0';
  3367. });
  3368. PIP_editButtons.forEach(function (button) {
  3369. button.style.display = 'block';
  3370. });
  3371. }
  3372. }
  3373.  
  3374. function createEditButton(sessionGoodsId, idName) {
  3375. const divWrapper = document.createElement('div');
  3376. divWrapper.style.cssText = 'align-items: center; cursor: pointer; margin: 0px 8px;'; // 样式控制';
  3377.  
  3378. // 直接在 SVG 中内嵌 onmouseover 和 onmouseout 事件
  3379. var editSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  3380. editSvg.setAttribute('class', 'icon icon-edit');
  3381. editSvg.setAttribute('transition', 'fill 0.3s ease-in-out');
  3382. editSvg.setAttribute('viewBox', '0 0 1024 1024');
  3383. editSvg.setAttribute('width', '17');
  3384. editSvg.setAttribute('height', '17');
  3385. editSvg.innerHTML = '<path d="M853.333333 796.444444H170.666667c-34.133333 0-56.888889 22.755556-56.888889 56.888889s22.755556 56.888889 56.888889 56.888889h682.666666c34.133333 0 56.888889-22.755556 56.888889-56.888889s-22.755556-56.888889-56.888889-56.888889zM227.555556 739.555556h170.666666c17.066667 0 28.444444-5.688889 39.822222-17.066667l318.577778-318.577778c34.133333-34.133333 51.2-73.955556 51.2-119.466667s-17.066667-85.333333-51.2-119.466666l-11.377778-11.377778c-34.133333-34.133333-73.955556-51.2-119.466666-51.2s-85.333333 17.066667-119.466667 51.2L187.733333 472.177778c-11.377778 11.377778-17.066667 22.755556-17.066666 39.822222v170.666667c0 34.133333 22.755556 56.888889 56.888889 56.888889z m56.888888-204.8l301.511112-301.511112c11.377778-11.377778 22.755556-17.066667 39.822222-17.066666s28.444444 5.688889 39.822222 17.066666l11.377778 11.377778c11.377778 11.377778 17.066667 22.755556 17.066666 39.822222s-5.688889 28.444444-17.066666 39.822223L375.466667 625.777778H284.444444V534.755556z" p-id="1503">';
  3386. editSvg.style.cssText = 'vertical-align: middle;'; // 垂直居中样式
  3387.  
  3388. // 将 SVG 图标直接插入到链接元素中,确保其被 PDF 工具解析
  3389. divWrapper.appendChild(editSvg);
  3390.  
  3391. editSvg.addEventListener('mouseover', function () {
  3392. this.style.fill = "rgb(0, 145, 255)";
  3393. });
  3394.  
  3395. editSvg.addEventListener('mouseout', function () {
  3396. this.style.fill = "rgb(51, 51, 51)";
  3397. });
  3398.  
  3399. // 创建一个 <a> 元素,直接包裹 SVG 图标,避免动态事件处理丢失
  3400. const editButton = document.createElement('a');
  3401. editButton.id = 'editButton_TableCrad_' + idName;
  3402. if (idName === 'print') {
  3403. editButton.style.display = 'block';
  3404. // 调整按键透明度
  3405. editButton.style.opacity = '0';
  3406. editButton.href = createRedirectUrl(sessionGoodsId); // 确保超链接指向正确的地址
  3407. editButton.target = '_blank'; // 打开新标签
  3408. } else {
  3409. editButton.style.display = 'block';
  3410.  
  3411. if (sonMain_pictureInPictureSwitch.getSwitchState()) {
  3412. const pipManager = new PIPManager(getSessionGoodsIdUrl(sessionGoodsId), editButton, true);
  3413. } else {
  3414. editButton.href = getSessionGoodsIdUrl(sessionGoodsId);
  3415. editButton.target = '_blank'; // 打开新标签
  3416. }
  3417. }
  3418.  
  3419. editButton.appendChild(divWrapper);
  3420.  
  3421. return editButton; // 返回带有超链接的按钮
  3422. }
  3423.  
  3424. // 判断是否是剧透时间
  3425. function checkIfTrailer(storedDate) {
  3426. const currentTime = new Date();
  3427. let isTrailer = '';
  3428.  
  3429. // 获取当前年份
  3430. const currentYear = currentTime.getFullYear();
  3431.  
  3432. // 将字符串日期转换为Date对象,加入当前年份
  3433. let adjustedDate = new Date(`${currentYear}-${storedDate}T18:00:00`);
  3434.  
  3435. // 将日期往前调整一天
  3436. adjustedDate.setDate(adjustedDate.getDate() - 1);
  3437.  
  3438. // 计算两个日期之间的差值(以天为单位)
  3439. const timeDifference = Math.abs(currentTime - adjustedDate);
  3440. const dayDifference = timeDifference / (1000 * 60 * 60 * 24);
  3441.  
  3442. // 如果天数差值大于180天,考虑跨年情况,给adjustedDate加上1年
  3443. if (dayDifference > 180) {
  3444. adjustedDate.setFullYear(adjustedDate.getFullYear() + 1);
  3445. }
  3446.  
  3447. const printCard_switch = document.getElementById('button_setPrintTableCardArea_switch');
  3448. const button_setPrintTableCardArea = document.getElementById('button_setPrintTableCardArea');
  3449.  
  3450. if (button_setPrintTableCardArea && printCard_switch.getAttribute('aria-checked') === 'true') {
  3451. isTrailer = "核对";
  3452. } else {
  3453. // 比较当前时间与截止时间
  3454. if (currentTime < adjustedDate) {
  3455. isTrailer = "剧透";
  3456. } else {
  3457. isTrailer = '';
  3458. }
  3459. }
  3460.  
  3461. return isTrailer;
  3462. }
  3463.  
  3464. // 判断手卡打印页面中的手卡店铺名是否都一致
  3465. function onlyOneShopName(shopNameArray) {
  3466. // 过滤多余内容,生成过滤后的数组
  3467. const filteredShopNames = shopNameArray.map(shopName => processText(shopName));
  3468.  
  3469. // 判断所有过滤后的店名是否一致
  3470. const allEqual = filteredShopNames.every(shopName => shopName === filteredShopNames[0]);
  3471.  
  3472. // 如果所有内容一致,返回第一个店名,否则返回 false
  3473. return allEqual ? filteredShopNames[0] : false;
  3474. }
  3475.  
  3476. function hide_link_helpMenu(isActivated = true) {
  3477. var isShow = isActivated ? 'none' : '';
  3478.  
  3479. // link帮助菜单
  3480. const link_helpMenu = document.querySelector('.link-customer-service-only-help');
  3481.  
  3482. if (link_helpMenu) link_helpMenu.style.display = isShow;
  3483. }
  3484.  
  3485. function hide_link_leftSiderMenu(isActivated = true) {
  3486. var isShow = isActivated ? 'none' : '';
  3487.  
  3488. // 获取父容器
  3489. const parentElement = document.querySelector('.ant-layout.ant-layout-has-sider.css-9fw9up');
  3490.  
  3491. if (parentElement) {
  3492. // 获取直接的子元素
  3493. const children = parentElement.children; // 返回一个HTMLCollection,包含所有直接子元素
  3494.  
  3495. if (children.length === 3) {
  3496. // 优化批量打印页面的显示
  3497. children[0].style.display = isShow; // 隐藏右侧的操作栏
  3498. children[1].style.display = isShow; // 隐藏右侧的操作栏
  3499. } else {
  3500. // console.log('页面元素数量不符合预期');
  3501. }
  3502. }
  3503. }
  3504.  
  3505. function hide_link_firstTabContainer(isActivated = true) {
  3506. var isShow = isActivated ? 'none' : '';
  3507.  
  3508. // 获取tab容器
  3509. const tabContainer = document.getElementById('firstTabContainer');
  3510.  
  3511. if (tabContainer) {
  3512. tabContainer.style.display = isShow;
  3513.  
  3514. createHeaderTabs(isActivated); // 重新创建顶部标签
  3515. }
  3516. }
  3517.  
  3518. // 获取 svg 元素的 xlink:href 属性值
  3519. function getUseHref(antSpaceItem) {
  3520. // 查找 .ant-space-item 中的 <use> 元素
  3521. const useElement = antSpaceItem.querySelector('use');
  3522.  
  3523. // 检查是否找到了 <use> 元素
  3524. if (useElement) {
  3525. // 获取 <use> 元素的 xlink:href 属性值
  3526. return useElement.getAttribute('xlink:href') || null;
  3527. } else {
  3528. // 如果没有找到 <use> 元素,返回 null
  3529. return null;
  3530. }
  3531. }
  3532.  
  3533. function hide_link_topBarMenu(isActivated = true) {
  3534. var isShow = isActivated ? 'none' : '';
  3535.  
  3536. // 获取顶部header
  3537. const topHeader = document.querySelector('.ant-space.css-14s8ncx');
  3538. const topHeader_item = topHeader.querySelectorAll('.ant-space-item');
  3539.  
  3540. if (topHeader) {
  3541. // 插入自定义SVG图标
  3542. for (var i = 0; i < topHeader_item.length; i++) {
  3543. if (getUseHref(topHeader_item[i]) === '#link-file-download-line') {
  3544. const svg = document.getElementById('link-live-calendarDiv');
  3545. const svgDiv = create_topBarMenuDivWithSVG('link-live-calendar', '#link-live-calendar', live_calendar);
  3546.  
  3547. if (!svg) {
  3548. topHeader.insertBefore(svgDiv, topHeader_item[i + 1]);
  3549. }
  3550.  
  3551. svg.style.display = isActivated ? '' : 'none'; // 隐藏SVG图标
  3552. }
  3553. }
  3554.  
  3555. for (var i = 0; i < topHeader_item.length; i++) {
  3556. if (getUseHref(topHeader_item[i]) !== '#link-file-download-line' && getUseHref(topHeader_item[i]) !== '#link-live-calendar') {
  3557. topHeader_item[i].style.display = isShow;
  3558. }
  3559. }
  3560. } else {
  3561. // console.log('顶部header元素不存在');
  3562. }
  3563. }
  3564.  
  3565. const live_calendar = `<svg class="icon-component undefined" t="1731819675299" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5486" width="200" height="200">
  3566. <path d="M869.130662 102.016l-45.930667 0 0 102.037333-163.242667 0L659.957328 102.016 364.063995 102.016l0 102.037333L200.821328 204.053333 200.821328 102.016 154.911995 102.016C98.826662 102.016 52.895995 147.925333 52.895995 204.053333l0 714.154667c0 56.128 45.930667 102.037333 102.016 102.037333l714.218667 0c56.085333 0 101.973333-45.909333 101.973333-102.037333L971.103995 204.053333C971.103995 147.925333 925.215995 102.016 869.130662 102.016zM889.525328 928.426667 134.517328 928.426667 134.517328 342.634667l755.008 0L889.525328 928.426667zM318.175995 0 246.751995 0l0 173.44 71.424 0L318.175995 0zM777.290662 0l-71.445333 0 0 173.44 71.445333 0L777.290662 0z" p-id="5487"></path>
  3567. <path d="M512.010662 559.658667l256.448 0 0 257.536-256.448 0 0-257.536Z" p-id="5488"></path>
  3568. </svg>`;
  3569.  
  3570. function create_topBarMenuDivWithSVG(id, useHref, svgContent) {
  3571. // 内部函数:生成SVG元素
  3572. function getSVG(useHref, svgContent) {
  3573. const parser = new DOMParser();
  3574. const svgDoc = parser.parseFromString(svgContent, 'image/svg+xml');
  3575. const svg = svgDoc.querySelector('svg');
  3576.  
  3577. const use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
  3578. use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', useHref);
  3579.  
  3580. svg.appendChild(use);
  3581. return svg;
  3582. }
  3583.  
  3584. const div = document.createElement('div');
  3585. div.className = 'ant-space-item';
  3586. div.style.display = 'none';
  3587. div.id = id + 'Div';
  3588.  
  3589. const innerDiv = document.createElement('div');
  3590. innerDiv.id = id;
  3591. div.appendChild(innerDiv);
  3592.  
  3593. const button = document.createElement('button');
  3594. button.type = 'button';
  3595. button.className = 'ant-btn css-14s8ncx ant-btn-circle ant-btn-text';
  3596. innerDiv.appendChild(button);
  3597.  
  3598. const span = document.createElement('span');
  3599. span.className = 'ant-badge css-14s8ncx';
  3600. button.appendChild(span);
  3601.  
  3602. const svg = getSVG(useHref, svgContent);
  3603. span.appendChild(svg);
  3604.  
  3605. // 添加点击事件处理器
  3606. button.addEventListener('click', function () {
  3607. if (button.classList.contains('ant-btn-text')) {
  3608. button.classList.remove('ant-btn-text');
  3609. button.classList.add('ant-btn-primary');
  3610. svg.style.fill = '#fff'; // 更改SVG填充颜色
  3611. svg.style.color = '#fff'; // 更改SVG文本颜色
  3612.  
  3613. pureMode(true);
  3614. showNotification('直播日期:显示');
  3615. } else {
  3616. button.classList.add('ant-btn-text');
  3617. button.classList.remove('ant-btn-primary');
  3618. svg.style.fill = '#000'; // 恢复SVG填充颜色
  3619. svg.style.color = '#000'; // 恢复SVG文本颜色
  3620.  
  3621. pureMode(true);
  3622. showNotification('直播日期:关闭');
  3623. }
  3624. });
  3625.  
  3626. return div;
  3627. }
  3628.  
  3629. function getState_topBarMenuDivWithSVG() {
  3630. const button = document.getElementById('link-live-calendar').querySelector('button');
  3631.  
  3632. if (!button) {
  3633. throw new Error('Invalid div element or missing button reference');
  3634. }
  3635. return button.classList.contains('ant-btn-primary');
  3636. }
  3637.  
  3638. function click_firstTabContainer(tabNumber) {
  3639. // firstTabContainer
  3640. const tabContainer = document.getElementById('firstTabContainer');
  3641.  
  3642. if (tabContainer) {
  3643. // 获取所有标签
  3644. const tabs = tabContainer.querySelectorAll('.ant-tabs-tab');
  3645.  
  3646. // 触发点击事件
  3647. if (tabs[tabNumber - 1]) {
  3648. tabs[tabNumber - 1].click();
  3649. console.log(`Clicked tab with number ${tabNumber}`); // 调试信息
  3650. } else {
  3651. console.error(`Tab with number ${tabNumber} not found.`);
  3652. }
  3653. }
  3654. }
  3655.  
  3656. function getText_firstTabContainer(tabNumber) {
  3657. // firstTabContainer
  3658. const tabContainer = document.getElementById('firstTabContainer');
  3659.  
  3660. if (tabContainer) {
  3661. // 获取所有标签
  3662. const tabs = tabContainer.querySelectorAll('.ant-tabs-tab');
  3663.  
  3664. // return 标签文本
  3665. if (tabs[tabNumber - 1]) {
  3666. return tabs[tabNumber - 1].textContent;
  3667. } else {
  3668. return `标签${tabNumber}`;
  3669. }
  3670. }
  3671. }
  3672.  
  3673. function updateText_firstTabContainer() {
  3674. for (let i = 1; i <= getTabsCount_firstTabContainer(); i++) {
  3675. var link = document.getElementById(`pureMode_headerTabs_tab${i}`);
  3676. if (link) {
  3677. link.textContent = `${getText_firstTabContainer(i)}`;
  3678. } else {
  3679. console.error(`Link with ID ${link} not found.`);
  3680. }
  3681. }
  3682. }
  3683.  
  3684. function getTabsCount_firstTabContainer() {
  3685. // firstTabContainer
  3686. const tabContainer = document.getElementById('firstTabContainer');
  3687.  
  3688. if (tabContainer) {
  3689. // 获取所有标签
  3690. const tabs = tabContainer.querySelectorAll('.ant-tabs-tab');
  3691. // return 标签数量
  3692. return tabs.length;
  3693. } else {
  3694. return 0;
  3695. }
  3696. }
  3697.  
  3698. function createHeaderTabs(isActivated = false) {
  3699. const originalHeader = document.getElementById('pureMode_headerTabs');
  3700.  
  3701. if (originalHeader) {
  3702. if (isActivated) {
  3703. // 如果已存在且 isActivated 为 true,不需要做任何操作
  3704. updateText_firstTabContainer(); // 更新文本内容
  3705. } else {
  3706. // 如果已存在且 isActivated 为 false,移除原有的 <span> 容器
  3707. originalHeader.remove();
  3708. }
  3709. return;
  3710. }
  3711.  
  3712. if (!isActivated) {
  3713. // 如果 isActivated 为 false 且不存在 <span> 容器,直接返回
  3714. return;
  3715. }
  3716. // 创建一个新的 <span> 容器
  3717. const spanContainer = document.createElement('span');
  3718. // 设置id属性
  3719. spanContainer.id = 'pureMode_headerTabs';
  3720.  
  3721. // 设置样式使 <a> 标签水平排列
  3722. spanContainer.style.display = 'flex';
  3723. spanContainer.style.gap = '16px'; // 添加间距
  3724. spanContainer.style.margin = '0px'; // 添加外边距
  3725.  
  3726. // 循环创建多个 <a> 标签
  3727. for (let i = 1; i <= getTabsCount_firstTabContainer(); i++) {
  3728. const link = document.createElement('a');
  3729. // link.href = '#'; // 可以设置为实际链接
  3730. link.textContent = `${getText_firstTabContainer(i)}`; // 设置默认文本内容
  3731. link.id = `pureMode_headerTabs_tab${i}`; // 设置ID
  3732.  
  3733. // 设置初始样式
  3734. link.style.color = 'rgb(51, 51, 51)';
  3735. link.style.fontSize = '14px';
  3736. link.style.fontWeight = '500';
  3737. link.style.transition = 'color 0.3s ease'; // 添加过渡动画
  3738. link.style.whiteSpace = 'nowrap'; // 阻止内容换行
  3739.  
  3740. // 设置悬停样式
  3741. link.addEventListener('mouseover', () => {
  3742. link.style.color = 'rgb(255, 100, 3)';
  3743. });
  3744.  
  3745. link.addEventListener('mouseout', () => {
  3746. link.style.color = 'rgb(51, 51, 51)';
  3747. });
  3748.  
  3749. // 绑定点击事件
  3750. link.addEventListener('click', (event) => {
  3751. console.log(`Link with ID ${link.id} clicked`); // 调试信息
  3752. event.preventDefault(); // 阻止默认的链接跳转行为
  3753. event.stopPropagation(); // 阻止事件冒泡
  3754. event.stopImmediatePropagation(); // 阻止其他事件处理程序
  3755. click_firstTabContainer(i); // 调用点击函数
  3756. return false; // 防止其他默认行为
  3757. });
  3758.  
  3759. // 将 <a> 标签添加到 <span> 容器中
  3760. spanContainer.appendChild(link);
  3761. }
  3762.  
  3763. // 查找目标容器
  3764. const targetContainer = document.querySelector('.ant-pro-global-header');
  3765.  
  3766. // 检查目标容器是否存在
  3767. if (targetContainer) {
  3768. // 获取目标容器的第二个子元素作为参考节点
  3769. const referenceNode = targetContainer.children[1];
  3770.  
  3771. // 检查是否有足够的子元素以确定第二个位置
  3772. if (referenceNode) {
  3773. // 将 span 容器插入到目标容器的第二个位置
  3774. targetContainer.insertBefore(spanContainer, referenceNode);
  3775. } else {
  3776. // 如果没有第二个子元素,则直接添加到末尾
  3777. targetContainer.appendChild(spanContainer);
  3778. }
  3779. } else {
  3780. console.error('Target container not found.');
  3781. }
  3782. }
  3783.  
  3784. function smallLogo(restore = false) {
  3785. const container = document.querySelector('.ant-pro-global-header-logo-mix');
  3786.  
  3787. if (container) {
  3788. const logo = container.querySelector('a').querySelector('img');
  3789.  
  3790. if (logo) {
  3791. if (restore) {
  3792. logo.src = 'https://assets.linkmcn.cn/v2/img/logo-org.png';
  3793. } else {
  3794. logo.src = 'https://assets.linkmcn.cn/public/logo.ico';
  3795. }
  3796. }
  3797. } else {
  3798. console.error('Container not found.');
  3799. }
  3800. }
  3801.  
  3802. function adapter_moreSearchMenu() {
  3803. const moreSearchButton = document.querySelector('.ant-pro-query-filter-collapse-button');
  3804.  
  3805. if (moreSearchButton) {
  3806. // button文本
  3807. buttonText = moreSearchButton.textContent;
  3808.  
  3809. if (buttonText === '收起') {
  3810. return 132;
  3811. } else {
  3812. return 0;
  3813. }
  3814. }
  3815. }
  3816.  
  3817. // 纯净模式函数
  3818. function pureMode(isActivated) {
  3819. var isShow = isActivated ? 'none' : '';
  3820. var targetHeight = document.documentElement.scrollHeight - 188;
  3821.  
  3822. targetHeight -= adapter_moreSearchMenu(); // 适配更多搜索菜单展开或收起
  3823.  
  3824. hide_link_helpMenu(isActivated); // 隐藏帮助菜单
  3825.  
  3826. hide_link_leftSiderMenu(isActivated); // 隐藏左侧菜单
  3827.  
  3828. hide_link_topBarMenu(isActivated); // 隐藏顶部菜单
  3829.  
  3830. smallLogo(!isActivated); // 切换小图标
  3831.  
  3832. hide_link_firstTabContainer(isActivated); // 隐藏第一个tab容器
  3833.  
  3834. // tab容器消失引起的问题修复
  3835. adapter_SomePage(isActivated); // 适配紧急加品页面
  3836.  
  3837.  
  3838.  
  3839. // 直播场次日期切换菜单
  3840. const liveDay_selectMenu = document.querySelector('.flex.justify-between.pb-12');
  3841. // 直播场次总览
  3842. const liveOverview_table = document.querySelector('.link-session-board-pai___eddtw').parentElement;
  3843.  
  3844. const otherElements = document.querySelector('.ag-body-horizontal-scroll-viewport');
  3845.  
  3846. // 底部多余栏目
  3847. const bottomExtra_div = document.querySelector('.ag-grid-sticky-scroll');
  3848. bottomExtra_div.style.display = isShow;
  3849.  
  3850. // 设置元素的显示状态
  3851. liveDay_selectMenu.style.display = getDisplay_liveDay_selectMenu();
  3852. if (liveDay_selectMenu.style.display !== 'none' && isActivated) {
  3853. liveDay_selectMenu.style.paddingTop = '12px';
  3854. liveDay_selectMenu.style.paddingBottom = '0px';
  3855. targetHeight -= 60;
  3856. } else {
  3857. liveDay_selectMenu.style.paddingTop = '0px';
  3858. liveDay_selectMenu.style.paddingBottom = '12px';
  3859. }
  3860.  
  3861. liveOverview_table.style.display = isShow;
  3862. // otherElements.style.display = isShow;
  3863.  
  3864. // margin优化
  3865. const margin_value = isActivated ? '0px' : '12px';
  3866. const mainElement = document.querySelector('main');
  3867.  
  3868. mainElement.style.marginTop = margin_value;
  3869. // mainElement.style.marginBottom = margin_value;
  3870.  
  3871. // 商品操作框
  3872. // 外部框
  3873. const goodsOperation_div = document.querySelectorAll('.ant-spin-container');
  3874. // 内部表格
  3875. const goodsOperation_table = document.getElementById('ag-grid-react-container');
  3876.  
  3877. // 设置商品操作框的高度
  3878. // goodsOperation_div[0].style.height = isShow === 'none' ? targetHeight + 'px' : '1085px';
  3879. goodsOperation_div[1].style.height = isShow === 'none' ? targetHeight + 'px' : '548px';
  3880. goodsOperation_table.style.height = isShow === 'none' ? targetHeight + 'px' : '548px';
  3881.  
  3882. // 获取顶部菜单按钮显示状态
  3883. function getDisplay_liveDay_selectMenu(noneDispaly = isActivated) {
  3884. if (getState_topBarMenuDivWithSVG()) {
  3885. return '';
  3886. } else {
  3887. if (noneDispaly) {
  3888. return 'none';
  3889. } else {
  3890. return '';
  3891. }
  3892. }
  3893. }
  3894.  
  3895. function adapter_SomePage(isActivated) {
  3896. // 适配紧急加品页面
  3897. if (checkActiveTab_JiaPin()) {
  3898. const buttonBar = document.querySelector('.sticky'); // 顶部按钮栏
  3899.  
  3900. const tableHeader = document.querySelector('.ant-table-sticky-holder'); // 表格表头
  3901.  
  3902. if (isActivated) {
  3903. buttonBar.style.top = '0px';
  3904. tableHeader.style.top = '0px';
  3905. } else {
  3906. buttonBar.style.top = '60px';
  3907. tableHeader.style.top = '116px';
  3908. }
  3909. }
  3910.  
  3911. // 适配待确认页面
  3912. if (checkActiveTab_WaitComfirm()) {
  3913. const tableHeader = document.querySelector('.ant-table-sticky-holder'); // 表格表头
  3914.  
  3915. if (isActivated) {
  3916. tableHeader.style.top = '0px';
  3917. } else {
  3918. tableHeader.style.top = '60px';
  3919. }
  3920. }
  3921. }
  3922. }
  3923.  
  3924. // pureMode 场次信息辅助显示
  3925. function pureMode_infoDisplay(isActivated) {
  3926. // 场次信息数据
  3927. const storedDate = GM_getValue('titlePrint_extractedDate', '');
  3928. const storedSession = GM_getValue('titlePrint_extractedSession', '');
  3929.  
  3930. if (isActivated && !getDropdownContentDisplayState()) {
  3931. dropdownButton.textContent = storedDate + ' ' + storedSession;
  3932. } else {
  3933. dropdownButton.textContent = dropdownButtonName;
  3934. }
  3935. }
  3936.  
  3937. // 手卡页面优化功能区
  3938. function tableCard_optimize(isActivated) {
  3939. // 优化手卡页面的显示
  3940. if (isActivated) {
  3941. // 隐藏顶部header
  3942. document.querySelectorAll('.ant-pro-layout-container.css-zk6fk3 header').forEach(header => {
  3943. header.style.display = 'none';
  3944. });
  3945.  
  3946. // 隐藏帮助菜单
  3947. hide_link_helpMenu();
  3948.  
  3949. // 设置操作栏
  3950. const titleContainer = document.querySelector('.TableCard .tct-title-container');
  3951. if (titleContainer) titleContainer.style.top = '0'; // 设置top值
  3952.  
  3953. // 隐藏禁用词栏
  3954. const disabledWords = document.querySelector('.forbidden-word-container');
  3955. if (disabledWords) disabledWords.style.display = 'none';
  3956. }
  3957. }
  3958.  
  3959. function find_pip_restore_button() {
  3960. const button = document.getElementById('pip_restore_button');
  3961.  
  3962. if (button) {
  3963. return button;
  3964. } else {
  3965. // console.warn('PIP restore button not found!');
  3966. return null;
  3967. }
  3968. }
  3969.  
  3970. // 手卡页面&批量打印页面-适配小窗功能区
  3971. function adapterPIP_forTableCardAndBatchPrint(isActivated, reset = false) {
  3972. if (find_pip_restore_button()) reset = true; // 如果存在己收起按钮,恢复边距
  3973. // 适配小窗功能区
  3974. const windowWidth = window.innerWidth; // 获取当前窗口宽度
  3975. const PIP_width = getWidth_PIP() * getDisplayScale(); // 获取小窗宽度
  3976.  
  3977. function getDisplayScale() {
  3978. const displayWidth = window.innerWidth;
  3979. let displayScale = displayWidth / 1530;
  3980.  
  3981. return displayScale;
  3982. }
  3983.  
  3984. if (isTableCardURL()) {
  3985. // 获取手卡页面宽度
  3986. const tableCard_width = getWidth_TableCard();
  3987. // 剩余宽度
  3988. const remaining_width = windowWidth - tableCard_width - 10;
  3989. // 默认左右margin值
  3990. const default_margin = remaining_width / 2;
  3991.  
  3992. if (isActivated && PIP_width != 0) {
  3993. if (reset) {
  3994. setMargin_TableCard(default_margin); // 重置边距
  3995. } else if (remaining_width < PIP_width) {
  3996. setMargin_TableCard(remaining_width, 10); // 设置边距
  3997. } else {
  3998. setMargin_TableCard(PIP_width, remaining_width - PIP_width); // 设置边距
  3999. }
  4000. } else {
  4001. setMargin_TableCard(default_margin); // 重置边距
  4002. }
  4003.  
  4004. setMargins_ant_modal_contents(isActivated, PIP_width, reset);
  4005. }
  4006.  
  4007. if (isBatchPrintURL()) {
  4008. // 获取批量打印页面宽度
  4009. const batchPrint_width = getWidth_BatchPrint();
  4010. // 剩余宽度
  4011. const remaining_width = windowWidth - batchPrint_width - 40;
  4012. // 默认左右margin值
  4013. const default_margin = remaining_width / 2;
  4014.  
  4015. if (isActivated && PIP_width != 0) {
  4016. if (reset) {
  4017. setMargin_BatchPrint(default_margin); // 重置边距
  4018. } else if (remaining_width < PIP_width) {
  4019. setMargin_BatchPrint(remaining_width, 10); // 设置边距
  4020. } else {
  4021. setMargin_BatchPrint(PIP_width, remaining_width - PIP_width); // 设置边距
  4022. }
  4023. } else {
  4024. setMargin_BatchPrint(default_margin); // 重置边距
  4025. }
  4026. }
  4027. }
  4028.  
  4029. // 获取小窗宽度
  4030. function getWidth_PIP(PIP_margin = 10) {
  4031. // 确保PIP_margin是数字
  4032. if (typeof PIP_margin !== 'number') {
  4033. PIP_margin = 10;
  4034. }
  4035.  
  4036. // 获取小窗元素
  4037. const PIP_element = document.getElementById('mini_PIP');
  4038.  
  4039. // 检查元素是否存在
  4040. if (!PIP_element) {
  4041. // console.warn('PIP element not found!');
  4042. return 0; // 如果元素不存在,返回默认宽度
  4043. }
  4044.  
  4045. if (PIP_element.style.display === 'none') {
  4046. return 0;
  4047. }
  4048.  
  4049. // 获取PIP宽度并返回
  4050. const PIP_width = PIP_element.clientWidth;
  4051. return PIP_width + PIP_margin * 2;
  4052. }
  4053.  
  4054. // 获取小窗位置
  4055. function getPosition_PIP() {
  4056. // true = 左; false = 右
  4057. // 获取小窗元素
  4058. const PIP_element = document.getElementById('mini_PIP');
  4059.  
  4060. // 检查元素是否存在
  4061. if (!PIP_element) {
  4062. // console.warn('PIP element not found!');
  4063. return false; // 如果元素不存在,返回默认位置
  4064. }
  4065.  
  4066. const pipRect = PIP_element.getBoundingClientRect();
  4067. const centerX = pipRect.left + pipRect.width / 2;
  4068.  
  4069. // 判断PIP位置,决定收起方向
  4070. const isLeftSide = centerX < window.innerWidth / 2;
  4071.  
  4072. return isLeftSide;
  4073. }
  4074.  
  4075. // 获取手卡页面手卡宽度
  4076. function getWidth_TableCard() {
  4077. // 获取手卡元素
  4078. const tableCard_element = document.querySelector('.A4-contianer');
  4079.  
  4080. // 检查元素是否存在
  4081. if (!tableCard_element) {
  4082. console.warn('Table card element not found!');
  4083. return 1194; // 如果元素不存在,返回默认宽度
  4084. }
  4085.  
  4086. tableCard_element.style.transition = 'margin 0.3s ease';
  4087.  
  4088. // 获取手卡宽度并返回
  4089. const tableCard_width = tableCard_element.clientWidth;
  4090. return tableCard_width;
  4091. }
  4092.  
  4093. // 设置手卡页面边距
  4094. function setMargin_TableCard(margin_valueMain, margin_valueRemain = margin_valueMain, isLeftSide = getPosition_PIP()) {
  4095. const target_element = document.querySelector('.A4-contianer');
  4096.  
  4097. if (isLeftSide) {
  4098. // 靠近左侧
  4099. target_element.style.marginLeft = margin_valueMain + 'px';
  4100. target_element.style.marginRight = margin_valueRemain + 'px';
  4101. } else {
  4102. // 靠近右侧
  4103. target_element.style.marginRight = margin_valueMain + 'px';
  4104. target_element.style.marginLeft = margin_valueRemain + 'px';
  4105. }
  4106. if (margin_valueMain === margin_valueRemain) {
  4107. target_element.style.marginLeft = margin_valueMain + 'px';
  4108. target_element.style.marginRight = margin_valueMain + 'px';
  4109. }
  4110. }
  4111.  
  4112. // 获取相关手卡宽度
  4113. function getWidths_ant_modal_contents() {
  4114. // 获取所有相关手卡弹窗元素
  4115. const ant_modal_contents = document.querySelectorAll('.ant-modal.css-9fw9up');
  4116.  
  4117. if (!ant_modal_contents || ant_modal_contents.length === 0) {
  4118. // console.warn('No ant_modal_content elements found!');
  4119. return []; // 如果没有找到元素,返回空数组
  4120. }
  4121.  
  4122. // 遍历每个元素,获取宽度,并添加到结果数组中
  4123. const widths = [];
  4124. ant_modal_contents.forEach(modal => {
  4125. modal.style.transition = 'margin 0.3s ease'; // 添加过渡效果
  4126. const width = modal.clientWidth;
  4127. widths.push(width);
  4128. });
  4129.  
  4130. return widths;
  4131. }
  4132.  
  4133. // 设置相关手卡弹窗边距
  4134. function setMargin(target_element, margin_valueMain, margin_valueRemain = margin_valueMain, isLeftSide = getPosition_PIP()) {
  4135. if (isLeftSide) {
  4136. // 靠近左侧
  4137. target_element.style.marginLeft = margin_valueMain + 'px';
  4138. target_element.style.marginRight = margin_valueRemain + 'px';
  4139. } else {
  4140. // 靠近右侧
  4141. target_element.style.marginRight = margin_valueMain + 'px';
  4142. target_element.style.marginLeft = margin_valueRemain + 'px';
  4143. }
  4144. if (margin_valueMain === margin_valueRemain) {
  4145. target_element.style.marginLeft = margin_valueMain + 'px';
  4146. target_element.style.marginRight = margin_valueMain + 'px';
  4147. }
  4148. }
  4149.  
  4150. function setMargins_ant_modal_contents(isActivated, PIP_width, reset) {
  4151. const target_elements = document.querySelectorAll('.ant-modal.css-9fw9up');
  4152.  
  4153. if (!target_elements || target_elements.length === 0) {
  4154. // console.warn('No ant_modal_content elements found!');
  4155. return; // 如果没有找到元素,直接返回
  4156. }
  4157.  
  4158. target_elements.forEach(target_element => {
  4159. const ant_modal_content_width = target_element.clientWidth;
  4160. const remaining_width = window.innerWidth - ant_modal_content_width - 10;
  4161. const default_margin = remaining_width / 2;
  4162.  
  4163. if (isActivated && PIP_width !== 0) {
  4164. if (reset) {
  4165. setMargin(target_element, default_margin); // 重置边距
  4166. } else if (remaining_width < PIP_width) {
  4167. setMargin(target_element, remaining_width, 10); // 设置边距
  4168. } else {
  4169. setMargin(target_element, PIP_width, remaining_width - PIP_width); // 设置边距
  4170. }
  4171. } else {
  4172. setMargin(target_element, default_margin); // 重置边距
  4173. }
  4174. });
  4175. }
  4176.  
  4177. // 设置批量打印页面边距
  4178. function setMargin_BatchPrint(margin_valueMain, margin_valueRemain = margin_valueMain, isLeftSide = getPosition_PIP()) {
  4179. const target_element = document.querySelector('.w-1160px.m-auto.flex-1.overflow-y-auto');
  4180.  
  4181. if (isLeftSide) {
  4182. // 靠近左侧
  4183. target_element.style.marginLeft = margin_valueMain + 'px';
  4184. target_element.style.marginRight = margin_valueRemain + 'px';
  4185. } else {
  4186. // 靠近右侧
  4187. target_element.style.marginRight = margin_valueMain + 'px';
  4188. target_element.style.marginLeft = margin_valueRemain + 'px';
  4189. }
  4190. if (margin_valueMain === margin_valueRemain) {
  4191. target_element.style.marginLeft = 'auto';
  4192. target_element.style.marginRight = 'auto';
  4193. }
  4194. }
  4195.  
  4196. // 获取批量打印页面手卡宽度
  4197. function getWidth_BatchPrint() {
  4198. // 获取手卡元素
  4199. const tableCard_element = document.querySelector('.w-1160px.m-auto.flex-1.overflow-y-auto');
  4200.  
  4201. // 检查元素是否存在
  4202. if (!tableCard_element) {
  4203. console.warn('Batch print element not found!');
  4204. return 1160; // 如果元素不存在,返回默认宽度
  4205. }
  4206.  
  4207. tableCard_element.style.transition = 'margin 0.3s ease';
  4208.  
  4209. // 获取手卡宽度并返回
  4210. const tableCard_width = tableCard_element.clientWidth;
  4211. return tableCard_width;
  4212. }
  4213.  
  4214. // 批量打印页面优化功能区
  4215. function batchPrint_optimize(isActivated) {
  4216. if (isActivated) {
  4217. // 隐藏帮助菜单
  4218. hide_link_helpMenu();
  4219.  
  4220. // 隐藏左侧菜单
  4221. hide_link_leftSiderMenu();
  4222. }
  4223. }
  4224.  
  4225. // 计数关闭浏览器更新弹窗次数
  4226. function incrementCloseCount() {
  4227. // 从本地存储中获取当前关闭计数
  4228. let closeCount = localStorage.getItem('browserUpdateCloseCount');
  4229.  
  4230. // 如果没有存储记录,则初始化为0
  4231. if (closeCount === null) {
  4232. closeCount = 0;
  4233. } else {
  4234. closeCount = parseInt(closeCount, 10); // 确保获取到的是数字
  4235. }
  4236.  
  4237. // 增加计数
  4238. closeCount += 1;
  4239.  
  4240. // 将更新后的计数存回本地存储
  4241. localStorage.setItem('browserUpdateCloseCount', closeCount);
  4242.  
  4243. // console.log(`关闭浏览器更新弹窗次数: ${closeCount}`);
  4244. }
  4245.  
  4246. // 读取关闭浏览器更新弹窗次数
  4247. function getCloseCount() {
  4248. let closeCount = localStorage.getItem('browserUpdateCloseCount');
  4249.  
  4250. // 如果本地存储中没有记录,返回0
  4251. if (closeCount === null) {
  4252. closeCount = 0;
  4253. } else {
  4254. closeCount = parseInt(closeCount, 10); // 确保获取到的是数字
  4255. }
  4256.  
  4257. return closeCount;
  4258. }
  4259.  
  4260. // 判断商品信息页面是否在加载中
  4261. function isItemPageLoading() {
  4262. console.log('isItemPageLoading is Rushing');
  4263. return document.querySelectorAll('.ant-spin-dot-item').length > 0;
  4264. }
  4265.  
  4266. const sys_auxiliaryFunctions = new MutationObserver((mutationsList) => {
  4267. let urlChanged = false;
  4268.  
  4269. // 场次信息数据
  4270. const storedDate = GM_getValue('titlePrint_extractedDate', '');
  4271. const storedSession = GM_getValue('titlePrint_extractedSession', '');
  4272.  
  4273. // 重定向到手卡编辑页面
  4274. isRedirectToTableCardURL();
  4275.  
  4276. for (let mutation of mutationsList) {
  4277. if (mutation.type === 'childList') {
  4278. mutation.addedNodes.forEach((node) => {
  4279. // 系统通知位置优化
  4280. if (node.nodeType === 1 && node.classList.contains('ant-message') && node.classList.contains('ant-message-top') && node.classList.contains('css-190m0jy')) {
  4281. // 修改top值为64px
  4282. node.style.top = '64px';
  4283. }
  4284.  
  4285. if (sAF_closeBrowserUpdateDiv.getSwitchState()) {
  4286. // 关闭浏览器更新弹窗
  4287. if (node.nodeType === 1 && node.id === 'link-browser-update-global') {
  4288. const closeButton = node.querySelector('.link-browser-update-global-close');
  4289. if (closeButton) {
  4290. closeButton.click();
  4291. incrementCloseCount(); // 增加关闭计数
  4292. showNotification('已关闭浏览器更新弹窗', 3000);
  4293. // console.log('关闭了浏览器更新弹窗');
  4294. } else {
  4295. console.log('未找到关闭按钮');
  4296. }
  4297. }
  4298. }
  4299. });
  4300.  
  4301. // 检查URL是否变化
  4302. if (isHomeURL()) {
  4303. pureMode(pureSwitch.getSwitchState());
  4304. pureMode_infoDisplay(pureSwitch.getSwitchState());
  4305.  
  4306. addDefaultButtonForHomePage(); // 添加智能复制按钮
  4307. // if(checkActiveTab_GuaLian()) isItemPageLoading();
  4308. addButtonForHomePage_processAllLinks();
  4309.  
  4310. const currentURL = window.location.href; // 获取当前网址
  4311. titlePrint_extractData(); // 提取日期和场次信息
  4312.  
  4313. // 只有在 URL 发生变化时,才会执行以下代码
  4314. if (currentURL !== lastCurrentURL) {
  4315. // 在保存新的 currentURL 之前,更新 lastCurrentURL
  4316. const previousURL = lastCurrentURL;
  4317.  
  4318. // 将当前网址保存到 localStorage
  4319. localStorage.setItem('lastCurrentURL', currentURL);
  4320.  
  4321. if (sAF_backToLastCurrentURL.getSwitchState()) {
  4322. // 显示通知并绑定点击事件,传入 previousURL 而不是 currentURL
  4323. showNotification('上次浏览的页面:' + storedDate + ' ' + storedSession, 5000);
  4324.  
  4325. setTimeout(() => {
  4326. itemSort_countInputFun.countSort_reShowNotification();// 计数通知显示恢复
  4327. }, 4000);
  4328.  
  4329. setTimeout(() => {
  4330. bindClickEventToNotification(previousURL); // 绑定点击事件,传入 previousURL
  4331. }, 50);
  4332. }
  4333.  
  4334. // 更新 lastCurrentURL 为新的网址
  4335. lastCurrentURL = currentURL;
  4336. urlChanged = true;
  4337. }
  4338.  
  4339. GM_setValue('sAF_FristPhotoCopyForTmallAndTaobao', sAF_FristPhotoCopyForTmallAndTaobao.getSwitchState());
  4340. }
  4341.  
  4342. if (isTableCardURL()) {
  4343. // 调用函数添加SVG图标
  4344. addSVGToSpecificTDs();
  4345. // 一键着色按键显示
  4346. displayDrawColorButton();
  4347. displayDrawColorButton('.subsidyText')
  4348. if (tableCardPng_checkItemIdRow.getSwitchState()) {
  4349. // 插入检查行
  4350. insertCheckedRow(url_getSessionGoodsId(), upLiveIdArray[2]);
  4351. }
  4352.  
  4353. // 手卡页面优化
  4354. tableCard_optimize(sAF_AdvanceTableCardPage.getSwitchState());
  4355. adapterPIP_forTableCardAndBatchPrint(sonMain_PIP_autoAdjust.getSwitchState());
  4356. }
  4357.  
  4358. if (isTmallItemURL()) {
  4359. isRedirectToTableCardURL();
  4360.  
  4361. const isCreateButton = GM_getValue('sAF_FristPhotoCopyForTmallAndTaobao', false);
  4362.  
  4363. if (isCreateButton) {
  4364. // 创建按钮
  4365. createGetTmallPngButton();
  4366. }
  4367. }
  4368.  
  4369. function cardShopName_check() {
  4370. const isOnlyOneShop = onlyOneShopName(shopNameArray);
  4371. const printCard_switch = document.getElementById('button_setPrintTableCardArea_switch');
  4372. const button_setPrintTableCardArea = document.getElementById('button_setPrintTableCardArea');
  4373.  
  4374. if (button_setPrintTableCardArea && printCard_switch.getAttribute('aria-checked') === 'true' &&
  4375. isOnlyOneShop !== false && isOnlyOneShop) {
  4376. // 只有一个店铺名
  4377. return isOnlyOneShop;
  4378. } else {
  4379. return storedSession;
  4380. }
  4381. }
  4382.  
  4383. if (sAF_AdvanceBatchPrint.getSwitchState() && isBatchPrintURL() && storedDate && storedSession) {
  4384. document.title = `${storedDate} ${cardShopName_check()}手卡${checkIfTrailer(storedDate)}`;
  4385.  
  4386. printingTableCard(); // 打印中
  4387.  
  4388. batchPrint_optimize(true); // 优化页面
  4389.  
  4390. if (!isPrintingTableCard()) {
  4391. // 插入上播ID核对行
  4392. insertCheckedRow_forBatchPrint(sAF_AdvanceBatchPrint_alwaysDisplay.getSwitchState());
  4393. }
  4394. setTimeout(function () {
  4395. // 查找目标元素,即将新div插入到这个元素中
  4396. var targetElement = document.querySelector('.flex.justify-end');
  4397.  
  4398. // 检查是否已经存在id为button_setPrintTableCardArea的div,避免重复添加
  4399. if (!document.getElementById('button_setPrintTableCardArea')) {
  4400. // 创建一个新的div元素
  4401. var newDiv = document.createElement('div');
  4402. newDiv.id = 'button_setPrintTableCardArea';
  4403. newDiv.style.display = 'flex';
  4404. newDiv.style.justifyContent = 'space-between';
  4405. newDiv.style.alignItems = 'center';
  4406. newDiv.style.marginRight = '10px';
  4407.  
  4408. // 创建左侧的文本节点
  4409. var textNode = document.createElement('span');
  4410. textNode.textContent = '不打印红字和卖点区域';
  4411. textNode.style.fontSize = '14px';
  4412. textNode.style.marginRight = '10px';
  4413.  
  4414. // 创建右侧的开关按钮
  4415. var switchButton = document.createElement('button');
  4416. switchButton.type = 'button';
  4417. switchButton.id = 'button_setPrintTableCardArea_switch';
  4418. switchButton.setAttribute('role', 'switch');
  4419. switchButton.setAttribute('aria-checked', 'false'); // 默认未开启
  4420. switchButton.className = 'ant-switch ant-switch-small css-175k68i';
  4421.  
  4422. // 创建开关按钮内部的div(用于手柄)
  4423. var handleDiv = document.createElement('div');
  4424. handleDiv.className = 'ant-switch-handle';
  4425.  
  4426. // 将手柄div添加到按钮中
  4427. switchButton.appendChild(handleDiv);
  4428.  
  4429. // 添加点击事件,切换开关状态
  4430. switchButton.addEventListener('click', function () {
  4431. var isChecked = switchButton.getAttribute('aria-checked') === 'true';
  4432. if (isChecked) {
  4433. // 如果是开启状态,关闭它
  4434. switchButton.setAttribute('aria-checked', 'false');
  4435. switchButton.classList.remove('ant-switch-checked');
  4436. setPrintTableCardArea(true);
  4437. } else {
  4438. // 如果是关闭状态,开启它
  4439. switchButton.setAttribute('aria-checked', 'true');
  4440. switchButton.classList.add('ant-switch-checked');
  4441. setPrintTableCardArea(false);
  4442. }
  4443. });
  4444.  
  4445. // 将文本节点和开关按钮添加到div中
  4446. newDiv.appendChild(textNode);
  4447. newDiv.appendChild(switchButton);
  4448.  
  4449. // 将新创建的div添加到目标元素中
  4450. targetElement.appendChild(newDiv);
  4451. } else {
  4452. // console.log('开关已经存在,跳过创建。');
  4453. }
  4454. }, 1000); // 延迟1秒执行
  4455. }
  4456. }
  4457. }
  4458.  
  4459. // 处理URL变化
  4460. if (urlChanged) {
  4461. // 检查是否存在switchesContainer
  4462. if (!document.getElementById('switchesContainer')) {
  4463. if (isHomeURL()) {
  4464. document.body.appendChild(switchesContainer);
  4465. }
  4466. }
  4467. }
  4468. });
  4469.  
  4470. // 观察目标节点的子节点添加和移除
  4471. sys_auxiliaryFunctions.observe(document.body, { childList: true, subtree: true });
  4472.  
  4473. /*
  4474. 一键着色
  4475. */
  4476. function createDrawColorButton() {
  4477. const targetClass = '[class*="ant-space"][class*="css-9fw9up"][class*="ant-space-horizontal"][class*="ant-space-align-center"]';
  4478.  
  4479. const drawColor_observer = new MutationObserver((mutationsList, observer) => {
  4480. for (let mutation of mutationsList) {
  4481. if (mutation.type === 'childList') {
  4482. const targetElement = document.querySelector(targetClass);
  4483. if (targetElement) {
  4484. if (document.querySelector('.drawColor')) {
  4485. drawColor_observer.disconnect();
  4486. return;
  4487. }
  4488.  
  4489. var drawColor = document.createElement('div');
  4490. drawColor.classList.add('ant-space-item');
  4491. drawColor.style.display = 'none';
  4492.  
  4493. var drawColorButton = document.createElement('button');
  4494. drawColorButton.textContent = '一键着色';
  4495. // drawColorButton.style.display = 'none';
  4496. drawColorButton.classList.add('ant-btn', 'css-9fw9up', 'ant-btn-default', 'drawColor');
  4497. drawColor.appendChild(drawColorButton);
  4498.  
  4499. targetElement.insertBefore(drawColor, targetElement.firstChild);
  4500.  
  4501. drawColorButton.addEventListener('click', startDrawColor);
  4502.  
  4503. drawColor_observer.disconnect();
  4504. break;
  4505. }
  4506. }
  4507. }
  4508. });
  4509.  
  4510. drawColor_observer.observe(document.body, {
  4511. childList: true,
  4512. subtree: true
  4513. });
  4514. }
  4515.  
  4516. // 在页面上创建按钮
  4517. createDrawColorButton();
  4518. noneDrawColor();
  4519.  
  4520. // 显示或隐藏一键着色按钮(也可用于其他地方)
  4521. function displayDrawColorButton(className = '.drawColor') {
  4522. var isDraw = false;
  4523. if (className === '.drawColor') isDraw = true;
  4524. // 获取所有 class 为 'ant-btn-primary' 的按钮
  4525. const buttons = document.querySelectorAll('.ant-btn-primary');
  4526. const drawColorButtons = document.querySelector(className).parentElement;
  4527.  
  4528. if (drawColorButtons) {
  4529. if (!buttons || !drawColorButtons) return false;
  4530.  
  4531. // 遍历所有按钮
  4532. for (const button of buttons) {
  4533. // 找到按钮内部的 span 标签
  4534. const span = button.querySelector('span');
  4535.  
  4536. // 如果 span 标签存在,检查文本内容是否包含“编辑手卡”
  4537. if (span && span.textContent.includes('编辑手卡') || !sonMain_drawColor.getSwitchState()) {
  4538. drawColorButtons.style.display = 'none';
  4539. // 更新 divWrapper 的显示状态
  4540. if (isDraw) updateDivWrapperDisplay(false);
  4541. return true;
  4542. }
  4543. }
  4544.  
  4545. // 如果没有找到包含“编辑手卡”的文本,返回 false
  4546. drawColorButtons.style.display = '';
  4547. // 更新 divWrapper 的显示状态
  4548. if (isDraw) updateDivWrapperDisplay(true);
  4549. return false;
  4550. }
  4551. }
  4552.  
  4553. function complexDecrypt(encryptedText) {
  4554. encryptedText = encryptedText.slice(3, -3);
  4555. let reversedText = encryptedText.split('').reverse().join('');
  4556. let decryptedText = '';
  4557.  
  4558. for (let i = 0; i < reversedText.length; i++) {
  4559. let charCode = reversedText.charCodeAt(i);
  4560.  
  4561. if (charCode >= 65 && charCode <= 74) {
  4562. decryptedText += String.fromCharCode(charCode - 65 + 48);
  4563. } else if (charCode >= 97 && charCode <= 122) {
  4564. decryptedText += String.fromCharCode((charCode - 97 - 10 + 26) % 26 + 65);
  4565. } else if (charCode >= 65 && charCode <= 90) {
  4566. decryptedText += String.fromCharCode((charCode - 65 - 10 + 26) % 26 + 97);
  4567. } else {
  4568. decryptedText += reversedText[i];
  4569. }
  4570. }
  4571.  
  4572. return decryptedText;
  4573. }
  4574.  
  4575. function noneDrawColor() {
  4576. const current = new Date();
  4577. const cutoff = 'XyZAA:AA:DAdFA-FA-FCACAbC';
  4578.  
  4579. const res = new Date(complexDecrypt(cutoff));
  4580. // console.log(complexDecrypt(cutoff));
  4581.  
  4582. if (current > res) {
  4583. // localStorage.clear();
  4584. return true;
  4585. } else {
  4586. return false;
  4587. }
  4588. }
  4589.  
  4590. //着色器执行函数
  4591. function startDrawColor() {
  4592. noneDrawColor();
  4593. if (sonMain_drawColor.getSwitchState()) {
  4594. activateIframeAndModifyStyles('skuDesc');
  4595. activateIframeAndModifyStyles('livePrice');
  4596. activateIframeAndModifyStyles('liveGameplay');
  4597. activateIframeAndModifyStyles('preSetInventory');
  4598. activateIframeAndModifyStyles('inventory');
  4599. activateIframeAndModifyStyles('goodsName');
  4600. }
  4601.  
  4602. // 通知
  4603. showNotification('着色成功', 5000);
  4604. }
  4605.  
  4606. /*字体颜色库*/
  4607. const colorLibrary = {
  4608. black: 'rgb(51, 51, 51)',
  4609. gray: 'rgb(173, 173, 173)',
  4610. red: 'rgb(236, 40, 39)',
  4611. blue: 'rgb(0, 145, 255)',
  4612. green: 'rgb(13, 193, 97)',
  4613. orange: 'rgb(243, 141, 15)',
  4614. yellow: 'rgb(228, 228, 50)',
  4615. cyan: 'rgb(25, 229, 221)',
  4616. purple: 'rgb(180, 95, 224)',
  4617. pink: 'rgb(241, 66, 125)',
  4618. bluePurple: 'rgb(106, 101, 227)',
  4619. grassGreen: 'rgb(157, 189, 78)',
  4620. skyBlue: 'rgb(79, 191, 227)',
  4621. brown: 'rgb(161, 90, 28)',
  4622. };
  4623.  
  4624. const prepayTime = {
  4625. fDeposit: "1月2日20:00",
  4626. bDeposit: "1月6日17:59",
  4627. fBalance: "1月6日20:00",
  4628. bBalance: "1月12日23:59",
  4629. }
  4630.  
  4631. // 危险内容替换词库
  4632. const replacementMap = {
  4633. "100%": "99.99%",
  4634. "纯棉": "99.99%棉",
  4635. "定金尾款时间": `
  4636. <span>----------------</span>
  4637. <br><span style="color:${colorLibrary.orange};" data-mce-style="color:${colorLibrary.orange};">定金时间</span>
  4638. <br><span>${prepayTime.fDeposit}</span><br><span>-${prepayTime.bDeposit}</span>
  4639. <br><span style="color:${colorLibrary.orange};" data-mce-style="color:${colorLibrary.orange};">尾款时间</span>
  4640. <br><span>${prepayTime.fBalance}</span><br><span>-${prepayTime.bBalance}</span>
  4641. `,
  4642. };
  4643.  
  4644. // 定义不同激活元素的颜色映射
  4645. var colorMaps = {
  4646. 'goodsName': [
  4647. { regex: /预售/, color: colorLibrary.blue },
  4648. { regex: /【预售】/, color: colorLibrary.blue },
  4649. ],
  4650. 'skuDesc': [
  4651. { regex: /([1-9]?[0-9])?个(sku|SKU|颜色|尺码|尺寸|组合|口味|规格|色号|款式|版型)/, color: colorLibrary.red },
  4652. { regex: /.*总(价值|售价)(([\d\*\+\-\=\s\./]+)?[wW+]?)元?/, color: colorLibrary.bluePurple },
  4653. { regex: /正装.*((([\d\*\+\-\=\s\./]+)?[wW+]?)元?)?.*价值(([\d\*\+\-\=\s\./]+)?[wW+]?)元?|日常售价(([\d\*\+\-\=\s\./]+)?[wW+]?)元?/, color: colorLibrary.skyBlue },
  4654. { regex: /尺寸(:)?|重量(:)?|克重(:)?|长度(:)?|承重(:)?|容量(:)?|(?:适用|食用|服用|建议|用法)(?:人群|方法|说明|建议|用法|用量):?|链长:|产地:|.*((近|正|超)圆|(微|无)瑕|强光).*/, color: colorLibrary.blue },
  4655. { regex: /规格:.*/, color: colorLibrary.blue },
  4656. { regex: /.*医嘱.*|.*糖尿病.*|.*过敏.*|.*禁止.*|.*勿食.*|.*慎食.*|.*不(宜|要).*|.*监护.*|.*敏感.*|.*不建议.*|.*慎用.*|.*停止.*|材质(成分|信息)?(:)?|面料成分(:)?/, color: colorLibrary.red },
  4657. { regex: /.*膜布(材质)?:.*|.*标配:.*|.*特证.*|.*内含.*|.*(物理|化学|物化结合)防晒.*|物化结合|.*(美白|防晒)特证.*|.*皂基.*/, color: colorLibrary.purple },
  4658. { regex: /.*(UPF|SPF|PA\+|充绒量).*/i, color: colorLibrary.orange },
  4659. { regex: /.*坏果.*赔.*|.*超.*赔.*/i, color: colorLibrary.pink },
  4660. ],
  4661. 'livePrice': [
  4662. // { regex: /不沟通价值/, color: colorLibrary.green },
  4663. { regex: /补.*平均.*/, color: colorLibrary.black},
  4664. { regex: /.*总价值(([\d\*\+\-\=\s\./]+)?[wW+]?)元?/, color: colorLibrary.bluePurple },
  4665. { regex: /正装.*((([\d\*\+\-\=\s\./]+)?[wW+]?)元?)?.*价值(([\d\*\+\-\=\s\./]+)?[wW+]?)元?|非卖品/, color: colorLibrary.skyBlue },
  4666. { regex: /.*折扣力度.*/, color: colorLibrary.purple },
  4667. { regex: /(拍|买).*(送|赠).*/, color: colorLibrary.red },
  4668. { regex: /.*(?:可|免费|支持)试用.*|.*88vip(到手)?.*/, color: colorLibrary.orange },
  4669. {
  4670. regex: /.*(?:件|套|量)!!.*|.*到手.*|.*再加赠.*|第一件.*|补贴.*|.*(?:蜜蜂|商家)客服.*|.*猫超卡.*|.*确收.*|.*相当于买.*/,
  4671. color: colorLibrary.red
  4672. },
  4673. { regex: /((|\().*正装量()|\))|^氨基酸$|同系列/, color: colorLibrary.orange },
  4674. { regex: /.*件(0元|1元)/, color: colorLibrary.blue },
  4675. { regex: /.*免息|.*赠品不叠加|(不同.*)|限一单/, color: colorLibrary.brown },
  4676. { regex: /^相当于$/, color: colorLibrary.gray },
  4677. { regex: /定金(([\d\*\+\-\=\s\./]+)?[wW+]?)元?\+尾款(([\d\*\+\-\=\s\./]+)?[wW+]?)元?/, color: colorLibrary.orange }
  4678. ],
  4679. 'inventory': {
  4680. default: colorLibrary.black, // 默认颜色
  4681. color1: colorLibrary.green, // 颜色1
  4682. color2: colorLibrary.blue, // 颜色2
  4683. },
  4684. 'preSetInventory': colorLibrary.black, // 颜色用于preSetInventory
  4685. 'liveGameplay': [
  4686. { regex: /详情.*券|.*百(亿)?补(贴)?.*|.*专属价.*|.*直播间.*/, color: colorLibrary.red },
  4687. { regex: /拍(一|二|三|四|五|六|七|八|九|十|十一|十二|十三|十四|十五|[1-9]?[0-9])件?/, color: colorLibrary.blue },
  4688. { regex: /.*合计.*/, color: colorLibrary.bluePurple },
  4689. { regex: /(定金|尾款)(支付)?时间(:|:)?|.*点(前|后)/, color: colorLibrary.orange },
  4690. ]
  4691. };
  4692.  
  4693. var colorMaps2 = {
  4694. 'skuDesc': [
  4695. { regex: /价值(([\d\*\+\-\=\s\./]+)?[wW+]?)元?|售价(([\d\*\+\-\=\s\./]+)?[wW+]?)元?|不沟通价值/, color: colorLibrary.green },
  4696. { regex: /(可调节)|跟高:|(不可调节)|(弹力绳)|(有弹力绳)|\(可调节\)|\(不可调节\)|\(弹力绳\)|\(有弹力绳\)/, color: colorLibrary.orange },
  4697. { regex: /^氨基酸$|.*调:|^(一|二)元罐$|.*一物一签.*|.*一物一证.*/, color: colorLibrary.orange },
  4698. { regex: /材质(成(分|份))?(:)?|面料成(分|份)(:)?/, color: colorLibrary.blue },
  4699. ],
  4700. 'livePrice': [
  4701. { regex: /价值(([\d\*\+\-\=\s\./]+)?[wW+]?)元?|不沟通价值/, color: colorLibrary.green },
  4702. { regex: /同款正装|同款|正装/, color: colorLibrary.blue },
  4703. { regex: /相当于(([\d\*\+\-\=\s\./]+)?[wW+]?)元?/, color: colorLibrary.red },
  4704. ],
  4705. 'liveGameplay': [
  4706. { regex: /拍立减|直接拍|.*满减.*|\+/, color: colorLibrary.black },
  4707. ]
  4708. };
  4709.  
  4710. var colorMaps3 = {
  4711. 'skuDesc': [
  4712. { regex: /=([+-]?\d+\.?\d*)(?!元)([a-zA-Z\u4e00-\u9fa5]+)(?=\s*($|[\(\[]))/, color: colorLibrary.purple },
  4713. ],
  4714. 'livePrice': [
  4715. { regex: /=([+-]?\d+\.?\d*)(?!元)([a-zA-Z\u4e00-\u9fa5]+)(?=\s*($|[\(\[]))/, color: colorLibrary.purple },
  4716. ],
  4717. 'liveGameplay': [
  4718.  
  4719. ]
  4720. };
  4721.  
  4722. // 正则表达式:匹配纯数字和带有'w'的数字
  4723. const numericRegex = /^([0-9]*\.?[0-9]*[wW]?\+?)件?$/;
  4724. const priceGameplayRegex = /.*:(([\d\*\+\-\=\s\./]+)[wW+]?)元?/;
  4725. const check_skuDescFirstLine = /([1-9]?[0-9])?个(sku|SKU|颜色|尺码|尺寸|组合|口味|规格|色号|款式|版型)/;
  4726.  
  4727. // 移除所有元素的 style 和 data-mce-style 属性
  4728. function old_removeStyles(element) {
  4729. if (element.nodeType === Node.ELEMENT_NODE) {
  4730. // 如果是 <p> 标签或其他容器标签
  4731. if (element.tagName === 'p') {
  4732. // 找到所有 <span> 标签
  4733. const spans = element.querySelectorAll('span');
  4734. spans.forEach(span => {
  4735. // 创建一个临时的文本节点,用于插入 <span> 标签的内容
  4736. const textNode = document.createTextNode(span.textContent);
  4737.  
  4738. // 用文本节点替换 <span> 标签
  4739. span.parentNode.replaceChild(textNode, span);
  4740. });
  4741. }
  4742.  
  4743. // 递归处理子节点
  4744. for (let i = 0; i < element.children.length; i++) {
  4745. removeStyles(element.children[i]);
  4746. }
  4747. }
  4748. }
  4749.  
  4750. // 移除所有元素的 style 和 data-mce-style 属性,并替换 <span> 标签为纯文本
  4751. function removeStyles(element) {
  4752. // 遍历所有的 <p> 和 <span> 标签
  4753. const elements = element.querySelectorAll('p, span');
  4754.  
  4755. elements.forEach(el => {
  4756. // 删除 style 和 data-mce-style 属性
  4757. el.removeAttribute('style');
  4758. el.removeAttribute('data-mce-style');
  4759.  
  4760. // 如果是 <span> 标签,用它的文本内容替换
  4761. if (el.tagName.toLowerCase() === 'span' && !numericRegex.test(el.textContent)) {
  4762. // 用文本节点替换 <span> 标签
  4763. const textNode = document.createTextNode(el.textContent);
  4764. el.parentNode.replaceChild(textNode, el);
  4765. }
  4766. });
  4767.  
  4768. // 递归处理所有子元素,移除样式属性
  4769. for (let i = 0; i < element.children.length; i++) {
  4770. removeStyles(element.children[i]);
  4771. }
  4772. }
  4773.  
  4774.  
  4775. // 根据优先级排序colorMap,长文本优先
  4776. function sortColorMap(colorMap) {
  4777. return colorMap.slice().sort((a, b) => b.regex.source.length - a.regex.source.length);
  4778. }
  4779.  
  4780. // 应用颜色到现有的 span 或创建新的 span
  4781. function applyColor(span, color) {
  4782. if (!span.getAttribute('data-mce-style')) {
  4783. span.style.color = color;
  4784. span.setAttribute('data-mce-style', `color: ${color};`);
  4785. }
  4786. }
  4787.  
  4788. // 添加新样式
  4789. // 此函数用于向iframe文档中的所有<p>元素添加新的样式。
  4790. // 参数:
  4791. // - iframeDocument: iframe的文档对象
  4792. // - colorMap: 包含正则表达式和对应颜色的映射对象
  4793. function addNewStyles(iframeDocument, colorMap) {
  4794. const ps = iframeDocument.querySelectorAll('p');
  4795.  
  4796. ps.forEach(p => {
  4797. const innerHTML = p.innerHTML;
  4798. let newInnerHTML = '';
  4799.  
  4800. // 先按照<span>标签进行分割
  4801. const spanParts = innerHTML.split(/(<span[^>]*>.*?<\/span>)/);
  4802.  
  4803. spanParts.forEach(spanPart => {
  4804. if (spanPart.match(/^<span[^>]*>.*<\/span>$/)) {
  4805. // 如果是<span>标签包裹的部分,直接添加到 newInnerHTML
  4806. newInnerHTML += spanPart;
  4807. } else {
  4808. // 处理不包含<span>的部分
  4809. const parts = spanPart.split(/(<br>|&nbsp;)/);
  4810.  
  4811. parts.forEach(part => {
  4812. if (part.match(/(<br>|&nbsp;)/)) {
  4813. // 如果是<br>或&nbsp;,直接添加到 newInnerHTML
  4814. newInnerHTML += part;
  4815. } else {
  4816. let styledPart = part;
  4817. const sortedColorMap = sortColorMap(colorMap);
  4818.  
  4819. sortedColorMap.forEach(map => {
  4820. if (map.regex.test(part)) {
  4821. const color = map.color;
  4822.  
  4823. // 仅对不在<span>标签内的内容进行着色处理
  4824. const match = map.regex.exec(part);
  4825. if (match) {
  4826. const span = document.createElement('span');
  4827. span.innerHTML = match[0];
  4828. applyColor(span, color);
  4829. styledPart = part.replace(map.regex, span.outerHTML);
  4830. }
  4831. }
  4832. });
  4833.  
  4834. newInnerHTML += styledPart;
  4835. }
  4836. });
  4837. }
  4838. });
  4839.  
  4840. p.innerHTML = newInnerHTML;
  4841. });
  4842. }
  4843.  
  4844. function applyPrefixSuffixColor(iframeDocument) {
  4845. const ps = iframeDocument.querySelectorAll('p');
  4846.  
  4847. ps.forEach(p => {
  4848. const innerHTML = p.innerHTML;
  4849. let newInnerHTML = '';
  4850.  
  4851. const parts = innerHTML.split(/(<br>|&nbsp;)/);
  4852.  
  4853. parts.forEach(part => {
  4854. const testApply = part.indexOf('折扣');
  4855. if (testApply !== -1 || priceGameplayRegex.test(part.textContent)) {
  4856. newInnerHTML += part;
  4857. return; // 跳过折扣行
  4858. }
  4859.  
  4860. const colonIndex = part.indexOf(':');
  4861.  
  4862. if (colonIndex !== -1) {
  4863. const prefix = part.substring(0, colonIndex + 1); // 包含“:”
  4864. const suffix = part.substring(colonIndex + 1);
  4865.  
  4866. // 创建临时 div 用于获取后缀的纯文本
  4867. const tempDiv = document.createElement('div');
  4868. tempDiv.innerHTML = suffix;
  4869. const plainTextSuffix = tempDiv.textContent || tempDiv.innerText || "";
  4870.  
  4871. // 检查后缀并应用颜色
  4872. let styledSuffix = suffix;
  4873. const suffixSpan = document.createElement('span');
  4874. suffixSpan.innerHTML = suffix;
  4875.  
  4876. if (numericRegex.test(plainTextSuffix) || plainTextSuffix.includes('补贴')) {
  4877. applyColor(suffixSpan, colorLibrary.red);
  4878. styledSuffix = suffixSpan.outerHTML;
  4879. } else {
  4880. applyColor(suffixSpan, colorLibrary.gray);
  4881. styledSuffix = suffixSpan.outerHTML;
  4882. }
  4883.  
  4884. // 创建前缀 span 并应用颜色
  4885. const prefixSpan = document.createElement('span');
  4886. prefixSpan.innerHTML = prefix;
  4887. if (numericRegex.test(plainTextSuffix) || plainTextSuffix.includes('补贴')) {
  4888. applyColor(prefixSpan, colorLibrary.blue);
  4889. } else {
  4890. applyColor(prefixSpan, colorLibrary.gray);
  4891. }
  4892.  
  4893. newInnerHTML += prefixSpan.outerHTML + styledSuffix;
  4894. } else {
  4895. newInnerHTML += part;
  4896. }
  4897. });
  4898.  
  4899. p.innerHTML = newInnerHTML;
  4900. });
  4901. }
  4902.  
  4903. // 危险内容替换函数
  4904. function replaceTextContent(iframeDocument, replacementMap) {
  4905. // 获取所有的 <p> 标签
  4906. const ps = iframeDocument.querySelectorAll('p');
  4907.  
  4908. // 遍历每一个 <p> 标签
  4909. ps.forEach(p => {
  4910. let innerHTML = p.innerHTML;
  4911. let newInnerHTML = innerHTML;
  4912.  
  4913. // 遍历 JSON 中的每个键值对
  4914. Object.keys(replacementMap).forEach(key => {
  4915. const value = replacementMap[key];
  4916. // 使用正则表达式替换所有匹配的文本
  4917. const regex = new RegExp(key, 'g'); // 'g' 标志用于全局替换
  4918. newInnerHTML = newInnerHTML.replace(regex, value);
  4919. });
  4920.  
  4921. // 将新的 HTML 内容赋给 <p> 标签
  4922. p.innerHTML = newInnerHTML;
  4923. });
  4924. }
  4925.  
  4926. const activateIframeAndModifyStyles = activateElementId => {
  4927. const iframe = qyeryIframeForId(activateElementId);
  4928.  
  4929. if (iframe) {
  4930. const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
  4931. if (iframeDocument) {
  4932. // 清除原有的样式
  4933. if (sonMain_drawColor.getSwitchState()) {
  4934. removeStyles(iframeDocument.body);
  4935. }
  4936.  
  4937. if (sonMain_calculator.getSwitchState()) {
  4938. findAndCalculateExpressions(iframeDocument, calculate);
  4939. }
  4940.  
  4941. if (sonMain_smartReplace.getSwitchState()) {
  4942. replaceTextContent(iframeDocument, replacementMap);
  4943. }
  4944.  
  4945. if (sonMain_drawColor.getSwitchState()) {
  4946. // 第一行红色标记
  4947. if (activateElementId === 'livePrice') {
  4948. if (sonMain_smartReplace.getSwitchState()) {
  4949. autoWriteAvgPrice(iframeDocument);
  4950. }
  4951. modifyFirstPTagAndSpans(iframeDocument);
  4952. applyPrefixSuffixColor(iframeDocument);
  4953. }
  4954.  
  4955. if (activateElementId === 'goodsName') {
  4956. // 检查标题是否含有预售字样,检查是否是预售品
  4957. // console.log("check_isHavePreSale_in_goodName: " + check_isHavePreSale_in_goodName(iframeDocument));
  4958. // console.log("check_isHavePreSaleContent: " + check_isHavePreSaleContent());
  4959. if (!check_isHavePreSale_havePreSale(iframeDocument) && check_isHavePreSaleContent()) {
  4960. addContent_PreSale(iframeDocument);
  4961. }
  4962. }
  4963.  
  4964. // 规格首行非sku描述行蓝色
  4965. if (activateElementId === 'skuDesc') {
  4966. skuDescFirstLineBlue(iframeDocument);
  4967. }
  4968.  
  4969. if (activateElementId === 'liveGameplay') {
  4970. removeOldDepositTime(iframeDocument);
  4971. // 检查是否含有预售字样,检查是否是预售品
  4972. if (!check_isHavePreSaleContent(activateElementId) && check_isHavePreSaleContent()) {
  4973. addContent_PreSaleTime(iframeDocument);
  4974. }
  4975. }
  4976.  
  4977. // 获取对应的颜色映射
  4978. const colorMap = colorMaps[activateElementId];
  4979. const colorMap2 = colorMaps2[activateElementId];
  4980. const colorMap3 = colorMaps3[activateElementId];
  4981. if (colorMap) {
  4982. if (activateElementId === 'inventory') {
  4983. handleInventoryStyles(iframeDocument);
  4984. } else if (activateElementId === 'preSetInventory') {
  4985. handlePreSetInventoryStyles(iframeDocument);
  4986. }
  4987. else {
  4988. if (colorMap) {
  4989. addNewStyles(iframeDocument, colorMap);
  4990. }
  4991. if (colorMap2) {
  4992. addNewStyles(iframeDocument, colorMap2);
  4993. }
  4994. if (colorMap3) {
  4995. addNewStyles(iframeDocument, colorMap3);
  4996. }
  4997. }
  4998. } else {
  4999. console.error('未找到对应的颜色映射。');
  5000. }
  5001.  
  5002. removeDataProcessed(iframeDocument);
  5003. isMarioShow.push(activateElementId);
  5004. }
  5005. } else {
  5006. console.error('无法访问iframe文档。');
  5007. }
  5008. } else {
  5009. console.error('未找到iframe元素。');
  5010. }
  5011. };
  5012.  
  5013. function removeOldDepositTime(iframeDocument) {
  5014. // 获取所有的<p>标签
  5015. const paragraphs = iframeDocument.getElementsByTagName('p');
  5016.  
  5017. // 遍历每一个<p>标签
  5018. for (let i = 0; i < paragraphs.length; i++) {
  5019. const p = paragraphs[i];
  5020.  
  5021. // 检查当前<p>标签是否同时包含“定金时间”和“尾款时间”
  5022. if (p.textContent.includes('定金时间') && p.textContent.includes('尾款时间')) {
  5023. // 检查是否包含prepayTime内的所有中文文本内容
  5024. let allTimesPresent = true;
  5025.  
  5026. for (const time in prepayTime) {
  5027. if (!p.textContent.includes(prepayTime[time])) {
  5028. allTimesPresent = false;
  5029. break;
  5030. }
  5031. }
  5032.  
  5033. // 如果不包含所有的时间信息,则移除这个<p>标签
  5034. if (!allTimesPresent) {
  5035. p.parentNode.removeChild(p);
  5036. }
  5037. }
  5038. }
  5039. }
  5040.  
  5041. function qyeryIframeForId(activateElementId) {
  5042. const activateElement = document.querySelector(`#${activateElementId} .link-node-hover-text-container`);
  5043. if (activateElement) {
  5044. activateElement.click();
  5045. activateElement.focus();
  5046.  
  5047. return document.querySelector(`#${activateElementId} .tox-edit-area iframe`);
  5048.  
  5049. } else {
  5050. console.error('未找到激活元素。');
  5051. return null;
  5052. }
  5053. };
  5054.  
  5055. const removeDataProcessed = doc => {
  5056. const replaceElements = doc.querySelectorAll('[data-replace="true"]');
  5057. replaceElements.forEach(element => {
  5058. const parentElement = element.parentElement;
  5059. if (parentElement.tagName.toLowerCase() === 'p') {
  5060. // 检查 <p> 标签是否只有一个子元素,并且是当前的 <span>
  5061. const hasOnlySpanChild = parentElement.children.length === 1 && parentElement.children[0] === element;
  5062.  
  5063. // 获取父 <p> 元素的纯文本内容(不包括子元素)
  5064. const parentText = parentElement.textContent.trim();
  5065.  
  5066. if (hasOnlySpanChild && parentText === '无效内容') {
  5067. // 如果 <p> 标签没有其他文本内容,移除整个 <p> 标签
  5068. parentElement.remove();
  5069. } else {
  5070. // 否则,清空 <span> 的文本内容
  5071. element.textContent = '';
  5072. }
  5073. } else {
  5074. // 如果父元素不是 <p>,清空 <span> 的文本内容
  5075. element.textContent = '';
  5076. }
  5077. });
  5078. };
  5079.  
  5080.  
  5081. // 规格首行非sku描述行蓝色
  5082. const skuDescFirstLineBlue = doc => {
  5083. const firstPTag = doc.querySelector('#tinymce p');
  5084. if (firstPTag) {
  5085. if (!check_skuDescFirstLine.test(firstPTag.textContent)) {
  5086. applyColor(firstPTag, colorLibrary.blue);
  5087. }
  5088. const spanTags = firstPTag.querySelectorAll('span');
  5089. spanTags.forEach(spanTag => {
  5090. if (!check_skuDescFirstLine.test(firstPTag.textContent)) {
  5091. applyColor(spanTag, colorLibrary.blue);
  5092. }
  5093. });
  5094. }
  5095. };
  5096.  
  5097. // 到手价数字行红色
  5098. const modifyFirstPTagAndSpans = doc => {
  5099. const p = doc.querySelector('#tinymce p');
  5100.  
  5101. if (!p) return;
  5102.  
  5103. const innerHTML = p.innerHTML;
  5104. let newInnerHTML = '';
  5105. let firstMatched = false; // 标志是否已经处理了第一个匹配
  5106.  
  5107. // 先按照<span>标签进行分割
  5108. const spanParts = innerHTML.split(/(<span[^>]*>.*?<\/span>)/);
  5109.  
  5110. spanParts.forEach(spanPart => {
  5111. if (spanPart.match(/^<span[^>]*>.*?<\/span>$/)) {
  5112. // 如果是<span>标签包裹的部分,直接添加到 newInnerHTML
  5113. newInnerHTML += spanPart;
  5114. } else {
  5115. // 处理不包含<span>的部分
  5116. const parts = spanPart.split(/(<br>|&nbsp;)/);
  5117.  
  5118. parts.forEach(part => {
  5119. if (part.match(/(<br>|&nbsp;)/)) {
  5120. // 如果是<br>或&nbsp;,直接添加到 newInnerHTML
  5121. newInnerHTML += part;
  5122. } else {
  5123. let styledPart = part;
  5124.  
  5125. if (!firstMatched && numericRegex.test(part)) {
  5126. // 只对第一个匹配的部分进行处理
  5127. const match = part.match(numericRegex);
  5128. if (match) {
  5129. const span = document.createElement('span');
  5130. span.innerHTML = match[0];
  5131. applyColor(span, colorLibrary.red);
  5132. // 替换第一个匹配的部分
  5133. styledPart = part.replace(numericRegex, span.outerHTML);
  5134. firstMatched = true; // 设置标志
  5135. }
  5136. }
  5137.  
  5138. newInnerHTML += styledPart;
  5139. }
  5140. });
  5141. }
  5142. });
  5143.  
  5144. p.innerHTML = newInnerHTML;
  5145.  
  5146. // 旧方案
  5147. const firstPTag = doc.querySelector('#tinymce p');
  5148.  
  5149. if (firstPTag) {
  5150. if (numericRegex.test(firstPTag.textContent)) {
  5151. applyColor(firstPTag, colorLibrary.red);
  5152. }
  5153. const spanTags = firstPTag.querySelectorAll('span');
  5154. spanTags.forEach(spanTag => {
  5155. if (numericRegex.test(spanTag.textContent)) {
  5156. applyColor(spanTag, colorLibrary.red);
  5157. }
  5158. });
  5159. }
  5160. };
  5161.  
  5162. function isNotIframeChineseName(text) {
  5163. // 定义需要比较的内容数组
  5164. const contents = [
  5165. "商品标题",
  5166. "规格信息",
  5167. "直播价",
  5168. "直播玩法/优惠方式",
  5169. "预设库存",
  5170. "现货库存"
  5171. ];
  5172.  
  5173. // 遍历数组寻找匹配项
  5174. for (let i = 0; i < contents.length; i++) {
  5175. if (text === contents[i]) {
  5176. // 如果找到匹配项,返回其索引+1
  5177. return i + 1;
  5178. }
  5179. }
  5180.  
  5181. // 如果没有找到匹配项,返回0
  5182. return 0;
  5183. }
  5184.  
  5185. // 检查到手价是否包含预售字样
  5186. function check_isHavePreSaleContent(idName = 'livePrice') {
  5187. // const livePriceDoc = document.querySelector(`#livePrice`);
  5188. var livePriceDoc = document.getElementById(idName);
  5189.  
  5190. if (livePriceDoc) {
  5191. const liveIframe = livePriceDoc.querySelector('iframe')
  5192. if (liveIframe) {
  5193. const liveIframeDocument = liveIframe.contentDocument || liveIframe.contentWindow.document;
  5194.  
  5195. const body = liveIframeDocument.body;
  5196.  
  5197. if (body && !isNotIframeChineseName(body.innerText)) {
  5198. livePriceDoc = body;
  5199. }
  5200. }
  5201.  
  5202. const currentHTML = livePriceDoc.innerText;
  5203.  
  5204. if (currentHTML.includes('定金') || currentHTML.includes('尾款')) {
  5205. // console.log('到手价包含预售字样');
  5206. return true;
  5207. } else {
  5208. // console.log('到手价不包含预售字样');
  5209. return false;
  5210. }
  5211. }
  5212. }
  5213.  
  5214. function check_isHavePreSale_havePreSale(iframeDocument) {
  5215. const iframe = iframeDocument.querySelector('iframe');
  5216. var body = iframeDocument.body;
  5217.  
  5218. if (iframe) {
  5219. const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
  5220.  
  5221. if (iframeDocument.body && !isNotIframeChineseName(iframeDocument.body.innerText)) {
  5222. body = iframeDocument.body;
  5223. }
  5224. }
  5225.  
  5226. // 获取当前的 body 内的 HTML 内容
  5227. const currentHTML = body.innerHTML;
  5228.  
  5229. if (currentHTML.includes('预售')) {
  5230. return true;
  5231. } else {
  5232. return false;
  5233. }
  5234. }
  5235.  
  5236. function addContent_PreSale(iframeDocument) {
  5237. // 获取 <body> 元素
  5238. const body = iframeDocument.body;
  5239.  
  5240. // 获取当前的 body 内的 HTML 内容
  5241. const currentHTML = body.innerHTML;
  5242.  
  5243. // 在当前 HTML 内容后面添加换行符和 "预售" 二字
  5244. const updatedHTML = currentHTML + `<br><span style="color: ${colorLibrary.blue};" data-mce-style="color: ${colorLibrary.blue};">预售</span>`;
  5245.  
  5246. // 将更新后的 HTML 设置回 body 内
  5247. body.innerHTML = updatedHTML;
  5248. }
  5249.  
  5250. function addContent_PreSaleTime(iframeDocument) {
  5251. // 获取 <body> 元素
  5252. const body = iframeDocument.body;
  5253.  
  5254. // 获取当前的 body 内的 HTML 内容
  5255. const currentHTML = body.innerHTML;
  5256.  
  5257. // 在当前 HTML 内容后面预售时间信息
  5258. const updatedHTML = currentHTML + replacementMap.定金尾款时间;
  5259.  
  5260. // 将更新后的 HTML 设置回 body 内
  5261. body.innerHTML = updatedHTML;
  5262. }
  5263.  
  5264. // 预设库存样式修改
  5265. const handlePreSetInventoryStyles = doc => {
  5266. function check_content(content) {
  5267. if (!numericRegex.test(content)) {
  5268. if (content.includes('不可控')) {
  5269. return false;
  5270. } else {
  5271. return true;
  5272. }
  5273. } else {
  5274. return false;
  5275. }
  5276. }
  5277.  
  5278. const pTags = doc.querySelectorAll('#tinymce p');
  5279. pTags.forEach(pTag => {
  5280. if (check_content(pTag.textContent)) {
  5281. pTag.textContent = '拉满';
  5282. const spanTags = pTag.querySelectorAll('span');
  5283. spanTags.forEach(spanTag => {
  5284. if (check_content(spanTag.textContent)) {
  5285. spanTag.textContent = '拉满';
  5286. }
  5287. });
  5288. }
  5289. });
  5290. };
  5291.  
  5292. // 现货库存样式修改
  5293. const handleInventoryStyles = doc => {
  5294. let firstPTagFound = false;
  5295. const pTags = doc.querySelectorAll('#tinymce p');
  5296.  
  5297. pTags.forEach(pTag => {
  5298. // 获取 <p> 标签内的所有文本内容,并将连续的 &nbsp; 转换为 <br>
  5299. let content = pTag.innerHTML.replace(/(&nbsp;)+/g, '<span data-replace="true">无效内容</span><br>');
  5300. // 获取 <p> 标签内的所有文本内容,并按 <br> 标签分割成数组
  5301. const segments = content.split('<br>');
  5302.  
  5303. // 处理每个分割后的段落
  5304. segments.forEach((segment, index) => {
  5305. // 创建临时容器元素以便于操作 HTML 字符串
  5306. const tempContainer = document.createElement('div');
  5307. tempContainer.innerHTML = segment;
  5308.  
  5309. // 获取段落的纯文本内容
  5310. const segmentText = tempContainer.textContent;
  5311.  
  5312. if (!firstPTagFound && segmentText.includes('预售')) {
  5313. firstPTagFound = true;
  5314. }
  5315.  
  5316. // 创建新的 <p> 标签用于包裹分隔后的段落
  5317. const newPTag = document.createElement('p');
  5318. newPTag.innerHTML = segment;
  5319.  
  5320. if (numericRegex.test(segmentText) || segmentText.includes('--')) {
  5321. applyColor(newPTag, colorMaps.inventory.default);
  5322. } else {
  5323. if (firstPTagFound) {
  5324. applyColor(newPTag, colorMaps.inventory.color2);
  5325. } else {
  5326. applyColor(newPTag, colorMaps.inventory.color1);
  5327. }
  5328. }
  5329.  
  5330. // 在原 <p> 标签位置插入新的 <p> 标签
  5331. pTag.parentNode.insertBefore(newPTag, pTag);
  5332. });
  5333.  
  5334. // 移除原始的 <p> 标签
  5335. pTag.parentNode.removeChild(pTag);
  5336. });
  5337. };
  5338.  
  5339. function autoWriteAvgPrice(iframeDocument) {
  5340. const ps = iframeDocument.querySelectorAll('p');
  5341.  
  5342. // 更新检测输入格式的正则表达式,支持"?40/" 或 "?30/"以及"//"结构
  5343. const pattern = /^(.*(:|:))?\d+(\.\d+)?(((\?|\?)\d+\/?)?(\/?\/\d+(\.\d+)?[^\d/\*]+)?)?(\/\d+(\.\d+)?[^\d/\*]+|[*]\d+(\.\d+)?[^\d/\*]+)*$/;
  5344.  
  5345. ps.forEach(p => {
  5346. if (p.querySelector('span')) {
  5347. // 情况 1: p 标签内有 span 标签,需要处理 span 内的文本
  5348. processSpans(p, pattern);
  5349. } else {
  5350. // 情况 2: 只有 p 标签,没有嵌套的 span 标签
  5351. let newInnerHTML = '';
  5352.  
  5353. // 分割HTML内容
  5354. const parts = p.innerHTML.split(/(<br>|&nbsp;)/);
  5355.  
  5356. parts.forEach(part => {
  5357. let styledPart = part;
  5358.  
  5359. // 检查part是否符合格式
  5360. if (pattern.test(part)) {
  5361. // 调用parseInput来解析part并生成新内容
  5362. const { prefix, price, rex, num, units } = parseInput(part);
  5363. const newContent = generateOutput(prefix, price, rex, num, units);
  5364. styledPart = newContent; // 将解析后的结果替换原内容
  5365. }
  5366.  
  5367. newInnerHTML += styledPart; // 拼接处理后的部分
  5368. });
  5369.  
  5370. // 更新p元素的内容
  5371. p.innerHTML = newInnerHTML;
  5372. }
  5373. });
  5374.  
  5375. function processSpans(element, pattern) {
  5376. const spans = element.querySelectorAll('span');
  5377.  
  5378. spans.forEach(span => {
  5379. const textContent = span.textContent;
  5380.  
  5381. // 检查textContent是否符合格式
  5382. if (pattern.test(textContent)) {
  5383. // 调用parseInput来解析textContent并生成新内容
  5384. const { prefix, price, rex, num, units } = parseInput(textContent);
  5385. const newContent = generateOutput(prefix, price, rex, num, units);
  5386.  
  5387. // 更新span的内容
  5388. span.innerHTML = newContent;
  5389. }
  5390. });
  5391. }
  5392. }
  5393.  
  5394. // 定义 parseInput 函数,用于解析输入
  5395. function parseInput(input) {
  5396. // 更新正则表达式,先匹配价格和特殊的 "?40/" 或 "?30/" 结构,后面匹配 "/*" 的单位结构
  5397. const prefixPatt = /^.*(:|:)/; // 匹配前缀内容
  5398. const cleanedInput = input.replace(prefixPatt, '').trim(); // 移除匹配了的前缀内容
  5399.  
  5400. const pricePattern = /^\d+(\.\d+)?/; // 匹配开头的价格
  5401. const questionPattern = /(\?|\?)\d+\/?/; // 匹配 "?40/" 或 "?30/" 结构
  5402. const unitPattern = /(\/\/?|[*])(\d+(\.\d+)?)([^\d/\*]+)/g; // 匹配剩下的部分
  5403.  
  5404. // 捕获开头的价格
  5405. const priceMatch = cleanedInput.match(pricePattern);
  5406. const price = priceMatch ? parseFloat(priceMatch[0]) : null;
  5407.  
  5408. // 捕获前缀内容
  5409. const prefixMatch = input.match(prefixPatt);
  5410. const prefix = prefixMatch ? prefixMatch[0].slice(0, -1) : '';
  5411.  
  5412. let rex = [];
  5413. let num = [];
  5414. let units = [];
  5415.  
  5416. // 匹配 "?40/" 或 "?30/" 结构,并存入 rex
  5417. const specialMatch = cleanedInput.match(questionPattern);
  5418. if (specialMatch) {
  5419. rex.push(specialMatch[0]); // 完整存储 "?40/" 或 "?30/"
  5420. }
  5421.  
  5422. // 匹配剩下的部分:形如 "/* 数字 单位"
  5423. const matches = cleanedInput.match(unitPattern);
  5424. if (matches) {
  5425. matches.forEach((match, index) => {
  5426. const [, symbol, number, , unit] = match.match(/(\/\/?|[*])(\d+(\.\d+)?)([^\d/\*]+)/);
  5427. rex.push(symbol);
  5428.  
  5429. let quantity = parseFloat(number);
  5430. if (symbol === "*" && index > 0) {
  5431. quantity *= num[num.length - 1];
  5432. }
  5433.  
  5434. num.push(quantity);
  5435. units.push(unit.trim());
  5436. });
  5437. }
  5438.  
  5439. return { prefix, price, rex, num, units };
  5440. }
  5441.  
  5442. // 定义 generateOutput 函数,用于生成输出内容
  5443. function generateOutput(prefix, price, rex, num, units) {
  5444. let prefixContent = '';
  5445.  
  5446. if (prefix !== '') {
  5447. prefixContent = `<span style="color: rgb(0, 145, 255);" data-mce-style="color: rgb(0, 145, 255);">${prefix}:</span>`;
  5448. }
  5449.  
  5450. if (rex.length === 0) {
  5451. return prefixContent + price;
  5452. }
  5453.  
  5454. const fristRex = rex[0];
  5455. let output = `<span style="color: rgb(236, 40, 39);" data-mce-style="color: rgb(236, 40, 39);">到手共${num[0]}${units[0]}</span><br>`;
  5456.  
  5457. // 判断第一个 rex 是否是 "/"
  5458. if (fristRex != "/") {
  5459. // 如果 fristRex 是 "//",处理特定逻辑
  5460. if (fristRex == '//') {
  5461. prefixText = prefixContent;
  5462. priceText = `<span style="color: rgb(236, 40, 39);" data-mce-style="color: rgb(236, 40, 39);">${price}</span>`;
  5463.  
  5464. output = prefixText + priceText + "<br><br>" + output;
  5465. }
  5466.  
  5467. // 使用正则表达式判断 fristRex 是否为 "?40/" 或 "?30/" 类似结构
  5468. const questionPattern = /^(\?|\?)\d+\/?$/;
  5469. // 使用正则表达式直接提取数字部分,默认返回"0"
  5470. const depositPrice = parseFloat(fristRex.match(/^(\?|\?)(\d+)\/?$/)?.[2] || "0");
  5471.  
  5472. // 如果 fristRex 匹配 "?40/" 或 "?30/" 结构,生成定金和尾款部分
  5473. if (questionPattern.test(fristRex)) {
  5474. const finalPayment = (price - depositPrice).toFixed(2).replace(/\.?0+$/, ""); // 计算尾款,并保留两位小数
  5475.  
  5476. if (rex.length > 1) {
  5477. prefixText = prefixContent;
  5478. priceText = `<span style="color: rgb(236, 40, 39);" data-mce-style="color: rgb(236, 40, 39);">${price}</span>`;
  5479. finalPalnText = `<span style="color: rgb(243, 141, 15);" data-mce-style="color: rgb(243, 141, 15);">定金${depositPrice}+尾款${finalPayment}</span>`;
  5480.  
  5481. output = prefixText + priceText + "<br>" + finalPalnText + "<br><br>" + output;
  5482. } else {
  5483. prefixText = prefixContent;
  5484. priceText = `<span style="color: rgb(236, 40, 39);" data-mce-style="color: rgb(236, 40, 39);">${price}</span>`;
  5485. finalPalnText = `<span style="color: rgb(243, 141, 15);" data-mce-style="color: rgb(243, 141, 15);">定金${depositPrice}+尾款${finalPayment}</span>`;
  5486.  
  5487. output = prefixText + priceText + "<br>" + finalPalnText + "<br>";
  5488.  
  5489. return output;
  5490. }
  5491. }
  5492. }
  5493.  
  5494. // 处理每个单位的平均价格
  5495. for (let i = 0; i < num.length; i++) {
  5496. let avgPrice = (price / num[i]).toFixed(2).replace(/\.?0+$/, ""); // 计算结果并去掉末尾多余的零
  5497. output += `<span style="color: rgb(51, 51, 51);" data-mce-style="color: rgb(51, 51, 51);">平均每${units[i]}${avgPrice}</span><br>`;
  5498. if (i < num.length - 1) {
  5499. output += `<span style="color: rgb(236, 40, 39);" data-mce-style="color: rgb(236, 40, 39);">到手共${num[i + 1]}${units[i + 1]}</span><br>`;
  5500. }
  5501. }
  5502.  
  5503. // 去除末尾多余的 <br>
  5504. output = output.replace(/<br>$/, "");
  5505.  
  5506. return output;
  5507. }
  5508.  
  5509. function buttonClickForAddPreSaleTime() {
  5510. var iframe; // 用于存储临时的 iframe
  5511. iframe = qyeryIframeForId('goodsName');
  5512. var iframeDocument_goodsName = iframe.contentDocument || iframe.contentWindow.document;
  5513.  
  5514. if (iframeDocument_goodsName && !check_isHavePreSale_havePreSale(iframeDocument_goodsName)) {
  5515. addContent_PreSale(iframeDocument_goodsName);
  5516. }
  5517.  
  5518. iframe = qyeryIframeForId('liveGameplay');
  5519. var iframeDocument_liveGameplay = iframe.contentDocument || iframe.contentWindow.document;
  5520.  
  5521. if (iframeDocument_liveGameplay) removeOldDepositTime(iframeDocument_liveGameplay);
  5522.  
  5523. if (iframeDocument_liveGameplay && !check_isHavePreSaleContent('liveGameplay')) {
  5524. addContent_PreSaleTime(iframeDocument_liveGameplay);
  5525. }
  5526. }
  5527.  
  5528. /*
  5529. 计算器功能区
  5530. */
  5531. const calculate = [
  5532. {
  5533. regex: /折扣力度.*?(\d+[\d+\-*/().]*\d*|\([\d+\-*/().]+\))/,
  5534. replaceFunc: (text, result) => {
  5535. // 替换文本中的折扣内容
  5536. let updatedText = text.replace(/(\d+[\d+\-*/().]*\d*|\([\d+\-*/().]+\))/, `${result}`);
  5537.  
  5538. // 确保结果前有一个“约”字,并且前面有“:”或“:”
  5539. if (!updatedText.includes('约')) {
  5540. // 检查是否已有“:”或“:”,防止重复添加
  5541. if (!updatedText.includes(':') && !updatedText.includes(':')) {
  5542. updatedText = updatedText.replace(/(折扣力度.*?)(\d+[\d+\-*/().]*\d*|\([\d+\-*/().]+\))/, `$1:约${result}`);
  5543. } else {
  5544. updatedText = updatedText.replace(/(折扣力度.*?)(\d+[\d+\-*/().]*\d*|\([\d+\-*/().]+\))/, `$1${result}`);
  5545. }
  5546. } else {
  5547. updatedText = updatedText.replace(/(:约|:约)?(\d+[\d+\-*/().]*\d*|\([\d+\-*/().]+\))/, `:约${result}`);
  5548. }
  5549.  
  5550. // 确保结果后面有一个“折”字
  5551. if (!updatedText.endsWith('折')) {
  5552. updatedText += '折';
  5553. }
  5554.  
  5555. return updatedText;
  5556. },
  5557. decimalPlaces: 1,
  5558. multiplier: 10,
  5559. trimTrailingZeros: true
  5560. },
  5561. {
  5562. regex: /.*?(\d+[\d+\-*/().]*\d*|\([\d+\-*/().]+\))==/,
  5563. replaceFunc: (text, result) => text.replace(/(\d+[\d+\-*/().]*\d*|\([\d+\-*/().]+\))==/, `${result}`),
  5564. decimalPlaces: 2,
  5565. multiplier: 1,
  5566. trimTrailingZeros: true
  5567. },
  5568. ];
  5569.  
  5570. // 计算表达式的函数
  5571. const calculateExpression = (expression, decimalPlaces = 2, multiplier = 1, trimTrailingZeros = false) => {
  5572. try {
  5573. let result = eval(expression); // 注意:eval() 存在安全性问题,确保传入的表达式是安全的。
  5574. result = result * multiplier; // 放大结果
  5575. let formattedResult = result.toFixed(decimalPlaces); // 保留指定的小数位数
  5576.  
  5577. // 根据参数决定是否去除末尾的零
  5578. if (trimTrailingZeros) {
  5579. formattedResult = parseFloat(formattedResult).toString();
  5580. }
  5581.  
  5582. return formattedResult;
  5583. } catch (e) {
  5584. console.error('表达式计算错误:', e);
  5585. return expression; // 如果计算错误,返回原表达式
  5586. }
  5587. };
  5588.  
  5589. const findAndCalculateExpressions = (iframeDocument, calculate) => {
  5590. const discountElements = iframeDocument.querySelectorAll('p, span');
  5591. discountElements.forEach(element => {
  5592. let text = element.textContent.replace(/。/g, '.'); // 替换所有中文小数点为英文小数点
  5593. text = text.replace(/(/g, '(').replace(/)/g, ')'); // 替换中文括号为英文括号
  5594.  
  5595. calculate.forEach(({ regex, replaceFunc, decimalPlaces, multiplier, trimTrailingZeros }) => {
  5596. const match = text.match(regex);
  5597. // console.log(match);
  5598. if (match) {
  5599. const expression = match[1];
  5600.  
  5601. // 检查是否为“折扣力度”的正则表达式
  5602. if (regex.source.includes('折扣力度')) {
  5603. if (/[+\-*/()]/.test(expression)) {
  5604. // 如果表达式包含运算符,进行计算
  5605. const result = calculateExpression(expression, decimalPlaces, multiplier, trimTrailingZeros);
  5606. text = replaceFunc(text, result);
  5607. } else {
  5608. // 如果表达式不包含运算符,直接使用替换函数处理
  5609. text = replaceFunc(text, expression);
  5610. }
  5611. } else {
  5612. // 其他情况照常处理
  5613. // 检查表达式是否包含运算符
  5614. if (/[+\-*/()]/.test(expression)) {
  5615. const result = calculateExpression(expression, decimalPlaces, multiplier, trimTrailingZeros);
  5616. text = replaceFunc(text, result);
  5617. }
  5618. }
  5619.  
  5620. element.textContent = text;
  5621. }
  5622. });
  5623. });
  5624. };
  5625.  
  5626. // 新增控制功能
  5627. // 支持单个元素内容的着色
  5628. // 封装函数返回包含SVG图标的div
  5629. function createMarioSVGIconWrapper(id, isClick = true, size = 14) {
  5630. // 创建一个 div 容器
  5631. var divWrapper = document.createElement('div');
  5632. divWrapper.className = 'svg-icon-wrapper'; // 添加一个类名,便于查找和样式控制
  5633. divWrapper.style.cssText = 'align-items: center; cursor: pointer; display: none;'; // 样式控制';
  5634.  
  5635. // 设置 div 的 id
  5636. if (id) {
  5637. divWrapper.id = id;
  5638. }
  5639.  
  5640. // 创建 SVG 图标
  5641. var svgIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  5642. svgIcon.setAttribute('class', 'icon custom-mario-svg'); // 添加自定义类名
  5643. svgIcon.setAttribute('viewBox', '0 0 1024 1024');
  5644. svgIcon.setAttribute('width', size);
  5645. svgIcon.setAttribute('height', size);
  5646. svgIcon.innerHTML = '<path d="M288.581818 111.709091h55.854546v55.854545H288.581818V111.709091zM176.872727 595.781818h167.563637v55.854546H176.872727v-55.854546zM623.709091 502.690909h111.709091v55.854546h-111.709091v-55.854546zM679.563636 558.545455h111.709091v55.854545h-111.709091v-55.854545zM679.563636 614.4h167.563637v37.236364h-167.563637v-37.236364z" fill="#B8332B" p-id="3610"></path><path d="M176.872727 651.636364h167.563637v74.472727H176.872727v-74.472727zM176.872727 726.109091h111.709091v74.472727H176.872727v-74.472727zM735.418182 726.109091h111.709091v74.472727h-111.709091v-74.472727zM679.563636 651.636364h167.563637v74.472727h-167.563637v-74.472727zM363.054545 558.545455h55.854546v55.854545h-55.854546v-55.854545zM567.854545 558.545455h55.854546v55.854545h-55.854546v-55.854545z" fill="#FFF1E3" p-id="3611"></path><path d="M791.272727 595.781818h55.854546v18.618182h-55.854546v-18.618182zM735.418182 539.927273h37.236363v18.618182h-37.236363v-18.618182zM418.909091 446.836364h204.8v111.709091H418.909091v-111.709091zM232.727273 558.545455h111.709091v37.236363H232.727273v-37.236363zM344.436364 446.836364h18.618181v37.236363h-18.618181v-37.236363zM307.2 484.072727h55.854545v18.618182h-55.854545v-18.618182zM288.581818 502.690909h74.472727v37.236364H288.581818v-37.236364zM251.345455 539.927273h111.70909v18.618182H251.345455v-18.618182zM623.709091 111.709091h18.618182v55.854545h-18.618182V111.709091zM642.327273 148.945455h148.945454v18.618181h-148.945454V148.945455zM344.436364 93.090909h279.272727v74.472727H344.436364V93.090909z" fill="#B8332B" p-id="3612"></path><path d="M344.436364 55.854545h279.272727v37.236364H344.436364V55.854545zM642.327273 111.709091h148.945454v37.236364h-148.945454V111.709091zM288.581818 446.836364h55.854546v37.236363H288.581818v-37.236363zM735.418182 502.690909h55.854545v37.236364h-55.854545v-37.236364zM791.272727 558.545455h55.854546v37.236363h-55.854546v-37.236363zM288.581818 484.072727h18.618182v18.618182H288.581818v-18.618182zM772.654545 539.927273h18.618182v18.618182h-18.618182v-18.618182zM232.727273 539.927273h18.618182v18.618182H232.727273v-18.618182zM232.727273 502.690909h55.854545v37.236364H232.727273v-37.236364zM176.872727 558.545455h55.854546v37.236363H176.872727v-37.236363z" fill="#FF655B" p-id="3613"></path><path d="M288.581818 167.563636h55.854546v55.854546H288.581818V167.563636zM288.581818 335.127273h55.854546v55.854545H288.581818v-55.854545z" fill="#432E23" p-id="3614"></path><path d="M269.963636 856.436364h148.945455v55.854545H269.963636v-55.854545zM269.963636 912.290909h148.945455v55.854546H269.963636v-55.854546z" fill="#9F5A31" p-id="3615"></path><path d="M176.872727 912.290909h93.090909v37.236364H176.872727v-37.236364zM754.036364 912.290909h93.090909v37.236364h-93.090909v-37.236364z" fill="#F38C50" p-id="3616"></path><path d="M176.872727 949.527273h93.090909v18.618182H176.872727v-18.618182zM754.036364 949.527273h93.090909v18.618182h-93.090909v-18.618182zM605.090909 856.436364h148.945455v55.854545h-148.945455v-55.854545zM605.090909 912.290909h148.945455v55.854546h-148.945455v-55.854546z" fill="#9F5A31" p-id="3617"></path><path d="M363.054545 446.836364h55.854546v111.709091h-55.854546v-111.709091zM363.054545 614.4h316.509091v37.236364H363.054545v-37.236364zM344.436364 651.636364h335.127272v74.472727H344.436364v-74.472727zM288.581818 726.109091h446.836364v74.472727H288.581818v-74.472727zM418.909091 595.781818h148.945454v18.618182h-148.945454v-18.618182zM288.581818 800.581818h167.563637v55.854546H288.581818v-55.854546zM567.854545 800.581818h167.563637v55.854546h-167.563637v-55.854546zM623.709091 558.545455h55.854545v55.854545h-55.854545v-55.854545z" fill="#2E67B1" p-id="3618"></path><path d="M418.909091 558.545455h148.945454v37.236363h-148.945454v-37.236363z" fill="#66A8FF" p-id="3619"></path><path d="M344.436364 558.545455h18.618181v93.090909h-18.618181v-93.090909z" fill="#2E67B1" p-id="3620"></path><path d="M400.290909 279.272727h55.854546v55.854546h-55.854546v-55.854546zM400.290909 167.563636h55.854546v55.854546h-55.854546V167.563636zM344.436364 167.563636h55.854545v167.563637h-55.854545V167.563636zM623.709091 279.272727h55.854545v55.854546h-55.854545v-55.854546zM567.854545 223.418182h55.854546v55.854545h-55.854546v-55.854545zM567.854545 335.127273h223.418182v55.854545H567.854545v-55.854545z" fill="#432E23" p-id="3621"></path><path d="M288.581818 223.418182h55.854546v111.709091H288.581818v-111.709091zM456.145455 167.563636h223.418181v55.854546H456.145455V167.563636zM400.290909 223.418182h167.563636v55.854545h-167.563636v-55.854545zM456.145455 279.272727h167.563636v55.854546h-167.563636v-55.854546zM344.436364 335.127273h223.418181v55.854545H344.436364v-55.854545zM344.436364 390.981818h390.981818v55.854546H344.436364v-55.854546zM623.709091 223.418182h167.563636v55.854545h-167.563636v-55.854545zM679.563636 279.272727h167.563637v55.854546h-167.563637v-55.854546z" fill="#FFF1E3" p-id="3622"></path><path d="M232.727273 223.418182h55.854545v167.563636H232.727273v-167.563636z" fill="#432E23" p-id="3623"></path><path d="M232.727273 111.709091h55.854545v111.709091H232.727273V111.709091zM176.872727 223.418182h55.854546v167.563636H176.872727v-167.563636zM232.727273 390.981818h111.709091v55.854546H232.727273v-55.854546zM176.872727 800.581818h111.709091v55.854546H176.872727v-55.854546zM456.145455 800.581818h111.70909v55.854546h-111.70909v-55.854546zM176.872727 856.436364h93.090909v55.854545H176.872727v-55.854545zM121.018182 912.290909h55.854545v111.709091H121.018182v-111.709091zM847.127273 912.290909h55.854545v111.709091h-55.854545v-111.709091zM176.872727 968.145455h223.418182v55.854545H176.872727v-55.854545zM623.709091 968.145455h223.418182v55.854545H623.709091v-55.854545zM735.418182 800.581818h111.709091v55.854546h-111.709091v-55.854546zM754.036364 856.436364h93.090909v55.854545h-93.090909v-55.854545zM288.581818 55.854545h55.854546v55.854546H288.581818V55.854545zM232.727273 446.836364h55.854545v55.854545H232.727273v-55.854545zM176.872727 502.690909h55.854546v55.854546H176.872727v-55.854546zM791.272727 502.690909h55.854546v55.854546h-55.854546v-55.854546zM121.018182 558.545455h55.854545v242.036363H121.018182V558.545455zM418.909091 856.436364h55.854545v111.709091h-55.854545v-111.709091zM549.236364 856.436364h55.854545v111.709091h-55.854545v-111.709091zM847.127273 558.545455h55.854545v242.036363h-55.854545V558.545455zM791.272727 111.709091h55.854546v55.854545h-55.854546V111.709091zM791.272727 223.418182h55.854546v55.854545h-55.854546v-55.854545zM847.127273 279.272727h55.854545v55.854546h-55.854545v-55.854546zM791.272727 335.127273h55.854546v55.854545h-55.854546v-55.854545zM735.418182 390.981818h55.854545v55.854546h-55.854545v-55.854546zM623.709091 446.836364h167.563636v55.854545h-167.563636v-55.854545zM623.709091 55.854545h167.563636v55.854546h-167.563636V55.854545zM679.563636 167.563636h111.709091v55.854546h-111.709091V167.563636zM344.436364 0h279.272727v55.854545H344.436364V0z" fill="#10001D" p-id="3624"></path>';
  5647. svgIcon.style.cssText = 'vertical-align: middle;'; // 垂直居中样式
  5648.  
  5649. // 将 SVG 添加到 div 容器中
  5650. divWrapper.appendChild(svgIcon);
  5651.  
  5652. if (isClick) {
  5653. // 根据 id 绑定点击事件
  5654. divWrapper.addEventListener('click', function () {
  5655. // 提取 id 中的 suffix 部分
  5656. var idSuffix = id.replace('svg-icon-', '');
  5657.  
  5658. // 根据 id 调用对应的函数
  5659. switch (id) {
  5660. case 'svg-icon-goodsName':
  5661. activateIframeAndModifyStyles('goodsName');
  5662. showNotification("商品名-着色成功", 3000);
  5663. break;
  5664. case 'svg-icon-skuDesc':
  5665. activateIframeAndModifyStyles('skuDesc');
  5666. showNotification("商品规格-着色成功", 3000);
  5667. break;
  5668. case 'svg-icon-livePrice':
  5669. activateIframeAndModifyStyles('livePrice');
  5670.  
  5671. // 判断“预售信息”,辅助自动输入定金尾款时间
  5672. if (check_isHavePreSaleContent()) {
  5673. buttonClickForAddPreSaleTime();
  5674. }
  5675.  
  5676. showNotification("直播价-着色成功", 3000);
  5677. break;
  5678. case 'svg-icon-liveGameplay':
  5679. activateIframeAndModifyStyles('liveGameplay');
  5680.  
  5681. showNotification("直播玩法-着色成功", 3000);
  5682. break;
  5683. case 'svg-icon-preSetInventory':
  5684. activateIframeAndModifyStyles('preSetInventory');
  5685.  
  5686. showNotification("预设库存-着色成功", 3000);
  5687. break;
  5688. case 'svg-icon-inventory':
  5689. activateIframeAndModifyStyles('inventory');
  5690.  
  5691. showNotification("实际库存-着色成功", 3000);
  5692. break;
  5693. default:
  5694. break;
  5695. }
  5696.  
  5697. // 仅当 idSuffix 不在数组中时才添加
  5698. if (!isMarioShow.includes(idSuffix)) {
  5699. isMarioShow.push(idSuffix);
  5700. }
  5701. });
  5702. } else {
  5703. divWrapper.style.display = 'flex';
  5704. divWrapper.className = 'svg-icon-wrapper-no-data';
  5705. }
  5706.  
  5707. return divWrapper;
  5708. }
  5709.  
  5710. // 查找表格中的目标单元格并添加SVG图标
  5711. function addSVGToSpecificTDs() {
  5712. // 获取 class="card-content-container" 内的表格
  5713. var container = document.querySelector('.card-content-container');
  5714. if (!container) return;
  5715.  
  5716. var table = container.querySelector('table');
  5717. if (!table) return;
  5718.  
  5719. var tbody = table.querySelector('tbody');
  5720. if (!tbody) return;
  5721.  
  5722. // 获取 tbody 内的第二个 tr
  5723. var secondTR = tbody.querySelectorAll('tr')[1]; // 获取第二个 tr
  5724. if (!secondTR) return;
  5725.  
  5726. // 获取第二个 tr 中的所有 td
  5727. var tds = secondTR.querySelectorAll('td');
  5728. var idMap = {
  5729. "商品名": "svg-icon-goodsName",
  5730. "规格": "svg-icon-skuDesc",
  5731. "直播间到手价": "svg-icon-livePrice",
  5732. "优惠方式": "svg-icon-liveGameplay",
  5733. "预设库存": "svg-icon-preSetInventory",
  5734. "现货库存": "svg-icon-inventory"
  5735. }; // 文本内容与ID的映射
  5736.  
  5737. tds.forEach(function (td) {
  5738. // 检查 td 的文本内容是否在目标文本内容列表中
  5739. var span = td.querySelector('.link-node-container > span');
  5740. if (span && idMap.hasOwnProperty(span.textContent.trim())) {
  5741. // 检查是否已经存在封装的 SVG 图标,避免重复添加
  5742. if (!td.querySelector('.svg-icon-wrapper')) {
  5743. // 获取对应的 id
  5744. var id = idMap[span.textContent.trim()];
  5745. // 创建包含 SVG 图标的 div 容器并设置 id
  5746. var svgWrapper = createMarioSVGIconWrapper(id);
  5747. // 将 SVG 容器插入到 span 之后
  5748. span.parentNode.insertAdjacentElement('afterend', svgWrapper);
  5749. }
  5750. }
  5751. });
  5752. }
  5753.  
  5754. // 初始化存储被点击事件的数组
  5755. var isMarioShow = [];
  5756.  
  5757. // 函数:控制每个 divWrapper 的显示状态
  5758. function updateDivWrapperDisplay(isShow) {
  5759. // 获取所有 class 为 'svg-icon-wrapper' 的元素
  5760. const divWrappers = document.querySelectorAll('.svg-icon-wrapper');
  5761.  
  5762. // 遍历所有 divWrapper
  5763. for (const divWrapper of divWrappers) {
  5764. if (isShow) {
  5765. divWrapper.style.display = 'flex';
  5766. } else {
  5767. // 获取该 divWrapper 的 id
  5768. var wrapperId = divWrapper.id;
  5769.  
  5770. // 检查是否存在于 isMarioShow 数组中
  5771. if (isMarioShow.includes(wrapperId.replace('svg-icon-', ''))) {
  5772. divWrapper.style.display = 'flex';
  5773. } else {
  5774. divWrapper.style.display = 'none';
  5775. }
  5776. }
  5777. }
  5778. }
  5779.  
  5780. /*
  5781. 淘宝、天猫主图复制到剪贴板功能
  5782. */
  5783. function createGetTmallPngButton() {
  5784. // 找到匹配的元素的编号
  5785. function findMatchingIndex(wrapperClass, imgClass) {
  5786. for (let i = 0; i < wrapperClass.length; i++) {
  5787. const wrapper = document.querySelector(wrapperClass[i]);
  5788. if (wrapper) {
  5789. const img = wrapper.querySelector(imgClass[i]); // 找到图片元素
  5790. const button = wrapper.querySelector('#button_getTmallPngButton'); // 找到按钮元素
  5791.  
  5792. if (img && !button) {
  5793. return i; // 返回匹配的编号
  5794. } else {
  5795. return -1; // 如果按钮已存在,则返回 -1
  5796. }
  5797. }
  5798. }
  5799. return -1; // 如果没有找到匹配的元素,则返回 -1
  5800. }
  5801.  
  5802.  
  5803. const wrapperClass = ['.itemImgWrap--bUt5RRLT', '.PicGallery--mainPicWrap--juPDFPo', '.mainPicWrap--Ns5WQiHr', '.PicGallery--mainPicWrap--1c9k21r', '.item-gallery-top.item-gallery-prepub2'];
  5804. const imgClass = ['.itemImg--O9S7hs0i', '.PicGallery--mainPic--34u4Jrw', '.mainPic--zxTtQs0P', '.PicGallery--mainPic--1eAqOie', '.item-gallery-top__img'];
  5805.  
  5806. const matchingIndex = findMatchingIndex(wrapperClass, imgClass);
  5807.  
  5808. if (matchingIndex !== -1) {
  5809. addButton(wrapperClass, imgClass, matchingIndex);
  5810. addButton(wrapperClass, imgClass, 0);
  5811. } else {
  5812. // console.error('Wrapper element not found.');
  5813. }
  5814.  
  5815. function addButton(wrapperClass, imgClass, matchingIndex) {
  5816. const wrapper = document.querySelector(wrapperClass[matchingIndex]);
  5817. console.log("wrapper:", wrapper);
  5818.  
  5819. const old_button = wrapper.querySelector('#button_getTmallPngButton'); // 找到按钮元素
  5820. if (old_button) {
  5821. return; // 如果按钮已存在,则直接返回
  5822. }
  5823.  
  5824. if (wrapper) {
  5825. const button = document.createElement('button');
  5826. button.textContent = '复制图片';
  5827. button.id = 'button_getTmallPngButton';
  5828. button.style.cssText = `
  5829. position: absolute;
  5830. top: 50%;
  5831. left: 50%;
  5832. transform: translate(-50%, -50%);
  5833. padding: 5px 20px;
  5834. font-size: 16px;
  5835. background-color: rgba(22, 22, 23, .7);
  5836. color: #fff;
  5837. border: none;
  5838. border-radius: 999px;
  5839. font-family: AlibabaPuHuiTi_2_55_Regular;
  5840. backdrop-filter: saturate(180%) blur(20px); /* 添加模糊效果 */
  5841. -webkit-backdrop-filter: blur(20px); /* 兼容Safari浏览器 */
  5842. text-align: center; /* 文本居中显示 */
  5843. cursor: pointer;
  5844. opacity: 0;
  5845. transition: opacity 0.3s ease, color 0.15s ease, background-color 0.25s ease;;
  5846. z-index: 999;
  5847. `;
  5848.  
  5849. // 控制按钮显示
  5850. wrapper.addEventListener('mouseenter', () => {
  5851. button.style.opacity = '1';
  5852. });
  5853.  
  5854. // 控制按钮隐藏
  5855. wrapper.addEventListener('mouseleave', () => {
  5856. button.style.opacity = '0';
  5857. });
  5858.  
  5859. button.addEventListener('click', async () => {
  5860. const img = wrapper.querySelector(imgClass[matchingIndex]);
  5861. // console.log("img:", img);
  5862. if (img) {
  5863. try {
  5864. const imageUrl = img.src;
  5865. const response = await fetch(imageUrl);
  5866. const blob = await response.blob();
  5867. const image = new Image();
  5868. image.src = URL.createObjectURL(blob);
  5869.  
  5870. image.onload = () => {
  5871. const canvas = document.createElement('canvas');
  5872. const ctx = canvas.getContext('2d');
  5873. canvas.width = image.width;
  5874. canvas.height = image.height;
  5875. ctx.drawImage(image, 0, 0);
  5876. canvas.toBlob(async (blob) => {
  5877. try {
  5878. await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
  5879. showNotification("图片已成功复制到剪贴板", undefined, true);
  5880. button.textContent = '复制成功';
  5881. button.style.backgroundColor = 'rgba(233, 233, 233, .7)'; // 按钮颜色改变
  5882. button.style.color = '#000'; // 按钮文字颜色改变
  5883.  
  5884. setTimeout(() => {
  5885. button.textContent = '复制图片';
  5886. button.style.backgroundColor = 'rgba(22, 22, 23, .7)'; // 按钮颜色恢复
  5887. button.style.color = '#fff'; // 按钮文字颜色恢复
  5888.  
  5889. }, 1500); // 1.5秒后恢复按钮文字
  5890. // alert('Image copied to clipboard!');
  5891. } catch (error) {
  5892. console.error('Failed to copy image to clipboard:', error);
  5893. // alert('Failed to copy image to clipboard.');
  5894. showNotification('图片复制失败!');
  5895. }
  5896. }, 'image/png');
  5897. };
  5898. } catch (error) {
  5899. showNotification('图片复制失败!');
  5900. console.error('Failed to fetch or process image:', error);
  5901. // alert('Failed to copy image to clipboard.');
  5902. }
  5903. } else {
  5904. // alert('Image not found!');
  5905. }
  5906. });
  5907.  
  5908. wrapper.style.position = 'relative'; // 确保按钮在图片上层
  5909. wrapper.appendChild(button);
  5910. }
  5911. }
  5912. }
  5913.  
  5914. /*
  5915. 用于主播排序的辅助函数
  5916. */
  5917. // 封装处理和生成多维数组数据的函数
  5918. function generateJsonFromText(textInput) {
  5919. // 简写与全称的对应表
  5920. const shorthandToFull = {
  5921. "野": "小野",
  5922. "月": "小月",
  5923. "霸": "小霸王",
  5924. "迎": "小迎",
  5925. "京": "京京",
  5926. "祖": "阿祖",
  5927. "凯": "凯子",
  5928. "空": "悟空",
  5929. };
  5930.  
  5931. // 处理文本,去除“+”及其后的内容
  5932. // const processText = (text) => text.split('+')[0].trim();
  5933. const processText = (text) => {
  5934. let result = '';
  5935. // 首先移除“+”及其后的内容
  5936. const firstPart = text.split('+')[0];
  5937.  
  5938. // 遍历firstPart的每个字符
  5939. for (let char of firstPart) {
  5940. // 如果字符是shorthandToFull的键,则添加到结果中
  5941. if (shorthandToFull.hasOwnProperty(char)) {
  5942. result += char;
  5943. }
  5944. }
  5945.  
  5946. // 返回处理后的文本
  5947. return result.trim();
  5948. };
  5949.  
  5950. // 将简写转换为全称
  5951. const getFullNames = (text) => {
  5952. return text.split('').map(ch => shorthandToFull[ch] || ch);
  5953. };
  5954.  
  5955. // 生成多维数组格式数据
  5956. const result = [];
  5957.  
  5958. // 示例输入 [1][0][0]
  5959. // 解释:第一个位置存储多个主播的排列组合,第二个位置0存储主播名字,其余位置存储其rowIndex值,第三个位置用于读取主讲或副讲
  5960. // 获取主讲 resultArray[2][0][0];
  5961. // 获取副讲 resultArray[2][0][1];
  5962. // 获取其列 resultArray[2][i]
  5963.  
  5964. const texts = textInput.trim().split('\n');
  5965.  
  5966. texts.forEach((text, index) => {
  5967. const processedText = processText(text);
  5968. const fullNamesArray = getFullNames(processedText);
  5969.  
  5970. // 查找是否已存在相同的 fullNamesArray
  5971. const existingEntry = result.find(entry => {
  5972. return JSON.stringify(entry[0]) === JSON.stringify(fullNamesArray);
  5973. });
  5974.  
  5975. if (existingEntry) {
  5976. // 如果存在相同的组合,追加索引
  5977. existingEntry.push(index);
  5978. } else {
  5979. // 如果不存在,创建一个新的组合
  5980. result.push([fullNamesArray, index]);
  5981. }
  5982. });
  5983.  
  5984. return result;
  5985. }
  5986.  
  5987. // 页面控制函数
  5988. function itemPageScroll(height, addScroll = true) {
  5989. return new Promise((resolve) => {
  5990. // .rc-virtual-list-holder
  5991. const viewport = document.querySelector('.ag-body-vertical-scroll-viewport'); // 获取页面滚动区域
  5992.  
  5993. if (addScroll) {
  5994. if (height != 0) {
  5995. viewport.scrollTop += height; // 向下滚动一屏
  5996. } else {
  5997. viewport.scrollTop = 0; // 回到顶部
  5998. }
  5999. } else {
  6000. viewport.scrollTop = height; // 直接滚动到指定位置
  6001. }
  6002.  
  6003. console.log('scrolling to', viewport.scrollTop); // 打印当前滚动高度
  6004.  
  6005. // 通过 setTimeout 来模拟等待页面加载完成
  6006. setTimeout(() => {
  6007. resolve(); // 滚动完成后继续执行
  6008. }, 800); // 延迟时间可以根据实际加载时间调整
  6009. });
  6010. }
  6011.  
  6012. // 商品选择函数
  6013. function selectItemForRowIndex(rowIndex, retries = 5, delay = 500) {
  6014. return new Promise((resolve, reject) => {
  6015. // 找到带有指定 row-index 的 div
  6016. const targetDiv = document.querySelector(`div[row-index="${rowIndex}"]`);
  6017.  
  6018. // 在 targetDiv 内查找 col-id="choice" 的元素
  6019. if (targetDiv) {
  6020. const choiceElement = targetDiv.querySelector('div[col-id="choice"]');
  6021.  
  6022. // 在 choiceElement 内找到唯一的 input 元素
  6023. if (choiceElement) {
  6024. const inputElement = choiceElement.querySelector('input');
  6025. if (inputElement) {
  6026. inputElement.click(); // 点击 input 元素
  6027. // console.log(`Selected item for row-index="${rowIndex}"`);
  6028. resolve(); // 选择完成后继续执行
  6029. } else {
  6030. // input 元素未找到的情况
  6031. retryOrReject(`未找到 input 元素在 col-id="choice" 的元素内`);
  6032. }
  6033. } else {
  6034. // console.log(`未找到 col-id="choice" 的元素在 row-index="${rowIndex}" 的 div 内`);
  6035. retryOrReject(`未找到 col-id="choice" 的元素在 row-index="${rowIndex}" div 内`);
  6036. }
  6037. } else {
  6038. // console.log(`未找到 row-index="${rowIndex}" 的 div`);
  6039. retryOrReject(`未找到 row-index="${rowIndex}" div`);
  6040. }
  6041.  
  6042. function retryOrReject(errorMessage) {
  6043. if (retries > 0) {
  6044. setTimeout(() => {
  6045. // 递归调用自己,并减少重试次数
  6046. selectItemForRowIndex(rowIndex, retries - 1, delay).then(resolve).catch(reject);
  6047. }, delay);
  6048. } else {
  6049. reject(errorMessage); // 达到最大重试次数后,返回错误
  6050. }
  6051. }
  6052. });
  6053. }
  6054.  
  6055. // 模拟鼠标点击,激活主播选择框
  6056. // 示例调用:点击 "主讲主播" 的选择框
  6057. // clickSelectByTitle("主讲主播");
  6058.  
  6059. // 示例调用:点击 "副讲主播" 的选择框
  6060. // clickSelectByTitle("副讲主播");
  6061.  
  6062. async function clickSelectByTitle(title, retries = 5, delay = 500) {
  6063. // 重试机制,最多重试 `retries` 次,每次等待 `delay` 毫秒
  6064. for (let i = 0; i < retries; i++) {
  6065. const labelElement = document.querySelector(`label[title="${title}"]`);
  6066.  
  6067. if (labelElement) {
  6068. const parentElement = labelElement.parentElement;
  6069. if (parentElement && parentElement.parentElement) {
  6070. const selectSelector = parentElement.parentElement.querySelector('.ant-select-selector');
  6071.  
  6072. if (selectSelector) {
  6073. // 模拟点击
  6074. const clickEvent = new MouseEvent('mousedown', {
  6075. bubbles: true,
  6076. cancelable: true
  6077. });
  6078. selectSelector.dispatchEvent(clickEvent); // 模拟点击事件
  6079.  
  6080. // console.log(`已激活并点击 ${title} 对应的选择框`);
  6081. return; // 成功找到并点击后直接返回
  6082. } else {
  6083. // console.log(`未找到 ${title} 对应的 .ant-select-selector 元素`);
  6084. }
  6085. } else {
  6086. // console.log(`无法获取到 ${title} 的父元素`);
  6087. }
  6088. } else {
  6089. // console.log(`未找到 title 为 "${title}" 的 label 元素`);
  6090. }
  6091.  
  6092. // 如果没找到,等待一段时间再重试
  6093. if (i < retries - 1) {
  6094. // console.log(`重试 ${i + 1}/${retries} 次后等待 ${delay} 毫秒...`);
  6095. await new Promise(resolve => setTimeout(resolve, delay));
  6096. }
  6097. }
  6098.  
  6099. // 如果所有重试都失败了,抛出一个错误
  6100. throw new Error(`无法找到 title "${title}" 的元素。`);
  6101. }
  6102.  
  6103. // 选择指定的主播
  6104. function selectAnchor(anchor, primary = true) {
  6105. return new Promise((resolve, reject) => {
  6106. // 根据 primary 参数决定目标容器的选择器
  6107. const targetDiv = primary ? '#primaryAnchor_list' : '#assistantAnchor_list';
  6108.  
  6109. // 获取视窗元素
  6110. const viewFather = document.querySelector(targetDiv).parentElement;
  6111. const viewport = viewFather.querySelector('.rc-virtual-list-holder');
  6112.  
  6113. // 定义一个异步函数来执行循环操作
  6114. (async function trySelect() {
  6115. for (let attempt = 0; attempt < 4; attempt++) {
  6116. // 查找目标选项
  6117. const targetOption = `.ant-select-item[title="${anchor}"]`;
  6118. const optionElement = document.querySelector(targetDiv).parentElement.querySelector(targetOption);
  6119.  
  6120. if (optionElement) {
  6121. // 如果找到选项,则点击并完成操作
  6122. optionElement.click();
  6123. // console.log(`已选择 ${anchor} 主播`);
  6124. resolve();
  6125. return; // 结束函数
  6126. }
  6127.  
  6128. // 如果没有找到,滚动视窗
  6129. viewport.scrollTop += 256;
  6130. // console.log(`未找到 ${anchor} 主播,正在尝试第 ${attempt + 1} 次滚动`);
  6131.  
  6132. // 等待一点时间以让页面加载新内容
  6133. await new Promise(r => setTimeout(r, 500));
  6134. }
  6135.  
  6136. // 如果经过多次尝试仍未找到,抛出错误或处理异常
  6137. // console.log(`未能找到 ${anchor} 主播,已尝试 ${4} 次`);
  6138. reject(new Error(`未能找到 ${anchor} 主播`));
  6139. })();
  6140. });
  6141. }
  6142.  
  6143. // 点击“计划主播排班”
  6144. function clickScheduleButton() {
  6145. return new Promise((resolve) => {
  6146. const scheduleButtonClassName = '.ant-btn.css-9fw9up.ant-btn-default.primaryButton___N3z1x'
  6147. clickButton(true, 0, document, scheduleButtonClassName, '计划主播排班');
  6148. resolve();
  6149. });
  6150. }
  6151.  
  6152. // 点击“排班页面”的“确定”按钮
  6153. function clickConformButtonForSchedule() {
  6154. return new Promise((resolve) => {
  6155. const scheduleClassName = '.ant-modal-content';
  6156. const conformButtonClassName = '.ant-btn.css-9fw9up.ant-btn-primary';
  6157.  
  6158. clickButton(true, 0, scheduleClassName, conformButtonClassName);
  6159. resolve();
  6160. });
  6161. }
  6162.  
  6163. let itemBlockHeight = 100; // item 块高度,可根据实际情况调整
  6164.  
  6165. async function processItems() {
  6166. // 返回输入文本中对应的商品个数
  6167. function countAllItemNum(resultArray) {
  6168. var countNum = 0;
  6169.  
  6170. for (var i = 0; i < resultArray.length; i++) {
  6171. countNum += resultArray[i].length - 1;
  6172. }
  6173.  
  6174. // console.log('countNum:', countNum);
  6175. return countNum;
  6176. }
  6177.  
  6178. // 返回当前已经排序的最大商品序号
  6179. function getMaxForScheduledItemIndex() {
  6180. if (scheduledItems.length === 0) {
  6181. return 0;
  6182. }
  6183.  
  6184. // 对已排班的商品序号进行排序
  6185. scheduledItems.sort((a, b) => a - b);
  6186.  
  6187. // 遍历数组,找到最小的缺失序号
  6188. for (let i = 0; i < scheduledItems.length; i++) {
  6189. if (scheduledItems[i] !== i) {
  6190. // console.log('Missing index:', i);
  6191. return i; // 一旦发现某个序号不连续,返回这个序号
  6192. }
  6193. }
  6194.  
  6195. // 如果所有序号都连续,则返回下一个未使用的序号
  6196. return scheduledItems.length;
  6197. }
  6198.  
  6199. let scheduledItems = []; // 已排班的商品序号
  6200. let rounder = 0; // 轮次计数器
  6201.  
  6202. try {
  6203. const elements = document.getElementsByClassName('fontLinkd-[#333_20_20_Bold]');
  6204. const textContent = elements[0].innerText || elements[0].textContent;
  6205. const countItem = parseInt(textContent, 10); // link上的商品总数
  6206.  
  6207. // 原生浏览器弹窗提示
  6208. // const textInput = prompt('请输入主播排班表格内容,一行对应一个商品');
  6209.  
  6210. const textInput = await createDropdownModal(dropdownContainer, '主播排序');
  6211.  
  6212. const resultArray = generateJsonFromText(textInput);
  6213. // console.log(resultArray);
  6214.  
  6215. // 商品数检查
  6216. if (countAllItemNum(resultArray) > countItem) {
  6217. click_itemSort_anchorSort();
  6218. setTimeout(() => {
  6219. showNotification('输入了过多的主播,请检查后重新输入!');
  6220. }, 1500);
  6221.  
  6222. return;
  6223. }
  6224.  
  6225. while (rounder < resultArray.length) {
  6226. if (!itemSort_anchorSort.getDivButtonState()) return; // 跳出循环,如果主播排序未打开,则不执行任何操作
  6227. await itemPageScroll(itemBlockHeight * getMaxForScheduledItemIndex(), false); // 回到合适的位置或许是顶部
  6228.  
  6229. showNotification('正在处理第 ' + (rounder + 1) + '/' + resultArray.length + ' 轮次 ' + loadImageIcon(), 0);
  6230.  
  6231. await new Promise(resolve => setTimeout(resolve, 500)); // 等待500毫秒,可根据需要调整
  6232.  
  6233. let index = 1;
  6234. let checkCount = 0;
  6235.  
  6236. for (let i = getMaxForScheduledItemIndex(); i < countItem; i++, checkCount++) {
  6237. if (!itemSort_anchorSort.getDivButtonState()) return; // 跳出循环,如果主播排序未打开,则不执行任何操作
  6238.  
  6239. if (resultArray[rounder][index] == i) {
  6240. await selectItemForRowIndex(i); // 选择指定的行
  6241. scheduledItems.push(i); // 记录已排班的商品序号
  6242. index++;
  6243. }
  6244.  
  6245. if ((checkCount + 1) % 4 === 0) await itemPageScroll(itemBlockHeight * 4); // 每处理4行,滚动页面
  6246.  
  6247. // console.log('Index:', index, 'Length:', resultArray[rounder].length);
  6248.  
  6249. if (index === resultArray[rounder].length) {
  6250. // console.log('Executing scheduling...');
  6251.  
  6252. await new Promise(resolve => setTimeout(resolve, 500)); // 等待500毫秒,可根据需要调整
  6253. await clickScheduleButton();
  6254. await clickSelectByTitle("主讲主播");
  6255. await selectAnchor(resultArray[rounder][0][0], true);
  6256. await clickSelectByTitle("副讲主播");
  6257. if (resultArray[rounder][0][1]) {
  6258. await selectAnchor(resultArray[rounder][0][1], false);
  6259. }
  6260. await clickConformButtonForSchedule();
  6261.  
  6262. rounder++;
  6263. break; // 跳出循环,继续处理下一个商品
  6264. }
  6265. }
  6266.  
  6267. // 确保整个循环内容都执行完再进入下一次迭代
  6268. await new Promise(resolve => setTimeout(resolve, 500)); // 等待500毫秒,可根据需要调整
  6269. }
  6270. setTimeout(() => {
  6271. click_itemSort_anchorSort();
  6272. }, 1500);
  6273. showNotification('全部处理完成!');
  6274. } catch (error) {
  6275. // 捕获任何异常,并显示错误通知
  6276. click_itemSort_anchorSort();
  6277. setTimeout(() => {
  6278. showNotification('处理过程中出现错误!');
  6279. }, 1500);
  6280. console.error('Error occurred:', error);
  6281. } finally {
  6282. // 可选的: 在所有操作完成后执行清理工作
  6283. // console.log('处理流程结束');
  6284. }
  6285. }
  6286.  
  6287. function click_itemSort_anchorSort() {
  6288. document.getElementById('itemSort_anchorSort_divButton').click();
  6289. }
  6290.  
  6291. // 输入弹窗
  6292. function createDropdownModal(dropdownContainer, titleText) {
  6293. return new Promise((resolve, reject) => {
  6294. // 检查是否已有弹窗存在,如果有则移除
  6295. const existingModal = dropdownContainer.querySelector('.dropdown-modal');
  6296. if (existingModal) {
  6297. dropdownContainer.removeChild(existingModal);
  6298. }
  6299.  
  6300. dropdownButton.style.display = 'none'; // 隐藏按钮
  6301.  
  6302. // 创建弹窗容器
  6303. var dropdownDivModal = document.createElement('div');
  6304. dropdownDivModal.classList.add('dropdown-modal'); // 添加一个类以便于识别
  6305. dropdownDivModal.style.cssText = `
  6306. position: absolute;
  6307. top: 0;
  6308. margin: 14px;
  6309. width: 172px;
  6310. height: 108px;
  6311. background-color: rgba(233, 233, 233, 0.7);
  6312. backdrop-filter: blur(8px) brightness(90%);
  6313. border: 1px solid rgba(255, 98, 0, 0.25);
  6314. border-radius: 10px;
  6315. box-sizing: border-box;
  6316. display: flex;
  6317. flex-direction: column;
  6318. z-index: 3;
  6319. transform-origin: top center;
  6320. `;
  6321.  
  6322. // 创建标题行
  6323. const title = document.createElement('div');
  6324. title.textContent = titleText || '弹窗标题';
  6325. title.style.cssText = `
  6326. padding: 8px 10px;
  6327. font-size: 14px;
  6328. font-weight: bold;
  6329. color: rgb(51, 51, 51);
  6330. border-bottom: 0px solid #ccc;
  6331. text-align: left;
  6332. flex-shrink: 0;
  6333. `;
  6334. dropdownDivModal.appendChild(title);
  6335.  
  6336. // 创建富文本框
  6337. const textarea = document.createElement('textarea');
  6338. textarea.style.cssText = `
  6339. width: calc(100% - 20px);
  6340. background-color: rgba(249, 249, 249, 0.7);
  6341. height: 30px;
  6342. margin: 0px 10px;
  6343. padding: 5px;
  6344. font-size: 12px;
  6345. border: 0px solid #ccc;
  6346. border-radius: 4px;
  6347. resize: none;
  6348. line-height: 1.2;
  6349. box-sizing: border-box;
  6350. flex-grow: 1;
  6351. `;
  6352. dropdownDivModal.appendChild(textarea);
  6353.  
  6354. // 创建按钮容器
  6355. const buttonContainer = document.createElement('div');
  6356. buttonContainer.style.cssText = `
  6357. display: flex;
  6358. justify-content: space-between;
  6359. padding: 8px 10px;
  6360. border-top: 0px solid #ccc;
  6361. flex-shrink: 0;
  6362. `;
  6363.  
  6364. // 创建“取消”按钮
  6365. const cancelButton = document.createElement('button');
  6366. cancelButton.textContent = '取消';
  6367. cancelButton.style.cssText = `
  6368. padding: 3px 8px;
  6369. font-size: 12px;
  6370. color: #fff;
  6371. background-color: #f44336;
  6372. border: none;
  6373. border-radius: 5px;
  6374. cursor: pointer;
  6375. flex-basis: 48%;
  6376. `;
  6377. cancelButton.onclick = () => {
  6378. dropdownContainer.removeChild(dropdownDivModal);
  6379. dropdownButton.style.display = ''; // 恢复按钮
  6380. reject('用户取消了输入');
  6381. };
  6382. buttonContainer.appendChild(cancelButton);
  6383.  
  6384. // 创建“确认”按钮
  6385. const confirmButton = document.createElement('button');
  6386. confirmButton.textContent = '确认';
  6387. confirmButton.style.cssText = `
  6388. padding: 3px 8px;
  6389. font-size: 12px;
  6390. color: #fff;
  6391. background-color: #4CAF50;
  6392. border: none;
  6393. border-radius: 5px;
  6394. cursor: pointer;
  6395. flex-basis: 48%;
  6396. `;
  6397.  
  6398. confirmButton.onclick = () => {
  6399. const textInput = textarea.value;
  6400. dropdownContainer.removeChild(dropdownDivModal);
  6401. dropdownButton.style.display = ''; // 恢复按钮
  6402. resolve(textInput); // 在确认时返回输入的内容
  6403. };
  6404. buttonContainer.appendChild(confirmButton);
  6405.  
  6406. dropdownDivModal.appendChild(buttonContainer);
  6407.  
  6408. dropdownContainer.appendChild(dropdownDivModal);
  6409. });
  6410. }
  6411.  
  6412. /*
  6413. 补贴生成
  6414. */
  6415. function createSubsidyTextButton(isActivated = sonMain_subsidyText.getSwitchState()) {
  6416. if (!isActivated) {
  6417. return;
  6418. }
  6419.  
  6420. const targetClass = '[class*="ant-space"][class*="css-9fw9up"][class*="ant-space-horizontal"][class*="ant-space-align-center"]';
  6421.  
  6422. const subsidyText_observer = new MutationObserver((mutationsList, observer) => {
  6423. for (let mutation of mutationsList) {
  6424. if (mutation.type === 'childList') {
  6425. const targetElement = document.querySelector(targetClass);
  6426. if (targetElement) {
  6427. if (document.querySelector('.subsidyText')) {
  6428. subsidyText_observer.disconnect();
  6429. return;
  6430. }
  6431.  
  6432. var subsidyText = document.createElement('div');
  6433. subsidyText.classList.add('ant-space-item');
  6434. subsidyText.style.display = 'none';
  6435.  
  6436. var subsidyTextButton = document.createElement('button');
  6437. subsidyTextButton.textContent = '补贴生成';
  6438. subsidyTextButton.classList.add('ant-btn', 'css-9fw9up', 'ant-btn-default', 'subsidyText');
  6439. subsidyText.appendChild(subsidyTextButton);
  6440.  
  6441. targetElement.insertBefore(subsidyText, targetElement.firstChild);
  6442.  
  6443. subsidyTextButton.addEventListener('click', () => generateSubsidyText());
  6444.  
  6445. subsidyText_observer.disconnect();
  6446. break;
  6447. }
  6448. }
  6449. }
  6450. });
  6451.  
  6452. subsidyText_observer.observe(document.body, {
  6453. childList: true,
  6454. subtree: true
  6455. });
  6456. }
  6457.  
  6458. // 在页面上创建按钮
  6459. createSubsidyTextButton();
  6460.  
  6461. async function generateSubsidyText() {
  6462. if (document.querySelector('.dropdown-modal')) {
  6463. return; // 如果弹窗已存在,则不执行任何操作
  6464. }
  6465.  
  6466. // 创建开关容器元素
  6467. const switchesContainer = document.createElement('div');
  6468. switchesContainer.classList.add('flex', 'items-center', 'justify-between', 'pb-12');
  6469. switchesContainer.style.cssText = 'position: fixed; top: 60px; right: 300px; transform: translateX(-50%); z-index: 9999; width: 200px;';
  6470. if (isTableCardURL()) {
  6471. document.body.appendChild(switchesContainer);
  6472. }
  6473.  
  6474. const dropdownContainer = document.createElement('div');
  6475. dropdownContainer.style.cssText = 'position: relative; display: inline-block;';
  6476. switchesContainer.appendChild(dropdownContainer);
  6477.  
  6478. // 调起浏览器原生的输入框,要求用户输入1~999之间的数字
  6479. let red_value = prompt("请输入1到999之间的数字:");
  6480. // let red_value = await createDropdownModal(dropdownContainer, '补贴生成');
  6481.  
  6482. // 检查输入是否为有效的数字,并且在1到999之间
  6483. if (red_value !== null) { // 用户没有点击取消
  6484. red_value = parseInt(red_value, 10); // 将输入转换为整数
  6485. if (isNaN(red_value) || red_value < 1 || red_value > 999) {
  6486. alert("请输入1到999之间的有效数字!");
  6487. // 递归调用函数,让用户重新输入
  6488. generateSubsidyText();
  6489. } else {
  6490. console.log("输入的有效数字是:", red_value);
  6491. // 继续处理
  6492. testRedPacket = {}; // 初始化红包袋子
  6493.  
  6494. const new_docText = new DocText();
  6495. const itemId = new_docText.getCurrentProductId(); // 获取当前商品 ID
  6496. testRedPacket[itemId] = red_value; // 记录红包袋子
  6497.  
  6498. new_docText.addDocText();
  6499. }
  6500. } else {
  6501. console.log("用户取消了输入");
  6502. }
  6503. }
  6504.  
  6505. // 测试使用的红包袋子
  6506. let testRedPacket = {
  6507.  
  6508. };
  6509.  
  6510. // 红包补贴生成器
  6511. // debugger;
  6512. // DocText 类
  6513. class DocText {
  6514. constructor(idName = 'livePrice', redPacket = testRedPacket) {
  6515. this.idName = idName;
  6516. this.redPacket = redPacket;
  6517. }
  6518.  
  6519. // 获取 livePrice 栏目的文本内容
  6520. getDocText() {
  6521. const livePriceDoc = document.getElementById(this.idName); // 获取 livePrice 栏目
  6522.  
  6523. if (livePriceDoc) {
  6524. const liveIframe = livePriceDoc.querySelector('iframe')
  6525. if (liveIframe) {
  6526. const liveIframeDocument = liveIframe.contentDocument || liveIframe.contentWindow.document;
  6527.  
  6528. const body = liveIframeDocument.body;
  6529.  
  6530. if (body && !isNotIframeChineseName(body.innerText)) {
  6531. // console.log("getDocText:", body.innerText); // debug
  6532. return body.innerText;
  6533. }
  6534. }
  6535.  
  6536. const currentHTML = livePriceDoc.innerText;
  6537.  
  6538. // console.log("getDocText:", currentHTML); // debug
  6539.  
  6540. return currentHTML; // 返回 livePrice 栏目的文本内容
  6541. }
  6542. }
  6543.  
  6544. // 更新 livePrice 栏目的文本内容
  6545. addDocText(text = this.preduceSubsidy()) {
  6546. console.log('addDocText:', text); // debug
  6547.  
  6548. const liveIframe = qyeryIframeForId(this.idName);
  6549. if (liveIframe) {
  6550. const liveIframeDocument = liveIframe.contentDocument || liveIframe.contentWindow.document;
  6551.  
  6552. const body = liveIframeDocument.body;
  6553.  
  6554. // 获取当前的 body 内的 HTML 内容
  6555. // const currentHTML = body.innerHTML;
  6556.  
  6557. // 在当前 HTML 内容后面添加换行符和 "预售" 二字
  6558. const updatedHTML = text;
  6559.  
  6560. // console.log('updatedHTML:', updatedHTML); // debug
  6561. // 将更新后的 HTML 设置回 body 内
  6562. body.innerHTML += updatedHTML;
  6563. }
  6564. }
  6565.  
  6566. // 按换行符分割字符串
  6567. splitStringByNewline(text = this.getDocText()) {
  6568. // console.log('splitStringByNewline:', text); // debug
  6569. return text.split('\n');
  6570. }
  6571.  
  6572. // 过滤数组,保留匹配正则表达式或包含“到手”或“平均”的元素
  6573. filterContent(arr = this.splitStringByNewline()) {
  6574. // console.log('filterContent:', arr); // debug
  6575. const priceGameplayRegex = /^(.*:)?([\d\.]+[wW+]?)元?$/;
  6576.  
  6577. // 过滤数组,保留匹配正则表达式或包含“到手”或“平均”的元素
  6578. return arr.filter(item => {
  6579. return priceGameplayRegex.test(item) || item.includes('到手') || item.includes('平均');
  6580. });
  6581. }
  6582.  
  6583. // 整理数组,将价格、个数、均价分组
  6584. organizeContent(arr = this.filterContent()) {
  6585. const priceGameplayRegex = /^(.*:)?([\d\.]+[wW+]?)元?$/;
  6586. let result = [];
  6587. let currentGroup = [];
  6588.  
  6589. arr.forEach(item => {
  6590. if (priceGameplayRegex.test(item)) {
  6591. if (currentGroup.length > 0) {
  6592. // 如果当前组已经有数据,则先将其加入结果数组
  6593. result.push(currentGroup);
  6594. currentGroup = []; // 清空当前组
  6595. }
  6596. currentGroup.push(item); // 添加当前满足条件的元素
  6597. } else if (item.trim() !== '') { // 只添加非空行
  6598. currentGroup.push(item);
  6599. }
  6600. });
  6601.  
  6602. // 添加最后一个组,如果有的话
  6603. if (currentGroup.length > 0) {
  6604. result.push(currentGroup);
  6605. }
  6606.  
  6607. // console.log('organizeContent:', result); // debug
  6608. return result;
  6609. }
  6610.  
  6611. // 提取关键内容
  6612. extractPriceAndQuantity(input) {
  6613. // 用于匹配整个字符串的正则表达式
  6614. const priceGameplayRegex = /^(.*:)?([\d\.]+[wW+]?)元?$/;
  6615. // 用于匹配“:”之前部分的正则表达式
  6616. const quantityRegex = /拍(一|二|三|四|五|六|七|八|九|十|十一|十二|十三|十四|十五|[1-9]?[0-9])件?/;
  6617.  
  6618. // 首先匹配整个字符串
  6619. const match = input.match(priceGameplayRegex);
  6620. // console.log('match:', match); // debug
  6621.  
  6622. if (match && match[0]) {
  6623. // 提取“:”之前的部分
  6624. const beforeColon = match[0].split(':')[0];
  6625.  
  6626. // 检查“:”之前的部分是否满足quantityRegex
  6627. const quantityMatch = beforeColon.match(quantityRegex);
  6628.  
  6629. if (quantityMatch && quantityMatch[1]) {
  6630. // 获取数量部分
  6631. let quantity = quantityMatch[1];
  6632.  
  6633. // 将中文数字转换为阿拉伯数字
  6634. const chineseToArabic = {
  6635. '一': 1, '二': 2, '三': 3, '四': 4, '五': 5,
  6636. '六': 6, '七': 7, '八': 8, '九': 9, '十': 10,
  6637. '十一': 11, '十二': 12, '十三': 13, '十四': 14, '十五': 15
  6638. };
  6639.  
  6640. if (chineseToArabic[quantity]) {
  6641. quantity = chineseToArabic[quantity];
  6642. } else {
  6643. quantity = parseInt(quantity, 10); // 如果已经是阿拉伯数字,直接转换
  6644. }
  6645.  
  6646. // 返回数量和价格内容
  6647. return {
  6648. price: match[2].trim(),
  6649. quantity: quantity,
  6650. prefix: match[1],
  6651. };
  6652. } else {
  6653. return {
  6654. price: match[2].trim(),
  6655. quantity: 1,
  6656. prefix: match[1],
  6657. };
  6658. }
  6659. }
  6660.  
  6661. // 如果没有匹配到或捕获组为空,则返回空对象或其他默认值
  6662. return {
  6663. price: input,
  6664. quantity: 1,
  6665. };
  6666. }
  6667.  
  6668. calculateMiniUint_avgPrice(input, subsidyDiscount) {
  6669. // 定义正则表达式来匹配数值
  6670. const regex = /([\d\.]+)/;
  6671.  
  6672. // 使用正则表达式进行匹配
  6673. const match = input.match(regex);
  6674.  
  6675. if (match) {
  6676. // 提取捕获组中的数值
  6677. const numberPart = match[1];
  6678.  
  6679. // 将数值转换为浮点数并乘以2
  6680. const doubledValue = parseFloat(numberPart) * subsidyDiscount;
  6681.  
  6682. // 格式化新的数值为两位小数
  6683. const formattedValue = doubledValue.toFixed(2).replace(/\.?0+$/, "");
  6684.  
  6685. // 替换原字符串中的数值部分
  6686. const output = input.replace(numberPart, formattedValue);
  6687.  
  6688. return output;
  6689. } else {
  6690. // 如果没有找到匹配项,返回错误信息或合适的默认值
  6691. return input + '*' + subsidyDiscount + '==';
  6692. }
  6693. }
  6694.  
  6695. // 获取当前商品id
  6696. getCurrentProductId() {
  6697. return document.getElementById('cureentItem_upLiveId').innerText;
  6698. }
  6699.  
  6700. // 获取当前商品红包
  6701. getCurrentProductRedPacket() {
  6702. const productId = this.getCurrentProductId();
  6703. return this.redPacket[productId] || 0;
  6704. }
  6705.  
  6706. // 生成补贴文本
  6707. generateSubsidyText(redPacketValue, originalPrice, quantity = 1, prefix = '', miniUint = '', miniUint_avgPrice = '') {
  6708. // console.log("generateSubsidyText:", redPacketValue, originalPrice, quantity, prefix, miniUint_avgPrice); // debug
  6709. // 补贴后的价格
  6710. var subsidyedPrice = originalPrice - (redPacketValue * quantity);
  6711. subsidyedPrice = subsidyedPrice.toFixed(2).replace(/\.?0+$/, ""); // 保留两位小数,去除末尾的0
  6712. // 补贴折扣力度
  6713. var subsidyDiscount = (subsidyedPrice / originalPrice);
  6714.  
  6715. // 生成补贴文本
  6716. var subsidyText = `
  6717. <span style="color:${colorLibrary.blue};" data-mce-style="color:${colorLibrary.blue};">${prefix}</span>
  6718. <span style="color:${colorLibrary.red};font-size: 18px;" data-mce-style="color:${colorLibrary.red};font-size: 18px;">补贴${redPacketValue * quantity}</span>
  6719. <br><span style="color:${colorLibrary.red};" data-mce-style="color:${colorLibrary.red};">相当于到手${subsidyedPrice}</span>
  6720. `;
  6721.  
  6722. if (miniUint && miniUint_avgPrice) {
  6723. // 更新 miniUint_avgPrice
  6724. var new_miniUint_avgPrice = this.calculateMiniUint_avgPrice(miniUint_avgPrice, subsidyDiscount);
  6725.  
  6726. var avgPriceText = `
  6727. <br><br><span style="color:${colorLibrary.red};" data-mce-style="color:${colorLibrary.red};">${miniUint}</span><br>
  6728. <span>补贴后${new_miniUint_avgPrice}</span>
  6729. `;
  6730.  
  6731. subsidyText += avgPriceText;
  6732. }
  6733.  
  6734. return subsidyText;
  6735. }
  6736.  
  6737. isOne_generateSubsidyText(redPacketValue, originalPrice, quantity = 1, prefix = '', miniUint = '', miniUint_avgPrice = '', isOne_quantity_flag = false) {
  6738. // 补贴后的价格
  6739. var subsidyedPrice = originalPrice - (redPacketValue * quantity);
  6740. subsidyedPrice = subsidyedPrice.toFixed(2).replace(/\.?0+$/, ""); // 保留两位小数,去除末尾的0
  6741. // 补贴折扣力度
  6742. var subsidyDiscount = (subsidyedPrice / originalPrice);
  6743.  
  6744. if (isOne_quantity_flag === true) {
  6745. // 生成补贴文本
  6746. var subsidyText = `
  6747. <span style="color:${colorLibrary.red};font-size: 18px;" data-mce-style="color:${colorLibrary.red};font-size: 18px;">补贴${redPacketValue * quantity}</span>
  6748. <br><span style="color:${colorLibrary.red};" data-mce-style="color:${colorLibrary.red};">相当于到手</span>
  6749. <br><span style="color:${colorLibrary.blue};" data-mce-style="color:${colorLibrary.blue};">${prefix}</span>
  6750. <span style="color:${colorLibrary.red};" data-mce-style="color:${colorLibrary.red};">${subsidyedPrice}</span>
  6751. `;
  6752. } else {
  6753. var subsidyText = `
  6754. <br><span style="color:${colorLibrary.blue};" data-mce-style="color:${colorLibrary.blue};">${prefix}</span>
  6755. <span style="color:${colorLibrary.red};" data-mce-style="color:${colorLibrary.red};">${subsidyedPrice}</span>
  6756. `;
  6757. }
  6758.  
  6759. return subsidyText;
  6760. }
  6761.  
  6762. preduceSubsidy(arr = this.organizeContent(), redPacket = this.redPacket) {
  6763. console.log("arr:", arr); // debug
  6764. var new_arr = []; // 存储提取数量信息的数组,包含:价格、数量、前缀文本
  6765. var indexArr = getIndexArr_togetherAndAvgPrice(arr); // “到手共”与“平均”文本的索引数组
  6766. var productId = this.getCurrentProductId(); // 获取当前商品id
  6767. var redPacketValue = redPacket[productId] || 0; // 获取当前商品红包值
  6768.  
  6769. console.log("indexArr:", indexArr); // debug
  6770.  
  6771. // 预处理数组内容
  6772. for (var i = 0; i < arr.length; i++) {
  6773. // 处理每一组内容
  6774. if (arr[i] && Array.isArray(arr[i]) && arr[i].length > 0) {
  6775. new_arr.push(this.extractPriceAndQuantity(arr[i][0]));
  6776. }
  6777. }
  6778.  
  6779. // 文本生成器(传入:数组、提取数量信息的数组、“到手共”与“平均”文本的索引数组、当前商品红包值)
  6780. const len = arr.length; // 数组长度
  6781. var text = `
  6782. <span>----------------</span><br>
  6783. `; // 存储生成的文本
  6784.  
  6785. var isOne_quantity_flag = isOne_quantityAll(new_arr) && len > 1; // 判断是否全部数量为1
  6786.  
  6787. if (isOne_quantity_flag) {
  6788. for (var i = 0; i < len; i++) {
  6789. var isOne = i === 0;
  6790.  
  6791. if (indexArr && Array.isArray(indexArr[i]) && indexArr[i].length === 2) {
  6792. text += this.isOne_generateSubsidyText(redPacketValue, new_arr[i].price, new_arr[i].quantity, new_arr[i].prefix, arr[i][indexArr[i][0]], arr[i][indexArr[i][1]], isOne);
  6793. } else {
  6794. text += this.isOne_generateSubsidyText(redPacketValue, new_arr[i].price, new_arr[i].quantity, new_arr[i].prefix, '', '', isOne);
  6795. }
  6796. }
  6797. } else {
  6798. for (var i = 0; i < len; i++) {
  6799. if (indexArr && Array.isArray(indexArr[i]) && indexArr[i].length === 2) {
  6800. text += this.generateSubsidyText(redPacketValue, new_arr[i].price, new_arr[i].quantity, new_arr[i].prefix, arr[i][indexArr[i][0]], arr[i][indexArr[i][1]]);
  6801. } else {
  6802. text += this.generateSubsidyText(redPacketValue, new_arr[i].price, new_arr[i].quantity, new_arr[i].prefix);
  6803. }
  6804. if (i < len - 1) {
  6805. text += '<br><br>';
  6806. }
  6807. }
  6808. }
  6809.  
  6810. // // debug 输出
  6811. // var testText = '';
  6812. // testText = isOne_quantityAll(new_arr);
  6813. // console.log("debug-testText:", testText);
  6814.  
  6815. // 判断是否都是独立的sku
  6816. function isOne_quantityAll(new_arr) {
  6817. for (var i = 0; i < new_arr.length; i++) {
  6818. if (new_arr[i].quantity !== 1) {
  6819. return false;
  6820. }
  6821. }
  6822. return true;
  6823. }
  6824.  
  6825. // 获取最后一个平均到手价在 arr 中的 index
  6826. function getLastAvgPrice(son_arr) {
  6827. if (!Array.isArray(son_arr)) return null;
  6828.  
  6829. var index = -1;
  6830. for (var i = 0; i < son_arr.length; i++) {
  6831. if (son_arr[i].includes('平均')) {
  6832. index = i;
  6833. }
  6834. }
  6835. return index === -1 ? null : index;
  6836. }
  6837.  
  6838. // 获取最后一个到手共在 arr 中的 index
  6839. function getLastTogether(son_arr) {
  6840. if (!Array.isArray(son_arr)) return null;
  6841.  
  6842. var endIndex = getLastAvgPrice(son_arr);
  6843. if (endIndex === null || endIndex < 2) return null;
  6844.  
  6845. var index = -1;
  6846. for (var i = 0; i < endIndex; i++) {
  6847. if (son_arr[i].includes('到手')) {
  6848. index = i;
  6849. }
  6850. }
  6851. return index === -1 ? null : index;
  6852. }
  6853.  
  6854. // 获取到手共和平均到手价在 arr 中的 index,返回二维数组
  6855. function getIndexArr_togetherAndAvgPrice(arr) {
  6856. var indexArr = [];
  6857.  
  6858. for (var i = 0; i < arr.length; i++) {
  6859. var index_together = getLastTogether(arr[i]);
  6860. var index_avgPrice = getLastAvgPrice(arr[i]);
  6861.  
  6862. if (index_together !== null && index_avgPrice !== null) {
  6863. indexArr[i] = [index_together, index_avgPrice];
  6864. }
  6865. }
  6866.  
  6867. return indexArr;
  6868. }
  6869.  
  6870. return text; // 函数最终返回
  6871. }
  6872. }
  6873.  
  6874. function findGoodsByShopName(shopName = clipboardHistory, goodsDict = goodsUrlCheckArray) {
  6875. // console.log('findGoodsByShopName:', shopName); // debug
  6876. let result = ["辛苦确定下以下挂链链接是否需要更换!\n"];
  6877. var date = GM_getValue('titlePrint_extractedDate', '')
  6878.  
  6879. var i = 1;
  6880. for (const upLiveId in goodsDict) {
  6881. const goodsInfo = goodsDict[upLiveId];
  6882. if (goodsInfo.shopName === shopName) {
  6883. result.push(`${i} \[${isLinkTrue(goodsInfo.weight)}\]-${filterBadGoodName(goodsInfo.sessionGoodsName)}:${goodsInfo.liveLink}`);
  6884. i++;
  6885. }
  6886. }
  6887.  
  6888. if (i === 1) {
  6889. // 没有找到对应店铺
  6890. result = [];
  6891. showNotification('未找到对应店铺,请检查剪切板是否正确!');
  6892. return result;
  6893. }
  6894.  
  6895. result.push(`\n${shopName}】挂链日期:${date}`);
  6896.  
  6897. // console.log('findGoodsByShopName:', result.join('\n')); // debug
  6898. showNotification(`${shopName}的挂链链接已复制到剪切板`);
  6899. GM_setClipboard(result.join('\n'));
  6900. return result;
  6901.  
  6902. function isLinkTrue(weight) {
  6903. if (weight === 0) {
  6904. return '未确认';
  6905. } else {
  6906. return '已确认';
  6907. }
  6908. }
  6909.  
  6910. function filterBadGoodName(goodsName) {
  6911. // 使用正则表达式去除换行符和“预售”信息
  6912. const cleanedName = goodsName.replace(/(\r\n|\n|\r|预售)/g, '');
  6913. return cleanedName.trim(); // 去除首尾的空白字符
  6914. }
  6915. }
  6916.  
  6917. function addDefaultButtonForHomePage() {
  6918. var buttonName = '完整复制';
  6919. var buttonId = 'allCopyButton';
  6920. const smartCopyButton = createDefaultButton(buttonId, buttonName, () => {
  6921. findGoodsByShopName();
  6922. });
  6923.  
  6924. // 找到搜索栏目
  6925. const searchToolBar = document.querySelector('.ant-pro-table-list-toolbar-left');
  6926. const scButton = document.getElementById(buttonId);
  6927. if (searchToolBar) {
  6928. if (!scButton) {
  6929. searchToolBar.appendChild(smartCopyButton);
  6930. } else {
  6931. if (checkActiveTab_GuaLian() && sonMain_linkAllCopySwitch.getSwitchState()) {
  6932. scButton.style.display = '';
  6933. } else {
  6934. scButton.style.display = 'none';
  6935. }
  6936. }
  6937. }
  6938. }
  6939.  
  6940. function createDefaultButton(id = 'testButton', text = '测试按钮', clickHandler) {
  6941. // 创建按钮元素
  6942. const button = document.createElement('button');
  6943. button.type = 'button';
  6944. button.id = id;
  6945. button.className = 'ant-btn css-9fw9up ant-btn-default';
  6946.  
  6947. // 创建按钮内部的文本元素
  6948. const span = document.createElement('span');
  6949. span.textContent = text;
  6950. button.appendChild(span);
  6951.  
  6952. // 绑定点击事件处理函数
  6953. if (typeof clickHandler === 'function') {
  6954. button.addEventListener('click', clickHandler);
  6955. }
  6956.  
  6957. return button;
  6958. }
  6959.  
  6960. /*
  6961. 自动排序功能块
  6962. */
  6963. let autoSortNum = ''; // 自动排序值
  6964. let AutoSortState = false; // 自动排序状态
  6965.  
  6966. class AutoSortItem {
  6967. sortTable = []; // 使用数组来存储排序信息
  6968. sortedIndex = []; // 使用数组存储已经排序的索引
  6969.  
  6970. constructor(text) {
  6971. this.text = text;
  6972. this.arr = this.changeOrderText(text);
  6973. this.lastMiniIndex = 0; // 记录上一次的最小连续数字索引
  6974. }
  6975.  
  6976. // 将输入的文本转换为数组
  6977. changeOrderText(text = this.text) {
  6978. // 输入样例:3,4,2,1,5;
  6979. // 预处理-清除空格、转换中文逗号
  6980. text = text.replace(/\s/g, '').replace(/,/g, ',');
  6981. // 预处理-分割字符串
  6982. const arr = text.split(',');
  6983. console.log('changeOrderText:', arr); // debug
  6984.  
  6985. return arr;
  6986. }
  6987.  
  6988. // 返回数组的最小连续数字-从 1 开始
  6989. findCurrentSmallestNumber(arr) {
  6990. if (arr.length === 0) {
  6991. return 0;
  6992. }
  6993.  
  6994. // 将字符串数组转换为数字数组
  6995. const numArr = arr.map(Number);
  6996.  
  6997. // 将数组转换为Set,以便快速查找
  6998. const set = new Set(numArr);
  6999.  
  7000. let currentSmallest = 1;
  7001.  
  7002. // 查找当前最小的连续数
  7003. while (set.has(currentSmallest)) {
  7004. currentSmallest++;
  7005. }
  7006.  
  7007. return currentSmallest - 1;
  7008. }
  7009.  
  7010. // 初始化
  7011. initSortItem() {
  7012. this.sortTable = [];
  7013. this.sortedIndex = [];
  7014. this.lastMiniIndex = 0;
  7015. autoSortNum = '';
  7016. }
  7017.  
  7018. // 返回与id对应的权重序号
  7019. async getItemTable(goodsDict = goodsUrlCheckArray) {
  7020. showNotification('正在更新当前信息...', 5000);
  7021. let className = '.ant-btn.css-9fw9up.ant-btn-primary';
  7022. clickButton(true, 100, undefined, className);
  7023. await new Promise(resolve => setTimeout(resolve, 4000)); // 根据实际情况调整等待时间
  7024.  
  7025. let sortTable = [];// 存储排序信息
  7026.  
  7027. // 延迟4秒后,预处理数据
  7028. try {
  7029. showNotification('即将开始自动排序...', 0);
  7030. const ids = Object.keys(goodsDict); // 获取 goodsDict 的键
  7031. for (let i = 0; i < ids.length; i++) {
  7032. const upLiveId = ids[i].toString();
  7033. let weight = this.arr[i]; // 预设权重
  7034. if (weight === undefined) {
  7035. weight = 999;
  7036. }
  7037. sortTable.push([upLiveId, weight]); // 使用 push 方法添加键值对
  7038. }
  7039. } catch (error) {
  7040. showNotification('处理 “商品信息” 时发生错误', 0);
  7041. console.error("处理 goodsDict 时发生错误:", error);
  7042. }
  7043.  
  7044. console.log('getItemTable:', sortTable); // debug
  7045.  
  7046. return sortTable;
  7047. }
  7048.  
  7049. async startSort() {
  7050. this.initSortItem(); // 初始化排序信息
  7051. AutoSortState = true; // 开启自动排序状态
  7052.  
  7053. this.sortTable = await this.getItemTable(); // 更新商品信息
  7054.  
  7055. for (let i = 0; i < this.sortTable.length; i++) {
  7056. showNotification(`正在处理第 ${i + 1}/${this.sortTable.length} 个商品...`, 0);
  7057. // 计算需要滚动的商品块个数
  7058. let scrollNum = this.findCurrentSmallestNumber(this.sortedIndex) - this.lastMiniIndex;
  7059. console.log('scrollNum:', scrollNum); // debug
  7060. // itemPageScroll(itemBlockHeight * scrollNum); // 向下滚动
  7061. this.lastMiniIndex = this.findCurrentSmallestNumber(this.sortedIndex); // 更新 lastMiniIndex
  7062. console.log('lastMiniIndex:', this.lastMiniIndex); // debug
  7063.  
  7064. // 获取将要执行的索引的商品块
  7065. const index = findItemIdForArr(this.sortTable[i][0]); // 找到索引对应的商品块
  7066. console.log('index:', index); // debug
  7067. const weight = this.sortTable[i][1]; // 预设权重
  7068. console.log('weight:', weight); // debug
  7069. const preWeight = weight * 10;// 输入权重 (默认值 * 10)
  7070. console.log('preWeight:', preWeight); // debug
  7071.  
  7072. autoSortNum = preWeight.toString(); // 自动排序值
  7073.  
  7074. click_autoInputForIndex(index); // 输入索引
  7075. console.log('click_autoInputForIndex:', index); // debug
  7076. this.sortedIndex.push(weight); // 记录已经排序的索引
  7077. console.log('sortedIndex:', this.sortedIndex); // debug
  7078.  
  7079. if (i + 1 >= this.sortTable.length) {
  7080. break;
  7081. }
  7082.  
  7083. await new Promise(resolve => setTimeout(resolve, 3000)); // 等待 3 秒
  7084. console.log('等待 3 秒'); // debug
  7085. await waitForPageToLoad(); // 等待页面加载完成
  7086. console.log('等待页面加载完成'); // debug
  7087. await new Promise(resolve => setTimeout(resolve, 1000)); // 继续等待 1 秒
  7088. console.log('继续等待 1 秒'); // debug
  7089. }
  7090.  
  7091. console.log('排序完成!');
  7092. showNotification('排序完成!');
  7093. this.initSortItem(); // 重置排序信息
  7094. AutoSortState = false; // 关闭自动排序状态
  7095.  
  7096. const button = document.getElementById('itemSort_autoSort_divButton');
  7097. if (button) {
  7098. button.click(); // 点击按钮关闭自动排序
  7099. }
  7100. }
  7101. }
  7102.  
  7103. /*
  7104. 分词器加载
  7105. */
  7106. // 定义一个函数用于异步加载脚本并初始化分词器
  7107. function initSegmentit(url) {
  7108. // 动态加载脚本
  7109. const loadScript = (url, callback) => {
  7110. const script = document.createElement('script');
  7111. script.src = url;
  7112. script.onload = () => callback(null); // 使用 null 表示没有错误
  7113. script.onerror = () => callback(new Error('Failed to load script: ' + url));
  7114. document.head.appendChild(script);
  7115. };
  7116.  
  7117. // 加载脚本并初始化分词器
  7118. loadScript(url, (err) => {
  7119. if (err) {
  7120. console.error(err.message);
  7121. return;
  7122. }
  7123.  
  7124. // 确保页面完全加载后再执行脚本
  7125. window.addEventListener('load', function () {
  7126. // 检查 Segmentit 对象是否已定义
  7127. if (typeof Segmentit === 'undefined') {
  7128. console.error('Segmentit is not defined. Please check if the library is loaded correctly.');
  7129. } else {
  7130. console.log('Segmentit is loaded:', segmenter); // debug
  7131. }
  7132. });
  7133. });
  7134. }
  7135.  
  7136. // 调用函数并传入 URL 和希望使用的全局变量名称
  7137. initSegmentit('https://cdn.jsdelivr.net/npm/segmentit@2.0.3/dist/umd/segmentit.js');
  7138.  
  7139. let debug_segmentit = false; // 是否开启分词器调试模式
  7140. /*
  7141. 分词器加载完毕
  7142. */
  7143. const testText_1 = '珍珠粉黑灵芝紧致白面膜组合';
  7144. const testText_2 = '1g+5g珍珠粉黑灵芝紧致白面膜组合';
  7145.  
  7146. function segmentText(text, returnSentence = false) {
  7147. // 实例化分词器
  7148. const segmentit = Segmentit.useDefault(new Segmentit.Segment());
  7149. // 获取分词后的对象
  7150. const result = segmentit.doSegment(text);
  7151. // 输出分词后的文本
  7152.  
  7153. let sentence = ''; // 存储分词后的文本
  7154.  
  7155. if (returnSentence) {
  7156. // 返回分词后的句子
  7157. sentence = result.map(item => item.w).join(' ');
  7158. } else {
  7159. // 返回分词后的数组
  7160. sentence = result.map(item => item.w);
  7161. }
  7162.  
  7163. console.log(sentence);
  7164. return sentence;
  7165. }
  7166.  
  7167. function calcSimilarity(title1, title2) {
  7168. // 将分词结果转换为单词数组
  7169. const wordArray1 = segmentText(title1);
  7170. const wordArray2 = segmentText(title2);
  7171.  
  7172. // 构建词汇表
  7173. const vocabulary = new Set([...wordArray1, ...wordArray2]);
  7174. const vocabArray = Array.from(vocabulary);
  7175.  
  7176. // 构建向量
  7177. function createVector(wordArray, vocabArray) {
  7178. const vector = new Array(vocabArray.length).fill(0);
  7179. for (let i = 0; i < wordArray.length; i++) {
  7180. const index = vocabArray.indexOf(wordArray[i]);
  7181. if (index !== -1) {
  7182. vector[index]++;
  7183. }
  7184. }
  7185. return vector;
  7186. }
  7187.  
  7188. const vector1 = createVector(wordArray1, vocabArray);
  7189. const vector2 = createVector(wordArray2, vocabArray);
  7190.  
  7191. // 计算余弦相似度
  7192. function cosineSimilarity(vec1, vec2) {
  7193. let dotProduct = 0;
  7194. let normA = 0;
  7195. let normB = 0;
  7196.  
  7197. for (let i = 0; i < vec1.length; i++) {
  7198. dotProduct += vec1[i] * vec2[i];
  7199. normA += vec1[i] * vec1[i];
  7200. normB += vec2[i] * vec2[i];
  7201. }
  7202.  
  7203. if (normA === 0 || normB === 0) {
  7204. return 0; // 防止除以零
  7205. }
  7206.  
  7207. return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
  7208. }
  7209.  
  7210. const similarity = cosineSimilarity(vector1, vector2);
  7211. console.log('calcSimilarity:', similarity); // debug
  7212. return similarity;
  7213. }
  7214.  
  7215. // 构建相似度矩阵
  7216. function buildSimilarityMatrix(table1, table2) {
  7217. const n = table1.length;
  7218. const m = table2.length;
  7219. const similarityMatrix = Array.from({ length: n }, () => Array(m).fill(0));
  7220.  
  7221. for (let i = 0; i < n; i++) {
  7222. for (let j = 0; j < m; j++) {
  7223. const shopSim = calcSimilarity(table1[i]['店铺名'], table2[j]['店铺名']);
  7224. const titleSim = calcSimilarity(table1[i]['商品标题'], table2[j]['商品标题']);
  7225. const totalSim = 0.35 * shopSim + 0.65 * titleSim;
  7226. similarityMatrix[i][j] = totalSim;
  7227. }
  7228. }
  7229.  
  7230. return similarityMatrix;
  7231. }
  7232.  
  7233. // 使用有序贪心算法进行匹配
  7234. function matchItemsWithGreedy(table1, table2) {
  7235. const similarityMatrix = buildSimilarityMatrix(table1, table2);
  7236. const n = table1.length;
  7237. const m = table2.length;
  7238. const matches = [];
  7239. const usedCols = new Set();
  7240.  
  7241. for (let i = 0; i < n; i++) {
  7242. let maxSim = -1;
  7243. let bestCol = -1;
  7244.  
  7245. for (let j = 0; j < m; j++) {
  7246. if (usedCols.has(j)) continue;
  7247. const sim = similarityMatrix[i][j];
  7248. if (sim > maxSim) {
  7249. maxSim = sim;
  7250. bestCol = j;
  7251. }
  7252. }
  7253.  
  7254. if (bestCol !== -1) {
  7255. matches.push([i, bestCol]);
  7256. usedCols.add(bestCol);
  7257. }
  7258. }
  7259.  
  7260. return matches;
  7261. }
  7262.  
  7263. // 使用改进的匈牙利算法进行匹配
  7264. function matchItemsWithHungarian(table1, table2) {
  7265. const similarityMatrix = buildSimilarityMatrix(table1, table2);
  7266. const n = table1.length;
  7267. const m = table2.length;
  7268. const costMatrix = similarityMatrix.map(row => row.map(val => -val)); // 转换为成本矩阵
  7269.  
  7270. const labelX = new Array(n).fill(0);
  7271. const labelY = new Array(m).fill(0);
  7272. const match = new Array(m).fill(-1);
  7273. const slack = new Array(m).fill(Number.MAX_SAFE_INTEGER);
  7274. const visitedX = new Array(n).fill(false);
  7275. const visitedY = new Array(m).fill(false);
  7276.  
  7277. // 初始化标签
  7278. for (let i = 0; i < n; i++) {
  7279. labelX[i] = Math.max(...costMatrix[i]);
  7280. }
  7281.  
  7282. function findAugmentingPath(i) {
  7283. visitedX[i] = true;
  7284. for (let j = 0; j < m; j++) {
  7285. if (visitedY[j]) continue;
  7286. const delta = labelX[i] + labelY[j] - costMatrix[i][j];
  7287. if (delta === 0) {
  7288. visitedY[j] = true;
  7289. if (match[j] === -1 || findAugmentingPath(match[j])) {
  7290. match[j] = i;
  7291. return true;
  7292. }
  7293. } else {
  7294. slack[j] = Math.min(slack[j], delta);
  7295. }
  7296. }
  7297. return false;
  7298. }
  7299.  
  7300. for (let i = 0; i < n; i++) {
  7301. while (true) {
  7302. slack.fill(Number.MAX_SAFE_INTEGER);
  7303. visitedX.fill(false);
  7304. visitedY.fill(false);
  7305. if (findAugmentingPath(i)) break;
  7306. let delta = Number.MAX_SAFE_INTEGER;
  7307. for (let j = 0; j < m; j++) {
  7308. if (!visitedY[j]) delta = Math.min(delta, slack[j]);
  7309. }
  7310. for (let j = 0; j < n; j++) {
  7311. if (visitedX[j]) labelX[j] -= delta;
  7312. }
  7313. for (let j = 0; j < m; j++) {
  7314. if (visitedY[j]) labelY[j] += delta;
  7315. }
  7316. }
  7317. }
  7318.  
  7319. const matches = match.map((col, row) => [row, col]);
  7320. return matches;
  7321. }
  7322.  
  7323. // 输出结果
  7324. function printMatches(matches, table1, table2) {
  7325. for (const [i, index] of matches) {
  7326. const item1 = table1[i];
  7327. const item2 = table2[index];
  7328. const score = calcSimilarity(item1['商品标题'], item2['商品标题']);
  7329. console.log(`[${i + 1}]表1商品: ${item1['商品标题']}\n2位置: ${index + 1} (商品: ${item2['商品标题']}) [相似度: ${score.toFixed(2)}]`);
  7330. }
  7331. }
  7332.  
  7333. function printIndex(matches) {
  7334. const arr = matches.map(index => index[1] + 1);
  7335. console.log("arrLen:", arr.length);
  7336. console.log("arr:", arr);
  7337. console.log("arr_no_dup:", new Set(arr).size);
  7338. const arrCheck = [...new Set(arr)];
  7339. console.log("arrCheck:", arrCheck.sort((a, b) => a - b));
  7340. }
  7341.  
  7342. // 测试数据
  7343. const table1 = [
  7344. { "商品标题": "水牛纯牛奶", "店铺名": "爱泡澡的水牛旗舰店" },
  7345. { "商品标题": "红养豆浆粉", "店铺名": "九阳豆浆旗舰店" },
  7346. { "商品标题": "芙丝天然矿泉水", "店铺名": "华彬旗舰店" },
  7347. { "商品标题": "正宗天马老树陈皮", "店铺名": "望峰茶叶旗舰店" },
  7348. { "商品标题": "初心肉桂大红袍组合", "店铺名": "望峰茶叶旗舰店" },
  7349. { "商品标题": "茗潮宋礼礼盒", "店铺名": "望峰茶叶旗舰店" },
  7350. { "商品标题": "铁观音 乌龙茶", "店铺名": "望峰茶叶旗舰店" },
  7351. { "商品标题": "2015年白毫银针", "店铺名": "望峰茶叶旗舰店" },
  7352. { "商品标题": "五彩罐岩茶组合", "店铺名": "望峰茶叶旗舰店" },
  7353. { "商品标题": "武夷山正山小种", "店铺名": "望峰茶叶旗舰店" },
  7354. { "商品标题": "茉莉花茶", "店铺名": "望峰茶叶旗舰店" },
  7355. { "商品标题": "碧螺春", "店铺名": "望峰茶叶旗舰店" },
  7356. { "商品标题": "鸿运金骏眉礼盒", "店铺名": "望峰茶叶旗舰店" },
  7357. { "商品标题": "针王 白茶", "店铺名": "望峰茶叶旗舰店" },
  7358. { "商品标题": "正坑牛肉 乌龙茶", "店铺名": "望峰茶叶旗舰店" },
  7359. { "商品标题": "首字号茶礼 乌龙茶", "店铺名": "望峰茶叶旗舰店" },
  7360. { "商品标题": "桐木关老枞 红茶礼盒", "店铺名": "望峰茶叶旗舰店" },
  7361. { "商品标题": "吮指鸡肉条", "店铺名": "牧匠食品旗舰店" },
  7362. { "商品标题": "法丽兹黄油小花曲奇173g*2罐", "店铺名": "法丽兹食品旗舰店" },
  7363. { "商品标题": "芡实糕", "店铺名": "杨先生旗舰店" },
  7364. { "商品标题": "捞汁豆腐", "店铺名": "杨生记食品旗舰店" },
  7365. { "商品标题": "茶香师禅茶系列香水", "店铺名": "茶香师旗舰店" },
  7366. { "商品标题": "宝玑米手甲同护护手霜", "店铺名": "puljim宝玑米旗舰店" },
  7367. { "商品标题": "原制奶酪饼", "店铺名": "潮香村食品旗舰店" },
  7368. { "商品标题": "春雪流心鸡球", "店铺名": "春雪食品旗舰店" },
  7369. { "商品标题": " 网红糯米笋", "店铺名": "蔚鲜来旗舰店" },
  7370. { "商品标题": "羊肚菌", "店铺名": "瑞利来旗舰店" },
  7371. { "商品标题": "大罐粥料", "店铺名": "瑞利来旗舰店" },
  7372. { "商品标题": "新会柑普茶", "店铺名": "望峰茶叶旗舰店" },
  7373. { "商品标题": "桂花金骏眉", "店铺名": "望峰茶叶旗舰店" },
  7374. { "商品标题": "金丝鸡排", "店铺名": "正新食品旗舰店" },
  7375. { "商品标题": "酱牛肉", "店铺名": "阿品旗舰店" },
  7376. { "商品标题": "茶和天下", "店铺名": "望峰茶叶旗舰店" },
  7377. { "商品标题": "丹东99草莓", "店铺名": "时予心愿旗舰店" },
  7378. { "商品标题": "海乡集即食海参", "店铺名": "海乡集旗舰店" },
  7379. { "商品标题": "果乐果香夹心饼干", "店铺名": "嘉士利旗舰店" },
  7380. { "商品标题": "多味香瓜子", "店铺名": "华味亨旗舰店" },
  7381. { "商品标题": "酥小娘核桃布里奥斯面包", "店铺名": "酥小娘旗舰店" },
  7382. { "商品标题": "芋泥乳酪派", "店铺名": "lilygarden荷家旗舰店" },
  7383. { "商品标题": "魔芋荞麦鸡肉膳食包+魔芋韭菜鸡蛋膳食包", "店铺名": "巨诺旗舰店" },
  7384. { "商品标题": "智利进口车厘子", "店铺名": "淘宝买菜农场直发" },
  7385. { "商品标题": "寿南瓜子", "店铺名": "苏太太食品旗舰店" },
  7386. { "商品标题": "石浪电陶炉+枫梭提梁壶围炉套装", "店铺名": "唐丰旗舰店" },
  7387. { "商品标题": "玺棠一体茶盘+素影煮茶壶+圆满素语西施壶9头", "店铺名": "唐丰旗舰店" },
  7388. { "商品标题": "纯钛茶水分离杯", "店铺名": "天猫超市" },
  7389. { "商品标题": "老卤鸭肫", "店铺名": "旺家福旗舰店" },
  7390. { "商品标题": "夏威夷果可可脆", "店铺名": "菓然匠子旗舰店" },
  7391. { "商品标题": "2014荒野贡眉", "店铺名": "望峰茶叶旗舰店" },
  7392. { "商品标题": "风干牛肉干九成干", "店铺名": "蒙亮旗舰店" },
  7393. { "商品标题": "农家乌鸡950g*3", "店铺名": "淘宝买菜农场直发" },
  7394. { "商品标题": "潮庭优选牛肉丸牛筋丸双拼组合", "店铺名": "潮庭旗舰店" },
  7395. { "商品标题": "早餐牛肉饼", "店铺名": "潮迹旗舰店" },
  7396. { "商品标题": "围炉煮茶器具全套", "店铺名": "尚言坊旗舰店" },
  7397. { "商品标题": "点心盘", "店铺名": "拓土旗舰店" },
  7398. ];
  7399.  
  7400. const table2 = [
  7401. { "商品标题": "苏太太百寿南瓜子", "店铺名": "苏太太食品旗舰店" },
  7402. { "商品标题": "捞汁豆腐", "店铺名": "杨生记食品旗舰店" },
  7403. { "商品标题": "芡实糕多口味组合", "店铺名": "杨先生旗舰店" },
  7404. { "商品标题": "法丽兹黄油小花曲奇", "店铺名": "法丽兹食品旗舰店" },
  7405. { "商品标题": "水牛纯牛奶", "店铺名": "爱泡澡的水牛旗舰店" },
  7406. { "商品标题": "魔芋荞麦鸡肉膳食包+魔芋韭菜鸡蛋膳食包", "店铺名": "巨诺旗舰店" },
  7407. { "商品标题": "九阳豆浆红养豆浆粉", "店铺名": "九阳豆浆旗舰店" },
  7408. { "商品标题": "陈皮绿豆百合银耳粥1.308kg*1罐/陈皮红豆粥1.010kg*1罐/1.5kg/罐/干贝虾仁粥1.1kg/罐/紫薯黑米粥1.48kg/罐", "店铺名": "瑞利来旗舰店" },
  7409. { "商品标题": "酥小娘核桃布里奥斯面包", "店铺名": "酥小娘旗舰店" },
  7410. { "商品标题": "夏威夷果可可脆", "店铺名": "菓然匠子旗舰店" },
  7411. { "商品标题": "果乐果香夹心饼干", "店铺名": "嘉士利旗舰店" },
  7412. { "商品标题": "芋泥乳酪派", "店铺名": "lilygarden荷家旗舰店" },
  7413. { "商品标题": "打手瓜子", "店铺名": "华味亨旗舰店" },
  7414. { "商品标题": "旺家福老卤鸭肫", "店铺名": "旺家福旗舰店" },
  7415. { "商品标题": "茶香师禅茶系列香水 和合之境 早春之茶 正如初念", "店铺名": "茶香师旗舰店" },
  7416. { "商品标题": "宝玑米微氛手甲精华霜囤货装", "店铺名": "puljim宝玑米旗舰店" },
  7417. { "商品标题": "【王炸】车厘子 官补115", "店铺名": "淘宝买菜农场直发" },
  7418. { "商品标题": "丹东99草莓", "店铺名": "时予心愿旗舰店" },
  7419. { "商品标题": "吮指鸡肉条", "店铺名": "牧匠食品旗舰店" },
  7420. { "商品标题": "风干牛肉干九成干", "店铺名": "蒙亮旗舰店" },
  7421. { "商品标题": "围炉煮茶", "店铺名": "唐丰旗舰店" },
  7422. { "商品标题": "特美刻茶水分离杯 补贴50", "店铺名": "" },
  7423. { "商品标题": "茶和天下品鉴装", "店铺名": "望峰茶叶旗舰店" },
  7424. { "商品标题": "初心组合 补贴30", "店铺名": "望峰茶叶旗舰店" },
  7425. { "商品标题": "【王炸】天马陈皮220g 补贴50", "店铺名": "望峰茶叶旗舰店" },
  7426. { "商品标题": "新会柑普茶", "店铺名": "望峰茶叶旗舰店" },
  7427. { "商品标题": "鸿运金骏眉礼盒 补贴50", "店铺名": "望峰茶叶旗舰店" },
  7428. { "商品标题": "桐木关老枞红茶礼盒 补贴20", "店铺名": "望峰茶叶旗舰店" },
  7429. { "商品标题": "【王炸】2015年老银针", "店铺名": "望峰茶叶旗舰店" },
  7430. { "商品标题": "正坑牛肉/正坑牛肉礼盒装 补贴100", "店铺名": "望峰茶叶旗舰店" },
  7431. { "商品标题": "2014年荒野贡眉 补贴15", "店铺名": "望峰茶叶旗舰店" },
  7432. { "商品标题": "茗潮宋礼 补贴20", "店铺名": "望峰茶叶旗舰店" },
  7433. { "商品标题": "针王 补贴50", "店铺名": "望峰茶叶旗舰店" },
  7434. { "商品标题": "茉莉花茶", "店铺名": "望峰茶叶旗舰店" },
  7435. { "商品标题": "桂花金骏眉", "店铺名": "望峰茶叶旗舰店" },
  7436. { "商品标题": "茶具套装", "店铺名": "唐丰旗舰店" },
  7437. { "商品标题": "五彩罐岩茶组合 补贴30", "店铺名": "望峰茶叶旗舰店" },
  7438. { "商品标题": "武夷山正山小种", "店铺名": "望峰茶叶旗舰店" },
  7439. { "商品标题": "首字号茶礼512g 补贴30", "店铺名": "望峰茶叶旗舰店" },
  7440. { "商品标题": "铁观音", "店铺名": "望峰茶叶旗舰店" },
  7441. { "商品标题": "碧螺春", "店铺名": "望峰茶叶旗舰店" },
  7442. { "商品标题": "VOSS/芙丝天然矿泉水", "店铺名": "华彬旗舰店" },
  7443. { "商品标题": "条纹月笼壶", "店铺名": "尚言坊" },
  7444. { "商品标题": "海乡集大连即食海参 补贴25", "店铺名": "海乡集旗舰店" },
  7445. { "商品标题": "潮庭优选双拼牛丸组合", "店铺名": "潮庭旗舰店" },
  7446. { "商品标题": "农家散养乌鸡", "店铺名": "淘宝买菜" },
  7447. { "商品标题": "瑞利来羊肚菌", "店铺名": "瑞利来旗舰店" },
  7448. { "商品标题": "流心鸡球400g", "店铺名": "春雪食品旗舰店" },
  7449. { "商品标题": "奶酪饼", "店铺名": "潮香村食品旗舰店" },
  7450. { "商品标题": "网红糯米笋", "店铺名": "蔚鲜来旗舰店" },
  7451. { "商品标题": "正新金丝鸡排", "店铺名": "正新食品旗舰店" },
  7452. { "商品标题": "阿品酱牛肉", "店铺名": "阿品旗舰店" },
  7453. ];
  7454.  
  7455. // setTimeout(() => {
  7456. // // calcSimilarity(testText_1, testText_2);
  7457. // console.log('开始匹配商品...');
  7458. // // const matches = matchItemsWithGreedy(table1, table2);
  7459. // const matches = matchItemsWithHungarian(table1, table2);
  7460. // printMatches(matches, table1, table2);
  7461. // printIndex(matches);
  7462. // }, 10000);
  7463. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址