This commit is contained in:
topfive 2025-06-03 17:07:32 +08:00
parent e3a61f776a
commit 1a109e2ff9
11 changed files with 22493 additions and 1714 deletions

View File

@ -7,6 +7,10 @@ trigger:
webhook: gitlink@1.0.0
event:
- ref: push
ruleset:
- param-ref: branch
operator: EQ
value: '""'
ruleset-operator: AND
workflow:
- ref: start
@ -16,9 +20,9 @@ workflow:
name: git clone
task: git_clone@1.2.9
input:
username: ((ganshihao21.ganshihao21_user))
password: ((ganshihao21.ganshihao21))
remote_url: '"https://gitlink.org.cn/ganshihao21/reposync.git"'
username: ((yjh.gitlink_name))
password: ((yjh.gitlink_passwd))
remote_url: '"https://www.gitlink.org.cn/jkcl/gitlink_help_center.git"'
ref: '"refs/heads/master"'
commit_id: '""'
depth: 1
@ -28,11 +32,11 @@ workflow:
name: docker镜像构建
task: docker_image_build@1.6.0
input:
docker_username: ((nudt_devops.nudt_devops))
docker_password: ((nudt_devops.nudt_devops))
image_name: '"registry.cn-guangzhou.aliyuncs.com/nudt_devops/reposync_group5"'
docker_username: ((yjh.aliyun_name))
docker_password: ((yjh.aliyun_passwd))
image_name: '"crpi-rlxgl92hhxkh8r6q.cn-hangzhou.personal.cr.aliyuncs.com/yuanjiahong/group_07"'
image_tag: '"latest"'
registry_address: '"registry.cn-guangzhou.aliyuncs.com"'
registry_address: '"crpi-rlxgl92hhxkh8r6q.cn-hangzhou.personal.cr.aliyuncs.com"'
docker_file: '"Dockerfile"'
docker_build_path: '"."'
workspace: git_clone_0.git_path
@ -40,23 +44,24 @@ workflow:
build_args: '""'
needs:
- git_clone_0
- ref: ssh_cmd_0
name: ssh执行命令
task: ssh_cmd@1.1.1
input:
ssh_pass: ((ssh_key.ssh_key))
ssh_ip: '"47.96.164.145"'
ssh_port: '"22"'
ssh_user: '"root"'
ssh_cmd: '"docker stop abc_group5 && docker rm abc_group5 && docker pull
registry.cn-guangzhou.aliyuncs.com/nudt_devops/reposync_group5:latest &&
docker run -d -p 3000:3000 --name abc_group5
registry.cn-guangzhou.aliyuncs.com/nudt_devops/reposync_group5:latest"'
needs:
- docker_image_build_0
- ref: end
name: 结束
task: end
needs:
- ssh_cmd_0
- ref: ssh_cmd_0
name: ssh执行命令
task: ssh_cmd@1.1.1
input:
ssh_pass: ((yjh.ssh_passwd))
ssh_ip: '"114.55.237.214"'
ssh_port: '"22"'
ssh_user: '"root"'
ssh_cmd: '"docker stop group_07 || true && docker rm group_07 || true && docker
pull
crpi-rlxgl92hhxkh8r6q.cn-hangzhou.personal.cr.aliyuncs.com/yuanjiahong/group_07:latest;
docker run -d -p 3000:3000 --name group_07
crpi-rlxgl92hhxkh8r6q.cn-hangzhou.personal.cr.aliyuncs.com/yuanjiahong/group_07:latest"'
needs:
- docker_image_build_0

View File

@ -1,5 +1,6 @@
const lightCodeTheme = require('prism-react-renderer/themes/github');
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
import("@easyops-cn/docusaurus-search-local").PluginOptions;
/** @type {import('@docusaurus/types').DocusaurusConfig} */
module.exports = {
@ -161,15 +162,21 @@ module.exports = {
],
],
themes: [
// ... Your other themes.
[
require.resolve("@cmfcmf/docusaurus-search-local"),
require.resolve("@easyops-cn/docusaurus-search-local"),
{
// Options for @cmfcmf/docusaurus-search-local
indexDocs: true,
indexBlog: true,
indexPages: false,
// ... Your options.
// `hashed` is recommended as long-term-cache of index file is possible.
hashed: true,
language: ["en", "zh"],
maxSearchResults: 8,
highlightSearchTermsOnTargetPage: true,
blogRouteBasePath: "/",
explicitSearchResultPath: true,
// For Docs using Chinese, The `language` is recommended to set to:
// ```
// language: ["en", "zh"],
// ```
},
],
],

