-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/img/video-icon.png b/img/video-icon.png
new file mode 100644
index 0000000..dfcca77
Binary files /dev/null and b/img/video-icon.png differ
diff --git a/js/details.js b/js/details.js
index da66801..7fac8cf 100644
--- a/js/details.js
+++ b/js/details.js
@@ -8,28 +8,18 @@ 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();
-
- setTimeout(() => (permissions.value = window["permissions"] || ["comment.edit", "comment.delete", "offercollege.hide", "offersummary.hide", "mj.hide", "topic:manager", "topic:hide"]), 1000);
});
- let isLogin = ref(true);
- let realname = ref(1); // 是否已经实名
- let userInfoWin = ref({
- authority: ["comment.edit", "comment.delete", "offercollege.hide", "offersummary.hide", "mj.hide", "topic:manager", "topic:hide"],
- avatar: "https://nas.gter.net:9008/avatar/97K4EWIMLrsbGTWXslC2WFVSEKWOikN42jDKLNjtax7HL4xtfMOJSdU9oWFhY2E~/middle?random=1761733169",
- groupid: 3,
- nickname: "肖荣豪",
- realname: 1,
- token: "01346a38444d71aaadb3adad52b52c39",
- uid: 500144,
- uin: 4238049,
- });
+ let isLogin = ref(false);
+ let realname = ref(0); // 是否已经实名
+ let userInfoWin = ref({});
let permissions = ref([]);
@@ -42,6 +32,7 @@ const appSectionIndex = createApp({
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);
};
@@ -78,28 +69,65 @@ const appSectionIndex = createApp({
let timestamp = ref("");
let updatedTime = ref("");
let token = "";
+ let tokentoken = ref("");
let uniqid = "";
let sectionn = ref([]);
let tags = ref([]);
+ let uniqidRef = ref(null);
+
onMounted(() => {
- const params = getUrlParams();
- uniqid = params.uniqid || "";
+ 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) => {
+ 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;
- console.log(data, "data");
let targetInfo = data.info;
+ console.log("data", data);
if (!targetInfo.hidden) targetInfo.hidden = 0;
@@ -114,30 +142,129 @@ const appSectionIndex = createApp({
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 = targetInfo.tags;
+ tags.value = newTag;
timestamp.value = strtimeago(targetInfo.release_at, 4);
updatedTime.value = targetInfo.updated_at ? strtimeago(targetInfo.updated_at, 4) : null;
+
+ // targetInfo.content = "[attach]976054[/attach]\n\n[attachimg]1008585[/attachimg]\n[attach]850105[/attach]";
+ // targetInfo.attachments = {
+ // images: [
+ // {
+ // aid: 1008585,
+ // url: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_pokcHeD1NFX9ddrB_WbUGy8P79gQxcXHOeQ4soV7NkzNDQyOQ~~",
+ // thumb: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_pokcHeD1NFX9ddrB_WbUWy8K_hyVFweFbPD7IZK4sVMAmnF5Vzp9Fkg0jQ0Mjk~",
+ // },
+ // ],
+ // files: [],
+ // videos: [
+ // {
+ // aid: 976054,
+ // posterurl: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_pokc36P1NFX9ddrB_WbUWy8K_hyVFweFrPD7IZK4sVMAm3Btwy-9Fkg0jQ0Mjk~",
+ // url: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-c-ZwscHvqqsgFptxhXa6RWi26P-BuTQFFE7SQttkb8LQ0NDI5",
+ // thumb: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-c-ZwscHvqqsgFptxhXa6QWi2uePJ5Bg8VFLPIqoYV7MtbCG2RtAz_-kVNNDQyOQ~~",
+ // },
+ // {
+ // aid: 850105,
+ // posterurl: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG_Y2l-U_pokc32G1NFX9ddrB_WbUWy8K_hyVFweFrPD7IZK4sVMA2eU5Vvl9Fkg0jQ0Mjk~",
+ // url: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-d-5otdXrqqsgFptxhXa6RWi26P-BuTQNAEuHBs9kb8LQ0NDI5",
+ // thumb: "https://o.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-d-5otdXrqqsgFptxhXa6QWi2uePJ5Bg8VFLPIqoYV7MsICzzBsFj_-kVNNDQyOQ~~",
+ // },
+ // ],
+ // };
+ if (targetInfo.content) targetInfo.content = restoreHtml(targetInfo.content, targetInfo.attachments);
+
info.value = targetInfo;
token = data.token;
+ tokentoken.value = data.token;
- getAuthorInfo();
- getTopicOperation();
+ if (info.value["anonymous"] == 0) getAuthorInfo();
- getCoinConfig();
+ 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. 还原换行符为
标签
+ html = html.replace(/\n/g, "
");
+
+ // 2. 还原块级标签的换行标记
+ html = html.replace(/
/g, "
");
+ html = html.replace(/<\/div>
/g, "
");
+
+ // 3. 还原标签标记为span.blue
+ html = html.replace(/\[tag\]([^[]+)\[\/tag\]/gi, '
#$1 ');
+
+ // 4. 还原粗体标记为h2标签
+ html = html.replace(/\[b\]([^[]+)\[\/b\]/gi, "
$1
");
+
+ // 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 `

`;
+ }
+ if (item.type === "file") {
+ return `
`;
+ }
+ return `
`;
+ });
+
+ // 6. 还原填充标签
+ html = html.replace(/(
[^<]+<\/span>)\s+/gi, '$1 ');
+
+ // 7. 清理多余的
标签
+ html = html.replace(/
/g, "
");
+
+ if (type != "comment") {
+ byAid.forEach((item, aid) => {
+ if (item.type === "image") html += `
`;
+ else if (item.type === "file") html += `
`;
+ else html += `
`;
+ });
+ }
+
+ 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) => {
+ 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) => {
@@ -153,7 +280,7 @@ const appSectionIndex = createApp({
let recentlyList = ref([]);
const getCreationList = (token) => {
- ajaxget(`/v2/api/forum/getSpaceTopicList?token=${token}&simple=1`).then((res) => {
+ 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);
@@ -167,19 +294,30 @@ const appSectionIndex = createApp({
ajax(`/v2/api/forum/getTopicOperation`, {
token,
actions: ["like", "collection"],
- }).then((res) => {
- const data = res.data;
- const like = data.like;
- const collection = data.collection;
+ })
+ .then((res) => {
+ console.log("res", res);
- islike.value = like.status;
- iscollect.value = collection.status;
- });
+ 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) => {
@@ -217,19 +355,29 @@ const appSectionIndex = createApp({
let defaultcoinnum = 0;
const getCoinConfig = () => {
- ajaxget(`/v2/api/forum/getCoinConfig`).then((res) => {
+ 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 = () => {
- coinsState.value = true;
- document.body.style.overflow = "hidden";
- if (!coinListRequest) getCoinRankList();
+ BiComponent.initComponent();
+
+ // getCoinConfig();
+ // coinsState.value = true;
+ // document.body.style.overflow = "hidden";
+ // if (!coinListRequest) getCoinRankList();
};
const closeCoinBox = () => {
@@ -270,7 +418,7 @@ const appSectionIndex = createApp({
let coinList = ref([]);
let coinListRequest = false; // 控制请求次数
const getCoinRankList = () => {
- ajaxget(`/v2/api/forum/getCoinRankList?token=${token}&limit=1000`).then((res) => {
+ ajaxGet(`/v2/api/forum/getCoinRankList?token=${token}&limit=1000`).then((res) => {
const data = res.data;
coinNubmer.value = data.nubmer;
coinList.value = data.data;
@@ -281,7 +429,7 @@ const appSectionIndex = createApp({
let relatedList = ref([]);
let relatedTime = ref("");
const getRelatedTopics = () => {
- ajaxget(`/v2/api/forum/getRelatedTopics?uniqid=${uniqid}&limit=8`).then((res) => {
+ ajaxGet(`/v2/api/forum/getRelatedTopics?uniqid=${uniqid}&limit=8`).then((res) => {
const data = res.data;
relatedTime.value = data.updated_at || "";
relatedList.value = data.list || [];
@@ -295,9 +443,10 @@ const appSectionIndex = createApp({
let commentTotalCount = ref(0);
const getComment = () => {
- if (commentPage.value == 0 || isgetCommentSate) return;
+ 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=1500`)
+ ajaxGet(`/v2/api/forum/getCommentList?token=${token}&page=${commentPage.value}&limit=20`)
.then((res) => {
if (res.code != 200) {
creationAlertBox("error", res.message || "");
@@ -309,26 +458,31 @@ const appSectionIndex = createApp({
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--;
- }
- }
- }
+ // 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.count;
+ commentTotalCount.value = data.commentcount;
commentPage.value = data.count > commentList.value.length ? commentPage.value + 1 : 0;
})
.finally(() => {
@@ -339,8 +493,8 @@ const appSectionIndex = createApp({
let picture = ref([]);
const openUserInfo = (index, i) => {
- if (i != undefined && commentList.value[index].child[i].user["uin"] > 0) commentList.value[index].child[i]["avatarState"] = true;
- if (i == undefined && index != undefined && commentList.value[index].user["uin"] > 0) commentList.value[index]["avatarState"] = true;
+ 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) => {
@@ -348,6 +502,8 @@ const appSectionIndex = createApp({
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) {
@@ -364,6 +520,8 @@ const appSectionIndex = createApp({
if (i == null) commentList.value[index]["childState"] = true;
else commentList.value[index].child[i]["childState"] = true;
+
+ isReplyBoxShow.value = false;
};
// 关闭 回答-评论 的子评论
@@ -378,21 +536,24 @@ const appSectionIndex = createApp({
});
}
});
+
+ isReplyBoxShow.value = true;
};
- let dialogSrc = ref("");
const handleAnswerText = (e) => {
if (e.target.tagName === "IMG") {
var src = e.target.getAttribute("src");
- dialogSrc.value = src;
- window.addEventListener("keydown", handleKeydown);
- }
- };
+ const div = document.createElement("div");
+ div.innerHTML = `
`;
+ div.className = "detail-image-mask flexcenter";
+ div.addEventListener("click", () => {
+ document.body.style.overflow = "auto";
+ div.remove();
+ });
- const handleKeydown = (event) => {
- if (event.key !== "Escape") return;
- dialogSrc.value = "";
- window.removeEventListener("keydown", handleKeydown); // 取消监听
+ document.body.appendChild(div);
+ document.body.style.overflow = "hidden";
+ }
};
// 回答-评论 点赞
@@ -407,7 +568,7 @@ const appSectionIndex = createApp({
return;
}
- ajax("https://api.gter.net/v2/api/forum/likeComment", {
+ ajax("/v2/api/forum/likeComment", {
token,
}).then((res) => {
if (res.code != 200) {
@@ -471,8 +632,8 @@ const appSectionIndex = createApp({
editEmojiState.value = false;
};
- const TAHomePage = (uin) => goHomePage(uin);
- const sendMessage = (uin) => goSendMessage(uin);
+ const TAHomePage = (token) => goHomePage(token);
+ const sendMessage = (token) => goSendMessage(token);
let emojiData = ref(["😀", "😁", "😆", "😅", "😂", "😉", "😍", "🥰", "😘", "🤥", "😪", "😵💫", "🤓", "🥺", "😋", "😜", "🤪", "😎", "🤩", "🥳", "😔", "🙁", "😭", "😡", "😳", "🤗", "🤔", "🤭", "🤫", "😯", "😵", "🙄", "🥴", "🤢", "🤑", "🤠", "👌", "✌️", "🤟", "🤘", "🤙", "👍", "👎", "✊", "👏", "🤝", "🙏", "💪", "❎️", "✳️", "✴️", "❇️", "#️⃣", "*️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟", "🆗", "🈶", "🉐", "🉑", "🌹", "🥀", "🌸", "🌺", "🌷", "🌲", "☘️", "🍀", "🍁", "🌙", "⭐", "🌍", "☀️", "⭐️", "🌟", "☁️", "🌈", "☂️", "❄️", "☃️", "☄️", "🔥", "💧", "🍎", "🍐", "🍊", "🍉", "🍓", "🍑", "🍔", "🍟", "🍕", "🥪", "🍜", "🍡", "🍨", "🍦", "🎂", "🍰", "🍭", "🍿", "🍩", "🧃", "🍹", "🍒", "🥝", "🥒", "🥦", "🥨", "🌭", "🥘", "🍱", "🍢", "🥮", "🍩", "🍪", "🧁", "🍵", "🍶", "🍻", "🥂", "🧋", "🎉", "🎁", "🧧", "🎃", "🎄", "🧨", "✨️", "🎈", "🎊", "🎋", "🎍", "🎀", "🎖️", "🏆️", "🏅", "💌", "📬", "🚗", "🚕", "🚲", "🛵", "🚀", "🚁", "⛵", "🚢", "🔮", "🧸", "🀄️"]);
@@ -497,7 +658,7 @@ const appSectionIndex = createApp({
if (!isLogin.value) goLogin();
};
- const maxPicture = 10;
+ let maxPicture = ref(10);
const handleFileUpload = (event, index, i) => {
closeEmoji();
@@ -518,13 +679,11 @@ const appSectionIndex = createApp({
else target = picture.value;
}
- if (target.length >= maxPicture) {
- creationAlertBox("error", `最多只能上传 ${maxPicture} 张图片`);
+ if (target.length >= maxPicture.value) {
+ creationAlertBox("error", `最多只能上传 ${maxPicture.value} 张图片`);
return;
}
- console.log("现有图片", target);
-
const reader = new FileReader();
reader.onload = (e) => {
const base64 = e.target.result;
@@ -580,7 +739,7 @@ const appSectionIndex = createApp({
const getUploadConfig = () => {
return new Promise((resolve, reject) => {
- ajax("https://api.gter.net/v1/config/upload?type=comment").then((res) => {
+ ajaxGet("/v2/api/config/upload?type=comment").then((res) => {
let data = res.data;
uploadConfig = data;
resolve();
@@ -641,6 +800,8 @@ const appSectionIndex = createApp({
images: image,
};
+ console.log("userInfoWin", userInfoWin.value);
+
ajax("/v2/api/forum/postComment", {
content,
token,
@@ -670,6 +831,7 @@ const appSectionIndex = createApp({
attachments,
picture: [],
timestamp,
+ user: { ...userInfoWin.value },
};
commentList.value[index]["child"].push(targetData);
@@ -686,6 +848,7 @@ const appSectionIndex = createApp({
attachments,
picture: [],
timestamp,
+ user: { ...userInfoWin.value },
};
commentList.value[index]["child"].unshift(targetData);
commentList.value[index]["childnum"]++;
@@ -701,6 +864,7 @@ const appSectionIndex = createApp({
attachments,
picture: [],
timestamp,
+ user: { ...userInfoWin.value },
};
commentList.value.unshift(targetData);
inputTextarea.value = "";
@@ -839,8 +1003,8 @@ const appSectionIndex = createApp({
else target = picture.value;
}
- if (target.length >= maxPicture) {
- creationAlertBox("error", `最多只能上传 ${maxPicture} 张图片`);
+ if (target.length >= maxPicture.value) {
+ creationAlertBox("error", `最多只能上传 ${maxPicture.value} 张图片`);
return;
}
@@ -870,6 +1034,39 @@ const appSectionIndex = createApp({
}
};
+ 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"; // 重置高度
@@ -916,7 +1113,108 @@ const appSectionIndex = createApp({
});
};
- return { 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, dialogSrc, 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 };
+ 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 };
},
});
@@ -929,5 +1227,7 @@ 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");
diff --git a/js/edit.js b/js/edit.js
index 269fbac..84eb501 100644
--- a/js/edit.js
+++ b/js/edit.js
@@ -6,12 +6,18 @@ const editApp = createApp({
setup() {
let titleLength = ref(200);
+ let uniqid = ref("");
onMounted(() => {
+ const params = getUrlParams();
+ uniqid.value = params.uniqid || "";
+
getUserInfoWin();
cUpload();
init();
+ checkWConfig();
+
// 添加selectionchange事件监听,当鼠标选中区域内容时更新lastSelection
document.addEventListener("selectionchange", handleSelectionChange);
});
@@ -21,19 +27,46 @@ const editApp = createApp({
document.removeEventListener("selectionchange", handleSelectionChange);
});
- let isLogin = ref(true);
- let realname = ref(1); // 是否已经实名
- let userInfoWin = ref({
- authority: ["comment.edit", "comment.delete", "offercollege.hide", "offersummary.hide", "mj.hide", "topic:manager", "topic:hide"],
- avatar: "https://nas.gter.net:9008/avatar/97K4EWIMLrsbGTWXslC2WFVSEKWOikN42jDKLNjtax7HL4xtfMOJSdU9oWFhY2E~/middle?random=1761733169",
- groupid: 3,
- nickname: "肖荣豪",
- realname: 1,
- token: "01346a38444d71aaadb3adad52b52c39",
- uid: 500144,
- uin: 4238049,
- });
+ let imageLength = 10;
+ let videoLength = 5;
+ 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 || {};
+ titleLength.value = config.max_topic_title_length;
+ imageLength = config.topic_image_count || 0;
+ videoLength = config.topic_video_count || 0;
+ }
+ } else {
+ getWConfig();
+ }
+ };
+
+ const getWConfig = () => {
+ ajaxGet("/v2/api/config/website").then((res) => {
+ if (res.code == 200) {
+ let data = res["data"] || {};
+ const config = data.config || {};
+ titleLength.value = config.max_topic_title_length;
+ imageLength = config.topic_image_count || 0;
+ videoLength = config.topic_video_count || 0;
+
+ data.time = new Date().toISOString();
+ localStorage.setItem("wConfig", JSON.stringify(data));
+ }
+ });
+ };
+
+ let isLogin = ref(false);
+ let realname = ref(0); // 是否已经实名
+ let userInfoWin = ref({});
let permissions = ref([]);
const getUserInfoWin = () => {
@@ -82,22 +115,21 @@ const editApp = createApp({
let token = ref("");
let infoImages = [];
const init = () => {
- ajax("/v2/api/forum/postPublishInit")
+ ajax("/v2/api/forum/postPublishInit", {
+ uniqid: uniqid.value,
+ })
.then((res) => {
const data = res.data;
if (res.code != 200) {
- creationAlertBox(res.message || "操作失败");
+ creationAlertBox("error", res.message || "操作失败");
return;
}
const infoTarget = data.info || {};
- infoImages = infoTarget.attachments?.images || [];
-
- if (infoTarget.content) infoTarget.content = restoreHtml(infoTarget.content, infoImages);
+ if (infoTarget.content) infoTarget.content = restoreHtml(infoTarget.content, infoTarget.attachments);
info.value = infoTarget;
- tagList.value = data.tagList;
token.value = data.token;
nextTick(() => {
@@ -109,7 +141,12 @@ const editApp = createApp({
});
};
- const restoreHtml = (formattedText, imageList) => {
+ const restoreHtml = (formattedText, attachments) => {
+ const imageList = attachments?.images || [];
+
+ const filesList = attachments?.files || [];
+ const videosList = attachments?.videos || [];
+
let html = formattedText;
// 1. 还原换行符为
标签
@@ -130,22 +167,52 @@ const editApp = createApp({
// 查找对应的图片信息
const image = imageList.find((img) => img.aid == aid);
if (image) {
- return `
`;
+ imageList.splice(imageList.indexOf(image), 1);
+ return `
`;
}
return match; // 未找到对应图片时保留原始标记
});
+ html = html.replace(/\[attach\](\d+)\[\/attach\]/gi, (match, aid) => {
+ // 查找对应的图片信息
+ const image = imageList.find((img) => img.aid == aid);
+ if (image) {
+ imageList.splice(imageList.indexOf(image), 1);
+ return `
`;
+ }
+
+ // 查找对应的视频信息
+ const video = videosList.find((v) => v.aid == aid);
+ if (video) {
+ console.log("video", video);
+ videosList.splice(videosList.indexOf(video), 1);
+ return ``;
+ }
+
+ return match; // 未找到对应图片时保留原始标记
+ });
+
// 6. 还原填充标签
html = html.replace(/([^<]+<\/span>)\s+/gi, '$1 ');
// 7. 清理多余的
标签
html = html.replace(/
/g, "
");
+ imageList.forEach((element) => {
+ html += `
`;
+ });
+
+ // video 不要预加载
+ videosList.forEach((element) => {
+ html += `
`;
+ });
+
return html;
};
onMounted(() => {
setTimeout(() => focusLastNode(), 1000);
+ // document.addEventListener("keydown", handleUndoKeydown);
});
const editorRef = ref(null);
@@ -166,51 +233,89 @@ const editApp = createApp({
const maxSize = 20 * 1024 * 1024; // 20MB
const insertImage = (event) => {
- let config = uConfigData;
+ const images = extractImages(editorRef.value);
+
+ const count = imageLength - images.length || 0;
+
+ if (count == 0) {
+ creationAlertBox("error", `最多只能上传 ${imageLength} 张图片`);
+ return;
+ }
+
const target = event.target.files[0];
if (!target) return; // 处理未选择文件的情况
if (target.size > maxSize) {
- creationAlertBox("文件大小不能超过 20MB");
+ creationAlertBox("error", "文件大小不能超过 20MB");
return;
}
loading.value = true;
- // 不要删除,后面会用
- const formData = new FormData();
- formData.append(config.requestName, target); // 文件数据
- formData.append("name", target.name); // 文件名
- formData.append("type", "image"); // 文件名
- formData.append("data", config.params.data); // 文件名
+ uploading(target, target.name, "image").then((data) => {
+ const selection = window.getSelection();
+ editorRef.value.focus();
+ if (lastSelection) {
+ selection.removeAllRanges();
+ selection.addRange(lastSelection);
+ }
+ const html = `
`;
+ document.execCommand("insertHTML", false, html);
+ judgeIsEmpty();
+ });
+ };
- ajax(config.url, formData)
- .then((res) => {
- const data = res.data;
- try {
- const range = lastSelection;
- const img = document.createElement("img");
+ const insertVideo = async (event) => {
+ const videos = extractVideos(editorRef.value);
- img.src = data.url;
- img.setAttribute("data-aid", data.aid);
- range.insertNode(img);
- const div = document.createElement("div");
- range.insertNode(div);
- judgeIsEmpty();
- } catch (error) {
- console.error("插入图片出错:", error);
- }
- })
- .finally(() => {
- loading.value = false;
- });
+ const count = videoLength - videos.length || 0;
+
+ if (count == 0) {
+ creationAlertBox("error", `最多只能上传 ${videoLength} 个视频`);
+ return;
+ }
+
+ const videoFile = event.target.files[0];
+
+ if (!videoFile) return; // 处理未选择文件的情况
+
+ if (videoFile.size > maxSize) {
+ creationAlertBox("error", "文件大小不能超过 20MB");
+ return;
+ }
+
+ loading.value = true;
+
+ console.log("videoFile", videoFile);
+
+ // 步骤1:提取视频第一帧(等待提取完成)
+ const coverFile = await getVideoFirstFrame(videoFile);
+ console.log("第一帧提取成功", coverFile);
+
+ // 步骤2:先上传视频文件(type 传 'video',按后端要求调整)
+ const videoUploadRes = await uploading(videoFile, videoFile.name, "video");
+ console.log("视频上传成功", videoUploadRes);
+
+ // 步骤3:再上传第一帧封面(type 传 'cover',按后端要求调整)
+ const coverUploadRes = await uploading(coverFile, coverFile.name, "image");
+ console.log("封面上传成功", coverUploadRes);
+
+ console.log("最终", videoUploadRes, videoUploadRes);
+
+ const selection = window.getSelection();
+ editorRef.value.focus();
+ if (lastSelection) {
+ selection.removeAllRanges();
+ selection.addRange(lastSelection);
+ }
+ const html = `
`;
+ document.execCommand("insertHTML", false, html);
+ judgeIsEmpty();
};
let isEmpty = ref(true);
const onEditorInput = (event) => {
- console.log("onEditorInput");
-
const selection = window.getSelection();
if (selection.rangeCount > 0) {
@@ -221,7 +326,7 @@ const editApp = createApp({
judgeIsEmpty();
- debouncedGetTagList();
+ // debouncedGetTagList();
};
// 防抖函数
@@ -260,26 +365,28 @@ const editApp = createApp({
};
const getTagList = () => {
+ if (!isLogin.value) {
+ goLogin();
+ return;
+ }
const content = editorRef.value.innerText;
- axios
- .post("https://api.gter.net/v2/api/forum/postPublishTags", {
- content,
- })
- .then((res) => {
- res = res.data;
- if (res.code != 200) return;
- let data = res.data || [];
+ ajax("/v2/api/forum/postPublishTags", {
+ content,
+ }).then((res) => {
+ res = res.data;
+ if (res.code != 200) return;
+ let data = res.data || [];
- // 随机生成一下数据
- for (let i = 0; i < 5; i++) {
- data.push({
- title: getRandomChinese() + getRandomChinese(),
- tagId: generateRandomString(),
- });
- }
+ // 随机生成一下数据
+ for (let i = 0; i < 5; i++) {
+ data.push({
+ title: getRandomChinese() + getRandomChinese(),
+ tagId: generateRandomString(),
+ });
+ }
- tagList.value = data;
- });
+ tagList.value = data;
+ });
};
const debouncedGetTagList = debounce(getTagList, 500);
@@ -296,7 +403,7 @@ const editApp = createApp({
// 判断是否为空
const judgeIsEmpty = () => {
const text = editorRef.value.innerText;
- isEmpty.value = text.length == 0 && !editorRef.value.querySelector("img");
+ isEmpty.value = text.length == 0 && !editorRef.value.querySelector("img") && !editorRef.value.querySelector("video");
};
// 处理选中文本变化的函数
@@ -309,7 +416,6 @@ const editApp = createApp({
const commonAncestor = range.commonAncestorContainer;
if (editorRef.value.contains(commonAncestor)) {
console.log("选中区域在编辑器内", range);
-
lastSelection = range;
}
}
@@ -366,36 +472,6 @@ const editApp = createApp({
const cutAnonymity = () => (info.value.anonymous = info.value.anonymous ? 0 : 1);
- const insertLabel = (id) => {
- const index = tagList.value.findIndex((item) => item.tagId == id);
- if (index == -1) return;
- const label = tagList.value[index].title;
-
- const span = document.createElement("span");
- span.innerHTML = `#${label} `;
- lastSelection.insertNode(span);
-
- // 移动光标到元素后面并确保光标位置被正确设置和获取
- const newRange = document.createRange();
- newRange.setStartAfter(span);
- newRange.setEndAfter(span);
-
- // 更新选择范围
- const selection = window.getSelection();
- selection.removeAllRanges();
- selection.addRange(newRange);
- lastSelection = newRange;
-
- // 手动触发selectionchange事件,确保其他组件知道光标位置变化
- const selectionChangeEvent = new Event("selectionchange", { bubbles: true });
- document.dispatchEvent(selectionChangeEvent);
-
- judgeIsEmpty();
-
- // 删除 tagList 中当前标签
- tagList.value.splice(index, 1);
- };
-
let emojiState = ref(false);
const optionEmoji = ref(["😀", "😁", "😆", "😅", "😂", "😉", "😍", "🥰", "😘", "🤥", "😪", "😵💫", "🤓", "🥺", "😋", "😜", "🤪", "😎", "🤩", "🥳", "😔", "🙁", "😭", "😡", "😳", "🤗", "🤔", "🤭", "🤫", "😯", "😵", "🙄", "🥴", "🤢", "🤑", "🤠", "👌", "✌️", "🤟", "🤘", "🤙", "👍", "👎", "✊", "👏", "🤝", "🙏", "💪", "❎️", "✳️", "✴️", "❇️", "#️⃣", "*️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟", "🆗", "🈶", "🉐", "🉑", "🌹", "🥀", "🌸", "🌺", "🌷", "🌲", "☘️", "🍀", "🍁", "🌙", "⭐", "🌍", "☀️", "⭐️", "🌟", "☁️", "🌈", "☂️", "❄️", "☃️", "☄️", "🔥", "💧", "🍎", "🍐", "🍊", "🍉", "🍓", "🍑", "🍔", "🍟", "🍕", "🥪", "🍜", "🍡", "🍨", "🍦", "🎂", "🍰", "🍭", "🍿", "🍩", "🧃", "🍹", "🍒", "🥝", "🥒", "🥦", "🥨", "🌭", "🥘", "🍱", "🍢", "🥮", "🍩", "🍪", "🧁", "🍵", "🍶", "🍻", "🥂", "🧋", "🎉", "🎁", "🧧", "🎃", "🎄", "🧨", "✨️", "🎈", "🎊", "🎋", "🎍", "🎀", "🎖️", "🏆️", "🏅", "💌", "📬", "🚗", "🚕", "🚲", "🛵", "🚀", "🚁", "⛵", "🚢", "🔮", "🧸", "🀄️"]);
@@ -405,22 +481,13 @@ const editApp = createApp({
const closeEmoji = () => (emojiState.value = false);
const selectEmoji = (emoji) => {
- const textNode = document.createTextNode(emoji);
- lastSelection.insertNode(textNode);
-
- // 移动光标到emoji后面并确保光标位置被正确设置和获取
- const newRange = document.createRange();
- newRange.setStartAfter(textNode);
- newRange.setEndAfter(textNode);
-
- // 更新选择范围
const selection = window.getSelection();
- selection.removeAllRanges();
- lastSelection = newRange;
-
- // 手动触发selectionchange事件,确保其他组件知道光标位置变化
- const selectionChangeEvent = new Event("selectionchange", { bubbles: true });
- document.dispatchEvent(selectionChangeEvent);
+ editorRef.value.focus();
+ if (lastSelection) {
+ selection.removeAllRanges();
+ selection.addRange(lastSelection);
+ }
+ document.execCommand("insertText", false, emoji);
closeEmoji();
judgeIsEmpty();
};
@@ -430,14 +497,21 @@ const editApp = createApp({
const infoTarget = { ...info.value } || {};
let content = editorRef.value.innerHTML;
- const images = extractImages(content);
+ const images = extractImages(editorRef.value);
+ const videos = extractVideos(editorRef.value);
+ infoTarget.attachments = infoTarget.attachments || {};
infoTarget.attachments.images = images;
+ infoTarget.attachments.videos = videos;
+ info.value["attachments"] = info.value["attachments"] || {};
info.value["attachments"]["images"] = images;
- console.log("转换前:", content);
+ info.value["attachments"]["videos"] = videos;
+
+ console.log(content);
content = formatContent(content);
- console.log("转换后:", content);
+ console.log(content);
+
const data = {
...infoTarget,
content,
@@ -456,8 +530,8 @@ const editApp = createApp({
creationAlertBox("success", res.message || "操作成功");
const back = () => {
- if (status == 1) redirectToExternalWebsite("./details.html?uniqid=" + data.uniqid);
- else redirectToExternalWebsite("./index.html");
+ if (status == 1) redirectToExternalWebsite("/details/" + data.uniqid, "_self");
+ else redirectToExternalWebsite("/", "_self");
};
setTimeout(() => back(), 1500);
@@ -468,15 +542,12 @@ const editApp = createApp({
// 1. 替换图片标签
html = html.replace(/
]*data-aid="(\d+)"[^>]*>/gi, "[attachimg]$1[/attachimg]");
+ // 1.1 替换视频标签
+ html = html.replace(/