// 简单版本的论坛编辑器,确保图片插入功能正常
const { createApp, ref, computed, onMounted, nextTick, onUnmounted } = Vue;
import { headTop } from "../component/head-top/head-top.js";
const { createEditor, createToolbar } = window.wangEditor;
console.log("createEditor", createEditor);
const editApp = createApp({
setup() {
let titleLength = ref(200);
let uniqid = ref("");
onMounted(() => {
const params = getUrlParams();
uniqid.value = params.uniqid || "";
getUserInfoWin();
checkWConfig();
cUpload();
// 添加selectionchange事件监听,当鼠标选中区域内容时更新lastSelection
document.addEventListener("selectionchange", handleSelectionChange);
});
// 组件卸载时移除事件监听
onUnmounted(() => {
document.removeEventListener("selectionchange", handleSelectionChange);
});
let imageLength = 10;
let videoLength = 5;
const checkWConfig = () => {
const wConfig = JSON.parse(localStorage.getItem("wConfig")) || {};
console.log("wConfig", wConfig);
if (wConfig.time) {
const time = new Date(wConfig.time);
const now = new Date();
if (now - time > 24 * 60 * 60 * 1000) getWConfig();
else {
const config = wConfig.config || {};
titleLength.value = config.max_topic_title_length;
imageLength = config.topic_image_count || 0;
videoLength = config.topic_video_count || 0;
}
} else {
getWConfig();
}
};
const getWConfig = () => {
ajaxGet("/v2/api/config/website").then((res) => {
if (res.code == 200) {
let data = res["data"] || {};
const config = data.config || {};
titleLength.value = config.max_topic_title_length;
imageLength = config.topic_image_count || 0;
videoLength = config.topic_video_count || 0;
data.time = new Date().toISOString();
localStorage.setItem("wConfig", JSON.stringify(data));
}
});
};
let isLogin = ref(false);
let realname = ref(0); // 是否已经实名
let userInfoWin = ref({});
let permissions = ref([]);
const getUserInfoWin = () => {
const checkUser = () => {
const user = window.userInfoWin;
if (!user) return;
document.removeEventListener("getUser", checkUser);
realname.value = user.realname;
userInfoWin.value = user;
if (user?.uin > 0 || user?.uid > 0) isLogin.value = true;
permissions.value = user?.authority || [];
};
document.addEventListener("getUser", checkUser);
};
const openAttest = () => {
const handleAttestClose = () => {
document.removeEventListener("closeAttest", handleAttestClose);
realname.value = window.userInfoWin?.realname || 0;
};
// 启动认证流程时添加监听
document.addEventListener("closeAttest", handleAttestClose);
loadAttest(2);
};
// 跳转登录
const goLogin = () => {
if (typeof window === "undefined") return;
if (window["userInfoWin"] && Object.keys(window["userInfoWin"]).length !== 0) {
if (window["userInfoWin"]["uid"]) isLogin.value = true;
else ajax_login();
} else ajax_login();
};
let uConfigData = {};
const cUpload = () => {
ajaxGet(`/v2/api/config/upload?type=topic`).then((res) => {
const data = res.data;
uConfigData = data;
console.log("uConfigData", uConfigData);
init();
});
};
let info = ref({});
let tagList = ref([]);
let token = ref("");
let infoImages = [];
const init = () => {
ajax("/v2/api/forum/postPublishInit", {
uniqid: uniqid.value,
})
.then((res) => {
const data = res.data;
if (res.code != 200) {
creationAlertBox("error", res.message || "操作失败");
return;
}
const infoTarget = data.info || {};
if (infoTarget.content) infoTarget.content = restoreHtml(infoTarget.content, infoTarget.attachments);
info.value = infoTarget;
token.value = data.token;
// nextTick(() => {
// judgeIsEmpty();
// });
initEditor();
})
.catch((err) => {
console.log("err", err);
});
};
let editor = null;
let toolbarRef = ref(null);
const initEditor = () => {
let infoTarget = info.value || {};
console.log("infoTarget", infoTarget);
// 转换图片链接
async function customCheckImageFn(src, alt, url) {
// JS 语法
if (!src) {
return;
}
// 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); // 文件名
await setTimeout(() => {
console.log("1111");
return true;
}, 2000);
// setTimeout(() => {
// return "图片网址必须以 http/https 开头";
// }, 2000);
if (src.indexOf("http") !== 0) {
// return "图片网址必须以 http/https 开头";
}
// return true;
// 返回值有三种选择:
// 1. 返回 true ,说明检查通过,编辑器将正常插入图片
// 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
// 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
}
// 【新增】判断节点的对齐方式
const getNodeAlign = (node) => {
if (!node) return "left"; // 默认居左
// 获取节点的text-align样式(优先内联样式,再取CSS计算样式)
const inlineAlign = node.style.textAlign;
if (inlineAlign) return inlineAlign;
const computedStyle = window.getComputedStyle(node);
return computedStyle.textAlign || "left";
};
// 【新增】切换对齐方式(居中 ↔ 居左)
const toggleAlign = () => {
const editorInst = editor.value;
if (!editorInst) return;
// 禁用编辑器默认的居中命令
editorInst.off("clickToolbar", "justifyCenter");
// 获取当前选中的节点(优先段落/块级节点)
const selectedNode = getSelectedNode(editorInst);
const blockNode = DomEditor.getClosestBlock(selectedNode); // 获取块级节点(p/div等)
if (!blockNode) return;
// 判断当前对齐方式
const currentAlign = getNodeAlign(blockNode);
// 切换对齐:居中 → 居左;其他 → 居中
const newAlign = currentAlign === "center" ? "left" : "center";
// 设置节点对齐样式
editorInst.restoreSelection(); // 恢复选区
blockNode.style.textAlign = newAlign;
// 触发编辑器更新
editorInst.change();
editorInst.focus(); // 保持焦点
};
const editorConfig = {
placeholder: "Type here...",
enabledMenus: [],
MENU_CONF: {
["emotion"]: {
emotions: optionEmoji.value,
},
["insertImage"]: {
onInsertedImage(imageNode) {
console.log("imageNode", imageNode);
// TS 语法
// onInsertedImage(imageNode) { // JS 语法
if (imageNode == null) return;
const { src, alt, url, href } = imageNode;
console.log("inserted image", src, alt, url, href);
},
// checkImage: async (src, alt, url) => await customCheckImageFn(src, alt, url), // 也支持 async 函数
},
["uploadImage"]: {
server: uConfigData.url,
// form-data fieldName ,默认值 'wangeditor-uploaded-image'
fieldName: uConfigData.requestName,
// 单个文件的最大体积限制,默认为 2M
maxFileSize: maxSize, // 1M
// 最多可上传几个文件,默认为 100
maxNumberOfFiles: imageLength,
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
allowedFileTypes: ["image/*", ".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 秒
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); // 传入图片的可访问 URL
});
} catch (err) {
console.error("上传出错:", err);
}
},
},
["uploadVideo"]: {
server: uConfigData.url,
// form-data fieldName ,默认值 'wangeditor-uploaded-video'
fieldName: uConfigData.requestName,
// 单个文件的最大体积限制,默认为 10M
maxFileSize: maxSize, // 1M
// 最多可上传几个文件,默认为 5
maxNumberOfFiles: videoLength,
// 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
allowedFileTypes: ["video/*"],
// 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
meta: { ...uConfigData.params },
// 将 meta 拼接到 url 参数中,默认 false
metaWithUrl: false,
// 自定义增加 http header
headers: { accept: "application/json, text/plain, */*", ...uConfigData.headers },
// 跨域是否传递 cookie ,默认为 false
withCredentials: true,
// 超时时间,默认为 30 秒
timeout: 15 * 1000, // 15 秒
// 视频不支持 base64 格式插入
async customUpload(file, insertFn) {
try {
const videoUploadRes = await uploading(file, file.name, "video");
const coverFile = await getVideoFirstFrame(file);
console.log("第一帧提取成功", coverFile);
// 步骤3:再上传第一帧封面(type 传 'cover',按后端要求调整)
const coverUploadRes = await uploading(coverFile, coverFile.name, "image");
console.log("封面上传成功", coverUploadRes);
insertFn(videoUploadRes.url, coverUploadRes.url);
} catch (err) {
console.error("上传出错:", err);
}
},
},
["justifyCenter"]: {
onClick: (editor) => {
console.log("editor", editor);
toggleAlign(); // 替换为自定义切换逻辑
},
// 【可选】自定义居中按钮的激活状态(选中时高亮)
isActive: (editor) => {
const selectedNode = getSelectedNode(editor);
const blockNode = DomEditor.getClosestBlock(selectedNode);
return blockNode && getNodeAlign(blockNode) === "center";
},
},
},
onChange(editor) {
const html = editor.getHtml();
// console.log('"editor', editor);
console.log("editor content", html);
updateWHeadingStatus();
},
};
editor = createEditor({
selector: "#editor-container",
html: "
555
",
config: editorConfig,
mode: "default",
});
// const toolbar = DomEditor.getToolbar(editor)
const toolbarConfig = {
// toolbarKeys: ["bold", "italic", "list"],
toolbarKeys: [
"header2", // 标题
{
key: "group-image",
title: "图片",
menuKeys: ["insertImage", "uploadImage"],
},
{
key: "group-video",
title: "视频",
menuKeys: ["insertVideo", "uploadVideo"],
},
// "insertVideo",
"emotion", // 表情
// "uploadImage", // 插入图片
// "uploadVideo", // 插入视频
"insertLink", // 插入链接
"bold", // 粗体
"justifyCenter",
],
};
const toolbar = createToolbar({
editor,
selector: "#toolbar-container",
config: toolbarConfig,
mode: "default",
});
console.log("editor.commands", editor);
nextTick(() => {
const h2 = toolbarRef.value.querySelector('[data-menu-key="header2"]');
const h2Item = h2.parentElement;
h2Item.classList.add("toolbar-item", "flexacenter");
h2.innerHTML = '
段落标题';
const image = toolbarRef.value.querySelector('[data-menu-key="group-image"]');
const imageItem = image.parentElement;
imageItem.classList.add("toolbar-item", "flexacenter");
image.innerHTML = '
图片';
const video = toolbarRef.value.querySelector('[data-menu-key="group-video"]');
const videoItem = video.parentElement;
videoItem.classList.add("toolbar-item", "flexacenter");
video.innerHTML = '
视频';
const emotion = toolbarRef.value.querySelector('[data-menu-key="emotion"]');
const emotionItem = emotion.parentElement;
emotionItem.classList.add("toolbar-item", "flexacenter");
emotion.innerHTML = '
表情';
const link = toolbarRef.value.querySelector('[data-menu-key="insertLink"]');
const linkItem = link.parentElement;
linkItem.classList.add("toolbar-item", "flexacenter");
link.innerHTML = '
链接';
const bold = toolbarRef.value.querySelector('[data-menu-key="bold"]');
const boldItem = bold.parentElement;
boldItem.classList.add("toolbar-item", "flexacenter");
bold.innerHTML = '
粗体';
const justifyCenter = toolbarRef.value.querySelector('[data-menu-key="justifyCenter"]');
const justifyCenterItem = justifyCenter.parentElement;
justifyCenterItem.classList.add("toolbar-item", "flexacenter");
justifyCenter.innerHTML = '
居中';
});
};
const restoreHtml = (formattedText, attachments) => {
const imageList = attachments?.images || [];
const filesList = attachments?.files || [];
const videosList = attachments?.videos || [];
let html = formattedText;
// 1. 还原换行符为
标签
html = html.replace(/\n/g, "
");
// 2. 还原块级标签的换行标记
html = html.replace(/
/g, "
");
html = html.replace(/<\/div>
/g, "
");
// 3. 还原标签标记为span.blue
html = html.replace(/\[tag\]([^[]+)\[\/tag\]/gi, '
#$1 ');
// 4. 还原粗体标记为h2标签
html = html.replace(/\[b\]([\s\S]*?)\[\/b\]/gi, "
$1
");
// 5. 还原图片标记为img标签(使用提供的imageList)
html = html.replace(/\[attachimg\](\d+)\[\/attachimg\]/gi, (match, aid) => {
// 查找对应的图片信息
const image = imageList.find((img) => img.aid == aid);
if (image) {
imageList.splice(imageList.indexOf(image), 1);
return `

`;
}
return match; // 未找到对应图片时保留原始标记
});
html = html.replace(/\[attach\](\d+)\[\/attach\]/gi, (match, aid) => {
// 查找对应的图片信息
const image = imageList.find((img) => img.aid == aid);
if (image) {
imageList.splice(imageList.indexOf(image), 1);
return `

`;
}
// 查找对应的视频信息
const video = videosList.find((v) => v.aid == aid);
if (video) {
console.log("video", video);
videosList.splice(videosList.indexOf(video), 1);
return `
`;
}
return match; // 未找到对应图片时保留原始标记
});
// 6. 还原填充标签
html = html.replace(/(
[^<]+<\/span>)\s+/gi, '$1 ');
// 7. 清理多余的
标签
// html = html.replace(/
/g, "
");
imageList.forEach((element) => {
html += `
`;
});
// video 不要预加载
videosList.forEach((element) => {
html += `
`;
});
return html;
};
onMounted(() => {
setTimeout(() => focusLastNode(), 1000);
// document.addEventListener("keydown", handleUndoKeydown);
});
const editorRef = ref(null);
const focusLastNode = () => {
return;
const newRange = document.createRange();
const textNode = document.createTextNode("");
editorRef.value.appendChild(textNode);
newRange.setStartAfter(textNode, 0);
newRange.setEndAfter(textNode, 0);
lastSelection = newRange;
};
let lastSelection = null;
let lastSelectionW = null;
const isH1 = ref(false);
const updateWHeadingStatus = () => {
const wRoot = document.querySelector("#editor-container");
let node = null;
try {
const DomEditor = window.wangEditor && window.wangEditor.DomEditor;
if (DomEditor && editor) node = DomEditor.getSelectionNode(editor);
} catch (e) {}
if (!node) {
const sel = window.getSelection();
if (sel && sel.rangeCount) node = sel.getRangeAt(0).commonAncestorContainer;
}
let el = node && node.nodeType === 3 ? node.parentElement : node;
while (el && el !== wRoot && el && el.nodeType === 1) {
if (window.getComputedStyle(el).getPropertyValue("text-align") === "center") {
console.log("居中");
const justifyCenter = toolbarRef.value.querySelector('[data-menu-key="justifyCenter"]');
if (justifyCenter) {
const justifyCenterItem = justifyCenter.parentElement;
justifyCenterItem.classList.add("active");
}
break;
}
el = el.parentElement;
}
};
let loading = ref(false);
const maxSize = 20 * 1024 * 1024; // 20MB
const insertImage = (event) => {
const images = extractImages(editorRef.value);
const count = imageLength - images.length || 0;
if (count == 0) {
creationAlertBox("error", `最多只能上传 ${imageLength} 张图片`);
return;
}
const target = event.target.files[0];
if (!target) return; // 处理未选择文件的情况
if (target.size > maxSize) {
creationAlertBox("error", "文件大小不能超过 20MB");
return;
}
loading.value = true;
uploading(target, target.name, "image").then((data) => {
const selection = window.getSelection();
editorRef.value.focus();
if (lastSelection) {
selection.removeAllRanges();
selection.addRange(lastSelection);
}
const html = `
`;
document.execCommand("insertHTML", false, html);
judgeIsEmpty();
});
};
const insertVideo = async (event) => {
const videos = extractVideos(editorRef.value);
const count = videoLength - videos.length || 0;
if (count == 0) {
creationAlertBox("error", `最多只能上传 ${videoLength} 个视频`);
return;
}
const videoFile = event.target.files[0];
if (!videoFile) return; // 处理未选择文件的情况
if (videoFile.size > maxSize) {
creationAlertBox("error", "文件大小不能超过 20MB");
return;
}
loading.value = true;
console.log("videoFile", videoFile);
// 步骤1:提取视频第一帧(等待提取完成)
const coverFile = await getVideoFirstFrame(videoFile);
console.log("第一帧提取成功", coverFile);
// 步骤2:先上传视频文件(type 传 'video',按后端要求调整)
const videoUploadRes = await uploading(videoFile, videoFile.name, "video");
console.log("视频上传成功", videoUploadRes);
// 步骤3:再上传第一帧封面(type 传 'cover',按后端要求调整)
const coverUploadRes = await uploading(coverFile, coverFile.name, "image");
console.log("封面上传成功", coverUploadRes);
console.log("最终", videoUploadRes, videoUploadRes);
const selection = window.getSelection();
editorRef.value.focus();
if (lastSelection) {
selection.removeAllRanges();
selection.addRange(lastSelection);
}
const html = `
`;
document.execCommand("insertHTML", false, html);
judgeIsEmpty();
};
let isEmpty = ref(true);
const onEditorInput = (event) => {
const selection = window.getSelection();
if (selection.rangeCount > 0) {
lastSelection = selection.getRangeAt(0);
// console.log("更新选区");
updatePTitleStatus();
}
judgeIsEmpty();
// debouncedGetTagList();
};
// 防抖函数
const debounce = (fn, delay = 500) => {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
};
};
const getRandomChinese = () => {
// 中文 Unicode 范围:\u4e00 - \u9fa5(共约 2 万个汉字)
const start = 0x4e00; // 起始编码
const end = 0x9fa5; // 结束编码
// 生成范围内的随机整数,转为字符
return String.fromCodePoint(Math.floor(Math.random() * (end - start + 1) + start));
};
const generateRandomString = (length = 5) => {
// 定义字符集:包含大小写字母和数字
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
// 循环生成指定长度的随机字符
for (let i = 0; i < length; i++) {
// 从字符集中随机取一个字符
const randomIndex = Math.floor(Math.random() * chars.length);
result += chars[randomIndex];
}
return result;
};
const getTagList = () => {
if (!isLogin.value) {
goLogin();
return;
}
const content = editorRef.value.innerText;
ajax("/v2/api/forum/postPublishTags", {
content,
}).then((res) => {
res = res.data;
if (res.code != 200) return;
let data = res.data || [];
// 随机生成一下数据
for (let i = 0; i < 5; i++) {
data.push({
title: getRandomChinese() + getRandomChinese(),
tagId: generateRandomString(),
});
}
tagList.value = data;
});
};
const debouncedGetTagList = debounce(getTagList, 500);
let isBottomState = ref(false); // 底部按钮 显示
const onEditorFocus = () => {
isBottomState.value = true;
};
const onEditorBlur = () => {
isBottomState.value = false;
};
// 判断是否为空
const judgeIsEmpty = () => {
const text = editorRef.value.innerText;
isEmpty.value = text.length == 0 && !editorRef.value.querySelector("img") && !editorRef.value.querySelector("video");
};
// 处理选中文本变化的函数
const handleSelectionChange = () => {
return;
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
const commonAncestor = range.commonAncestorContainer;
if (editorRef.value.contains(commonAncestor)) {
lastSelection = range;
}
const wRoot = document.querySelector("#editor-container");
if (wRoot && wRoot.contains(commonAncestor)) {
lastSelectionW = range;
updateWHeadingStatus();
}
}
};
let isPTitle = ref(false);
const paragraphTitle = () => {
console.log("editor", editor.addMark);
// editor.addMark("bold", true); // 加粗
// editorRef.value.focus();
// if (!lastSelection) return;
// const selection = window.getSelection();
// selection.removeAllRanges();
// selection.addRange(lastSelection);
// // 使用try-catch确保即使命令执行失败也能恢复滚动位置
// try {
// document.execCommand("formatBlock", false, isPTitle.value ? "P" : "H2");
// } catch (error) {
// console.error("应用段落格式失败:", error);
// }
// // 更新状态
// setTimeout(() => updatePTitleStatus(), 100);
};
const updatePTitleStatus = () => {
if (lastSelection) {
let parentElement = lastSelection.commonAncestorContainer;
// 死循环,直到遇到终止条件
while (true) {
// 如果没有父元素了(到达文档根节点),退出循环返回false
if (!parentElement) {
isPTitle.value = false;
return;
}
// 遇到id为"editor"的元素,返回false
if (parentElement.id === "editor") {
isPTitle.value = false;
return;
}
// 遇到nodeName为"H2"的元素,返回true(注意nodeName是大写的)
if (parentElement.nodeName === "H2") {
isPTitle.value = true;
return;
}
// 继续向上查找父元素
parentElement = parentElement.parentElement;
}
}
};
const cutAnonymity = () => (info.value.anonymous = info.value.anonymous ? 0 : 1);
let emojiState = ref(false);
const optionEmoji = ref(["😀", "😁", "😆", "😅", "😂", "😉", "😍", "🥰", "😘", "🤥", "😪", "😵💫", "🤓", "🥺", "😋", "😜", "🤪", "😎", "🤩", "🥳", "😔", "🙁", "😭", "😡", "😳", "🤗", "🤔", "🤭", "🤫", "😯", "😵", "🙄", "🥴", "🤢", "🤑", "🤠", "👌", "✌️", "🤟", "🤘", "🤙", "👍", "👎", "✊", "👏", "🤝", "🙏", "💪", "❎️", "✳️", "✴️", "❇️", "#️⃣", "*️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟", "🆗", "🈶", "🉐", "🉑", "🌹", "🥀", "🌸", "🌺", "🌷", "🌲", "☘️", "🍀", "🍁", "🌙", "⭐", "🌍", "☀️", "⭐️", "🌟", "☁️", "🌈", "☂️", "❄️", "☃️", "☄️", "🔥", "💧", "🍎", "🍐", "🍊", "🍉", "🍓", "🍑", "🍔", "🍟", "🍕", "🥪", "🍜", "🍡", "🍨", "🍦", "🎂", "🍰", "🍭", "🍿", "🍩", "🧃", "🍹", "🍒", "🥝", "🥒", "🥦", "🥨", "🌭", "🥘", "🍱", "🍢", "🥮", "🍩", "🍪", "🧁", "🍵", "🍶", "🍻", "🥂", "🧋", "🎉", "🎁", "🧧", "🎃", "🎄", "🧨", "✨️", "🎈", "🎊", "🎋", "🎍", "🎀", "🎖️", "🏆️", "🏅", "💌", "📬", "🚗", "🚕", "🚲", "🛵", "🚀", "🚁", "⛵", "🚢", "🔮", "🧸", "🀄️"]);
const openEmoji = () => (emojiState.value = true);
const closeEmoji = () => (emojiState.value = false);
const selectEmoji = (emoji) => {
const selection = window.getSelection();
editorRef.value.focus();
if (lastSelection) {
selection.removeAllRanges();
selection.addRange(lastSelection);
}
document.execCommand("insertText", false, emoji);
closeEmoji();
judgeIsEmpty();
};
let format = ref("");
const submit = (status) => {
const infoTarget = { ...info.value } || {};
let content = editorRef.value.innerHTML;
const images = extractImages(editorRef.value);
const videos = extractVideos(editorRef.value);
infoTarget.attachments = infoTarget.attachments || {};
infoTarget.attachments.images = images;
infoTarget.attachments.videos = videos;
info.value["attachments"] = info.value["attachments"] || {};
info.value["attachments"]["images"] = images;
info.value["attachments"]["videos"] = videos;
console.log(content);
content = formatContent(content);
console.log(content);
return;
const data = {
...infoTarget,
content,
};
ajax("/v2/api/forum/postPublishTopic", {
info: data,
token: token.value,
status,
}).then((res) => {
const data = res.data;
if (res.code != 200) {
creationAlertBox("error", res.message);
return;
}
creationAlertBox("success", res.message || "操作成功");
const back = () => {
if (status == 1) redirectToExternalWebsite("/details/" + data.uniqid, "_self");
else redirectToExternalWebsite("/", "_self");
};
setTimeout(() => back(), 1500);
});
};
const formatContent = (html) => {
// 1. 替换图片标签
html = html.replace(/
]*data-aid="(\d+)"[^>]*>/gi, "[attachimg]$1[/attachimg]");
// 1.1 替换视频标签
html = html.replace(/