22085
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -18,13 +18,12 @@
"@docusaurus/core": "2.4.1",
"@docusaurus/plugin-client-redirects": "2.4.1",
"@docusaurus/preset-classic": "2.4.1",
"@easyops-cn/docusaurus-search-local": "^0.35.0",
"@mdx-js/react": "1.6.22",
"@node-rs/jieba": "^1.7.0",
"clsx": "1.2.1",
"docusaurus-gtm-plugin": "0.0.2",
"docusaurus-plugin-image-zoom": "1.0.1",
"html2canvas": "^1.4.1",
"jspdf": "^3.0.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-modal": "3.16.1"

View File

@ -1,48 +0,0 @@
{
"name": "docusaurus",
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"dev": "docusaurus start --port 3000",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@cmfcmf/docusaurus-search-local": "1.1.0",
"@docusaurus/core": "2.4.1",
"@docusaurus/plugin-client-redirects": "2.4.1",
"@docusaurus/preset-classic": "2.4.1",
"@easyops-cn/docusaurus-search-local": "^0.35.0",
"@mdx-js/react": "1.6.22",
"@node-rs/jieba": "^1.7.0",
"clsx": "1.2.1",
"docusaurus-gtm-plugin": "0.0.2",
"docusaurus-plugin-image-zoom": "1.0.1",
"html2canvas": "^1.4.1",
"jspdf": "^3.0.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-modal": "3.16.1"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"engines": {
"node": ">=14.18.1 <=18.x"
}
}

View File

