forked from Gitlink/gitlink_help_center
This commit is contained in:
parent
e3a61f776a
commit
1a109e2ff9
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"],
|
||||
// ```
|
||||
},
|
||||
],
|
||||
],
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue