feat(editor): 添加预加载动画和编辑器功能优化
- 新增预加载动画组件及样式 - 优化编辑器图片和视频上传处理逻辑 - 修复编辑器内容转换和格式处理问题 - 添加上传进度显示功能 - 改进编辑器工具栏图标和布局
This commit is contained in:
320
js/edit.js
320
js/edit.js
@@ -1,6 +1,6 @@
|
||||
// 简单版本的论坛编辑器,确保图片插入功能正常
|
||||
const { createApp, ref, computed, onMounted, nextTick, onUnmounted } = Vue;
|
||||
import { headTop } from "../component/head-top/head-top.js";
|
||||
const { headTop } = await import(withVer("../component/head-top/head-top.js"));
|
||||
const { createEditor, createToolbar, SlateTransforms, Boot, SlateEditor } = window.wangEditor;
|
||||
|
||||
class MyButtonMenu {
|
||||
@@ -40,6 +40,10 @@ const editApp = createApp({
|
||||
let titleLength = ref(200);
|
||||
|
||||
let uniqid = ref("");
|
||||
|
||||
const valueA = ref(null);
|
||||
let valueUrl = "";
|
||||
|
||||
onMounted(() => {
|
||||
const params = getUrlParams();
|
||||
uniqid.value = params.uniqid || "";
|
||||
@@ -48,6 +52,21 @@ const editApp = createApp({
|
||||
checkWConfig();
|
||||
|
||||
cUpload();
|
||||
|
||||
// console.log(valueA.value);
|
||||
|
||||
valueUrl = valueA.value.innerText;
|
||||
|
||||
if (location.hostname == "127.0.0.1") {
|
||||
realname.value = 1;
|
||||
userInfoWin.value = {
|
||||
uin: 1234567890,
|
||||
uid: 1234567890,
|
||||
realname: "测试用户",
|
||||
};
|
||||
|
||||
isLogin.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
let imageLength = 10;
|
||||
@@ -139,54 +158,20 @@ const editApp = createApp({
|
||||
uniqid: uniqid.value,
|
||||
})
|
||||
.then((res) => {
|
||||
// res = {
|
||||
// code: 200,
|
||||
// message: "成功",
|
||||
// data: {
|
||||
// info: {
|
||||
// uniqid: "8a0yn9CWGjur",
|
||||
// tags: [],
|
||||
// title: "香港🇭🇰梦中情校offer,叉烧包我来了!",
|
||||
// attachments: {
|
||||
// images: [
|
||||
// {
|
||||
// aid: 1008535,
|
||||
// url: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_pokcHyD1NFX9ddrB_WbUGy8P79gQxdHE7aVs5oV7NkzNDQyOQ~~",
|
||||
// },
|
||||
// {
|
||||
// aid: 1008032,
|
||||
// url: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_pokdXyE1NFX9ddrB_WbUGy8P79gQxdAFefK5JoV7NkzNDQyOQ~~",
|
||||
// },
|
||||
// ],
|
||||
// files: [],
|
||||
// videos: [
|
||||
// {
|
||||
// aid: 1008621,
|
||||
// posterid: 1008676,
|
||||
// posterurl: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_pokc3iA1NFX9ddrB_WbUGy8P79gQxdHFOXC5J0V7NkzNDQyOQ~~",
|
||||
// url: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_pokc32H1NFX9ddrB_WbUGy8P79gQxcSFeCW5s8V7NkzNDQyOQ~~",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// anonymous: 1,
|
||||
// created_at: "2025-11-10 15:49:15",
|
||||
// type: "thread",
|
||||
// content: "[attach]1008621[/attach]\n[b]哈哈哈fdsafdaf🤫afafafas[/b]\n\n[b]😘[/b]\n🍻\n[b]婆婆[/b]\n一\n[b]样[/b]\n😉\n[b]噜噜噜fdsafdsafdafafsafafdsfdafsafsafsas[/b]\n[attachimg]1008535[/attachimg]\n\nfds\n[b]afsa[/b]\nfsdafafafafdas\n[b]魂牵梦萦 fsdaf[/b]\n[attachimg]1008032[/attachimg]",
|
||||
// role: {
|
||||
// type: "public",
|
||||
// },
|
||||
// },
|
||||
// token: "zkzAyP2l9uzYy4S63Ew3zJ1N1QkpC0ZJ7BTUBmhaeQsjc_ACxctWNq5ZtxRkFzPoNTM4W2BkojS6qZ14BLHTPRi3ohhoRKpC22Bui4qps4MDDbdu22VQtra72BDqIykNcfkCj2MDyxbHXAlC6VWGmUbA3VQ0NmUz",
|
||||
// tagList: [],
|
||||
// },
|
||||
// };
|
||||
const data = res.data;
|
||||
|
||||
if (res.code != 200) {
|
||||
creationAlertBox("error", res.message || "操作失败");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
if (infoTarget.content) infoTarget.content = restoreHtml(infoTarget.content, infoTarget.attachments);
|
||||
console.log("content", infoTarget.content);
|
||||
@@ -194,6 +179,8 @@ const editApp = createApp({
|
||||
info.value = infoTarget;
|
||||
token.value = data.token;
|
||||
|
||||
console.log("data", data);
|
||||
|
||||
initEditor();
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -204,6 +191,18 @@ const editApp = createApp({
|
||||
let editor = null;
|
||||
let toolbarRef = ref(null);
|
||||
|
||||
// 自定义转换视频
|
||||
function customParseVideoSrc(src) {
|
||||
// console.log("customParseVideoSrc", "src:", src);
|
||||
// if (src.includes(".bilibili.com")) {
|
||||
// // 转换 bilibili url 为 iframe (仅作为示例,不保证代码正确和完整)
|
||||
// const arr = location.pathname.split("/");
|
||||
// const vid = arr[arr.length - 1];
|
||||
// return `<iframe src="//player.bilibili.com/player.html?bvid=${vid}" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>`;
|
||||
// }
|
||||
return src;
|
||||
}
|
||||
|
||||
const initEditor = () => {
|
||||
let infoTarget = info.value || {};
|
||||
|
||||
@@ -218,58 +217,54 @@ const editApp = createApp({
|
||||
["insertImage"]: {
|
||||
onInsertedImage(imageNode) {
|
||||
console.log("insertImage");
|
||||
if (imageNode == null) return;
|
||||
// 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) {
|
||||
// 如果图片链接中已经包含了 ?aid= ,则说明是本站图片,直接返回,无需处理
|
||||
if (src.includes("?aid=")) return src;
|
||||
|
||||
// 对于不含 ?aid= 的外部图片,执行上传转换
|
||||
if (!uConfigData || !uConfigData.url) return src;
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("uploadType", "url");
|
||||
formData.append("url", src);
|
||||
if (uConfigData.params && uConfigData.params.data) {
|
||||
formData.append("data", uConfigData.params.data);
|
||||
}
|
||||
|
||||
const res = await ajax(uConfigData.url, formData);
|
||||
if (res.code == 200 && res.data) return `${res.data.url}?aid=${res.data.aid}`;
|
||||
else creationAlertBox("error", res.message || "操作失败");
|
||||
} catch (e) {
|
||||
console.error("Transform network image failed", e);
|
||||
}
|
||||
return src;
|
||||
},
|
||||
},
|
||||
|
||||
["uploadImage"]: {
|
||||
server: uConfigData.url,
|
||||
|
||||
// form-data fieldName ,默认值 'wangeditor-uploaded-image'
|
||||
fieldName: uConfigData.requestName,
|
||||
|
||||
// 单个文件的最大体积限制,默认为 2M
|
||||
maxFileSize: maxSize, // 1M
|
||||
|
||||
// 最多可上传几个文件,默认为 100
|
||||
maxNumberOfFiles: imageLength,
|
||||
|
||||
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
|
||||
allowedFileTypes: ["image/png", "image/jpeg", "image/jpg"], // .png, .jpg, .jpeg
|
||||
|
||||
// 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
|
||||
meta: { ...uConfigData.params },
|
||||
|
||||
// 将 meta 拼接到 url 参数中,默认 false
|
||||
metaWithUrl: false,
|
||||
|
||||
// 自定义增加 http header
|
||||
headers: { accept: "application/json, text/plain, */*", ...uConfigData.headers },
|
||||
|
||||
// 跨域是否传递 cookie ,默认为 false
|
||||
withCredentials: true,
|
||||
|
||||
// 超时时间,默认为 10 秒
|
||||
|
||||
timeout: 60 * 1000, // 15 秒
|
||||
async customUpload(file, insertFn) {
|
||||
try {
|
||||
let config = uConfigData;
|
||||
// 1. 构造 FormData(包含你的接口所需字段)
|
||||
const formData = new FormData();
|
||||
formData.append(config.requestName, file); // 文件数据
|
||||
formData.append("name", file.name); // 文件名
|
||||
formData.append("type", "image"); // 文件名
|
||||
formData.append("data", config.params.data); // 文件名
|
||||
// uploading(file, file.name, "image").then((data) => {
|
||||
// insertFn(data.url); // 传入图片的可访问 URL
|
||||
// });
|
||||
ajax(config.url, formData).then((res) => {
|
||||
const data = res.data;
|
||||
console.log("上传成功:", data);
|
||||
insertFn(`${data.url}?aid=${data.aid}`); // 传入图片的可访问 URL
|
||||
});
|
||||
const img = await uploading(file, file.name, "image");
|
||||
insertFn(`${img.url}?aid=${img.aid}`);
|
||||
} catch (err) {
|
||||
console.error("上传出错:", err);
|
||||
}
|
||||
@@ -284,6 +279,7 @@ const editApp = createApp({
|
||||
const { src } = videoNode;
|
||||
// console.log("inserted video", src);
|
||||
},
|
||||
parseVideoSrc: customParseVideoSrc, // 也支持 async 函数
|
||||
},
|
||||
|
||||
["uploadVideo"]: {
|
||||
@@ -291,26 +287,24 @@ const editApp = createApp({
|
||||
fieldName: uConfigData.requestName,
|
||||
maxFileSize: maxSize, // 1M
|
||||
maxNumberOfFiles: videoLength,
|
||||
allowedFileTypes: ["video/*"],
|
||||
allowedFileTypes: ["video/flv", "video/mkv", "video/avi", "video/rm", "video/rmvb", "video/mpeg", "video/mpg", "video/ogg", "video/ogv", "video/mov", "video/wmv", "video/mp4", "video/webm", "video/m4v"],
|
||||
meta: { ...uConfigData.params },
|
||||
metaWithUrl: false,
|
||||
headers: { accept: "application/json, text/plain, */*", ...uConfigData.headers },
|
||||
withCredentials: true,
|
||||
timeout: 15 * 1000, // 15 秒
|
||||
timeout: 60 * 1000, // 15 秒
|
||||
async customUpload(file, insertFn) {
|
||||
try {
|
||||
const videoUploadRes = await uploading(file, file.name, "video");
|
||||
|
||||
progress.value = 0;
|
||||
const coverFile = await getVideoFirstFrame(file);
|
||||
console.log("第一帧提取成功", coverFile);
|
||||
|
||||
// 步骤3:再上传第一帧封面(type 传 'cover',按后端要求调整)
|
||||
// console.log("第一帧提取成功", coverFile);
|
||||
const coverUploadRes = await uploading(coverFile, coverFile.name, "image");
|
||||
console.log("封面上传成功", coverUploadRes);
|
||||
|
||||
// console.log("封面上传成功", coverUploadRes);
|
||||
insertFn(`${videoUploadRes.url}?aid=${videoUploadRes.aid}`, `${coverUploadRes.url}?aid=${coverUploadRes.aid}`);
|
||||
} catch (err) {
|
||||
console.error("上传出错:", err);
|
||||
progress.value = 0;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -323,9 +317,6 @@ const editApp = createApp({
|
||||
|
||||
hoverbarKeys: { text: { menuKeys: [] }, video: { menuKeys: [] } },
|
||||
};
|
||||
|
||||
// html: infoTarget.content,
|
||||
|
||||
editor = createEditor({
|
||||
selector: "#editor-container",
|
||||
html: infoTarget.content,
|
||||
@@ -334,7 +325,18 @@ const editApp = createApp({
|
||||
});
|
||||
|
||||
const toolbarConfig = {
|
||||
toolbarKeys: ["header1", "uploadImage", "uploadVideo", "emotion", "insertLink", "bold"],
|
||||
toolbarKeys: [
|
||||
"header1",
|
||||
{
|
||||
key: "group-image",
|
||||
title: "图片",
|
||||
menuKeys: ["insertImage", "uploadImage"],
|
||||
},
|
||||
"uploadVideo",
|
||||
"emotion",
|
||||
"insertLink",
|
||||
"bold",
|
||||
],
|
||||
};
|
||||
|
||||
const menu1Conf = {
|
||||
@@ -362,37 +364,37 @@ const editApp = createApp({
|
||||
const h1 = toolbarRef.value.querySelector('[data-menu-key="header1"]');
|
||||
const h1Item = h1.parentElement;
|
||||
h1Item.classList.add("toolbar-item", "flexacenter");
|
||||
h1.innerHTML = '<img class="icon" src="{@/img/t-icon.png}" alt="段落标题" /> <span>段落标题</span>';
|
||||
h1.innerHTML = `<img class="icon" src="${valueUrl}/img/t-icon.png" alt="段落标题" /> <span>段落标题</span>`;
|
||||
|
||||
const image = toolbarRef.value.querySelector('[data-menu-key="uploadImage"]');
|
||||
const image = toolbarRef.value.querySelector('[data-menu-key="group-image"]');
|
||||
const imageItem = image.parentElement;
|
||||
imageItem.classList.add("toolbar-item", "flexacenter");
|
||||
image.innerHTML = '<img class="icon" src="{@/img/img-icon.png}" alt="图片" /> <span>图片</span>';
|
||||
image.innerHTML = `<img class="icon" src="${valueUrl}/img/img-icon.png" alt="图片" /> <span>图片</span>`;
|
||||
|
||||
const video = toolbarRef.value.querySelector('[data-menu-key="uploadVideo"]');
|
||||
const videoItem = video.parentElement;
|
||||
videoItem.classList.add("toolbar-item", "flexacenter");
|
||||
video.innerHTML = '<img class="icon" src="{@/img/video-icon.png}" alt="视频" /> <span>视频</span>';
|
||||
video.innerHTML = `<img class="icon" src="${valueUrl}/img/video-icon.png" alt="视频" /> <span>视频</span>`;
|
||||
|
||||
const emotion = toolbarRef.value.querySelector('[data-menu-key="emotion"]');
|
||||
const emotionItem = emotion.parentElement;
|
||||
emotionItem.classList.add("toolbar-item", "flexacenter");
|
||||
emotion.innerHTML = '<img class="icon" src="{@/img/emotion-icon.png}" alt="表情" /> <span>表情</span>';
|
||||
emotion.innerHTML = `<img class="icon" src="${valueUrl}/img/smiling-face-round-black.png" alt="表情" /> <span>表情</span>`;
|
||||
|
||||
const link = toolbarRef.value.querySelector('[data-menu-key="insertLink"]');
|
||||
const linkItem = link.parentElement;
|
||||
linkItem.classList.add("toolbar-item", "flexacenter");
|
||||
link.innerHTML = '<img class="icon" src="{@/img/link-icon.png}" alt="链接" /> <span>链接</span>';
|
||||
link.innerHTML = `<img class="icon" src="${valueUrl}/img/link-icon.png" alt="链接" /> <span>链接</span>`;
|
||||
|
||||
const bold = toolbarRef.value.querySelector('[data-menu-key="bold"]');
|
||||
const boldItem = bold.parentElement;
|
||||
boldItem.classList.add("toolbar-item", "flexacenter");
|
||||
bold.innerHTML = '<img class="icon" src="{@/img/bold-icon.png}" alt="加粗" /> <span>加粗</span>';
|
||||
bold.innerHTML = `<img style="width: 14px;height: 14px;" class="icon" src="${valueUrl}/img/overstriking-icon.png" alt="加粗" /> <span>加粗</span>`;
|
||||
|
||||
const customCenter = toolbarRef.value.querySelector('[data-menu-key="customCenter"]');
|
||||
const customCenterItem = customCenter.parentElement;
|
||||
customCenterItem.classList.add("toolbar-item", "flexacenter");
|
||||
customCenter.innerHTML = '<img class="icon" src="{@/img/justify-center-icon.png}" alt="居中" /> <span>居中</span>';
|
||||
customCenter.innerHTML = `<img class="icon" src="${valueUrl}/img/between -icon.png" alt="居中" /> <span>居中</span>`;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -405,7 +407,7 @@ const editApp = createApp({
|
||||
let html = formattedText;
|
||||
|
||||
// 0. 将所有 <div> 转为 <p>, </div>转为</p>
|
||||
html = html.replace(/<div>/g, "<p>");
|
||||
html = html.replace(/<div(\s|>)/gi, "<p$1");
|
||||
html = html.replace(/<\/div>/g, "</p>");
|
||||
|
||||
// 1. 还原换行符为<br>标签
|
||||
@@ -424,6 +426,23 @@ const editApp = createApp({
|
||||
// html = html.replace(/\[center\]([\s\S]*?)\[\/center\]/gi, '<p style="text-align: center;">$1</p>');
|
||||
// console.log("html1", html);
|
||||
|
||||
// 5. 还原 a > [img] 的情况
|
||||
html = html.replace(/<a href="([^\"]+)"[^>]*>\[img(?:=([0-9]+(?:\.[0-9]+)?)(?:,([0-9]+(?:\.[0-9]+)?))?)?\](\d+)\[\/img\]<\/a>/gi, (match, href, width, height, aid) => {
|
||||
const image = imageList.find((img) => String(img.aid) === String(aid));
|
||||
if (!image) return match;
|
||||
|
||||
const index = imageList.findIndex((img) => String(img.aid) === String(aid));
|
||||
if (index > -1) imageList.splice(index, 1);
|
||||
|
||||
let style = "";
|
||||
const w = width ? Number(width) : 0;
|
||||
const h = height ? Number(height) : 0;
|
||||
if (w > 0 && h > 0) style = `style="width: ${w}px; height: ${h}px;"`;
|
||||
else if (w > 0) style = `style="width: ${w}px;"`;
|
||||
|
||||
return `<img src="${image.url}?aid=${aid}" data-aid="${aid}" data-href="${href}" ${style}>`;
|
||||
});
|
||||
|
||||
// 5. 还原【新增图片格式】[img=width,height]aid[/img] 或 [img]aid[/img]
|
||||
html = html.replace(/\[img(?:=([0-9]+(?:\.[0-9]+)?)(?:,([0-9]+(?:\.[0-9]+)?))?)?\](\d+)\[\/img\]/gi, (match, width, height, aid) => {
|
||||
const image = imageList.find((img) => String(img.aid) === String(aid)); // 统一字符串比较,避免类型问题
|
||||
@@ -451,7 +470,7 @@ const editApp = createApp({
|
||||
const image = imageList.find((img) => img.aid == aid);
|
||||
if (image) {
|
||||
imageList.splice(imageList.indexOf(image), 1);
|
||||
return `<img src="${image.url}" data-aid="${aid}">`;
|
||||
return `<img src="${image.url}?aid=${aid}" data-aid="${aid}">`;
|
||||
}
|
||||
return match; // 未找到对应图片时保留原始标记
|
||||
});
|
||||
@@ -498,7 +517,6 @@ const editApp = createApp({
|
||||
Array.from(__c.querySelectorAll("video")).forEach((vd) => {
|
||||
const p = vd.parentElement;
|
||||
if (!p || p.tagName !== "P") {
|
||||
console.log(999999999999999999999999999999);
|
||||
const wrap = document.createElement("p");
|
||||
p ? p.insertBefore(wrap, vd) : __c.appendChild(wrap);
|
||||
wrap.appendChild(vd);
|
||||
@@ -507,7 +525,7 @@ const editApp = createApp({
|
||||
|
||||
html = __c.innerHTML;
|
||||
|
||||
console.log("html3", html);
|
||||
console.log("初始化显示的html", html);
|
||||
return html;
|
||||
};
|
||||
|
||||
@@ -532,10 +550,10 @@ const editApp = createApp({
|
||||
return;
|
||||
}
|
||||
|
||||
// if (!isLogin.value) {
|
||||
// goLogin();
|
||||
// return;
|
||||
// }
|
||||
if (!isLogin.value) {
|
||||
goLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
const infoTarget = { ...info.value } || {};
|
||||
let content = editor.getHtml();
|
||||
@@ -551,16 +569,16 @@ const editApp = createApp({
|
||||
info.value["attachments"]["images"] = images;
|
||||
info.value["attachments"]["videos"] = videos;
|
||||
|
||||
console.log(content);
|
||||
console.log("原始html", content);
|
||||
content = formatContent(content);
|
||||
console.log(content);
|
||||
console.log("最终html", content);
|
||||
const data = {
|
||||
...infoTarget,
|
||||
content,
|
||||
};
|
||||
|
||||
console.log("data", data);
|
||||
// return
|
||||
if (location.hostname == "127.0.0.1") return;
|
||||
|
||||
ajax("/v2/api/forum/postPublishTopic", {
|
||||
info: data,
|
||||
@@ -584,9 +602,6 @@ const editApp = createApp({
|
||||
};
|
||||
|
||||
const formatContent = (html) => {
|
||||
// 1. 替换图片标签
|
||||
// html = html.replace(/<img[^>]*data-aid="(\d+)"[^>]*>/gi, "[attachimg]$1[/attachimg]");
|
||||
|
||||
// 1. 替换图片标签(优先解析src中的aid+宽高生成自定义格式,再兼容原有data-aid逻辑)
|
||||
html = html.replace(/<img[^>]*>/gi, (imgTag) => {
|
||||
const srcMatch = imgTag.match(/src="([^"]+)"/i);
|
||||
@@ -625,12 +640,18 @@ const editApp = createApp({
|
||||
console.log("width", width, "height", height);
|
||||
|
||||
// 第四步:按规则生成格式
|
||||
if (width == 0 && height == 0) return `[img]${aid}[/img]`;
|
||||
else return `[img=${width}${height ? "," + height : ""}]${aid}[/img]`;
|
||||
let result;
|
||||
if (width == 0 && height == 0) result = `[img]${aid}[/img]`;
|
||||
else result = `[img=${width}${height ? "," + height : ""}]${aid}[/img]`;
|
||||
|
||||
// 提取 data-href 并添加 a 标签
|
||||
const dataHrefMatch = imgTag.match(/data-href="([^"]+)"/i);
|
||||
if (dataHrefMatch && dataHrefMatch[1]) result = `<a href="${dataHrefMatch[1]}" target="_blank">${result}</a>`;
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
// 1.1 替换视频标签
|
||||
// html = html.replace(/<video[^>]*aid="(\d+)"[^>]*>[\s\S]*?<\/video>/gi, "[attach]$1[/attach]");
|
||||
html = html.replace(/<video[^>]*>[\s\S]*?<\/video>/gi, (videoTag) => {
|
||||
// 第一步:提取video内source标签的src属性
|
||||
const sourceSrcMatch = videoTag.match(/<source\s+src="([^"]+)"[^>]*>/i);
|
||||
@@ -652,25 +673,17 @@ const editApp = createApp({
|
||||
});
|
||||
|
||||
// 2. 替换H2标签
|
||||
html = html.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, "[b]$1[/b]");
|
||||
html = html.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, "[section]$1[/section]");
|
||||
html = html.replace(/<strong[^>]*>([\s\S]*?)<\/strong>/gi, "[b]$1[/b]");
|
||||
|
||||
// html = html.replace(/<strong[^>]*>([\s\S]*?)<\/strong>/gi, "[strong]$1[/strong]");
|
||||
|
||||
// html = html.replace(/<p[^>]*style=["'][^"']*text-align\s*:\s*center[^"']*["'][^>]*>([\s\S]*?)<\/p>/gi, "[center]$1[/center]");
|
||||
|
||||
// 5. 处理块级标签换行(仅<div>等块级标签前后换行,保持行内内容连续)
|
||||
// 块级标签:div、p、h1-h6等,这里以div为例
|
||||
// html = html.replace(/<\/div>\s*/gi, "</div>\n"); // 闭合div后换行
|
||||
// html = html.replace(/\s*<div[^>]*>/gi, "\n<div>"); // 开启div前换行
|
||||
// 3.<a href="ghj hgj gh jghj " target="_blank">ghj hgj ghj </a> 替换为 [url=ghj hgj gh jghj ]ghj hgj ghj [/url]
|
||||
html = html.replace(/<a\s+href="([^"]+)"\s+target="_blank">([\s\S]*?)<\/a>/gi, (match, href, content) => {
|
||||
return `[url=${href}]${content}[/url]`;
|
||||
});
|
||||
|
||||
// 6. 处理<br>为换行
|
||||
html = html.replace(/<br\s*\/?>/gi, "\n");
|
||||
|
||||
// 7. 移除所有剩余HTML标签 a标签除外
|
||||
// html = html.replace(/<(?!(a\b|\/a\b))[^>]+>/gi, "");
|
||||
|
||||
// 8. 清理连续换行(最多保留两个空行,避免过多空行)
|
||||
// html = html.replace(/\n{3,}/g, "\n\n");
|
||||
// 去除首尾空白
|
||||
html = html.trim();
|
||||
|
||||
@@ -736,26 +749,49 @@ const editApp = createApp({
|
||||
return result;
|
||||
};
|
||||
|
||||
const progress = ref(0);
|
||||
|
||||
const uploading = (target, name, type) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let config = uConfigData;
|
||||
const formData = new FormData();
|
||||
formData.append(config.requestName, target); // 文件数据
|
||||
formData.append("name", name); // 文件名
|
||||
formData.append("type", type); // 文件名
|
||||
formData.append("data", config.params.data); // 文件名
|
||||
formData.append(config.requestName, target);
|
||||
formData.append("name", name);
|
||||
formData.append("type", type);
|
||||
if (config.params && config.params.data) {
|
||||
formData.append("data", config.params.data);
|
||||
}
|
||||
|
||||
ajax(config.url, formData)
|
||||
.then((res) => {
|
||||
const data = res.data;
|
||||
try {
|
||||
resolve(data);
|
||||
} catch (error) {
|
||||
console.error("插入图片出错:", error);
|
||||
axios
|
||||
.post(config.url, formData, {
|
||||
headers: {
|
||||
...config.headers,
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
onUploadProgress: (e) => {
|
||||
progress.value = Math.round((e.loaded / e.total) * 100);
|
||||
console.log("progress.value", progress.value);
|
||||
},
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const res = response.data;
|
||||
if (res.code == 200) {
|
||||
resolve(res.data);
|
||||
} else {
|
||||
creationAlertBox("error", res.message || "上传失败");
|
||||
reject(res);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
.catch((error) => {
|
||||
if (error.response) {
|
||||
creationAlertBox("error", `HTTP错误: ${error.response.status}`);
|
||||
} else if (error.request) {
|
||||
creationAlertBox("error", "网络错误");
|
||||
} else {
|
||||
creationAlertBox("error", "请求设置错误");
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -824,7 +860,7 @@ const editApp = createApp({
|
||||
});
|
||||
};
|
||||
|
||||
return { toolbarRef, uniqid, userInfoWin, titleLength, submit, info, tagList, token, cutAnonymity, editorRef };
|
||||
return { progress, valueA, toolbarRef, uniqid, userInfoWin, titleLength, submit, info, tagList, token, cutAnonymity, editorRef };
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user