Files
PC-Light-Forum/js/details.js
DESKTOP-RQ919RC\Pc 51179c3842 refactor: 移除调试日志并优化代码结构
移除多个文件中的console.log调试语句
添加search-tag.css和search-tag.less中的set-hint-box样式
新增recommend.js推荐功能模块
优化ajaxGet调用和用户信息处理逻辑
2025-12-17 19:36:53 +08:00

1345 lines
58 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { createApp, ref, onMounted, nextTick, onUnmounted, computed, watch, provide, defineAsyncComponent } = Vue;
(async function () {
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"));
const { itemVote } = await import(withVer("../component/item-vote/item-vote.js"));
const { itemMj } = await import(withVer("../component/item-mj/item-mj.js"));
const { itemTenement } = await import(withVer("../component/item-tenement/item-tenement.js"));
// const itemForum = defineAsyncComponent(() => import(withVer("../component/item-forum/item-forum.js")).then(m => m.itemForum));
// const itemOffer = defineAsyncComponent(() => import(withVer("../component/item-offer/item-offer.js")).then(m => m.itemOffer));
// const itemSummary = defineAsyncComponent(() => import(withVer("../component/item-summary/item-summary.js")).then(m => m.itemSummary));
// const itemVote = defineAsyncComponent(() => import(withVer("../component/item-vote/item-vote.js")).then(m => m.itemVote));
// const itemMj = defineAsyncComponent(() => import(withVer("../component/item-mj/item-mj.js")).then(m => m.itemMj));
// const itemTenement = defineAsyncComponent(() => import(withVer("../component/item-tenement/item-tenement.js")).then(m => m.itemTenement));
// const latestList = defineAsyncComponent(() => import(withVer("../component/latest-list/latest-list.js")).then(m => m.latestList));
// const slideshowBox = defineAsyncComponent(() => import(withVer("../component/slideshow-box/slideshow-box.js")).then(m => m.slideshowBox));
// const like = defineAsyncComponent(() => import(withVer("../component/like/like.js")).then(m => m.like));
// const report = defineAsyncComponent(() => import(withVer("../component/report/report.js")).then(m => m.report));
// const headTop = defineAsyncComponent(() => import(withVer("../component/head-top/head-top.js")).then(m => m.headTop));
// 判断 ismobile 为 true 是 不加载 latestList 和 slideshowBox
// if (!window.isMobile) {
// const { latestList } = await import(withVer("../component/latest-list/latest-list.js"));
// const { slideshowBox } = await import(withVer("../component/slideshow-box/slideshow-box.js"));
// }
let latestList, slideshowBox;
if (!window.isMobile) {
({ latestList } = await import(withVer("../component/latest-list/latest-list.js")));
({ slideshowBox } = await import(withVer("../component/slideshow-box/slideshow-box.js")));
} else {
latestList = { name: "latestList", template: "<div></div>" };
slideshowBox = { name: "slideshowBox", template: "<div></div>" };
}
// const { latestList } = await import(withVer("../component/latest-list/latest-list.js"));
// const { slideshowBox } = await import(withVer("../component/slideshow-box/slideshow-box.js"));
const { like } = await import(withVer("../component/like/like.js"));
const { report } = await import(withVer("../component/report/report.js"));
const { headTop } = await import(withVer("../component/head-top/head-top.js"));
const appSectionIndex = createApp({
setup() {
onMounted(() => {
getUserInfoWin();
const preLoader = document.getElementById("pre-loader");
if (preLoader) preLoader.style.display = "none";
document.querySelectorAll(".vuehide").forEach((item) => {
item.style.display = "none";
});
});
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 || [];
ismanager.value = permissions.value.indexOf("topic:manager") >= 0;
};
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();
};
provide("isLogin", isLogin);
provide("userInfoWin", userInfoWin);
provide("realname", realname);
provide("openAttest", openAttest);
provide("goLogin", goLogin);
let authorInfo = ref({});
let info = ref({});
let ismyself = ref(false);
let timestamp = ref("");
let updatedTime = ref("");
let token = "";
let tokentoken = ref("");
let uniqid = "";
let sectionn = ref([]);
let tags = ref([]);
let uniqidRef = ref(null);
onMounted(() => {
uniqid = uniqidRef.value.innerText;
init();
window.addEventListener("scroll", handleScroll);
checkWConfig();
});
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 || {};
maxPicture.value = config.topic_image_count;
}
} else {
getWConfig();
}
};
const getWConfig = () => {
ajaxGet("/v2/api/config/website").then((res) => {
if (res.code == 200) {
let data = res["data"] || {};
const config = data.config || {};
maxPicture.value = config.topic_image_count;
data.time = new Date().toISOString();
localStorage.setItem("wConfig", JSON.stringify(data));
}
});
};
const init = () => {
ajaxGet(`/v2/api/forum/getTopicDetails?uniqid=${uniqid}`).then((res) => {
if (res.code != 200) {
creationAlertBox("error", res.message || "主题不存在");
// setTimeout(() => redirectToExternalWebsite(`/`), 3000);
return;
}
const data = res.data;
let targetInfo = data.info;
if (!targetInfo.hidden) targetInfo.hidden = 0;
// 替换换行
targetInfo.content = targetInfo.content?.replace(/\n/g, "<br>") || "";
if (!targetInfo.content) {
targetInfo.content = targetInfo.title;
targetInfo.title = "";
}
authorInfo.value = Array.isArray(data.authorInfo) ? null : data.authorInfo;
ismyself.value = data.ismyself || false;
const sectionNameSet = new Set(targetInfo.sectionn.map((item) => item.name));
const newTag = targetInfo.tags.filter((tagName) => !sectionNameSet.has(tagName));
sectionn.value = targetInfo.sectionn;
tags.value = newTag;
timestamp.value = strtimeago(targetInfo.release_at, 4);
updatedTime.value = targetInfo.updated_at ? strtimeago(targetInfo.updated_at, 4) : null;
if (targetInfo.content) targetInfo.content = restoreHtml(targetInfo.content, targetInfo.attachments);
info.value = targetInfo;
token = data.token;
tokentoken.value = data.token;
if (info.value["anonymous"] == 0) getAuthorInfo();
isLogin.value = data.islogin;
if (isLogin.value) getTopicOperation();
getRelatedTopics();
getComment();
getQrcode();
});
};
const restoreHtml = (formattedText, attachments, type) => {
let imageList = attachments?.images || [];
if (imageList && typeof imageList === "object" && !Array.isArray(imageList)) imageList = Object.values(imageList);
let filesList = attachments?.files || [];
if (filesList && typeof filesList === "object" && !Array.isArray(filesList)) filesList = Object.values(filesList);
let videosList = attachments?.videos || [];
if (videosList && typeof videosList === "object" && !Array.isArray(videosList)) videosList = Object.values(videosList);
// const filesList = attachments?.files || [];
// const videosList = attachments?.videos || [];
let html = formattedText;
html = html.replaceAll("<b>", "[b]");
html = html.replaceAll("</b>", "[/b]");
// 1. 还原换行符为<br>标签
html = html.replace(/\n/g, "<br>");
// 2. 还原块级标签的换行标记
html = html.replace(/<br><div>/g, "<div>");
html = html.replace(/<\/div><br>/g, "</div>");
// 3. 还原标签标记为span.blue
html = html.replace(/\[tag\]([^[]+)\[\/tag\]/gi, '<a class="blue" href="/tag/$1" target="_blank">#$1</a> <span class="fill"></span> ');
// 4. 还原粗体标记为h2标签
html = html.replace(/\[b\]([\s\S]*?)\[\/b\]/gi, "<h2>$1</h2>");
// 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)); // 统一字符串比较,避免类型问题
if (!image) return match;
// 从列表中移除已匹配的图片(避免重复使用)
const index = imageList.findIndex((img) => String(img.aid) === String(aid));
if (index > -1) imageList.splice(index, 1);
// 拼接img标签带宽高样式宽高为0则不设置
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}" ${style}>`;
});
// 5. 统一在单次遍历中按出现顺序替换 attach/attachimg
const byAid = new Map();
imageList.forEach((e) => byAid.set(Number(e.aid), { type: "image", ...e }));
filesList.forEach((e) => byAid.set(Number(e.aid), { type: "file", ...e }));
videosList.forEach((e) => byAid.set(Number(e.aid), { type: "video", ...e }));
html = html.replace(/\[(attachimg|attach)\](\d+)\[\/\1\]/gi, (match, tag, aidStr) => {
const aid = Number(aidStr);
const item = byAid.get(aid);
if (!item) return match;
byAid.delete(aid);
if (item.type === "image") {
return `<img src="${item.url}" data-aid="${aid}"><br/>`;
}
if (item.type === "file") {
return `<div class="flexacenter"><a href="${item.url}" download="${item.filename}">${item.filename}</a>【点击下载附件】</div>`;
}
return `<video src="${item.url}" width="400" height="400" preload="none" poster="${item.posterurl}" controls></video><br/>`;
});
// 6. 还原填充标签
html = html.replace(/(<span class="blue">[^<]+<\/span>)\s+/gi, '$1 <span class="fill"></span> ');
// 7. 清理多余的<br>标签
// html = html.replace(/<br><br>/g, "<br>");
if (type != "comment") {
byAid.forEach((item, aid) => {
if (item.type === "image") html += `<img src="${item.url}" data-aid="${aid}"><br/>`;
else if (item.type === "file") html += `<div class="flexacenter"><a href="${item.url}" download="${item.name || item.filename}">${item.name || item.filename}</a>【点击下载附件】</div><br/>`;
else html += `<video src="${item.url}" width="400" height="400" preload="none" poster="${item.posterurl}" controls></video><br/>`;
});
}
return html;
};
let QRcode = ref("");
const getQrcode = () => {
ajaxGet(`/v2/api/forum/getQrcode?token=${token}`).then((res) => {
if (res.code != 200) return;
const data = res.data || [];
QRcode.value = data.url || "";
});
};
let count = ref(0);
let medal = ref([]);
const getAuthorInfo = () => {
ajaxGet(`/v2/api/forum/getSpaceDetail?uid=${authorInfo.value.uid || 0}&uin=${authorInfo.value.uin || 0}`).then((res) => {
const data = res.data;
const countList = data.count || [];
count.value = countList.reduce((sum, item) => {
const currentCount = Number(item.count) || 0;
return sum + currentCount;
}, 0);
medal.value = data.medal || [];
authorInfo.value = data.info;
getCreationList(data.token);
});
};
let recentlyList = ref([]);
const getCreationList = (token) => {
ajaxGet(`/v2/api/forum/getSpaceTopicList?token=${token}&simple=1`).then((res) => {
const data = res.data;
recentlyList.value = data.data || [];
recentlyList.value = recentlyList.value.slice(0, 8);
});
};
let islike = ref(0);
let iscollect = ref(0);
const getTopicOperation = () => {
ajax(`/v2/api/forum/getTopicOperation`, {
token,
actions: ["like", "collection"],
})
.then((res) => {
const data = res.data;
const like = data.like;
const collection = data.collection;
islike.value = like.status;
iscollect.value = collection.status;
})
.catch((err) => {
console.log("err", err);
});
};
let isLikeGif = ref(false);
const likeClick = () => {
if (realname.value == 0 && userInfoWin.value?.uin > 0) {
openAttest();
return;
}
if (!isLogin.value) {
goLogin();
return;
}
ajax(`/v2/api/forum/postTopicLike`, {
token,
}).then((res) => {
if (res.code != 200) {
creationAlertBox("error", res.message);
return;
}
const data = res.data;
islike.value = data.status;
info.value.likes = data.likes;
if (data.status) {
isLikeGif.value = true;
setTimeout(() => (isLikeGif.value = false), 2000);
}
});
};
const collectClick = () => {
ajax(`/v2/api/forum/postTopicCollect`, {
token,
}).then((res) => {
if (res.code != 200) {
creationAlertBox("error", res.message);
return;
}
const data = res.data;
iscollect.value = data.status;
info.value.collections = data.collections;
});
};
let strategy = ref("");
let mybalance = ref(0);
let defaultcoinnum = 0;
const getCoinConfig = () => {
if (!isLogin.value) {
goLogin();
return;
}
ajaxGet(`/v2/api/forum/getCoinConfig`).then((res) => {
const data = res.data;
strategy.value = data.config.strategy.url || 0;
mybalance.value = data.mybalance || 0;
defaultcoinnum = data.defaultcoinnum || 0;
// openCoinBox();
});
};
let coinsState = ref(false);
const openCoinBox = () => {
BiComponent.initComponent();
// getCoinConfig();
// coinsState.value = true;
// document.body.style.overflow = "hidden";
// if (!coinListRequest) getCoinRankList();
};
const closeCoinBox = () => {
coinsState.value = false;
document.body.style.overflow = "auto";
};
let coinAmount = ref("");
const coinSubmit = () => {
const num = Number(coinAmount.value || defaultcoinnum) || 0;
ajax(`/v2/api/forum/postTopicCoin`, {
token,
num,
})
.then((res) => {
if (res.code == 200) creationAlertBox("success", res.message);
else creationAlertBox("error", res.message);
if (res.code != 200) return;
let data = res.data;
mybalance.value = mybalance.value - num || 0;
coinAmount.value = "";
info.value.coins = data.coins || 0;
coinNubmer.value = 0;
coinList.value = [];
getCoinRankList();
})
.finally(() => {
// wx.hideLoading();
});
};
let coinNubmer = ref(0);
let coinList = ref([]);
let coinListRequest = false; // 控制请求次数
const getCoinRankList = () => {
ajaxGet(`/v2/api/forum/getCoinRankList?token=${token}&limit=1000`).then((res) => {
const data = res.data;
coinNubmer.value = data.nubmer;
coinList.value = data.data;
coinListRequest = true;
});
};
let relatedList = ref([]);
let relatedTime = ref("");
const getRelatedTopics = () => {
ajaxGet(`/v2/api/forum/getRelatedTopics?uniqid=${uniqid}&limit=8`).then((res) => {
const data = res.data;
relatedTime.value = data.updated_at || "";
relatedList.value = data.list || [];
});
};
let alreadyCommentIdList = [];
let commentPage = ref(1);
let isgetCommentSate = false;
let commentList = ref([]);
let commentTotalCount = ref(0);
const getComment = () => {
if (commentPage.value == 0 || isgetCommentSate || !token) return;
isgetCommentSate = true;
ajaxGet(`/v2/api/forum/getCommentList?token=${token}&page=${commentPage.value}&limit=20`)
.then((res) => {
if (res.code != 200) {
creationAlertBox("error", res.message || "");
return;
}
let data = res.data;
data.data.forEach((element, index) => {
element.timestamp = strtimeago(element.created_at, 4);
element["picture"] = [];
element["isReplyBoxShow"] = 0;
if (element["content"]) element["content"] = restoreHtml(element["content"], element.attachments, "comment");
if (element.child.length > 0) {
element.child.forEach((el) => {
el["picture"] = [];
el.timestamp = strtimeago(el.created_at, 4);
el["isReplyBoxShow"] = 0;
if (el["content"]) el["content"] = restoreHtml(el["content"], el.attachments, "comment");
});
}
});
// if (commentPage.value > 1) {
// for (let index = 0; index < data.data.length; index++) {
// if (alreadyCommentIdList.includes(data.data[index].id)) {
// data.data.splice(index, 1);
// index--;
// }
// }
// }
commentList.value = commentList.value.concat(data.data);
commentTotalCount.value = data.commentcount;
commentPage.value = data.count > commentList.value.length ? commentPage.value + 1 : 0;
})
.finally(() => {
isgetCommentSate = false;
});
};
let picture = ref([]);
const openUserInfo = (index, i) => {
if (i != undefined && (commentList.value[index].child[i].user["uin"] > 0 || commentList.value[index].child[i].user["uid"] > 0)) commentList.value[index].child[i]["avatarState"] = true;
if (i == undefined && index != undefined && (commentList.value[index].user["uin"] > 0 || commentList.value[index].user["uid"] > 0)) commentList.value[index]["avatarState"] = true;
};
const closeUserInfo = (index, i) => {
if (i != undefined) commentList.value[index].child[i]["avatarState"] = false;
else if (index != undefined) commentList.value[index]["avatarState"] = false;
};
let isReplyBoxShow = ref(true);
// 打开 回答-评论 的子评论
const openAnswerCommentsChild = (index, i) => {
if (realname.value == 0 && userInfoWin.value?.uin > 0) {
openAttest();
return;
}
if (!isLogin.value) {
goLogin();
return;
}
closeAnswerCommentsChild();
if (i == null) commentList.value[index]["childState"] = true;
else commentList.value[index].child[i]["childState"] = true;
isReplyBoxShow.value = false;
};
// 关闭 回答-评论 的子评论
const closeAnswerCommentsChild = () => {
commentList.value.forEach((ele) => {
ele["childState"] = false;
ele["commentInput"] = ""; // 删除原本输入值
if (ele["child"] && ele["child"].length != 0) {
ele["child"].forEach((el) => {
el["childState"] = false;
el["commentInput"] = "";
});
}
});
isReplyBoxShow.value = true;
};
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);
}
};
// 回答-评论 点赞
const operateAnswerCommentsLike = (token, index, i) => {
if (realname.value == 0 && userInfoWin.value?.uin > 0) {
openAttest();
return;
}
if (!isLogin.value) {
goLogin();
return;
}
ajax("/v2/api/forum/likeComment", {
token,
}).then((res) => {
if (res.code != 200) {
creationAlertBox("error", res.message || "操作成功");
return;
}
let data = res.data;
if (i != undefined) {
commentList.value[index].child[i]["islike"] = data["status"];
commentList.value[index].child[i]["likenum"] = data["count"];
} else {
commentList.value[index]["islike"] = data["status"];
commentList.value[index]["likenum"] = data["count"];
}
creationAlertBox("success", res.message || "操作成功");
if (data["status"]) {
isLikeGif.value = true;
setTimeout(() => (isLikeGif.value = false), 2000);
}
});
};
let emojiState = ref(false);
let emojiMaskState = ref(false);
let inputTextarea = ref("");
const inputTextareaRef = ref(null);
// 打开 Emoji
const openEmoji = (event, index, i) => {
if (!isLogin.value) {
goLogin();
return;
}
if (i != undefined) commentList.value[index].child[i]["emojiState"] = true;
else if (index != undefined) commentList.value[index]["emojiState"] = true;
else {
closeEmoji();
closeAnswerCommentsChild();
emojiState.value = true;
}
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
const closeEmoji = (index, i) => {
commentList.value.forEach((ele) => {
ele["emojiState"] = false;
if (ele["child"] && ele["child"].length != 0) {
ele["child"].forEach((el) => {
el["emojiState"] = false;
});
}
});
emojiState.value = false;
emojiMaskState.value = false;
editEmojiState.value = false;
};
const TAHomePage = (token) => goHomePage(token);
const sendMessage = (token) => goSendMessage(token);
let emojiData = ref(["😀", "😁", "😆", "😅", "😂", "😉", "😍", "🥰", "😘", "🤥", "😪", "😵‍💫", "🤓", "🥺", "😋", "😜", "🤪", "😎", "🤩", "🥳", "😔", "🙁", "😭", "😡", "😳", "🤗", "🤔", "🤭", "🤫", "😯", "😵", "🙄", "🥴", "🤢", "🤑", "🤠", "👌", "✌️", "🤟", "🤘", "🤙", "👍", "👎", "✊", "👏", "🤝", "🙏", "💪", "❎️", "✳️", "✴️", "❇️", "#️⃣", "*️⃣", "1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟", "🆗", "🈶", "🉐", "🉑", "🌹", "🥀", "🌸", "🌺", "🌷", "🌲", "☘️", "🍀", "🍁", "🌙", "⭐", "🌍", "☀️", "⭐️", "🌟", "☁️", "🌈", "☂️", "❄️", "☃️", "☄️", "🔥", "💧", "🍎", "🍐", "🍊", "🍉", "🍓", "🍑", "🍔", "🍟", "🍕", "🥪", "🍜", "🍡", "🍨", "🍦", "🎂", "🍰", "🍭", "🍿", "🍩", "🧃", "🍹", "🍒", "🥝", "🥒", "🥦", "🥨", "🌭", "🥘", "🍱", "🍢", "🥮", "🍩", "🍪", "🧁", "🍵", "🍶", "🍻", "🥂", "🧋", "🎉", "🎁", "🧧", "🎃", "🎄", "🧨", "✨️", "🎈", "🎊", "🎋", "🎍", "🎀", "🎖️", "🏆️", "🏅", "💌", "📬", "🚗", "🚕", "🚲", "🛵", "🚀", "🚁", "⛵", "🚢", "🔮", "🧸", "🀄️"]);
const handleEditFile = () => {
editEmojiState.value = false;
judgeLogin();
};
// 选择 Emoji
const selectEmoji = (key, index, i) => {
closeEmoji();
if (i != undefined) {
if (!commentList.value[index]["child"][i]["commentInput"]) commentList.value[index]["child"][i]["commentInput"] = "";
const id = commentList.value[index]["child"][i]?.id;
const textarea = document.querySelector(`.input-textarea-${id}`);
if (textarea) {
const currentValue = textarea.value;
const startPos = textarea.selectionStart || 0;
const endPos = textarea.selectionEnd || 0;
const newValue = currentValue.substring(0, startPos) + key + currentValue.substring(endPos);
commentList.value[index]["child"][i]["commentInput"] = newValue;
nextTick(() => {
textarea.focus();
textarea.selectionStart = textarea.selectionEnd = startPos + key.length;
});
} else commentList.value[index]["child"][i]["commentInput"] += key;
} else if (index != undefined) {
if (!commentList.value[index]["commentInput"]) commentList.value[index]["commentInput"] = "";
const id = commentList.value[index]?.id;
const textarea = document.querySelector(`.input-textarea-${id}`);
if (textarea) {
const currentValue = textarea.value;
const startPos = textarea.selectionStart || 0;
const endPos = textarea.selectionEnd || 0;
const newValue = currentValue.substring(0, startPos) + key + currentValue.substring(endPos);
commentList.value[index]["commentInput"] = newValue;
nextTick(() => {
textarea.focus();
textarea.selectionStart = textarea.selectionEnd = startPos + key.length;
});
} else commentList.value[index]["commentInput"] += key;
} else {
const textarea = inputTextareaRef.value;
if (!textarea) return;
const currentValue = textarea.value;
const startPos = textarea.selectionStart || 0;
const endPos = textarea.selectionEnd || 0;
const newValue = currentValue.substring(0, startPos) + key + currentValue.substring(endPos);
inputTextarea.value = newValue;
nextTick(() => {
textarea.focus();
textarea.selectionStart = textarea.selectionEnd = startPos + key.length;
});
}
};
const judgeLogin = () => {
if (!isLogin.value) goLogin();
};
let maxPicture = ref(10);
const handleFileUpload = (event, index, i) => {
closeEmoji();
const file = event.target.files[0]; // 获取选择的文件
if (!file) return;
if (file.size > maxSize) {
creationAlertBox("error", "文件大小不能超过 20MB");
return;
}
let target = [];
if (editCommentState.value) target = editPicture.value;
else {
if (i != undefined) target = commentList.value[index].child[i]["picture"];
else if (index != undefined) target = commentList.value[index]["picture"];
else target = picture.value;
}
if (target.length >= maxPicture.value) {
creationAlertBox("error", `最多只能上传 ${maxPicture.value} 张图片`);
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const base64 = e.target.result;
uploadImg(file).then((res) => {
const obj = {
aid: res.aid || "",
url: res.url || "",
};
target.push(obj);
if (editCommentState.value) editPicture.value = target;
else {
if (i != undefined) commentList.value[index].child[i]["picture"] = target;
else if (index != undefined) commentList.value[index]["picture"] = target;
else picture.value = target;
}
creationAlertBox("success", "上传成功");
});
};
reader.readAsDataURL(file);
};
let uploadConfig = null;
const maxSize = 20 * 1024 * 1024; // 20MB
// 上传图片 获取图片url
const uploadImg = (file) => {
return new Promise((resolve, reject) => {
const upload = () => {
let config = uploadConfig;
const formData = new FormData();
formData.append(config.requestName, file); // 文件数据
formData.append("name", file.name); // 文件名
formData.append("type", "image"); // 文件名
formData.append("data", config.params.data); // 文件名
ajax(config.url, formData).then((res) => {
if (res.code != 200) {
creationAlertBox("error", "上传失败");
return;
}
let data = res.data;
resolve(data);
});
};
if (uploadConfig) upload();
else getUploadConfig().then(() => upload());
});
};
const getUploadConfig = () => {
return new Promise((resolve, reject) => {
ajaxGet("/v2/api/config/upload?type=comment").then((res) => {
let data = res.data;
uploadConfig = data;
resolve();
});
});
};
// 删除上传的图片
const closeFileUpload = (aid, index, i) => {
let target = [];
if (i != undefined) target = [...commentList.value[index].child[i]["picture"]];
else if (index != undefined) target = [...commentList.value[index]["picture"]];
else target = [...picture.value];
let sub = target.findIndex((item) => item.aid == aid);
if (sub != -1) target.splice(sub, 1);
if (i != undefined) commentList.value[index].child[i]["picture"] = target;
else if (index != undefined) commentList.value[index]["picture"] = target;
else picture.value = target;
};
const closePictureUpload = (index) => picture.value.splice(index, 1);
// 提交回答-评论
const submitAnswerComments = (index, i) => {
if (realname.value == 0 && userInfoWin.value?.uin > 0) {
openAttest();
return;
}
if (!isLogin.value) {
goLogin();
return;
}
let content = "";
let parentid = null;
// let token = this.token;
let image = [];
if (i != null) {
content = commentList.value[index]["child"][i]["commentInput"];
parentid = commentList.value[index]["child"][i]["id"];
image = [...commentList.value[index]["child"][i]["picture"]];
} else if (index != null) {
content = commentList.value[index]["commentInput"];
parentid = commentList.value[index]["id"];
image = [...commentList.value[index]["picture"]];
} else {
content = inputTextarea.value;
image = [...picture.value];
}
// this.detailLoading = true;
const attachments = {
images: image,
};
ajax("/v2/api/forum/postComment", {
content,
token,
attachments,
replyid: parentid,
})
.then((res) => {
if (res.code != 200) {
creationAlertBox("error", res.message || "操作成功");
return;
}
let data = res.data;
const timestamp = strtimeago(new Date());
if (i != null) {
let targetData = {
id: data["commentid"],
content,
isauthor: 1,
islike: 0,
likenum: 0,
reply: {
nickname: commentList.value[index]["child"][i]["nickname"],
},
...data,
attachments,
picture: [],
timestamp,
user: { ...userInfoWin.value },
};
commentList.value[index]["child"].push(targetData);
commentList.value[index]["childnum"]++;
} else if (index != null) {
let targetData = {
id: data["commentid"],
content,
isauthor: 1,
islike: 0,
likenum: 0,
reply: [],
...data,
attachments,
picture: [],
timestamp,
user: { ...userInfoWin.value },
};
commentList.value[index]["child"].unshift(targetData);
commentList.value[index]["childnum"]++;
} else {
let targetData = {
id: data["commentid"],
content,
isauthor: 1,
islike: 0,
likenum: 0,
...data,
child: [],
attachments,
picture: [],
timestamp,
user: { ...userInfoWin.value },
};
commentList.value.unshift(targetData);
inputTextarea.value = "";
picture.value = [];
}
commentTotalCount.value = data.count || 0;
// if (!inputTextarea.value) {
// const textarea = this.$refs["input-textarea"]
// textarea.style.height = "80px"
// }
closeAnswerCommentsChild();
creationAlertBox("success", res.message || "操作成功");
})
.finally(() => {
// this.detailLoading = false;
});
};
let editCommentState = ref(false);
let editToken = ref("");
let editPicture = ref([]);
let editInput = ref("");
let editEmojiState = ref(false);
const openEdit = (token, index, i) => {
const list = JSON.parse(JSON.stringify(commentList.value));
let target = {};
if (i != null) target = list[index]["child"][i];
else target = list[index];
editToken.value = target.token || "";
editInput.value = target.content || "";
editPicture.value = target.attachments?.images || [];
editCommentState.value = true;
nextTick(() => {
editInputRef.value.style.height = "auto"; // 重置高度
editInputRef.value.style.height = `${editInputRef.value.scrollHeight}px`; // 设置为内容高度
});
};
const closeEdit = () => {
editPicture.value = {};
editToken.value = "";
editInput.value = "";
editCommentState.value = false;
};
// 打开 Emoji
const openEditEmoji = (index, i) => {
if (!isLogin.value) {
goLogin();
return;
}
editEmojiState.value = true;
};
const closeEditEmoji = () => {
editEmojiState.value = false;
};
const selectEditEmoji = (key) => {
closeEmoji();
editInput.value += key;
};
const postEditComment = () => {
if (!isLogin.value) {
goLogin();
return;
}
const image = editPicture.value;
const attachments = {
images: image,
};
ajax("/v2/api/forum/postCommentEdit", {
content: editInput.value,
token: editToken.value,
attachments,
}).then((res) => {
if (res.code != 200) {
creationAlertBox("error", res.message || "操作失败");
return;
}
commentList.value.forEach((element) => {
if (element.token == editToken.value) {
element["content"] = editInput.value;
element["attachments"] = attachments;
}
element.child &&
element.child.forEach((ele) => {
if (ele.token == editToken.value) {
ele["content"] = editInput.value;
ele["attachments"] = attachments;
}
});
});
editPicture.value = [];
editToken.value = "";
editCommentState.value = false;
editEmojiState.value = false;
creationAlertBox("success", res.message || "操作成功");
});
};
const closeEditFileUpload = (aid) => {
let target = [...editPicture.value];
let sub = target.findIndex((item) => item.aid == aid);
if (sub != -1) target.splice(sub, 1);
editPicture.value = target;
};
const handleInputPaste = (event, index, ii) => {
const items = event.clipboardData.items; // 获取粘贴的内容
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.type.startsWith("image/")) {
event.preventDefault();
const file = item.getAsFile(); // 获取文件
if (file.size > maxSize) {
creationAlertBox("error", "文件大小不能超过 20MB");
return;
}
let target = [];
if (editCommentState.value) target = editPicture.value;
else {
if (ii != undefined) target = commentList.value[index].child[ii]["picture"];
else if (index != undefined) target = commentList.value[index]["picture"];
else target = picture.value;
}
if (target.length >= maxPicture.value) {
creationAlertBox("error", `最多只能上传 ${maxPicture.value} 张图片`);
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const base64 = e.target.result;
uploadImg(file).then((res) => {
const obj = {
aid: res.aid || "",
url: res.url || "",
};
target.push(obj);
if (editCommentState.value) editPicture.value = target;
else {
if (ii != undefined) commentList.value[index].child[ii]["picture"] = target;
else if (index != undefined) commentList.value[index]["picture"] = target;
else picture.value = target;
}
creationAlertBox("success", "上传成功");
});
};
reader.readAsDataURL(file);
}
}
};
const alsoCommentsData = (index, i) => {
if (!isLogin.value) {
goLogin();
return;
}
const parentid = commentList.value[index]["id"];
ajax("/v2/api/forum/childrenList", {
token,
parentid,
limit: 2000,
page: 1,
childlimit: 3,
}).then((res) => {
if (res.code != 200) {
creationAlertBox("error", res.message || "操作成功");
return;
}
let data = res.data;
data.data.forEach((element, index) => {
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))];
commentList.value[index]["child"] = merged;
});
};
// 自动输入框增高
const autoResize = (e) => {
e.target.style.height = "auto"; // 重置高度
e.target.style.height = `${e.target.scrollHeight}px`; // 设置为内容高度
};
let commemtDelete = {};
// 点击删除
const commentDelete = (token, index, i) => {
const post = () => {
ajax("/v2/api/forum/deleteComment", {
token,
}).then((res) => {
if (res.code != 200) {
creationAlertBox("error", res.message);
return;
}
if (i >= 0) {
commentList.value[index].child.splice(i, 1);
commentList.value[index].childnum -= 1;
} else {
commentList.value.splice(index, 1);
}
commentTotalCount.value -= 1;
creationAlertBox("success", res.message || "操作成功");
});
};
const isConfirmed = confirm(`确定要删除讨论吗?`);
if (isConfirmed) post();
};
const openDiscuss = () => {
let dom = document.querySelector(".answer-discuss");
if (!dom) return;
const rect = dom.getBoundingClientRect();
const scrollPosition = window.pageYOffset + rect.top - 50;
window.scrollTo({
top: scrollPosition,
behavior: "smooth",
});
};
let show = ref(false);
let ismanager = ref(false);
const cutShow = () => {
show.value = !show.value; // 修改为切换显示状态
};
let reportState = ref(false);
let reportToken = ref("");
provide("reportState", reportState);
// 举报
const report = (token) => {
cutShow();
reportState.value = true;
reportToken.value = token;
};
// 隐藏
const hide = () => {
const target = info.value;
managerHide(token, target.hidden, "thread").then((value) => {
target.hidden = value;
info.value = target;
cutShow();
});
};
// 推荐
const recommend = () => {
const target = info.value;
managerRecommend(token, target.recommend).then((value) => {
target.recommend = value;
info.value = target;
cutShow();
});
};
// 精华
const essence = () => {
const target = info.value;
managerEssence(token, target.best).then((value) => {
target.best = value;
info.value = target;
cutShow();
});
};
const copyLinkClick = () => {
copyForumUid(location.href);
};
const goPersonalHomepage = (token) => {
if (!token) return;
redirectToExternalWebsite(`/u/${token}`);
};
let searchInput = ref("");
let defaultSearchText = ref("屯特");
const goSearch = () => {
const searchText = searchInput.value || defaultSearchText.value;
redirectToExternalWebsite("/search/" + searchText);
};
const edit = () => {
redirectToExternalWebsite(`/publish?uniqid=${info.value.uniqid}`);
};
let pitchInputState = ref(false);
const sidebarFixed = ref(false);
const handleScroll = () => {
matterHeight.value = -(matterRef.value.offsetHeight - window.innerHeight);
sidebarHeight.value = -(sidebarRef.value.offsetHeight - window.innerHeight);
if (matterHeight.value > 0) matterHeight.value = 12;
if (sidebarHeight.value > 0) sidebarHeight.value = 12;
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
const clientHeight = window.innerHeight;
// 列表下 滑动到底部 获取新数据
if (scrollTop + clientHeight >= scrollHeight - 200) getComment();
};
const matterRef = ref(null);
const sidebarRef = ref(null);
const deleteItem = () => {
managerDelete(token)
.then(() => redirectToExternalWebsite("/", "_self"))
.finally(() => cutShow());
};
let sidebarHeight = ref(0);
let matterHeight = ref(0);
const share = () => {
ajax(`/v2/api/forum/postTopicShare`, { token });
};
return { inputTextareaRef, 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 };
},
});
appSectionIndex.component("itemForum", itemForum);
appSectionIndex.component("itemOffer", itemOffer);
appSectionIndex.component("itemSummary", itemSummary);
appSectionIndex.component("itemVote", itemVote);
appSectionIndex.component("itemMj", itemMj);
appSectionIndex.component("itemTenement", itemTenement);
appSectionIndex.component("latestList", latestList);
appSectionIndex.component("slideshowBox", slideshowBox);
appSectionIndex.component("like", like);
appSectionIndex.component("report", report);
appSectionIndex.component("headTop", headTop);
appSectionIndex.mount("#details");
})();