mirror of
https://github.com/blossom-editor/blossom
synced 2024-11-17 14:39:21 +08:00
feat: 临时访问增加水印
This commit is contained in:
parent
c5866839fd
commit
64b9a61276
@ -265,7 +265,13 @@ public class ArticleController {
|
|||||||
}
|
}
|
||||||
String reportHtml = ArticleUtil.toHtml(article,
|
String reportHtml = ArticleUtil.toHtml(article,
|
||||||
userService.selectById(AuthContext.getUserId()),
|
userService.selectById(AuthContext.getUserId()),
|
||||||
userParamService.getValue(AuthContext.getUserId(), UserParamEnum.WEB_BLOG_COLOR).getParamValue());
|
userParamService.getValue(AuthContext.getUserId(), UserParamEnum.WEB_BLOG_COLOR).getParamValue(),
|
||||||
|
userParamService.getValue(AuthContext.getUserId(), UserParamEnum.WEB_BLOG_WATERMARK_ENABLED).getParamValue(),
|
||||||
|
userParamService.getValue(AuthContext.getUserId(), UserParamEnum.WEB_BLOG_WATERMARK_CONTENT).getParamValue(),
|
||||||
|
userParamService.getValue(AuthContext.getUserId(), UserParamEnum.WEB_BLOG_WATERMARK_FONTSIZE).getParamValue(),
|
||||||
|
userParamService.getValue(AuthContext.getUserId(), UserParamEnum.WEB_BLOG_WATERMARK_COLOR).getParamValue(),
|
||||||
|
userParamService.getValue(AuthContext.getUserId(), UserParamEnum.WEB_BLOG_WATERMARK_GAP).getParamValue()
|
||||||
|
);
|
||||||
try (InputStream is = new ByteArrayInputStream(reportHtml.getBytes(StandardCharsets.UTF_8));
|
try (InputStream is = new ByteArrayInputStream(reportHtml.getBytes(StandardCharsets.UTF_8));
|
||||||
BufferedInputStream bis = new BufferedInputStream(is)) {
|
BufferedInputStream bis = new BufferedInputStream(is)) {
|
||||||
String filename = URLEncodeUtil.encode(article.getName() + ".html");
|
String filename = URLEncodeUtil.encode(article.getName() + ".html");
|
||||||
@ -339,6 +345,12 @@ public class ArticleController {
|
|||||||
return ArticleUtil.toHtml(
|
return ArticleUtil.toHtml(
|
||||||
article,
|
article,
|
||||||
userService.selectById(visit.getUserId()),
|
userService.selectById(visit.getUserId()),
|
||||||
userParamService.getValue(visit.getUserId(), UserParamEnum.WEB_BLOG_COLOR).getParamValue());
|
userParamService.getValue(visit.getUserId(), UserParamEnum.WEB_BLOG_COLOR).getParamValue(),
|
||||||
|
userParamService.getValue(visit.getUserId(), UserParamEnum.WEB_BLOG_WATERMARK_ENABLED).getParamValue(),
|
||||||
|
userParamService.getValue(visit.getUserId(), UserParamEnum.WEB_BLOG_WATERMARK_CONTENT).getParamValue(),
|
||||||
|
userParamService.getValue(visit.getUserId(), UserParamEnum.WEB_BLOG_WATERMARK_FONTSIZE).getParamValue(),
|
||||||
|
userParamService.getValue(visit.getUserId(), UserParamEnum.WEB_BLOG_WATERMARK_COLOR).getParamValue(),
|
||||||
|
userParamService.getValue(visit.getUserId(), UserParamEnum.WEB_BLOG_WATERMARK_GAP).getParamValue()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
package com.blossom.backend.server.utils;
|
package com.blossom.backend.server.utils;
|
||||||
|
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.RandomUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.blossom.backend.base.user.pojo.UserEntity;
|
import com.blossom.backend.base.user.pojo.UserEntity;
|
||||||
import com.blossom.backend.server.article.draft.pojo.ArticleEntity;
|
import com.blossom.backend.server.article.draft.pojo.ArticleEntity;
|
||||||
import com.blossom.common.base.util.DateUtils;
|
import com.blossom.common.base.enums.YesNo;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文章工具类
|
* 文章工具类
|
||||||
@ -105,7 +103,7 @@ public class ArticleUtil {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private static final String SCRIPT_TAG_BLOG_COLOR = " <script>\n" +
|
private static final String HEAD_SCRIPT_BLOG_COLOR = " <script>\n" +
|
||||||
" window.addEventListener('load', function() {\n" +
|
" window.addEventListener('load', function() {\n" +
|
||||||
" const rgb = '{BLOSSOM_WEB_BLOG_COLOR}'\n" +
|
" const rgb = '{BLOSSOM_WEB_BLOG_COLOR}'\n" +
|
||||||
" if (rgb && !rgb.toLowerCase().startsWith('rgb(')) {\n" +
|
" if (rgb && !rgb.toLowerCase().startsWith('rgb(')) {\n" +
|
||||||
@ -128,8 +126,210 @@ public class ArticleUtil {
|
|||||||
" </script>" +
|
" </script>" +
|
||||||
"</head>";
|
"</head>";
|
||||||
|
|
||||||
|
private static final String HEAD_SCRIPT_WATERMARK = "<script>\n" +
|
||||||
|
" window.addEventListener(\"load\", function () {\n" +
|
||||||
|
" const FontGap = 3\n" +
|
||||||
|
"\n" +
|
||||||
|
" function prepareCanvas(width, height, ratio = 1){\n" +
|
||||||
|
" const canvas = document.createElement('canvas')\n" +
|
||||||
|
" const ctx = canvas.getContext('2d')\n" +
|
||||||
|
" const realWidth = width * ratio\n" +
|
||||||
|
" const realHeight = height * ratio\n" +
|
||||||
|
" canvas.setAttribute('width', `${realWidth}px`)\n" +
|
||||||
|
" canvas.setAttribute('height', `${realHeight}px`)\n" +
|
||||||
|
" ctx.save()\n" +
|
||||||
|
" return [ctx, canvas, realWidth, realHeight]\n" +
|
||||||
|
" }\n" +
|
||||||
|
"\n" +
|
||||||
|
" // Get single clips\n" +
|
||||||
|
" function getClips(content, rotate, ratio, width, height, font, gapX, gapY) {\n" +
|
||||||
|
" // ================= Text =================\n" +
|
||||||
|
" const [ctx, canvas, contentWidth, contentHeight] = prepareCanvas(width, height, ratio)\n" +
|
||||||
|
" const {color, fontSize, fontStyle, fontWeight, fontFamily } = font\n" +
|
||||||
|
" const mergedFontSize = Number(fontSize) * ratio\n" +
|
||||||
|
" ctx.font = `${fontStyle} normal ${fontWeight} ${mergedFontSize}px/${height}px ${fontFamily}`\n" +
|
||||||
|
" ctx.fillStyle = color\n" +
|
||||||
|
" ctx.textAlign = 'center'\n" +
|
||||||
|
" ctx.textBaseline = 'top'\n" +
|
||||||
|
" const contents = Array.isArray(content) ? content : [content]\n" +
|
||||||
|
" contents?.forEach((item, index) => {\n" +
|
||||||
|
" ctx.fillText(\n" +
|
||||||
|
" item ?? 'ccccc',\n" +
|
||||||
|
" contentWidth / 2,\n" +
|
||||||
|
" index * (mergedFontSize + FontGap * ratio)\n" +
|
||||||
|
" )\n" +
|
||||||
|
" })\n" +
|
||||||
|
"\n" +
|
||||||
|
" // ==================== Rotate ====================\n" +
|
||||||
|
" const angle = (Math.PI / 180) * Number(rotate)\n" +
|
||||||
|
" const maxSize = Math.max(width, height)\n" +
|
||||||
|
" const [rCtx, rCanvas, realMaxSize] = prepareCanvas(maxSize, maxSize, ratio)\n" +
|
||||||
|
"\n" +
|
||||||
|
" // Copy from `ctx` and rotate\n" +
|
||||||
|
" rCtx.translate(realMaxSize / 2, realMaxSize / 2)\n" +
|
||||||
|
" rCtx.rotate(angle)\n" +
|
||||||
|
" if (contentWidth > 0 && contentHeight > 0) {\n" +
|
||||||
|
" rCtx.drawImage(canvas, -contentWidth / 2, -contentHeight / 2)\n" +
|
||||||
|
" }\n" +
|
||||||
|
"\n" +
|
||||||
|
" // Get boundary of rotated text\n" +
|
||||||
|
" function getRotatePos(x, y) {\n" +
|
||||||
|
" const targetX = x * Math.cos(angle) - y * Math.sin(angle)\n" +
|
||||||
|
" const targetY = x * Math.sin(angle) + y * Math.cos(angle)\n" +
|
||||||
|
" return [targetX, targetY]\n" +
|
||||||
|
" }\n" +
|
||||||
|
"\n" +
|
||||||
|
" let left = 0\n" +
|
||||||
|
" let right = 0\n" +
|
||||||
|
" let top = 0\n" +
|
||||||
|
" let bottom = 0\n" +
|
||||||
|
"\n" +
|
||||||
|
" const halfWidth = contentWidth / 2\n" +
|
||||||
|
" const halfHeight = contentHeight / 2\n" +
|
||||||
|
" const points = [\n" +
|
||||||
|
" [0 - halfWidth, 0 - halfHeight],\n" +
|
||||||
|
" [0 + halfWidth, 0 - halfHeight],\n" +
|
||||||
|
" [0 + halfWidth, 0 + halfHeight],\n" +
|
||||||
|
" [0 - halfWidth, 0 + halfHeight],\n" +
|
||||||
|
" ]\n" +
|
||||||
|
" points.forEach(([x, y]) => {\n" +
|
||||||
|
" const [targetX, targetY] = getRotatePos(x, y)\n" +
|
||||||
|
" left = Math.min(left, targetX)\n" +
|
||||||
|
" right = Math.max(right, targetX)\n" +
|
||||||
|
" top = Math.min(top, targetY)\n" +
|
||||||
|
" bottom = Math.max(bottom, targetY)\n" +
|
||||||
|
" })\n" +
|
||||||
|
"\n" +
|
||||||
|
" const cutLeft = left + realMaxSize / 2\n" +
|
||||||
|
" const cutTop = top + realMaxSize / 2\n" +
|
||||||
|
" const cutWidth = right - left\n" +
|
||||||
|
" const cutHeight = bottom - top\n" +
|
||||||
|
"\n" +
|
||||||
|
" // ================ Fill Alternate ================\n" +
|
||||||
|
" const realGapX = gapX * ratio\n" +
|
||||||
|
" const realGapY = gapY * ratio\n" +
|
||||||
|
" const filledWidth = (cutWidth + realGapX) * 2\n" +
|
||||||
|
" const filledHeight = cutHeight + realGapY\n" +
|
||||||
|
"\n" +
|
||||||
|
" const [fCtx, fCanvas] = prepareCanvas(filledWidth, filledHeight)\n" +
|
||||||
|
"\n" +
|
||||||
|
" function drawImg(targetX = 0, targetY = 0) {\n" +
|
||||||
|
" fCtx.drawImage(rCanvas, cutLeft, cutTop, cutWidth, cutHeight, targetX, targetY, cutWidth, cutHeight)\n" +
|
||||||
|
" }\n" +
|
||||||
|
" drawImg()\n" +
|
||||||
|
" drawImg(cutWidth + realGapX, -cutHeight / 2 - realGapY / 2)\n" +
|
||||||
|
" drawImg(cutWidth + realGapX, +cutHeight / 2 + realGapY / 2)\n" +
|
||||||
|
" return [fCanvas.toDataURL(), filledWidth / ratio, filledHeight / ratio]\n" +
|
||||||
|
" }\n" +
|
||||||
|
" \n" +
|
||||||
|
" const getMarkSize = (ctx, content) => {\n" +
|
||||||
|
" let defaultWidth = 120\n" +
|
||||||
|
" let defaultHeight = 64\n" +
|
||||||
|
" const width = 120\n" +
|
||||||
|
" const height = 64\n" +
|
||||||
|
" if (ctx.measureText) {\n" +
|
||||||
|
" ctx.font = `${Number('15')}px sans-serif`\n" +
|
||||||
|
" const contents = Array.isArray(content) ? content : [content]\n" +
|
||||||
|
" const sizes = contents.map((item) => {\n" +
|
||||||
|
" const metrics = ctx.measureText(item)\n" +
|
||||||
|
"\n" +
|
||||||
|
" return [metrics.width,\n" +
|
||||||
|
" metrics.fontBoundingBoxAscent !== undefined\n" +
|
||||||
|
" ? metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent\n" +
|
||||||
|
" : metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent,\n" +
|
||||||
|
" ]\n" +
|
||||||
|
" })\n" +
|
||||||
|
" defaultWidth = Math.ceil(Math.max(...sizes.map((size) => size[0])))\n" +
|
||||||
|
" defaultHeight =\n" +
|
||||||
|
" Math.ceil(Math.max(...sizes.map((size) => size[1]))) * contents.length +\n" +
|
||||||
|
" (contents.length - 1) * FontGap\n" +
|
||||||
|
" }\n" +
|
||||||
|
" return [width ?? defaultWidth, height ?? defaultHeight]\n" +
|
||||||
|
" }\n" +
|
||||||
|
" \n" +
|
||||||
|
" function toLowercaseSeparator(key) {\n" +
|
||||||
|
" return key.replace(/([A-Z])/g, '-$1').toLowerCase()\n" +
|
||||||
|
" }\n" +
|
||||||
|
"\n" +
|
||||||
|
" function getStyleStr(style) {\n" +
|
||||||
|
" return Object.keys(style)\n" +
|
||||||
|
" .map(\n" +
|
||||||
|
" (key) =>\n" +
|
||||||
|
" `${toLowercaseSeparator(key)}: ${style[key]};`\n" +
|
||||||
|
" )\n" +
|
||||||
|
" .join(' ')\n" +
|
||||||
|
" }\n" +
|
||||||
|
"\n" +
|
||||||
|
" const getMarkStyle = (rotate) => {\n" +
|
||||||
|
" const markStyle = {zIndex: 9, position: 'absolute', left: 0, top: 0, width: '100%', height: '100%', pointerEvents: 'none', backgroundRepeat: 'repeat' }\n" +
|
||||||
|
"\n" +
|
||||||
|
" /** Calculate the style of the offset */\n" +
|
||||||
|
" let positionLeft = rotate / 2 - rotate / 2\n" +
|
||||||
|
" let positionTop = rotate / 2 - rotate / 2\n" +
|
||||||
|
" if (positionLeft > 0) {\n" +
|
||||||
|
" markStyle.left = `${positionLeft}px`\n" +
|
||||||
|
" markStyle.width = `calc(100% - ${positionLeft}px)`\n" +
|
||||||
|
" positionLeft = 0\n" +
|
||||||
|
" }\n" +
|
||||||
|
" if (positionTop > 0) {\n" +
|
||||||
|
" markStyle.top = `${positionTop}px`\n" +
|
||||||
|
" markStyle.height = `calc(100% - ${positionTop}px)`\n" +
|
||||||
|
" positionTop = 0\n" +
|
||||||
|
" }\n" +
|
||||||
|
" markStyle.backgroundPosition = `${positionLeft}px ${positionTop}px`\n" +
|
||||||
|
" return markStyle\n" +
|
||||||
|
" }\n" +
|
||||||
|
" \n" +
|
||||||
|
" let stopObservation = false\n" +
|
||||||
|
" const observerContainer = document.getElementsByClassName('bl-preview')[0]\n" +
|
||||||
|
" const observerConfig = { attributes: true, childList: true, subtree: true };\n" +
|
||||||
|
" const observer = new MutationObserver(() => {\n" +
|
||||||
|
" renderWatermark()\n" +
|
||||||
|
" });\n" +
|
||||||
|
"\n" +
|
||||||
|
" const renderWatermark = () => {\n" +
|
||||||
|
" const canvas = document.createElement(\"canvas\")\n" +
|
||||||
|
" const ctx = canvas.getContext(\"2d\")\n" +
|
||||||
|
" const rotate = -22\n" +
|
||||||
|
" const content = '{WEB_BLOG_WATERMARK_CONTENT}'\n" +
|
||||||
|
" const color = '{WEB_BLOG_WATERMARK_COLOR}'\n" +
|
||||||
|
" const fontSize = {WEB_BLOG_WATERMARK_FONTSIZE}\n" +
|
||||||
|
" const gap = {WEB_BLOG_WATERMARK_GAP}\n" +
|
||||||
|
"\n" +
|
||||||
|
" if (ctx) {\n" +
|
||||||
|
" const ratio = window.devicePixelRatio || 1\n" +
|
||||||
|
" const [markWidth, markHeight] = getMarkSize(ctx, content)\n" +
|
||||||
|
"\n" +
|
||||||
|
" const drawCanvas = (drawContent) => {\n" +
|
||||||
|
" const [textClips, clipWidth] = getClips(drawContent, rotate, ratio, markWidth, markHeight, \n" +
|
||||||
|
" {color: color, fontSize: fontSize, fontStyle: 'normal', fontWeight: 'normal'}, gap, gap\n" +
|
||||||
|
" )\n" +
|
||||||
|
" let watermark = document.createElement('div')\n" +
|
||||||
|
" let container = document.getElementsByClassName('bl-preview')[0]\n" +
|
||||||
|
" stopObservation = true\n" +
|
||||||
|
" observer.disconnect()\n" +
|
||||||
|
" watermark.setAttribute('style',getStyleStr({\n" +
|
||||||
|
" ...getMarkStyle(rotate),\n" +
|
||||||
|
" backgroundImage: `url('${textClips}')`,\n" +
|
||||||
|
" backgroundSize: `${Math.floor(clipWidth)}px`,\n" +
|
||||||
|
" })\n" +
|
||||||
|
" )\n" +
|
||||||
|
" container.append(watermark)\n" +
|
||||||
|
" setTimeout(() => {\n" +
|
||||||
|
" observer.observe(observerContainer, observerConfig)\n" +
|
||||||
|
" })\n" +
|
||||||
|
" };\n" +
|
||||||
|
" \n" +
|
||||||
|
" drawCanvas(content);\n" +
|
||||||
|
" }\n" +
|
||||||
|
" };\n" +
|
||||||
|
"\n" +
|
||||||
|
" renderWatermark()\n" +
|
||||||
|
" })\n" +
|
||||||
|
" </script>";
|
||||||
|
|
||||||
private static final String prefix = "\n" +
|
|
||||||
|
private static final String BODY_HEADER_AND_TOC = "\n" +
|
||||||
"<body><div class=\"header\">\n" +
|
"<body><div class=\"header\">\n" +
|
||||||
" <div class=\"copyright\">本文作者:{BLOSSOM_EXPORT_HTML_AUTHOR}。著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。</div><a\n" +
|
" <div class=\"copyright\">本文作者:{BLOSSOM_EXPORT_HTML_AUTHOR}。著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。</div><a\n" +
|
||||||
" href=\"https://github.com/blossom-editor/blossom\" target=\"_blank\"><span>Export by Blossom</span><svg\n" +
|
" href=\"https://github.com/blossom-editor/blossom\" target=\"_blank\"><span>Export by Blossom</span><svg\n" +
|
||||||
@ -142,18 +342,18 @@ public class ArticleUtil {
|
|||||||
" <div class=\"toc\" id=\"blossom-toc\">\n" +
|
" <div class=\"toc\" id=\"blossom-toc\">\n" +
|
||||||
" <div style=\"font-size: 15px;color:#727272;padding:10px 0\">《{BLOSSOM_EXPORT_HTML_ARTICLE_NAME}》</div>\n" +
|
" <div style=\"font-size: 15px;color:#727272;padding:10px 0\">《{BLOSSOM_EXPORT_HTML_ARTICLE_NAME}》</div>\n" +
|
||||||
" <div style=\"font-size: 20px;color:#727272;border-bottom:2px solid #eaeaea;padding-bottom: 10px;margin-bottom: 10px;\">目录</div>\n" +
|
" <div style=\"font-size: 20px;color:#727272;border-bottom:2px solid #eaeaea;padding-bottom: 10px;margin-bottom: 10px;\">目录</div>\n" +
|
||||||
" </div><div class=\"main bl-preview\" id=\"blossom-view\">";
|
" </div><div class=\"main\"><div class=\"bl-preview\" id=\"blossom-view\">";
|
||||||
|
|
||||||
private static final String suffix = "</div></div></body></html>";
|
private static final String suffix = "</div></div></div></body></html>";
|
||||||
|
|
||||||
private static String htmlTag;
|
private static String htmlTemplate;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Resource resource = new ClassPathResource("exportTemplate.html");
|
Resource resource = new ClassPathResource("exportTemplate.html");
|
||||||
try (InputStream is = resource.getInputStream()) {
|
try (InputStream is = resource.getInputStream()) {
|
||||||
byte[] bytes = new byte[is.available()];
|
byte[] bytes = new byte[is.available()];
|
||||||
is.read(bytes);
|
is.read(bytes);
|
||||||
htmlTag = new String(bytes);
|
htmlTemplate = new String(bytes);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -162,48 +362,74 @@ public class ArticleUtil {
|
|||||||
/**
|
/**
|
||||||
* 将文章转换为 html 格式
|
* 将文章转换为 html 格式
|
||||||
*
|
*
|
||||||
* @param article 文章
|
* @param article 文章
|
||||||
* @param user 用户, 用户获取作者
|
* @param user 用户, 用户获取作者
|
||||||
* @param blogColor 主颜色
|
* @param blogColor 主颜色
|
||||||
|
* @param WEB_BLOG_WATERMARK_ENABLED 开启水印
|
||||||
|
* @param WEB_BLOG_WATERMARK_CONTENT 水印内容
|
||||||
|
* @param WEB_BLOG_WATERMARK_FONTSIZE 水印字体大小
|
||||||
|
* @param WEB_BLOG_WATERMARK_COLOR 水印颜色
|
||||||
|
* @param WEB_BLOG_WATERMARK_GAP 水印密集度
|
||||||
* @return html 内容
|
* @return html 内容
|
||||||
*/
|
*/
|
||||||
public static String toHtml(ArticleEntity article, UserEntity user, String blogColor) {
|
public static String toHtml(ArticleEntity article,
|
||||||
return htmlTag +
|
UserEntity user,
|
||||||
SCRIPT_TAG_BLOG_COLOR
|
String blogColor,
|
||||||
.replaceAll("\\{BLOSSOM_WEB_BLOG_COLOR}", blogColor) +
|
String WEB_BLOG_WATERMARK_ENABLED,
|
||||||
// 替换作者, 文章名称
|
String WEB_BLOG_WATERMARK_CONTENT,
|
||||||
prefix
|
String WEB_BLOG_WATERMARK_FONTSIZE,
|
||||||
.replaceAll("\\{BLOSSOM_EXPORT_HTML_AUTHOR}", user.getNickName())
|
String WEB_BLOG_WATERMARK_COLOR,
|
||||||
.replaceAll("\\{BLOSSOM_EXPORT_HTML_ARTICLE_NAME}", article.getName()) +
|
String WEB_BLOG_WATERMARK_GAP) {
|
||||||
article.getHtml() + suffix;
|
return htmlTemplate
|
||||||
|
+ appendHeadScript(blogColor,
|
||||||
|
WEB_BLOG_WATERMARK_ENABLED,
|
||||||
|
WEB_BLOG_WATERMARK_CONTENT,
|
||||||
|
WEB_BLOG_WATERMARK_FONTSIZE,
|
||||||
|
WEB_BLOG_WATERMARK_COLOR,
|
||||||
|
WEB_BLOG_WATERMARK_GAP)
|
||||||
|
+ appendBodyHeader(article, user)
|
||||||
|
+ article.getHtml()
|
||||||
|
+ suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
private static void genHeatmap() {
|
* 添加 head 下的动态 script
|
||||||
Date begin = DateUtils.parse("2023-04-01", DateUtils.PATTERN_YYYYMMDD);
|
*
|
||||||
Date end = DateUtils.parse("2023-06-30", DateUtils.PATTERN_YYYYMMDD);
|
* @param blogColor 主题色
|
||||||
for (; DateUtils.compare(begin, end) <= 0; begin = DateUtils.offsetDay(begin, 1)) {
|
* @param WEB_BLOG_WATERMARK_ENABLED 开启水印
|
||||||
String dt = DateUtils.format(begin, DateUtils.PATTERN_YYYYMMDD);
|
* @param WEB_BLOG_WATERMARK_CONTENT 水印内容
|
||||||
int value = RandomUtil.randomInt(0, 10);
|
* @param WEB_BLOG_WATERMARK_FONTSIZE 水印字体大小
|
||||||
System.out.println(String.format("insert into blossom_stat values(null, 1, '%s' ,%s);", dt, value));
|
* @param WEB_BLOG_WATERMARK_COLOR 水印颜色
|
||||||
|
* @param WEB_BLOG_WATERMARK_GAP 水印密集度
|
||||||
|
* @return head 下的所有动态 script
|
||||||
|
*/
|
||||||
|
private static String appendHeadScript(String blogColor,
|
||||||
|
String WEB_BLOG_WATERMARK_ENABLED,
|
||||||
|
String WEB_BLOG_WATERMARK_CONTENT,
|
||||||
|
String WEB_BLOG_WATERMARK_FONTSIZE,
|
||||||
|
String WEB_BLOG_WATERMARK_COLOR,
|
||||||
|
String WEB_BLOG_WATERMARK_GAP) {
|
||||||
|
String script = HEAD_SCRIPT_BLOG_COLOR.replaceAll("\\{BLOSSOM_WEB_BLOG_COLOR}", blogColor);
|
||||||
|
if (YesNo.YES.getValue().toString().equals(WEB_BLOG_WATERMARK_ENABLED)) {
|
||||||
|
script += HEAD_SCRIPT_WATERMARK
|
||||||
|
.replaceAll("\\{WEB_BLOG_WATERMARK_CONTENT}", WEB_BLOG_WATERMARK_CONTENT)
|
||||||
|
.replaceAll("\\{WEB_BLOG_WATERMARK_FONTSIZE}", WEB_BLOG_WATERMARK_FONTSIZE)
|
||||||
|
.replaceAll("\\{WEB_BLOG_WATERMARK_COLOR}", WEB_BLOG_WATERMARK_COLOR)
|
||||||
|
.replaceAll("\\{WEB_BLOG_WATERMARK_GAP}", WEB_BLOG_WATERMARK_GAP);
|
||||||
}
|
}
|
||||||
|
return script + "</head>";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void genWords() {
|
/**
|
||||||
int value = 203012;
|
* 增加页面顶部的文章作者和目录顶部的文章名称
|
||||||
Date begin = DateUtils.parse("2019-01-01", DateUtils.PATTERN_YYYYMMDD);
|
*
|
||||||
Date end = DateUtils.parse("2023-06-30", DateUtils.PATTERN_YYYYMMDD);
|
* @param article 文章
|
||||||
for (; DateUtils.compare(begin, end) <= 0; begin = DateUtils.offsetMonth(begin, 1)) {
|
* @param user 用户, 用户获取作者
|
||||||
String dt = DateUtils.format(begin, DateUtils.PATTERN_YYYYMMDD);
|
* @return 页面顶部的文章作者和文章名称
|
||||||
value = value + RandomUtil.randomInt(0, 10000);
|
*/
|
||||||
System.out.println(String.format("insert into blossom_stat values(null, 2, '%s' ,%s);", dt, value));
|
private static String appendBodyHeader(ArticleEntity article, UserEntity user) {
|
||||||
}
|
return BODY_HEADER_AND_TOC
|
||||||
}
|
.replaceAll("\\{BLOSSOM_EXPORT_HTML_AUTHOR}", user.getNickName())
|
||||||
|
.replaceAll("\\{BLOSSOM_EXPORT_HTML_ARTICLE_NAME}", article.getName());
|
||||||
public static void main(String[] args) {
|
|
||||||
// ArticleEntity a = new ArticleEntity();
|
|
||||||
// a.setName("123123213");
|
|
||||||
// a.setHtml("asdasd");
|
|
||||||
// System.out.println(exportHtml(a));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1358,6 +1358,9 @@
|
|||||||
color: #c5c5c5;
|
color: #c5c5c5;
|
||||||
border-bottom: 1px solid #eaeaea;
|
border-bottom: 1px solid #eaeaea;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 999;
|
||||||
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header .copyright {
|
.header .copyright {
|
||||||
@ -1380,7 +1383,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content .main {
|
.content .main {
|
||||||
@ -1388,7 +1391,8 @@
|
|||||||
width: calc(100% - 300px);
|
width: calc(100% - 300px);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: scroll
|
overflow-y: scroll;
|
||||||
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc {
|
.toc {
|
||||||
@ -1398,6 +1402,7 @@
|
|||||||
border-right: 1px solid #eeeeee;
|
border-right: 1px solid #eeeeee;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc > h1,
|
.toc > h1,
|
||||||
@ -1434,8 +1439,6 @@
|
|||||||
transition: opacity 0.1s;
|
transition: opacity 0.1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.toc > h1:hover,
|
.toc > h1:hover,
|
||||||
.toc > h2:hover,
|
.toc > h2:hover,
|
||||||
.toc > h3:hover,
|
.toc > h3:hover,
|
||||||
@ -1615,6 +1618,7 @@
|
|||||||
background-color: var(--bl-preview-bg-color);
|
background-color: var(--bl-preview-bg-color);
|
||||||
line-height: 23px;
|
line-height: 23px;
|
||||||
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bl-preview * {
|
.bl-preview * {
|
||||||
@ -2034,7 +2038,6 @@
|
|||||||
for (let i = 0; i < heads.length; i++) {
|
for (let i = 0; i < heads.length; i++) {
|
||||||
let head = heads[i];
|
let head = heads[i];
|
||||||
let tocHead = document.createElement(head.localName);
|
let tocHead = document.createElement(head.localName);
|
||||||
console.log(head.id);
|
|
||||||
tocHead.setAttribute('class', `toc-${head.localName}`);
|
tocHead.setAttribute('class', `toc-${head.localName}`);
|
||||||
tocHead.innerText = head.innerText;
|
tocHead.innerText = head.innerText;
|
||||||
tocHead.onclick = function () {
|
tocHead.onclick = function () {
|
||||||
@ -2042,7 +2045,8 @@
|
|||||||
if (ele == null || ele == undefined) {
|
if (ele == null || ele == undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ele.parentNode.scrollTop = ele.offsetTop - 30
|
// ele.parentNode.scrollTop = ele.offsetTop - 30
|
||||||
|
ele.scrollIntoView()
|
||||||
};
|
};
|
||||||
tocContainer.appendChild(tocHead)
|
tocContainer.appendChild(tocHead)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user