// 简单版本的论坛编辑器,确保图片插入功能正常
const { createApp, ref, computed, onMounted, nextTick, onUnmounted } = Vue;
const { headTop } = await import(withVer("../component/head-top/head-top.js"));
const { createEditor, createToolbar, SlateTransforms, Boot, SlateEditor } = window.wangEditor;
class MyButtonMenu {
// JS 语法
constructor() {
this.title = "居中"; // 自定义菜单标题
this.tag = "button";
}
// 获取菜单执行时的 value ,用不到则返回空 字符串或 false
getValue(editor) {
return " hello ";
}
// 菜单是否需要激活(如选中加粗文本,“加粗”菜单会激活),用不到则返回 false
isActive(editor) {
if (editor.getFragment()?.[0]?.textAlign == "center") return true;
return false;
}
// 菜单是否需要禁用(如选中 H1 ,“引用”菜单被禁用),用不到则返回 false
isDisabled(editor) {
return false;
}
// 点击菜单时触发的函数
exec(editor, value) {
let align = this.isActive(editor) ? "left" : "center";
SlateTransforms.setNodes(editor, {
textAlign: align,
});
}
}
const editApp = createApp({
setup() {
let titleLength = ref(200);
let uniqid = ref("");
const valueA = ref(null);
let valueUrl = "";
onMounted(() => {
const params = getUrlParams();
uniqid.value = params.uniqid || "";
getUserInfoWin();
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;
let videoLength = 5;
const checkWConfig = () => {
const wConfig = JSON.parse(localStorage.getItem("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;
init();
});
};
let info = ref({});
let tagList = ref([]);
let token = ref("");
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 (location.hostname == "127.0.0.1")
// infoTarget.content = `

//
//
`;
console.log("content", infoTarget.content);
if (infoTarget.content) infoTarget.content = restoreHtml(infoTarget.content, infoTarget.attachments);
console.log("content", infoTarget.content);
info.value = infoTarget;
token.value = data.token;
console.log("data", data);
initEditor();
})
.catch((err) => {
console.log("err", err);
});
};
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 ``;
// }
return src;
}
const initEditor = () => {
let infoTarget = info.value || {};
const editorConfig = {
placeholder: "输入正文",
enabledMenus: [],
MENU_CONF: {
["emotion"]: {
emotions: optionEmoji.value,
},
["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) {
// 如果图片链接中已经包含了 ?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,
fieldName: uConfigData.requestName,
maxFileSize: maxSize, // 1M
maxNumberOfFiles: imageLength,
allowedFileTypes: ["image/png", "image/jpeg", "image/jpg"], // .png, .jpg, .jpeg
meta: { ...uConfigData.params },
metaWithUrl: false,
headers: { accept: "application/json, text/plain, */*", ...uConfigData.headers },
withCredentials: true,
timeout: 60 * 1000, // 15 秒
async customUpload(file, insertFn) {
try {
const img = await uploading(file, file.name, "image");
insertFn(`${img.url}?aid=${img.aid}`);
} catch (err) {
console.error("上传出错:", err);
}
},
},
["insertVideo"]: {
onInsertedVideo(videoNode) {
if (videoNode == null) return;
// console.log(videoNode);
const { src } = videoNode;
// console.log("inserted video", src);
},
parseVideoSrc: customParseVideoSrc, // 也支持 async 函数
},
["uploadVideo"]: {
server: uConfigData.url,
fieldName: uConfigData.requestName,
maxFileSize: maxSize, // 1M
maxNumberOfFiles: videoLength,
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: 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);
const coverUploadRes = await uploading(coverFile, coverFile.name, "image");
// console.log("封面上传成功", coverUploadRes);
insertFn(`${videoUploadRes.url}?aid=${videoUploadRes.aid}`, `${coverUploadRes.url}?aid=${coverUploadRes.aid}`);
} catch (err) {
console.error("上传出错:", err);
progress.value = 0;
}
},
},
},
onChange(editor) {
const html = editor.getHtml();
console.log("edior content", html);
},
hoverbarKeys: { text: { menuKeys: [] }, video: { menuKeys: [] } },
};
editor = createEditor({
selector: "#editor-container",
html: infoTarget.content,
config: editorConfig,
mode: "default",
});
const toolbarConfig = {
toolbarKeys: [
"header1",
{
key: "group-image",
title: "图片",
menuKeys: ["insertImage", "uploadImage"],
},
"uploadVideo",
"emotion",
"insertLink",
"bold",
],
};
const menu1Conf = {
key: "customCenter", // 定义 menu key :要保证唯一、不重复(重要)
factory() {
return new MyButtonMenu(); // 把 `YourMenuClass` 替换为你菜单的 class
},
};
Boot.registerMenu(menu1Conf);
toolbarConfig.insertKeys = {
index: 7, // 插入的位置,基于当前的 toolbarKeys
keys: ["customCenter"],
};
const toolbar = createToolbar({
editor,
selector: "#toolbar-container",
config: toolbarConfig,
mode: "default",
});
nextTick(() => {
const h1 = toolbarRef.value.querySelector('[data-menu-key="header1"]');
const h1Item = h1.parentElement;
h1Item.classList.add("toolbar-item", "flexacenter");
h1.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="uploadVideo"]');
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 customCenter = toolbarRef.value.querySelector('[data-menu-key="customCenter"]');
const customCenterItem = customCenter.parentElement;
customCenterItem.classList.add("toolbar-item", "flexacenter");
customCenter.innerHTML = `
居中`;
});
};
const restoreHtml = (formattedText, attachments) => {
const imageList = attachments?.images || [];
const filesList = attachments?.files || [];
const videosList = attachments?.videos || [];
let html = formattedText;
// 0. 将所有 转为
html = html.replace(/