@ -1,415 +0,0 @@
import React, { useState, useEffect } from 'react';
const FontSizeController = () => {
const [fontSize, setFontSize] = useState('medium');
const [mounted, setMounted] = useState(false);
const [isVisible, setIsVisible] = useState(false);
// 字体大小配置 - 增加更多选项
const fontSizes = {
extraSmall: {
name: '极小',
icon: '🔍',
scale: 0.75, // 12px 基于 16px
description: '超密集阅读'
},
small: {
name: '小号',
icon: '🔤',
scale: 0.875, // 14px 基于 16px
description: '密集阅读'
},
medium: {
name: '标准',
icon: '📝',
scale: 1, // 16px 标准
description: '推荐大小'
},
large: {
name: '大号',
icon: '📖',
scale: 1.125, // 18px 基于 16px
description: '舒适阅读'
},
extraLarge: {
name: '特大',
icon: '📚',
scale: 1.25, // 20px 基于 16px
description: '长时间阅读'
},
huge: {
name: '巨大',
icon: '🔍',
scale: 1.375, // 22px 基于 16px
description: '视力辅助'
}
};
useEffect(() => {
setMounted(true);
// 从localStorage读取保存的字体大小设置
const savedFontSize = localStorage.getItem('doc-font-size') || 'medium';
const savedVisibility = localStorage.getItem('font-controller-visible') === 'true';
setFontSize(savedFontSize);
setIsVisible(savedVisibility);
applyFontSize(savedFontSize);
}, []);
// 应用字体大小到文档
const applyFontSize = (size) => {
const scale = fontSizes[size].scale;
// 创建或更新CSS变量
const root = document.documentElement;
root.style.setProperty('--doc-font-scale', scale);
// 直接应用到文档内容区域
const contentSelectors = [
'article',
'.markdown',
'.theme-doc-markdown',
'[class*="docItemContainer"]',
'[class*="docItemCol"]'
];
contentSelectors.forEach(selector => {
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
element.style.fontSize = `${scale}rem`;
});
});
// 调整特定元素
const specificElements = {
'h1': `${scale * 2.5}rem`,
'h2': `${scale * 2}rem`,
'h3': `${scale * 1.75}rem`,
'h4': `${scale * 1.5}rem`,
'h5': `${scale * 1.25}rem`,
'h6': `${scale * 1.125}rem`,
'p, li, td, th, blockquote': `${scale}rem`,
'code': `${scale * 0.875}rem`,
'.table-of-contents': `${scale * 0.875}rem`
};
Object.entries(specificElements).forEach(([selectors, size]) => {
const elements = document.querySelectorAll(selectors);
elements.forEach(element => {
// 只调整文档内容区域的元素,避免影响导航等
const isInContent = element.closest('article, .markdown, .theme-doc-markdown');
if (isInContent) {
element.style.fontSize = size;
}
});
});
};
// 切换字体大小
const changeFontSize = (size) => {
setFontSize(size);
localStorage.setItem('doc-font-size', size);
applyFontSize(size);
showNotification(`✅ 字体大小已切换为:${fontSizes[size].name}`, 'success');
};
// 切换显示/隐藏
const toggleVisibility = () => {
const newVisibility = !isVisible;
setIsVisible(newVisibility);
localStorage.setItem('font-controller-visible', newVisibility.toString());
showNotification(newVisibility ? '📝 字体调节器已显示' : '👁️ 字体调节器已隐藏', 'info');
};
// 显示通知
const showNotification = (message, type = 'info') => {
try {
const notification = document.createElement('div');
notification.textContent = message;
const bgColors = {
success: '#25c2a0',
info: '#3498db',
warning: '#f39c12'
};
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${bgColors[type] || bgColors.info};
color: white;
padding: 12px 20px;
border-radius: 6px;
font-size: 14px;
z-index: 10000;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
animation: slideIn 0.3s ease;
max-width: 300px;
word-wrap: break-word;
`;
// 添加动画样式
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
`;
if (!document.head.querySelector('[data-font-notification-style]')) {
style.setAttribute('data-font-notification-style', 'true');
document.head.appendChild(style);
}
document.body.appendChild(notification);
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 3000);
} catch (error) {
console.error('显示通知失败:', error);
}
};
// 如果组件还没挂载,不渲染任何内容
if (!mounted) {
return null;
}
const containerStyle = {
position: 'fixed',
top: '50%',
right: '20px', // 移到右侧
transform: 'translateY(-50%)',
zIndex: 1001,
fontFamily: 'system-ui, -apple-system, sans-serif'
};
const toggleButtonStyle = {
position: 'fixed',
top: '50%',
right: isVisible ? '140px' : '20px', // 根据显示状态调整位置
transform: 'translateY(-50%)',
zIndex: 1002,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '40px',
height: '40px',
backgroundColor: '#3b82f6',
color: 'white',
border: 'none',
borderRadius: '50%',
cursor: 'pointer',
fontSize: '16px',
boxShadow: '0 4px 12px rgba(59, 130, 246, 0.3)',
transition: 'all 0.3s ease',
fontFamily: 'system-ui, -apple-system, sans-serif'
};
const buttonStyle = {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '6px',
padding: '10px',
backgroundColor: 'white',
border: '2px solid #e5e7eb',
borderRadius: '10px',
cursor: 'pointer',
fontSize: '11px',
fontWeight: '600',
color: '#374151',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
transition: 'all 0.2s ease',
minWidth: '50px',
textAlign: 'center'
};
const activeButtonStyle = {
...buttonStyle,
backgroundColor: '#3b82f6',
borderColor: '#3b82f6',
color: 'white',
transform: 'translateY(-2px)',
boxShadow: '0 6px 16px rgba(59, 130, 246, 0.3)'
};
return (
<>
{/* 切换显示/隐藏按钮 */}
<button
onClick={toggleVisibility}
style={toggleButtonStyle}
onMouseEnter={(e) => {
e.target.style.backgroundColor = '#2563eb';
e.target.style.transform = 'translateY(-50%) scale(1.1)';
}}
onMouseLeave={(e) => {
e.target.style.backgroundColor = '#3b82f6';
e.target.style.transform = 'translateY(-50%) scale(1)';
}}
title={isVisible ? '隐藏字体调节器' : '显示字体调节器'}
>
{isVisible ? '✕' : 'Aa'}
</button>
{/* 字体大小控制面板 */}
{isVisible && (
<div style={containerStyle}>
<div style={{
display: 'flex',
flexDirection: 'column',
gap: '6px',
backgroundColor: 'white',
padding: '12px',
borderRadius: '16px',
boxShadow: '0 8px 24px rgba(0,0,0,0.15)',
border: '1px solid #e5e7eb',
animation: 'slideInFromRight 0.3s ease'
}}>
{/* 标题 */}
<div style={{
fontSize: '11px',
fontWeight: '700',
color: '#6b7280',
textAlign: 'center',
marginBottom: '4px',
letterSpacing: '0.5px'
}}>
字体大小
</div>
{/* 当前字体大小显示 */}
<div style={{
fontSize: '10px',
fontWeight: '600',
color: '#3b82f6',
textAlign: 'center',
marginBottom: '8px',
padding: '4px 8px',
backgroundColor: '#eff6ff',
borderRadius: '6px'
}}>
当前: {fontSizes[fontSize].name} ({Math.round(fontSizes[fontSize].scale * 16)}px)
</div>
{/* 字体大小选项 */}
<div style={{
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: '6px'
}}>
{Object.entries(fontSizes).map(([size, config]) => (
<button
key={size}
onClick={() => changeFontSize(size)}
style={fontSize === size ? activeButtonStyle : buttonStyle}
onMouseEnter={(e) => {
if (fontSize !== size) {
e.target.style.backgroundColor = '#f3f4f6';
e.target.style.borderColor = '#d1d5db';
e.target.style.transform = 'translateY(-1px)';
}
}}
onMouseLeave={(e) => {
if (fontSize !== size) {
e.target.style.backgroundColor = 'white';
e.target.style.borderColor = '#e5e7eb';
e.target.style.transform = 'translateY(0)';
}
}}
title={`${config.name} (${Math.round(config.scale * 16)}px) - ${config.description}`}
>
<span style={{ fontSize: '14px' }}>{config.icon}</span>
<span style={{ fontSize: '9px', fontWeight: '600' }}>
{config.name}
</span>
</button>
))}
</div>
{/* 快捷操作按钮 */}
<div style={{
display: 'flex',
gap: '6px',
marginTop: '8px'
}}>
<button
onClick={() => changeFontSize('medium')}
style={{
...buttonStyle,
backgroundColor: fontSize === 'medium' ? '#6b7280' : '#f9fafb',
borderColor: fontSize === 'medium' ? '#6b7280' : '#e5e7eb',
color: fontSize === 'medium' ? 'white' : '#6b7280',
fontSize: '9px',
padding: '6px 10px',
minWidth: 'auto',
flex: 1
}}
onMouseEnter={(e) => {
if (fontSize !== 'medium') {
e.target.style.backgroundColor = '#f3f4f6';
e.target.style.borderColor = '#d1d5db';
}
}}
onMouseLeave={(e) => {
if (fontSize !== 'medium') {
e.target.style.backgroundColor = '#f9fafb';
e.target.style.borderColor = '#e5e7eb';
}
}}
title="重置为标准大小"
>
重置
</button>
<button
onClick={toggleVisibility}
style={{
...buttonStyle,
backgroundColor: '#ef4444',
borderColor: '#ef4444',
color: 'white',
fontSize: '9px',
padding: '6px 10px',
minWidth: 'auto',
flex: 1
}}
onMouseEnter={(e) => {
e.target.style.backgroundColor = '#dc2626';
e.target.style.borderColor = '#dc2626';
}}
onMouseLeave={(e) => {
e.target.style.backgroundColor = '#ef4444';
e.target.style.borderColor = '#ef4444';
}}
title="隐藏字体调节器"
>
隐藏
</button>
</div>
</div>
</div>
)}
{/* 添加动画样式 */}
<style>{`
@keyframes slideInFromRight {
from {
transform: translateX(100%) translateY(-50%);
opacity: 0;
}
to {
transform: translateX(0) translateY(-50%);
opacity: 1;
}
}
`}</style>
</>
);
};
export default FontSizeController;

