From 68000d7e43f4bcfe6d3ee24a6d30e551d45eb361 Mon Sep 17 00:00:00 2001
From: "DESKTOP-RQ919RC\\Pc" <1300399510@qq.com>
Date: Tue, 11 Nov 2025 19:05:46 +0800
Subject: [PATCH] =?UTF-8?q?feat(Details):=20=E6=96=B0=E5=A2=9E=E6=8A=95?=
=?UTF-8?q?=E7=A5=A8=E8=AF=A6=E6=83=85=E9=A1=B5=E7=AE=A1=E7=90=86=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD=E5=92=8C=E8=AF=84=E8=AE=BA=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 添加投票详情页的管理功能,包括隐藏、推荐、精华和删除操作
- 优化评论组件,支持多图上传和显示
- 新增投币功能组件
- 更新API接口调用方式,适配新后端接口
- 完善用户权限管理逻辑
- 修复样式问题和交互体验
---
app.vue | 12 +-
assets/css/DetailsComments.css | 4 +
assets/css/DetailsComments.less | 4 +
assets/css/details.css | 72 ++++++++
assets/css/details.less | 83 +++++++++
assets/img/bi-black-icon.png | Bin 0 -> 1623 bytes
assets/img/dot-dot-dot-gray.png | Bin 0 -> 542 bytes
components/DetailsArea.vue | 67 ++++---
components/DetailsComments.vue | 248 +++++++++++++++----------
components/Like.vue | 1 +
composables/api.js | 46 ++++-
composables/utils.js | 311 +++++++++++++++++++++++---------
pages/details/[id].vue | 194 ++++++++++++++++++--
utils/http.js | 23 ++-
14 files changed, 839 insertions(+), 226 deletions(-)
create mode 100644 assets/img/bi-black-icon.png
create mode 100644 assets/img/dot-dot-dot-gray.png
diff --git a/app.vue b/app.vue
index 92ae9a4..e268fea 100644
--- a/app.vue
+++ b/app.vue
@@ -49,6 +49,8 @@ let isNeedLogin = ref(true); // 是否需要登录状态
let isGetLoginState = ref(true); // 在获取登录状态 false 代表没有已经确定了
let realname = ref(1); // 是否已经实名
let userInfoWin = ref({});
+let permissions = ref([]);
+let ismanager = ref(false);
provide("userInfoWin", userInfoWin);
@@ -81,12 +83,15 @@ const goLogin = () => {
const getUserInfoWin = () => {
const checkUser = () => {
- const user = window.userInfoWin;
+ 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) isNeedLogin.value = false;
+
+ permissions.value = user?.authority || [];
+ ismanager.value = permissions.value.indexOf("topic:manager") >= 0;
};
document.addEventListener("getUser", checkUser);
};
@@ -106,6 +111,9 @@ provide("goLogin", goLogin);
provide("isGetLoginState", isGetLoginState);
provide("realname", realname);
provide("openAttest", openAttest);
+provide("permissions", permissions);
+provide("ismanager", ismanager);
+
diff --git a/composables/api.js b/composables/api.js
index 0b6b4f5..81dc3e1 100644
--- a/composables/api.js
+++ b/composables/api.js
@@ -11,22 +11,26 @@ export const detailsHttp = (query) => {
// 详情数据 - 获取评论数据
export const commentListHttp = (params) => {
- return Http.post("/api/comment/lists", params);
+ // return Http.post("/api/comment/lists", params);
+ return Http.get("https://api.gter.net/v2/api/forum/getCommentList", params);
};
// 评论相关 - 评论点赞
export const detailsLikeCommentHttp = (query) => {
- return Http.post("/api/comment/like", query);
+ // return Http.post("/api/comment/like", query);
+ return Http.post("https://api.gter.net/v2/api/forum/likeComment", query);
};
// 详情数据 - 获取子评论数据
export const detailsChildCommentListHttp = (query) => {
- return Http.post("/api/comment/childrenList", query);
+ // return Http.post("/api/comment/childrenList", query);
+ return Http.post("https://api.gter.net/v2/api/forum/childrenList", query);
};
// 详情数据 - 提交评论
export const detailsSubmitommentListHttp = (query) => {
- return Http.post("/api/comment/submit", query);
+ // return Http.post("/api/comment/submit", query);
+ return Http.postV2("https://api.gter.net/v2/api/forum/postComment", query);
};
// 详情数据 - 提交评论
@@ -56,12 +60,14 @@ export const deleteHttp = (query) => {
// 操作-点赞
export const operateLikeHttp = (query) => {
- return Http.post("/api/operate/like", query);
+ // return Http.post("/api/operate/like", query);
+ return Http.post("https://api.gter.net/v2/api/forum/postTopicLike", query);
};
// 数据操作 - 收藏
export const operateCollectHttp = (query) => {
- return Http.post("/api/operate/collect", query);
+ // return Http.post("/api/operate/collect", query);
+ return Http.post("https://api.gter.net/v2/api/forum/postTopicCollect", query);
};
// 数据操作 - 投票操作
@@ -101,7 +107,8 @@ export const MyUserCollectHttp = (query) => {
// 评论相关 - 举报 Comment related
export const commentReportHttp = (query) => {
- return Http.post("/api/comment/report", query);
+ // return Http.post("/api/comment/report", query);
+ return Http.postV2("https://api.gter.net/v2/api/forum/postTopicReport", query);
};
// 回应相关 - 回应列表
@@ -130,3 +137,28 @@ export const commonUploadConfigHttp = (query) => {
export const commentDeleteHttp = (query) => {
return Http.post("/api/comment/commentDelete", query);
};
+
+// 详情数据 - 投票详情
+export const topicDetailHttp = (query) => {
+ return Http.get("https://api.gter.net/v2/api/forum/getTopicDetails", query);
+};
+
+export const topicRecommendHttp = (query) => {
+ return Http.post("https://api.gter.net/v2/api/forum/setTopicRecommend", query);
+};
+
+export const topicEssenceHttp = (query) => {
+ return Http.post("https://api.gter.net/v2/api/forum/setTopicBest", query);
+};
+
+export const topicDeleteHttp = (query) => {
+ return Http.del("https://api.gter.net/v2/api/forum/deleteTopic", query);
+};
+
+export const topicHideHttp = (query) => {
+ return Http.post("https://api.gter.net/v2/api/forum/setTopicHide", query);
+};
+
+export const topicgetOperationHttp = (query) => {
+ return Http.post("https://api.gter.net/v2/api/forum/getTopicOperation", query);
+};
diff --git a/composables/utils.js b/composables/utils.js
index 46dc17f..7ee5474 100644
--- a/composables/utils.js
+++ b/composables/utils.js
@@ -1,115 +1,256 @@
// 处理时间
export const handleDate = (dateTimeStamp = new Date()) => {
- dateTimeStamp = dateTimeStamp ? dateTimeStamp : null
- if (!dateTimeStamp) return '刚刚'
- var timestamp = new Date(dateTimeStamp)
- timestamp = timestamp.getTime()
- var minute = 1000 * 60
- var hour = minute * 60
- var day = hour * 24
- var now = new Date().getTime()
- var diffValue = now - timestamp
- var result
- if (diffValue < 0) return
+ dateTimeStamp = dateTimeStamp ? dateTimeStamp : null;
+ if (!dateTimeStamp) return "刚刚";
+ var timestamp = new Date(dateTimeStamp);
+ timestamp = timestamp.getTime();
+ var minute = 1000 * 60;
+ var hour = minute * 60;
+ var day = hour * 24;
+ var now = new Date().getTime();
+ var diffValue = now - timestamp;
+ var result;
+ if (diffValue < 0) return;
- var dayC = diffValue / day
- var hourC = diffValue / (hour + 1)
- var minC = diffValue / minute
+ var dayC = diffValue / day;
+ var hourC = diffValue / (hour + 1);
+ var minC = diffValue / minute;
if (dayC >= 7) {
- let date = new Date(timestamp)
- let Y = date.getFullYear() + "-"
- let M = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1) + "-"
- let D = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " "
+ let date = new Date(timestamp);
+ let Y = date.getFullYear() + "-";
+ let M = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1) + "-";
+ let D = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " ";
// let h = (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":"
// let m = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()
- result = "" + Y + M + D
- } else if (dayC >= 1) result = "" + Math.round(dayC) + "天前"
- else if (hourC >= 1) result = "" + Math.round(hourC) + "小时前"
- else if (minC >= 1) result = "" + Math.round(minC) + "分钟前"
- else result = "刚刚"
+ result = "" + Y + M + D;
+ } else if (dayC >= 1) result = "" + Math.round(dayC) + "天前";
+ else if (hourC >= 1) result = "" + Math.round(hourC) + "小时前";
+ else if (minC >= 1) result = "" + Math.round(minC) + "分钟前";
+ else result = "刚刚";
- return result
-}
+ return result;
+};
// 处理 截止时间
export const handleDeadline = (dateTimeStamp = new Date()) => {
- if (typeof dateTimeStamp == "number") dateTimeStamp = dateTimeStamp ? dateTimeStamp * 1000 : null
+ if (typeof dateTimeStamp == "number") dateTimeStamp = dateTimeStamp ? dateTimeStamp * 1000 : null;
if (typeof dateTimeStamp == "string" && dateTimeStamp.match(/^\d{4}-\d{2}-\d{2}$/)) dateTimeStamp += " 23:59:59";
+ var timestamp = new Date(dateTimeStamp);
+ timestamp = timestamp.getTime();
+ var minute = 1000 * 60;
+ var hour = minute * 60;
+ var day = hour * 24;
+ var now = new Date().getTime();
+ var diffValue = timestamp - now;
+ var result;
+ if (diffValue < 0) return "投票已";
- var timestamp = new Date(dateTimeStamp)
- timestamp = timestamp.getTime()
- var minute = 1000 * 60
- var hour = minute * 60
- var day = hour * 24
- var now = new Date().getTime()
- var diffValue = timestamp - now
- var result
- if (diffValue < 0) return "投票已"
+ var dayC = diffValue / day;
+ var hourC = diffValue / (hour + 1);
+ var minC = diffValue / minute;
+ if (dayC >= 1) result = "" + Math.round(dayC) + "天后";
+ else if (hourC >= 1) result = "" + Math.round(hourC) + "小时后";
+ else if (minC >= 1) result = "" + Math.round(minC) + "分钟后";
- var dayC = diffValue / day
- var hourC = diffValue / (hour + 1)
- var minC = diffValue / minute
- if (dayC >= 1) result = "" + Math.round(dayC) + "天后"
- else if (hourC >= 1) result = "" + Math.round(hourC) + "小时后"
- else if (minC >= 1) result = "" + Math.round(minC) + "分钟后"
-
- return result
-}
+ return result;
+};
export const timestampToDate = (timestamp) => {
var date = new Date(timestamp);
var year = date.getFullYear();
var month = ("0" + (date.getMonth() + 1)).slice(-2); // Months are zero based. Add leading 0.
- var day = ("0" + date.getDate()).slice(-2); // Add leading 0.
+ var day = ("0" + date.getDate()).slice(-2); // Add leading 0.
return `${year}-${month}-${day}`;
-}
-
+};
// isblank 是否需要 新标签页 默认是新标签页
export const goToURL = (url, isblank = true) => {
- if (typeof document !== "object") return
+ if (typeof document !== "object") return;
- let aTab = document.createElement('a')
- document.body.appendChild(aTab)
- aTab.setAttribute('href', url)
- if (isblank) aTab.setAttribute('target', "_blank")
- aTab.click()
-}
+ let aTab = document.createElement("a");
+ document.body.appendChild(aTab);
+ aTab.setAttribute("href", url);
+ if (isblank) aTab.setAttribute("target", "_blank");
+ aTab.click();
+};
-export const colourValue = [{
- main: "rgba(44, 186, 230, 1)",
- bg: "rgba(234, 245, 248, 1)",
- bc: "rgba(213, 235, 242, 1)",
-}, {
- main: "rgba(49, 215, 46, 1)",
- bg: "rgba(244, 247, 244, 1)",
- bc: "rgba(225, 244, 225, 1)",
-}, {
- main: "rgba(106, 117, 217, 1)",
- bg: "rgba(237, 238, 247, 1)",
- bc: "rgba(227, 228, 246, 1)",
-}, {
- main: "rgba(172, 183, 46, 1)",
- bg: "rgba(245, 246, 228, 1)",
- bc: "rgba(238, 238, 215, 1)",
-}, {
- main: "rgba(38, 223, 190, 1)",
- bg: "rgba(237, 247, 245, 1)",
- bc: "rgba(220, 244, 239, 1)",
-}, {
- main: "rgba(242, 122, 71, 1)",
- bg: "rgba(255, 244, 239, 1)",
- bc: "rgba(249, 231, 224, 1)",
-}]
+export const colourValue = [
+ {
+ main: "rgba(44, 186, 230, 1)",
+ bg: "rgba(234, 245, 248, 1)",
+ bc: "rgba(213, 235, 242, 1)",
+ },
+ {
+ main: "rgba(49, 215, 46, 1)",
+ bg: "rgba(244, 247, 244, 1)",
+ bc: "rgba(225, 244, 225, 1)",
+ },
+ {
+ main: "rgba(106, 117, 217, 1)",
+ bg: "rgba(237, 238, 247, 1)",
+ bc: "rgba(227, 228, 246, 1)",
+ },
+ {
+ main: "rgba(172, 183, 46, 1)",
+ bg: "rgba(245, 246, 228, 1)",
+ bc: "rgba(238, 238, 215, 1)",
+ },
+ {
+ main: "rgba(38, 223, 190, 1)",
+ bg: "rgba(237, 247, 245, 1)",
+ bc: "rgba(220, 244, 239, 1)",
+ },
+ {
+ main: "rgba(242, 122, 71, 1)",
+ bg: "rgba(255, 244, 239, 1)",
+ bc: "rgba(249, 231, 224, 1)",
+ },
+];
-export const base62ToDecimal = base62 => {
- const base = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
- let decimal = 0
+export const base62ToDecimal = (base62) => {
+ const base = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ let decimal = 0;
for (let i = 0; i < base62.length; i++) {
- decimal += base.indexOf(base62[i]) * Math.pow(62, base62.length - i - 1)
+ decimal += base.indexOf(base62[i]) * Math.pow(62, base62.length - i - 1);
}
- return decimal
-}
\ No newline at end of file
+ return decimal;
+};
+
+export const managerHide = (token, state, type = "offer") => {
+ return new Promise((resolve, reject) => {
+ const obj = {
+ offer: "Offer",
+ offer_summary: "总结",
+ interviewexperience: "面经",
+ thread: "帖子",
+ question: "帖子",
+ vote: "投票",
+ };
+
+ const isConfirmed = confirm(`确定要${state == 0 ? "隐藏" : "显示"}该${obj[type]}吗?`);
+ if (isConfirmed) {
+ topicHideHttp({
+ token,
+ hidden: Number(state !== 1),
+ }).then((res) => {
+ const data = res.data;
+ creationAlertBox("success", res.message || "");
+ resolve(data.hidden);
+ });
+ }
+ });
+};
+
+// 推荐
+export const managerRecommend = (token, state) => {
+ return new Promise((resolve, reject) => {
+ const post = () => {
+ topicRecommendHttp({
+ token,
+ recommend: state == 1 ? 0 : 1,
+ }).then((res) => {
+ const data = res.data;
+ creationAlertBox("success", res.message || "");
+ resolve(data.recommend);
+ });
+ };
+
+ if (state == 1) {
+ const isConfirmed = confirm(`确定要取消推荐吗?`);
+ if (isConfirmed) post();
+ else resolve(state);
+ } else post();
+ });
+};
+
+// 精华
+export const managerEssence = (token, state) => {
+ return new Promise((resolve, reject) => {
+ const post = () => {
+ topicEssenceHttp({
+ token,
+ best: state == 1 ? 0 : 1,
+ }).then((res) => {
+ const data = res.data;
+ creationAlertBox("success", res.message || "");
+ resolve(data.best);
+ });
+ };
+
+ if (state == 1) {
+ const isConfirmed = confirm(`确定要取消精华吗?`);
+ if (isConfirmed) post();
+ else resolve(state);
+ } else post();
+ });
+};
+
+// 删除
+export const managerDelete = (token) => {
+ return new Promise((resolve, reject) => {
+ const post = () => {
+ topicDeleteHttp({
+ token,
+ }).then((res) => {
+ creationAlertBox("success", res.message || "");
+ resolve();
+ });
+ };
+
+ const isConfirmed = confirm(`确定要删除吗?`);
+ if (!isConfirmed) reject();
+ else post();
+ });
+};
+
+export const strtimeago = (dateStr, type = 1) => {
+ dateStr = dateStr + ""; // 反之传入的不是字符串
+ dateStr = dateStr.replaceAll("-", "/"); // 修改格式
+ var minute = 1000 * 60; // 把分,时,天,周,半个月,一个月用毫秒表示
+ var hour = minute * 60;
+ var day = hour * 24;
+ var now = new Date().getTime(); // 获取当前时间毫秒
+ let objectTime = new Date(dateStr).getTime();
+ var diffValue = now - objectTime; // 时间差
+ if (diffValue < 0) return "刚刚";
+
+ var minC = diffValue / minute; // 计算时间差的分,时,天,周,月
+ var hourC = diffValue / hour;
+ var dayC = diffValue / day;
+
+ const diffInMilliseconds = now - objectTime;
+ const diffInYears = diffInMilliseconds / (1000 * 60 * 60 * 24 * 365);
+ const diffInMonths = diffInYears * 12;
+
+ let result = "";
+ if (dayC >= 7) {
+ var datetime = new Date(dateStr);
+ var Nyear = datetime.getFullYear();
+ var Nmonth = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;
+ var Ndate = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();
+ var Nhour = datetime.getHours() < 10 ? "0" + datetime.getHours() : datetime.getHours();
+ var Nmin = datetime.getMinutes() < 10 ? "0" + datetime.getMinutes() : datetime.getMinutes();
+ if (type == 4) {
+ if (new Date().getFullYear() !== Nyear) result = `${Nyear}年${Nmonth}月${Ndate}日`;
+ else result = `${Nmonth}月${Ndate}日 ${Nhour}:${Nmin}`;
+ }
+ if (type == 1) result = Nyear + "-" + Nmonth + "-" + Ndate;
+ if (type == 2) {
+ result = `${Nmonth}月${Ndate}日 ${Nhour}:${Nmin}`;
+ if (new Date().getFullYear() !== Nyear) result = `${Nyear}年${result}`;
+ }
+ if (type == 3) {
+ if (diffInYears >= 1) result = Math.floor(diffInYears) + "年前";
+ else if (diffInMonths >= 1) result = Math.floor(diffInMonths) + "个月前";
+ else result = parseInt(dayC) + "天前";
+ }
+ } else if (dayC >= 1) result = parseInt(dayC) + "天前";
+ else if (hourC >= 1 && hourC <= 24) result = parseInt(hourC) + "小时前";
+ else if (minC >= 1 && minC <= 60) result = parseInt(minC) + "分钟前";
+ else result = "刚刚";
+
+ return result;
+};
diff --git a/pages/details/[id].vue b/pages/details/[id].vue
index a17905b..2183d86 100644
--- a/pages/details/[id].vue
+++ b/pages/details/[id].vue
@@ -1,4 +1,5 @@
+
{{ `${seo["title"] || "投票"} - 寄托天下出国留学网` }}
@@ -7,13 +8,39 @@
@@ -147,8 +173,7 @@
- 共
{{ ripostecount.user }}人回应
+ 共
{{ ripostecount.user }}人回应
@@ -166,6 +191,9 @@
+
+
+