feat: 新增发布主题页面及编辑器功能

refactor(css): 优化详情页样式并移除冗余代码
feat(js): 实现发布主题的编辑器功能及图片/视频上传
docs: 添加编辑器样式文件及发布页面HTML结构
This commit is contained in:
DESKTOP-RQ919RC\Pc
2025-12-11 19:11:43 +08:00
parent 6ce06b133a
commit 620d21dd5d
12 changed files with 26127 additions and 36 deletions

View File

@@ -155,22 +155,15 @@
#details .matter .matter-left .html {
padding: 0 30px;
font-family: "PingFangSC-Regular", "PingFang SC", sans-serif;
font-size: 15px;
font-size: 18px;
color: #555555;
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 !important;
}
#details .matter .matter-left .html a * {
color: #04b0d5 !important;
}
#details .matter .matter-left .html .blue {
font-size: 15px;
line-height: 26px;
@@ -192,9 +185,11 @@
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
font-weight: 650;
color: #000000 !important;
font-size: 18px;
line-height: 30px;
}
#details .matter .matter-left .html p {
margin: 15px 0;
}
#details .matter .matter-left .last-time {
color: #aaaaaa;
font-size: 13px;

View File

@@ -182,23 +182,24 @@
.html {
padding: 0 30px;
font-family: "PingFangSC-Regular", "PingFang SC", sans-serif;
font-size: 15px;
// font-size: 15px;
font-size: 18px;
color: #555555;
line-height: 26px;
margin-bottom: 66px;
* {
background: transparent !important;
color: #555555 !important;
}
// * {
// background: transparent !important;
// // color: #555555 !important;
// }
a {
text-decoration: underline;
color: #04b0d5 !important;
* {
color: #04b0d5 !important;
}
// * {
// color: #04b0d5 !important;
// }
}
.blue {
@@ -225,9 +226,13 @@
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
font-weight: 650;
color: #000000 !important;
font-size: 18px;
// font-size: 18px;
line-height: 30px;
}
p {
margin: 15px 0;
}
}
.last-time {
@@ -1635,7 +1640,7 @@
overflow: auto;
border: none;
padding-top: 15px;
&::after {
display: none;
}

27
css/editorStyle.css Normal file

File diff suppressed because one or more lines are too long

24
css/index1.css Normal file

File diff suppressed because one or more lines are too long

1230
css/katex.css Normal file

File diff suppressed because it is too large Load Diff

2
css/normalize.min.css vendored Normal file
View File

@@ -0,0 +1,2 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
/*# sourceMappingURL=normalize.min.css.map */

1
css/textbus.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -13,13 +13,16 @@ const getScriptParameter = (paramName) => {
return null;
};
// 判断是否已经创建了v参数
const vParam = getScriptParameter("v");
// 导出ajax函数
const ajax = (url, data) => {
axios.defaults.withCredentials = true;
axios.defaults.emulateJSON = true;
url = url.indexOf("https://") > -1 ? url : forumBaseURL + url;
if (data) data["v"] = getScriptParameter("v") || "v2";
if (data) data["v"] = vParam || "v2";
return new Promise(function (resolve, reject) {
if (location.hostname == "127.0.0.1") axios.defaults.headers.common["Authorization"] = "n1pstcsmw6m6bcx49z705xhvduqviw29";
@@ -51,7 +54,7 @@ const ajaxdelete = (url, data) => {
url = url.indexOf("https://") > -1 ? url : forumBaseURL + url;
return new Promise(function (resolve, reject) {
if (data) data["v"] = getScriptParameter("v") || "v2";
if (data) data["v"] = vParam || "v2";
axios
.delete(url, {
emulateJSON: true,
@@ -83,7 +86,7 @@ const ajaxGet = (url) => {
url = url.indexOf("https://") > -1 ? url : forumBaseURL + url;
const paramSymbol = url.includes("?") ? "&" : "?";
url = `${url}${paramSymbol}v=${getScriptParameter("v") || "v2" }`;
url = `${url}${paramSymbol}v=${vParam || "v2"}`;
return new Promise(function (resolve, reject) {
if (location.hostname == "127.0.0.1") axios.defaults.headers.common["Authorization"] = "n1pstcsmw6m6bcx49z705xhvduqviw29";
@@ -107,6 +110,7 @@ const ajaxGet = (url) => {
resolve(data);
})
.catch((error) => {
if (error?.status == 401) go_ajax_Login();
reject(error);
});
});
@@ -419,17 +423,3 @@ const go_ajax_Login = () => {
if (typeof ajax_login === "function") ajax_login();
else window.open("https://passport.gter.net/?referer=" + escape(location.href), "_self");
};
// const loadJsFile = (url) => {
// var xhr = new XMLHttpRequest();
// xhr.open("GET", url, true);
// xhr.onreadystatechange = function () {
// if (xhr.readyState === 4 && xhr.status === 200) {
// var scriptCode = xhr.responseText;
// var script = document.createElement("script");
// script.innerHTML = scriptCode;
// document.head.appendChild(script);
// }
// };
// xhr.send();
// };

448
js/publish_admin.js Normal file
View File

@@ -0,0 +1,448 @@
// 简单版本的论坛编辑器,确保图片插入功能正常
const { createApp, ref, computed, onMounted, nextTick, onUnmounted } = Vue;
const { createEditor, createToolbar, SlateTransforms, Boot, SlateEditor } = window.wangEditor;
const editApp = createApp({
setup() {
const E = window.wangEditor;
const LANG = location.href.indexOf("lang=en") > 0 ? "en" : "zh-CN";
E.i18nChangeLanguage(LANG);
const title = ref("");
const saveStatus = ref("");
const uniqid = ref("");
const info = ref({});
const token = ref("");
let editor = null;
let toolbar = null;
const draftKey = "publish_admin_draft";
let uConfigData = {};
let imageLength = 10;
let videoLength = 5;
const formatTime = (d) => {
const pad = (n) => String(n).padStart(2, "0");
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
};
const extractImages = (dom) => {
const images = [];
const imgElements = dom.querySelectorAll("img");
imgElements.forEach((imgEl) => {
let url = imgEl.getAttribute("src")?.trim() || "";
const urlObj = new URL(url);
const aid = urlObj.searchParams.get("aid");
const queryIndex = url.indexOf("?");
const cleanUrl = queryIndex !== -1 ? url.substring(0, queryIndex) : url;
if (Number(aid)) {
images.push({
url: cleanUrl,
aid: Number(aid),
});
}
});
return images;
};
const extractVideos = (dom) => {
const videoElements = dom.querySelectorAll("video");
const result = [];
videoElements.forEach((videoEl) => {
const posterurl = videoEl.getAttribute("poster")?.trim() || ""; // 视频地址
const urlObj = new URL(posterurl);
const posterid = urlObj.searchParams.get("aid");
const sourceEl = videoEl.querySelector("source");
const url = sourceEl.getAttribute("src") || null;
const obj = new URL(url);
const aid = obj.searchParams.get("aid");
const queryIndex = url.indexOf("?");
const cleanUrl = queryIndex !== -1 ? url.substring(0, queryIndex) : url;
const queryIndex2 = posterurl.indexOf("?");
const cleanPosterurl = queryIndex2 !== -1 ? posterurl.substring(0, queryIndex2) : posterurl;
result.push({
aid: Number(aid),
posterid: Number(posterid),
url: cleanUrl,
posterurl: cleanPosterurl,
});
});
return result;
};
const cutAnonymity = () => (info.value.anonymous = info.value.anonymous ? 0 : 1);
// 提交
const submit = (status) => {
const infoTarget = { ...info.value } || {};
let content = editor.getHtml();
const editorDom = document.getElementById("editor-text-area");
const images = extractImages(editorDom);
const videos = extractVideos(editorDom);
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;
infoTarget.title = title.value;
const data = {
...infoTarget,
content,
};
ajax("/v2/api/forum/postPublishTopic", {
info: data,
token: token.value,
status,
htmledit: 1,
}).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 cUpload = () => {
ajaxGet(`/v2/api/config/upload?type=topic`).then((res) => {
const data = res.data;
uConfigData = data;
});
};
const init = () => {
ajax("/v2/api/forum/postPublishInit", {
uniqid: uniqid.value,
htmledit: 1,
})
.then((res) => {
const data = res.data;
if (res.code != 200) {
creationAlertBox("error", res.message || "操作失败");
return;
}
const infoTarget = data.info || {};
// if (infoTarget.content) infoTarget.content = `<div style=\"text-align: center;\"><strong>2026年度研究生课程火热招生中!</strong></div><div>\n<strong>2026</strong>年度研究生课程火热招生中!</div>`
info.value = infoTarget;
token.value = data.token;
if (infoTarget.title) title.value = infoTarget.title;
nextTick(() => {
initEditor();
});
})
.catch((err) => {
console.log("err", err);
});
};
// 上传图片/视频 获取url
const uploading = (file, name, type) => {
return new Promise((resolve, reject) => {
const upload = () => {
let config = uConfigData;
const formData = new FormData();
formData.append(config.requestName, file); // 文件数据
formData.append("name", name); // 文件名
formData.append("type", type); // 文件名
if (config.params && config.params.data) {
formData.append("data", config.params.data);
}
const xhr = new XMLHttpRequest();
xhr.open("POST", config.url, true);
xhr.withCredentials = true; // 允许携带 Cookie
// 监听上传进度
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
// const percentComplete = (event.loaded / event.total) * 100;
// progress.value = Math.round(percentComplete);
}
};
xhr.onload = function () {
if (xhr.status === 200) {
const res = JSON.parse(xhr.responseText);
if (res.code == 200) {
const data = res.data;
resolve(data);
} else {
creationAlertBox("error", res.message || "上传失败");
reject(res);
}
} else {
creationAlertBox("error", "上传失败");
reject(new Error("Upload failed"));
}
};
xhr.onerror = function () {
creationAlertBox("error", "网络错误,上传失败");
reject(new Error("Network error"));
};
xhr.send(formData);
};
if (!uConfigData || !uConfigData.url) {
ajaxGet(`/v2/api/config/upload?type=topic`).then((res) => {
const data = res.data;
uConfigData = data;
upload();
});
} else {
upload();
}
});
};
const initEditor = () => {
const maxSize = 1 * 1024 * 1024; // 1M
const editorConfig = {
// 自定义 HTML 处理规则
htmlFilter: {
// 保留 img 标签(核心)
tags: {
img: true, // true 表示不过滤该标签,也可配置具体属性白名单
},
// 更精细控制:指定 img 允许的属性(避免过滤关键属性)
attrs: {
img: ["src", "alt", "width", "height", "data-id", "class"], // 按需添加自定义属性
},
},
// 关闭「粘贴/插入 HTML 时自动清理空标签」(若 img 暂未赋值 src 可能触发)
autoFormatAfterInit: false,
autoFormatOnEditable: false,
placeholder: "输入正文",
// scroll: true, // 禁止编辑器滚动
MENU_CONF: {
["emotion"]: {
emotions: ["😀", "😁", "😆", "😅", "😂", "😉", "😍", "🥰", "😘", "🤥", "😪", "😵‍💫", "🤓", "🥺", "😋", "😜", "🤪", "😎", "🤩", "🥳", "😔", "🙁", "😭", "😡", "😳", "🤗", "🤔", "🤭", "🤫", "😯", "😵", "🙄", "🥴", "🤢", "🤑", "🤠", "👌", "✌️", "🤟", "🤘", "🤙", "👍", "👎", "✊", "👏", "🤝", "🙏", "💪", "❎️", "✳️", "✴️", "❇️", "#️⃣", "*️⃣", "1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟", "🆗", "🈶", "🉐", "🉑", "🌹", "🥀", "🌸", "🌺", "🌷", "🌲", "☘️", "🍀", "🍁", "🌙", "⭐", "🌍", "☀️", "⭐️", "🌟", "☁️", "🌈", "☂️", "❄️", "☃️", "☄️", "🔥", "💧", "🍎", "🍐", "🍊", "🍉", "🍓", "🍑", "🍔", "🍟", "🍕", "🥪", "🍜", "🍡", "🍨", "🍦", "🎂", "🍰", "🍭", "🍿", "🍩", "🧃", "🍹", "🍒", "🥝", "🥒", "🥦", "🥨", "🌭", "🥘", "🍱", "🍢", "🥮", "🍩", "🍪", "🧁", "🍵", "🍶", "🍻", "🥂", "🧋", "🎉", "🎁", "🧧", "🎃", "🎄", "🧨", "✨️", "🎈", "🎊", "🎋", "🎍", "🎀", "🎖️", "🏆️", "🏅", "💌", "📬", "🚗", "🚕", "🚲", "🛵", "🚀", "🚁", "⛵", "🚢", "🔮", "🧸", "🀄️"],
},
["insertImage"]: {
onInsertedImage(imageNode) {
const { src, alt, url, href } = imageNode;
},
async parseImageSrc(src) {
console.log("parseImageSrc");
// 如果图片链接中已经包含了 ?aid= ,则说明是本站图片,直接返回,无需处理
if (src.includes("?aid=")) return src;
console.log("parseImageSrc");
// 对于不含 ?aid= 的外部图片,执行上传转换
console.log("uConfigData", uConfigData);
if (!uConfigData || !uConfigData.url) return src;
console.log("parseImageSrc");
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 || "操作失败");
return "";
}
} catch (e) {
console.error("Transform network image failed", e);
return "";
}
},
checkImage: (src, alt, url) => {
if (src.indexOf("http") !== 0) {
return "图片网址必须以 http/https 开头";
}
return true;
}, // 也支持 async 函数
},
["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");
console.log("img", img);
insertFn(`${img.url}?aid=${img.aid}`);
} catch (err) {
console.error("上传出错:", err);
}
},
},
["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");
const coverFile = await getVideoFirstFrame(file);
const coverUploadRes = await uploading(coverFile, coverFile.name, "image");
insertFn(`${videoUploadRes.url}?aid=${videoUploadRes.aid}`, `${coverUploadRes.url}?aid=${coverUploadRes.aid}`);
} catch (err) {
console.error("上传出错:", err);
}
},
},
},
onChange(editor) {
// console.log(editor.getHtml());
saveStatus.value = "有未保存的更改";
},
hoverbarKeys: { text: { menuKeys: [] }, video: { menuKeys: [] } },
};
try {
editor = E.createEditor({
selector: "#editor-text-area",
// content: [],
html: info.value?.content || "",
// html: `<div style="text-align: center;"><img src="https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_pstfX-G1NFX9ddrB_WbUGy8P79gQxdCR7TG75gV7NkzNDQyOQ~~?aid=1011800" /></div>`,
config: editorConfig,
});
} catch (error) {
console.log("error", error);
}
// 如果有远程数据,使用远程数据
// if (info.value && info.value.content) {
// editor.setHtml(info.value.content);
// } else {
// // 恢复草稿 (仅在没有远程数据时)
// const cache = localStorage.getItem(draftKey);
// if (cache) {
// try {
// const data = JSON.parse(cache);
// if (data && (data.title || data.content)) {
// if (data.title) title.value = data.title;
// if (data.content) editor.setHtml(data.content);
// if (data.updatedAt) {
// saveStatus.value = `已保存 ${formatTime(new Date(data.updatedAt))}`;
// }
// }
// } catch (_) {}
// }
// }
const toolbarConfig = {
excludeKeys: ["insertVideo", "fullScreen"],
};
toolbar = E.createToolbar({
editor,
selector: "#editor-toolbar",
config: toolbarConfig,
});
// 点击空白处 focus 编辑器
document.getElementById("editor-text-area").addEventListener("click", (e) => {
if (e.target.id === "editor-text-area") {
editor.blur();
editor.focus(true); // focus 到末尾
}
});
};
// 提取视频第一帧作为封面
const getVideoFirstFrame = (file) => {
return new Promise((resolve) => {
const video = document.createElement("video");
video.src = URL.createObjectURL(file);
video.currentTime = 1; // 截取第 1 秒
video.onloadeddata = () => {
video.currentTime = 1;
};
video.onseeked = () => {
const canvas = document.createElement("canvas");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext("2d").drawImage(video, 0, 0, canvas.width, canvas.height);
canvas.toBlob((blob) => {
const coverFile = new File([blob], "cover.jpg", { type: "image/jpeg" });
resolve(coverFile);
}, "image/jpeg");
};
});
};
const handleTitleInput = () => {
saveStatus.value = "有未保存的更改";
};
onMounted(() => {
const params = getUrlParams();
uniqid.value = params.uniqid || "";
cUpload();
nextTick(() => {
init();
});
});
onUnmounted(() => {
if (editor == null) return;
editor.destroy();
editor = null;
});
return {
title,
saveStatus,
submit,
handleTitleInput,
cutAnonymity,
info,
};
},
});
editApp.mount("#edit");

