refactor(editor): 优化富文本编辑器内容格式转换逻辑
- 改进 HTML 到 Markdown 的转换规则,处理块级元素和行内元素的嵌套关系 - 优化图片和视频标签的解析逻辑,确保附件正确提取 - 修复换行符处理问题,避免产生多余空行 - 增强居中文本和标题的格式转换准确性 - 清理调试日志和冗余代码
This commit is contained in:
@@ -160,14 +160,21 @@
|
||||
line-height: 26px;
|
||||
margin-bottom: 66px;
|
||||
}
|
||||
#details .matter .matter-left .html * {
|
||||
background: transparent !important;
|
||||
color: #555555 !important;
|
||||
}
|
||||
#details .matter .matter-left .html a {
|
||||
text-decoration: underline;
|
||||
color: #04b0d5;
|
||||
color: #04b0d5 !important;
|
||||
}
|
||||
#details .matter .matter-left .html a * {
|
||||
color: #04b0d5 !important;
|
||||
}
|
||||
#details .matter .matter-left .html .blue {
|
||||
font-size: 15px;
|
||||
line-height: 26px;
|
||||
color: #026277;
|
||||
color: #026277 !important;
|
||||
margin: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -176,19 +183,17 @@
|
||||
display: inline-block;
|
||||
}
|
||||
#details .matter .matter-left .html video {
|
||||
margin: 0 auto;
|
||||
margin: 0 auto 5px;
|
||||
height: 300px;
|
||||
display: block;
|
||||
}
|
||||
#details .matter .matter-left .html h1 {
|
||||
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
|
||||
font-weight: 650;
|
||||
color: #000000;
|
||||
color: #000000 !important;
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
}
|
||||
#details .matter .matter-left .html tr,
|
||||
#details .matter .matter-left .html td {
|
||||
background: transparent;
|
||||
}
|
||||
#details .matter .matter-left .last-time {
|
||||
color: #aaaaaa;
|
||||
font-size: 13px;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
.content {
|
||||
flex-direction: column;
|
||||
|
||||
.name {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
@@ -55,6 +56,7 @@
|
||||
|
||||
.operate {
|
||||
position: relative;
|
||||
|
||||
.view {
|
||||
.icon {
|
||||
width: 13px;
|
||||
@@ -185,15 +187,25 @@
|
||||
line-height: 26px;
|
||||
margin-bottom: 66px;
|
||||
|
||||
* {
|
||||
background: transparent !important;
|
||||
color: #555555 !important;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: #04b0d5;
|
||||
color: #04b0d5 !important;
|
||||
|
||||
* {
|
||||
color: #04b0d5 !important;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.blue {
|
||||
font-size: 15px;
|
||||
line-height: 26px;
|
||||
color: #026277;
|
||||
color: #026277 !important;
|
||||
margin: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -204,21 +216,20 @@
|
||||
}
|
||||
|
||||
video {
|
||||
margin: 0 auto;
|
||||
margin: 0 auto 5px;
|
||||
height: 300px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
|
||||
font-weight: 650;
|
||||
color: #000000;
|
||||
color: #000000 !important;
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
tr,
|
||||
td {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.last-time {
|
||||
@@ -244,6 +255,7 @@
|
||||
height: 20px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
cursor: pointer;
|
||||
@@ -254,6 +266,7 @@
|
||||
|
||||
&.share {
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
.share-box {
|
||||
display: flex;
|
||||
@@ -353,6 +366,7 @@
|
||||
padding-top: 20px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
|
||||
.text {
|
||||
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
|
||||
font-weight: 650;
|
||||
@@ -494,6 +508,7 @@
|
||||
.author-info {
|
||||
color: #7f7f7f;
|
||||
font-size: 13px;
|
||||
|
||||
.amount {
|
||||
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
|
||||
font-weight: 650;
|
||||
@@ -508,6 +523,7 @@
|
||||
width: calc(100% - 16px);
|
||||
padding-bottom: 22px;
|
||||
margin-left: 16px;
|
||||
|
||||
.medal-title {
|
||||
font-size: 14px;
|
||||
color: #7f7f7f;
|
||||
@@ -551,6 +567,7 @@
|
||||
&:hover {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
@@ -608,6 +625,7 @@
|
||||
font-weight: 400;
|
||||
color: #7f7f7f;
|
||||
font-size: 14px;
|
||||
|
||||
.sum {
|
||||
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
|
||||
font-weight: 650;
|
||||
@@ -682,6 +700,7 @@
|
||||
.coins-total {
|
||||
color: #7f7f7f;
|
||||
font-size: 14px;
|
||||
|
||||
.sum {
|
||||
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
|
||||
font-weight: 650;
|
||||
@@ -713,6 +732,7 @@
|
||||
.user {
|
||||
color: #555555;
|
||||
font-size: 13px;
|
||||
|
||||
.avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
@@ -724,6 +744,7 @@
|
||||
.amount {
|
||||
color: #000000;
|
||||
font-size: 16px;
|
||||
|
||||
.text {
|
||||
font-size: 13px;
|
||||
margin-left: 2px;
|
||||
@@ -883,8 +904,7 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.answer-discuss .input-box .bottom .operate .item {
|
||||
}
|
||||
.answer-discuss .input-box .bottom .operate .item {}
|
||||
|
||||
.answer-discuss .input-box .bottom .operate .item .emoji-box {
|
||||
width: 582px;
|
||||
@@ -1421,29 +1441,35 @@
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 12;
|
||||
}
|
||||
|
||||
.answer-discuss .edit-comment .box {
|
||||
width: 650px;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.answer-discuss .edit-comment .box .text {
|
||||
font-size: 18px;
|
||||
font-weight: 650;
|
||||
margin-bottom: 15px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.answer-discuss .edit-comment .box .input-box {
|
||||
margin-right: 0;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.answer-discuss .edit-comment .box .input-box .bottom {
|
||||
// border-top: 1px solid #ebebeb;
|
||||
}
|
||||
|
||||
.answer-discuss .edit-comment .box .btn-list {
|
||||
padding: 15px 0;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.answer-discuss .edit-comment .box .btn-list .btn {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
@@ -1457,6 +1483,7 @@
|
||||
border: 1px solid #ebebeb;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.answer-discuss .edit-comment .box .btn-list .btn.send {
|
||||
background-color: #fddf6d;
|
||||
border: 1px solid #fddf6d;
|
||||
@@ -1469,6 +1496,7 @@
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.answer-discuss .emoji-box-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@@ -1483,6 +1511,7 @@
|
||||
@media screen and (max-width: 850px) {
|
||||
#details {
|
||||
padding: 10px 10px 0;
|
||||
|
||||
.head-top {
|
||||
display: none;
|
||||
}
|
||||
@@ -1494,6 +1523,7 @@
|
||||
.matter {
|
||||
.matter-left {
|
||||
margin-right: 0 !important;
|
||||
|
||||
.action-bar {
|
||||
margin-right: 0 !important;
|
||||
justify-content: space-around;
|
||||
@@ -1508,10 +1538,13 @@
|
||||
.related-head {
|
||||
padding-left: 14px;
|
||||
}
|
||||
|
||||
.list {
|
||||
padding: 14px;
|
||||
|
||||
.item {
|
||||
width: 100% !important;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
@@ -1523,10 +1556,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-box {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.answer-discuss {
|
||||
padding: 15px;
|
||||
|
||||
@@ -1594,6 +1629,7 @@
|
||||
.comments-item {
|
||||
.comments-header {
|
||||
font-size: 12px;
|
||||
|
||||
.comments-title {
|
||||
height: 14px !important;
|
||||
}
|
||||
@@ -1610,6 +1646,7 @@
|
||||
.comments-box {
|
||||
.comments-item {
|
||||
.comments-header {
|
||||
|
||||
.comment-icon,
|
||||
.like-box {
|
||||
margin-left: 15px;
|
||||
|
||||
@@ -398,12 +398,16 @@
|
||||
}
|
||||
#appIndex .header-content-box .header-content-right .offer-box {
|
||||
width: 240px;
|
||||
height: 214px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e9eef2;
|
||||
border-radius: 10px;
|
||||
padding: 17px 10px;
|
||||
overflow: hidden;
|
||||
height: 64px;
|
||||
padding: 15px 10px;
|
||||
}
|
||||
#appIndex .header-content-box .header-content-right .offer-box.big {
|
||||
height: 214px;
|
||||
padding: 17px 10px;
|
||||
}
|
||||
#appIndex .header-content-box .header-content-right .offer-box.small {
|
||||
height: 64px;
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
&:not(:last-child) {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 468px;
|
||||
height: 60px;
|
||||
@@ -37,6 +39,7 @@
|
||||
border-radius: 20px 20px 20px 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.icon {
|
||||
width: 15px;
|
||||
height: 12px;
|
||||
@@ -91,6 +94,7 @@
|
||||
.people {
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
@@ -119,6 +123,7 @@
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 50%;
|
||||
|
||||
.img {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
@@ -128,12 +133,15 @@
|
||||
&:nth-child(4) {
|
||||
margin-right: -9px;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
margin-right: -9px;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
margin-right: -7px;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
margin-right: -5px;
|
||||
}
|
||||
@@ -145,6 +153,7 @@
|
||||
.topic-list {
|
||||
.item {
|
||||
cursor: pointer;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
@@ -222,8 +231,10 @@
|
||||
border-radius: 10px;
|
||||
padding-left: 12px;
|
||||
margin-top: 12px;
|
||||
|
||||
.adv {
|
||||
margin-right: 26px;
|
||||
|
||||
.adv-icon {
|
||||
width: 295px;
|
||||
height: 118px;
|
||||
@@ -279,6 +290,7 @@
|
||||
|
||||
.header-content-right {
|
||||
width: 240px;
|
||||
|
||||
.post-entrance {
|
||||
background-color: #fff;
|
||||
border: 1px solid #e9eef2;
|
||||
@@ -357,9 +369,11 @@
|
||||
height: 140px;
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 240px;
|
||||
height: 140px;
|
||||
@@ -394,12 +408,15 @@
|
||||
&:nth-child(1) {
|
||||
border-top-left-radius: 8px;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
border-top-right-radius: 8px;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
@@ -429,6 +446,7 @@
|
||||
line-height: 22px;
|
||||
|
||||
position: relative;
|
||||
|
||||
.title {
|
||||
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
|
||||
font-weight: 650;
|
||||
@@ -455,6 +473,7 @@
|
||||
padding: 20px;
|
||||
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.168627450980392);
|
||||
flex-direction: column;
|
||||
|
||||
.QRcode {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
@@ -472,13 +491,21 @@
|
||||
|
||||
.offer-box {
|
||||
width: 240px;
|
||||
height: 214px;
|
||||
// height: 214px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e9eef2;
|
||||
border-radius: 10px;
|
||||
padding: 17px 10px;
|
||||
// padding: 17px 10px;
|
||||
overflow: hidden;
|
||||
|
||||
height: 64px;
|
||||
padding: 15px 10px;
|
||||
|
||||
&.big {
|
||||
height: 214px;
|
||||
padding: 17px 10px;
|
||||
}
|
||||
|
||||
&.small {
|
||||
height: 64px;
|
||||
padding: 15px 10px;
|
||||
@@ -521,6 +548,7 @@
|
||||
|
||||
.admission {
|
||||
margin-bottom: 30px;
|
||||
|
||||
.admission-header {
|
||||
margin-bottom: 24px;
|
||||
display: inline-flex;
|
||||
@@ -543,6 +571,7 @@
|
||||
|
||||
.admission-list {
|
||||
flex-wrap: wrap;
|
||||
|
||||
.admission-item {
|
||||
width: 291px;
|
||||
height: 103px;
|
||||
@@ -602,6 +631,7 @@
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.item {
|
||||
font-family: "PingFangSC-Regular", "PingFang SC", sans-serif;
|
||||
font-weight: 400;
|
||||
@@ -622,6 +652,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-box {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
@@ -641,6 +672,7 @@
|
||||
height: 220px;
|
||||
margin-bottom: 12px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 291px;
|
||||
height: 220px;
|
||||
|
||||
124
js/edit.js
124
js/edit.js
@@ -53,8 +53,6 @@ const editApp = createApp({
|
||||
|
||||
cUpload();
|
||||
|
||||
// console.log(valueA.value);
|
||||
|
||||
valueUrl = valueA.value.innerText;
|
||||
|
||||
if (location.hostname == "127.0.0.1") {
|
||||
@@ -67,6 +65,7 @@ const editApp = createApp({
|
||||
|
||||
isLogin.value = true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
let imageLength = 10;
|
||||
@@ -167,19 +166,13 @@ const editApp = createApp({
|
||||
|
||||
const infoTarget = data.info || {};
|
||||
|
||||
// if (location.hostname == "127.0.0.1")
|
||||
// infoTarget.content = `<p><img src="https://oss.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_polfHeD1NFX9ddrB_WbUGy8P79gQxccQOeR45kV7NkzNDQyOQ~~?aid=1009985" alt="图片描述" data-href="https://i-operation.csdnimg.cn/ad/ad_pic/a0beaaca1e2047e0ae5c0783e02b3c0a.png" style=""/></p><div data-w-e-type="video" data-w-e-is-void>
|
||||
// <video poster="https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_polcniH1NFX9ddrB_WbUGy8P79gQxcSQbvLtMsV7NkzNDQyOQ~~?aid=1009771" controls="true" width="auto" height="auto"><source src="https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_polcniG1NFX9ddrB_WbUGy8P79gQxcSFbqQ78MV7NkzNDQyOQ~~?aid=1009770" type="video/mp4"/></video>
|
||||
// </div><p><br></p>`;
|
||||
|
||||
console.log("content", infoTarget.content);
|
||||
// console.log("content", infoTarget.content);
|
||||
if (infoTarget.content) infoTarget.content = restoreHtml(infoTarget.content, infoTarget.attachments);
|
||||
console.log("content", infoTarget.content);
|
||||
// console.log("content", infoTarget.content);
|
||||
|
||||
info.value = infoTarget;
|
||||
token.value = data.token;
|
||||
|
||||
console.log("data", data);
|
||||
|
||||
initEditor();
|
||||
})
|
||||
@@ -216,18 +209,9 @@ const editApp = createApp({
|
||||
|
||||
["insertImage"]: {
|
||||
onInsertedImage(imageNode) {
|
||||
console.log("insertImage");
|
||||
// if (imageNode == null) return;
|
||||
|
||||
const { src, alt, url, href } = imageNode;
|
||||
|
||||
console.log("src", src);
|
||||
console.log("alt", alt);
|
||||
console.log("url", url);
|
||||
console.log("href", href);
|
||||
},
|
||||
async parseImageSrc(src) {
|
||||
console.log("parseImageSrc", src);
|
||||
// 如果图片链接中已经包含了 ?aid= ,则说明是本站图片,直接返回,无需处理
|
||||
if (src.includes("?aid=")) return src;
|
||||
|
||||
@@ -323,6 +307,7 @@ const editApp = createApp({
|
||||
editor = createEditor({
|
||||
selector: "#editor-container",
|
||||
html: infoTarget.content,
|
||||
// html: "",
|
||||
config: editorConfig,
|
||||
mode: "default",
|
||||
});
|
||||
@@ -409,10 +394,10 @@ const editApp = createApp({
|
||||
let html = formattedText || "";
|
||||
|
||||
// 0.5 [p] -> <p>
|
||||
html = html.replace(/\[p\]([\s\S]*?)\[\/p\]/gi, "<p>$1</p>");
|
||||
// html = html.replace(/\[p\]([\s\S]*?)\[\/p\]/gi, "<p>$1</p>");
|
||||
|
||||
// 0. 基础清理:换行符转 <br>,div 转 p (保持原有逻辑)
|
||||
html = html.replace(/\n/g, "<br>");
|
||||
html = html.replace(/(\r\n|\n|\r)/g, "<br>");
|
||||
html = html.replace(/<div(\s|>)/gi, "<p$1");
|
||||
html = html.replace(/<\/div>/g, "</p>");
|
||||
html = html.replace(/<br><div>/g, "<div>");
|
||||
@@ -432,6 +417,10 @@ const editApp = createApp({
|
||||
// 普通居中
|
||||
html = html.replace(/\[align=center\]([\s\S]*?)\[\/align\]/gi, '<p style="text-align: center;">$1</p>');
|
||||
|
||||
// 移除 align 后紧跟的换行符(因为 align 转换为了块级元素 p/h1,其后的换行符通常是多余的)
|
||||
html = html.replace(/(<p style="text-align: center;">.*?<\/p>)\s*<br>/gi, "$1");
|
||||
html = html.replace(/(<h1 style="text-align: center;">.*?<\/h1>)\s*<br>/gi, "$1");
|
||||
|
||||
// 定义图片处理函数
|
||||
const processImg = (aid, width, height, href) => {
|
||||
const image = imageList.find((img) => String(img.aid) === String(aid));
|
||||
@@ -519,29 +508,62 @@ const editApp = createApp({
|
||||
|
||||
// 确保所有顶层元素都是块级元素 (Slate/WangEditor 要求)
|
||||
const blockTags = ['P', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'BLOCKQUOTE', 'UL', 'OL', 'PRE', 'TABLE', 'FIGURE', 'HR'];
|
||||
const children = Array.from(__c.childNodes);
|
||||
let wrapper = null;
|
||||
const newContainer = document.createElement('div');
|
||||
let currentInlineNodes = [];
|
||||
let lastWasBlock = false;
|
||||
|
||||
children.forEach((node) => {
|
||||
const flushInlines = () => {
|
||||
if (currentInlineNodes.length > 0) {
|
||||
if (currentInlineNodes.length > 0) {
|
||||
const p = document.createElement('p');
|
||||
currentInlineNodes.forEach(node => p.appendChild(node));
|
||||
newContainer.appendChild(p);
|
||||
}
|
||||
currentInlineNodes = [];
|
||||
}
|
||||
};
|
||||
|
||||
Array.from(__c.childNodes).forEach((node) => {
|
||||
// 判断是否为块级元素
|
||||
const isBlock = node.nodeType === 1 && blockTags.includes(node.tagName);
|
||||
|
||||
if (isBlock) {
|
||||
wrapper = null; // 遇到块级元素,重置包裹容器
|
||||
} else {
|
||||
// 如果是行内元素(文本、图片、span、strong等),放入 p 标签
|
||||
// 忽略纯空白文本节点(如果它们在块级元素之间),避免产生过多的空段落
|
||||
// 但为了保险起见(避免吞掉有意义的空格),这里暂时全部包裹
|
||||
|
||||
if (!wrapper) {
|
||||
wrapper = document.createElement('p');
|
||||
__c.insertBefore(wrapper, node);
|
||||
flushInlines();
|
||||
newContainer.appendChild(node);
|
||||
// 记录最后添加的是块级元素
|
||||
lastWasBlock = true;
|
||||
} else if (node.nodeName === 'BR') {
|
||||
if (currentInlineNodes.length > 0) {
|
||||
flushInlines();
|
||||
lastWasBlock = false; // 刚刚结束了一个段落,不算紧挨着块级
|
||||
} else {
|
||||
// 如果前面紧挨着块级元素,忽略这个 BR(避免块级元素后的换行产生空行)
|
||||
if (lastWasBlock) {
|
||||
// 忽略
|
||||
lastWasBlock = false; // 消耗掉块级后的换行状态,避免连续 BR 被吞
|
||||
} else {
|
||||
const p = document.createElement('p');
|
||||
p.innerHTML = '<br>';
|
||||
newContainer.appendChild(p);
|
||||
lastWasBlock = false;
|
||||
}
|
||||
}
|
||||
wrapper.appendChild(node);
|
||||
} else {
|
||||
// 过滤掉块级元素之间或开头的纯空白文本节点,避免产生空的 P 标签
|
||||
if (node.nodeType === 3 && !node.textContent.trim()) {
|
||||
if (currentInlineNodes.length > 0) {
|
||||
currentInlineNodes.push(node);
|
||||
}
|
||||
} else {
|
||||
currentInlineNodes.push(node);
|
||||
}
|
||||
lastWasBlock = false;
|
||||
}
|
||||
});
|
||||
|
||||
html = __c.innerHTML;
|
||||
flushInlines();
|
||||
|
||||
html = newContainer.innerHTML;
|
||||
|
||||
console.log("初始化显示的html", html);
|
||||
return html;
|
||||
@@ -563,6 +585,8 @@ const editApp = createApp({
|
||||
|
||||
// 提交
|
||||
const submit = (status) => {
|
||||
if (location.hostname == "127.0.0.1") status = 0
|
||||
|
||||
if (realname.value == 0 && userInfoWin.value?.uin > 0) {
|
||||
openAttest();
|
||||
return;
|
||||
@@ -587,18 +611,15 @@ const editApp = createApp({
|
||||
info.value["attachments"]["images"] = images;
|
||||
info.value["attachments"]["videos"] = videos;
|
||||
|
||||
console.log("原始html", content);
|
||||
// console.log("原始html", content);
|
||||
content = formatContent(content);
|
||||
console.log("最终html", content);
|
||||
// console.log("最终html", content);
|
||||
const data = {
|
||||
...infoTarget,
|
||||
content,
|
||||
};
|
||||
|
||||
console.log("data", data);
|
||||
if (location.hostname == "127.0.0.1") {
|
||||
status = 0;
|
||||
}
|
||||
// console.log("data", data);
|
||||
|
||||
ajax("/v2/api/forum/postPublishTopic", {
|
||||
info: data,
|
||||
@@ -623,6 +644,8 @@ const editApp = createApp({
|
||||
|
||||
const formatContent = (html) => {
|
||||
if (!html) return "";
|
||||
// <p><br></p> 转换为单个换行符
|
||||
html = html.replace(/<p><br><\/p>/gi, "\n");
|
||||
|
||||
// 1. 处理居中 [align=center] (p, div, h1-h6)
|
||||
html = html.replace(/<(p|div|h[1-6])[^>]*style="[^"]*text-align:\s*center;[^"]*"[^>]*>([\s\S]*?)<\/\1>/gi, (match, tag, content) => {
|
||||
@@ -637,7 +660,10 @@ const editApp = createApp({
|
||||
html = html.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, "[section]$1[/section]");
|
||||
|
||||
// 2.5 处理段落 [p]
|
||||
html = html.replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, "[p]$1[/p]");
|
||||
// 优先处理空段落 <p><br></p>,将其替换为单个换行符,避免后续双重换行
|
||||
html = html.replace(/<p[^>]*>\s*<br\s*\/?>\s*<\/p>/gi, "\n");
|
||||
// 处理普通段落结束符
|
||||
html = html.replace(/<\/p>/gi, "\n");
|
||||
|
||||
// 3. 处理加粗 [b] (对应 strong, b)
|
||||
html = html.replace(/<(strong|b)[^>]*>([\s\S]*?)<\/\1>/gi, "[b]$2[/b]");
|
||||
@@ -703,14 +729,16 @@ const editApp = createApp({
|
||||
|
||||
// 处理 data-href,包裹在 [url] 中
|
||||
const hrefMatch = imgTag.match(/data-href="([^"]+)"/i);
|
||||
if (hrefMatch && hrefMatch[1]) {
|
||||
result = `[url=${hrefMatch[1]}]${result}[/url]`;
|
||||
}
|
||||
if (hrefMatch && hrefMatch[1]) result = `[url=${hrefMatch[1]}]${result}[/url]`;
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
// 5. 处理视频 [attach]
|
||||
html = html.replace(/<div[^>]*data-w-e-type="video"[^>]*>([\s\S]*?)<\/div>/gi, (match, content) => {
|
||||
return content.trim(); // 去掉包裹视频的 div,并去除首尾空白,防止产生额外换行
|
||||
});
|
||||
|
||||
html = html.replace(/<video[^>]*>[\s\S]*?<\/video>/gi, (videoTag) => {
|
||||
let aid = "";
|
||||
const dataAid = videoTag.match(/data-aid="(\d+)"/i);
|
||||
@@ -742,7 +770,7 @@ const editApp = createApp({
|
||||
|
||||
// 7. 换行处理
|
||||
html = html.replace(/<br\s*\/?>/gi, "\n");
|
||||
// html = html.replace(/<\/(p|div)>/gi, "\n"); // 块级元素结束视为换行
|
||||
|
||||
html = html.replace(/<\/(div)>/gi, "\n"); // 块级元素结束视为换行
|
||||
|
||||
// 8. 清理剩余标签和解码
|
||||
@@ -781,8 +809,6 @@ const editApp = createApp({
|
||||
});
|
||||
});
|
||||
|
||||
console.log("提取完成的图片列表:", images);
|
||||
|
||||
return images;
|
||||
};
|
||||
|
||||
@@ -817,7 +843,6 @@ const editApp = createApp({
|
||||
});
|
||||
});
|
||||
|
||||
console.log("提取完成的视频列表:", result);
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -842,7 +867,6 @@ const editApp = createApp({
|
||||
},
|
||||
onUploadProgress: (e) => {
|
||||
progress.value = Math.round((e.loaded / e.total) * 100);
|
||||
console.log("progress.value", progress.value);
|
||||
},
|
||||
withCredentials: true,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user