View File

@ -1,128 +0,0 @@
import React from 'react';
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
const PDFExport = ({ documentTitle }) => {
const exportToPDF = async () => {
try {
// 显示加载状态
const button = document.querySelector('.pdf-export-btn');
const originalText = button.textContent;
button.textContent = '导出中...';
button.disabled = true;
// 获取主要内容区域
const element = document.querySelector('article') || document.querySelector('.markdown') || document.querySelector('main');
if (!element) {
alert('未找到可导出的内容');
return;
}
// 配置 html2canvas 选项
const canvas = await html2canvas(element, {
scale: 2, // 提高清晰度
useCORS: true,
allowTaint: true,
backgroundColor: '#ffffff',
width: element.scrollWidth,
height: element.scrollHeight,
});
// 创建 PDF
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({
orientation: 'portrait',
unit: 'mm',
format: 'a4',
});
// 计算图片在PDF中的尺寸
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const imgWidth = canvas.width;
const imgHeight = canvas.height;
const ratio = Math.min(pdfWidth / imgWidth, pdfHeight / imgHeight);
const imgX = (pdfWidth - imgWidth * ratio) / 2;
const imgY = 20;
// 如果内容高度超过一页,需要分页
if (imgHeight * ratio > pdfHeight - 40) {
const totalPages = Math.ceil((imgHeight * ratio) / (pdfHeight - 40));
for (let i = 0; i < totalPages; i++) {
if (i > 0) pdf.addPage();
const yOffset = -i * (pdfHeight - 40) / ratio;
pdf.addImage(
imgData,
'PNG',
imgX,
imgY + yOffset * ratio,
imgWidth * ratio,
imgHeight * ratio
);
}
} else {
pdf.addImage(
imgData,
'PNG',
imgX,
imgY,
imgWidth * ratio,
imgHeight * ratio
);
}
// 下载PDF
const fileName = documentTitle ? `${documentTitle}.pdf` : '文档.pdf';
pdf.save(fileName);
// 恢复按钮状态
button.textContent = originalText;
button.disabled = false;
} catch (error) {
console.error('PDF导出失败:', error);
alert('PDF导出失败请重试');
// 恢复按钮状态
const button = document.querySelector('.pdf-export-btn');
if (button) {
button.textContent = '导出PDF';
button.disabled = false;
}
}
};
return (
<button
className="pdf-export-btn"
onClick={exportToPDF}
style={{
backgroundColor: '#25c2a0',
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px',
marginLeft: '10px',
transition: 'all 0.2s ease',
}}
onMouseEnter={(e) => {
e.target.style.backgroundColor = '#21af90';
e.target.style.transform = 'translateY(-1px)';
}}
onMouseLeave={(e) => {
e.target.style.backgroundColor = '#25c2a0';
e.target.style.transform = 'translateY(0)';
}}
>
📄 导出PDF
</button>
);
};
export default PDFExport;