2
js/textbus.min.js vendored Normal file

File diff suppressed because one or more lines are too long

24129
js/wangeditor.js Normal file

File diff suppressed because one or more lines are too long

238
publish_admin.html Normal file
View File

@@ -0,0 +1,238 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>发布主题</title>
<link href="https://framework.x-php.com/gter/forum/css/normalize.min.css" rel="stylesheet">
<link href="https://framework.x-php.com/gter/forum/css/editorStyle.css" rel="stylesheet">
<script src="https://framework.x-php.com/gter/forum/js/vue.global.js"></script>
<link rel="stylesheet" href="/css/textbus.min.css">
<script src="/js/textbus.min.js"></script>
<style>
html,
body {
background-color: #fff;
height: 100%;
overflow: hidden;
color: #333;
}
#top-container {
border-bottom: 1px solid #e8e8e8;
padding-left: 30px;
}
#editor-toolbar {
width: 1350px;
background-color: #FCFCFC;
margin: 0 auto;
}
#content {
height: calc(100% - 40px);
background-color: rgb(245, 245, 245);
overflow-y: auto;
position: relative;
}
#editor-container {
/* width: 850px;
margin: 30px auto 150px auto;
background-color: #fff;
padding: 20px 50px 10px 50px;
border: 1px solid #e8e8e8;
box-shadow: 0 2px 10px rgb(0 0 0 / 12%); */
width: 100vh;
margin: 20px auto 20px auto;
background-color: #fff;
padding: 10px;
border: 1px solid #e8e8e8;
box-shadow: 0 2px 10px rgb(0 0 0 / 12%);
}
#title-container {
padding: 20px 0;
border-bottom: 1px solid #e8e8e8;
}
#title-container input {
font-size: 30px;
border: 0;
outline: none;
width: 100%;
line-height: 1;
}
#editor-text-area {
margin-top: 20px;
/* height: 500px; */
/* max-height: 80vh; */
height: calc(100vh - 370px);
font-size: 18px;
line-height: 1.5;
color: rgb(51, 51, 51);
}
.bottom-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-top: 1px solid #e8e8e8;
padding: 10px 20px;
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
z-index: 1000;
}
.btn {
padding: 8px 16px;
border: 1px solid #d9d9d9;
background: #fafafa;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.btn-primary {
background: #1890ff;
border-color: #1890ff;
color: #fff;
}
.save-status {
font-size: 12px;
color: #888;
}
.action-buttons {
border-top: 1px solid #ebebeb;
padding: 0 36px;
justify-content: space-between;
}
.action-buttons .left-section {
font-size: 14px;
color: #333;
cursor: pointer;
}
.action-buttons .left-section .icon-pitch {
width: 16px;
height: 16px;
margin-right: 5px;
}
.action-buttons .left-section .icon {
width: 16px;
height: 16px;
border: 1px solid #797979;
margin-right: 5px;
}
.action-buttons .right-section {
height: 80px;
}
.action-buttons .right-section .draft-btn,
.action-buttons .right-section .publish-btn {
font-size: 14px;
border-radius: 8px;
cursor: pointer;
}
.action-buttons .right-section .draft-btn {
width: 100px;
height: 42px;
line-height: 42px;
background-color: rgba(242, 242, 242, 0.69803922);
color: #7f7f7f;
font-size: 14px;
margin-right: 15px;
transition: background-color 0.3s ease;
}
.action-buttons .right-section .draft-btn .icon {
width: 20px;
height: 20px;
margin-right: 6px;
}
.action-buttons .right-section .draft-btn:hover {
background-color: #ebebeb;
}
.action-buttons .right-section .publish-btn {
width: 150px;
height: 40px;
line-height: 40px;
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
font-weight: 650;
font-style: normal;
font-size: 16px;
color: #000000;
background-color: #50e3c2;
transition: background-color 0.3s ease;
}
.action-buttons .right-section .publish-btn:hover {
background-color: #40d1aa;
}
</style>
</head>
<body>
<script src="https://app.gter.net/bottom?tpl=header&menukey=bbs"></script>
<div class="container" id="edit" v-cloak>
<div id="top-container">
<p>
<a href="./">&lt;&lt; 返回</a>
</p>
</div>
<div style="border-bottom: 1px solid #e8e8e8;">
<div id="editor-toolbar"></div>
</div>
<div id="content">
<div id="editor-container">
<div id="title-container">
<input id="title-input" placeholder="Page title..." v-model="title" @input="handleTitleInput">
</div>
<div id="editor-text-area"></div>
</div>
</div>
<!-- <div class="bottom-bar">
<button id="save-btn" class="btn btn-primary" @click="saveDraft">保存</button>
<span id="save-status" class="save-status">{{ saveStatus }}</span>
</div> -->
<div class="bottom-bar action-buttons flexacenter">
<div class="left-section flexacenter" @click="cutAnonymity">
<img v-if="info.anonymous == 1" class="icon-pitch" src="https://framework.x-php.com/gter/forum/img/tick-box.svg" />
<div v-else class="icon"></div>
<div class="text">匿名发布</div>
</div>
<div class="right-section flexcenter">
<div class="draft-btn flexcenter" @click="submit(0)"><img class="icon" src="https://framework.x-php.com/gter/forum/img/draft-icon.png?v=iem44eqj4HfC"> 存草稿 </div>
<div id="save-btn" class="publish-btn flexcenter" @click="submit(1)">保存</div>
</div>
</div>
</div>
<script type="module" src="https://framework.x-php.com/gter/forum/js/editor.js"></script>
<script src="https://framework.x-php.com/gter/forum/js/axios.min.js"></script>
<script src="https://framework.x-php.com/gter/forum/js/public.js"></script>
<script type="module" src="https://framework.x-php.com/gter/forum/js/publish_admin.js"></script>
</body>
</html>