feat(editor): 添加预加载动画和编辑器功能优化

- 新增预加载动画组件及样式
- 优化编辑器图片和视频上传处理逻辑
- 修复编辑器内容转换和格式处理问题
- 添加上传进度显示功能
- 改进编辑器工具栏图标和布局
This commit is contained in:
DESKTOP-RQ919RC\Pc
2025-11-28 18:06:40 +08:00
parent 2a227a806d
commit 0960a310aa
8 changed files with 366 additions and 192 deletions

View File

@@ -1,6 +1,5 @@
const { createApp, ref, onMounted, nextTick, onUnmounted, computed, watch, provide } = Vue;
const ASSET_VERSION = window.__ASSET_VERSION__ || "20251126";
const withVer = (p) => `${p}?v=${ASSET_VERSION}`;
const { itemForum } = await import(withVer("../component/item-forum/item-forum.js"));
const { itemOffer } = await import(withVer("../component/item-offer/item-offer.js"));
const { itemSummary } = await import(withVer("../component/item-summary/item-summary.js"));
@@ -80,6 +79,9 @@ const appSectionIndex = createApp({
let uniqidRef = ref(null);
onMounted(() => {
const preLoader = document.getElementById("pre-loader");
if (preLoader) preLoader.style.display = "none";
uniqid = uniqidRef.value.innerText;
init();
@@ -121,7 +123,7 @@ const appSectionIndex = createApp({
ajaxGet(`/v2/api/forum/getTopicDetails?uniqid=${uniqid}`).then((res) => {
if (res.code != 200) {
creationAlertBox("error", res.message || "主题不存在");
setTimeout(() => redirectToExternalWebsite(`/`), 3000);
// setTimeout(() => redirectToExternalWebsite(`/`), 3000);
return;
}
const data = res.data;
@@ -130,27 +132,6 @@ const appSectionIndex = createApp({
if (!targetInfo.hidden) targetInfo.hidden = 0;
targetInfo.attachments = {
images: [
{
aid: 708161,
url: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-S_pItc37qqsgFptxhXa6RWi26P-BuTQYWFOfCsdkb8LQ0NDI5",
},
],
files: [],
videos: [
{
aid: 1009770,
posterid: 1009849,
posterurl: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_polfXuP1NFX9ddrB_WbUGy8P79gQxdHR-HKts0V7NkzNDQyOQ~~",
url: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_polcniG1NFX9ddrB_WbUGy8P79gQxcSFbqQ78MV7NkzNDQyOQ~~",
},
],
};
// targetInfo.content = '<p style="text-align: center;">红红火火<strong>恍恍惚惚</strong></p>[b]红红火火恍恍惚惚有[/b]<p>\n</p><p>\n</p><p>[attach]1009770[/attach]</p><p>\n</p><p>\n</p><p style="text-align: center;">[img=96]708161[/img]</p><p style="text-align: center;">65456456456456465&nbsp;<a href="11111" target="_blank" contenteditable="false">111</a>&nbsp;</p><p style="text-align: center;">\n</p>';
targetInfo.content = '如果你热爱古典文献,又希望在现代职场大展身手——这个项目可能就是你的“本命”!作为香港最正统的中国语言文学项目,它既传承经典,又为你打跨境传播等全新赛道!\n\n<b>🌟 项目核心亮点</b>权威认证:中国语言文学专业认证,考公考编无障碍\n古今结合深耕古典文献与理论同时对接AI内容创作等新兴领域\n语言友好全程中文授课普通话+粤语),无语言适应压力\n规模可观每年录取150+,机会相对较多\n\n点击前往 [港校项目库] 查看 \n<a href="https://program.gter.net/details/tf1yFYIBSda7Y5k7s9iHeLVSxDiuYTljNA~~" target="_blank" contenteditable="false">中国语言文学</a>\n手机扫码查看\n[attachimg]1008942[/attachimg]\n\n<b>🎯 谁最适合申请?</b>中文系、汉语言、古代文学等对口专业背景\n希望在教育、传媒、AI内容或国际中文教育领域发展\n看重学校声誉与专业正统性的同学\n<b>💼 毕业出路超多元</b>除了教师、公务员等传统路径,毕业生还活跃于:\n✔ 跨境文化传播\n✔ AI内容策划与生成\n✔ 国际中文教育\n✔ 出版与编辑行业\n<b>📌 申请指南</b>专业背景:严格限定中文相关专业,暂不接受跨专业申请\n成绩要求985/211同学建议86+\n语言成绩雅思7.0小分5.5)即可\n面试体验氛围轻松专业问题较少\n<b>💡 内部消息参考</b>前几轮拿到面试邀请的同学基本都能录取\n985背景优势明显建议尽早提交申请\n双非同学如背景特别匹配也可尝试\n<b>🤝 欢迎交流</b>你对中国文学在AI时代的发展有什么想法或者对哪个就业方向申请问题欢迎在评论区分享交流\n欢迎加入寄托香港群交流\n\n[attachimg]969489[/attachimg]';
// 替换换行
targetInfo.content = targetInfo.content?.replace(/\n/g, "<br>") || "";
@@ -330,6 +311,11 @@ const appSectionIndex = createApp({
let isLikeGif = ref(false);
const likeClick = () => {
if (realname.value == 0 && userInfoWin.value?.uin > 0) {
openAttest();
return;
}
if (!isLogin.value) {
goLogin();
return;
@@ -558,6 +544,15 @@ const appSectionIndex = createApp({
const handleAnswerText = (e) => {
if (e.target.tagName === "IMG") {
// 检查点击的图片是否被a标签包裹
const anchorTag = e.target.closest("a");
// 如果被a标签包裹则不执行图片预览让链接正常跳转
if (anchorTag) {
return;
}
// 否则,执行图片预览
var src = e.target.getAttribute("src");
previewImage.initComponent(src);
}
@@ -603,7 +598,6 @@ const appSectionIndex = createApp({
let emojiState = ref(false);
let emojiMaskState = ref(false);
let emojiBottomDistance = ref(0);
let inputTextarea = ref("");
// 打开 Emoji
@@ -622,6 +616,28 @@ const appSectionIndex = createApp({
}
emojiMaskState.value = true;
let emojiBottomDistance = 0;
const doc = document.documentElement;
try {
const targetEl = (event && (event.currentTarget || event.target)) || null;
const rect = targetEl && targetEl.getBoundingClientRect ? targetEl.getBoundingClientRect() : null;
if (rect) {
const elementBottomDocY = rect.bottom + window.scrollY;
emojiBottomDistance = Math.max(doc.scrollHeight - elementBottomDocY, 0);
} else {
const pageY = event && (event.pageY != null ? event.pageY : event.clientY != null ? event.clientY + window.scrollY : 0);
emojiBottomDistance = Math.max(doc.scrollHeight - pageY, 0);
}
const itemEl = targetEl && targetEl.closest ? targetEl.closest(".item") : null;
const boxEl = itemEl ? itemEl.querySelector(".emoji-box") : null;
if (boxEl) {
if (emojiBottomDistance < 500) boxEl.classList.add("top");
else boxEl.classList.remove("top");
}
} catch (e) {}
};
// 关闭 Emoji
@@ -1063,6 +1079,7 @@ const appSectionIndex = createApp({
element.timestamp = strtimeago(element.created_at, 4);
element["isReplyBoxShow"] = 0;
element["picture"] = [];
if (element["content"]) element["content"] = restoreHtml(element["content"], element.attachments, "comment");
});
let merged = [...commentList.value[index]["child"], ...data.data.filter((item2) => !commentList.value[index]["child"].find((item1) => item1.id == item2.id))];
@@ -1218,7 +1235,7 @@ const appSectionIndex = createApp({
ajax(`/v2/api/forum/postTopicShare`, { token });
};
return { emojiBottomDistance, uniqidRef, share, reportToken, isReplyBoxShow, matterHeight, sidebarHeight, deleteItem, maxPicture, sidebarFixed, matterRef, sidebarRef, pitchInputState, ismyself, edit, searchInput, defaultSearchText, goSearch, goPersonalHomepage, QRcode, alsoCommentsData, copyLinkClick, reportState, tokentoken, essence, recommend, hide, report, cutShow, ismanager, show, openDiscuss, commentDelete, handleInputPaste, autoResize, editCommentState, selectEditEmoji, closeEditEmoji, openEditEmoji, closeEdit, openEdit, closeEditFileUpload, postEditComment, submitAnswerComments, closePictureUpload, closeFileUpload, picture, editToken, editPicture, editInput, editEmojiState, handleFileUpload, inputTextarea, judgeLogin, handleEditFile, selectEmoji, emojiData, emojiMaskState, emojiState, closeEmoji, openEmoji, closeAnswerCommentsChild, openAnswerCommentsChild, handleAnswerText, sendMessage, TAHomePage, operateAnswerCommentsLike, closeUserInfo, openUserInfo, permissions, commentList, commentPage, commentTotalCount, picture, userInfoWin, relatedList, relatedTime, coinNubmer, coinList, coinAmount, coinSubmit, strategy, mybalance, coinsState, openCoinBox, closeCoinBox, isLikeGif, likeClick, collectClick, islike, iscollect, recentlyList, medal, count, sectionn, tags, authorInfo, info, timestamp, updatedTime };
return { uniqidRef, share, reportToken, isReplyBoxShow, matterHeight, sidebarHeight, deleteItem, maxPicture, sidebarFixed, matterRef, sidebarRef, pitchInputState, ismyself, edit, searchInput, defaultSearchText, goSearch, goPersonalHomepage, QRcode, alsoCommentsData, copyLinkClick, reportState, tokentoken, essence, recommend, hide, report, cutShow, ismanager, show, openDiscuss, commentDelete, handleInputPaste, autoResize, editCommentState, selectEditEmoji, closeEditEmoji, openEditEmoji, closeEdit, openEdit, closeEditFileUpload, postEditComment, submitAnswerComments, closePictureUpload, closeFileUpload, picture, editToken, editPicture, editInput, editEmojiState, handleFileUpload, inputTextarea, judgeLogin, handleEditFile, selectEmoji, emojiData, emojiMaskState, emojiState, closeEmoji, openEmoji, closeAnswerCommentsChild, openAnswerCommentsChild, handleAnswerText, sendMessage, TAHomePage, operateAnswerCommentsLike, closeUserInfo, openUserInfo, permissions, commentList, commentPage, commentTotalCount, picture, userInfoWin, relatedList, relatedTime, coinNubmer, coinList, coinAmount, coinSubmit, strategy, mybalance, coinsState, openCoinBox, closeCoinBox, isLikeGif, likeClick, collectClick, islike, iscollect, recentlyList, medal, count, sectionn, tags, authorInfo, info, timestamp, updatedTime };
},
});