View File

@ -1,166 +0,0 @@
import { useEffect, useCallback } from 'react';
import { useLocation } from '@docusaurus/router';
const ReadingPosition = () => {
const location = useLocation();
// 保存滚动位置
const saveScrollPosition = useCallback(() => {
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
const currentPath = location.pathname;
// 使用localStorage保存每个页面的滚动位置
const savedPositions = JSON.parse(localStorage.getItem('docReadingPositions') || '{}');
savedPositions[currentPath] = {
scrollTop: scrollPosition,
timestamp: Date.now(),
};
// 只保留最近30天的阅读位置
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
Object.keys(savedPositions).forEach(path => {
if (savedPositions[path].timestamp < thirtyDaysAgo) {
delete savedPositions[path];
}
});
localStorage.setItem('docReadingPositions', JSON.stringify(savedPositions));
}, [location.pathname]);
// 恢复滚动位置
const restoreScrollPosition = useCallback(() => {
const currentPath = location.pathname;
const savedPositions = JSON.parse(localStorage.getItem('docReadingPositions') || '{}');
if (savedPositions[currentPath]) {
const { scrollTop, timestamp } = savedPositions[currentPath];
// 如果保存的位置是在最近7天内的才恢复位置
const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
if (timestamp > sevenDaysAgo && scrollTop > 100) { // 只有滚动超过100px才恢复
// 延迟恢复,确保页面完全加载
setTimeout(() => {
window.scrollTo({
top: scrollTop,
behavior: 'smooth'
});
// 显示提示信息
showPositionNotification(scrollTop);
}, 300);
}
}
}, [location.pathname]);
// 显示位置恢复提示
const showPositionNotification = (scrollTop) => {
// 创建提示元素
const notification = document.createElement('div');
notification.innerHTML = `
<div style="
position: fixed;
top: 80px;
right: 20px;
background: #25c2a0;
color: white;
padding: 12px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 9999;
font-size: 14px;
max-width: 280px;
animation: slideInRight 0.3s ease-out;
">
<div style="display: flex; align-items: center; gap: 8px;">
<span>📖</span>
<span>已恢复到上次阅读位置</span>
<button onclick="this.parentElement.parentElement.parentElement.remove()"
style="background: none; border: none; color: white; font-size: 16px; cursor: pointer; margin-left: auto;">×</button>
</div>
</div>
`;
document.body.appendChild(notification);
// 添加动画样式
if (!document.querySelector('#reading-position-styles')) {
const styles = document.createElement('style');
styles.id = 'reading-position-styles';
styles.textContent = `
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
`;
document.head.appendChild(styles);
}
// 3秒后自动隐藏
setTimeout(() => {
if (notification.parentElement) {
notification.remove();
}
}, 3000);
};
// 节流函数
const throttle = (func, delay) => {
let timeoutId;
let lastExecTime = 0;
return function (...args) {
const currentTime = Date.now();
if (currentTime - lastExecTime > delay) {
func.apply(this, args);
lastExecTime = currentTime;
} else {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
lastExecTime = Date.now();
}, delay - (currentTime - lastExecTime));
}
};
};
useEffect(() => {
// 页面加载时恢复滚动位置
restoreScrollPosition();
// 监听滚动事件,节流保存位置
const throttledSavePosition = throttle(saveScrollPosition, 1000);
window.addEventListener('scroll', throttledSavePosition);
// 页面卸载时保存位置
const handleBeforeUnload = () => {
saveScrollPosition();
};
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('scroll', throttledSavePosition);
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [saveScrollPosition, restoreScrollPosition]);
// 路径变化时保存当前位置并尝试恢复新位置
useEffect(() => {
saveScrollPosition();
// 短暂延迟后恢复新页面的位置
const timer = setTimeout(() => {
restoreScrollPosition();
}, 100);
return () => clearTimeout(timer);
}, [location.pathname, saveScrollPosition, restoreScrollPosition]);
return null; // 这是一个功能性组件,不渲染任何内容
};
export default ReadingPosition;

