mirror of
https://gitee.com/kekingcn/file-online-preview.git
synced 2026-04-08 17:27:34 +00:00
5.0版本 发布 新增pptm 新增heif 美化heif 模板 美化tif 美化md 优化http方法 优化首页目录读取方法
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# kkFileView
|
||||
文档在线预览项目解决方案,项目使用流行的spring boot搭建,易上手和部署。万能的文件预览开源项目,基本支持主流文档格式预览,如:
|
||||
1. 支持 doc, docx, xls, xlsx, xlsm, ppt, pptx, csv, tsv, dotm, xlt, xltm, dot, dotx,xlam, xla ,pages 等 Office 办公文档
|
||||
1. 支持 doc, docx, xls, xlsx, xlsm, ppt, pptx, csv, tsv, dotm, xlt, xltm, dot, dotx,xlam, xla ,pages ,pptm 等 Office 办公文档
|
||||
2. 支持 wps, dps, et, ett, wpt 等国产 WPS Office 办公文档
|
||||
3. 支持 odt, ods, ots, odp, otp, six, ott, fodt, fods 等OpenOffice、LibreOffice 办公文档
|
||||
4. 支持 vsd, vsdx 等 Visio 流程图文件
|
||||
@@ -15,7 +15,7 @@
|
||||
13. 支持 dwg, dxf, dwf, iges , igs, dwt, dng, ifc, dwfx, stl, cf2, plt 等 CAD 模型文件
|
||||
14. 支持 txt, xml(渲染), xbrl(渲染), md(渲染), java, php, py, js, css 等所有纯文本
|
||||
15. 支持 zip, rar, jar, tar, gzip, 7z 等压缩包
|
||||
16. 支持 jpg, jpeg, png, gif, bmp, ico, jfif, webp ,heic等图片预览(翻转,缩放,镜像)
|
||||
16. 支持 jpg, jpeg, png, gif, bmp, ico, jfif, webp ,heic ,heif等图片预览(翻转,缩放,镜像)
|
||||
17. 支持 tif, tiff 图信息模型文件
|
||||
18. 支持 tga 图像格式文件
|
||||
19. 支持 svg 矢量图像格式文件
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Document online preview project solution, built using the popular Spring Boot framework for easy setup and deployment. This versatile open source project provides basic support for a wide range of document formats, including:
|
||||
|
||||
1. Supports Office documents such as `doc`, `docx`, `xls`, `xlsx`, `xlsm`, `ppt`, `pptx`, `csv`, `tsv`, , `dotm`, `xlt`, `xltm`, `dot`, `xlam`, `dotx`, `xla,` ,`pages` etc.
|
||||
1. Supports Office documents such as `doc`, `docx`, `xls`, `xlsx`, `xlsm`, `ppt`, `pptx`, `csv`, `tsv`, , `dotm`, `xlt`, `xltm`, `dot`, `xlam`, `dotx`, `xla,` ,`pages` ,`pptm` etc.
|
||||
2. Supports domestic WPS Office documents such as `wps`, `dps`, `et` , `ett`, ` wpt`.
|
||||
3. Supports OpenOffice, LibreOffice office documents such as `odt`, `ods`, `ots`, `odp`, `otp`, `six`, `ott`, `fodt` and `fods`.
|
||||
4. Supports Visio flowchart files such as `vsd`, `vsdx`.
|
||||
@@ -19,7 +19,7 @@ Document online preview project solution, built using the popular Spring Boot fr
|
||||
13. Supports CAD model files such as `dwg`, `dxf`, `dwf` `iges` ,` igs`, `dwt` , `dng` , `ifc` , `dwfx` , `stl` , `cf2` , `plt`, etc.
|
||||
14. Supports all plain text files such as `txt`, `xml` (rendering), `md` (rendering), `java`, `php`, `py`, `js`, `css`, etc.
|
||||
15. Supports compressed packages such as `zip`, `rar`, `jar`, `tar`, `gzip`, `7z`, etc.
|
||||
16. Supports image previewing (flip, zoom, mirror) of `jpg`, `jpeg`, `png`, `gif`, `bmp`, `ico`, `jfif`, `webp`, `heic`, etc.
|
||||
16. Supports image previewing (flip, zoom, mirror) of `jpg`, `jpeg`, `png`, `gif`, `bmp`, `ico`, `jfif`, `webp`, `heic`, ,`heif` etc.
|
||||
17. Supports image information model files such as `tif` and `tiff`.
|
||||
18. Supports image format files such as `tga`.
|
||||
19. Supports vector image format files such as `svg`.
|
||||
|
||||
@@ -36,8 +36,8 @@ public enum FileType {
|
||||
MSG("msgFilePreviewImpl"),
|
||||
DRAWIO("drawioFilePreviewImpl");
|
||||
|
||||
private static final String[] OFFICE_TYPES = {"docx", "wps", "doc", "docm", "xls", "xlsx", "csv" ,"xlsm", "ppt", "pptx", "vsd", "rtf", "odt", "wmf", "emf", "dps", "et", "ods", "ots", "tsv", "odp", "otp", "sxi", "ott", "vsdx", "fodt", "fods", "xltx","tga","psd","dotm","ett","xlt","xltm","wpt","dot","xlam","dotx","xla","pages", "eps"};
|
||||
private static final String[] PICTURE_TYPES = {"jpg", "jpeg", "png", "gif", "bmp", "ico", "jfif", "webp", "heic", "avif"};
|
||||
private static final String[] OFFICE_TYPES = {"docx", "wps", "doc", "docm", "xls", "xlsx", "csv" ,"xlsm", "ppt", "pptx", "vsd", "rtf", "odt", "wmf", "emf", "dps", "et", "ods", "ots", "tsv", "odp", "otp", "sxi", "ott", "vsdx", "fodt", "fods", "xltx","tga","psd","dotm","ett","xlt","xltm","wpt","dot","xlam","dotx","xla","pages", "eps", "pptm"};
|
||||
private static final String[] PICTURE_TYPES = {"jpg", "jpeg", "png", "gif", "bmp", "ico", "jfif", "webp", "heic", "avif", "heif"};
|
||||
private static final String[] ARCHIVE_TYPES = {"rar", "zip", "jar", "7-zip", "tar", "gzip", "7z"};
|
||||
private static final String[] ONLINE3D_TYPES = {"obj", "3ds", "stl", "ply", "off", "3dm", "fbx", "dae", "wrl", "3mf", "ifc","glb","o3dv","gltf","stp","bim","fcstd","step","iges","brep"};
|
||||
private static final String[] EML_TYPES = {"eml"};
|
||||
|
||||
@@ -45,7 +45,7 @@ public class PictureFilePreviewImpl extends CommonPreviewImpl {
|
||||
}
|
||||
|
||||
}
|
||||
if(suffix.equalsIgnoreCase("heic")){
|
||||
if(suffix.equalsIgnoreCase("heic")||suffix.equalsIgnoreCase("heif")){
|
||||
return HEIC_FILE_PREVIEW_PAGE;
|
||||
}else {
|
||||
return PICTURE_FILE_PREVIEW_PAGE;
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.web.client.RequestCallback;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
@@ -54,6 +55,43 @@ public class HttpRequestUtils {
|
||||
private static final int DEFAULT_MAX_TOTAL = 200; // 最大连接数
|
||||
private static final int DEFAULT_MAX_PER_ROUTE = 50; // 每个路由最大连接数
|
||||
|
||||
/**
|
||||
* 判断是否为客户端中断连接的异常
|
||||
*/
|
||||
public static boolean isClientAbortException(Throwable e) {
|
||||
if (e == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查异常链
|
||||
Throwable cause = e;
|
||||
while (cause != null) {
|
||||
// 检查异常消息
|
||||
if (cause instanceof IOException) {
|
||||
String message = cause.getMessage();
|
||||
if (message != null && (
|
||||
message.contains("你的主机中的软件中止了一个已建立的连接") ||
|
||||
message.contains("Broken pipe") ||
|
||||
message.contains("Connection reset by peer") ||
|
||||
message.contains("ClientAbortException"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查异常类型
|
||||
String className = cause.getClass().getName();
|
||||
if (className.contains("ClientAbortException") ||
|
||||
className.contains("AbortedException") ||
|
||||
className.contains("AsyncRequestNotUsableException")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cause = cause.getCause();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化连接池管理器(懒加载)
|
||||
*/
|
||||
@@ -238,12 +276,31 @@ public class HttpRequestUtils {
|
||||
try {
|
||||
handler.handleResponse(wrapper);
|
||||
} catch (Exception e) {
|
||||
logger.error("处理文件响应时出错", e);
|
||||
throw new RuntimeException(e);
|
||||
// 如果是客户端中断连接,不再记录为错误
|
||||
if (isClientAbortException(e)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("客户端中断连接,可能用户取消了下载,URL: {}", url);
|
||||
}
|
||||
} else {
|
||||
logger.error("处理文件响应时出错", e);
|
||||
}
|
||||
try {
|
||||
throw e;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
// 如果是客户端中断连接,不再记录为错误
|
||||
if (isClientAbortException(e)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("客户端中断连接,URL: {}", url);
|
||||
}
|
||||
throw e; // 重新抛出,让调用者处理
|
||||
}
|
||||
|
||||
// 如果是SSL证书错误,给出建议
|
||||
if (e.getMessage() != null &&
|
||||
(e.getMessage().contains("SSL") ||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -174,7 +174,7 @@ public class OnlinePreviewController {
|
||||
}
|
||||
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(urlPath, req);
|
||||
InputStream inputStream = null;
|
||||
logger.info("读取跨域pdf文件url:{}", urlPath);
|
||||
logger.info("读取跨域文件url:{}", urlPath);
|
||||
if (!isFtpUrl(url)) {
|
||||
CloseableHttpClient httpClient = HttpRequestUtils.createConfiguredHttpClient();
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
3488
server/src/main/resources/static/js/viewer.min.js
vendored
3488
server/src/main/resources/static/js/viewer.min.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<title>CADViewer - 中文界面</title>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<#include "*/commonHeader.ftl">
|
||||
<!-- 核心样式 - 精简版本 -->
|
||||
<link href="cadviewer/app/css/cadviewer-core-styles.css" media="screen" rel="stylesheet" type="text/css" />
|
||||
<link href="cadviewer/app/css/font-awesome.min.css" media="screen" rel="stylesheet" type="text/css" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -145,7 +145,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<ol>
|
||||
<li>支持 doc, docx, xls, xlsx, xlsm, ppt, pptx, csv, tsv, dotm, xlt, xltm, dot, dotx, xlam, xla, pages 等 Office 办公文档</li>
|
||||
<li>支持 doc, docx, xls, xlsx, xlsm, ppt, pptx, csv, tsv, dotm, xlt, xltm, dot, dotx, xlam, xla, pages ,pptm 等 Office 办公文档</li>
|
||||
<li>支持 wps, dps, et, ett, wpt 等国产 WPS Office 办公文档</li>
|
||||
<li>支持 odt, ods, ots, odp, otp, six, ott, fodt, fods 等OpenOffice、LibreOffice 办公文档</li>
|
||||
<li>支持 vsd, vsdx 等 Visio 流程图文件</li>
|
||||
@@ -160,7 +160,7 @@
|
||||
<li>支持 dwg, dxf, dwf, iges , igs, dwt, dng, ifc, dwfx, stl, cf2, plt 等 CAD 模型文件</li>
|
||||
<li>支持 txt, xml(渲染), md(渲染), java, php, py, js, css 等所有纯文本</li>
|
||||
<li>支持 zip, rar, jar, tar, gzip, 7z 等压缩包</li>
|
||||
<li>支持 jpg, jpeg, png, gif, bmp, ico, jfif, webp, heic 等图片预览(翻转,缩放,镜像)</li>
|
||||
<li>支持 jpg, jpeg, png, gif, bmp, ico, jfif, webp, heic ,heif 等图片预览(翻转,缩放,镜像)</li>
|
||||
<li>支持 tif, tiff 图信息模型文件(翻转,缩放)</li>
|
||||
<li>支持 tga 图像格式文件</li>
|
||||
<li>支持 svg 矢量图像格式文件 (翻转,缩放)</li>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
8. 优化 marked 解析 <br>
|
||||
<h4>新增</h4>
|
||||
1. 新增 msg 邮件 解析 <br>
|
||||
2. 新增 heic 图片 解析 <br>
|
||||
2. 新增 heic、 heif 图片 解析 <br>
|
||||
3. 新增 跨域方法 <br>
|
||||
4. 新增 高亮方法 <br>
|
||||
5. 新增 页码方法 <br>
|
||||
@@ -61,6 +61,7 @@
|
||||
10. 新增 异步等待 <br>
|
||||
11. 新增 上传限制不支持的文件禁止上传 <br>
|
||||
12. 新增 cadviewer转换方法<br>
|
||||
13. 新增 pptm<br>
|
||||
<h4>修复</h4>
|
||||
1. 压缩包路径问题 <br>
|
||||
2. 安全问题 <br>
|
||||
|
||||
@@ -12,42 +12,40 @@
|
||||
<script src="js/marked.min.js" type="text/javascript"></script>
|
||||
<script src="js/base64.min.js" type="text/javascript"></script>
|
||||
<script src="js/codemirror.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="js/codemirror.css"/>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<input hidden id="textData" value="${textData}"/>
|
||||
|
||||
<!-- 目录区域 - 左侧 -->
|
||||
<div id="directory">
|
||||
<div>文档目录</div>
|
||||
<div id="content">
|
||||
<ul></ul>
|
||||
<div class="empty-toc" style="display:none;">暂无目录</div>
|
||||
<div class="main-container">
|
||||
<!-- 目录区域 - 左侧 -->
|
||||
<div id="directory">
|
||||
<div>文档目录</div>
|
||||
<div id="content">
|
||||
<ul></ul>
|
||||
<div class="empty-toc" style="display:none;">暂无目录</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<div class="container">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h6 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
|
||||
${file.name}
|
||||
</a>
|
||||
</h6>
|
||||
</div>
|
||||
|
||||
<!-- 视图切换按钮 -->
|
||||
<div class="view-toggle">
|
||||
<button id="preview_btn" class="view-btn active">预览模式</button>
|
||||
<button id="source_btn" class="view-btn">源代码</button>
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div id="markdown"></div>
|
||||
<!-- 主内容区域 -->
|
||||
<div class="content-container">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h6 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
|
||||
${file.name}
|
||||
</a>
|
||||
</h6>
|
||||
</div>
|
||||
|
||||
<!-- 视图切换按钮 -->
|
||||
<div class="view-toggle">
|
||||
<button id="preview_btn" class="view-btn active">预览模式</button>
|
||||
<button id="source_btn" class="view-btn">源代码</button>
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div id="markdown"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,6 +62,8 @@
|
||||
theme: "default",
|
||||
viewportMargin: Infinity
|
||||
});
|
||||
|
||||
var kkkeyword = '${highlightall}';
|
||||
|
||||
// 初始化目录
|
||||
function initTOC() {
|
||||
@@ -75,8 +75,11 @@
|
||||
let id = "heading-" + index++;
|
||||
$(this).attr('id', id);
|
||||
let level = this.tagName.toLowerCase().replace('h', '');
|
||||
let text = $(this).text().substring(0, 50);
|
||||
html += '<li class="li-h' + level + '"><a href="#' + id + '">' + text + '</a></li>';
|
||||
let text = $(this).text();
|
||||
if (text.length > 50) {
|
||||
text = text.substring(0, 50) + "...";
|
||||
}
|
||||
html += '<li class="li-h' + level + '"><a href="#' + id + '" title="' + $(this).text() + '">' + text + '</a></li>';
|
||||
});
|
||||
|
||||
$("#directory ul").html(html);
|
||||
@@ -90,27 +93,22 @@
|
||||
$("#directory ul").show();
|
||||
}
|
||||
|
||||
// 更新目录高度
|
||||
updateTOCHeight();
|
||||
}
|
||||
|
||||
// 更新目录高度
|
||||
function updateTOCHeight() {
|
||||
const windowHeight = window.innerHeight;
|
||||
const tocTop = document.getElementById('directory').getBoundingClientRect().top;
|
||||
const availableHeight = windowHeight - tocTop - 20;
|
||||
document.getElementById('directory').style.maxHeight = availableHeight + 'px';
|
||||
// 确保目录滚动条可见
|
||||
setTimeout(function() {
|
||||
const contentDiv = document.getElementById('content');
|
||||
if (contentDiv.scrollHeight > contentDiv.clientHeight) {
|
||||
contentDiv.style.paddingRight = '8px';
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// 安全HTML转义函数
|
||||
function htmlEscape(str) {
|
||||
if (!str) return "";
|
||||
return str
|
||||
.replace(/&/g, "&")
|
||||
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'")
|
||||
|
||||
.replace(/javascript/gi, "javascript ");
|
||||
}
|
||||
|
||||
@@ -120,20 +118,44 @@
|
||||
var textData = Base64.decode($("#textData").val());
|
||||
textData = htmlEscape(textData);
|
||||
|
||||
window.textPreData = "<pre style='background-color: #f8f9fa;border:1px solid #e9ecef;border-radius:8px;padding:18px;overflow-x:auto;'>" + textData + "</pre>";
|
||||
window.textMarkdownData = marked.parse(textData);
|
||||
|
||||
// 应用关键字高亮
|
||||
const highlightedText = highlightKeyword(textData, kkkeyword);
|
||||
window.textPreData = "<div style='width:100%;'><pre>" + highlightedText + "</pre></div>";
|
||||
window.textMarkdownData = marked.parse(highlightedText);
|
||||
|
||||
$("#markdown").html(window.textMarkdownData);
|
||||
editor.setValue(textData);
|
||||
|
||||
// 更新编辑器容器宽度
|
||||
setTimeout(function() {
|
||||
editor.refresh();
|
||||
// 设置编辑器高度
|
||||
const panelBody = $('.panel-body');
|
||||
const editorHeight = panelBody.height() - 40; // 减去一些padding
|
||||
editor.setSize('100%', editorHeight);
|
||||
}, 100);
|
||||
|
||||
// 初始化目录
|
||||
initTOC();
|
||||
|
||||
} catch (e) {
|
||||
console.error("加载内容失败:", e);
|
||||
$("#markdown").html("<div class='alert alert-danger' style='padding:15px;border-radius:8px;'>加载内容失败: " + e.message + "</div>");
|
||||
$("#markdown").html("<div class='alert alert-danger' style='padding:15px;border-radius:8px;width:100%;'>加载内容失败: " + e.message + "</div>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function highlightKeyword(text, keyword) {
|
||||
if (!keyword || keyword.trim() === '') return text;
|
||||
|
||||
const escapedKeyword = keyword.replace(/[.*+?^${'$'}{}()|[\]\\]/g, '\\$&');
|
||||
const regex = new RegExp('(' + escapedKeyword + ')', 'gi');
|
||||
|
||||
return text.replace(regex, '<span class="highlight-keyword">$1</span>');
|
||||
}
|
||||
|
||||
// 切换视图
|
||||
function switchView(mode) {
|
||||
@@ -148,8 +170,21 @@
|
||||
$("#markdown").html(window.textPreData);
|
||||
$("#directory .empty-toc").show();
|
||||
$("#directory ul").hide();
|
||||
|
||||
// 源代码模式下确保pre元素样式正确
|
||||
$("#markdown pre").css({
|
||||
'max-width': '100%',
|
||||
'overflow-x': 'auto',
|
||||
'background-color': '#f8f9fa',
|
||||
'border': '1px solid #e9ecef',
|
||||
'border-radius': '8px',
|
||||
'padding': '18px',
|
||||
'word-wrap': 'break-word',
|
||||
'white-space': 'pre-wrap'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 页面加载完成
|
||||
$(document).ready(function() {
|
||||
@@ -162,18 +197,20 @@
|
||||
loadMarkdown();
|
||||
|
||||
// 监听编辑器变化
|
||||
editor.on('change', function(cm) {
|
||||
// 更新预览
|
||||
var content = cm.getValue();
|
||||
window.textPreData = "<pre style='background-color: #f8f9fa;border:1px solid #e9ecef;border-radius:8px;padding:18px;overflow-x:auto;'>" + content + "</pre>";
|
||||
window.textMarkdownData = marked.parse(content);
|
||||
|
||||
// 如果当前是预览模式,更新预览内容
|
||||
if ($("#preview_btn").hasClass('active')) {
|
||||
$("#markdown").html(window.textMarkdownData);
|
||||
initTOC();
|
||||
}
|
||||
});
|
||||
editor.on('change', function(cm) {
|
||||
// 更新预览
|
||||
var content = cm.getValue();
|
||||
// 应用关键字高亮
|
||||
const highlightedContent = highlightKeyword(content, kkkeyword);
|
||||
window.textPreData = "<div style='width:100%;'><pre>" + highlightedContent + "</pre></div>";
|
||||
window.textMarkdownData = marked.parse(highlightedContent);
|
||||
|
||||
// 如果当前是预览模式,更新预览内容
|
||||
if ($("#preview_btn").hasClass('active')) {
|
||||
$("#markdown").html(window.textMarkdownData);
|
||||
initTOC();
|
||||
}
|
||||
});
|
||||
|
||||
// 绑定视图切换事件
|
||||
$("#preview_btn").click(function() {
|
||||
@@ -189,20 +226,28 @@
|
||||
e.preventDefault();
|
||||
var target = $(this.getAttribute('href'));
|
||||
if (target.length) {
|
||||
$('html, body').animate({
|
||||
scrollTop: target.offset().top - 100
|
||||
// 滚动主内容区域
|
||||
$('.panel-body').animate({
|
||||
scrollTop: target.offset().top + $('.panel-body').scrollTop() - 20
|
||||
}, 500);
|
||||
|
||||
// 高亮当前目录项
|
||||
$('#directory li a').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
// 窗口调整大小时更新目录高度
|
||||
// 窗口调整大小时重新计算高度
|
||||
$(window).resize(function() {
|
||||
updateTOCHeight();
|
||||
// 更新编辑器大小
|
||||
if (editor) {
|
||||
const panelBody = $('.panel-body');
|
||||
const editorHeight = panelBody.height() - 40;
|
||||
editor.setSize('100%', editorHeight);
|
||||
editor.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化目录高度
|
||||
setTimeout(updateTOCHeight, 100);
|
||||
|
||||
// 添加键盘快捷键
|
||||
$(document).keydown(function(e) {
|
||||
// Ctrl+1 切换到预览模式
|
||||
@@ -219,9 +264,36 @@
|
||||
|
||||
// 添加目录项悬停效果
|
||||
$(document).on('mouseenter', '#directory li a', function() {
|
||||
$(this).parent().addClass('hover');
|
||||
$(this).css('background-color', '#e9ecef');
|
||||
}).on('mouseleave', '#directory li a', function() {
|
||||
$(this).parent().removeClass('hover');
|
||||
if (!$(this).hasClass('active')) {
|
||||
$(this).css('background-color', 'transparent');
|
||||
}
|
||||
});
|
||||
|
||||
// 监听主内容区域滚动,更新目录高亮
|
||||
$('.panel-body').scroll(function() {
|
||||
if ($("#preview_btn").hasClass('active')) {
|
||||
var scrollTop = $('.panel-body').scrollTop();
|
||||
var found = false;
|
||||
|
||||
$('#markdown h1, #markdown h2, #markdown h3, #markdown h4, #markdown h5').each(function() {
|
||||
var elementTop = $(this).offset().top;
|
||||
var elementBottom = elementTop + $(this).outerHeight();
|
||||
|
||||
if (elementTop <= scrollTop + 50 && elementBottom > scrollTop + 50) {
|
||||
var id = $(this).attr('id');
|
||||
$('#directory li a').removeClass('active');
|
||||
$('#directory li a[href="#' + id + '"]').addClass('active');
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (!found) {
|
||||
$('#directory li a').removeClass('active');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -51,19 +51,21 @@
|
||||
try{
|
||||
imageData = ctx.createImageData(canvas.width, canvas.height);
|
||||
} catch(e){
|
||||
if (e.message.indexOf("CanvasRenderingContext2D"))
|
||||
// 修改异常处理部分,让旋转按钮与正常解析部分保持一致
|
||||
if (e.message.indexOf("CanvasRenderingContext2D"))
|
||||
{
|
||||
var html = "";
|
||||
html += "<div class=\"img-area\">";
|
||||
html += "<div class=\"image-container\">";
|
||||
html += '<img class="my-photo" id="page1" src="'+url+'">';
|
||||
html += "<div class=\"button-container\">";
|
||||
html += "<button onclick=\"rotateImg('page1', false)\">旋转</button>";
|
||||
html += "</div>";
|
||||
html += "</div>";
|
||||
html += "</div>";
|
||||
myp.innerHTML = html;
|
||||
return;
|
||||
var html = "";
|
||||
html += "<div class=\"img-area\">";
|
||||
html += "<div class=\"image-container\" style=\"position:relative;\">";
|
||||
html += '<img class="my-photo" id="page1" src="'+url+'">';
|
||||
html += "<div class=\"button-container\" style=\"position:absolute; bottom:5px; right:5px; opacity:0.1; transition:opacity 0.2s;\" onmouseover=\"this.style.opacity='0.9'\" onmouseout=\"this.style.opacity='0.1'\">";
|
||||
html += "<button class=\"nszImg\" style=\"margin-right:3px; font-size:11px; padding:2px 6px; background:rgba(255,255,255,0.9); border:1px solid #999; border-radius:2px; min-width:50px;\">1/1页</button>";
|
||||
html += "<button class=\"sszImg\" onclick=\"rotateImg('page1', true)\" style=\"font-size:11px; padding:2px 6px; background:rgba(255,255,255,0.9); border:1px solid #999; border-radius:2px;\">↻</button>";
|
||||
html += "</div>";
|
||||
html += "</div>";
|
||||
html += "</div>";
|
||||
myp.innerHTML = html;
|
||||
return;
|
||||
}
|
||||
console.log("错误:" + e);
|
||||
var html = "";
|
||||
|
||||
Reference in New Issue
Block a user