📖 精确搜索
https://m.jjwxc.net/assort#
分享者: guaner001125 (317)发布时间: 04/10 10:31
更改加载页面为滚动加载
{ "articleStyle": 0, "customOrder": 24, "enableJs": true, "enabled": true, "enabledCookieJar": true, "header": "{\n\t\"referer\":\"http:\/\/android.jjwxc.net\/?v=357\",\n\t\"versionCode\":\"357\",\n\t\"versiontype\":\"reading\",\n \"User-Agent\": \"Dalvik\/2.1.0\"\n}", "injectJs": "\nlet previousUrl = \"\";\nlet currentUrl = '';\nlet bqUrl = \"\";\nlet currentOffset = 0;\nlet totalPages = 1;\nlet isLoading = false;\nlet hasMore = true;\nlet hasPrevious = false;\nlet scrollDebounceTimer = null;\n\n\/\/ 标签编解码\nfunction encode(str) {\n str = str.replace(\/https:.*?bq=\/g,'');\n return btoa(encodeURI(str));\n}\n\nfunction decode(str) {\n str = \"https:\/\/android.jjwxc.net\/search\/getSearchForKeyWords?offset=0&limit=20&bq=\" + atob(str);\n return str;\n}\n\n\/\/ 书籍定位\nfunction recordBookPosition(novelid,offset) {\n let previousUrl = sessionStorage.getItem('previousUrl');\n sessionStorage.setItem('bookPosition', JSON.stringify({\n url:previousUrl,\n novelid: novelid,\n offset: offset \n }));\n}\n\nfunction restoreBookPosition() {\n const bookPosition = sessionStorage.getItem('bookPosition');\n if (!bookPosition) return;\n \n let { url, novelid, offset, timestamp } = JSON.parse(bookPosition);\n const pUrl = new URL(url);\n pUrl.searchParams.set('offset', offset);\n currentOffset = offset;\n fetchAndRender(pUrl.toString(), function(error) {\n if (error) {\n console.error('加载失败,无法滚动:', error);\n return;\n }\n \n const bookElement = document.querySelector(`.book[data-id=\"${novelid}\"]`);\n if (bookElement) {\n updateCurrentPage()\n window.scrollTo({\n top: bookElement.offsetTop - 100,\n behavior: 'auto'\n });\n }\n});\n \n}\n\nfunction fetchAndRender(url, callback) {\n showLoadingMessage();\n isLoading = true;\n sessionStorage.setItem('previousUrl',url);\n fetch(url)\n .then(response => response.json())\n .then(jsonData => {\n currentUrl = url;\n totalPages = jsonData.total_page;\n currentOffset = parseInt(new URL(url).searchParams.get('offset')) || 0;\n hasMore = (Math.floor(currentOffset \/ 20) + 1) < jsonData.total_page;\n hasPrevious = currentOffset > 0; \/\/ 设置是否有上一页\n sessionStorage.setItem('previousUrl',url);\n generatePageContent(jsonData,currentOffset);\n addStyles();\n \n if (typeof callback === 'function') {\n callback(null, jsonData);\n }\n })\n .catch(error => {\n console.error('加载失败:', error);\n if (typeof callback === 'function') {\n callback(error);\n }\n })\n .finally(() => {\n isLoading = false;\n hideLoadingMessage();\n bookPosition = sessionStorage.getItem('bookPosition'); \n sessionStorage.removeItem('bookPosition'); \n if (!bookPosition) {\n window.scrollTo(0,0); \t\n return;\n }\n });\n}\n\n\/\/ 下拉加载\nfunction initScrollListener() {\n \/\/ 使用被动事件监听器提高滚动性能\n window.addEventListener('scroll', handleScroll, { passive: true });\n}\n\nfunction handleScroll() {\n \/\/ 防抖处理\n clearTimeout(scrollDebounceTimer);\n scrollDebounceTimer = setTimeout(() => {\n if (!isLoading) {\n updateCurrentPage();\n }\n \n \/\/ 检查是否到达边界\n checkScrollBoundaries();\n }, 50);\n}\n\nfunction checkScrollBoundaries() {\n if (isLoading) return;\n \n const scrollPosition = window.scrollY;\n const documentHeight = document.body.offsetHeight;\n const windowHeight = window.innerHeight;\n \n \/\/ 检查是否到达底部\n if (scrollPosition + windowHeight >= documentHeight - 100 && hasMore) {\n loadNextPage();\n }\n \n \/\/ 检查是否到达顶部\n if (scrollPosition <= 100 && hasPrevious) {\n loadPreviousPage();\n }\n}\n\nfunction loadPreviousPage() {\n if (isLoading) return;\n \n isLoading = true;\n document.getElementById('loading').style.display = 'block';\n \n const newOffset = Math.max(0, currentOffset - 20);\n const url = new URL(currentUrl);\n url.searchParams.set('offset', newOffset);\n \n \/\/ 保存当前滚动位置\n const scrollPositionBefore = window.scrollY;\n \n fetch(url.toString())\n .then(response => response.json())\n .then(jsonData => {\n \/\/ 使用requestAnimationFrame确保平滑加载\n requestAnimationFrame(() => {\n \/\/ 加载新内容\n prependBookElements(jsonData, newOffset);\n \n \/\/ 更新状态\n currentOffset = newOffset;\n updateCurrentPage();\n hasMore = (Math.floor(newOffset \/ 20) + 1) < jsonData.total_page;\n hasPrevious = newOffset > 0;\n \n \/\/ 计算并保持滚动位置\n const newBooks = document.querySelectorAll('.book');\n if (newBooks.length > 0) {\n \/\/ 滚动到新加载内容的最后一本(即之前那页的第一本)\n const targetBook = newBooks[jsonData.items.length - 1];\n if (targetBook) {\n const targetPosition = targetBook.offsetTop +100; \/\/ 留出一些顶部空间\n \n \/\/ 使用平滑滚动\n window.scrollTo({\n top: targetPosition,\n behavior: 'auto'\n });\n }\n }\n });\n })\n .catch(error => {\n console.error('加载上一页失败:', error);\n })\n .finally(() => {\n isLoading = false;\n document.getElementById('loading').style.display = 'none';\n });\n}\n\n\/\/ 优化后的加载下一页函数\nfunction loadNextPage() {\n if (isLoading) return;\n \n isLoading = true;\n document.getElementById('loading').style.display = 'block';\n \n const newOffset = currentOffset + 20;\n const url = new URL(currentUrl);\n url.searchParams.set('offset', newOffset);\n \n fetch(url.toString())\n .then(response => response.json())\n .then(jsonData => {\n requestAnimationFrame(() => {\n currentOffset = newOffset;\n appendBookElements(jsonData, newOffset);\n updateCurrentPage();\n hasMore = (Math.floor(newOffset \/ 20) + 1) < jsonData.total_page;\n });\n })\n .catch(error => {\n console.error('加载下一页失败:', error);\n })\n .finally(() => {\n isLoading = false;\n document.getElementById('loading').style.display = 'none';\n });\n}\n\nfunction createBookElement(item,offset){\nlet bookDiv = document.createElement('div');\n bookDiv.className = 'book';\n bookDiv.setAttribute('data-id', item.novelid);\n bookDiv.setAttribute('data-offset', offset);\n\n bookDiv.innerHTML = `\n <div class=\"bookdes\">\n <div class=\"pic\"><img src=\"${item.cover}\" referrerpolicy=\"no-referrer\"><\/div>\n <div class=\"details\">\n <p class=\"bookname\">\n <span class=\"name\"><a href=\"https:\/\/m.jjwxc.net\/book2\/${item.novelid}\">${item.novelname}<\/a><\/span>\n <br>\n <span class=\"author\">——${item.authorname}<\/span>\n <\/p>\n <p class=\"tag\">⭐️ ${item.novelSizeformat}字•${item.novelstep === '2' ? '<span style=\"color:red\">已完结<\/span>' : '<span style=\"color:blue\">连载中<\/span>'}<br><br>📖 ${item.novelintroshort}<br><br>🔖 ${item.novelClass}<br><br>🏷 <span style=\"color:green\">${item.tags}<\/span><\/p>\n <\/div>\n <\/div>\n `;\n \n const link = bookDiv.querySelector('a');\n link.addEventListener('click', (e) => {\n e.preventDefault();\n recordBookPosition(item.novelid, offset);\n window.location.href = link.href;\n });\nreturn bookDiv\n}\n\n\n\nfunction prependBookElements(data, offset) {\n const bookContainer = document.getElementById('bookContainer');\n const fragment = document.createDocumentFragment(); \n data.items.forEach((item, index) => { \n let bookDiv = createBookElement(item,offset)\n fragment.appendChild(bookDiv);\n }); \n bookContainer.insertBefore(fragment, bookContainer.firstChild);\n}\n\n\n\/\/ 书籍列表生成\nfunction createBookElements(data,offset) {\n const bookContainer = document.getElementById('bookContainer');\n bookContainer.innerHTML = '';\n data.items.forEach((item, index) => {\n let bookDiv = createBookElement(item,offset)\n bookContainer.appendChild(bookDiv); \n });\n}\n\nfunction appendBookElements(data,offset) {\n const bookContainer = document.getElementById('bookContainer');\n data.items.forEach((item, index) => {\n let bookDiv = createBookElement(item,offset)\n bookContainer.appendChild(bookDiv);\n });\n}\n\n\/\/ 页面操作\nfunction updateUrlParam(key, value) {\n const url = new URL(currentUrl);\n url.searchParams.set(key, value);\n url.searchParams.set('offset', 0);\n currentOffset = 0;\n updateCurrentPage()\n fetchAndRender(url.toString());\n}\n\nfunction updateOffset(delta) {\n const url = new URL(currentUrl);\n const newOffset = Math.max(0, currentOffset + delta);\n currentOffset = newOffset;\n url.searchParams.set('offset', newOffset);\n fetchAndRender(url.toString());\n}\n\nfunction jumpToPage() {\n const pageInput = document.querySelector('.page-input');\n const page = parseInt(pageInput.value);\n if (isNaN(page) || page < 1 || page > totalPages) {\n alert(`请输入有效的页码(1-${totalPages})`);\n return;\n }\n \n currentOffset = (page - 1) * 20;\n const url = new URL(currentUrl);\n url.searchParams.set('offset', currentOffset);\n fetchAndRender(url.toString());\n}\n\nfunction updateCurrentPage() {\n const books = document.querySelectorAll('.book');\n if (books.length === 0) return;\n \n \/\/ 使用IntersectionObserver检测可见书籍\n const observer = new IntersectionObserver((entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n const bookOffsetAttr = entry.target.getAttribute('data-offset');\n \nconst bookOffset = bookOffsetAttr !== null ? parseInt(bookOffsetAttr) : currentOffset;\n\n const currentPage = Math.floor(bookOffset \/ 20) + 1;\n \n \/\/ 只有当页码确实改变时才更新DOM\n const currentDisplay = document.querySelector('.current-page').textContent;\n if (currentDisplay !== currentPage.toString()) {\n document.querySelector('.current-page').textContent = currentPage;\n document.querySelector('.current').textContent = currentPage;\n currentOffset = bookOffset;\n }\n }\n });\n }, {\n threshold: 0.5 \/\/ 当书籍50%可见时触发\n });\n \n \/\/ 观察所有书籍\n books.forEach(book => {\n observer.observe(book);\n });\n \n \/\/ 清理观察器\n return () => {\n observer.disconnect();\n };\n}\n\n\n\/\/ 辅助函数\nfunction showLoadingMessage() {\n const loadingDiv = document.createElement('div');\n loadingDiv.id = 'loading-message';\n loadingDiv.style.position = 'fixed';\n loadingDiv.style.top = '20px';\n loadingDiv.style.left = '50%';\n loadingDiv.style.transform = 'translateX(-50%)';\n loadingDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';\n loadingDiv.style.color = 'white';\n loadingDiv.style.padding = '10px 20px';\n loadingDiv.style.borderRadius = '5px';\n loadingDiv.style.zIndex = '1000';\n loadingDiv.textContent = '正在加载,请稍候...';\n document.body.appendChild(loadingDiv);\n}\n\nfunction hideLoadingMessage() {\n const loadingDiv = document.getElementById('loading-message');\n if (loadingDiv) loadingDiv.remove();\n}\n\nfunction generateFinalUrl(formData) {\n const variables = {};\n for (let [key, value] of formData.entries()) {\n const variableName = key.replace(\/\\d+\/g, '');\n if (!variables[variableName]) {\n variables[variableName] = [];\n }\n variables[variableName].push(value);\n }\n\n for (const key in variables) {\n variables[key] = variables[key].join(',');\n }\n\n return `https:\/\/android.jjwxc.net\/search\/getSearchForKeyWords?offset=0&limit=20&bq=${variables.bq || ''}&removetags=${variables.removebq || ''}¬likecollectionTypes=${variables.notlikecollectiontypes || ''}&fw=${variables.fw || ''}&yc=${variables.yc || ''}&xx=${variables.xx || ''}&sd=${variables.sd || ''}&lx=${variables.lx || ''}&mainview=${variables.mainview || ''}&fbsj=${variables.fbsj || ''}&novelbefavoritedcount=${variables.novelbefavoritedcount || ''}&isfinish=${variables.isfinish || ''}&collectionTypes=${variables.collectiontypes || ''}&searchkeyWords=${variables.searchkeywords || ''}`;\n}\n\n\/\/ 页面生成\/\nfunction generatePageContent(jsonData,offset) {\n document.documentElement.innerHTML = `\n <!-- 悬浮按钮组 -->\n <div class=\"float-buttons\">\n <div class=\"button-group\">\n <!-- 页码控制面板 -->\n <div class=\"page-control\">\n <div class=\"page-info\">\n <span class=\"current-page\">1<\/span>\n <div class=\"divider\"><\/div>\n <span class=\"total-pages\">${jsonData.total_page}<\/span>\n <\/div>\n <div class=\"page-jump\">\n <input type=\"number\" min=\"1\" max=\"${jsonData.total_page}\" value=\"1\" class=\"page-input\">\n <button class=\"go-btn\">GO<\/button>\n <\/div>\n <\/div>\n <!-- 功能按钮(从下到上顺序) -->\n <button class=\"func-btn home\" title=\"返回首页\">⌂<\/button>\n <button class=\"func-btn bottom\" title=\"页面底部\">↓<\/button>\n <button class=\"func-btn top\" title=\"页面顶部\">↑<\/button>\n \n\n <\/div>\n \n <!-- 主按钮 - 深绿色圆形 -->\n <button class=\"main-btn\" title=\"展开功能\">\n <span class=\"current\">1<\/span>\n <div class=\"divider\"><\/div>\n <span class=\"total\">${jsonData.total_page}<\/span>\n <\/button>\n <\/div>\n \n <div class=\"center\" style=\"text-align: center;\">\n 按 <select name=\"sortType\" id=\"orderstr\">\n <option value=\"2\">积分<\/option>\n <option value=\"1\">最近更新<\/option>\n <option value=\"3\">最新发表<\/option>\n <option value=\"5\">字数<\/option>\n <option value=\"4\">收藏数<\/option>\n <option value=\"10\">完结高分<\/option>\n <\/select> 排序\n <span style=\"margin: 0 10px;\"> | <\/span>\n 只显示 <select name=\"isfinish\" id=\"isfinish\">\n <option value=\"0\">无限制<\/option>\n <option value=\"1\">连载<\/option>\n <option value=\"2\">完结<\/option>\n <\/select>\n <\/div>\n <div style=\"height: 60px;\"><\/div>\n <br>\n <div id=\"bookContainer\" class=\"book-container\"><\/div>\n <div id=\"loading\" style=\"text-align: center; padding: 10px; display: none;\">\n 正在加载更多...\n <\/div>\n `;\n\n const sortTypeSelect = document.getElementById(\"orderstr\");\n const isFinishSelect = document.getElementById(\"isfinish\");\n const urlParams = new URLSearchParams(currentUrl.split('?')[1]);\n \/\/ 获取DOM元素\n const mainBtn = document.querySelector('.main-btn');\n const floatButtons = document.querySelector('.float-buttons');\n const totalPagesEl = document.querySelector('.total-pages');\n const pageInput = document.querySelector('.page-input');\n const pageSections = document.querySelectorAll('.page-section');\n const homeBtn = document.querySelector('.func-btn.home');\n const topBtn = document.querySelector('.func-btn.top');\n const bottomBtn = document.querySelector('.func-btn.bottom');\n \n \/\/ 设置总页数\n totalPages = jsonData.total_page; \/\/ 示例使用六位数页码\n totalPagesEl.textContent = totalPages;\n pageInput.max = totalPages;\n \n \/\/ 检测是否为长页码\n if (totalPages >= 10000) {\n floatButtons.classList.add('long-page');\n }\n \n \/\/ 切换按钮组显示\n mainBtn.addEventListener('click', function(e) {\n e.stopPropagation();\n floatButtons.classList.toggle('expanded');\n mainBtn.style.display = 'none';\n \n });\n \n \/\/ 点击页面其他区域收起按钮组\n document.addEventListener('click', function() {\n floatButtons.classList.remove('expanded');\n mainBtn.style.display = 'flex';\n });\n \n \/\/ 阻止按钮组内部点击事件冒泡\n document.querySelector('.button-group').addEventListener('click', function(e) {\n e.stopPropagation();\n });\n \n \/\/ 跳转按钮事件\n document.querySelector('.go-btn').addEventListener('click', jumpToPage);\n \n\n \/\/ 功能按钮事件\n homeBtn.addEventListener('click', function() {\n sessionStorage.setItem('previousUrl', \"\");\n window.location.reload();\n });\n \n topBtn.addEventListener('click', function() {\n window.scrollTo({top: 0, behavior: 'smooth'});\n floatButtons.classList.remove('expanded');\n mainBtn.style.display = 'flex';\n });\n \n bottomBtn.addEventListener('click', function() {\n window.scrollTo({top: document.body.scrollHeight-100, behavior: 'smooth'});\n floatButtons.classList.remove('expanded');\n mainBtn.style.display = 'flex';\n });\n \n sortTypeSelect.value = urlParams.get('sortType') || '2';\n isFinishSelect.value = urlParams.get('isfinish') || '0';\n\n sortTypeSelect.onchange = () => updateUrlParam('sortType', sortTypeSelect.value);\n isFinishSelect.onchange = () => updateUrlParam('isfinish', isFinishSelect.value);\n\n initScrollListener();\n createBookElements(jsonData,offset);\n updateCurrentPage();\n \n const checkScrollable = () => {\n if (document.body.scrollHeight <= window.innerHeight) {\n \/\/ 如果内容不足以滚动,尝试加载更多\n if (hasMore) {\n loadNextPage();\n } else if (hasPrevious) {\n \/\/ 或者尝试加载上一页(如果有)\n loadPreviousPage();\n }\n }\n };\n \n \/\/ 初始检查\n checkScrollable();\n}\nwindow.addEventListener('beforeunload', () => {\n window.removeEventListener('scroll', handleScroll);\n window.removeEventListener('resize', checkScrollable);\n});\n \n \n\/\/ 标签管理\nif(\/#$\/.test(window.location.href)){\n bqUrl = window.location.href;\n const urls = bqUrl.split('#');\n const buttonData = urls.slice(1).filter(part => part.trim() !== '');\n\n const buttonContainer = document.createElement('div');\n buttonContainer.style.position = 'fixed';\n buttonContainer.style.bottom = '50px';\n buttonContainer.style.right = '20px';\n buttonContainer.style.zIndex = '1000';\n buttonContainer.style.display = 'flex';\n buttonContainer.style.flexDirection = 'column';\n buttonContainer.style.gap = '10px';\n document.body.appendChild(buttonContainer);\n\n buttonData.forEach((data, index) => {\n const [label, url] = data.split('@');\n const button = document.createElement('button');\n button.textContent = decodeURIComponent(label) || `标签${index + 1}`;\n button.style.padding = '5px 10px';\n button.style.backgroundColor = '#007BFF';\n button.style.color = 'white';\n button.style.border = 'none';\n button.style.borderRadius = '5px';\n button.style.cursor = 'pointer';\n button.addEventListener('click', () => fetchAndRender(decode(url)));\n buttonContainer.appendChild(button);\n });\n\n const button = document.createElement('button');\n button.textContent = '生成标签';\n button.style.position = 'fixed';\n button.style.bottom = '20px';\n button.style.right = '20px';\n button.style.zIndex = '1000';\n button.style.padding = '5px 10px';\n button.style.backgroundColor = '#007BFF';\n button.style.color = 'white';\n button.style.border = 'none';\n button.style.borderRadius = '5px';\n button.style.cursor = 'pointer';\n document.body.appendChild(button);\n\n button.addEventListener('click', () => {\n const form = document.getElementsByTagName('form')[1];\n const formData = new FormData(form);\n const finalUrl = generateFinalUrl(formData);\n \n const overlay = document.createElement('div');\n overlay.style.position = 'fixed';\n overlay.style.top = '0';\n overlay.style.left = '0';\n overlay.style.width = '100%';\n overlay.style.height = '100%';\n overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';\n overlay.style.zIndex = '1001';\n overlay.style.display = 'flex';\n overlay.style.justifyContent = 'center';\n overlay.style.alignItems = 'center';\n \n const dialog = document.createElement('div');\n dialog.style.backgroundColor = 'white';\n dialog.style.padding = '20px';\n dialog.style.borderRadius = '10px';\n dialog.style.width = '80%';\n dialog.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.3)';\n dialog.style.textAlign = 'center';\n\n const promptText = document.createElement('p');\n promptText.textContent = '复制标签网址到【源URL】保存标签';\n promptText.style.margin = '10px 0';\n promptText.style.fontSize = '14px';\n promptText.style.color = '#333';\n dialog.appendChild(promptText);\n\n const urlTextarea = document.createElement('textarea');\n urlTextarea.value = `标签名@${encode(finalUrl)}#`;\n urlTextarea.style.width = '90%';\n urlTextarea.style.height = '100px';\n urlTextarea.style.padding = '10px';\n urlTextarea.style.margin = '10px';\n urlTextarea.style.outline = 'none';\n urlTextarea.style.border = 'none';\n urlTextarea.style.resize = 'none';\n urlTextarea.readOnly = true;\n dialog.appendChild(urlTextarea);\n\n const copyButton = document.createElement('button');\n copyButton.textContent = '一键复制';\n copyButton.style.margin = '10px';\n copyButton.style.padding = '10px 20px';\n copyButton.style.backgroundColor = '#28a745';\n copyButton.style.color = 'white';\n copyButton.style.border = 'none';\n copyButton.style.borderRadius = '5px';\n copyButton.style.cursor = 'pointer';\n copyButton.addEventListener('click', () => {\n urlTextarea.select();\n document.execCommand('copy');\n alert('已复制到剪贴板!');\n document.body.removeChild(overlay);\n });\n dialog.appendChild(copyButton);\n\n const closeButton = document.createElement('button');\n closeButton.textContent = '关闭';\n closeButton.style.margin = '10px';\n closeButton.style.padding = '10px 20px';\n closeButton.style.backgroundColor = '#dc3545';\n closeButton.style.color = 'white';\n closeButton.style.border = 'none';\n closeButton.style.borderRadius = '5px';\n closeButton.style.cursor = 'pointer';\n closeButton.addEventListener('click', () => {\n document.body.removeChild(overlay);\n });\n dialog.appendChild(closeButton);\n\n overlay.appendChild(dialog);\n document.body.appendChild(overlay);\n });\n\n const forms = document.getElementsByTagName('form')[1];\n restoreBookPosition();\n forms.addEventListener('submit', function(event) {\n event.preventDefault();\n const formData = new FormData(forms);\n currentUrl = generateFinalUrl(formData);\n previousUrl = currentUrl;\n currentOffset = 0;\n fetchAndRender(currentUrl);\n });\n}\n\n\/\/ 书籍详情页按钮\nif (\/m\\.jjwxc\\.net\\\/book2\\\/\\d+\/.test(window.location.href)) {\n document.getElementById(\"app\").remove();\n const bookid = window.location.href.split('\/').pop();\n const booksrc = `legado:\/\/import\/addToBookshelf?src=${encodeURIComponent(`http:\/\/app-cdn.jjwxc.net\/androidapi\/novelbasicinfo?novelId=${bookid}`)}`;\n const title = document.querySelector(\".big.o\").innerText.replace(\/首页>\/, '');\n const author = document.querySelector(\".authorname-content a\").innerText;\n\n createButton(title, \"addBookshelf\", 14, booksrc);\n createButton(title, \"bookName\", 18);\n createButton(author, \"bookAuthor\", 22);\n}\n\nfunction createButton(name, type, top, url) {\n const typeButton = type === \"bookName\" ? \"搜索书名\" : \n type === \"bookAuthor\" ? \"搜索作者\" : \"加入书架\";\n const query = `${type}=${name}`;\n const href = url ? `${url}#${query}` : `${window.location.pathname}?${query}`;\n const button = document.createElement('a');\n button.href = href.replace(\/#addBookshelf.*\/, '');\n button.innerHTML = `\n <button style=\"\n outline: none; \n position: fixed; \n top: ${top}%; \n right: 7%; \n z-index: 999; \n border-radius: 15px; \n color: #166188;\n border: 0px solid #000;\n background: #E6F3F5;\n padding: 4px 6px;\n box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);\n \">\n <b>${typeButton}<\/b>\n <\/button>\n `;\n document.body.appendChild(button);\n}\n\n\/\/ 样式\nfunction addStyles() {\n const style = document.createElement('style');\n style.innerHTML = `\n .center {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n background: white; \/* 添加背景色避免文字重叠 *\/\n z-index: 999; \/* 确保在最上层 *\/\n padding: 10px 0; \/* 适当内边距 *\/\n box-shadow: 0 2px 5px rgba(0,0,0,0.1); \/* 可选:添加阴影效果 *\/\n}\n\n a:link { color: #415E44; }\n a:visited { color: #8EB28B; }\n button, select, input {\n background: #E9F5F3;\n color: green;\n font-weight: bold;\n border-radius: 15px;\n border: 0px solid #000;\n padding: 5px 10px;\n box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);\n }\n \/* 悬浮按钮容器 *\/\n .float-buttons {\n position: fixed;\n right: 10px;\n bottom: 10px;\n display: flex;\n flex-direction: column-reverse;\n align-items: center;\n gap: 8px;\n z-index: 999;\n }\n \n \/* 主按钮 - 深绿色圆形 *\/\n .main-btn {\n width: 50px;\n height: 50px;\n border-radius: 50%;\n background: #E9F5F3;\n color: green;\n border: none;\n cursor: pointer;\n box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n outline: none;\n -webkit-tap-highlight-color: transparent;\n transition: all 0.3s ease;\n padding: 5px 2px;\n position: relative;\n }\n \n \/* 当前页和总页数颜色区分 *\/\n .main-btn .current {\n color: green; \/* 亮黄色突出当前页 *\/\n font-weight: bold;\n margin-bottom: 2px;\n }\n \n .main-btn .total {\n color: rgba(0, 0, 0, 0.8); \/* 半透明白色显示总页数 *\/\n font-size: 0.9em;\n margin-top: 2px;\n }\n \n \/* 分割线 *\/\n .main-btn .divider {\n width: 60%;\n height: 1px;\n background: green;\n margin: 3px 0;\n }\n \n \/* 功能按钮容器 - 默认隐藏 *\/\n .button-group {\n display: none;\n flex-direction: column-reverse;\n align-items: center;\n gap: 8px;\n }\n \n \/* 展开时显示 *\/\n .expanded .button-group {\n display: flex;\n \n }\n \n \/* 功能按钮样式 *\/\n .func-btn {\n width: 50px;\n height: 50px;\n border-radius: 50%;\n background: #495057;\n color: white;\n border: none;\n cursor: pointer;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.7);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n outline: none;\n \n \n \n }\n \n \/* 不同功能按钮颜色 *\/\n .func-btn.home { background: #9B5F64; } \/* 红色 *\/\n .func-btn.bottom { background: #8DA371; } \/* 绿色 *\/\n .func-btn.top { background: #67749A; } \/* 蓝色 *\/\n \n \/* 页码控制面板 *\/\n .page-control {\n background: #E9F5F3;\n color: white;\n padding: 10px;\n border-radius: 25px;\n font-size: 14px;\n box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);\n display: flex;\n flex-direction: column;\n align-items: center;\n width: 30px;\n \n }\n \n .page-info {\n display: flex;\n flex-direction: column;\n align-items: center;\n margin-bottom: 8px;\n font-size: 12px;\n line-height: 1.3;\n position: relative;\n width: 100%;\n }\n \n \/* 控制面板中的分割线 *\/\n .page-info .divider {\n width: 90%;\n height: 1px;\n background: green;\n margin: 4px 0;\n }\n \n \/* 控制面板中的页码颜色区分 *\/\n .page-info .current-page {\n color: green; \/* 亮黄色 *\/\n font-weight: bold;\n font-size: 14px;\n }\n \n .page-info .total-pages {\n color: rgba(0, 0, 0, 0.6); \/* 半透明白色 *\/\n font-size: 11px;\n }\n \n .page-jump {\n display: flex;\n flex-direction: column;\n align-items: center;\n width: 100%;\n }\n \n .page-jump input {\n width: 30px;\n padding: 6px;\n border: none;\n border-radius: 15px;\n text-align: center;\n outline: none;\n font-size: 12px;\n margin-bottom: 6px;\n background: #f8f9fa;\n }\n \n .page-jump button {\n width: 30px;\n height: 25px;\n background: #2b8a3e;\n color: white;\n border: none;\n border-radius: 15px;\n cursor: pointer;\n font-size: 12px;\n transition: background 0.2s;\n }\n \n .page-jump button:hover {\n background: #2f9e44;\n }\n \n \/* 按钮悬停效果 *\/\n .main-btn:hover, .func-btn:hover {\n transform: translateY(-3px);\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25);\n }\n \n \/* 按钮点击效果 *\/\n .main-btn:active, .func-btn:active {\n transform: translateY(0);\n }\n \n \/* 长页码时的特殊样式 *\/\n .long-page .main-btn {\n font-size: 11px;\n padding: 3px 1px;\n }\n \n .book {\n width: 100%;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n margin-bottom: 8px;\n }\n .bookdes {\n display: flex;\n flex-direction: row;\n margin: 5px;\n width: 100%;\n position: relative;\n align-items: center;\n }\n .pic {\n width: 25vw;\n margin-right: 20px;\n align-self: center;\n }\n .details {\n width: 72vw;\n margin-left: 20px;\n align-self: center;\n margin-left: auto;\n }\n .pic img {\n border: 1px solid #C4C4C4;\n border-radius: 5px;\n box-shadow: 1px 2px 2px black;\n width: 97%;\n height: auto;\n }\n .name { font-size: 20px; color: #000; }\n .author {\n font-size: 15px;\n display: inline-block;\n position: absolute;\n right: 8px;\n }\n .tag { font-size: 12px; margin-top: -10px; }\n #loading {\n background: rgba(0,0,0,0.1);\n \n margin: 10px 0;\n }\n `;\n document.head.appendChild(style);\n}\n\n", "lastUpdateTime": 0, "loadWithBaseUrl": true, "ruleArticles": "", "shouldOverrideUrlLoading": "if(\/[\\?#&]book[AN]\/.test(url)){\n\ttitle = url.match(\/[\\?#&]book[AN].+?=(.*)\/)[1];\n\tURLDecoder = Packages.java.net.URLDecoder;\n\tgb2312Str = URLDecoder.decode(title,\"gbk\");\n utf8Str = URLDecoder.decode(title,\"utf-8\");\n\t result = gb2312Str.length() >= utf8Str.length();\n\t title = result?decodeURI(title):gb2312Str;\n\t java.searchBook(title);\n\t result = true\n\t}", "singleUrl": true, "sourceComment": "❗️保存标签,请按照以下格式放在【源URL】:\n\nhttps:\/\/m.jjwxc.net\/assort#标签1@生成的标签#标签2@生成的标签#标签3@生成的标签#\n\n\n❗️注意开头的https:\/\/m.jjwxc.net\/assort#不要删除,还有末尾的#也不要删除,每个标签分隔是用#。\n\n\n下面是完整【源URL】案例\n\nhttps:\/\/m.jjwxc.net\/assort#百合强强并重生@MTksNzUmcmVtb3ZldGFncz0mbm90bGlrZWNvbGxlY3Rpb25UeXBlcz1vcnMmZnc9MCZ5Yz0wJnh4PTMmc2Q9MCZseD0wJm1haW52aWV3PTAmZmJzaj0wJm5vdmVsYmVmYXZvcml0ZWRjb3VudD0wJmlzZmluaXNoPSZjb2xsZWN0aW9uVHlwZXM9YW5kcyZzZWFyY2hrZXlXb3Jkcz0=#百合甜爽穿@MTI0LDEzNywxMzQmcmVtb3ZldGFncz0mbm90bGlrZWNvbGxlY3Rpb25UeXBlcz1vcnMmZnc9MCZ5Yz0wJnh4PTMmc2Q9MCZseD0wJm1haW52aWV3PTAmZmJzaj0wJm5vdmVsYmVmYXZvcml0ZWRjb3VudD0wJmlzZmluaXNoPSZjb2xsZWN0aW9uVHlwZXM9b3JzJnNlYXJjaGtleVdvcmRzPQ==#", "sourceGroup": "阅读", "sourceIcon": "https:\/\/m-static.jjwxc.net\/images\/wap\/logo.png", "sourceName": " 📖\n精确搜索", "sourceUrl": "https:\/\/m.jjwxc.net\/assort#", "style": "" }