// ==UserScript== // @name 漫画导入器优化版 // @namespace http://tampermonkey.net/ // @version 2.1 // @description 将漫画信息导入到Notion数据库(优化移动端体验) // @author pipi // @match *://*/* // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_addStyle // @grant GM_notification // @connect api.notion.com // @license pipi // @downloadURL https://update.greasyfork.icu/scripts/531184/%E6%BC%AB%E7%94%BB%E5%AF%BC%E5%85%A5%E5%99%A8%E4%BC%98%E5%8C%96%E7%89%88.user.js // @updateURL https://update.greasyfork.icu/scripts/531184/%E6%BC%AB%E7%94%BB%E5%AF%BC%E5%85%A5%E5%99%A8%E4%BC%98%E5%8C%96%E7%89%88.meta.js // ==/UserScript== (function() { 'use strict'; // 配置存储 const CONFIG_KEY = 'NotionImporterConfig'; const SITE_SELECTORS_KEY = 'NotionImporterSiteSelectors'; const IMPORTED_PAGES_KEY = 'NotionImporterImportedPages'; // 默认配置 const defaultConfig = { apiKey: '', mangaDbId: '' }; // 粉色主题颜色 const PRIMARY_COLOR = '#fd9abd'; const SECONDARY_COLOR = '#ffb3d1'; const DANGER_COLOR = '#ff6b8b'; const SUCCESS_COLOR = '#a8e6cf'; const INFO_COLOR = '#84c7f9'; // 获取或初始化配置 function getConfig() { const savedConfig = GM_getValue(CONFIG_KEY, JSON.stringify(defaultConfig)); return JSON.parse(savedConfig); } // 保存配置 function saveConfig(config) { GM_setValue(CONFIG_KEY, JSON.stringify(config)); } // 获取站点选择器配置 function getSiteSelectors(domain) { const selectors = GM_getValue(SITE_SELECTORS_KEY, '{}'); return JSON.parse(selectors)[domain] || {}; } // 保存站点选择器配置 function saveSiteSelectors(domain, selectorConfig) { const allSelectors = JSON.parse(GM_getValue(SITE_SELECTORS_KEY, '{}')); allSelectors[domain] = selectorConfig; GM_setValue(SITE_SELECTORS_KEY, JSON.stringify(allSelectors)); } // 获取已导入页面 function getImportedPages() { const pages = GM_getValue(IMPORTED_PAGES_KEY, '{}'); return JSON.parse(pages); } // 保存已导入页面 function saveImportedPage(url, pageId) { const pages = getImportedPages(); pages[url] = pageId; GM_setValue(IMPORTED_PAGES_KEY, JSON.stringify(pages)); } // 创建悬浮按钮 function createFloatingButton(isImported = false) { const existingBtn = document.getElementById('notion-importer-floating-btn'); if (existingBtn) existingBtn.remove(); const floatingBtn = document.createElement('div'); floatingBtn.id = 'notion-importer-floating-btn'; floatingBtn.style.position = 'fixed'; floatingBtn.style.right = '20px'; floatingBtn.style.bottom = '20px'; floatingBtn.style.zIndex = '9999'; floatingBtn.style.width = '50px'; floatingBtn.style.height = '50px'; floatingBtn.style.borderRadius = '50%'; floatingBtn.style.backgroundColor = isImported ? SUCCESS_COLOR : PRIMARY_COLOR; floatingBtn.style.color = 'white'; floatingBtn.style.display = 'flex'; floatingBtn.style.justifyContent = 'center'; floatingBtn.style.alignItems = 'center'; floatingBtn.style.cursor = 'pointer'; floatingBtn.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)'; floatingBtn.innerHTML = isImported ? '' : '+'; floatingBtn.style.touchAction = 'manipulation'; document.body.appendChild(floatingBtn); return floatingBtn; } // 创建确认对话框 function createConfirmDialog(message, onConfirm, onCancel) { const overlay = document.createElement('div'); overlay.style.position = 'fixed'; overlay.style.top = '0'; overlay.style.left = '0'; overlay.style.width = '100%'; overlay.style.height = '100%'; overlay.style.backgroundColor = 'rgba(0,0,0,0.5)'; overlay.style.zIndex = '10000'; overlay.style.display = 'flex'; overlay.style.justifyContent = 'center'; overlay.style.alignItems = 'center'; const dialog = document.createElement('div'); dialog.style.backgroundColor = 'white'; dialog.style.padding = '20px'; dialog.style.borderRadius = '8px'; dialog.style.width = '80%'; dialog.style.maxWidth = '400px'; const messageEl = document.createElement('div'); messageEl.textContent = message; messageEl.style.marginBottom = '20px'; messageEl.style.fontSize = '16px'; const buttonContainer = document.createElement('div'); buttonContainer.style.display = 'flex'; buttonContainer.style.justifyContent = 'flex-end'; buttonContainer.style.gap = '10px'; const confirmBtn = document.createElement('button'); confirmBtn.textContent = '确定'; confirmBtn.style.padding = '10px 20px'; confirmBtn.style.fontSize = '16px'; confirmBtn.style.backgroundColor = PRIMARY_COLOR; confirmBtn.style.color = 'white'; confirmBtn.style.border = 'none'; confirmBtn.style.borderRadius = '6px'; confirmBtn.onclick = function() { document.body.removeChild(overlay); onConfirm && onConfirm(); }; const cancelBtn = document.createElement('button'); cancelBtn.textContent = '取消'; cancelBtn.style.padding = '10px 20px'; cancelBtn.style.fontSize = '16px'; cancelBtn.style.backgroundColor = DANGER_COLOR; cancelBtn.style.color = 'white'; cancelBtn.style.border = 'none'; cancelBtn.style.borderRadius = '6px'; cancelBtn.onclick = function() { document.body.removeChild(overlay); onCancel && onCancel(); }; buttonContainer.appendChild(cancelBtn); buttonContainer.appendChild(confirmBtn); dialog.appendChild(messageEl); dialog.appendChild(buttonContainer); overlay.appendChild(dialog); document.body.appendChild(overlay); return overlay; } // 去除文本中的括号内容 function cleanText(text) { if (!text) return ''; return text.replace(/\[.*?\]/g, '').replace(/\(.*?\)/g, '').trim(); } // 获取元素值 function getElementValue(selector) { if (!selector) return ''; try { const element = document.querySelector(selector); return element ? cleanText(getTextContent(element)) : ''; } catch (e) { console.error('获取元素值出错:', e); return ''; } } // 获取文本内容 function getTextContent(element) { if (!element) return ''; // 如果是输入元素 if (element.tagName.toLowerCase() === 'input' || element.tagName.toLowerCase() === 'textarea') { return element.value || ''; } // 如果是选择元素 if (element.tagName.toLowerCase() === 'select') { return element.options[element.selectedIndex]?.text || ''; } // 默认获取文本内容 return element.textContent?.trim() || ''; } // 获取图片URL(自动匹配第一张大图) function getImageUrl(selector) { // 优先使用选择器指定的图片 if (selector) { try { const element = document.querySelector(selector); if (element) { // 如果是img标签 if (element.tagName.toLowerCase() === 'img') { return element.src || ''; } // 如果是背景图 const backgroundImage = window.getComputedStyle(element).backgroundImage; if (backgroundImage && backgroundImage !== 'none') { const urlMatch = backgroundImage.match(/url\(["']?(.*?)["']?\)/); if (urlMatch && urlMatch[1]) { return urlMatch[1]; } } } } catch (e) { console.error('获取图片URL出错:', e); } } // 如果没有选择器或选择器无效,自动获取页面第一张大图 const images = document.querySelectorAll('img'); for (let img of images) { // 过滤小图标(宽度或高度大于100px) if (img.naturalWidth > 100 && img.naturalHeight > 100) { return img.src || ''; } } return ''; } // Notion API请求 function notionApiRequest(endpoint, data, method = 'POST') { const config = getConfig(); const apiKey = config.apiKey; return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: method, url: `https://api.notion.com/v1/${endpoint}`, headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', 'Notion-Version': '2022-06-28' }, data: JSON.stringify(data), onload: function(response) { if (response.status >= 200 && response.status < 300) { try { resolve(JSON.parse(response.responseText)); } catch (e) { resolve(response.responseText); } } else { try { const error = JSON.parse(response.responseText); reject(new Error(error.message || 'Notion API错误')); } catch (e) { reject(new Error(`HTTP ${response.status}: ${response.statusText}`)); } } }, onerror: function(error) { reject(error); } }); }); } // 更新Notion页面 async function updateNotionPage(pageId, data) { try { const response = await notionApiRequest(`pages/${pageId}`, data, 'PATCH'); return response; } catch (error) { console.error('更新Notion页面出错:', error); throw error; } } // 创建选择器界面 function createSelectorInterface(propertyName, callback) { // 隐藏悬浮按钮 const floatingBtn = document.getElementById('notion-importer-floating-btn'); if (floatingBtn) floatingBtn.style.display = 'none'; // 创建选择器覆盖层 const selectorOverlay = document.createElement('div'); selectorOverlay.id = 'notion-importer-selector-overlay'; selectorOverlay.style.position = 'fixed'; selectorOverlay.style.top = '0'; selectorOverlay.style.left = '0'; selectorOverlay.style.width = '100%'; selectorOverlay.style.height = '100%'; selectorOverlay.style.zIndex = '10000'; selectorOverlay.style.pointerEvents = 'none'; // 创建提示框 const tooltip = document.createElement('div'); tooltip.style.position = 'fixed'; tooltip.style.bottom = '20px'; tooltip.style.left = '50%'; tooltip.style.transform = 'translateX(-50%)'; tooltip.style.backgroundColor = 'rgba(0,0,0,0.7)'; tooltip.style.color = 'white'; tooltip.style.padding = '15px 20px'; tooltip.style.borderRadius = '8px'; tooltip.style.zIndex = '10001'; tooltip.style.maxWidth = '90%'; tooltip.style.textAlign = 'center'; tooltip.style.fontSize = '16px'; tooltip.innerHTML = `
正在选择: ${propertyName}
点击页面元素进行选择
按住Shift可多选
`; selectorOverlay.appendChild(tooltip); document.body.appendChild(selectorOverlay); // 高亮元素 function highlightElement(element) { if (!element) return; element.style.outline = '2px solid ' + PRIMARY_COLOR; element.style.outlineOffset = '2px'; } // 取消高亮 function unhighlightElement(element) { if (!element) return; element.style.outline = ''; element.style.outlineOffset = ''; } let selectedElements = []; let currentSelector = ''; let hoveredElement = null; let shiftPressed = false; // 更新当前悬停元素的提示 function updateHoveredElementInfo(element) { const selectorInfo = document.getElementById('notion-importer-current-selector'); if (element) { selectorInfo.textContent = generateSelector(element, false); } else { selectorInfo.textContent = '未悬停在元素上'; } } // 生成选择器 function generateSelector(element, isMultiSelect = false) { if (!element || !element.tagName) return ''; // 多选模式直接返回通用选择器 if (isMultiSelect) { // 优先返回类选择器 if (element.className && typeof element.className === 'string') { const classes = element.className.split(/\s+/).filter(c => c); if (classes.length > 0) { return `.${classes[0]}`; } } // 没有类名则返回标签名 return element.tagName.toLowerCase(); } // 单选模式生成精确选择器 const path = []; let current = element; while (current && current !== document.body) { const siblings = Array.from(current.parentNode.children); const index = siblings.indexOf(current) + 1; const tag = current.tagName.toLowerCase(); // 优先使用带位置的选择器 path.unshift(`${tag}:nth-child(${index})`); // 如果有类名则添加 if (current.className && typeof current.className === 'string') { const classes = current.className.split(/\s+/).filter(c => c); if (classes.length > 0) { path[0] = `${tag}.${classes[0]}:nth-child(${index})`; } } current = current.parentNode; } return path.join(' > '); } // 元素悬停逻辑 function handleElementHover(e) { const target = e.target; // 如果悬停在选择器界面本身,不处理 if (selectorOverlay.contains(target)) { if (hoveredElement) { unhighlightElement(hoveredElement); hoveredElement = null; updateHoveredElementInfo(null); } return; } // 如果悬停的元素变化了 if (target !== hoveredElement) { if (hoveredElement) { unhighlightElement(hoveredElement); } hoveredElement = target; highlightElement(hoveredElement); updateHoveredElementInfo(hoveredElement); } } // 元素选择逻辑 document.addEventListener('keydown', function(e) { if (e.key === 'Shift') { shiftPressed = true; } }); document.addEventListener('keyup', function(e) { if (e.key === 'Shift') { shiftPressed = false; } }); function handleElementClick(e) { e.preventDefault(); e.stopPropagation(); const target = e.target; // 如果点击的是选择器界面本身,不处理 if (selectorOverlay.contains(target)) { return; } if (shiftPressed) { // 多选模式 if (!selectedElements.includes(target)) { selectedElements.push(target); highlightElement(target); } } else { // 单选模式 selectedElements.forEach(el => unhighlightElement(el)); selectedElements = [target]; highlightElement(target); } // 更新选择器 if (selectedElements.length > 0) { currentSelector = generateSelector(selectedElements[0], shiftPressed); createConfirmationInterface(); } } // 创建确认界面 function createConfirmationInterface() { document.body.removeChild(selectorOverlay); document.removeEventListener('mouseover', handleElementHover); document.removeEventListener('click', handleElementClick, true); document.removeEventListener('keydown', handleKeyDown); const overlay = document.createElement('div'); overlay.className = 'notion-importer-interface'; overlay.style.position = 'fixed'; overlay.style.top = '0'; overlay.style.left = '0'; overlay.style.width = '100%'; overlay.style.height = '100%'; overlay.style.backgroundColor = 'rgba(0,0,0,0.5)'; overlay.style.zIndex = '10000'; overlay.style.display = 'flex'; overlay.style.flexDirection = 'column'; overlay.style.alignItems = 'center'; overlay.style.justifyContent = 'center'; const container = document.createElement('div'); container.style.backgroundColor = 'white'; container.style.padding = '20px'; container.style.borderRadius = '12px'; container.style.width = '90%'; container.style.maxWidth = '500px'; container.style.maxHeight = '80vh'; container.style.overflowY = 'auto'; const title = document.createElement('h3'); title.textContent = `确认选择: ${propertyName}`; title.style.marginTop = '0'; title.style.marginBottom = '15px'; title.style.color = PRIMARY_COLOR; title.style.fontSize = '18px'; const selectedInfo = document.createElement('div'); selectedInfo.style.marginBottom = '20px'; selectedInfo.style.padding = '15px'; selectedInfo.style.backgroundColor = '#f9f9f9'; selectedInfo.style.borderRadius = '8px'; selectedInfo.style.fontSize = '16px'; if (selectedElements.length === 0) { selectedInfo.innerHTML = '

未选择任何元素

'; } else { let infoHTML = `

