- // ==UserScript==
- // @name POE2 Trade ST工具箱
- // @namespace http://tampermonkey.net/
- // @version 2.3.2
- // @description 自动转换简繁中文(页面转简体,输入转繁体)- stomtian
- // @author stomtian
- // @match https://www.pathofexile.com/trade*
- // @match https://pathofexile.com/trade*
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant unsafeWindow
- // @license MIT
- // @require https://cdn.jsdelivr.net/npm/opencc-js@1.0.5/dist/umd/full.min.js
- // @run-at document-end
- // @noframes true
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- console.log('POE2 Trade ST工具箱已加载');
-
- const STATE = {
- pageSimplified: GM_getValue('pageSimplified', true),
- inputTraditional: GM_getValue('inputTraditional', true),
- originalTexts: new WeakMap(),
- configs: GM_getValue('savedConfigs', {}), // 保存的配置
- autoLoadEnabled: GM_getValue('autoLoadEnabled', false), // 自动加载开关
- matchedCards: [], // 添加匹配的卡片列表
- currentMatchIndex: -1, // 添加当前匹配索引
- showOnlyMatched: GM_getValue('showOnlyMatched', false), // 添加新的状态
- searchPresets: GM_getValue('searchPresets', {}) // 添加预设关键词存储
- };
-
- const CUSTOM_DICT = [
- ['回覆', '回復'],
- ['恢覆', '恢復'],
- ];
-
- const CONFIG = {
- maxAttempts: 50,
- checkInterval: 100,
- inputSelector: 'input[type="text"]:not(#config-name):not(#config-category), textarea',
- textSelector: '.search-bar, .search-advanced-pane, .results-container, .resultset',
- excludeSelector: 'script, style, input, textarea, select, .converter-controls'
- };
-
- function waitForElement(selector) {
- /**
- * 等待指定选择器的元素出现在页面上
- * @param {string} selector - CSS选择器
- * @returns {Promise} - 当元素出现时解析的Promise
- */
- return new Promise(resolve => {
- // 如果元素已经存在,立即解析
- if (document.querySelector(selector)) {
- resolve();
- return;
- }
-
- // 创建观察器监听DOM变化
- const observer = new MutationObserver(() => {
- try {
- if (document.querySelector(selector)) {
- observer.disconnect();
- resolve();
- }
- } catch (error) {
- console.debug('等待元素时出错:', error);
- }
- });
-
- // 开始观察DOM变化
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
- });
- }
-
- function waitForOpenCC() {
- /**
- * 等待OpenCC库加载完成
- * @returns {Promise} - 当OpenCC可用时解析的Promise
- */
- return new Promise((resolve, reject) => {
- // 如果OpenCC已经加载,立即解析
- if (typeof window.OpenCC !== 'undefined') {
- resolve(window.OpenCC);
- return;
- }
-
- let attempts = 0;
- const maxAttempts = CONFIG.maxAttempts;
- const checkInterval = CONFIG.checkInterval;
-
- // 定期检查OpenCC是否已加载
- const checkOpenCC = setInterval(() => {
- attempts++;
-
- if (typeof window.OpenCC !== 'undefined') {
- clearInterval(checkOpenCC);
- resolve(window.OpenCC);
- return;
- }
-
- // 超过最大尝试次数后放弃
- if (attempts >= maxAttempts) {
- clearInterval(checkOpenCC);
- reject(new Error('加载OpenCC超时'));
- }
- }, checkInterval);
- });
- }
-
- function createConverters(OpenCC) {
- /**
- * 创建简繁转换器
- * @param {Object} OpenCC - OpenCC库对象
- * @returns {Object} - 包含简体到繁体和繁体到简体的转换器
- */
- try {
- // 创建简体到繁体的转换器(输入转换用)
- const toTraditional = OpenCC.ConverterFactory(
- OpenCC.Locale.from.cn,
- OpenCC.Locale.to.tw.concat([CUSTOM_DICT])
- );
-
- // 创建繁体到简体的转换器(页面转换用)
- const toSimplified = OpenCC.ConverterFactory(
- OpenCC.Locale.from.tw,
- OpenCC.Locale.to.cn
- );
-
- return { toTraditional, toSimplified };
- } catch (error) {
- console.error('创建转换器时出错:', error);
- // 返回空转换器,避免脚本崩溃
- return {
- toTraditional: text => text,
- toSimplified: text => text
- };
- }
- }
-
- function createInputHandler(converter) {
- /**
- * 创建输入处理函数,用于将输入文本转换为繁体中文
- * @param {Object} converter - 文本转换器对象
- * @returns {Function} - 处理输入事件的函数
- */
- return function handleInput(e) {
- try {
- // 检查是否需要进行转换
- if (!e?.target || !STATE.inputTraditional) return;
-
- // 如果输入框标记了不需要转换,则直接返回
- if (e.target.dataset?.noConvert === 'true') return;
-
- // 检查输入值是否存在
- const text = e.target.value;
- if (!text) return;
-
- // 保存当前光标位置
- const cursorPosition = e.target.selectionStart;
-
- // 使用requestAnimationFrame优化性能
- requestAnimationFrame(() => {
- try {
- // 转换文本
- const convertedText = converter.toTraditional(text);
-
- // 如果转换前后文本相同,则不需要更新
- if (text === convertedText) return;
-
- // 更新输入框的值
- e.target.value = convertedText;
-
- // 恢复光标位置
- if (typeof cursorPosition === 'number') {
- e.target.setSelectionRange(cursorPosition, cursorPosition);
- }
-
- // 触发输入事件,确保其他监听器能够接收到更新
- e.target.dispatchEvent(new Event('input', {
- bubbles: true,
- cancelable: true
- }));
- } catch (error) {
- console.debug('转换输入文本时出错:', error);
- }
- });
- } catch (error) {
- console.debug('处理输入事件时出错:', error);
- }
- };
- }
-
- function convertPageText(converter, forceRestore = false) {
- /**
- * 转换页面上的文本内容(简繁转换)
- * @param {Object} converter - 文本转换器对象
- * @param {boolean} forceRestore - 是否强制恢复原始文本
- */
- // 如果不需要简体化且不是强制恢复,则直接返回
- if (!STATE.pageSimplified && !forceRestore) return;
-
- try {
- // 查找需要处理的元素
- const elements = document.querySelectorAll(CONFIG.textSelector);
- if (!elements.length) return;
-
- // 处理每个根元素
- elements.forEach(root => {
- try {
- // 创建TreeWalker遍历文本节点
- const walker = document.createTreeWalker(
- root,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode: function(node) {
- // 过滤空文本节点
- if (!node.textContent.trim()) return NodeFilter.FILTER_REJECT;
-
- // 检查父节点
- const parent = node.parentNode;
- if (!parent) return NodeFilter.FILTER_REJECT;
-
- // 排除特定元素内的文本
- if (parent.closest?.(CONFIG.excludeSelector)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 遍历并处理每个文本节点
- let node;
- while (node = walker.nextNode()) {
- const text = node.textContent.trim();
- if (!text) continue;
-
- // 保存原始文本(如果尚未保存)
- if (!STATE.originalTexts.has(node)) {
- STATE.originalTexts.set(node, text);
- }
-
- // 根据当前状态进行转换或恢复
- if (STATE.pageSimplified) {
- const convertedText = converter.toSimplified(text);
- if (text !== convertedText) {
- node.textContent = convertedText;
- }
- } else {
- const originalText = STATE.originalTexts.get(node);
- if (originalText && node.textContent !== originalText) {
- node.textContent = originalText;
- }
- }
- }
- } catch (error) {
- console.debug('处理文本节点时出错:', error);
- }
- });
- } catch (error) {
- console.debug('转换页面文本时出错:', error);
- }
- }
-
- function attachInputListener(handleInput) {
- /**
- * 为页面上的输入元素添加转换事件监听器
- * @param {Function} handleInput - 输入处理函数
- */
- try {
- const inputElements = document.querySelectorAll(CONFIG.inputSelector);
- if (!inputElements.length) return;
-
- inputElements.forEach(element => {
- try {
- // 排除已处理的元素和搜索框
- if (element?.dataset?.hasConverter || element?.dataset?.isSearchInput) {
- return;
- }
-
- // 添加输入事件监听器
- element.addEventListener('input', handleInput);
-
- // 标记元素已添加转换器
- element.dataset.hasConverter = 'true';
- } catch (error) {
- console.debug('为输入元素添加事件监听器时出错:', error);
- }
- });
- } catch (error) {
- console.debug('查找输入元素时出错:', error);
- }
- }
-
- function createObserver(handleInput, converter) {
- /**
- * 创建DOM变化观察器,监听页面变化并处理新添加的元素
- * @param {Function} handleInput - 输入处理函数
- * @param {Object} converter - 文本转换器对象
- * @returns {MutationObserver} - 配置好的观察器实例
- */
- return new MutationObserver(mutations => {
- let needsTextConversion = false;
-
- for (const mutation of mutations) {
- // 跳过没有新增节点的变化
- if (!mutation.addedNodes.length) continue;
-
- // 检查是否有新的输入元素
- const hasNewInputs = Array.from(mutation.addedNodes)
- .some(node => {
- try {
- // 检查节点是否包含输入元素
- return node.nodeType === Node.ELEMENT_NODE &&
- node.querySelectorAll?.(CONFIG.inputSelector)?.length > 0;
- } catch (error) {
- console.debug('检查新节点时出错:', error);
- return false;
- }
- });
-
- // 如果有新的输入元素,为它们添加事件监听器
- if (hasNewInputs) {
- attachInputListener(handleInput);
- }
-
- // 标记需要进行文本转换
- needsTextConversion = true;
- }
-
- // 如果需要文本转换,延迟执行以确保DOM已完全更新
- if (needsTextConversion) {
- setTimeout(() => convertPageText(converter), 100);
- }
- });
- }
-
- function createConfigModal() {
- const modalHtml = `
- <div id="config-modal" style="display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
- background: #1a1a1a; padding: 20px; border-radius: 8px; z-index: 10000; min-width: 600px; color: #fff;">
- <div style="display: flex; justify-content: space-between; margin-bottom: 15px;">
- <h3 style="margin: 0;">ST工具箱</h3>
- <button id="close-config-modal" style="background: none; border: none; color: #fff; cursor: pointer;">✕</button>
- </div>
-
- <!-- 标签栏 -->
- <div style="display: flex; gap: 10px; margin-bottom: 15px; border-bottom: 1px solid #444; padding-bottom: 10px;">
- <button id="tab-configs" class="modal-tab active" style="padding: 8px 20px; background: #4a90e2; border: none; color: #fff; cursor: pointer; border-radius: 4px; transition: all 0.2s;">配置管理</button>
- <button id="tab-presets" class="modal-tab" style="padding: 8px 20px; background: #3d3d3d; border: none; color: #fff; cursor: pointer; border-radius: 4px; transition: all 0.2s;">预设关键词</button>
- <button id="tab-settings" class="modal-tab" style="padding: 8px 20px; background: #3d3d3d; border: none; color: #fff; cursor: pointer; border-radius: 4px; transition: all 0.2s;">设置</button>
- <button id="tab-contact" class="modal-tab" style="padding: 8px 20px; background: #3d3d3d; border: none; color: #fff; cursor: pointer; border-radius: 4px; transition: all 0.2s;">联系我</button>
- </div>
-
- <!-- 配置管理面板 -->
- <div id="panel-configs" class="panel" style="display: block;">
- <div style="padding: 15px; background: #2d2d2d; border-radius: 4px;">
- <div style="margin-bottom: 15px;">
- <input type="text" id="config-name" placeholder="配置名称"
- style="padding: 5px; margin-right: 10px; background: #3d3d3d; border: 1px solid #444; color: #fff; width: 200px;">
- <div class="custom-select" style="display: inline-block; position: relative; width: 150px; margin-right: 10px;">
- <input type="text" id="config-category" placeholder="选择或输入分类"
- style="padding: 5px; background: #3d3d3d; border: 1px solid #444; color: #fff; width: 100%; cursor: pointer;">
- <div id="category-dropdown" style="display: none; position: absolute; top: 100%; left: 0; width: 100%;
- background: #3d3d3d; border: 1px solid #444; border-top: none; max-height: 200px; overflow-y: auto; z-index: 1000;">
- </div>
- </div>
- <button id="save-config" style="padding: 5px 10px; background: #4a90e2; border: none; color: #fff; cursor: pointer; border-radius: 3px;">
- 保存当前配置
- </button>
- </div>
- <div id="category-tabs" style="margin-bottom: 15px; border-bottom: 1px solid #444; padding-bottom: 10px;"></div>
- <div id="config-list" style="max-height: 300px; overflow-y: auto;">
- </div>
- </div>
- </div>
-
- <!-- 设置面板 -->
- <div id="panel-settings" class="panel" style="display: none;">
- <div style="padding: 15px; background: #2d2d2d; border-radius: 4px;">
- <!-- 功能开关 -->
- <div style="margin-bottom: 20px;">
- <div style="font-weight: bold; margin-bottom: 10px; color: #4a90e2;">功能开关</div>
- <div style="display: flex; gap: 10px;">
- <button id="toggle-page-simplified" style="flex: 1; padding: 8px 15px; background: ${STATE.pageSimplified ? '#4a90e2' : '#3d3d3d'}; border: none; color: #fff; cursor: pointer; border-radius: 3px; transition: background-color 0.2s; text-align: center;">
- ${STATE.pageSimplified ? '✓ 页面简体' : '✗ 页面简体'}
- </button>
- <button id="toggle-input-traditional" style="flex: 1; padding: 8px 15px; background: ${STATE.inputTraditional ? '#4a90e2' : '#3d3d3d'}; border: none; color: #fff; cursor: pointer; border-radius: 3px; transition: background-color 0.2s; text-align: center;">
- ${STATE.inputTraditional ? '✓ 输入繁体' : '✗ 输入繁体'}
- </button>
- <button id="toggle-auto-load" style="flex: 1; padding: 8px 15px; background: ${STATE.autoLoadEnabled ? '#4a90e2' : '#3d3d3d'}; border: none; color: #fff; cursor: pointer; border-radius: 3px; transition: background-color 0.2s; text-align: center;">
- ${STATE.autoLoadEnabled ? '✓ 自动加载' : '✗ 自动加载'}
- </button>
- </div>
- </div>
-
- <!-- 配置导入导出 -->
- <div style="margin-top: 20px;">
- <div style="font-weight: bold; margin-bottom: 10px; color: #4a90e2;">配置导入导出</div>
- <div style="display: flex; gap: 10px;">
- <button id="export-configs" style="flex: 1; padding: 8px 15px; background: #27ae60; border: none; color: #fff; cursor: pointer; border-radius: 3px;">导出配置</button>
- <button id="import-configs" style="flex: 1; padding: 8px 15px; background: #e67e22; border: none; color: #fff; cursor: pointer; border-radius: 3px;">导入配置</button>
- <input type="file" id="import-file" accept=".json" style="display: none;">
- </div>
- </div>
- </div>
- </div>
-
- <!-- 预设关键词面板 -->
- <div id="panel-presets" class="panel" style="display: none;">
- <div style="padding: 15px; background: #2d2d2d; border-radius: 4px;">
- <div style="margin-bottom: 15px; display: flex; justify-content: flex-end;">
- <button id="add-preset" style="padding: 8px 20px; background: #4a90e2; border: none; color: #fff; cursor: pointer; border-radius: 4px; transition: all 0.2s;">
- 添加预设
- </button>
- </div>
- <div id="preset-list" style="max-height: 400px; overflow-y: auto;">
- </div>
- </div>
- </div>
- <!-- 联系我面板 -->
- <div id="panel-contact" class="panel" style="display: none;">
- <div style="padding: 15px; background: #2d2d2d; border-radius: 4px;">
- <div style="text-align: center; padding: 20px;">
- <div style="font-size: 18px; color: #4a90e2; margin-bottom: 15px;">欢迎加入POE2 ST工具箱交流群</div>
- <div style="font-size: 24px; color: #FFD700; margin-bottom: 15px;">QQ群:858024457</div>
- <div style="color: #999; font-size: 14px;">
- 欢迎各位流亡者加入交流群,反馈使用问题,提出建议!
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <!-- 预设编辑弹窗 -->
- <div id="preset-edit-modal" style="display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
- background: #1a1a1a; padding: 20px; border-radius: 8px; z-index: 10002; width: 800px; color: #fff; box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);">
- <div style="display: flex; justify-content: space-between; margin-bottom: 15px;">
- <h3 style="margin: 0;" id="preset-edit-title">添加预设</h3>
- <button id="close-preset-edit" style="background: none; border: none; color: #fff; cursor: pointer; font-size: 20px;">✕</button>
- </div>
- <div style="margin-bottom: 15px;">
- <div style="margin-bottom: 10px;">
- <label style="display: block; margin-bottom: 5px;">预设名称</label>
- <input type="text" id="preset-name" style="width: 100%; padding: 8px; background: #2d2d2d; border: 1px solid #444; color: #fff; border-radius: 4px;">
- </div>
- <div>
- <label style="display: block; margin-bottom: 5px;">关键词(用;分隔)</label>
- <textarea id="preset-keywords" style="width: 100%; height: 200px; padding: 8px; background: #2d2d2d; border: 1px solid #444; color: #fff; border-radius: 4px; resize: vertical; font-family: monospace;"></textarea>
- </div>
- </div>
- <div style="display: flex; justify-content: flex-end; gap: 10px;">
- <button id="cancel-preset-edit" style="padding: 8px 20px; background: #3d3d3d; border: none; color: #fff; cursor: pointer; border-radius: 4px;">
- 取消
- </button>
- <button id="save-preset" style="padding: 8px 20px; background: #4a90e2; border: none; color: #fff; cursor: pointer; border-radius: 4px;">
- 保存
- </button>
- </div>
- </div>
-
- <!-- 添加遮罩层 -->
- <div id="preset-edit-overlay" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.7); z-index: 10001;"></div>
- </div>
- `;
-
- document.body.insertAdjacentHTML('beforeend', modalHtml);
-
- // 添加遮罩
- const overlay = document.createElement('div');
- overlay.id = 'config-modal-overlay';
- overlay.style.cssText = `
- display: none;
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.5);
- z-index: 9999;
- `;
- document.body.appendChild(overlay);
-
- // 添加样式
- const style = document.createElement('style');
- style.textContent = `
- .modal-tab.active {
- background: #4a90e2 !important;
- }
- .modal-tab:hover {
- background: #357abd !important;
- }
- .panel {
- transition: opacity 0.3s ease;
- }
- #config-list::-webkit-scrollbar {
- width: 8px;
- }
- #config-list::-webkit-scrollbar-track {
- background: #1a1a1a;
- }
- #config-list::-webkit-scrollbar-thumb {
- background: #444;
- border-radius: 4px;
- }
- #config-list::-webkit-scrollbar-thumb:hover {
- background: #555;
- }
- `;
- document.head.appendChild(style);
-
- setupConfigModalEvents();
- updateConfigList();
- setupCategoryDropdown();
- }
-
- function setupCategoryDropdown() {
- const categoryInput = document.getElementById('config-category');
- const dropdown = document.getElementById('category-dropdown');
- let isDropdownVisible = false;
-
- function updateDropdown() {
- const categories = Object.keys(STATE.configs);
- const inputValue = categoryInput.value.toLowerCase();
-
- dropdown.innerHTML = '';
-
- categories
- .filter(category => category.toLowerCase().includes(inputValue))
- .forEach(category => {
- const item = document.createElement('div');
- item.className = 'dropdown-item';
- item.textContent = category;
- item.onclick = () => {
- categoryInput.value = category;
- hideDropdown();
- };
- dropdown.appendChild(item);
- });
-
- if (categories.length === 0) {
- const item = document.createElement('div');
- item.className = 'dropdown-item';
- item.textContent = '无已有分类';
- item.style.color = '#666';
- dropdown.appendChild(item);
- }
- }
-
- function showDropdown() {
- updateDropdown();
- dropdown.style.display = 'block';
- isDropdownVisible = true;
- }
-
- function hideDropdown() {
- dropdown.style.display = 'none';
- isDropdownVisible = false;
- }
-
- categoryInput.addEventListener('focus', showDropdown);
- categoryInput.addEventListener('input', updateDropdown);
-
- // 点击外部区域时隐藏下拉列表
- document.addEventListener('click', (e) => {
- const isClickInside = categoryInput.contains(e.target) || dropdown.contains(e.target);
- if (!isClickInside && isDropdownVisible) {
- hideDropdown();
- }
- });
-
- // 阻止事件冒泡,避免点击下拉列表时触发外部点击事件
- dropdown.addEventListener('click', (e) => {
- e.stopPropagation();
- });
- }
-
- function setupConfigModalEvents() {
- const modal = document.getElementById('config-modal');
- const overlay = document.getElementById('config-modal-overlay');
- const closeBtn = document.getElementById('close-config-modal');
- const saveBtn = document.getElementById('save-config');
- const togglePageBtn = document.getElementById('toggle-page-simplified');
- const toggleInputBtn = document.getElementById('toggle-input-traditional');
- const toggleAutoLoadBtn = document.getElementById('toggle-auto-load');
- const exportBtn = document.getElementById('export-configs');
- const importBtn = document.getElementById('import-configs');
- const importFile = document.getElementById('import-file');
- const tabConfigs = document.getElementById('tab-configs');
- const tabSettings = document.getElementById('tab-settings');
- const tabPresets = document.getElementById('tab-presets');
- const tabContact = document.getElementById('tab-contact');
- const panelConfigs = document.getElementById('panel-configs');
- const panelSettings = document.getElementById('panel-settings');
- const panelPresets = document.getElementById('panel-presets');
- const panelContact = document.getElementById('panel-contact');
- const savePresetBtn = document.getElementById('save-preset');
-
- const addPresetBtn = document.getElementById('add-preset');
- const presetEditModal = document.getElementById('preset-edit-modal');
- const presetEditOverlay = document.getElementById('preset-edit-overlay');
- const closePresetEdit = document.getElementById('close-preset-edit');
- const cancelPresetEdit = document.getElementById('cancel-preset-edit');
- const presetEditTitle = document.getElementById('preset-edit-title');
-
- // 标签切换函数
- function switchTab(activeTab) {
- // 重置所有标签和面板
- [tabConfigs, tabSettings, tabPresets, tabContact].forEach(tab => {
- tab.classList.remove('active');
- tab.style.background = '#3d3d3d';
- });
- [panelConfigs, panelSettings, panelPresets, panelContact].forEach(panel => {
- panel.style.display = 'none';
- });
-
- // 激活选中的标签和面板
- activeTab.classList.add('active');
- activeTab.style.background = '#4a90e2';
-
- // 显示对应的面板
- if (activeTab === tabConfigs) {
- panelConfigs.style.display = 'block';
- } else if (activeTab === tabSettings) {
- panelSettings.style.display = 'block';
- } else if (activeTab === tabPresets) {
- panelPresets.style.display = 'block';
- updatePresetList();
- } else if (activeTab === tabContact) {
- panelContact.style.display = 'block';
- }
- }
-
- // 标签切换事件
- tabConfigs.addEventListener('click', () => switchTab(tabConfigs));
- tabSettings.addEventListener('click', () => switchTab(tabSettings));
- tabPresets.addEventListener('click', () => switchTab(tabPresets));
- tabContact.addEventListener('click', () => switchTab(tabContact));
-
- // 初始化显示配置管理标签
- switchTab(tabConfigs);
-
- closeBtn.addEventListener('click', () => {
- modal.style.display = 'none';
- overlay.style.display = 'none';
- });
-
- overlay.addEventListener('click', () => {
- modal.style.display = 'none';
- overlay.style.display = 'none';
- });
-
- togglePageBtn.addEventListener('click', () => {
- STATE.pageSimplified = !STATE.pageSimplified;
- GM_setValue('pageSimplified', STATE.pageSimplified);
- togglePageBtn.textContent = STATE.pageSimplified ? '✓ 页面简体' : '✗ 页面简体';
- togglePageBtn.style.backgroundColor = STATE.pageSimplified ? '#4a90e2' : '#3d3d3d';
- convertPageText(window.converter, true);
- });
-
- toggleInputBtn.addEventListener('click', () => {
- STATE.inputTraditional = !STATE.inputTraditional;
- GM_setValue('inputTraditional', STATE.inputTraditional);
- toggleInputBtn.textContent = STATE.inputTraditional ? '✓ 输入繁体' : '✗ 输入繁体';
- toggleInputBtn.style.backgroundColor = STATE.inputTraditional ? '#4a90e2' : '#3d3d3d';
- });
-
- toggleAutoLoadBtn.addEventListener('click', () => {
- STATE.autoLoadEnabled = !STATE.autoLoadEnabled;
- GM_setValue('autoLoadEnabled', STATE.autoLoadEnabled);
- toggleAutoLoadBtn.textContent = STATE.autoLoadEnabled ? '✓ 自动加载' : '✗ 自动加载';
- toggleAutoLoadBtn.style.backgroundColor = STATE.autoLoadEnabled ? '#4a90e2' : '#3d3d3d';
- });
-
- saveBtn.addEventListener('click', saveCurrentConfig);
-
- // 修改导出配置
- exportBtn.addEventListener('click', () => {
- const configData = {
- version: '2.0.0',
- configs: {},
- searchPresets: STATE.searchPresets
- };
-
- // 复制配置,但不包含 timestamp
- Object.keys(STATE.configs).forEach(category => {
- configData.configs[category] = {};
- Object.keys(STATE.configs[category]).forEach(name => {
- configData.configs[category][name] = {
- url: STATE.configs[category][name].url
- };
- });
- });
-
- const blob = new Blob([JSON.stringify(configData, null, 2)], { type: 'application/json' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = `poe2_trade_configs_${new Date().toISOString().slice(0,10)}.json`;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- });
-
- // 导入配置按钮点击
- importBtn.addEventListener('click', () => {
- importFile.click();
- });
-
- // 处理文件导入
- importFile.addEventListener('change', (e) => {
- const file = e.target.files[0];
- if (!file) return;
-
- const reader = new FileReader();
- reader.onload = (event) => {
- try {
- const importedData = JSON.parse(event.target.result);
-
- // 验证导入的数据
- if (!importedData.version || (!importedData.configs && !importedData.searchPresets)) {
- throw new Error('无效的配置文件格式');
- }
-
- // 确认导入
- if (confirm(`确定要导入这些配置吗?\n这将会覆盖同名的现有配置和预设关键词。`)) {
- // 合并配置
- if (importedData.configs) {
- Object.keys(importedData.configs).forEach(category => {
- if (!STATE.configs[category]) {
- STATE.configs[category] = {};
- }
- Object.assign(STATE.configs[category], importedData.configs[category]);
- });
- GM_setValue('savedConfigs', STATE.configs);
- updateConfigList();
- }
-
- // 合并预设关键词
- if (importedData.searchPresets) {
- Object.assign(STATE.searchPresets, importedData.searchPresets);
- GM_setValue('searchPresets', STATE.searchPresets);
- updatePresetList();
- }
-
- alert('配置导入成功!');
- }
- } catch (error) {
- alert('导入失败:' + error.message);
- }
-
- // 清除文件选择,允许重复导入同一个文件
- importFile.value = '';
- };
- reader.readAsText(file);
- });
-
- // 添加预设标签切换事件
- tabPresets.addEventListener('click', () => {
- tabPresets.classList.add('active');
- tabConfigs.classList.remove('active');
- tabSettings.classList.remove('active');
- tabConfigs.style.background = '#3d3d3d';
- tabSettings.style.background = '#3d3d3d';
- panelConfigs.style.display = 'none';
- panelSettings.style.display = 'none';
- panelPresets.style.display = 'block';
- updatePresetList();
- });
-
- // 添加保存预设事件
- savePresetBtn.addEventListener('click', saveSearchPreset);
-
- function closePresetEditModal() {
- presetEditModal.style.display = 'none';
- presetEditOverlay.style.display = 'none';
- }
-
- // 打开预设编辑弹窗
- addPresetBtn.addEventListener('click', () => {
- presetEditModal.style.display = 'block';
- presetEditOverlay.style.display = 'block';
- presetEditTitle.textContent = '添加预设';
- document.getElementById('preset-name').value = '';
- document.getElementById('preset-keywords').value = '';
- document.getElementById('preset-name').dataset.editMode = 'false';
- });
-
- // 关闭预设编辑弹窗
- [closePresetEdit, cancelPresetEdit, presetEditOverlay].forEach(btn => {
- btn.addEventListener('click', closePresetEditModal);
- });
-
- // 添加预设名称和关键词输入框的事件处理
- const presetNameInput = document.getElementById('preset-name');
- const presetKeywordsInput = document.getElementById('preset-keywords');
-
- // 标记这些输入框不需要转换
- presetNameInput.dataset.noConvert = 'true';
- presetKeywordsInput.dataset.noConvert = 'true';
-
- // 移除原有的输入转换处理
- [presetNameInput, presetKeywordsInput].forEach(input => {
- const oldInput = input.cloneNode(true);
- input.parentNode.replaceChild(oldInput, input);
- });
- }
-
- // 修改 saveCurrentConfig 函数
- function saveCurrentConfig() {
- const name = document.getElementById('config-name').value.trim();
- const category = document.getElementById('config-category').value.trim();
-
- if (!name) {
- alert('请输入配置名称');
- return;
- }
-
- if (!category) {
- alert('请输入分类名称');
- return;
- }
-
- if (!STATE.configs[category]) {
- STATE.configs[category] = {};
- }
-
- STATE.configs[category][name] = {
- url: window.location.href
- };
-
- GM_setValue('savedConfigs', STATE.configs);
- updateConfigList();
-
- document.getElementById('config-name').value = '';
- document.getElementById('config-category').value = '';
- }
-
- function updateConfigList() {
- const configList = document.getElementById('config-list');
- const categoryTabs = document.getElementById('category-tabs');
- configList.innerHTML = '';
- categoryTabs.innerHTML = '';
-
- // 获取所有分类
- const categories = Object.keys(STATE.configs);
-
- // 如果没有配置,显示提示信息
- if (categories.length === 0) {
- configList.innerHTML = '<div style="text-align: center; color: #666;">暂无保存的配置</div>';
- return;
- }
-
- // 创建标签
- categories.forEach((category, index) => {
- const tabButton = document.createElement('button');
- tabButton.textContent = category;
- tabButton.style.cssText = `
- background: ${index === 0 ? '#4a90e2' : '#3d3d3d'};
- border: none;
- color: #fff;
- padding: 5px 15px;
- cursor: pointer;
- border-radius: 3px;
- transition: background-color 0.2s;
- margin-right: 10px;
- `;
- tabButton.dataset.category = category;
- tabButton.title = '双击删除分类';
-
- tabButton.addEventListener('click', (e) => {
- document.querySelectorAll('#category-tabs button[data-category]').forEach(btn => {
- btn.style.backgroundColor = '#3d3d3d';
- });
- tabButton.style.backgroundColor = '#4a90e2';
- showCategoryConfigs(category);
- });
-
- tabButton.addEventListener('dblclick', (e) => {
- e.stopPropagation();
- deleteCategory(category);
- });
-
- categoryTabs.appendChild(tabButton);
- });
-
- // 默认显示第一个分类的配置
- showCategoryConfigs(categories[0]);
- }
-
- function deleteCategory(category) {
- /**
- * 删除指定的配置类别及其所有配置
- * @param {string} category - 要删除的配置类别
- */
- try {
- // 检查类别是否存在
- if (!STATE.configs[category]) {
- console.warn(`类别不存在: ${category}`);
- return;
- }
-
- // 获取该类别下的配置数量
- const configCount = Object.keys(STATE.configs[category]).length;
-
- // 确认删除操作
- if (confirm(`确定要删除分类 "${category}" 及其包含的 ${configCount} 个配置吗?`)) {
- // 删除类别
- delete STATE.configs[category];
-
- // 保存更新后的配置
- GM_setValue('savedConfigs', STATE.configs);
-
- // 更新配置列表显示
- updateConfigList();
-
- console.log(`已删除类别: ${category} (包含 ${configCount} 个配置)`);
- }
- } catch (error) {
- console.error('删除类别时出错:', error);
- }
- }
-
- function showCategoryConfigs(category) {
- /**
- * 显示指定类别的所有配置
- * @param {string} category - 配置类别
- */
- try {
- // 获取配置列表容器
- const configList = document.getElementById('config-list');
- if (!configList) {
- console.warn('无法找到配置列表容器');
- return;
- }
-
- // 清空当前列表
- configList.innerHTML = '';
-
- // 获取指定类别的配置
- const configs = STATE.configs[category];
- if (!configs || Object.keys(configs).length === 0) {
- const emptyMessage = document.createElement('div');
- emptyMessage.textContent = '该分类下没有配置';
- emptyMessage.style.padding = '10px';
- emptyMessage.style.color = '#999';
- configList.appendChild(emptyMessage);
- return;
- }
-
- // 遍历并显示每个配置
- Object.entries(configs).forEach(([name, data]) => {
- // 创建配置项容器
- const configItem = document.createElement('div');
- configItem.style.cssText = `
- display: grid;
- grid-template-columns: 1fr auto auto auto;
- align-items: center;
- padding: 8px;
- margin: 5px 0;
- background: #3d3d3d;
- border-radius: 4px;
- gap: 10px;
- `;
-
- // 创建配置名称元素
- const nameSpan = document.createElement('span');
- nameSpan.textContent = name;
- nameSpan.style.cssText = `
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- `;
-
- // 创建读取按钮
- const loadBtn = document.createElement('button');
- loadBtn.textContent = '读取';
- loadBtn.style.cssText = `
- background: #4a90e2;
- border: none;
- color: #fff;
- padding: 3px 12px;
- cursor: pointer;
- border-radius: 3px;
- transition: background-color 0.2s;
- `;
- loadBtn.onclick = () => {
- loadConfig(data.url);
- };
-
- // 创建更新按钮
- const updateBtn = document.createElement('button');
- updateBtn.textContent = '更新';
- updateBtn.style.cssText = `
- background: #27ae60;
- border: none;
- color: #fff;
- padding: 3px 12px;
- cursor: pointer;
- border-radius: 3px;
- transition: background-color 0.2s;
- `;
- updateBtn.onclick = (e) => {
- e.stopPropagation();
- updateConfig(category, name);
- };
-
- // 创建删除按钮
- const deleteBtn = document.createElement('button');
- deleteBtn.textContent = '删除';
- deleteBtn.style.cssText = `
- background: #e74c3c;
- border: none;
- color: #fff;
- padding: 3px 12px;
- cursor: pointer;
- border-radius: 3px;
- transition: background-color 0.2s;
- `;
- deleteBtn.onclick = (e) => {
- e.stopPropagation();
- deleteConfig(category, name);
- };
-
- // 添加所有元素到配置项
- configItem.appendChild(nameSpan);
- configItem.appendChild(loadBtn);
- configItem.appendChild(updateBtn);
- configItem.appendChild(deleteBtn);
-
- // 添加配置项到列表
- configList.appendChild(configItem);
- });
- } catch (error) {
- console.error('显示配置列表时出错:', error);
- }
- }
-
- function loadConfig(url) {
- /**
- * 加载指定URL的配置
- * @param {string} url - 要加载的配置URL
- */
- try {
- if (!url) {
- console.warn('加载配置失败: URL为空');
- return;
- }
-
- // 导航到指定URL
- window.location.href = url;
- console.log('正在加载配置:', url);
- } catch (error) {
- console.error('加载配置时出错:', error);
- }
- }
-
- function deleteConfig(category, name) {
- /**
- * 删除指定类别和名称的配置
- * @param {string} category - 配置类别
- * @param {string} name - 配置名称
- */
- try {
- // 确认删除操作
- if (confirm(`确定要删除配置 "${name}" 吗?`)) {
- // 删除指定配置
- if (STATE.configs[category] && STATE.configs[category][name]) {
- delete STATE.configs[category][name];
-
- // 如果类别下没有配置了,删除整个类别
- if (Object.keys(STATE.configs[category]).length === 0) {
- delete STATE.configs[category];
- }
-
- // 保存更新后的配置
- GM_setValue('savedConfigs', STATE.configs);
-
- // 更新配置列表显示
- updateConfigList();
-
- console.log(`已删除配置: ${category}/${name}`);
- } else {
- console.warn(`配置不存在: ${category}/${name}`);
- }
- }
- } catch (error) {
- console.error('删除配置时出错:', error);
- }
- }
-
- function createConfigButton() {
- const floatingButton = document.createElement('div');
- floatingButton.style.cssText = `
- position: fixed;
- right: 20px;
- top: 50%;
- transform: translateY(-50%);
- width: 50px;
- height: 50px;
- background: linear-gradient(135deg, #3c3c28 0%, #2a2a1c 100%);
- border-radius: 25px;
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- color: #ffd700;
- font-weight: bold;
- font-family: 'Fontin SmallCaps', Arial, sans-serif;
- font-size: 18px;
- box-shadow: 0 0 20px rgba(0,0,0,0.5),
- inset 0 0 8px rgba(255, 215, 0, 0.3),
- 0 0 30px rgba(255, 215, 0, 0.2);
- border: 1px solid rgba(255, 215, 0, 0.4);
- z-index: 9998;
- transition: all 0.3s ease;
- user-select: none;
- touch-action: none;
- text-shadow: 0 0 10px rgba(255, 215, 0, 0.6);
- animation: normalGlowing 2s ease-in-out infinite;
- `;
- floatingButton.textContent = 'ST';
- floatingButton.title = 'ST工具箱 (按住可拖动)';
-
- // 添加悬停效果
- floatingButton.addEventListener('mouseenter', () => {
- if (!isDragging) {
- floatingButton.style.transform = 'scale(1.1)';
- floatingButton.style.boxShadow = `
- 0 0 25px rgba(0,0,0,0.5),
- inset 0 0 12px rgba(255, 215, 0, 0.5),
- 0 0 40px rgba(255, 215, 0, 0.3)
- `;
- floatingButton.style.color = '#ffe44d';
- floatingButton.style.textShadow = '0 0 15px rgba(255, 215, 0, 0.8)';
- floatingButton.style.border = '1px solid rgba(255, 215, 0, 0.6)';
- floatingButton.style.animation = 'none';
-
- if (isHidden) {
- showButton();
- }
- }
- });
-
- floatingButton.addEventListener('mouseleave', () => {
- if (!isDragging) {
- floatingButton.style.transform = 'scale(1)';
- floatingButton.style.boxShadow = `
- 0 0 20px rgba(0,0,0,0.5),
- inset 0 0 8px rgba(255, 215, 0, 0.3),
- 0 0 30px rgba(255, 215, 0, 0.2)
- `;
- floatingButton.style.color = '#ffd700';
- floatingButton.style.textShadow = '0 0 10px rgba(255, 215, 0, 0.6)';
- floatingButton.style.border = '1px solid rgba(255, 215, 0, 0.4)';
- floatingButton.style.animation = 'normalGlowing 2s ease-in-out infinite';
-
- checkAndHideButton();
- }
- });
-
- // 添加拖拽功能
- let isDragging = false;
- let startX, startY;
- let lastX = GM_getValue('floatingButtonX', window.innerWidth - 70);
- let lastY = GM_getValue('floatingButtonY', window.innerHeight / 2);
- let dragDistance = 0;
- let mouseDownTime = 0;
- let isHidden = false;
-
- function dragStart(e) {
- isDragging = true;
- dragDistance = 0;
- mouseDownTime = Date.now();
- const rect = floatingButton.getBoundingClientRect();
- startX = e.clientX - rect.left;
- startY = e.clientY - rect.top;
- floatingButton.style.transition = 'none';
- floatingButton.style.transform = 'scale(1)';
- }
-
- function drag(e) {
- if (!isDragging) return;
- e.preventDefault();
-
- const x = e.clientX - startX;
- const y = e.clientY - startY;
-
- // 计算拖动距离
- const dx = x - lastX;
- const dy = y - lastY;
- dragDistance += Math.sqrt(dx * dx + dy * dy);
-
- // 限制拖动范围,添加边距防止完全贴边
- const margin = 20; // 边距
- const maxX = window.innerWidth - floatingButton.offsetWidth - margin;
- const maxY = window.innerHeight - floatingButton.offsetHeight - margin;
- const minX = margin;
- const minY = margin;
-
- lastX = Math.max(minX, Math.min(x, maxX));
- lastY = Math.max(minY, Math.min(y, maxY));
-
- floatingButton.style.left = lastX + 'px';
- floatingButton.style.top = lastY + 'px';
- floatingButton.style.right = 'auto';
- }
-
- function dragEnd(e) {
- if (!isDragging) return;
-
- const dragDuration = Date.now() - mouseDownTime;
- isDragging = false;
- floatingButton.style.transition = 'all 0.3s ease';
-
- // 保存位置
- GM_setValue('floatingButtonX', lastX);
- GM_setValue('floatingButtonY', lastY);
-
- // 如果拖动距离小于5像素且时间小于200ms,则认为是点击
- if (dragDistance < 5 && dragDuration < 200) {
- document.getElementById('config-modal').style.display = 'block';
- document.getElementById('config-modal-overlay').style.display = 'block';
- }
- }
-
- function checkAndHideButton() {
- const threshold = 20; // 距离边缘多少像素时触发隐藏
- const buttonWidth = floatingButton.offsetWidth;
- const buttonHeight = floatingButton.offsetHeight;
- const buttonRight = lastX + buttonWidth;
- const buttonBottom = lastY + buttonHeight;
- const windowWidth = window.innerWidth;
- const windowHeight = window.innerHeight;
-
- // 检查各个边缘
- if (buttonRight > windowWidth - threshold) {
- // 右边缘
- hideButton('right');
- } else if (lastX < threshold) {
- // 左边缘
- hideButton('left');
- } else if (lastY < threshold) {
- // 上边缘
- hideButton('top');
- } else if (buttonBottom > windowHeight - threshold) {
- // 下边缘
- hideButton('bottom');
- }
- }
-
- function hideButton(direction) {
- isHidden = true;
- floatingButton.style.transition = 'all 0.3s ease';
-
- // 添加金光动画
- floatingButton.style.animation = 'none';
- floatingButton.offsetHeight; // 触发重绘
- floatingButton.style.animation = 'glowing 1.5s ease-in-out infinite';
- floatingButton.style.background = 'linear-gradient(135deg, #5a5a42 0%, #3a3a2c 100%)';
-
- switch (direction) {
- case 'right':
- floatingButton.style.transform = 'translateY(-50%) translateX(60%)';
- floatingButton.style.borderRadius = '25px 0 0 25px';
- break;
- case 'left':
- floatingButton.style.transform = 'translateY(-50%) translateX(-60%)';
- floatingButton.style.borderRadius = '0 25px 25px 0';
- break;
- case 'top':
- floatingButton.style.transform = 'translateX(-50%) translateY(-60%)';
- floatingButton.style.borderRadius = '0 0 25px 25px';
- break;
- case 'bottom':
- floatingButton.style.transform = 'translateX(-50%) translateY(60%)';
- floatingButton.style.borderRadius = '25px 25px 0 0';
- break;
- }
- }
-
- function showButton() {
- isHidden = false;
- floatingButton.style.transition = 'all 0.3s ease';
- floatingButton.style.animation = 'normalGlowing 2s ease-in-out infinite';
- floatingButton.style.background = 'linear-gradient(135deg, #3c3c28 0%, #2a2a1c 100%)';
- floatingButton.style.transform = 'scale(1)';
- floatingButton.style.borderRadius = '25px';
- }
-
- // 添加金光动画样式
- const glowingStyle = document.createElement('style');
- glowingStyle.textContent = `
- @keyframes normalGlowing {
- 0% {
- box-shadow: 0 0 20px rgba(0,0,0,0.5),
- inset 0 0 8px rgba(255, 215, 0, 0.3),
- 0 0 30px rgba(255, 215, 0, 0.2);
- border-color: rgba(255, 215, 0, 0.4);
- color: #ffd700;
- text-shadow: 0 0 10px rgba(255, 215, 0, 0.6);
- }
- 50% {
- box-shadow: 0 0 25px rgba(0,0,0,0.5),
- inset 0 0 12px rgba(255, 215, 0, 0.4),
- 0 0 40px rgba(255, 215, 0, 0.3),
- 0 0 60px rgba(255, 215, 0, 0.2);
- border-color: rgba(255, 215, 0, 0.5);
- color: #ffe44d;
- text-shadow: 0 0 15px rgba(255, 215, 0, 0.7);
- }
- 100% {
- box-shadow: 0 0 20px rgba(0,0,0,0.5),
- inset 0 0 8px rgba(255, 215, 0, 0.3),
- 0 0 30px rgba(255, 215, 0, 0.2);
- border-color: rgba(255, 215, 0, 0.4);
- color: #ffd700;
- text-shadow: 0 0 10px rgba(255, 215, 0, 0.6);
- }
- }
-
- @keyframes glowing {
- 0% {
- box-shadow: 0 0 20px rgba(0,0,0,0.5),
- inset 0 0 8px rgba(255, 215, 0, 0.5),
- 0 0 30px rgba(255, 215, 0, 0.4),
- 0 0 50px rgba(255, 215, 0, 0.2);
- border-color: rgba(255, 215, 0, 0.6);
- color: #ffd700;
- text-shadow: 0 0 15px rgba(255, 215, 0, 0.8);
- }
- 50% {
- box-shadow: 0 0 30px rgba(0,0,0,0.6),
- inset 0 0 20px rgba(255, 215, 0, 0.8),
- 0 0 60px rgba(255, 215, 0, 0.6),
- 0 0 100px rgba(255, 215, 0, 0.4),
- 0 0 150px rgba(255, 215, 0, 0.2);
- border-color: rgba(255, 223, 0, 1);
- color: #ffe44d;
- text-shadow: 0 0 25px rgba(255, 215, 0, 1),
- 0 0 35px rgba(255, 215, 0, 0.7),
- 0 0 45px rgba(255, 215, 0, 0.4);
- }
- 100% {
- box-shadow: 0 0 20px rgba(0,0,0,0.5),
- inset 0 0 8px rgba(255, 215, 0, 0.5),
- 0 0 30px rgba(255, 215, 0, 0.4),
- 0 0 50px rgba(255, 215, 0, 0.2);
- border-color: rgba(255, 215, 0, 0.6);
- color: #ffd700;
- text-shadow: 0 0 15px rgba(255, 215, 0, 0.8);
- }
- }
- `;
- document.head.appendChild(glowingStyle);
-
- // 监听窗口大小变化
- window.addEventListener('resize', () => {
- if (!isDragging) {
- const margin = 20;
- const maxX = window.innerWidth - floatingButton.offsetWidth - margin;
- const maxY = window.innerHeight - floatingButton.offsetHeight - margin;
- const minX = margin;
- const minY = margin;
-
- lastX = Math.max(minX, Math.min(lastX, maxX));
- lastY = Math.max(minY, Math.min(lastY, maxY));
-
- floatingButton.style.left = lastX + 'px';
- floatingButton.style.top = lastY + 'px';
- }
- });
-
- floatingButton.addEventListener('mousedown', dragStart);
- document.addEventListener('mousemove', drag);
- document.addEventListener('mouseup', dragEnd);
-
- // 恢复上次保存的位置或使用默认位置
- const margin = 20;
- const maxX = window.innerWidth - 70 - margin;
- const maxY = window.innerHeight / 2;
- const minX = margin;
- const minY = margin;
-
- lastX = Math.max(minX, Math.min(GM_getValue('floatingButtonX', maxX), maxX));
- lastY = Math.max(minY, Math.min(GM_getValue('floatingButtonY', maxY), maxY));
-
- floatingButton.style.right = 'auto';
- floatingButton.style.top = lastY + 'px';
- floatingButton.style.left = lastX + 'px';
- floatingButton.style.transform = 'scale(1)';
- floatingButton.style.transform = 'scale(1)';
-
- return floatingButton;
- }
-
- function createControls() {
- const floatingButton = createConfigButton();
- document.body.appendChild(floatingButton);
-
- // 创建搜索框但不立即显示
- createSearchBox();
-
- // 添加快捷键监听
- document.addEventListener('keydown', (e) => {
- if (e.altKey && !e.ctrlKey && !e.shiftKey && e.key.toLowerCase() === 'f') {
- e.preventDefault(); // 阻止默认行为
- toggleSearchBox();
- }
- });
- }
-
- // 切换搜索框显示状态
- function toggleSearchBox() {
- const searchBox = document.querySelector('.search-box-container');
- if (searchBox) {
- searchBox.style.display = searchBox.style.display === 'none' ? 'flex' : 'none';
- if (searchBox.style.display === 'flex') {
- // 当显示搜索框时,自动聚焦到输入框
- const searchInput = searchBox.querySelector('input');
- if (searchInput) {
- searchInput.focus();
- }
- }
- }
- }
-
- // 创建搜索框
- function createSearchBox(handleInput) {
- const searchBoxContainer = document.createElement('div');
- searchBoxContainer.className = 'search-box-container';
- searchBoxContainer.style.cssText = `
- position: fixed;
- top: 10px;
- left: 20px;
- z-index: 9999;
- background: rgba(28, 28, 28, 0.95);
- padding: 15px 10px 10px;
- border-radius: 8px;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
- border: 1px solid #444;
- display: none;
- flex-direction: column;
- gap: 8px;
- `;
-
- const searchRow = document.createElement('div');
- searchRow.style.cssText = `
- display: flex;
- gap: 8px;
- align-items: center;
- `;
-
- const navigationRow = document.createElement('div');
- navigationRow.style.cssText = `
- display: flex;
- gap: 8px;
- align-items: center;
- `;
-
- // 创建搜索输入框
- const searchInput = document.createElement('input');
- searchInput.type = 'text';
- searchInput.placeholder = '输入关键词(用;分隔)';
- searchInput.dataset.isSearchInput = 'true';
- searchInput.style.cssText = `
- width: 250px;
- padding: 5px 10px;
- border: 1px solid #666;
- border-radius: 4px;
- background: #2d2d2d;
- color: #fff;
- `;
-
- // 添加搜索事件
- searchInput.addEventListener('input', (e) => {
- if (!e?.target?.value) return;
-
- // 如果页面是繁体模式,则将输入转换为繁体
- if (!STATE.pageSimplified) {
- const cursorPosition = e.target.selectionStart;
- const text = e.target.value;
-
- requestAnimationFrame(() => {
- try {
- const convertedText = window.converter.toTraditional(text);
- if (text === convertedText) return;
-
- e.target.value = convertedText;
-
- if (typeof cursorPosition === 'number') {
- e.target.setSelectionRange(cursorPosition, cursorPosition);
- }
- } catch (error) {}
- });
- }
- });
-
- const searchButton = document.createElement('button');
- searchButton.textContent = '搜索';
- searchButton.style.cssText = `
- padding: 5px 15px;
- background: #4a90e2;
- border: none;
- border-radius: 4px;
- color: #fff;
- cursor: pointer;
- transition: background 0.2s;
- `;
-
- // 添加预设下拉框
- const presetSelectContainer = document.createElement('div');
- presetSelectContainer.style.cssText = `
- position: relative;
- width: 120px;
- `;
-
- const presetInput = document.createElement('input');
- presetInput.readOnly = true;
- presetInput.placeholder = '选择预设';
- presetInput.style.cssText = `
- width: 100%;
- padding: 5px;
- background: #2d2d2d;
- border: 1px solid #666;
- border-radius: 4px;
- color: #fff;
- cursor: pointer;
- `;
-
- // 修改预设选择框的样式
- const presetDropdown = document.createElement('div');
- presetDropdown.style.cssText = `
- display: none;
- position: absolute;
- top: 100%;
- left: 0;
- width: 200px;
- max-height: 300px;
- overflow-y: auto;
- background: #2d2d2d;
- border: 1px solid #666;
- border-radius: 4px;
- z-index: 10000;
- margin-top: 4px;
- padding-top: 30px; // 为搜索框留出空间
- color: #fff; // 添加默认文字颜色
- `;
-
- // 添加预设搜索框
- const presetSearchInput = document.createElement('input');
- presetSearchInput.type = 'text';
- presetSearchInput.placeholder = '搜索预设...';
- presetSearchInput.style.cssText = `
- position: absolute;
- top: 0;
- left: 0;
- width: calc(100% - 16px);
- margin: 8px;
- padding: 4px 8px;
- background: #3d3d3d;
- border: 1px solid #666;
- border-radius: 3px;
- color: #fff;
- font-size: 12px;
- `;
-
- // 阻止搜索框的点击事件冒泡,避免关闭下拉框
- presetSearchInput.addEventListener('click', (e) => {
- e.stopPropagation();
- });
-
- // 添加搜索框的输入事件
- presetSearchInput.addEventListener('input', (e) => {
- const searchText = e.target.value.toLowerCase();
- const options = presetDropdown.querySelectorAll('.preset-option');
- options.forEach(option => {
- const name = option.querySelector('span').textContent.toLowerCase();
- if (name.includes(searchText)) {
- option.style.display = '';
- } else {
- option.style.display = 'none';
- }
- });
- });
-
- presetDropdown.appendChild(presetSearchInput);
-
- // 添加滚动条样式
- const styleSheet = document.createElement('style');
- styleSheet.textContent = `
- .preset-dropdown::-webkit-scrollbar {
- width: 8px;
- }
- .preset-dropdown::-webkit-scrollbar-track {
- background: #1a1a1a;
- }
- .preset-dropdown::-webkit-scrollbar-thumb {
- background: #444;
- border-radius: 4px;
- }
- .preset-dropdown::-webkit-scrollbar-thumb:hover {
- background: #555;
- }
- `;
- document.head.appendChild(styleSheet);
-
- let selectedPresets = new Set();
-
- // 修改预设选项的样式
- function updatePresetOptions() {
- // 保留搜索框
- presetDropdown.innerHTML = '';
- presetDropdown.appendChild(presetSearchInput);
- presetSearchInput.value = ''; // 清空搜索框
-
- // 创建分隔线
- const createDivider = () => {
- const divider = document.createElement('div');
- divider.style.cssText = `
- height: 1px;
- background: #666;
- margin: 5px 0;
- `;
- return divider;
- };
-
- // 创建预设选项
- const createPresetOption = (name, keywords, isSelected) => {
- const option = document.createElement('div');
- option.className = 'preset-option'; // 添加类名以便搜索
- option.style.cssText = `
- padding: 6px 10px;
- cursor: pointer;
- display: flex;
- align-items: center;
- gap: 8px;
- transition: all 0.2s;
- color: #fff;
- ${isSelected ? 'background: #2a4a6d;' : ''}
- `;
-
- const checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- checkbox.checked = isSelected;
- checkbox.style.cssText = `
- margin: 0;
- cursor: pointer;
- `;
-
- const label = document.createElement('span');
- label.textContent = name;
- label.style.cssText = `
- flex: 1;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- color: #fff;
- `;
-
- option.appendChild(checkbox);
- option.appendChild(label);
-
- option.addEventListener('mouseover', () => {
- option.style.backgroundColor = isSelected ? '#2a5a8d' : '#3d3d3d';
- });
-
- option.addEventListener('mouseout', () => {
- option.style.backgroundColor = isSelected ? '#2a4a6d' : 'transparent';
- });
-
- option.addEventListener('click', (e) => {
- e.stopPropagation();
- checkbox.checked = !checkbox.checked;
- if (checkbox.checked) {
- selectedPresets.add(name);
- } else {
- selectedPresets.delete(name);
- }
- updateSelectedPresets();
- // 重新渲染预设列表以更新顺序
- updatePresetOptions();
- });
-
- return option;
- };
-
- // 获取所有预设并分类
- const selectedOptions = [];
- const unselectedOptions = [];
-
- Object.entries(STATE.searchPresets).forEach(([name, keywords]) => {
- const isSelected = selectedPresets.has(name);
- const option = createPresetOption(name, keywords, isSelected);
-
- if (isSelected) {
- selectedOptions.push(option);
- } else {
- unselectedOptions.push(option);
- }
- });
-
- // 添加已选择的预设
- if (selectedOptions.length > 0) {
- const selectedTitle = document.createElement('div');
- selectedTitle.style.cssText = `
- padding: 5px 10px;
- color: #999;
- font-size: 12px;
- background: #262626;
- `;
- selectedTitle.textContent = '已选择的预设';
- presetDropdown.appendChild(selectedTitle);
-
- selectedOptions.forEach(option => presetDropdown.appendChild(option));
- }
-
- // 添加未选择的预设
- if (selectedOptions.length > 0 && unselectedOptions.length > 0) {
- presetDropdown.appendChild(createDivider());
- }
-
- if (unselectedOptions.length > 0) {
- if (selectedOptions.length > 0) {
- const unselectedTitle = document.createElement('div');
- unselectedTitle.style.cssText = `
- padding: 5px 10px;
- color: #999;
- font-size: 12px;
- background: #262626;
- `;
- unselectedTitle.textContent = '未选择的预设';
- presetDropdown.appendChild(unselectedTitle);
- }
-
- unselectedOptions.forEach(option => presetDropdown.appendChild(option));
- }
- }
-
- function updateSelectedPresets() {
- if (selectedPresets.size === 0) {
- presetInput.value = '';
- presetInput.placeholder = '选择预设';
- } else {
- const names = Array.from(selectedPresets).join(', ');
- presetInput.value = names;
- presetInput.placeholder = '';
- }
- }
-
- function applySelectedPresets() {
- if (selectedPresets.size === 0) return;
-
- const keywords = Array.from(selectedPresets)
- .map(name => STATE.searchPresets[name])
- .join(';');
-
- searchInput.value = keywords;
-
- // 手动触发输入转换
- if (!STATE.pageSimplified) {
- try {
- const convertedText = window.converter.toTraditional(keywords);
- if (keywords !== convertedText) {
- searchInput.value = convertedText;
- }
- } catch (error) {}
- }
-
- // 触发搜索
- performSearch();
- // 清空选择
- selectedPresets.clear();
- updateSelectedPresets();
- }
-
- // 点击输入框时显示/隐藏下拉框
- presetInput.addEventListener('click', (e) => {
- e.stopPropagation();
- const isVisible = presetDropdown.style.display === 'block';
- presetDropdown.style.display = isVisible ? 'none' : 'block';
- if (!isVisible) {
- updatePresetOptions();
- // 聚焦搜索框
- setTimeout(() => {
- presetSearchInput.focus();
- }, 0);
- }
- });
-
- // 点击其他地方时隐藏下拉框
- document.addEventListener('click', () => {
- presetDropdown.style.display = 'none';
- });
-
- // 添加搜索事件
- const performSearch = () => {
- // 获取所有预设关键词
- const presetKeywords = Array.from(selectedPresets)
- .map(name => STATE.searchPresets[name])
- .join(';');
-
- // 获取输入框关键词
- const inputKeywords = searchInput.value.trim();
-
- // 合并关键词
- const combinedKeywords = [presetKeywords, inputKeywords]
- .filter(k => k) // 过滤掉空字符串
- .join(';');
-
- // 如果页面是繁体模式,则将关键词转换为繁体
- let searchKeywords = combinedKeywords;
- if (!STATE.pageSimplified) {
- try {
- searchKeywords = window.converter.toTraditional(combinedKeywords);
- } catch (error) {
- console.error('转换繁体失败:', error);
- }
- }
-
- // 使用;或;作为分隔符
- const keywords = searchKeywords.toLowerCase().split(/[;;]/);
- // 过滤掉空字符串
- const filteredKeywords = keywords.filter(k => k.trim());
-
- if (!filteredKeywords.length) {
- clearHighlights();
- matchCounter.textContent = '';
- return;
- }
- searchAndHighlight(filteredKeywords, matchCounter);
- };
-
- searchInput.addEventListener('keyup', (e) => {
- if (e.key === 'Enter') {
- performSearch();
- }
- });
-
- searchButton.addEventListener('click', performSearch);
-
- // 添加导航按钮
- const prevButton = document.createElement('button');
- prevButton.textContent = '上一个';
- prevButton.style.cssText = `
- padding: 5px 15px;
- background: #2d2d2d;
- border: 1px solid #666;
- border-radius: 4px;
- color: #fff;
- cursor: pointer;
- transition: background 0.2s;
- flex: 1;
- `;
-
- const nextButton = document.createElement('button');
- nextButton.textContent = '下一个';
- nextButton.style.cssText = `
- padding: 5px 15px;
- background: #2d2d2d;
- border: 1px solid #666;
- border-radius: 4px;
- color: #fff;
- cursor: pointer;
- transition: background 0.2s;
- flex: 1;
- `;
-
- const matchCounter = document.createElement('span');
- matchCounter.className = 'search-counter';
- matchCounter.style.cssText = `
- color: #fff;
- font-size: 12px;
- padding: 0 10px;
- min-width: 60px;
- text-align: center;
- `;
-
- // 添加导航事件
- prevButton.addEventListener('click', () => {
- navigateHighlight('prev');
- });
-
- nextButton.addEventListener('click', () => {
- navigateHighlight('next');
- });
-
- // 添加hover效果
- [searchButton, prevButton, nextButton].forEach(button => {
- button.addEventListener('mouseover', () => {
- button.style.background = button === searchButton ? '#357abd' : '#3d3d3d';
- });
- button.addEventListener('mouseout', () => {
- button.style.background = button === searchButton ? '#4a90e2' : '#2d2d2d';
- });
- });
-
- // 组装界面
- presetSelectContainer.appendChild(presetInput);
- presetSelectContainer.appendChild(presetDropdown);
-
- searchRow.appendChild(presetSelectContainer);
- searchRow.appendChild(searchInput);
- searchRow.appendChild(searchButton);
-
- navigationRow.appendChild(prevButton);
- navigationRow.appendChild(matchCounter);
- navigationRow.appendChild(nextButton);
-
- searchBoxContainer.appendChild(searchRow);
- searchBoxContainer.appendChild(navigationRow);
-
- // 添加选项行
- const optionsRow = document.createElement('div');
- optionsRow.style.cssText = `
- display: flex;
- gap: 8px;
- align-items: center;
- padding: 0 5px;
- `;
-
- const showOnlyMatchedLabel = document.createElement('label');
- showOnlyMatchedLabel.style.cssText = `
- display: flex;
- align-items: center;
- gap: 5px;
- color: #fff;
- font-size: 12px;
- cursor: pointer;
- `;
-
- const showOnlyMatchedCheckbox = document.createElement('input');
- showOnlyMatchedCheckbox.type = 'checkbox';
- showOnlyMatchedCheckbox.checked = STATE.showOnlyMatched;
- showOnlyMatchedCheckbox.style.cssText = `
- margin: 0;
- cursor: pointer;
- `;
-
- showOnlyMatchedLabel.appendChild(showOnlyMatchedCheckbox);
- showOnlyMatchedLabel.appendChild(document.createTextNode('只显示匹配项'));
-
- showOnlyMatchedCheckbox.addEventListener('change', (e) => {
- STATE.showOnlyMatched = e.target.checked;
- GM_setValue('showOnlyMatched', STATE.showOnlyMatched);
- if (STATE.matchedCards.length > 0) {
- updateCardVisibility();
- }
- });
-
- optionsRow.appendChild(showOnlyMatchedLabel);
- searchBoxContainer.insertBefore(optionsRow, navigationRow);
-
- document.body.appendChild(searchBoxContainer);
-
- // 添加关闭按钮
- const closeButton = document.createElement('button');
- closeButton.textContent = '×';
- closeButton.style.cssText = `
- position: absolute;
- top: -10px;
- right: -10px;
- width: 20px;
- height: 20px;
- line-height: 1;
- padding: 0;
- background: #2d2d2d;
- border: 1px solid #666;
- border-radius: 50%;
- color: #999;
- font-size: 16px;
- font-weight: bold;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: all 0.2s;
- z-index: 10000;
- box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
- `;
-
- closeButton.addEventListener('mouseover', () => {
- closeButton.style.color = '#fff';
- closeButton.style.background = '#3d3d3d';
- closeButton.style.borderColor = '#999';
- });
-
- closeButton.addEventListener('mouseout', () => {
- closeButton.style.color = '#999';
- closeButton.style.background = '#2d2d2d';
- closeButton.style.borderColor = '#666';
- });
-
- closeButton.addEventListener('click', () => {
- searchBoxContainer.style.display = 'none';
- clearHighlights();
- searchInput.value = '';
- matchCounter.textContent = '';
- // 不清除选择的预设
- });
-
- searchBoxContainer.insertBefore(closeButton, searchBoxContainer.firstChild);
-
- // 添加键盘快捷键
- document.addEventListener('keydown', (e) => {
- if (e.key === 'F3' || (e.ctrlKey && e.key === 'g')) {
- e.preventDefault();
- if (e.shiftKey) {
- navigateHighlight('prev');
- } else {
- navigateHighlight('next');
- }
- }
- });
-
- // 在搜索框显示时更新预设选项
- const originalToggleSearchBox = toggleSearchBox;
- toggleSearchBox = function() {
- const searchBox = document.querySelector('.search-box-container');
- if (searchBox) {
- const isCurrentlyHidden = searchBox.style.display === 'none';
- if (isCurrentlyHidden) {
- updatePresetOptions();
- // 不清除选择的预设
- updateSelectedPresets();
- }
- searchBox.style.display = isCurrentlyHidden ? 'flex' : 'none';
- if (isCurrentlyHidden) {
- const searchInput = searchBox.querySelector('input[type="text"]');
- if (searchInput) {
- searchInput.focus();
- }
- }
- }
- };
-
- return updatePresetOptions;
- }
-
- // 清除所有高亮
- function clearHighlights() {
- const highlights = document.querySelectorAll('.st-highlight');
- highlights.forEach(highlight => {
- const parent = highlight.parentNode;
- parent.replaceChild(document.createTextNode(highlight.textContent), highlight);
- });
-
- // 清除所有高亮样式
- document.querySelectorAll('.current-highlight, .matched-card').forEach(card => {
- card.classList.remove('current-highlight', 'matched-card');
- });
-
- // 重置导航状态
- STATE.matchedCards = [];
- STATE.currentMatchIndex = -1;
-
- // 恢复所有卡片的可见性
- const allCards = document.querySelectorAll('.row[data-id]');
- allCards.forEach(card => {
- card.style.display = '';
- });
- }
-
- // 修改 searchAndHighlight 函数中的关键词处理部分
- function escapeRegExp(string) {
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
- }
-
- function searchAndHighlight(keywords, matchCounter) {
- clearHighlights();
-
- const itemCards = document.querySelectorAll('.row[data-id]');
- STATE.matchedCards = [];
- let hasMatch = false;
-
- // 预处理关键词,处理特殊字符和通配符
- const processedKeywords = keywords.map(keyword => {
- keyword = keyword.trim();
-
- // 处理带条件的通配符
- // 匹配模式:(&>2) 或 (&>=2) 或 (&<2) 或 (&<=2) 或 (&=2)
- // 或带加号的版本:+(&>2) 等
- const conditionalPattern = /(\(?&(>=|<=|>|<|=)(\d+)\)?)/;
- if (conditionalPattern.test(keyword)) {
- const match = keyword.match(conditionalPattern);
- const fullMatch = match[0];
- const operator = match[2];
- const targetNum = parseInt(match[3]);
-
- // 将关键词分成前后两部分
- const [before, after] = keyword.split(fullMatch);
-
- // 构建正则表达式和验证函数
- const numPattern = '(\\d+)';
- const beforePattern = before ? escapeRegExp(before) : '';
- const afterPattern = after ? escapeRegExp(after) : '';
-
- return {
- pattern: beforePattern + numPattern + afterPattern,
- validate: (foundNum) => {
- const num = parseInt(foundNum);
- switch(operator) {
- case '>': return num > targetNum;
- case '>=': return num >= targetNum;
- case '<': return num < targetNum;
- case '<=': return num <= targetNum;
- case '=': return num === targetNum;
- default: return false;
- }
- }
- };
- }
-
- // 处理简单通配符
- if (keyword.includes('&')) {
- // 处理带加号的通配符
- if (keyword.includes('+&')) {
- keyword = escapeRegExp(keyword).replace(/\\\+&/g, '\\+[0-9]+');
- } else {
- // 处理不带加号的通配符
- keyword = escapeRegExp(keyword).replace(/&/g, '[0-9]+');
- }
- } else {
- // 处理其他特殊字符
- keyword = escapeRegExp(keyword).replace(/\\\+/g, '[++]');
- }
-
- return { pattern: keyword };
- }).filter(k => k);
-
- itemCards.forEach(card => {
- const cardText = card.textContent;
- const matches = processedKeywords.map(keyword => {
- if (!keyword.validate) {
- // 简单模式匹配
- const regex = new RegExp(keyword.pattern, 'i');
- return regex.test(cardText);
- } else {
- // 条件模式匹配
- const regex = new RegExp(keyword.pattern, 'i');
- const match = cardText.match(regex);
- if (!match) return false;
-
- // 提取数字并验证条件
- const foundNum = match[1];
- return keyword.validate(foundNum);
- }
- });
- const allMatch = matches.every(match => match);
-
- if (allMatch) {
- hasMatch = true;
- STATE.matchedCards.push(card);
- highlightKeywords(card, processedKeywords.map(k => k.pattern));
- } else if (STATE.showOnlyMatched) {
- card.style.display = 'none';
- }
- });
-
- if (!hasMatch) {
- alert('未找到匹配的结果');
- if (matchCounter) {
- matchCounter.textContent = '0/0';
- }
- } else {
- STATE.currentMatchIndex = 0;
- updateHighlightNavigation();
- updateCardVisibility();
- }
- }
-
- // 修改 highlightKeywords 函数
- function highlightKeywords(element, patterns) {
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode: function(node) {
- if (node.parentNode.nodeName === 'SCRIPT' ||
- node.parentNode.nodeName === 'STYLE' ||
- node.parentNode.classList.contains('st-highlight')) {
- return NodeFilter.FILTER_REJECT;
- }
-
- const text = node.textContent;
- const containsAnyKeyword = patterns.some(pattern => {
- const regex = new RegExp(pattern, 'i');
- return regex.test(text);
- });
-
- return containsAnyKeyword ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
- }
- }
- );
-
- const nodes = [];
- let node;
- while (node = walker.nextNode()) {
- nodes.push(node);
- }
-
- nodes.forEach(textNode => {
- let text = textNode.textContent;
- let tempText = text;
-
- patterns.forEach(pattern => {
- const regex = new RegExp(`(${pattern})`, 'gi');
- if (regex.test(text)) {
- tempText = tempText.replace(regex, (match) => {
- return `<span class="st-highlight">${match}</span>`;
- });
- }
- });
-
- if (tempText !== text) {
- const tempDiv = document.createElement('div');
- tempDiv.innerHTML = tempText;
- const fragment = document.createDocumentFragment();
- while (tempDiv.firstChild) {
- fragment.appendChild(tempDiv.firstChild);
- }
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- });
- }
-
- // 更新高亮导航
- function updateHighlightNavigation() {
- const matchCounter = document.querySelector('.search-counter');
- if (!matchCounter) return;
-
- // 更新计数器
- matchCounter.textContent = `${STATE.currentMatchIndex + 1}/${STATE.matchedCards.length}`;
-
- // 移除之前的当前高亮
- document.querySelectorAll('.current-highlight, .matched-card').forEach(card => {
- card.classList.remove('current-highlight', 'matched-card');
- });
-
- // 添加新的当前高亮
- const currentCard = STATE.matchedCards[STATE.currentMatchIndex];
- if (currentCard) {
- currentCard.classList.add('current-highlight');
- // 滚动到当前卡片
- currentCard.scrollIntoView({
- behavior: 'smooth',
- block: 'center'
- });
- }
- }
-
- // 导航到上一个/下一个高亮
- function navigateHighlight(direction) {
- if (STATE.matchedCards.length === 0) return;
-
- if (direction === 'next') {
- STATE.currentMatchIndex = (STATE.currentMatchIndex + 1) % STATE.matchedCards.length;
- } else {
- STATE.currentMatchIndex = (STATE.currentMatchIndex - 1 + STATE.matchedCards.length) % STATE.matchedCards.length;
- }
-
- updateHighlightNavigation();
- }
-
- // 修改样式
- const style = document.createElement('style');
- style.textContent = `
- .current-highlight {
- background-color: rgba(255, 215, 0, 0.3) !important;
- }
- .matched-card {
- background-color: rgba(255, 215, 0, 0.1) !important;
- }
- .st-highlight {
- background-color: #ffd700;
- color: #000;
- border-radius: 2px;
- padding: 0 2px;
- }
- `;
- document.head.appendChild(style);
-
- function watchSearchResults(converter) {
- let lastUrl = location.href;
- const urlObserver = setInterval(() => {
- if (location.href !== lastUrl) {
- lastUrl = location.href;
- STATE.originalTexts = new WeakMap();
- setTimeout(() => {
- convertPageText(converter);
- }, 500);
- }
- }, 100);
-
- // 监视搜索结果变化
- const resultObserver = new MutationObserver((mutations) => {
- let needsConversion = false;
- for (const mutation of mutations) {
- if (mutation.type === 'childList' || mutation.type === 'characterData') {
- needsConversion = true;
- break;
- }
- }
- if (needsConversion) {
- setTimeout(() => convertPageText(converter), 100);
- }
- });
-
- const resultsContainer = document.querySelector('.results-container');
- if (resultsContainer) {
- resultObserver.observe(resultsContainer, {
- childList: true,
- subtree: true,
- characterData: true
- });
- }
- }
-
- function findReactInstance(element) {
- const key = Object.keys(element).find(key => key.startsWith('__reactFiber$'));
- return key ? element[key] : null;
- }
-
- function findLoadMoreHandler() {
- const loadMoreBtn = document.querySelector('.load-more-btn');
- if (!loadMoreBtn) {
- console.log('未找到加载更多按钮');
- return null;
- }
-
- // 尝试获取React实例
- const instance = findReactInstance(loadMoreBtn);
- if (!instance) {
- console.log('未找到React实例');
- return null;
- }
-
- // 遍历查找onClick处理函数
- let current = instance;
- while (current) {
- if (current.memoizedProps && current.memoizedProps.onClick) {
- return current.memoizedProps.onClick;
- }
- current = current.return;
- }
-
- console.log('未找到onClick处理函数');
- return null;
- }
-
- function clickLoadMoreIfExists() {
- // 使用正确的选择器
- const loadMoreBtn = document.querySelector('.btn.load-more-btn');
- if (!loadMoreBtn) {
- console.log('未找到加载更多按钮');
- return false;
- }
-
- const results = document.querySelectorAll('.resultset, .trade-result, [class*="result-item"]');
- const currentResultCount = results.length;
-
- if (currentResultCount >= 100) {
- return false;
- }
-
- try {
- // 尝试多种方式触发点击
- // 1. 原生点击
- loadMoreBtn.click();
-
- // 2. 模拟鼠标事件序列
- ['mousedown', 'mouseup', 'click'].forEach(eventType => {
- const event = new MouseEvent(eventType, {
- bubbles: true,
- cancelable: true,
- buttons: 1
- });
- loadMoreBtn.dispatchEvent(event);
- });
-
- // 3. 尝试点击内部的span
- const spanInButton = loadMoreBtn.querySelector('span');
- if (spanInButton) {
- spanInButton.click();
- ['mousedown', 'mouseup', 'click'].forEach(eventType => {
- const event = new MouseEvent(eventType, {
- bubbles: true,
- cancelable: true,
- buttons: 1
- });
- spanInButton.dispatchEvent(event);
- });
- }
-
- // 4. 使用 HTMLElement 的 click 方法
- HTMLElement.prototype.click.call(loadMoreBtn);
-
- return true;
- } catch (error) {
- console.log('触发加载更多时出错:', error);
- return false;
- }
- }
-
- function autoLoadAllResults() {
- let attempts = 0;
- const maxAttempts = 20;
- let lastResultCount = 0;
-
- function tryLoadMore() {
- const results = document.querySelectorAll('.resultset');
- const currentResultCount = results.length;
-
- if (currentResultCount >= 100 || attempts >= maxAttempts ||
- (currentResultCount === lastResultCount && attempts > 0)) {
- return;
- }
-
- if (clickLoadMoreIfExists()) {
- lastResultCount = currentResultCount;
- attempts++;
- // 修改加载更多的处理方式
- setTimeout(() => {
- // 确保新内容加载后计算DPS
- const newResults = document.querySelectorAll('.row[data-id]').length;
- if (newResults > currentResultCount) {
- calculateDPS();
- }
- tryLoadMore();
- }, 1000);
- }
- }
-
- setTimeout(tryLoadMore, 1000);
- }
-
- // 检查URL是否是搜索结果页面
- function isSearchResultPage() {
- const isPOE2Trade = window.location.href.includes('pathofexile.com/trade2/search/poe2');
- const hasResults = document.querySelector('.results-container, .trade-results, .search-results, [class*="results"]') !== null;
- return isPOE2Trade && hasResults;
- }
-
- // 将这些函数移到init函数外部
- function createDPSPanel() {
- const panel = document.createElement('div');
- panel.id = 'dps-sort-panel';
- panel.style.cssText = `
- position: fixed;
- right: 20px;
- top: 50%;
- transform: translateY(-50%);
- background: rgba(28, 28, 28, 0.95);
- padding: 8px;
- border-radius: 6px;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
- border: 1px solid #444;
- width: 200px; // 增加宽度以适应价格显示
- max-height: 60vh;
- z-index: 9997;
- display: none;
- `;
-
- // 添加标题
- const title = document.createElement('div');
- title.style.cssText = `
- font-weight: bold;
- color: #FFD700;
- margin-bottom: 6px;
- padding-bottom: 3px;
- border-bottom: 1px solid #444;
- display: flex;
- justify-content: space-between;
- align-items: center;
- cursor: pointer;
- font-size: 14px;
- user-select: none;
- height: 18px;
- `;
-
- // 添加展开/收起指示器
- const indicator = document.createElement('span');
- indicator.textContent = '▼';
- indicator.style.marginRight = '3px';
- indicator.style.fontSize = '10px';
- indicator.id = 'integrated-panel-indicator';
-
- const titleText = document.createElement('span');
- titleText.textContent = 'DPS';
-
- const titleLeft = document.createElement('div');
- titleLeft.style.display = 'flex';
- titleLeft.style.alignItems = 'center';
- titleLeft.appendChild(indicator);
- titleLeft.appendChild(titleText);
-
- title.appendChild(titleLeft);
-
- // 添加关闭按钮
- const closeBtn = document.createElement('button');
- closeBtn.textContent = '×';
- closeBtn.style.cssText = `
- background: none;
- border: none;
- color: #999;
- font-size: 20px;
- cursor: pointer;
- padding: 0 5px;
- `;
- closeBtn.onclick = (e) => {
- e.stopPropagation();
- panel.style.display = 'none';
- };
- title.appendChild(closeBtn);
-
- // 添加内容容器
- const content = document.createElement('div');
- content.id = 'dps-sort-content';
- content.style.cssText = `
- max-height: calc(60vh - 35px);
- overflow-y: auto;
- transition: max-height 0.3s ease-out;
- padding-right: 2px;
- `;
-
- // 添加展开/收起功能
- let isExpanded = true;
- title.onclick = () => {
- isExpanded = !isExpanded;
- content.style.maxHeight = isExpanded ? 'calc(60vh - 35px)' : '0';
- content.style.overflow = isExpanded ? 'auto' : 'hidden';
- indicator.textContent = isExpanded ? '▼' : '▶';
-
- // 调整护盾面板位置
- const shieldPanel = document.getElementById('shield-sort-panel');
- if (shieldPanel) {
- if (isExpanded) {
- shieldPanel.style.transform = 'translateY(calc(-50% + 200px))';
- } else {
- shieldPanel.style.transform = 'translateY(calc(-50% + 50px))';
- }
- }
- };
-
- // 添加滚动条样式
- const style = document.createElement('style');
- style.textContent = `
- #dps-sort-content::-webkit-scrollbar {
- width: 6px;
- }
- #dps-sort-content::-webkit-scrollbar-track {
- background: #1a1a1a;
- }
- #dps-sort-content::-webkit-scrollbar-thumb {
- background: #444;
- border-radius: 3px;
- }
- #dps-sort-content::-webkit-scrollbar-thumb:hover {
- background: #555;
- }
- .dps-item {
- padding: 4px 8px;
- margin: 1px 0;
- background: #2d2d2d;
- border-radius: 3px;
- cursor: pointer;
- transition: background 0.2s;
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 8px;
- font-size: 13px;
- white-space: nowrap;
- user-select: none;
- line-height: 1.2;
- }
- .dps-item:hover {
- background: #3d3d3d;
- }
- .dps-item:last-child {
- margin-bottom: 0;
- }
- .dps-value {
- color: #FFD700;
- font-weight: bold;
- }
- .price-value {
- color: #8acdff;
- font-size: 12px;
- text-align: right;
- }
- `;
-
- panel.appendChild(title);
- panel.appendChild(content);
- document.head.appendChild(style);
- document.body.appendChild(panel);
-
- return panel;
- }
-
- // 将convertCurrencyText函数移到全局作用域
- function convertCurrencyText(currencyText) {
- if (!currencyText) return '';
- if (currencyText.includes('Exalted') || currencyText.includes('exalted')) return 'E';
- if (currencyText.includes('Divine') || currencyText.includes('divine')) return 'D';
- if (currencyText.includes('Chaos') || currencyText.includes('chaos')) return 'C';
- if (currencyText.includes('崇高')) return 'E';
- if (currencyText.includes('神圣')) return 'D';
- if (currencyText.includes('混沌')) return 'C';
- return currencyText; // 其他货币保持原样
- }
-
- function updateDPSPanel() {
- try {
- console.log('Updating DPS panel...');
-
- // 获取或创建面板
- const panel = document.getElementById('integrated-sort-panel') || createIntegratedPanel();
- const content = document.getElementById('dps-sort-content');
- if (!content) {
- console.error('DPS sort content not found');
- return;
- }
-
- // 清空现有内容
- content.innerHTML = '';
-
- // 清除可能存在的旧数据属性
- content.removeAttribute('data-shield-content');
- content.removeAttribute('data-evasion-content');
- content.removeAttribute('data-armour-content');
- content.removeAttribute('data-defence-content');
-
- const items = document.querySelectorAll('.row[data-id]');
- const dpsData = [];
-
- items.forEach(item => {
- const dpsDisplay = item.querySelector('.dps-display');
- if (dpsDisplay) {
- const dps = parseInt(dpsDisplay.textContent.replace('DPS: ', ''));
- // 修改价格获取逻辑
- const priceElement = item.querySelector('.price [data-field="price"]');
- let price = '未标价';
- if (priceElement) {
- const amount = priceElement.querySelector('span:not(.price-label):not(.currency-text)');
- const currencyText = priceElement.querySelector('.currency-text');
- if (amount && currencyText) {
- // 获取数量和转换后的货币类型
- const amountText = amount.textContent.trim();
- const currency = currencyText.querySelector('span')?.textContent || currencyText.textContent;
- const simpleCurrency = convertCurrencyText(currency);
- price = `${amountText}${simpleCurrency}`;
- }
- }
-
- dpsData.push({
- dps,
- price,
- element: item
- });
- }
- });
-
- // 如果没有DPS数据,隐藏DPS选项卡
- const dpsTab = panel.querySelector('[data-tab="dps"]');
- if (dpsData.length === 0) {
- if (dpsTab) dpsTab.style.display = 'none';
-
- // 如果护盾选项卡也是隐藏的,则隐藏整个面板
- const shieldTab = panel.querySelector('[data-tab="shield"]');
- const evasionTab = panel.querySelector('[data-tab="evasion"]');
- const armourTab = panel.querySelector('[data-tab="armour"]');
- const defenceTab = panel.querySelector('[data-tab="defence"]');
-
- // 检查是否所有选项卡都隐藏了
- if ((shieldTab && shieldTab.style.display === 'none') &&
- (evasionTab && evasionTab.style.display === 'none') &&
- (armourTab && armourTab.style.display === 'none') &&
- (defenceTab && defenceTab.style.display === 'none')) {
- panel.style.display = 'none';
- }
- // 移除自动切换到护盾选项卡的代码,保留当前选项卡状态
- return;
- } else {
- // 有DPS数据,确保DPS选项卡可见
- if (dpsTab) dpsTab.style.display = '';
- }
-
- // 按DPS从高到低排序
- dpsData.sort((a, b) => b.dps - a.dps);
-
- // 更新面板内容
- dpsData.forEach(({dps, price, element}) => {
- const dpsItem = document.createElement('div');
- dpsItem.className = 'dps-item';
-
- // 创建DPS显示
- const dpsText = document.createElement('span');
- dpsText.className = 'dps-value';
- dpsText.textContent = dps.toString();
-
- // 创建价格显示
- const priceText = document.createElement('span');
- priceText.className = 'price-value';
- priceText.textContent = price;
-
- // 添加到DPS项
- dpsItem.appendChild(dpsText);
- dpsItem.appendChild(priceText);
-
- dpsItem.onclick = () => {
- element.scrollIntoView({ behavior: 'smooth', block: 'center' });
- // 添加高亮效果
- element.style.transition = 'background-color 0.3s';
- element.style.backgroundColor = 'rgba(255, 215, 0, 0.2)';
- setTimeout(() => {
- element.style.backgroundColor = '';
- }, 1500);
- };
- content.appendChild(dpsItem);
- });
-
- panel.style.display = 'block';
- } catch (error) {
- console.error('Error updating DPS panel:', error);
- }
- }
-
- function calculateDPS() {
- console.log('Calculating DPS...'); // 添加调试日志
- const items = document.querySelectorAll('.row[data-id]');
- console.log('Found items:', items.length); // 添加调试日志
-
- items.forEach(item => {
- // 查找已有的DPS显示,如果存在则跳过
- if (item.querySelector('.dps-display')) return;
-
- // 获取元素伤害
- const edamageSpan = item.querySelector('span[data-field="edamage"]');
- if (!edamageSpan) return;
-
- // 初始化伤害值
- let minTotal = 0;
- let maxTotal = 0;
-
- // 获取所有元素伤害值
- const damages = {
- fire: edamageSpan.querySelector('.colourFireDamage'),
- lightning: edamageSpan.querySelector('.colourLightningDamage'),
- cold: edamageSpan.querySelector('.colourColdDamage')
- };
-
- // 处理每种元素伤害
- Object.values(damages).forEach(dmg => {
- if (dmg) {
- const [min, max] = dmg.textContent.split('-').map(Number);
- if (!isNaN(min) && !isNaN(max)) {
- minTotal += min;
- maxTotal += max;
- }
- }
- });
-
- // 获取元素增伤
- let elementInc = 1;
- const elementIncSpan = item.querySelector('span[data-field="stat.explicit.stat_387439868"]');
- if (elementIncSpan) {
- const incMatch = elementIncSpan.textContent.match(/(\d+)%/);
- if (incMatch) {
- elementInc = 1 + (parseInt(incMatch[1]) / 100);
- }
- }
-
- // 获取攻击速度
- let aps = 1;
- const apsSpan = item.querySelector('span[data-field="aps"]');
- if (apsSpan) {
- let apsValue = apsSpan.querySelector('.colourDefault');
- if (!apsValue) {
- apsValue = apsSpan.querySelector('.colourAugmented');
- }
- if (apsValue) {
- aps = parseFloat(apsValue.textContent) || 1;
- }
- }
-
- // 计算DPS
- const dps = ((minTotal + maxTotal) / 2) * elementInc * aps;
- console.log('minTotal:', minTotal);
- console.log('maxTotal:', maxTotal);
- console.log('elementInc:', elementInc);
- console.log('aps:', aps);
- console.log('dps:', dps);
-
- // 创建DPS显示元素
- const dpsDisplay = document.createElement('span');
- dpsDisplay.className = 'dps-display';
- dpsDisplay.style.cssText = `
- color: #FFD700;
- font-weight: bold;
- margin-left: 10px;
- `;
- dpsDisplay.textContent = `DPS: ${Math.round(dps)}`;
-
- // 将DPS显示添加到元素伤害后面
- edamageSpan.appendChild(dpsDisplay);
- });
-
- // 更新DPS排序面板
- updateDPSPanel();
- }
-
- // 创建一个防抖函数来避免过于频繁的计算
- function debounce(func, wait) {
- let timeout;
- return function executedFunction(...args) {
- const later = () => {
- clearTimeout(timeout);
- func(...args);
- };
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- };
- }
-
- async function init() {
- /**
- * 初始化脚本,设置转换器、观察器和UI元素
- */
- try {
- // 等待页面完全加载
- await new Promise(resolve => setTimeout(resolve, 100));
-
- // 等待OpenCC库加载
- const OpenCC = await waitForOpenCC();
- console.log('OpenCC已加载');
-
- // 创建转换器
- const converter = createConverters(OpenCC);
- window.converter = converter;
-
- // 创建输入处理函数
- const handleInput = createInputHandler(converter);
-
- // 为现有输入元素添加事件监听器
- attachInputListener(handleInput);
-
- // 创建观察器监听DOM变化
- const observer = createObserver(handleInput, converter);
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
-
- // 创建控制面板
- createControls();
-
- // 创建配置按钮
- createConfigButton();
-
- // 创建配置模态框
- createConfigModal();
-
- // 设置配置模态框事件
- setupConfigModalEvents();
-
- // 创建搜索框
- createSearchBox(handleInput);
-
- // 如果启用了页面简体化,转换页面文本
- if (STATE.pageSimplified) {
- convertPageText(converter);
- }
-
- // 监视搜索结果
- watchSearchResults(converter);
-
- // 定期转换页面文本
- setInterval(() => {
- if (STATE.pageSimplified) {
- convertPageText(converter);
- }
- }, 1000);
-
- // 使用防抖包装calculateDPS和calculateShield
- const debouncedCalculateDPS = debounce(calculateDPS, 200);
- const debouncedCalculateShield = debounce(calculateShield, 200);
- const debouncedCalculateEvasion = debounce(calculateEvasion, 200);
- const debouncedCalculateArmour = debounce(calculateArmour, 200);
- const debouncedCalculateDefence = debounce(calculateDefence, 200);
-
- // 同时计算DPS、护盾、闪避、护甲和总防御的防抖函数
- const debouncedCalculate = debounce(() => {
- calculateDPS();
- calculateShield();
- calculateEvasion();
- calculateArmour();
- calculateDefence();
- }, 200);
-
- // 监听搜索按钮点击事件
- const setupSearchButtonListener = () => {
- // 使用MutationObserver监听搜索按钮的出现
- const searchBtnObserver = new MutationObserver((mutations, observer) => {
- const searchBtn = document.querySelector('.controls-center .search-btn');
- if (searchBtn) {
- console.log('搜索按钮已找到,添加点击事件监听器');
- searchBtn.addEventListener('click', () => {
- console.log('搜索按钮被点击,触发计算...');
- // 延迟执行计算,确保搜索结果已加载
- setTimeout(() => {
- debouncedCalculate();
- }, 1000);
- });
- observer.disconnect(); // 找到按钮后停止观察
- }
- });
-
- // 开始观察文档
- searchBtnObserver.observe(document.body, {
- childList: true,
- subtree: true
- });
-
- // 立即检查一次,以防按钮已经存在
- const searchBtn = document.querySelector('.controls-center .search-btn');
- if (searchBtn) {
- console.log('搜索按钮已存在,添加点击事件监听器');
- searchBtn.addEventListener('click', () => {
- console.log('搜索按钮被点击,触发计算...');
- // 延迟执行计算,确保搜索结果已加载
- setTimeout(() => {
- debouncedCalculate();
- }, 1000);
- });
- }
- };
-
- // 设置搜索按钮监听器
- setupSearchButtonListener();
-
- // 创建一个更强大的观察器来监视DOM变化
- const resultsObserver = new MutationObserver((mutations) => {
- let hasNewContent = false;
- mutations.forEach(mutation => {
- // 检查是否有新的结果项被添加
- if (mutation.type === 'childList' &&
- mutation.addedNodes.length > 0 &&
- Array.from(mutation.addedNodes).some(node =>
- node.nodeType === 1 && // 元素节点
- (node.classList?.contains('row') || node.querySelector?.('.row[data-id]'))
- )) {
- hasNewContent = true;
- }
- });
-
- if (hasNewContent) {
- console.log('New content detected, calculating DPS, Shield, Evasion and Armour...'); // 添加调试日志
- // 使用延时确保DOM完全更新
- setTimeout(() => {
- debouncedCalculate();
- }, 300);
- }
- });
-
- // 观察整个结果容器及其子元素
- const resultsContainer = document.querySelector('.results-container');
- if (resultsContainer) {
- resultsObserver.observe(resultsContainer, {
- childList: true,
- subtree: true,
- attributes: true,
- characterData: true
- });
-
- // 同时观察父元素,以防结果容器被替换
- if (resultsContainer.parentNode) {
- resultsObserver.observe(resultsContainer.parentNode, {
- childList: true
- });
- }
- }
-
- // 添加滚动事件监听器
- window.addEventListener('scroll', () => {
- // 检查是否滚动到底部附近
- if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 1000) {
- debouncedCalculate();
- }
- });
-
- // 将函数添加到window对象,以便其他地方可以调用
- window.calculateDPS = calculateDPS;
- window.calculateShield = calculateShield;
- window.calculateEvasion = calculateEvasion;
- window.calculateArmour = calculateArmour;
- window.calculateDefence = calculateDefence;
- window.debouncedCalculateDPS = debouncedCalculateDPS;
- window.debouncedCalculateShield = debouncedCalculateShield;
- window.debouncedCalculateEvasion = debouncedCalculateEvasion;
- window.debouncedCalculateArmour = debouncedCalculateArmour;
- window.debouncedCalculateDefence = debouncedCalculateDefence;
- window.debouncedCalculate = debouncedCalculate;
-
- // 监听URL变化
- let lastUrl = location.href;
- const urlCheckInterval = setInterval(() => {
- const currentUrl = location.href;
- if ((currentUrl !== lastUrl || currentUrl.includes('pathofexile.com/trade2/search/poe2')) && STATE.autoLoadEnabled) {
- lastUrl = currentUrl;
- setTimeout(() => {
- if (isSearchResultPage()) {
- autoLoadAllResults();
- }
- }, 100);
- }
- }, 100);
-
- // 初始检查
- setTimeout(() => {
- if (isSearchResultPage() && STATE.autoLoadEnabled) {
- autoLoadAllResults();
- }
- }, 100);
-
- // 延迟初始计算,确保页面元素已加载
- setTimeout(() => {
- // 初始计算DPS和护盾
- calculateDPS();
- calculateShield();
- calculateEvasion();
- calculateArmour();
- calculateDefence();
-
- // 如果没有找到物品,设置重试
- const retryCalculation = () => {
- const items = document.querySelectorAll('.row[data-id]');
- if (items.length > 0) {
- console.log('重试计算,找到物品数量:', items.length);
- calculateDPS();
- calculateShield();
- calculateEvasion();
- calculateArmour();
- calculateDefence();
- } else {
- console.log('未找到物品,500ms后重试...');
- setTimeout(retryCalculation, 500);
- }
- };
-
- // 如果初始计算没有找到物品,启动重试机制
- const items = document.querySelectorAll('.row[data-id]');
- if (items.length === 0) {
- console.log('初始计算未找到物品,启动重试机制');
- setTimeout(retryCalculation, 500);
- }
- }, 500);
-
- } catch (error) {
- console.error('初始化时出错:', error);
- }
- }
-
- // 修改 updateConfig 函数
- function updateConfig(category, name) {
- if (confirm(`确定要用当前页面更新配置 "${name}" 吗?`)) {
- STATE.configs[category][name] = {
- url: window.location.href
- };
- GM_setValue('savedConfigs', STATE.configs);
- updateConfigList();
- }
- }
-
- // 添加预设关键词相关函数
- function saveSearchPreset() {
- const nameInput = document.getElementById('preset-name');
- const keywordsInput = document.getElementById('preset-keywords');
- const name = nameInput.value.trim();
- const keywords = keywordsInput.value.trim();
- const saveBtn = document.getElementById('save-preset');
-
- if (!name || !keywords) {
- alert('请输入预设名称和关键词');
- return;
- }
-
- // 检查是否在编辑模式
- if (nameInput.dataset.editMode === 'true') {
- const originalName = nameInput.dataset.originalName;
- // 如果名称改变了,删除旧的预设
- if (originalName !== name) {
- delete STATE.searchPresets[originalName];
- }
- // 清除编辑模式标记
- delete nameInput.dataset.editMode;
- delete nameInput.dataset.originalName;
- saveBtn.textContent = '保存预设';
- } else if (STATE.searchPresets[name] && !confirm(`预设 "${name}" 已存在,是否覆盖?`)) {
- return;
- }
-
- STATE.searchPresets[name] = keywords;
- GM_setValue('searchPresets', STATE.searchPresets);
- updatePresetList();
-
- nameInput.value = '';
- keywordsInput.value = '';
- }
-
- function updatePresetList() {
- const presetList = document.getElementById('preset-list');
- presetList.innerHTML = '';
-
- Object.entries(STATE.searchPresets).forEach(([name, keywords]) => {
- const presetItem = document.createElement('div');
- presetItem.style.cssText = `
- display: grid;
- grid-template-columns: 1fr auto auto;
- align-items: center;
- padding: 8px;
- margin: 5px 0;
- background: #3d3d3d;
- border-radius: 4px;
- gap: 10px;
- `;
-
- const nameSpan = document.createElement('span');
- nameSpan.textContent = name; // 只显示预设名称
- nameSpan.title = keywords; // 将关键词设置为提示文本
- nameSpan.style.cssText = `
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- cursor: help; // 添加提示光标
- `;
-
- const editBtn = document.createElement('button');
- editBtn.textContent = '编辑';
- editBtn.style.cssText = `
- background: #27ae60;
- border: none;
- color: #fff;
- padding: 3px 12px;
- cursor: pointer;
- border-radius: 3px;
- `;
- editBtn.onclick = () => {
- const presetEditModal = document.getElementById('preset-edit-modal');
- const presetEditOverlay = document.getElementById('preset-edit-overlay');
- const presetEditTitle = document.getElementById('preset-edit-title');
- const nameInput = document.getElementById('preset-name');
- const keywordsInput = document.getElementById('preset-keywords');
-
- presetEditTitle.textContent = '编辑预设';
- nameInput.value = name;
- keywordsInput.value = keywords;
- nameInput.dataset.editMode = 'true';
- nameInput.dataset.originalName = name;
-
- presetEditModal.style.display = 'block';
- presetEditOverlay.style.display = 'block';
- };
-
- const deleteBtn = document.createElement('button');
- deleteBtn.textContent = '删除';
- deleteBtn.style.cssText = `
- background: #e74c3c;
- border: none;
- color: #fff;
- padding: 3px 12px;
- cursor: pointer;
- border-radius: 3px;
- `;
- deleteBtn.onclick = () => {
- if (confirm(`确定要删除预设 "${name}" 吗?`)) {
- delete STATE.searchPresets[name];
- GM_setValue('searchPresets', STATE.searchPresets);
- updatePresetList();
- }
- };
-
- presetItem.appendChild(nameSpan);
- presetItem.appendChild(editBtn);
- presetItem.appendChild(deleteBtn);
- presetList.appendChild(presetItem);
- });
- }
-
- setTimeout(init, 2000);
-
- // 在clearHighlights函数后添加
- function updateCardVisibility() {
- const allCards = document.querySelectorAll('.row[data-id]');
-
- if (STATE.showOnlyMatched) {
- // 如果启用了"只显示匹配项",隐藏所有非匹配卡片
- allCards.forEach(card => {
- if (STATE.matchedCards.includes(card)) {
- card.style.display = '';
- } else {
- card.style.display = 'none';
- }
- });
- } else {
- // 如果禁用了"只显示匹配项",显示所有卡片
- allCards.forEach(card => {
- card.style.display = '';
- });
- }
- }
-
- // 计算护盾值
- function calculateShield() {
- console.log('Calculating Shield...');
- const items = document.querySelectorAll('.row[data-id]');
- console.log('Found items:', items.length);
-
- let processedCount = 0;
- let successCount = 0;
-
- items.forEach((item, index) => {
- try {
- // 如果已经计算过护盾值,则跳过
- if (item.querySelector('.shield-display')) {
- processedCount++;
- return;
- }
-
- // 1. 获取符文孔数量
- const socketsDiv = item.querySelector('.sockets');
- if (!socketsDiv) {
- console.debug(`Item ${index}: No sockets div found`);
- return;
- }
-
- // 修改符文孔数量的获取逻辑
- let numSockets = 0;
- for (let i = 1; i <= 6; i++) { // 假设最多6个符文孔
- if (socketsDiv.classList.contains(`numSockets${i}`)) {
- numSockets = i;
- break;
- }
- }
- if (numSockets === 0) {
- // 尝试其他方式获取符文孔数量
- const socketText = socketsDiv.textContent.trim();
- if (socketText) {
- numSockets = socketText.length;
- } else {
- numSockets = 1; // 默认值
- }
- console.debug(`Item ${index}: Using alternative socket count: ${numSockets}`);
- }
-
- // 2. 获取能量护盾值
- const esSpan = item.querySelector('span[data-field="es"]');
- if (!esSpan) {
- console.debug(`Item ${index}: No ES span found`);
- return;
- }
-
- const esValue = esSpan.querySelector('.colourAugmented, .colourDefault');
- if (!esValue) {
- console.debug(`Item ${index}: No ES value found`);
- return;
- }
-
- const totalES = parseInt(esValue.textContent);
- if (isNaN(totalES)) {
- console.debug(`Item ${index}: Invalid ES value: ${esValue.textContent}`);
- return;
- }
-
- // 3. 获取当前品质
- let currentQuality = 0;
- const qualitySpan = item.querySelector('span[data-field="quality"]');
- if (qualitySpan) {
- const qualityMatch = qualitySpan.textContent.match(/\+(\d+)%/);
- if (qualityMatch) {
- currentQuality = parseInt(qualityMatch[1]);
- }
- }
- // 计算品质差值
- const qualityDiff = 20 - currentQuality;
-
- // 4. 获取护盾增加百分比
- let esInc = 0;
-
- // 定义可能包含护盾增加的词缀ID列表
- const possibleESIncIds = [
- 'stat.explicit.stat_4015621042', // 原有ID
- 'stat.explicit.stat_1999113824', // 增加闪避与能量护盾
- 'stat.explicit.stat_2866361420', // 增加能量护盾
- 'stat.explicit.stat_2511217560', // 增加最大能量护盾
- 'stat.explicit.stat_3489782002', // 增加能量护盾和魔力
- 'stat.explicit.stat_3321629045' // 增加护甲与能量护盾
- ];
-
- // 遍历所有可能的词缀ID
- for (const statId of possibleESIncIds) {
- const statSpan = item.querySelector(`span[data-field="${statId}"]`);
- if (statSpan) {
- // 检查词缀文本是否包含"能量护盾"或"Energy Shield"
- const statText = statSpan.textContent;
- if (statText.includes('能量护盾') || statText.includes('Energy Shield')) {
- // 提取百分比数值
- const incMatch = statText.match(/(\d+)%/);
- if (incMatch) {
- esInc += parseInt(incMatch[1]);
- console.debug(`Found ES increase: ${incMatch[1]}% from stat ${statId}`);
- }
- }
- }
- }
-
- // 通用方法:查找所有包含"能量护盾"或"Energy Shield"的显式词缀
- const allExplicitStats = item.querySelectorAll('span[data-field^="stat.explicit.stat_"]');
- allExplicitStats.forEach(statSpan => {
- // 检查是否已经在上面的特定ID列表中处理过
- const statId = statSpan.getAttribute('data-field');
- if (!possibleESIncIds.includes(statId)) {
- const statText = statSpan.textContent;
- // 检查是否包含能量护盾相关文本和百分比增加
- if ((statText.includes('能量护盾') || statText.includes('Energy Shield')) &&
- statText.includes('%') &&
- (statText.includes('增加') || statText.includes('increased'))) {
- const incMatch = statText.match(/(\d+)%/);
- if (incMatch) {
- esInc += parseInt(incMatch[1]);
- console.debug(`Found additional ES increase: ${incMatch[1]}% from stat ${statId}`);
- // 将新发现的ID添加到列表中,以便将来使用
- possibleESIncIds.push(statId);
- }
- }
- }
- });
-
- // 5. 获取已插入的符文提供的增益和数量
- let insertedRuneBonus = 0;
- let insertedRuneCount = 0;
- let esRuneInc = 0; // 新增:专门用于记录护盾符文的增益
-
- // 获取符文增益 - 能量护盾增加
- const esRuneStatSpan = item.querySelector('span[data-field="stat.rune.stat_3523867985"]');
- if (esRuneStatSpan) {
- const runeMatch = esRuneStatSpan.textContent.match(/(\d+)%/);
- if (runeMatch) {
- insertedRuneBonus = parseInt(runeMatch[1]);
- insertedRuneCount = insertedRuneBonus / 20;
- }
- }
-
- // 获取符文增益 - 能量护盾百分比增加
- const esIncRuneStatSpan = item.querySelector('span[data-field="stat.rune.stat_2866361420"]');
- if (esIncRuneStatSpan) {
- const esIncRuneMatch = esIncRuneStatSpan.textContent.match(/(\d+)%/);
- if (esIncRuneMatch) {
- esRuneInc = parseInt(esIncRuneMatch[1]);
- }
- }
-
- // 6. 计算可用的符文孔位增益
- let availableRuneBonus = 0;
- // 计算剩余可用的孔数
- const remainingSlots = numSockets - insertedRuneCount;
- availableRuneBonus = remainingSlots * 20;
-
- // 7. 计算基础护盾
- // 当前护盾 = 基础护盾 * (1 + 当前品质/100) * (1 + (已插入符文增益 + 装备增益 + 护盾符文增益)/100)
- const baseES = Math.round(totalES / (1 + currentQuality/100) / (1 + (insertedRuneBonus + esInc + esRuneInc)/100));
-
- // 8. 计算最大可能护盾(考虑品质提升和剩余符文孔)
- // 最大护盾 = 基础护盾 * (1 + (当前品质 + 品质差值)/100) * (1 + (装备增益 + 已插入符文增益 + 可用符文增益)/100)
- const maxES = Math.round(baseES * (1 + (currentQuality + qualityDiff)/100) * (1 + (esInc + insertedRuneBonus + availableRuneBonus)/100));
-
- // 创建护盾显示元素
- const shieldDisplay = document.createElement('span');
- shieldDisplay.className = 'shield-display';
- shieldDisplay.style.cssText = `
- color: #6495ED;
- font-weight: bold;
- margin-left: 10px;
- `;
- shieldDisplay.textContent = `总护盾: ${maxES}`;
-
- // 将护盾显示添加到能量护盾后面
- esSpan.appendChild(shieldDisplay);
-
- successCount++;
- processedCount++;
-
- // 添加调试信息
- console.debug(`Item ${index} Shield calculation:`, {
- totalES,
- currentQuality,
- qualityDiff,
- esInc,
- insertedRuneBonus,
- insertedRuneCount,
- numSockets,
- availableRuneBonus,
- baseES,
- maxES
- });
- } catch (error) {
- console.error(`Error calculating shield for item ${index}:`, error);
- }
- });
-
- console.log(`Shield calculation completed: ${successCount} successful, ${processedCount} processed out of ${items.length} items`);
-
- // 更新护盾排序面板
- updateShieldPanel();
- }
-
- // 创建护盾排序面板
- function createShieldPanel() {
- const panel = document.createElement('div');
- panel.id = 'shield-sort-panel';
- panel.style.cssText = `
- position: fixed;
- right: 20px;
- top: 50%;
- transform: translateY(calc(-50% + 200px));
- background: rgba(28, 28, 28, 0.95);
- padding: 8px;
- border-radius: 6px;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
- border: 1px solid #444;
- width: 180px;
- max-height: 60vh;
- z-index: 9997;
- display: none;
- `;
-
- // 添加标题
- const title = document.createElement('div');
- title.style.cssText = `
- font-weight: bold;
- color: #8acdff;
- margin-bottom: 6px;
- padding-bottom: 3px;
- border-bottom: 1px solid #444;
- display: flex;
- justify-content: space-between;
- align-items: center;
- cursor: pointer;
- font-size: 14px;
- user-select: none;
- height: 18px;
- `;
-
- // 添加展开/收起指示器
- const indicator = document.createElement('span');
- indicator.textContent = '▼';
- indicator.style.marginRight = '3px';
- indicator.style.fontSize = '10px';
- indicator.id = 'integrated-panel-indicator';
-
- const titleText = document.createElement('span');
- titleText.textContent = 'ES';
-
- const titleLeft = document.createElement('div');
- titleLeft.style.display = 'flex';
- titleLeft.style.alignItems = 'center';
- titleLeft.appendChild(indicator);
- titleLeft.appendChild(titleText);
-
- title.appendChild(titleLeft);
-
- // 添加关闭按钮
- const closeBtn = document.createElement('button');
- closeBtn.textContent = '×';
- closeBtn.style.cssText = `
- background: none;
- border: none;
- color: #999;
- font-size: 20px;
- cursor: pointer;
- padding: 0 5px;
- `;
- closeBtn.onclick = (e) => {
- e.stopPropagation();
- panel.style.display = 'none';
- };
- title.appendChild(closeBtn);
-
- // 添加内容容器
- const content = document.createElement('div');
- content.id = 'shield-sort-content';
- content.style.cssText = `
- max-height: calc(60vh - 35px);
- overflow-y: auto;
- transition: max-height 0.3s ease-out;
- padding-right: 2px;
- `;
-
- // 添加展开/收起功能
- let isExpanded = true;
- title.onclick = () => {
- isExpanded = !isExpanded;
- content.style.maxHeight = isExpanded ? 'calc(60vh - 35px)' : '0';
- content.style.overflow = isExpanded ? 'auto' : 'hidden';
- indicator.textContent = isExpanded ? '▼' : '▶';
- };
-
- // 添加组件到面板
- panel.appendChild(title);
- panel.appendChild(content);
-
- // 添加到文档
- document.body.appendChild(panel);
-
- console.log('Shield panel created successfully');
- return panel;
- }
-
- // 更新护盾排序面板
- function updateShieldPanel() {
- try {
- console.log('Updating shield panel...');
-
- // 获取或创建面板
- const panel = document.getElementById('integrated-sort-panel') || createIntegratedPanel();
- const content = document.getElementById('shield-sort-content');
- if (!content) {
- console.error('Shield sort content not found');
- return;
- }
-
- // 清空现有内容
- content.innerHTML = '';
-
- // 清除可能存在的旧数据属性
- content.removeAttribute('data-armour-content');
- content.removeAttribute('data-evasion-content');
- content.removeAttribute('data-dps-content');
- content.removeAttribute('data-defence-content');
-
- // 获取所有物品
- const items = document.querySelectorAll('.row[data-id]');
- const shieldData = [];
-
- // 收集护盾数据
- items.forEach(item => {
- const shieldDisplay = item.querySelector('.shield-display');
- if (shieldDisplay) {
- const shield = parseInt(shieldDisplay.textContent.replace('总护盾: ', ''));
-
- // 获取价格
- const priceElement = item.querySelector('.price [data-field="price"]');
- let price = '未标价';
- if (priceElement) {
- const amount = priceElement.querySelector('span:not(.price-label):not(.currency-text)');
- const currencyText = priceElement.querySelector('.currency-text');
- if (amount && currencyText) {
- const amountText = amount.textContent.trim();
- const currency = currencyText.querySelector('span')?.textContent || currencyText.textContent;
- // 使用全局的convertCurrencyText函数
- const simpleCurrency = convertCurrencyText(currency);
- price = `${amountText}${simpleCurrency}`;
- }
- }
-
- shieldData.push({
- shield,
- price,
- element: item
- });
- }
- });
-
- console.log(`Found ${shieldData.length} items with shield data`);
-
- // 如果没有护盾数据,隐藏护盾选项卡
- const shieldTab = panel.querySelector('[data-tab="shield"]');
- if (shieldData.length === 0) {
- if (shieldTab) shieldTab.style.display = 'none';
-
- // 如果DPS选项卡也是隐藏的,则隐藏整个面板
- const dpsTab = panel.querySelector('[data-tab="dps"]');
- const evasionTab = panel.querySelector('[data-tab="evasion"]');
- const armourTab = panel.querySelector('[data-tab="armour"]');
- const defenceTab = panel.querySelector('[data-tab="defence"]');
-
- // 检查是否所有选项卡都隐藏了
- if ((dpsTab && dpsTab.style.display === 'none') &&
- (evasionTab && evasionTab.style.display === 'none') &&
- (armourTab && armourTab.style.display === 'none') &&
- (defenceTab && defenceTab.style.display === 'none')) {
- panel.style.display = 'none';
- }
- // 移除自动切换到护盾选项卡的代码,保留当前选项卡状态
- return;
- } else {
- // 有护盾数据,确保护盾选项卡可见
- if (shieldTab) shieldTab.style.display = '';
- }
-
- // 按护盾值从高到低排序
- shieldData.sort((a, b) => b.shield - a.shield);
-
- // 更新面板内容
- shieldData.forEach(({shield, price, element}) => {
- const shieldItem = document.createElement('div');
- shieldItem.className = 'dps-item'; // 复用DPS项的样式
-
- // 创建护盾显示
- const shieldText = document.createElement('span');
- shieldText.className = 'shield-value'; // 使用护盾专用的样式
- shieldText.textContent = shield.toString();
-
- // 创建价格显示
- const priceText = document.createElement('span');
- priceText.className = 'price-value';
- priceText.textContent = price;
-
- // 添加到护盾项
- shieldItem.appendChild(shieldText);
- shieldItem.appendChild(priceText);
-
- // 添加点击事件
- shieldItem.onclick = () => {
- element.scrollIntoView({ behavior: 'smooth', block: 'center' });
- // 添加高亮效果
- element.style.transition = 'background-color 0.3s';
- element.style.backgroundColor = 'rgba(138, 205, 255, 0.2)';
- setTimeout(() => {
- element.style.backgroundColor = '';
- }, 1500);
- };
-
- content.appendChild(shieldItem);
- });
-
- // 显示面板
- panel.style.display = 'block';
- console.log('Shield panel updated successfully');
- } catch (error) {
- console.error('Error updating shield panel:', error);
- }
- }
-
- // 辅助函数:安全地查询选择器
- function safeQuerySelector(element, selector) {
- try {
- return element.querySelector(selector);
- } catch (error) {
- console.error(`Error querying selector "${selector}":`, error);
- return null;
- }
- }
-
- // 创建整合面板,包含总防御、DPS、护盾、闪避和护甲五个选项卡
- function createIntegratedPanel() {
- // 检查是否已存在面板
- let panel = document.getElementById('integrated-sort-panel');
- if (panel) return panel;
-
- panel = document.createElement('div');
- panel.id = 'integrated-sort-panel';
- panel.style.cssText = `
- position: fixed;
- right: 20px;
- top: 50%;
- transform: translateY(-50%);
- background: rgba(28, 28, 28, 0.95);
- padding: 8px;
- border-radius: 6px;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
- border: 1px solid #444;
- width: 180px;
- max-height: 60vh;
- z-index: 9997;
- display: none;
- `;
-
- // 创建标题栏
- const titleBar = document.createElement('div');
- titleBar.style.cssText = `
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 6px;
- padding-bottom: 3px;
- border-bottom: 1px solid #444;
- font-size: 13px;
- user-select: none;
- height: 18px;
- `;
-
- // 创建选项卡容器
- const tabsContainer = document.createElement('div');
- tabsContainer.style.cssText = `
- display: flex;
- gap: 4px;
- flex-wrap: nowrap;
- `;
-
- // 创建总防御选项卡(默认激活)
- const defenceTab = document.createElement('div');
- defenceTab.textContent = 'DEF';
- defenceTab.className = 'sort-tab active';
- defenceTab.dataset.tab = 'defence';
- defenceTab.style.cssText = `
- color: #9370DB;
- font-weight: bold;
- cursor: pointer;
- padding: 0 3px;
- font-size: 12px;
- `;
-
- // 创建DPS选项卡
- const dpsTab = document.createElement('div');
- dpsTab.textContent = 'DPS';
- dpsTab.className = 'sort-tab';
- dpsTab.dataset.tab = 'dps';
- dpsTab.style.cssText = `
- color: #FFD700;
- font-weight: bold;
- cursor: pointer;
- padding: 0 3px;
- opacity: 0.6;
- font-size: 12px;
- `;
-
- // 创建护盾选项卡
- const shieldTab = document.createElement('div');
- shieldTab.textContent = 'ES';
- shieldTab.className = 'sort-tab';
- shieldTab.dataset.tab = 'shield';
- shieldTab.style.cssText = `
- color: #8acdff;
- font-weight: bold;
- cursor: pointer;
- padding: 0 3px;
- opacity: 0.6;
- font-size: 12px;
- `;
-
- // 创建闪避选项卡
- const evasionTab = document.createElement('div');
- evasionTab.textContent = 'EV';
- evasionTab.className = 'sort-tab';
- evasionTab.dataset.tab = 'evasion';
- evasionTab.style.cssText = `
- color: #7FFF00;
- font-weight: bold;
- cursor: pointer;
- padding: 0 3px;
- opacity: 0.6;
- font-size: 12px;
- `;
-
- // 创建护甲选项卡
- const armourTab = document.createElement('div');
- armourTab.textContent = 'AR';
- armourTab.className = 'sort-tab';
- armourTab.dataset.tab = 'armour';
- armourTab.style.cssText = `
- color: #FF6347;
- font-weight: bold;
- cursor: pointer;
- padding: 0 3px;
- opacity: 0.6;
- font-size: 12px;
- `;
-
- // 添加选项卡到容器
- tabsContainer.appendChild(defenceTab);
- tabsContainer.appendChild(dpsTab);
- tabsContainer.appendChild(shieldTab);
- tabsContainer.appendChild(evasionTab);
- tabsContainer.appendChild(armourTab);
-
- // 创建展开/收起指示器
- const indicator = document.createElement('span');
- indicator.textContent = '▼';
- indicator.style.marginRight = '3px';
- indicator.style.fontSize = '10px';
- indicator.id = 'integrated-panel-indicator';
-
- // 创建左侧容器
- const titleLeft = document.createElement('div');
- titleLeft.style.cssText = `
- display: flex;
- align-items: center;
- `;
- titleLeft.appendChild(indicator);
- titleLeft.appendChild(tabsContainer);
-
- // 添加关闭按钮
- const closeBtn = document.createElement('button');
- closeBtn.textContent = '×';
- closeBtn.style.cssText = `
- background: none;
- border: none;
- color: #999;
- font-size: 20px;
- cursor: pointer;
- padding: 0 5px;
- `;
- closeBtn.onclick = (e) => {
- e.stopPropagation();
- panel.style.display = 'none';
- };
-
- // 组装标题栏
- titleBar.appendChild(titleLeft);
- titleBar.appendChild(closeBtn);
-
- // 创建内容容器
- const contentContainer = document.createElement('div');
- contentContainer.style.cssText = `
- position: relative;
- `;
-
- // 创建总防御内容
- const defenceContent = document.createElement('div');
- defenceContent.id = 'defence-sort-content';
- defenceContent.className = 'sort-content active';
- defenceContent.style.cssText = `
- max-height: calc(60vh - 35px);
- overflow-y: auto;
- transition: max-height 0.3s ease-out, opacity 0.3s ease-out;
- padding-right: 2px;
- `;
-
- // 创建DPS内容
- const dpsContent = document.createElement('div');
- dpsContent.id = 'dps-sort-content';
- dpsContent.className = 'sort-content';
- dpsContent.style.cssText = `
- max-height: calc(60vh - 35px);
- overflow-y: auto;
- transition: max-height 0.3s ease-out, opacity 0.3s ease-out;
- padding-right: 2px;
- display: none;
- `;
-
- // 创建护盾内容
- const shieldContent = document.createElement('div');
- shieldContent.id = 'shield-sort-content';
- shieldContent.className = 'sort-content';
- shieldContent.style.cssText = `
- max-height: calc(60vh - 35px);
- overflow-y: auto;
- transition: max-height 0.3s ease-out, opacity 0.3s ease-out;
- padding-right: 2px;
- display: none;
- `;
-
- // 创建闪避内容
- const evasionContent = document.createElement('div');
- evasionContent.id = 'evasion-sort-content';
- evasionContent.className = 'sort-content';
- evasionContent.style.cssText = `
- max-height: calc(60vh - 35px);
- overflow-y: auto;
- transition: max-height 0.3s ease-out, opacity 0.3s ease-out;
- padding-right: 2px;
- display: none;
- `;
-
- // 创建护甲内容
- const armourContent = document.createElement('div');
- armourContent.id = 'armour-sort-content';
- armourContent.className = 'sort-content';
- armourContent.style.cssText = `
- max-height: calc(60vh - 35px);
- overflow-y: auto;
- transition: max-height 0.3s ease-out, opacity 0.3s ease-out;
- padding-right: 2px;
- display: none;
- `;
-
- // 添加内容到容器
- contentContainer.appendChild(defenceContent);
- contentContainer.appendChild(dpsContent);
- contentContainer.appendChild(shieldContent);
- contentContainer.appendChild(evasionContent);
- contentContainer.appendChild(armourContent);
-
- // 添加展开/收起功能
- let isExpanded = true;
- indicator.onclick = (e) => {
- e.stopPropagation();
- isExpanded = !isExpanded;
- defenceContent.style.maxHeight = isExpanded ? 'calc(60vh - 35px)' : '0';
- dpsContent.style.maxHeight = isExpanded ? 'calc(60vh - 35px)' : '0';
- shieldContent.style.maxHeight = isExpanded ? 'calc(60vh - 35px)' : '0';
- evasionContent.style.maxHeight = isExpanded ? 'calc(60vh - 35px)' : '0';
- armourContent.style.maxHeight = isExpanded ? 'calc(60vh - 35px)' : '0';
- defenceContent.style.overflow = isExpanded ? 'auto' : 'hidden';
- dpsContent.style.overflow = isExpanded ? 'auto' : 'hidden';
- shieldContent.style.overflow = isExpanded ? 'auto' : 'hidden';
- evasionContent.style.overflow = isExpanded ? 'auto' : 'hidden';
- armourContent.style.overflow = isExpanded ? 'auto' : 'hidden';
- indicator.textContent = isExpanded ? '▼' : '▶';
- };
-
- // 添加选项卡切换功能
- defenceTab.onclick = () => {
- defenceTab.classList.add('active');
- dpsTab.classList.remove('active');
- shieldTab.classList.remove('active');
- evasionTab.classList.remove('active');
- armourTab.classList.remove('active');
- defenceContent.style.display = 'block';
- dpsContent.style.display = 'none';
- shieldContent.style.display = 'none';
- evasionContent.style.display = 'none';
- armourContent.style.display = 'none';
- defenceTab.style.opacity = '1';
- dpsTab.style.opacity = '0.6';
- shieldTab.style.opacity = '0.6';
- evasionTab.style.opacity = '0.6';
- armourTab.style.opacity = '0.6';
- };
-
- dpsTab.onclick = () => {
- dpsTab.classList.add('active');
- defenceTab.classList.remove('active');
- shieldTab.classList.remove('active');
- evasionTab.classList.remove('active');
- armourTab.classList.remove('active');
- dpsContent.style.display = 'block';
- defenceContent.style.display = 'none';
- shieldContent.style.display = 'none';
- evasionContent.style.display = 'none';
- armourContent.style.display = 'none';
- dpsTab.style.opacity = '1';
- defenceTab.style.opacity = '0.6';
- shieldTab.style.opacity = '0.6';
- evasionTab.style.opacity = '0.6';
- armourTab.style.opacity = '0.6';
- };
-
- shieldTab.onclick = () => {
- shieldTab.classList.add('active');
- defenceTab.classList.remove('active');
- dpsTab.classList.remove('active');
- evasionTab.classList.remove('active');
- armourTab.classList.remove('active');
- shieldContent.style.display = 'block';
- defenceContent.style.display = 'none';
- dpsContent.style.display = 'none';
- evasionContent.style.display = 'none';
- armourContent.style.display = 'none';
- shieldTab.style.opacity = '1';
- defenceTab.style.opacity = '0.6';
- dpsTab.style.opacity = '0.6';
- evasionTab.style.opacity = '0.6';
- armourTab.style.opacity = '0.6';
- };
-
- evasionTab.onclick = () => {
- evasionTab.classList.add('active');
- defenceTab.classList.remove('active');
- dpsTab.classList.remove('active');
- shieldTab.classList.remove('active');
- armourTab.classList.remove('active');
- evasionContent.style.display = 'block';
- defenceContent.style.display = 'none';
- dpsContent.style.display = 'none';
- shieldContent.style.display = 'none';
- armourContent.style.display = 'none';
- evasionTab.style.opacity = '1';
- defenceTab.style.opacity = '0.6';
- dpsTab.style.opacity = '0.6';
- shieldTab.style.opacity = '0.6';
- armourTab.style.opacity = '0.6';
- };
-
- armourTab.onclick = () => {
- armourTab.classList.add('active');
- defenceTab.classList.remove('active');
- dpsTab.classList.remove('active');
- shieldTab.classList.remove('active');
- evasionTab.classList.remove('active');
- armourContent.style.display = 'block';
- defenceContent.style.display = 'none';
- dpsContent.style.display = 'none';
- shieldContent.style.display = 'none';
- evasionContent.style.display = 'none';
- armourTab.style.opacity = '1';
- defenceTab.style.opacity = '0.6';
- dpsTab.style.opacity = '0.6';
- shieldTab.style.opacity = '0.6';
- evasionTab.style.opacity = '0.6';
- };
-
- // 添加滚动条样式
- const style = document.createElement('style');
- style.textContent = `
- #dps-sort-content::-webkit-scrollbar,
- #shield-sort-content::-webkit-scrollbar,
- #evasion-sort-content::-webkit-scrollbar,
- #armour-sort-content::-webkit-scrollbar {
- width: 6px;
- }
- #dps-sort-content::-webkit-scrollbar-track,
- #shield-sort-content::-webkit-scrollbar-track,
- #evasion-sort-content::-webkit-scrollbar-track {
- background: #1a1a1a;
- }
- #dps-sort-content::-webkit-scrollbar-thumb,
- #shield-sort-content::-webkit-scrollbar-thumb,
- #evasion-sort-content::-webkit-scrollbar-thumb {
- background: #444;
- border-radius: 3px;
- }
- #dps-sort-content::-webkit-scrollbar-thumb:hover,
- #shield-sort-content::-webkit-scrollbar-thumb:hover,
- #evasion-sort-content::-webkit-scrollbar-thumb:hover {
- background: #555;
- }
- .dps-item {
- padding: 4px 8px;
- margin: 1px 0;
- background: #2d2d2d;
- border-radius: 3px;
- cursor: pointer;
- transition: background 0.2s;
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 8px;
- font-size: 13px;
- white-space: nowrap;
- user-select: none;
- line-height: 1.2;
- }
- .dps-item:hover {
- background: #3d3d3d;
- }
- .dps-item:last-child {
- margin-bottom: 0;
- }
- .dps-value {
- color: #FFD700;
- font-weight: bold;
- }
- .shield-value {
- color: #8acdff;
- font-weight: bold;
- }
- .evasion-value {
- color: #7FFF00;
- font-weight: bold;
- }
- .price-value {
- color: #8acdff;
- font-size: 12px;
- text-align: right;
- }
- `;
-
- // 组装面板
- panel.appendChild(titleBar);
- panel.appendChild(contentContainer);
- document.head.appendChild(style);
- document.body.appendChild(panel);
-
- console.log('Integrated panel created successfully');
- return panel;
- }
-
- // 计算闪避值
- function calculateEvasion() {
- console.log('Calculating Evasion...');
- const items = document.querySelectorAll('.row[data-id]');
- console.log('Found items:', items.length);
-
- let processedCount = 0;
- let successCount = 0;
-
- items.forEach((item, index) => {
- try {
- // 如果已经计算过闪避值,则跳过
- if (item.querySelector('.evasion-display')) {
- processedCount++;
- return;
- }
-
- // 1. 获取符文孔数量
- const socketsDiv = item.querySelector('.sockets');
- if (!socketsDiv) {
- console.debug(`Item ${index}: No sockets div found`);
- return;
- }
-
- // 修改符文孔数量的获取逻辑
- let numSockets = 0;
- for (let i = 1; i <= 6; i++) { // 假设最多6个符文孔
- if (socketsDiv.classList.contains(`numSockets${i}`)) {
- numSockets = i;
- break;
- }
- }
- if (numSockets === 0) {
- // 尝试其他方式获取符文孔数量
- const socketText = socketsDiv.textContent.trim();
- if (socketText) {
- numSockets = socketText.length;
- } else {
- numSockets = 1; // 默认值
- }
- console.debug(`Item ${index}: Using alternative socket count: ${numSockets}`);
- }
-
- // 2. 获取闪避值
- const evasionSpan = item.querySelector('span[data-field="ev"]');
- if (!evasionSpan) {
- console.debug(`Item ${index}: No Evasion span found`);
- return;
- }
-
- const evasionValue = evasionSpan.querySelector('.colourAugmented, .colourDefault');
- if (!evasionValue) {
- console.debug(`Item ${index}: No Evasion value found`);
- return;
- }
-
- const totalEvasion = parseInt(evasionValue.textContent);
- if (isNaN(totalEvasion)) {
- console.debug(`Item ${index}: Invalid Evasion value: ${evasionValue.textContent}`);
- return;
- }
-
- // 3. 获取当前品质
- let currentQuality = 0;
- const qualitySpan = item.querySelector('span[data-field="quality"]');
- if (qualitySpan) {
- const qualityMatch = qualitySpan.textContent.match(/\+(\d+)%/);
- if (qualityMatch) {
- currentQuality = parseInt(qualityMatch[1]);
- }
- }
- // 计算品质差值
- const qualityDiff = 20 - currentQuality;
-
- // 4. 获取闪避增加百分比
- let evasionInc = 0;
-
- // 定义可能包含闪避增加的词缀ID列表
- const possibleEvasionIncIds = [
- 'stat.explicit.stat_1999113824', // 增加闪避与能量护盾
- 'stat.explicit.stat_2144192051', // 增加闪避
- 'stat.explicit.stat_3489782002', // 增加闪避和生命
- 'stat.explicit.stat_3321629045' // 增加护甲与闪避
- ];
-
- // 遍历所有可能的词缀ID
- for (const statId of possibleEvasionIncIds) {
- const statSpan = item.querySelector(`span[data-field="${statId}"]`);
- if (statSpan) {
- // 检查词缀文本是否包含"闪避"或"Evasion"
- const statText = statSpan.textContent;
- if (statText.includes('闪避') || statText.includes('Evasion')) {
- // 提取百分比数值
- const incMatch = statText.match(/(\d+)%/);
- if (incMatch) {
- evasionInc += parseInt(incMatch[1]);
- console.debug(`Found Evasion increase: ${incMatch[1]}% from stat ${statId}`);
- }
- }
- }
- }
-
- // 通用方法:查找所有包含"闪避"或"Evasion"的显式词缀
- const allExplicitStats = item.querySelectorAll('span[data-field^="stat.explicit.stat_"]');
- allExplicitStats.forEach(statSpan => {
- // 检查是否已经在上面的特定ID列表中处理过
- const statId = statSpan.getAttribute('data-field');
- if (!possibleEvasionIncIds.includes(statId)) {
- const statText = statSpan.textContent;
- // 检查是否包含闪避相关文本和百分比增加
- if ((statText.includes('闪避') || statText.includes('Evasion')) &&
- statText.includes('%') &&
- (statText.includes('增加') || statText.includes('increased'))) {
- const incMatch = statText.match(/(\d+)%/);
- if (incMatch) {
- evasionInc += parseInt(incMatch[1]);
- console.debug(`Found additional Evasion increase: ${incMatch[1]}% from stat ${statId}`);
- // 将新发现的ID添加到列表中,以便将来使用
- possibleEvasionIncIds.push(statId);
- }
- }
- }
- });
-
- // 5. 获取已插入的符文提供的增益和数量
- let insertedRuneBonus = 0;
- let insertedRuneCount = 0;
- let evasionRuneInc = 0; // 专门用于记录闪避符文的增益
-
- // 获取符文增益 - 闪避增加
- const evasionRuneStatSpan = item.querySelector('span[data-field="stat.rune.stat_3523867985"]');
- if (evasionRuneStatSpan) {
- const runeMatch = evasionRuneStatSpan.textContent.match(/(\d+)%/);
- if (runeMatch) {
- insertedRuneBonus = parseInt(runeMatch[1]);
- insertedRuneCount = insertedRuneBonus / 20;
- }
- }
-
- // 获取符文增益 - 闪避百分比增加
- const evasionIncRuneStatSpan = item.querySelector('span[data-field="stat.rune.stat_2866361420"]');
- if (evasionIncRuneStatSpan) {
- const evasionIncRuneMatch = evasionIncRuneStatSpan.textContent.match(/(\d+)%/);
- if (evasionIncRuneMatch) {
- evasionRuneInc = parseInt(evasionIncRuneMatch[1]);
- }
- }
-
- // 6. 计算可用的符文孔位增益
- let availableRuneBonus = 0;
- // 计算剩余可用的孔数
- const remainingSlots = numSockets - insertedRuneCount;
- availableRuneBonus = remainingSlots * 20;
-
- // 7. 计算基础闪避
- // 当前闪避 = 基础闪避 * (1 + 当前品质/100) * (1 + (已插入符文增益 + 装备增益 + 闪避符文增益)/100)
- const baseEvasion = Math.round(totalEvasion / (1 + currentQuality/100) / (1 + (insertedRuneBonus + evasionInc + evasionRuneInc)/100));
-
- // 8. 计算最大可能闪避(考虑品质提升和剩余符文孔)
- // 最大闪避 = 基础闪避 * (1 + (当前品质 + 品质差值)/100) * (1 + (装备增益 + 已插入符文增益 + 可用符文增益)/100)
- const maxEvasion = Math.round(baseEvasion * (1 + (currentQuality + qualityDiff)/100) * (1 + (evasionInc + insertedRuneBonus + availableRuneBonus)/100));
-
- // 创建闪避显示元素
- const evasionDisplay = document.createElement('span');
- evasionDisplay.className = 'evasion-display';
- evasionDisplay.style.cssText = `
- color: #32CD32;
- font-weight: bold;
- margin-left: 10px;
- `;
- evasionDisplay.textContent = `总闪避: ${maxEvasion}`;
-
- // 将闪避显示添加到闪避后面
- evasionSpan.appendChild(evasionDisplay);
-
- successCount++;
- processedCount++;
-
- // 添加调试信息
- console.debug(`Item ${index} Evasion calculation:`, {
- totalEvasion,
- currentQuality,
- qualityDiff,
- evasionInc,
- insertedRuneBonus,
- insertedRuneCount,
- numSockets,
- availableRuneBonus,
- baseEvasion,
- maxEvasion
- });
- } catch (error) {
- console.error(`Error calculating evasion for item ${index}:`, error);
- }
- });
-
- console.log(`Evasion calculation completed: ${successCount} successful, ${processedCount} processed out of ${items.length} items`);
-
- // 更新闪避排序面板
- updateEvasionPanel();
- }
-
- // 创建闪避排序面板
- function createEvasionPanel() {
- const panel = document.createElement('div');
- panel.id = 'evasion-sort-panel';
- panel.style.cssText = `
- position: fixed;
- right: 20px;
- top: 50%;
- transform: translateY(calc(-50% + 400px));
- background: rgba(28, 28, 28, 0.95);
- padding: 8px;
- border-radius: 6px;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
- border: 1px solid #444;
- width: 180px;
- max-height: 60vh;
- z-index: 9997;
- display: none;
- `;
-
- // 添加标题
- const title = document.createElement('div');
- title.style.cssText = `
- font-weight: bold;
- color: #7FFF00;
- margin-bottom: 6px;
- padding-bottom: 3px;
- border-bottom: 1px solid #444;
- display: flex;
- justify-content: space-between;
- align-items: center;
- cursor: pointer;
- font-size: 14px;
- user-select: none;
- height: 18px;
- `;
-
- // 添加展开/收起指示器
- const indicator = document.createElement('span');
- indicator.textContent = '▼';
- indicator.style.marginRight = '3px';
- indicator.style.fontSize = '10px';
- indicator.id = 'integrated-panel-indicator';
-
- const titleText = document.createElement('span');
- titleText.textContent = 'EV';
-
- const titleLeft = document.createElement('div');
- titleLeft.style.display = 'flex';
- titleLeft.style.alignItems = 'center';
- titleLeft.appendChild(indicator);
- titleLeft.appendChild(titleText);
-
- title.appendChild(titleLeft);
-
- // 添加关闭按钮
- const closeBtn = document.createElement('button');
- closeBtn.textContent = '×';
- closeBtn.style.cssText = `
- background: none;
- border: none;
- color: #999;
- font-size: 20px;
- cursor: pointer;
- padding: 0 5px;
- `;
- closeBtn.onclick = (e) => {
- e.stopPropagation();
- panel.style.display = 'none';
- };
- title.appendChild(closeBtn);
-
- // 添加内容容器
- const content = document.createElement('div');
- content.id = 'evasion-sort-content';
- content.style.cssText = `
- max-height: calc(60vh - 35px);
- overflow-y: auto;
- transition: max-height 0.3s ease-out;
- padding-right: 2px;
- `;
-
- // 添加展开/收起功能
- let isExpanded = true;
- title.onclick = () => {
- isExpanded = !isExpanded;
- content.style.maxHeight = isExpanded ? 'calc(60vh - 35px)' : '0';
- content.style.overflow = isExpanded ? 'auto' : 'hidden';
- indicator.textContent = isExpanded ? '▼' : '▶';
- };
-
- // 添加组件到面板
- panel.appendChild(title);
- panel.appendChild(content);
-
- // 添加到文档
- document.body.appendChild(panel);
-
- console.log('Evasion panel created successfully');
- return panel;
- }
-
- // 更新闪避排序面板
- function updateEvasionPanel() {
- try {
- console.log('Updating evasion panel...');
-
- // 获取或创建面板
- const panel = document.getElementById('integrated-sort-panel') || createIntegratedPanel();
- const content = document.getElementById('evasion-sort-content');
- if (!content) {
- console.error('Evasion sort content not found');
- return;
- }
-
- // 清空现有内容
- content.innerHTML = '';
-
- // 清除可能存在的旧数据属性
- content.removeAttribute('data-armour-content');
- content.removeAttribute('data-shield-content');
- content.removeAttribute('data-dps-content');
- content.removeAttribute('data-defence-content');
-
- // 获取所有物品
- const items = document.querySelectorAll('.row[data-id]');
- const evasionData = [];
-
- // 收集闪避数据
- items.forEach(item => {
- const evasionDisplay = item.querySelector('.evasion-display');
- if (evasionDisplay) {
- const evasion = parseInt(evasionDisplay.textContent.replace('总闪避: ', ''));
-
- // 获取价格
- const priceElement = item.querySelector('.price [data-field="price"]');
- let price = '未标价';
- if (priceElement) {
- const amount = priceElement.querySelector('span:not(.price-label):not(.currency-text)');
- const currencyText = priceElement.querySelector('.currency-text');
- if (amount && currencyText) {
- const amountText = amount.textContent.trim();
- const currency = currencyText.querySelector('span')?.textContent || currencyText.textContent;
- // 使用全局的convertCurrencyText函数
- const simpleCurrency = convertCurrencyText(currency);
- price = `${amountText}${simpleCurrency}`;
- }
- }
-
- evasionData.push({
- evasion,
- price,
- element: item
- });
- }
- });
-
- console.log(`Found ${evasionData.length} items with evasion data`);
-
- // 如果没有闪避数据,隐藏闪避选项卡
- const evasionTab = panel.querySelector('[data-tab="evasion"]');
- if (evasionData.length === 0) {
- if (evasionTab) evasionTab.style.display = 'none';
-
- // 如果其他选项卡也是隐藏的,则隐藏整个面板
- const dpsTab = panel.querySelector('[data-tab="dps"]');
- const shieldTab = panel.querySelector('[data-tab="shield"]');
- const armourTab = panel.querySelector('[data-tab="armour"]');
- const defenceTab = panel.querySelector('[data-tab="defence"]');
-
- // 检查是否所有选项卡都隐藏了
- if ((dpsTab && dpsTab.style.display === 'none') &&
- (shieldTab && shieldTab.style.display === 'none') &&
- (armourTab && armourTab.style.display === 'none') &&
- (defenceTab && defenceTab.style.display === 'none')) {
- panel.style.display = 'none';
- }
- // 移除自动切换到其他选项卡的代码,保留当前选项卡状态
- return;
- } else {
- // 有闪避数据,确保闪避选项卡可见
- if (evasionTab) evasionTab.style.display = '';
- }
-
- // 按闪避值从高到低排序
- evasionData.sort((a, b) => b.evasion - a.evasion);
-
- // 更新面板内容
- evasionData.forEach(({evasion, price, element}) => {
- const evasionItem = document.createElement('div');
- evasionItem.className = 'dps-item'; // 复用DPS项的样式
-
- // 创建闪避显示
- const evasionText = document.createElement('span');
- evasionText.className = 'evasion-value'; // 使用闪避专用的样式
- evasionText.textContent = evasion.toString();
-
- // 创建价格显示
- const priceText = document.createElement('span');
- priceText.className = 'price-value';
- priceText.textContent = price;
-
- // 添加到闪避项
- evasionItem.appendChild(evasionText);
- evasionItem.appendChild(priceText);
-
- // 添加点击事件
- evasionItem.onclick = () => {
- element.scrollIntoView({ behavior: 'smooth', block: 'center' });
- // 添加高亮效果
- element.style.transition = 'background-color 0.3s';
- element.style.backgroundColor = 'rgba(127, 255, 0, 0.2)';
- setTimeout(() => {
- element.style.backgroundColor = '';
- }, 1500);
- };
-
- content.appendChild(evasionItem);
- });
-
- // 显示面板
- panel.style.display = 'block';
- console.log('Evasion panel updated successfully');
- } catch (error) {
- console.error('Error updating evasion panel:', error);
- }
- }
-
- // 计算护甲值
- function calculateArmour() {
- console.log('Calculating Armour...');
- const items = document.querySelectorAll('.row[data-id]');
- console.log('Found items:', items.length);
-
- let processedCount = 0;
- let successCount = 0;
-
- items.forEach((item, index) => {
- try {
- // 如果已经计算过护甲值,则跳过
- if (item.querySelector('.armour-display')) {
- processedCount++;
- return;
- }
-
- // 1. 获取符文孔数量
- const socketsDiv = item.querySelector('.sockets');
- if (!socketsDiv) {
- console.debug(`Item ${index}: No sockets div found`);
- return;
- }
-
- // 修改符文孔数量的获取逻辑
- let numSockets = 0;
- for (let i = 1; i <= 6; i++) { // 假设最多6个符文孔
- if (socketsDiv.classList.contains(`numSockets${i}`)) {
- numSockets = i;
- break;
- }
- }
- if (numSockets === 0) {
- // 尝试其他方式获取符文孔数量
- const socketText = socketsDiv.textContent.trim();
- if (socketText) {
- numSockets = socketText.length;
- } else {
- numSockets = 1; // 默认值
- }
- console.debug(`Item ${index}: Using alternative socket count: ${numSockets}`);
- }
-
- // 2. 获取护甲值
- const armourSpan = item.querySelector('span[data-field="ar"]');
- if (!armourSpan) {
- console.debug(`Item ${index}: No Armour span found`);
- return;
- }
-
- const armourValue = armourSpan.querySelector('.colourAugmented, .colourDefault');
- if (!armourValue) {
- console.debug(`Item ${index}: No Armour value found`);
- return;
- }
-
- const totalArmour = parseInt(armourValue.textContent);
- if (isNaN(totalArmour)) {
- console.debug(`Item ${index}: Invalid Armour value: ${armourValue.textContent}`);
- return;
- }
-
- // 3. 获取当前品质
- let currentQuality = 0;
- const qualitySpan = item.querySelector('span[data-field="quality"]');
- if (qualitySpan) {
- const qualityMatch = qualitySpan.textContent.match(/\+(\d+)%/);
- if (qualityMatch) {
- currentQuality = parseInt(qualityMatch[1]);
- }
- }
- // 计算品质差值
- const qualityDiff = 20 - currentQuality;
-
- // 4. 获取护甲增加百分比
- let armourInc = 0;
-
- // 定义可能包含护甲增加的词缀ID列表
- const possibleArmourIncIds = [
- 'stat.explicit.stat_3321629045', // 增加护甲与能量护盾
- 'stat.explicit.stat_2866361420', // 增加护甲
- 'stat.explicit.stat_2511217560', // 增加最大护甲
- 'stat.explicit.stat_3489782002' // 增加护甲和生命
- ];
-
- // 遍历所有可能的词缀ID
- for (const statId of possibleArmourIncIds) {
- const statSpan = item.querySelector(`span[data-field="${statId}"]`);
- if (statSpan) {
- // 检查词缀文本是否包含"护甲"或"Armour"
- const statText = statSpan.textContent;
- if (statText.includes('护甲') || statText.includes('Armour')) {
- // 提取百分比数值
- const incMatch = statText.match(/(\d+)%/);
- if (incMatch) {
- armourInc += parseInt(incMatch[1]);
- console.debug(`Found Armour increase: ${incMatch[1]}% from stat ${statId}`);
- }
- }
- }
- }
-
- // 通用方法:查找所有包含"护甲"或"Armour"的显式词缀
- const allExplicitStats = item.querySelectorAll('span[data-field^="stat.explicit.stat_"]');
- allExplicitStats.forEach(statSpan => {
- // 检查是否已经在上面的特定ID列表中处理过
- const statId = statSpan.getAttribute('data-field');
- if (!possibleArmourIncIds.includes(statId)) {
- const statText = statSpan.textContent;
- // 检查是否包含护甲相关文本和百分比增加
- if ((statText.includes('护甲') || statText.includes('Armour')) &&
- statText.includes('%') &&
- (statText.includes('增加') || statText.includes('increased'))) {
- const incMatch = statText.match(/(\d+)%/);
- if (incMatch) {
- armourInc += parseInt(incMatch[1]);
- console.debug(`Found additional Armour increase: ${incMatch[1]}% from stat ${statId}`);
- // 将新发现的ID添加到列表中,以便将来使用
- possibleArmourIncIds.push(statId);
- }
- }
- }
- });
-
- // 5. 获取已插入的符文提供的增益和数量
- let insertedRuneBonus = 0;
- let insertedRuneCount = 0;
- let armourRuneInc = 0; // 专门用于记录护甲符文的增益
-
- // 获取符文增益 - 护甲增加
- const armourRuneStatSpan = item.querySelector('span[data-field="stat.rune.stat_3523867985"]');
- if (armourRuneStatSpan) {
- const runeMatch = armourRuneStatSpan.textContent.match(/(\d+)%/);
- if (runeMatch) {
- insertedRuneBonus = parseInt(runeMatch[1]);
- insertedRuneCount = insertedRuneBonus / 20;
- }
- }
-
- // 获取符文增益 - 护甲百分比增加
- const armourIncRuneStatSpan = item.querySelector('span[data-field="stat.rune.stat_2866361420"]');
- if (armourIncRuneStatSpan) {
- const armourIncRuneMatch = armourIncRuneStatSpan.textContent.match(/(\d+)%/);
- if (armourIncRuneMatch) {
- armourRuneInc = parseInt(armourIncRuneMatch[1]);
- }
- }
-
- // 6. 计算可用的符文孔位增益
- let availableRuneBonus = 0;
- // 计算剩余可用的孔数
- const remainingSlots = numSockets - insertedRuneCount;
- availableRuneBonus = remainingSlots * 20;
-
- // 7. 计算基础护甲
- // 当前护甲 = 基础护甲 * (1 + 当前品质/100) * (1 + (已插入符文增益 + 装备增益 + 护甲符文增益)/100)
- const baseArmour = Math.round(totalArmour / (1 + currentQuality/100) / (1 + (insertedRuneBonus + armourInc + armourRuneInc)/100));
-
- // 8. 计算最大可能护甲(考虑品质提升和剩余符文孔)
- // 最大护甲 = 基础护甲 * (1 + (当前品质 + 品质差值)/100) * (1 + (装备增益 + 已插入符文增益 + 可用符文增益)/100)
- const maxArmour = Math.round(baseArmour * (1 + (currentQuality + qualityDiff)/100) * (1 + (armourInc + insertedRuneBonus + availableRuneBonus)/100));
-
- // 创建护甲显示元素
- const armourDisplay = document.createElement('span');
- armourDisplay.className = 'armour-display';
- armourDisplay.style.cssText = `
- color: #CD853F;
- font-weight: bold;
- margin-left: 10px;
- `;
- armourDisplay.textContent = `总护甲: ${maxArmour}`;
-
- // 将护甲显示添加到护甲后面
- armourSpan.appendChild(armourDisplay);
-
- successCount++;
- processedCount++;
-
- // 添加调试信息
- console.debug(`Item ${index} Armour calculation:`, {
- totalArmour,
- currentQuality,
- qualityDiff,
- armourInc,
- insertedRuneBonus,
- insertedRuneCount,
- numSockets,
- availableRuneBonus,
- baseArmour,
- maxArmour
- });
- } catch (error) {
- console.error(`Error calculating armour for item ${index}:`, error);
- }
- });
-
- console.log(`Armour calculation completed: ${successCount} successful, ${processedCount} processed out of ${items.length} items`);
-
- // 更新护甲排序面板
- updateArmourPanel();
- }
-
- // 创建护甲排序面板
- function createArmourPanel() {
- const panel = document.createElement('div');
- panel.id = 'armour-sort-panel';
- panel.style.cssText = `
- position: fixed;
- right: 20px;
- top: 50%;
- transform: translateY(calc(-50% + 600px));
- background: rgba(28, 28, 28, 0.95);
- padding: 8px;
- border-radius: 6px;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
- border: 1px solid #444;
- width: 180px;
- max-height: 60vh;
- z-index: 9997;
- display: none;
- `;
-
- // 添加标题
- const title = document.createElement('div');
- title.style.cssText = `
- font-weight: bold;
- color: #FF6347;
- margin-bottom: 6px;
- padding-bottom: 3px;
- border-bottom: 1px solid #444;
- display: flex;
- justify-content: space-between;
- align-items: center;
- cursor: pointer;
- font-size: 14px;
- user-select: none;
- height: 18px;
- `;
-
- // 添加展开/收起指示器
- const indicator = document.createElement('span');
- indicator.textContent = '▼';
- indicator.style.marginRight = '3px';
- indicator.style.fontSize = '10px';
- indicator.id = 'integrated-panel-indicator';
-
- const titleText = document.createElement('span');
- titleText.textContent = 'AR';
-
- const titleLeft = document.createElement('div');
- titleLeft.style.display = 'flex';
- titleLeft.style.alignItems = 'center';
- titleLeft.appendChild(indicator);
- titleLeft.appendChild(titleText);
-
- title.appendChild(titleLeft);
-
- // 添加关闭按钮
- const closeBtn = document.createElement('button');
- closeBtn.textContent = '×';
- closeBtn.style.cssText = `
- background: none;
- border: none;
- color: #999;
- font-size: 20px;
- cursor: pointer;
- padding: 0 5px;
- `;
- closeBtn.onclick = (e) => {
- e.stopPropagation();
- panel.style.display = 'none';
- };
- title.appendChild(closeBtn);
-
- // 添加内容容器
- const content = document.createElement('div');
- content.id = 'armour-sort-content';
- content.style.cssText = `
- max-height: calc(60vh - 35px);
- overflow-y: auto;
- transition: max-height 0.3s ease-out;
- padding-right: 2px;
- `;
-
- // 添加展开/收起功能
- let isExpanded = true;
- title.onclick = () => {
- isExpanded = !isExpanded;
- content.style.maxHeight = isExpanded ? 'calc(60vh - 35px)' : '0';
- content.style.overflow = isExpanded ? 'auto' : 'hidden';
- indicator.textContent = isExpanded ? '▼' : '▶';
- };
-
- // 添加组件到面板
- panel.appendChild(title);
- panel.appendChild(content);
-
- // 添加到文档
- document.body.appendChild(panel);
-
- console.log('Armour panel created successfully');
- return panel;
- }
-
- // 更新护甲排序面板
- function updateArmourPanel() {
- try {
- console.log('Updating armour panel...');
-
- // 获取或创建面板
- const panel = document.getElementById('integrated-sort-panel') || createIntegratedPanel();
- const content = document.getElementById('armour-sort-content');
- if (!content) {
- console.error('Armour sort content not found');
- return;
- }
-
- // 清空现有内容
- content.innerHTML = '';
-
- // 获取所有物品
- const items = document.querySelectorAll('.row[data-id]');
- const armourData = [];
-
- // 收集护甲数据
- items.forEach(item => {
- const armourDisplay = item.querySelector('.armour-display');
- if (armourDisplay) {
- const armour = parseInt(armourDisplay.textContent.replace('总护甲: ', ''));
-
- // 获取价格
- const priceElement = item.querySelector('.price [data-field="price"]');
- let price = '未标价';
- if (priceElement) {
- const amount = priceElement.querySelector('span:not(.price-label):not(.currency-text)');
- const currencyText = priceElement.querySelector('.currency-text');
- if (amount && currencyText) {
- const amountText = amount.textContent.trim();
- const currency = currencyText.querySelector('span')?.textContent || currencyText.textContent;
- // 使用全局的convertCurrencyText函数
- const simpleCurrency = convertCurrencyText(currency);
- price = `${amountText}${simpleCurrency}`;
- }
- }
-
- armourData.push({
- armour,
- price,
- element: item
- });
- }
- });
-
- console.log(`Found ${armourData.length} items with armour data`);
-
- // 如果没有护甲数据,隐藏护甲选项卡
- const armourTab = panel.querySelector('[data-tab="armour"]');
- if (armourData.length === 0) {
- if (armourTab) armourTab.style.display = 'none';
-
- // 如果其他选项卡也是隐藏的,则隐藏整个面板
- const dpsTab = panel.querySelector('[data-tab="dps"]');
- const shieldTab = panel.querySelector('[data-tab="shield"]');
- const evasionTab = panel.querySelector('[data-tab="evasion"]');
- const defenceTab = panel.querySelector('[data-tab="defence"]');
-
- // 检查是否所有选项卡都隐藏了
- if ((dpsTab && dpsTab.style.display === 'none') &&
- (shieldTab && shieldTab.style.display === 'none') &&
- (evasionTab && evasionTab.style.display === 'none') &&
- (defenceTab && defenceTab.style.display === 'none')) {
- panel.style.display = 'none';
- }
- // 移除自动切换到其他选项卡的代码,保留当前选项卡状态
- return;
- } else {
- // 有护甲数据,确保护甲选项卡可见
- if (armourTab) armourTab.style.display = '';
- }
-
- // 按护甲值从高到低排序
- armourData.sort((a, b) => b.armour - a.armour);
-
- // 更新面板内容
- armourData.forEach(({armour, price, element}) => {
- const armourItem = document.createElement('div');
- armourItem.className = 'dps-item'; // 复用DPS项的样式
-
- // 创建护甲显示
- const armourText = document.createElement('span');
- armourText.className = 'armour-value'; // 使用护甲专用的样式
- armourText.textContent = armour.toString();
-
- // 创建价格显示
- const priceText = document.createElement('span');
- priceText.className = 'price-value';
- priceText.textContent = price;
-
- // 添加到护甲项
- armourItem.appendChild(armourText);
- armourItem.appendChild(priceText);
-
- // 添加点击事件
- armourItem.onclick = () => {
- element.scrollIntoView({ behavior: 'smooth', block: 'center' });
- // 添加高亮效果
- element.style.transition = 'background-color 0.3s';
- element.style.backgroundColor = 'rgba(255, 99, 71, 0.2)';
- setTimeout(() => {
- element.style.backgroundColor = '';
- }, 1500);
- };
-
- content.appendChild(armourItem);
- });
-
- // 显示面板
- panel.style.display = 'block';
- console.log('Armour panel updated successfully');
- } catch (error) {
- console.error('Error updating armour panel:', error);
- }
- }
-
- // 计算总防御值(护盾+闪避+护甲)
- function calculateDefence() {
- console.log('Calculating Defence...');
- const items = document.querySelectorAll('.row[data-id]');
- console.log('Found items:', items.length);
-
- let processedCount = 0;
- let successCount = 0;
-
- items.forEach((item, index) => {
- try {
- // 如果已经计算过总防御值,则跳过
- if (item.querySelector('.defence-display')) {
- processedCount++;
- return;
- }
-
- // 获取护盾、闪避和护甲值
- let totalDefence = 0;
-
- // 获取护盾值
- const shieldDisplay = item.querySelector('.shield-display');
- if (shieldDisplay) {
- const shield = parseInt(shieldDisplay.textContent.replace('总护盾: ', ''));
- if (!isNaN(shield)) {
- totalDefence += shield;
- }
- }
-
- // 获取闪避值
- const evasionDisplay = item.querySelector('.evasion-display');
- if (evasionDisplay) {
- const evasion = parseInt(evasionDisplay.textContent.replace('总闪避: ', ''));
- if (!isNaN(evasion)) {
- totalDefence += evasion;
- }
- }
-
- // 获取护甲值
- const armourDisplay = item.querySelector('.armour-display');
- if (armourDisplay) {
- const armour = parseInt(armourDisplay.textContent.replace('总护甲: ', ''));
- if (!isNaN(armour)) {
- totalDefence += armour;
- }
- }
-
- // 创建总防御显示元素
- const defenceDisplay = document.createElement('span');
- defenceDisplay.className = 'defence-display';
- defenceDisplay.style.cssText = `
- color: #9370DB;
- font-weight: bold;
- margin-left: 5px;
- font-size: 14px;
- background-color: rgba(147, 112, 219, 0.1);
- padding: 1px 4px;
- border-radius: 3px;
- `;
- defenceDisplay.textContent = `DEF: ${totalDefence}`;
-
- // 将总防御显示添加到装备名称旁边
- const itemHeader = item.querySelector('.itemHeader');
- if (itemHeader) {
- // 找到装备名称元素
- const itemName = itemHeader.querySelector('.itemName');
- if (itemName) {
- // 添加到装备名称后面
- itemName.appendChild(defenceDisplay);
- } else {
- // 如果找不到装备名称,直接添加到itemHeader
- itemHeader.appendChild(defenceDisplay);
- }
- } else {
- // 如果找不到itemHeader,尝试添加到其他位置
- const esSpan = item.querySelector('span[data-field="es"]');
- const evSpan = item.querySelector('span[data-field="ev"]');
- const arSpan = item.querySelector('span[data-field="ar"]');
-
- if (esSpan) {
- esSpan.appendChild(defenceDisplay);
- } else if (evSpan) {
- evSpan.appendChild(defenceDisplay);
- } else if (arSpan) {
- arSpan.appendChild(defenceDisplay);
- }
- }
-
- successCount++;
- processedCount++;
-
- // 添加调试信息
- console.debug(`Item ${index} Defence calculation: ${totalDefence}`);
- } catch (error) {
- console.error(`Error calculating defence for item ${index}:`, error);
- }
- });
-
- console.log(`Defence calculation completed: ${successCount} successful, ${processedCount} processed out of ${items.length} items`);
-
- // 更新总防御排序面板
- updateDefencePanel();
- }
-
- // 更新总防御排序面板
- function updateDefencePanel() {
- try {
- console.log('Updating defence panel...');
-
- // 获取或创建面板
- const panel = document.getElementById('integrated-sort-panel') || createIntegratedPanel();
- const content = document.getElementById('defence-sort-content');
- if (!content) {
- console.error('Defence sort content not found');
- return;
- }
-
- // 清空现有内容
- content.innerHTML = '';
-
- // 清除可能存在的旧数据属性
- content.removeAttribute('data-shield-content');
- content.removeAttribute('data-evasion-content');
- content.removeAttribute('data-armour-content');
- content.removeAttribute('data-dps-content');
-
- // 获取所有物品
- const items = document.querySelectorAll('.row[data-id]');
- const defenceData = [];
-
- // 收集总防御数据
- items.forEach(item => {
- const defenceDisplay = item.querySelector('.defence-display');
- if (defenceDisplay) {
- const defence = parseInt(defenceDisplay.textContent.replace('DEF: ', ''));
-
- // 获取价格
- const priceElement = item.querySelector('.price [data-field="price"]');
- let price = '未标价';
- if (priceElement) {
- const amount = priceElement.querySelector('span:not(.price-label):not(.currency-text)');
- const currencyText = priceElement.querySelector('.currency-text');
- if (amount && currencyText) {
- const amountText = amount.textContent.trim();
- const currency = currencyText.querySelector('span')?.textContent || currencyText.textContent;
- // 使用全局的convertCurrencyText函数
- const simpleCurrency = convertCurrencyText(currency);
- price = `${amountText}${simpleCurrency}`;
- }
- }
-
- defenceData.push({
- defence,
- price,
- element: item
- });
- }
- });
-
- console.log(`Found ${defenceData.length} items with defence data`);
-
- // 如果没有总防御数据,隐藏总防御选项卡
- const defenceTab = panel.querySelector('[data-tab="defence"]');
- if (defenceData.length === 0) {
- if (defenceTab) defenceTab.style.display = 'none';
-
- // 如果其他选项卡也是隐藏的,则隐藏整个面板
- const dpsTab = panel.querySelector('[data-tab="dps"]');
- const shieldTab = panel.querySelector('[data-tab="shield"]');
- const evasionTab = panel.querySelector('[data-tab="evasion"]');
- const armourTab = panel.querySelector('[data-tab="armour"]');
-
- // 检查是否所有选项卡都隐藏了
- if ((dpsTab && dpsTab.style.display === 'none') &&
- (shieldTab && shieldTab.style.display === 'none') &&
- (evasionTab && evasionTab.style.display === 'none') &&
- (armourTab && armourTab.style.display === 'none')) {
- panel.style.display = 'none';
- }
- // 移除自动切换到其他选项卡的代码,保留当前选项卡状态
- return;
- } else {
- // 有总防御数据,确保总防御选项卡可见
- if (defenceTab) defenceTab.style.display = '';
- }
-
- // 按总防御值从高到低排序
- defenceData.sort((a, b) => b.defence - a.defence);
-
- // 更新面板内容
- defenceData.forEach(({defence, price, element}) => {
- const defenceItem = document.createElement('div');
- defenceItem.className = 'dps-item'; // 复用DPS项的样式
-
- // 创建总防御显示
- const defenceText = document.createElement('span');
- defenceText.className = 'defence-value';
- defenceText.textContent = defence.toString();
-
- // 创建价格显示
- const priceText = document.createElement('span');
- priceText.className = 'price-value';
- priceText.textContent = price;
-
- // 添加到总防御项
- defenceItem.appendChild(defenceText);
- defenceItem.appendChild(priceText);
-
- // 添加点击事件
- defenceItem.onclick = () => {
- element.scrollIntoView({ behavior: 'smooth', block: 'center' });
- // 添加高亮效果
- element.style.transition = 'background-color 0.3s';
- element.style.backgroundColor = 'rgba(147, 112, 219, 0.2)';
- setTimeout(() => {
- element.style.backgroundColor = '';
- }, 1500);
- };
-
- content.appendChild(defenceItem);
- });
-
- // 显示面板
- panel.style.display = 'block';
- console.log('Defence panel updated successfully');
- } catch (error) {
- console.error('Error updating defence panel:', error);
- }
- }
- })();