Files
PC-Light-Forum/js/details.js
DESKTOP-RQ919RC\Pc c9e229a5cd fix(bi): 添加投币数量必须大于0的校验
feat(item-bottom): 实现二维码弹窗自适应右侧边界
添加判断逻辑使二维码弹窗在靠近右侧边界时向左弹出

refactor(public.js): 优化ajax请求配置和登录跳转逻辑
统一设置axios默认配置,提取登录跳转函数

style(public.css): 调整QRcode-box.right的定位
修复二维码弹窗靠近右侧时的显示问题

fix(details.js): 修复粗体标记正则匹配多行内容
使用[\s\S]*?匹配可能的多行内容

refactor(index.js): 优化列表加载和滚动逻辑
移除模拟数据,添加加载状态,调整滚动加载阈值

chore: 更新html模板中的唯一标识和广告类名
2025-11-19 19:27:43 +08:00

1209 lines
46 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.

const { createApp, ref, onMounted, nextTick, onUnmounted, computed, watch, provide } = Vue;
import { itemForum } from "../component/item-forum/item-forum.js";
import { itemOffer } from "../component/item-offer/item-offer.js";
import { itemSummary } from "../component/item-summary/item-summary.js";
import { itemVote } from "../component/item-vote/item-vote.js";
import { itemMj } from "../component/item-mj/item-mj.js";
import { itemTenement } from "../component/item-tenement/item-tenement.js";
import { latestList } from "../component/latest-list/latest-list.js";
import { slideshowBox } from "../component/slideshow-box/slideshow-box.js";
import { like } from "../component/like/like.js";
import { report } from "../component/report/report.js";
import { headTop } from "../component/head-top/head-top.js";
const appSectionIndex = createApp({
setup() {
onMounted(() => {
getUserInfoWin();
});
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")) || {};
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 || {};
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;
console.log("data", data);
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) => {
const imageList = attachments?.images || [];
const filesList = attachments?.files || [];
const videosList = attachments?.videos || [];
let html = formattedText;
// 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. 统一在单次遍历中按出现顺序替换 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) => {
console.log("res", 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 (!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 = () => {
console.log("commentPage.value", commentPage.value);
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(element.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") {
var src = e.target.getAttribute("src");
const div = document.createElement("div");
div.innerHTML = `<div class="detail-image flexcenter"><img class="detail-img" src="${src}" /></div>`;
div.className = "detail-image-mask flexcenter";
div.addEventListener("click", () => {
document.body.style.overflow = "auto";
div.remove();
});
document.body.appendChild(div);
document.body.style.overflow = "hidden";
}
};
// 回答-评论 点赞
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("");
// 打开 Emoji
const openEmoji = (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;
};
// 关闭 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"] = "";
commentList.value[index]["child"][i]["commentInput"] += key;
} else if (index != undefined) {
if (!commentList.value[index]["commentInput"]) commentList.value[index]["commentInput"] = "";
commentList.value[index]["commentInput"] += key;
} else inputTextarea.value += key;
};
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,
};
console.log("userInfoWin", userInfoWin.value);
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];
console.log("target", target);
editToken.value = target.token || "";
editInput.value = target.content || "";
editPicture.value = target.attachments?.images || [];
console.log("editCommentState", editPicture.value);
editCommentState.value = true;
};
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"] = [];
});
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 { 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");