已选择 ${selectedElements.length} 个元素:

`; const allTexts = []; selectedElements.forEach((el, index) => { const text = cleanText(el.textContent?.trim().substring(0, 50)) || '空内容'; allTexts.push(text); infoHTML += `
元素 ${index + 1}: ${text}
`; }); infoHTML += `
选择器: ${currentSelector}
`; if (selectedElements.length > 1) { infoHTML += `
合并文本: ${allTexts.join(', ')}
`; } selectedInfo.innerHTML = infoHTML; } const inputGroup = document.createElement('div'); inputGroup.style.display = 'flex'; inputGroup.style.flexDirection = 'column'; inputGroup.style.marginBottom = '20px'; const selectorLabel = document.createElement('label'); selectorLabel.textContent = '选择器 (可编辑)'; selectorLabel.style.marginBottom = '8px'; selectorLabel.style.fontSize = '16px'; const selectorInput = document.createElement('input'); selectorInput.type = 'text'; selectorInput.value = currentSelector; selectorInput.placeholder = '点击选择按钮选择元素或直接输入选择器'; selectorInput.style.padding = '12px'; selectorInput.style.border = '1px solid #ddd'; selectorInput.style.borderRadius = '6px'; selectorInput.style.fontSize = '16px'; selectorInput.style.marginBottom = '10px'; const selectButton = document.createElement('button'); selectButton.textContent = '重新选择'; selectButton.style.padding = '12px'; selectButton.style.fontSize = '16px'; selectButton.style.backgroundColor = SECONDARY_COLOR; selectButton.style.color = 'white'; selectButton.style.border = 'none'; selectButton.style.borderRadius = '6px'; selectButton.style.cursor = 'pointer'; selectButton.onclick = function() { document.body.removeChild(overlay); createSelectorInterface(propertyName, callback); }; inputGroup.appendChild(selectorLabel); inputGroup.appendChild(selectorInput); inputGroup.appendChild(selectButton); const buttonContainer = document.createElement('div'); buttonContainer.style.display = 'flex'; buttonContainer.style.justifyContent = 'flex-end'; buttonContainer.style.marginTop = '20px'; buttonContainer.style.gap = '10px'; const confirmButton = document.createElement('button'); confirmButton.textContent = '确认'; confirmButton.style.padding = '12px 20px'; confirmButton.style.fontSize = '16px'; confirmButton.style.backgroundColor = PRIMARY_COLOR; confirmButton.style.color = 'white'; confirmButton.style.border = 'none'; confirmButton.style.borderRadius = '6px'; confirmButton.style.cursor = 'pointer'; const cancelButton = document.createElement('button'); cancelButton.textContent = '取消'; cancelButton.style.padding = '12px 20px'; cancelButton.style.fontSize = '16px'; cancelButton.style.backgroundColor = DANGER_COLOR; cancelButton.style.color = 'white'; cancelButton.style.border = 'none'; cancelButton.style.borderRadius = '6px'; cancelButton.style.cursor = 'pointer'; buttonContainer.appendChild(cancelButton); buttonContainer.appendChild(confirmButton); container.appendChild(title); container.appendChild(selectedInfo); container.appendChild(inputGroup); container.appendChild(buttonContainer); overlay.appendChild(container); document.body.appendChild(overlay); // 确认选择 confirmButton.onclick = function() { const finalSelector = selectorInput.value.trim(); if (!finalSelector) { alert('请提供有效的选择器'); return; } // 清除高亮 selectedElements.forEach(el => unhighlightElement(el)); // 如果是多选模式,合并文本内容 let result; if (selectedElements.length > 1) { const allTexts = selectedElements.map(el => cleanText(getTextContent(el))).filter(t => t); result = allTexts.join(', '); } else { result = cleanText(getTextContent(selectedElements[0])); } // 执行回调 if (callback) { callback(finalSelector, result); } // 关闭界面 document.body.removeChild(overlay); // 恢复悬浮按钮 const floatingBtn = document.getElementById('notion-importer-floating-btn'); if (floatingBtn) floatingBtn.style.display = 'flex'; }; // 取消选择 cancelButton.onclick = function() { // 清除高亮 selectedElements.forEach(el => unhighlightElement(el)); // 关闭界面 document.body.removeChild(overlay); // 恢复悬浮按钮 const floatingBtn = document.getElementById('notion-importer-floating-btn'); if (floatingBtn) floatingBtn.style.display = 'flex'; }; // 点击外部关闭 overlay.onclick = function(e) { if (e.target === overlay) { // 清除高亮 selectedElements.forEach(el => unhighlightElement(el)); document.body.removeChild(overlay); // 恢复悬浮按钮 const floatingBtn = document.getElementById('notion-importer-floating-btn'); if (floatingBtn) floatingBtn.style.display = 'flex'; } }; } // 添加事件监听器 document.addEventListener('mouseover', handleElementHover); document.addEventListener('click', handleElementClick, true); // 添加键盘快捷键 (ESC取消) function handleKeyDown(e) { if (e.key === 'Escape') { // 取消选择 selectedElements.forEach(el => unhighlightElement(el)); document.body.removeChild(selectorOverlay); document.removeEventListener('mouseover', handleElementHover); document.removeEventListener('click', handleElementClick, true); document.removeEventListener('keydown', handleKeyDown); // 恢复悬浮按钮 const floatingBtn = document.getElementById('notion-importer-floating-btn'); if (floatingBtn) floatingBtn.style.display = 'flex'; } } document.addEventListener('keydown', handleKeyDown); } // 创建漫画导入界面 function createMangaImportInterface(isUpdate = false, pageId = null) { const domain = window.location.hostname; const currentUrl = window.location.href; const selectors = getSiteSelectors(domain); const isConfigured = Object.keys(selectors).length > 0; const overlay = document.createElement('div'); overlay.className = 'notion-importer-interface'; overlay.style.position = 'fixed'; overlay.style.top = '0'; overlay.style.left = '0'; overlay.style.width = '100%'; overlay.style.height = '100%'; overlay.style.backgroundColor = 'rgba(0,0,0,0.5)'; overlay.style.zIndex = '10000'; overlay.style.display = 'flex'; overlay.style.flexDirection = 'column'; overlay.style.alignItems = 'center'; overlay.style.justifyContent = 'center'; const container = document.createElement('div'); container.style.backgroundColor = 'white'; container.style.padding = '20px'; container.style.borderRadius = '12px'; container.style.width = '90%'; container.style.maxWidth = '500px'; container.style.maxHeight = '80vh'; container.style.overflowY = 'auto'; const title = document.createElement('h3'); title.textContent = isUpdate ? '更新漫画信息' : (isConfigured ? '导入漫画到Notion' : '配置漫画选择器'); title.style.marginTop = '0'; title.style.marginBottom = '15px'; title.style.color = PRIMARY_COLOR; title.style.fontSize = '18px'; const form = document.createElement('div'); form.style.display = 'flex'; form.style.flexDirection = 'column'; form.style.gap = '15px'; // 创建表单组 function createFormGroup(label, currentSelector, onSelect) { const group = document.createElement('div'); group.style.display = 'flex'; group.style.flexDirection = 'column'; const labelElement = document.createElement('label'); labelElement.textContent = label; labelElement.style.marginBottom = '8px'; labelElement.style.fontSize = '16px'; const input = document.createElement('input'); input.type = 'text'; input.value = currentSelector || ''; input.placeholder = '点击选择按钮选择元素或直接输入选择器'; input.style.padding = '12px'; input.style.border = '1px solid #ddd'; input.style.borderRadius = '6px'; input.style.fontSize = '16px'; input.style.marginBottom = '10px'; const selectButton = document.createElement('button'); selectButton.textContent = '选择'; selectButton.style.padding = '12px'; selectButton.style.fontSize = '16px'; selectButton.style.backgroundColor = PRIMARY_COLOR; selectButton.style.color = 'white'; selectButton.style.border = 'none'; selectButton.style.borderRadius = '6px'; selectButton.style.cursor = 'pointer'; selectButton.onclick = function(e) { e.stopPropagation(); // 隐藏整个导入界面 overlay.style.display = 'none'; createSelectorInterface(label, function(selector, value) { // 选择完成后恢复导入界面 overlay.style.display = 'flex'; input.value = selector; if (onSelect) onSelect(selector); }); }; group.appendChild(labelElement); group.appendChild(input); group.appendChild(selectButton); return group; } // 漫画名 const nameGroup = createFormGroup('漫画名', selectors['漫画名'] || '', (selector) => { selectors['漫画名'] = selector; saveSiteSelectors(domain, selectors); }); // 作者 const authorGroup = createFormGroup('作者', selectors['作者'] || '', (selector) => { selectors['作者'] = selector; saveSiteSelectors(domain, selectors); }); // 简介 const descriptionGroup = createFormGroup('简介', selectors['简介'] || '', (selector) => { selectors['简介'] = selector; saveSiteSelectors(domain, selectors); }); // 最新章节 const latestChapterGroup = createFormGroup('最新章节', selectors['最新章节'] || '', (selector) => { selectors['最新章节'] = selector; saveSiteSelectors(domain, selectors); }); // 更新状态 const statusGroup = createFormGroup('更新状态', selectors['更新状态'] || '', (selector) => { selectors['更新状态'] = selector; saveSiteSelectors(domain, selectors); }); // 封面 const coverGroup = createFormGroup('封面', selectors['封面'] || '', (selector) => { selectors['封面'] = selector; saveSiteSelectors(domain, selectors); }); // 追更 const trackingGroup = createFormGroup('追更', selectors['追更'] || '', (selector) => { selectors['追更'] = selector; saveSiteSelectors(domain, selectors); }); // 类型 const typeGroup = createFormGroup('类型', selectors['类型'] || '', (selector) => { selectors['类型'] = selector; saveSiteSelectors(domain, selectors); }); // 别名 const aliasGroup = createFormGroup('别名', selectors['别名'] || '', (selector) => { selectors['别名'] = selector; saveSiteSelectors(domain, selectors); }); // 地区 const regionGroup = createFormGroup('地区', selectors['地区'] || '', (selector) => { selectors['地区'] = selector; saveSiteSelectors(domain, selectors); }); form.appendChild(nameGroup); form.appendChild(authorGroup); form.appendChild(descriptionGroup); form.appendChild(latestChapterGroup); form.appendChild(statusGroup); form.appendChild(coverGroup); form.appendChild(trackingGroup); form.appendChild(typeGroup); form.appendChild(aliasGroup); form.appendChild(regionGroup); const buttonContainer = document.createElement('div'); buttonContainer.style.display = 'flex'; buttonContainer.style.justifyContent = 'flex-end'; buttonContainer.style.marginTop = '20px'; buttonContainer.style.gap = '10px'; const importButton = document.createElement('button'); importButton.textContent = isUpdate ? '更新' : '导入'; importButton.style.padding = '12px 20px'; importButton.style.fontSize = '16px'; importButton.style.backgroundColor = PRIMARY_COLOR; importButton.style.color = 'white'; importButton.style.border = 'none'; importButton.style.borderRadius = '6px'; importButton.style.cursor = 'pointer'; const cancelButton = document.createElement('button'); cancelButton.textContent = '取消'; cancelButton.style.padding = '12px 20px'; cancelButton.style.fontSize = '16px'; cancelButton.style.backgroundColor = DANGER_COLOR; cancelButton.style.color = 'white'; cancelButton.style.border = 'none'; cancelButton.style.borderRadius = '6px'; cancelButton.style.cursor = 'pointer'; buttonContainer.appendChild(cancelButton); buttonContainer.appendChild(importButton); container.appendChild(title); container.appendChild(form); container.appendChild(buttonContainer); overlay.appendChild(container); document.body.appendChild(overlay); // 导入/更新逻辑 importButton.onclick = async function() { const config = getConfig(); if (!config.apiKey || !config.mangaDbId) { alert('请先配置Notion API和数据库ID'); return; } try { // 获取所有输入框的值 const inputs = container.querySelectorAll('input'); const selectorValues = {}; inputs.forEach(input => { const label = input.closest('div').firstChild.textContent; selectorValues[label] = input.value; }); // 保存选择器配置 Object.keys(selectorValues).forEach(key => { selectors[key] = selectorValues[key]; }); saveSiteSelectors(domain, selectors); // 获取数据 const mangaData = { '漫画名': selectorValues['漫画名'] ? getElementValue(selectorValues['漫画名']) : '', '作者': selectorValues['作者'] ? getElementValue(selectorValues['作者']) : '', '简介': selectorValues['简介'] ? getElementValue(selectorValues['简介']) : '', '最新章节': selectorValues['最新章节'] ? getElementValue(selectorValues['最新章节']) : '', '更新状态': selectorValues['更新状态'] ? getElementValue(selectorValues['更新状态']) : '', '封面': getImageUrl(selectorValues['封面']), // 自动获取封面 '追更': selectorValues['追更'] ? getElementValue(selectorValues['追更']) : '', '类型': selectorValues['类型'] ? getElementValue(selectorValues['类型']) : '', '别名': selectorValues['别名'] ? getElementValue(selectorValues['别名']) : '', '地区': selectorValues['地区'] ? getElementValue(selectorValues['地区']) : '' }; const requestData = { parent: { database_id: config.mangaDbId }, properties: { '漫画名': { title: [ { text: { content: mangaData['漫画名'] || '未命名' } } ] }, '作者': { rich_text: [ { text: { content: mangaData['作者'] || '' } } ] }, '简介': { rich_text: [ { text: { content: mangaData['简介'] || '' } } ] }, '最新章节': { rich_text: [ { text: { content: mangaData['最新章节'] || '' } } ] }, '更新状态': { select: { name: mangaData['更新状态'] || '未知' } }, '追更': { rich_text: [ { text: { content: mangaData['追更'] || '' } } ] }, '类型': { select: { name: mangaData['类型'] || '其他' } }, '别名': { rich_text: [ { text: { content: mangaData['别名'] || '' } } ] }, '地区': { select: { name: mangaData['地区'] || '其他' } } }, cover: mangaData['封面'] ? { type: "external", external: { url: mangaData['封面'] } } : null }; let response; if (isUpdate && pageId) { // 更新现有页面 delete requestData.parent; // 更新时不需要parent response = await updateNotionPage(pageId, requestData); } else { // 创建新页面 response = await notionApiRequest('pages', requestData); } if (response && response.id) { // 保存已导入页面 saveImportedPage(currentUrl, response.id); // 更新悬浮按钮状态 createFloatingButton(true); // 显示成功通知 GM_notification({ text: isUpdate ? '更新成功!' : '导入成功!', title: 'Notion导入器', image: 'https://www.notion.so/images/favicon.ico' }); document.body.removeChild(overlay); } else { alert(isUpdate ? '更新失败: ' + (response.message || '未知错误') : '导入失败: ' + (response.message || '未知错误')); } } catch (error) { alert((isUpdate ? '更新出错: ' : '导入出错: ') + error.message); console.error('Notion API错误:', error); } }; // 取消 cancelButton.onclick = function() { document.body.removeChild(overlay); // 恢复悬浮按钮 const floatingBtn = document.getElementById('notion-importer-floating-btn'); if (floatingBtn) floatingBtn.style.display = 'flex'; }; // 点击外部关闭 overlay.onclick = function(e) { if (e.target === overlay) { document.body.removeChild(overlay); // 恢复悬浮按钮 const floatingBtn = document.getElementById('notion-importer-floating-btn'); if (floatingBtn) floatingBtn.style.display = 'flex'; } }; } // 初始化 function init() { // 添加CSS样式 GM_addStyle(` .notion-importer-highlighted { outline: 2px solid ${PRIMARY_COLOR} !important; outline-offset: 2px !important; } #notion-importer-floating-btn { background-color: ${PRIMARY_COLOR} !important; transition: transform 0.2s; touch-action: manipulation; } #notion-importer-floating-btn:hover { transform: scale(1.1); background-color: ${SECONDARY_COLOR} !important; } .notion-importer-interface { font-size: 16px; } .notion-importer-interface input, .notion-importer-interface button, .notion-importer-interface select { font-size: 16px; min-height: 44px; } .notion-importer-interface button { padding: 12px; margin: 5px 0; } .notion-importer-interface input { padding: 12px; margin-bottom: 10px; } @media (max-width: 768px) { #notion-importer-floating-btn { width: 60px !important; height: 60px !important; right: 15px !important; bottom: 15px !important; } .notion-importer-interface { width: 90% !important; padding: 15px !important; } .notion-importer-interface h3 { font-size: 18px !important; } .notion-importer-interface label { font-size: 16px !important; } } `); // 检查是否已导入当前页面 const currentUrl = window.location.href; const importedPages = getImportedPages(); const isImported = importedPages.hasOwnProperty(currentUrl); // 创建悬浮按钮 const floatingBtn = createFloatingButton(isImported); // 点击悬浮按钮 floatingBtn.addEventListener('click', function(e) { e.stopPropagation(); if (isImported) { // 如果已导入,询问是否更新 createConfirmDialog( '此页面已导入过,是否要更新数据?', function() { // 更新数据 createMangaImportInterface(true, importedPages[currentUrl]); }, function() { // 取消操作 floatingBtn.style.display = 'flex'; } ); } else { // 首次导入 createMangaImportInterface(); } }); // 点击外部隐藏菜单 document.addEventListener('click', function(e) { const floatingBtn = document.getElementById('notion-importer-floating-btn'); if (floatingBtn && !floatingBtn.contains(e.target)) { floatingBtn.style.display = 'flex'; } }); } // 启动脚本 init(); })();