View File

@ -7,32 +7,16 @@
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: #2e8555;
--ifm-color-primary-dark: #29784c;
--ifm-color-primary-darker: #277148;
--ifm-color-primary-darkest: #205d3b;
--ifm-color-primary-light: #33925d;
--ifm-color-primary-lighter: #359962;
--ifm-color-primary-lightest: #3cad6e;
--ifm-color-primary: #466aff;
--ifm-color-primary-dark: rgb(33, 175, 144);
--ifm-color-primary-darker: rgb(31, 165, 136);
--ifm-color-primary-darkest: rgb(26, 136, 112);
--ifm-color-primary-light: rgb(70, 203, 174);
--ifm-color-primary-lighter: rgb(102, 212, 189);
--ifm-color-primary-lightest: rgb(146, 224, 208);
--ifm-code-font-size: 95%;
--search-local-modal-background:#1b2440;
/* 字体大小缩放变量 */
--doc-font-scale: 1;
}
/* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme='dark'] {
--ifm-color-primary: #25c2a0;
--ifm-color-primary-dark: #21af90;
--ifm-color-primary-darker: #1fa588;
--ifm-color-primary-darkest: #1a8870;
--ifm-color-primary-light: #29d5b0;
--ifm-color-primary-lighter: #32d8b4;
--ifm-color-primary-lightest: #4fddbf;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
}
.navbar{
background-color: rgba(27, 36, 64, 1);
}
@ -165,438 +149,4 @@ html[data-theme='dark'] .docusaurus-highlight-code-line {
.widget{
display: none!important;
opacity: 0;
}
/* === PDF导出和阅读位置功能样式 === */
/* PDF导出按钮样式 */
.pdf-export-button {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
background-color: #25c2a0;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
transition: all 0.2s ease;
}
.pdf-export-button:hover {
background-color: #21af90;
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0,0,0,0.2);
}
.pdf-export-button:active {
transform: translateY(0);
}
.pdf-export-button:disabled {
background-color: #6c757d;
cursor: not-allowed;
transform: none;
}
/* 响应式设计 */
@media (max-width: 768px) {
.pdf-export-button {
padding: 10px 14px;
font-size: 12px;
}
}
/* 打印样式 */
@media print {
.pdf-export-button,
[data-reading-position],
[class*="FontSizeController"] {
display: none !important;
}
/* 打印时确保内容区域使用标准字体大小 */
article,
.markdown,
.theme-doc-markdown {
font-size: 12pt !important;
}
h1 { font-size: 20pt !important; }
h2 { font-size: 18pt !important; }
h3 { font-size: 16pt !important; }
h4 { font-size: 14pt !important; }
h5 { font-size: 13pt !important; }
h6 { font-size: 12pt !important; }
p, li, td, th, blockquote {
font-size: 12pt !important;
line-height: 1.4 !important;
}
code {
font-size: 10pt !important;
}
}
/* 移动设备优化 */
@media (max-width: 996px) {
/* 字体调节器在移动设备上的样式调整 - 更新为右侧位置 */
.font-size-controller {
right: 10px !important;
left: auto !important;
transform: translateY(-50%) scale(0.9) !important;
}
/* 字体调节器切换按钮 */
[style*="position: fixed"][style*="right:"] {
right: 10px !important;
}
}
@media (max-width: 576px) {
.font-size-controller {
right: 5px !important;
left: auto !important;
transform: translateY(-50%) scale(0.8) !important;
}
/* 字体调节器切换按钮在小屏幕设备上 */
[style*="position: fixed"][style*="right:"] {
right: 5px !important;
}
/* 字体调节器面板在移动端的调整 */
[style*="gridTemplateColumns: '1fr 1fr'"] {
grid-template-columns: 1fr !important;
gap: 4px !important;
}
}
/* 字体调节器的深色主题适配 */
[data-theme='dark'] .font-size-controller {
background-color: #2d3748;
border-color: #4a5568;
color: #e2e8f0;
}
[data-theme='dark'] .font-size-controller button {
background-color: #4a5568;
border-color: #718096;
color: #e2e8f0;
}
[data-theme='dark'] .font-size-controller button:hover {
background-color: #718096;
border-color: #a0aec0;
}
[data-theme='dark'] .font-size-controller button.active {
background-color: #3182ce;
border-color: #3182ce;
color: white;
}
/* 右侧字体调节器容器适配 */
@media (max-width: 768px) {
/* 确保字体调节器和PDF导出按钮不重叠 */
[style*="bottom: 80px"] {
bottom: 20px !important;
}
/* 字体调节器右侧位置优化 */
[style*="right: 20px"][style*="position: fixed"][style*="top: 50%"] {
right: 15px !important;
}
[style*="right: 140px"][style*="position: fixed"][style*="top: 50%"] {
right: 120px !important;
}
}
/* 字体调节器与其他固定元素的协调 */
@media (max-height: 600px) {
/* 在矮屏幕设备上调整位置 */
[style*="position: fixed"][style*="top: 50%"][style*="right:"] {
top: 40% !important;
}
}
/* 字体大小调节动画 */
article,
.markdown,
.theme-doc-markdown,
h1, h2, h3, h4, h5, h6,
p, li, td, th, blockquote, code {
transition: font-size 0.3s ease;
}
/* 确保字体调节器不影响导航和其他UI元素 */
.navbar,
.menu,
.footer,
.pagination-nav,
.table-of-contents,
.breadcrumbs {
font-size: initial !important;
}
.navbar *,
.menu *,
.footer *,
.pagination-nav *,
.table-of-contents *,
.breadcrumbs * {
font-size: inherit !important;
}
/* 阅读位置通知动画 */
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOutRight {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
/* 简单的滑入动画 */
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* 阅读进度指示器 */
.reading-progress {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, #25c2a0, #466aff);
transform-origin: left;
z-index: 9999;
transition: transform 0.1s ease;
}
/* 移动端适配 */
@media (max-width: 768px) {
.pdf-export-button {
padding: 6px 12px;
font-size: 12px;
}
/* 移动端固定按钮位置调整 */
.fixed-buttons {
bottom: 15px !important;
right: 15px !important;
}
.fixed-buttons button {
width: 40px !important;
height: 40px !important;
font-size: 16px !important;
}
}
/* 暗色主题适配 */
html[data-theme='dark'] .pdf-export-button {
background-color: #25c2a0 !important;
color: white !important;
}
html[data-theme='dark'] .pdf-export-button:hover {
background-color: #21af90 !important;
}
/* 打印时隐藏功能按钮 */
@media print {
.pdf-export-button,
.fixed-buttons,
.doc-toolbar {
display: none !important;
}
}
/* 滚动条样式优化 */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
transition: background 0.2s ease;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
html[data-theme='dark'] ::-webkit-scrollbar-track {
background: #2a2a2a;
}
html[data-theme='dark'] ::-webkit-scrollbar-thumb {
background: #555;
}
html[data-theme='dark'] ::-webkit-scrollbar-thumb:hover {
background: #777;
}
/* === 翻译功能样式 === */
/* 翻译按钮容器 */
.simple-translator {
user-select: none;
}
/* 翻译按钮动画 */
.translator-button {
animation: fadeInFromRight 0.5s ease-out;
}
@keyframes fadeInFromRight {
from {
transform: translateX(100%) translateY(-50%);
opacity: 0;
}
to {
transform: translateX(0) translateY(-50%);
opacity: 1;
}
}
/* 旋转加载动画 */
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 脉冲动画 */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
/* 翻译下拉菜单滚动条 */
.simple-translator div[style*="overflowY: auto"]::-webkit-scrollbar {
width: 6px;
}
.simple-translator div[style*="overflowY: auto"]::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.simple-translator div[style*="overflowY: auto"]::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.simple-translator div[style*="overflowY: auto"]::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* 翻译功能移动端适配 */
@media (max-width: 768px) {
.simple-translator .translator-button {
right: 5px !important;
minWidth: 140px !important;
}
.simple-translator button {
padding: 10px 12px !important;
fontSize: 12px !important;
}
/* 翻译提示框移动端适配 */
.simple-translator div[style*="bottom: 140px"] {
bottom: 120px !important;
right: 10px !important;
maxWidth: 160px !important;
fontSize: 11px !important;
}
}
/* 暗色主题翻译功能适配 */
html[data-theme='dark'] .simple-translator .translator-button {
backgroundColor: #2a2a2a !important;
border-color: #444 !important;
}
html[data-theme='dark'] .simple-translator button[style*="backgroundColor: white"] {
backgroundColor: #2a2a2a !important;
color: #e0e0e0 !important;
}
html[data-theme='dark'] .simple-translator button[style*="backgroundColor: white"]:hover {
backgroundColor: #333 !important;
}
html[data-theme='dark'] .simple-translator div[style*="backgroundColor: #f8f9fa"] {
backgroundColor: #1a1a1a !important;
border-color: #444 !important;
color: #e0e0e0 !important;
}
/* 点击外部关闭下拉菜单的遮罩层 */
.translator-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
background: transparent;
}
/* 翻译成功提示样式增强 */
.translation-notification {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
/* 避免与其他固定元素冲突 */
.simple-translator {
pointer-events: none;
}
.simple-translator .translator-button {
pointer-events: all;
}
/* 打印时隐藏翻译功能 */
@media print {
.simple-translator {
display: none !important;
}
}

View File

@ -1,86 +0,0 @@
import React from 'react';
import { useDoc } from '@docusaurus/theme-common/internal';
import DocItemLayout from '@theme-original/DocItem/Layout';
import PDFExport from '../../../components/PDFExport';
import ReadingPosition from '../../../components/ReadingPosition';
import FontSizeController from '../../../components/FontSizeController';
export default function DocItemLayoutWrapper(props) {
const { metadata } = useDoc();
const documentTitle = metadata.title || metadata.id || '文档';
return (
<>
<ReadingPosition />
<FontSizeController />
<div style={{ position: 'relative' }}>
{/* 原始文档布局 */}
<DocItemLayout {...props} />
{/* PDF导出按钮 - 固定在右下角 */}
<div
style={{
position: 'fixed',
bottom: '80px',
right: '20px',
zIndex: 1000,
}}
>
{/* PDF导出按钮 */}
<div
style={{
backgroundColor: 'white',
borderRadius: '8px',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
padding: '4px',
}}
>
<PDFExport documentTitle={documentTitle} />
</div>
</div>
{/* 文档顶部工具栏 */}
<div
style={{
position: 'absolute',
top: '10px',
right: '20px',
zIndex: 100,
display: 'flex',
gap: '8px',
}}
>
{/* 清除阅读记录按钮 */}
<button
onClick={() => {
if (confirm('确定要清除所有阅读记录吗?')) {
localStorage.removeItem('docReadingPositions');
alert('阅读记录已清除');
}
}}
style={{
backgroundColor: '#6c757d',
color: 'white',
border: 'none',
padding: '6px 12px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '12px',
opacity: 0.7,
transition: 'opacity 0.2s ease',
}}
onMouseEnter={(e) => {
e.target.style.opacity = '1';
}}
onMouseLeave={(e) => {
e.target.style.opacity = '0.7';
}}
title="清除所有阅读记录"
>
🗑 清除记录
</button>
</div>
</div>
</>
);
}

746
yarn.lock

File diff suppressed because it is too large Load Diff