diff --git a/babel.config.js b/babel.config.js
index 5df4526..ee2edf5 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -9,6 +9,7 @@ module.exports = {
compiler: 'webpack5',
}]
],
+
plugins: [
[
"import",
@@ -46,7 +47,12 @@ module.exports = {
// 自动加载 css 样式文件
// customStyleName: (name) => `@nutui/nutui-react-taro/dist/es/packages/${name.toLowerCase()}/style-jrkf/css`
},
+
+
'nutui-react',
],
+
+ ['transform-remove-console', { exclude: ['error', 'warn'] }],
+ ['@babel/plugin-transform-runtime', { corejs: false }]
],
}
diff --git a/package.json b/package.json
index 71d29f8..2207efa 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
],
"author": "",
"dependencies": {
+ "@babel/plugin-transform-runtime": "^7.28.3",
"@babel/runtime": "^7.21.5",
"@nutui/nutui-react-taro": "^2.6.14",
"@tarojs/components": "4.1.5",
@@ -53,6 +54,7 @@
"@tarojs/runtime": "4.1.5",
"@tarojs/shared": "4.1.5",
"@tarojs/taro": "4.1.5",
+ "babel-plugin-transform-remove-console": "^6.9.4",
"dayjs": "^1.11.13",
"qweather-icons": "^1.8.0",
"react": "^18.0.0",
diff --git a/project.config.json b/project.config.json
index eaad1b7..00f5065 100644
--- a/project.config.json
+++ b/project.config.json
@@ -1,46 +1,46 @@
{
- "miniprogramRoot": "dist/",
- "projectname": "playBallTogether",
- "description": "playBallTogether",
- "appid": "wx815b533167eb7b53",
- "setting": {
- "urlCheck": true,
- "es6": true,
- "enhance": true,
- "postcss": false,
- "preloadBackgroundData": false,
- "minified": false,
- "newFeature": true,
- "coverView": true,
- "nodeModules": false,
- "autoAudits": false,
- "showShadowRootInWxmlPanel": false,
- "scopeDataCheck": false,
- "uglifyFileName": false,
- "checkInvalidKey": true,
- "checkSiteMap": true,
- "uploadWithSourceMap": true,
- "compileHotReLoad": false,
- "useMultiFrameRuntime": true,
- "useApiHook": true,
- "useApiHostProcess": false,
- "babelSetting": {
- "ignore": [],
- "disablePlugins": [],
- "outputPath": ""
- },
- "enableEngineNative": false,
- "useIsolateContext": true,
- "useCompilerModule": false,
- "userConfirmedUseCompilerModuleSwitch": false,
- "userConfirmedBundleSwitch": false,
- "packNpmManually": false,
- "packNpmRelationList": [],
- "minifyWXSS": true,
- "minifyWXML": true
- },
- "compileType": "miniprogram",
- "simulatorType": "wechat",
- "simulatorPluginLibVersion": {},
- "condition": {}
- }
+ "miniprogramRoot": "dist/",
+ "projectname": "playBallTogether",
+ "description": "playBallTogether",
+ "appid": "wx815b533167eb7b53",
+ "setting": {
+ "urlCheck": true,
+ "es6": true,
+ "enhance": true,
+ "postcss": true,
+ "preloadBackgroundData": false,
+ "minified": true,
+ "newFeature": true,
+ "coverView": true,
+ "nodeModules": false,
+ "autoAudits": false,
+ "showShadowRootInWxmlPanel": false,
+ "scopeDataCheck": false,
+ "uglifyFileName": false,
+ "checkInvalidKey": true,
+ "checkSiteMap": true,
+ "uploadWithSourceMap": true,
+ "compileHotReLoad": false,
+ "useMultiFrameRuntime": true,
+ "useApiHook": true,
+ "useApiHostProcess": false,
+ "babelSetting": {
+ "ignore": [],
+ "disablePlugins": [],
+ "outputPath": ""
+ },
+ "enableEngineNative": false,
+ "useIsolateContext": true,
+ "useCompilerModule": false,
+ "userConfirmedUseCompilerModuleSwitch": false,
+ "userConfirmedBundleSwitch": false,
+ "packNpmManually": false,
+ "packNpmRelationList": [],
+ "minifyWXSS": true,
+ "minifyWXML": true
+ },
+ "compileType": "miniprogram",
+ "simulatorType": "wechat",
+ "simulatorPluginLibVersion": {},
+ "condition": {}
+}
\ No newline at end of file
diff --git a/project.private.config.json b/project.private.config.json
index 2c19aa8..aaeb056 100644
--- a/project.private.config.json
+++ b/project.private.config.json
@@ -3,12 +3,12 @@
"projectname": "playBallTogether",
"condition": {},
"setting": {
- "urlCheck": true,
+ "urlCheck": false,
"coverView": true,
"lazyloadPlaceholderEnable": false,
"skylineRenderEnable": false,
"preloadBackgroundData": false,
- "autoAudits": false,
+ "autoAudits": true,
"useApiHook": true,
"useApiHostProcess": true,
"showShadowRootInWxmlPanel": false,
diff --git a/src/app.config.ts b/src/app.config.ts
index 5ae88f1..09a4ec6 100644
--- a/src/app.config.ts
+++ b/src/app.config.ts
@@ -54,6 +54,8 @@ export default defineAppConfig({
root: 'other_pages',
pages: [
"message/index",
+ "comment_reply/index", // 收到的评论和回复
+ "new_follow/index", // 新增关注
"favorites/index", // 收藏页
"ntrp-evaluate/index", // NTRP评估页
],
@@ -84,4 +86,5 @@ export default defineAppConfig({
provider: "wx76a9a06e5b4e693e",
},
},
+ "lazyCodeLoading": "requiredComponents"
});
diff --git a/src/app.ts b/src/app.ts
index 565dd5f..bf0017c 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -1,7 +1,7 @@
import { Component, ReactNode } from "react";
import "./nutui-theme.scss";
import "./app.scss";
-import "qweather-icons/font/qweather-icons.css";
+import "./scss/qweather-icons/qweather-icons.css";
import { useGlobalStore } from "./store/global";
interface AppProps {
diff --git a/src/components/EmptyState/index.scss b/src/components/EmptyState/index.scss
new file mode 100644
index 0000000..dd79951
--- /dev/null
+++ b/src/components/EmptyState/index.scss
@@ -0,0 +1,33 @@
+.empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 124px 16px;
+ min-height: 400px;
+
+ .empty-icon {
+ width: 221px;
+ height: 200px;
+ position: relative;
+ border-radius: 20px;
+ margin-bottom: 12px;
+
+ .img {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 100%;
+ height: 100%;
+ }
+ }
+
+ .empty-text {
+ font-family: 'PingFang SC';
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 1.71;
+ color: rgba(0, 0, 0, 0.35);
+ }
+}
diff --git a/src/components/EmptyState/index.tsx b/src/components/EmptyState/index.tsx
new file mode 100644
index 0000000..f0d2f14
--- /dev/null
+++ b/src/components/EmptyState/index.tsx
@@ -0,0 +1,23 @@
+import { View, Text, Image } from "@tarojs/components";
+import "./index.scss";
+
+interface EmptyStateProps {
+ text?: string;
+ icon?: any; // 图片资源
+}
+
+const EmptyState = ({
+ text = "暂无数据",
+ icon = require("@/static/message/emi.svg")
+}: EmptyStateProps) => {
+ return (
+
+
+
+
+ {text}
+
+ );
+};
+
+export default EmptyState;
diff --git a/src/components/index.ts b/src/components/index.ts
index be082a9..49d9462 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -23,6 +23,7 @@ import FollowUserCard from './FollowUserCard/index';
import Comments from "./Comments";
import GeneralNavbar from "./GeneralNavbar";
import RadarChart from './Radar'
+import EmptyState from './EmptyState';
export {
ActivityTypeSwitch,
@@ -51,4 +52,5 @@ export {
Comments,
GeneralNavbar,
RadarChart,
+ EmptyState,
};
diff --git a/src/config/env.ts b/src/config/env.ts
index 00d5ebd..4587a0e 100644
--- a/src/config/env.ts
+++ b/src/config/env.ts
@@ -24,8 +24,8 @@ const envConfigs: Record = {
// 开发环境
development: {
name: '开发环境',
- apiBaseURL: 'https://sit.light120.com',
- // apiBaseURL: 'http://localhost:9098',
+ apiBaseURL: 'https://sit.light120.com',
+ // apiBaseURL: 'http://localhost:9098',
timeout: 15000,
enableLog: true,
enableMock: true,
diff --git a/src/other_pages/comment_reply/index.config.ts b/src/other_pages/comment_reply/index.config.ts
new file mode 100644
index 0000000..82a32e0
--- /dev/null
+++ b/src/other_pages/comment_reply/index.config.ts
@@ -0,0 +1,4 @@
+export default definePageConfig({
+ navigationStyle: "custom",
+ navigationBarTitleText: "收到的评论和回复",
+});
diff --git a/src/other_pages/comment_reply/index.scss b/src/other_pages/comment_reply/index.scss
new file mode 100644
index 0000000..92755ea
--- /dev/null
+++ b/src/other_pages/comment_reply/index.scss
@@ -0,0 +1,228 @@
+@use '~@/scss/images.scss' as img;
+
+.comment-reply-container {
+ width: 100%;
+ height: 100vh;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ background: #FFFFFF;
+
+ // 顶部导航栏
+ .navbar {
+ height: 100px;
+ background: #FFFFFF;
+ position: sticky;
+ top: 0;
+ z-index: 100;
+
+ .navbar-content {
+ height: 56px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0 15px;
+ margin-top: 44px;
+ position: relative;
+
+ .back-button {
+ position: absolute;
+ left: 10px;
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+
+ .back-icon {
+ width: 8px;
+ height: 16px;
+ position: relative;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 8px;
+ height: 8px;
+ border-left: 2.67px solid #000000;
+ border-bottom: 2.67px solid #000000;
+ transform: rotate(45deg);
+ }
+ }
+ }
+
+ .navbar-title {
+ font-family: 'PingFang SC';
+ font-weight: 600;
+ font-size: 20px;
+ line-height: 1.4;
+ letter-spacing: 0.019em;
+ color: #000000;
+ }
+ }
+ }
+
+ // 评论列表滚动区域
+ .comment-scroll {
+ flex: 1;
+ height: 100%;
+ padding: 0 15px;
+ }
+
+ // 评论列表
+ .comment-list {
+ padding-bottom: 20px;
+ }
+
+ // 评论项
+ .comment-item {
+ display: flex;
+ justify-content: space-between;
+ gap: 12px;
+ padding: 16px 0;
+ border-bottom: 0.5px solid rgba(0, 0, 0, 0.08);
+
+ .comment-left {
+ display: flex;
+ gap: 12px;
+ flex: 1;
+ min-width: 0;
+
+ .user-avatar {
+ width: 48px;
+ height: 48px;
+ border-radius: 999px;
+ flex-shrink: 0;
+ }
+
+ .comment-content {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ flex: 1;
+ min-width: 0;
+
+ .user-nickname {
+ font-family: 'PingFang SC';
+ font-weight: 600;
+ font-size: 14px;
+ line-height: 1.43;
+ color: #000000;
+ }
+
+ .action-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ .action-text,
+ .time-text {
+ font-family: 'PingFang SC';
+ font-weight: 400;
+ font-size: 10px;
+ line-height: 1.6;
+ color: rgba(60, 60, 67, 0.6);
+ }
+ }
+
+ .comment-text {
+ font-family: 'PingFang SC';
+ font-weight: 400;
+ font-size: 12px;
+ line-height: 1.5;
+ color: #000000;
+ word-break: break-word;
+ }
+
+ // 被回复的评论
+ .original-comment {
+ display: flex;
+ align-items: flex-start;
+ gap: 4px;
+ margin-top: 4px;
+
+ .quote-line {
+ width: 2px;
+ height: 14px;
+ background: rgba(120, 120, 128, 0.12);
+ border-radius: 7px;
+ flex-shrink: 0;
+ }
+
+ .original-text {
+ font-family: 'PingFang SC';
+ font-weight: 400;
+ font-size: 12px;
+ line-height: 1.5;
+ color: rgba(60, 60, 67, 0.6);
+ word-break: break-word;
+ flex: 1;
+ }
+ }
+
+ // 回复按钮
+ .reply-button {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ padding: 4px 8px;
+ background: rgba(120, 120, 128, 0.12);
+ border-radius: 20px;
+ width: fit-content;
+ margin-top: 4px;
+ cursor: pointer;
+
+ &:active {
+ opacity: 0.8;
+ }
+
+ .reply-icon {
+ width: 12px;
+ height: 12px;
+ }
+
+ .reply-text {
+ font-family: 'PingFang SC';
+ font-weight: 400;
+ font-size: 10px;
+ line-height: 1.6;
+ color: #000000;
+ }
+ }
+ }
+ }
+
+ // 右侧球局图片
+ .activity-image {
+ width: 48px;
+ height: 48px;
+ border-radius: 9px;
+ background: #F5F5F5;
+ flex-shrink: 0;
+ cursor: pointer;
+
+ &:active {
+ opacity: 0.8;
+ }
+ }
+ }
+
+ // 到底了提示
+ .bottom-tip {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 24px 0 12px;
+
+ .tip-text {
+ font-family: 'PingFang SC';
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 1.71;
+ color: rgba(0, 0, 0, 0.35);
+ }
+ }
+}
diff --git a/src/other_pages/comment_reply/index.tsx b/src/other_pages/comment_reply/index.tsx
new file mode 100644
index 0000000..8e94dd3
--- /dev/null
+++ b/src/other_pages/comment_reply/index.tsx
@@ -0,0 +1,244 @@
+import { useState, useEffect } from "react";
+import { View, Text, ScrollView, Image } from "@tarojs/components";
+import { Avatar } from "@nutui/nutui-react-taro";
+import { withAuth, EmptyState } from "@/components";
+import commentService, { CommentActivity } from "@/services/commentService";
+import Taro from "@tarojs/taro";
+import "./index.scss";
+
+// 评论/回复类型定义
+interface CommentReplyItem {
+ id: number;
+ user_avatar: string;
+ user_nickname: string;
+ action_type: "comment" | "reply"; // 评论了你的球局 / 回复了你的评论
+ time: string;
+ content: string;
+ original_comment?: string; // 被回复的评论内容
+ activity_image: string;
+ activity_id: number;
+ activity_title: string;
+}
+
+const CommentReply = () => {
+ const [commentList, setCommentList] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [refreshing, setRefreshing] = useState(false);
+
+ useEffect(() => {
+ getCommentReplyList();
+ }, []);
+
+ // 获取评论和回复列表
+ const getCommentReplyList = async () => {
+ if (loading) return;
+
+ setLoading(true);
+ try {
+ const res = await commentService.getMyActivities({
+ page: 1,
+ pageSize: 20,
+ });
+
+ if (res.code === 0 && res.data) {
+ // 映射数据
+ const mappedList = res.data.rows.map((item: CommentActivity) => ({
+ id: item.id,
+ user_avatar: item.user?.avatar_url || "",
+ user_nickname: item.user?.nickname || "匿名用户",
+ action_type: item.type === "reply" ? "reply" as const : "comment" as const,
+ time: item.create_time,
+ content: item.content || "",
+ original_comment: item.parent_comment?.content || "",
+ activity_image: item.game?.image_list?.[0] || "",
+ activity_id: item.game_id,
+ activity_title: item.game?.title || "",
+ }));
+
+ setCommentList(mappedList);
+ }
+ } catch (e) {
+ Taro.showToast({
+ title: "获取列表失败",
+ icon: "none",
+ duration: 2000,
+ });
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 格式化时间显示
+ const formatTime = (timeStr: string) => {
+ if (!timeStr) return "";
+
+ const date = new Date(timeStr);
+ const now = new Date();
+ const diff = now.getTime() - date.getTime();
+ const minutes = Math.floor(diff / (1000 * 60));
+ const hours = Math.floor(diff / (1000 * 60 * 60));
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24));
+
+ if (minutes < 60) {
+ return `${minutes}分钟前`;
+ } else if (hours < 24) {
+ return `${hours}小时前`;
+ } else if (days === 1) {
+ return "1天前";
+ } else if (days < 7) {
+ return `${days}天前`;
+ } else {
+ const month = date.getMonth() + 1;
+ const day = date.getDate();
+ return `${month}月${day}日`;
+ }
+ };
+
+ // 处理回复
+ const handleReply = (item: CommentReplyItem) => {
+ console.log("回复:", item);
+ // TODO: 跳转到回复页面或弹出回复框
+ };
+
+ // 处理点击球局
+ const handleGameClick = (gameId: number) => {
+ Taro.navigateTo({
+ url: `/game_pages/detail/index?id=${gameId}`,
+ });
+ };
+
+ // 处理返回
+ const handleBack = () => {
+ Taro.navigateBack();
+ };
+
+ // 处理下拉刷新
+ const handleRefresh = async () => {
+ setRefreshing(true);
+ try {
+ const res = await commentService.getMyActivities({
+ page: 1,
+ pageSize: 20,
+ });
+
+ if (res.code === 0 && res.data) {
+ const mappedList = res.data.rows.map((item: CommentActivity) => ({
+ id: item.id,
+ user_avatar: item.user?.avatar_url || "",
+ user_nickname: item.user?.nickname || "匿名用户",
+ action_type: item.type === "reply" ? "reply" as const : "comment" as const,
+ time: item.create_time,
+ content: item.content || "",
+ original_comment: item.parent_comment?.content || "",
+ activity_image: item.game?.image_list?.[0] || "",
+ activity_id: item.game_id,
+ activity_title: item.game?.title || "",
+ }));
+
+ setCommentList(mappedList);
+ }
+ } catch (e) {
+ Taro.showToast({
+ title: "刷新失败",
+ icon: "none",
+ duration: 2000,
+ });
+ } finally {
+ setRefreshing(false);
+ }
+ };
+
+ // 渲染评论/回复项
+ const renderCommentItem = (item: CommentReplyItem) => {
+ const actionText = item.action_type === "comment" ? "评论了你的球局" : "回复了你的评论";
+
+ return (
+
+
+
+
+
+ {item.user_nickname}
+
+
+ {actionText}
+ {formatTime(item.time)}
+
+
+ {item.content}
+
+ {/* 如果是回复,显示被回复的评论 */}
+ {item.action_type === "reply" && item.original_comment && (
+
+
+ {item.original_comment}
+
+ )}
+
+ {/* 回复按钮 */}
+ handleReply(item)}>
+
+ 回复
+
+
+
+
+ {/* 右侧球局图片 */}
+ handleGameClick(item.activity_id)}
+ />
+
+ );
+ };
+
+ return (
+
+ {/* 顶部导航栏 */}
+
+
+
+
+
+ 收到的评论和回复
+
+
+
+ {/* 评论列表 */}
+
+ {commentList.length > 0 ? (
+
+ {commentList.map(renderCommentItem)}
+
+ {/* 到底了提示 */}
+
+ 到底了
+
+
+ ) : (
+
+ )}
+
+
+ );
+};
+
+export default withAuth(CommentReply);
diff --git a/src/other_pages/message/index.scss b/src/other_pages/message/index.scss
index d2730dc..b644b3d 100644
--- a/src/other_pages/message/index.scss
+++ b/src/other_pages/message/index.scss
@@ -6,23 +6,22 @@
box-sizing: border-box;
display: flex;
flex-direction: column;
+ background: #FFFFFF;
- // 导航栏
+ // 顶部导航栏
.navbar {
- height: 56px;
+ height: 100px;
background: #FFFFFF;
- padding-top: 44px;
position: sticky;
top: 0;
z-index: 100;
- box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.06);
.navbar-content {
height: 56px;
display: flex;
align-items: center;
- justify-content: space-between;
padding: 0 15px;
+ margin-top: 44px;
.navbar-left {
display: flex;
@@ -39,52 +38,109 @@
font-weight: 600;
font-size: 20px;
line-height: 1.4;
+ letter-spacing: 0.019em;
color: #000000;
}
}
}
}
+ // 分类标签区
+ .category-tabs {
+ display: flex;
+ align-items: stretch;
+ gap: 20px;
+ padding: 6px 24px;
+ background: #FFFFFF;
- // 消息列表
- .message-list {
- flex: 1;
- overflow: hidden;
- box-sizing: border-box;
- // margin-bottom:100px;
- background-color: none !important;
-
- .message-list-content {
+ .tab-item {
display: flex;
flex-direction: column;
- padding: 12px 12px 112px;
+ justify-content: center;
+ align-items: center;
gap: 8px;
+ padding: 12px 15px;
+ flex: 1;
+ cursor: pointer;
+ transition: all 0.2s;
+
+ .tab-icon {
+ width: 56px;
+ height: 56px;
+ border-radius: 56px;
+ transition: all 0.3s;
+ }
+
+ .tab-text {
+ font-family: 'PingFang SC';
+ font-weight: 600;
+ font-size: 16px;
+ line-height: 1.5;
+ color: #000000;
+ }
+
+ &.active {
+ .tab-icon {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
+ transform: scale(1.05);
+ }
+ }
+
+ &:active {
+ .tab-icon {
+ transform: scale(0.95);
+ }
+ }
+ }
+ }
+
+ // 消息滚动区域
+ .message-scroll {
+ flex: 1;
+ overflow: hidden;
+ background: #FFFFFF;
+
+ .message-cards {
+ padding: 12px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ padding-bottom: 120px;
}
- // 系统消息样式
- .system-message {
+ // 系统消息卡片
+ .message-card {
background: #FFFFFF;
border: 0.5px solid rgba(0, 0, 0, 0.08);
border-radius: 20px;
- padding: 0 0 12px;
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
- box-sizing: border-box;
+ padding: 0 0 12px;
+ transition: all 0.2s;
- .message-header {
- display: flex;
- align-items: center;
+ &:active {
+ transform: scale(0.98);
+ opacity: 0.95;
+ }
+
+ .card-title-row {
padding: 12px 15px 0;
- .message-title {
+ .card-title {
font-family: 'PingFang SC';
font-weight: 600;
font-size: 16px;
line-height: 1.5;
color: #000000;
- flex: 1;
}
+ }
- .message-time {
+ .card-time-row {
+ padding: 4px 15px 0;
+ display: flex;
+ align-items: center;
+ gap: 2px;
+
+ .card-time {
font-family: 'PingFang SC';
font-weight: 400;
font-size: 12px;
@@ -93,31 +149,37 @@
}
}
- .message-content {
+ .card-content-row {
padding: 8px 15px 0;
+ display: flex;
+ align-items: stretch;
+ gap: 2px;
- .message-text {
+ .card-content {
font-family: 'PingFang SC';
font-weight: 400;
font-size: 14px;
line-height: 1.43;
color: rgba(0, 0, 0, 0.7);
+ flex: 1;
}
}
- .message-action {
+ .card-footer {
padding: 12px 15px 0;
- .action-divider {
+ .footer-divider {
height: 0.5px;
background: rgba(0, 0, 0, 0.08);
margin-bottom: 12px;
+ width: 100%;
}
- .action-button {
+ .footer-action {
display: flex;
justify-content: space-between;
align-items: center;
+ cursor: pointer;
.action-text {
font-family: 'PingFang SC';
@@ -128,151 +190,94 @@
}
.action-arrow {
- width: 16px;
- height: 16px;
+ position: relative;
+ .img {
+ position: absolute;
+ left: -16px;
+ top: -9px;
+ width: 14px;;
+ height: 14px;
+ }
+ }
+
+ &:active {
+ opacity: 0.7;
}
}
}
}
- // 用户消息样式
- .user-message {
+ // 到底了提示
+ .bottom-tip {
display: flex;
+ justify-content: center;
align-items: center;
- gap: 12px;
- padding: 12px 15px;
- background: #FFFFFF;
- border: 0.5px solid rgba(0, 0, 0, 0.08);
- border-radius: 20px;
- box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
- box-sizing: border-box;
+ padding: 24px 0 12px;
- .message-avatar {
- position: relative;
- flex-shrink: 0;
-
- .unread-dot {
- position: absolute;
- top: -2px;
- right: -2px;
- width: 10px;
- height: 10px;
- background: #FF4848;
- border-radius: 50%;
- }
- }
-
- .message-info {
- flex: 1;
- min-width: 0;
-
- .message-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 4px;
-
- .message-title {
- font-family: 'PingFang SC';
- font-weight: 600;
- font-size: 16px;
- line-height: 1.5;
- color: #000000;
- }
-
- .message-time {
- font-family: 'PingFang SC';
- font-weight: 400;
- font-size: 14px;
- line-height: 1.71;
- color: rgba(0, 0, 0, 0.35);
- }
- }
-
- .message-content {
- display: flex;
- align-items: center;
- gap: 4px;
- min-width: 0;
-
- .message-text {
- font-family: 'PingFang SC';
- font-weight: 400;
- font-size: 14px;
- line-height: 1.29;
- color: rgba(0, 0, 0, 0.35);
- flex: 1;
- min-width: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- .unread-indicator {
- width: 10px;
- height: 10px;
- background: #FF4848;
- border-radius: 50%;
- flex-shrink: 0;
- }
- }
+ .tip-text {
+ font-family: 'PingFang SC';
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 1.71;
+ color: rgba(0, 0, 0, 0.35);
}
}
}
- // 空状态
- .empty-state {
+ // 悬浮新建消息按钮
+ .floating-button {
+ position: fixed;
+ right: 12px;
+ bottom: 132px;
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ background: radial-gradient(circle at 27% 8%, rgba(189, 255, 74, 1) 17%, rgba(149, 242, 62, 1) 54%, rgba(50, 216, 56, 1) 100%);
+ border: 2px solid rgba(0, 0, 0, 0.06);
+ box-shadow: 0px 4px 48px 0px rgba(0, 0, 0, 0.08);
display: flex;
- flex-direction: column;
align-items: center;
- padding: 124px 16px;
- height: 746px;
+ justify-content: center;
+ cursor: pointer;
+ z-index: 99;
+ transition: all 0.2s;
- .empty-icon {
- width: 300px;
- height: 225px;
- margin-bottom: 12px;
- position: relative;
-
- .empty-message-icon {
- width: 100%;
- height: 100%;
- background: linear-gradient(135deg, #f0f0f0 0%, #e0e0e0 100%);
- border-radius: 12px;
- position: relative;
-
- &::before {
- content: '';
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 60px;
- height: 60px;
- background: #d0d0d0;
- border-radius: 50%;
- }
-
- &::after {
- content: '';
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 30px;
- height: 30px;
- background: #a0a0a0;
- border-radius: 50%;
- }
- }
+ &:active {
+ transform: scale(0.92);
}
- .empty-text {
- font-family: 'PingFang SC';
- font-weight: 500;
- font-size: 14px;
- line-height: 1.71;
- color: rgba(0, 0, 0, 0.85);
+ .button-icon {
+ width: 36px;
+ height: 36px;
+ position: relative;
+
+ // 加号图标 - 竖线
+ &::before {
+ content: '';
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ width: 4px;
+ height: 25px;
+ background: #FFFFFF;
+ border-radius: 2px;
+ box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2);
+ }
+
+ // 加号图标 - 横线
+ &::after {
+ content: '';
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ width: 25px;
+ height: 4px;
+ background: #FFFFFF;
+ border-radius: 2px;
+ box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2);
+ }
}
}
}
\ No newline at end of file
diff --git a/src/other_pages/message/index.tsx b/src/other_pages/message/index.tsx
index e4a6653..342b4c3 100644
--- a/src/other_pages/message/index.tsx
+++ b/src/other_pages/message/index.tsx
@@ -1,136 +1,249 @@
import { useState, useEffect } from "react";
-import { View, Text, ScrollView, Image } from "@tarojs/components";
+import { View, Text, Image, ScrollView } from "@tarojs/components";
import { Avatar } from "@nutui/nutui-react-taro";
import GuideBar from "@/components/GuideBar";
-import { withAuth } from "@/components";
+import { withAuth, EmptyState } from "@/components";
import noticeService from "@/services/noticeService";
-import Taro, { useRouter } from "@tarojs/taro";
+import { useUserInfo } from "@/store/userStore";
+import Taro from "@tarojs/taro";
import "./index.scss";
// 消息类型定义
interface MessageItem {
id: string;
- type: "system" | "user" | "like" | "comment" | "follow";
+ notification_type: string;
title: string;
content: string;
- time: string;
- avatar?: string;
- isRead: boolean;
- hasAction?: boolean;
- actionText?: string;
+ create_time: string;
+ is_read: number;
+ related_user_avatar?: string;
+ related_user_nickname?: string;
+ activity_image?: string;
}
+// 消息分类类型
+type MessageCategory = "comment" | "follow";
+
const Message = () => {
- const [activeTab] = useState<"all" | "like" | "comment" | "follow">("all");
+ const userInfo = useUserInfo() as any;
+ const [activeTab, setActiveTab] = useState(null);
const [messageList, setMessageList] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [reachedBottom, setReachedBottom] = useState(false);
+ const [refreshing, setRefreshing] = useState(false);
+
+ // 获取消息列表
+ const getNoticeList = async () => {
+ if (loading) return;
+ setLoading(true);
+ try {
+ const res = await noticeService.getNotificationList({});
+ if (res.code === 0) {
+ setMessageList(res.data.list || []);
+ }
+ } catch (e) {
+ Taro.showToast({
+ title: "获取列表失败,请重试",
+ icon: "none",
+ duration: 2000,
+ });
+ } finally {
+ setLoading(false);
+ }
+ };
useEffect(() => {
- const getNoticeList = async () => {
- try {
- const res = await noticeService.getNotificationList({});
- if (res.code === 0) {
- setMessageList(res.data.list);
- }
- } catch (e) {
- Taro.showToast({
- title: "获取列表失败,请重试",
- icon: "none",
- duration: 2000,
- });
- } finally {
- }
- };
getNoticeList();
}, []);
- // 过滤消息
- const filteredMessages = messageList.filter((message) => {
- if (activeTab === "all") return true;
- return message.type === activeTab;
- });
+ // 过滤系统消息
+ const filteredMessages = messageList;
- // 渲染消息项
- const renderMessageItem = (message: MessageItem) => {
- if (message.type === "system") {
- return (
-
-
- {message.title}
- {message.time}
-
-
- {message.content}
-
- {message.hasAction && (
-
-
-
- {message.actionText}
-
-
-
- )}
-
- );
+ // 处理分类标签点击
+ const handleTabClick = (tab: MessageCategory) => {
+ // 点击评论标签跳转到评论和回复页面
+ if (tab === "comment") {
+ Taro.navigateTo({
+ url: "/other_pages/comment_reply/index",
+ });
+ return;
}
+
+ // 点击关注标签跳转到新增关注页面
+ if (tab === "follow") {
+ Taro.navigateTo({
+ url: "/other_pages/new_follow/index",
+ });
+ return;
+ }
+
+ setActiveTab(activeTab === tab ? null : tab);
+ };
- return (
-
-
-
-
-
-
- {message.title}
- {message.time}
-
-
- {message.content}
- {!message.isRead && }
-
-
-
- );
+ // 处理查看详情
+ const handleViewDetail = (messageId: string) => {
+ console.log("查看详情:", messageId);
+ // TODO: 根据消息类型跳转到对应详情页
+ };
+
+ // 处理滚动到底部
+ const handleScrollToLower = () => {
+ if (!reachedBottom && filteredMessages.length > 0) {
+ setReachedBottom(true);
+ // 2秒后隐藏提示
+ setTimeout(() => {
+ setReachedBottom(false);
+ }, 2000);
+ }
+ };
+
+ // 处理下拉刷新
+ const handleRefresh = async () => {
+ setRefreshing(true);
+ try {
+ const res = await noticeService.getNotificationList({});
+ if (res.code === 0) {
+ setMessageList(res.data.list || []);
+ }
+ } catch (e) {
+ Taro.showToast({
+ title: "刷新失败",
+ icon: "none",
+ duration: 2000,
+ });
+ } finally {
+ setRefreshing(false);
+ }
+ };
+
+ // 格式化时间显示
+ const formatTime = (timeStr: string) => {
+ if (!timeStr) return "";
+ const date = new Date(timeStr);
+ const now = new Date();
+ const diff = now.getTime() - date.getTime();
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24));
+
+ if (days === 0) {
+ const hours = date.getHours();
+ const minutes = date.getMinutes();
+ return `今天 ${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
+ } else if (days === 1) {
+ const hours = date.getHours();
+ const minutes = date.getMinutes();
+ return `昨天 ${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
+ } else if (days < 3) {
+ return `${days}天前`;
+ } else {
+ const year = date.getFullYear();
+ const month = date.getMonth() + 1;
+ const day = date.getDate();
+ const hours = date.getHours();
+ const minutes = date.getMinutes();
+ return `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')} ${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
+ }
};
return (
- {/* 导航栏 */}
+ {/* 顶部导航栏 */}
消息
- {/* 消息列表 */}
-
+ {/* 分类标签 */}
+
+ handleTabClick("comment")}
+ >
+
+ 评论和回复
+
+ handleTabClick("follow")}
+ >
+
+ 新增关注
+
+
+
+ {/* 系统消息卡片列表 */}
+
{filteredMessages.length > 0 ? (
-
- {filteredMessages.map(renderMessageItem)}
+
+ {filteredMessages.map((message) => (
+
+
+ {message.title}
+
+
+ {formatTime(message.create_time)}
+
+
+ {message.content}
+
+
+
+ handleViewDetail(message.id)}>
+ 查看详情
+
+
+
+
+
+
+
+
+ ))}
+ {/* 到底了提示 */}
+ {filteredMessages.length > 0 && (
+
+ 到底了
+
+ )}
) : (
-
-
-
-
- 暂无消息
-
+
)}
+ {/* 悬浮新建按钮 */}
+ console.log("新建消息")}>
+
+
+
{/* 底部导航 */}
);
};
-export default withAuth(Message);
+export default withAuth(Message);
\ No newline at end of file
diff --git a/src/other_pages/new_follow/index.config.ts b/src/other_pages/new_follow/index.config.ts
new file mode 100644
index 0000000..4626cb8
--- /dev/null
+++ b/src/other_pages/new_follow/index.config.ts
@@ -0,0 +1,4 @@
+export default definePageConfig({
+ navigationStyle: "custom",
+ navigationBarTitleText: "新增关注",
+});
diff --git a/src/other_pages/new_follow/index.scss b/src/other_pages/new_follow/index.scss
new file mode 100644
index 0000000..5e8dc98
--- /dev/null
+++ b/src/other_pages/new_follow/index.scss
@@ -0,0 +1,199 @@
+@use '~@/scss/images.scss' as img;
+
+.new-follow-container {
+ width: 100%;
+ height: 100vh;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ background: #FFFFFF;
+
+ // 顶部导航栏
+ .navbar {
+ height: 100px;
+ background: #FFFFFF;
+ position: sticky;
+ top: 0;
+ z-index: 100;
+
+ .navbar-content {
+ height: 56px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0 15px;
+ margin-top: 44px;
+ position: relative;
+
+ .back-button {
+ position: absolute;
+ left: 10px;
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+
+ .back-icon {
+ width: 8px;
+ height: 16px;
+ position: relative;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 8px;
+ height: 8px;
+ border-left: 2.67px solid #000000;
+ border-bottom: 2.67px solid #000000;
+ transform: rotate(45deg);
+ }
+ }
+ }
+
+ .navbar-title {
+ font-family: 'PingFang SC';
+ font-weight: 600;
+ font-size: 20px;
+ line-height: 1.4;
+ letter-spacing: 0.019em;
+ color: #000000;
+ }
+ }
+ }
+
+ // 关注列表滚动区域
+ .follow-scroll {
+ flex: 1;
+ height: 100%;
+ }
+
+ // 关注列表
+ .follow-list {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 0 0 20px;
+ }
+
+ // 关注项
+ .follow-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 12px;
+ padding: 8px 20px;
+
+ .follow-left {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ flex: 1;
+ min-width: 0;
+ cursor: pointer;
+
+ .user-avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 999px;
+ flex-shrink: 0;
+ }
+
+ .user-info {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ flex: 1;
+ min-width: 0;
+ height: 40px;
+ justify-content: center;
+
+ .user-nickname {
+ font-family: 'PingFang SC';
+ font-weight: 600;
+ font-size: 14px;
+ line-height: 1.14;
+ color: rgba(0, 0, 0, 0.8);
+ }
+
+ .user-signature {
+ font-family: 'PingFang SC';
+ font-weight: 400;
+ font-size: 12px;
+ line-height: 1.33;
+ color: rgba(60, 60, 67, 0.6);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .action-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ .action-text,
+ .time-text {
+ font-family: 'PingFang SC';
+ font-weight: 400;
+ font-size: 12px;
+ line-height: 1.33;
+ color: rgba(60, 60, 67, 0.6);
+ }
+ }
+ }
+ }
+
+ // 回关按钮
+ .follow-button {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 4px 16px;
+ border: 0.5px solid #000000;
+ border-radius: 20px;
+ cursor: pointer;
+ flex-shrink: 0;
+
+ &:active {
+ opacity: 0.8;
+ }
+
+ &.mutual {
+ border-color: rgba(120, 120, 128, 0.12);
+ background: transparent;
+ }
+
+ .button-text {
+ font-family: 'PingFang SC';
+ font-weight: 400;
+ font-size: 12px;
+ line-height: 1.33;
+ color: #000000;
+ }
+
+ &.mutual .button-text {
+ color: rgba(0, 0, 0, 0.8);
+ }
+ }
+ }
+
+ // 到底了提示
+ .bottom-tip {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 24px 0 12px;
+
+ .tip-text {
+ font-family: 'PingFang SC';
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 1.71;
+ color: rgba(0, 0, 0, 0.35);
+ }
+ }
+}
diff --git a/src/other_pages/new_follow/index.tsx b/src/other_pages/new_follow/index.tsx
new file mode 100644
index 0000000..c7d722c
--- /dev/null
+++ b/src/other_pages/new_follow/index.tsx
@@ -0,0 +1,261 @@
+import { useState, useEffect } from "react";
+import { View, Text, ScrollView } from "@tarojs/components";
+import { Avatar } from "@nutui/nutui-react-taro";
+import { withAuth, EmptyState } from "@/components";
+import FollowService from "@/services/followService";
+import Taro from "@tarojs/taro";
+import "./index.scss";
+
+// 关注项类型定义
+interface FollowItem {
+ id: number;
+ user_id: number;
+ user_avatar: string;
+ user_nickname: string;
+ user_signature?: string;
+ city?: string;
+ ntrp_level?: string;
+ time: string;
+ is_mutual: boolean; // 是否互相关注
+}
+
+const NewFollow = () => {
+ const [followList, setFollowList] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [refreshing, setRefreshing] = useState(false);
+
+ useEffect(() => {
+ getFollowList();
+ }, []);
+
+ // 获取新增关注列表
+ const getFollowList = async () => {
+ if (loading) return;
+
+ setLoading(true);
+ try {
+ const res = await FollowService.get_new_fans_list(1, 20);
+
+ if (res.list) {
+ // 映射数据
+ const mappedList = res.list.map((item: any) => ({
+ id: item.id,
+ user_id: item.id,
+ user_avatar: item.avatar_url || "",
+ user_nickname: item.nickname || "匿名用户",
+ user_signature: item.personal_profile || "",
+ city: item.city || "",
+ ntrp_level: item.ntrp_level || "",
+ time: item.follow_time,
+ is_mutual: item.is_mutual || false,
+ }));
+
+ setFollowList(mappedList);
+ }
+ } catch (e) {
+ Taro.showToast({
+ title: "获取列表失败",
+ icon: "none",
+ duration: 2000,
+ });
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 格式化时间显示
+ const formatTime = (timeStr: string) => {
+ if (!timeStr) return "";
+
+ const date = new Date(timeStr);
+ const now = new Date();
+ const diff = now.getTime() - date.getTime();
+ const minutes = Math.floor(diff / (1000 * 60));
+ const hours = Math.floor(diff / (1000 * 60 * 60));
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24));
+
+ if (minutes < 1) {
+ return "刚刚";
+ } else if (minutes < 60) {
+ return `${minutes}分钟前`;
+ } else if (hours < 24) {
+ return `${hours}小时前`;
+ } else if (days === 1) {
+ return "1天前";
+ } else if (days < 7) {
+ return `${days}天前`;
+ } else {
+ const month = date.getMonth() + 1;
+ const day = date.getDate();
+ return `${month}月${day}日`;
+ }
+ };
+
+ // 处理回关/取消关注
+ const handleFollowBack = async (item: FollowItem) => {
+ try {
+ if (item.is_mutual) {
+ // 已经互相关注,点击取消关注
+ await FollowService.unfollow_user(item.user_id);
+
+ Taro.showToast({
+ title: "取消关注成功",
+ icon: "success",
+ duration: 2000,
+ });
+
+ // 更新列表
+ setFollowList(prevList =>
+ prevList.map(followItem =>
+ followItem.id === item.id
+ ? { ...followItem, is_mutual: false }
+ : followItem
+ )
+ );
+ } else {
+ // 未互关,点击回关
+ await FollowService.follow_back(item.user_id);
+
+ Taro.showToast({
+ title: "关注成功",
+ icon: "success",
+ duration: 2000,
+ });
+
+ // 更新列表
+ setFollowList(prevList =>
+ prevList.map(followItem =>
+ followItem.id === item.id
+ ? { ...followItem, is_mutual: true }
+ : followItem
+ )
+ );
+ }
+ } catch (e) {
+ Taro.showToast({
+ title: item.is_mutual ? "取消关注失败" : "关注失败",
+ icon: "none",
+ duration: 2000,
+ });
+ }
+ };
+
+ // 处理返回
+ const handleBack = () => {
+ Taro.navigateBack();
+ };
+
+ // 处理点击用户
+ const handleUserClick = (userId: number) => {
+ Taro.navigateTo({
+ url: `/user_pages/other/index?user_id=${userId}`,
+ });
+ };
+
+ // 处理下拉刷新
+ const handleRefresh = async () => {
+ setRefreshing(true);
+ try {
+ const res = await FollowService.get_new_fans_list(1, 20);
+
+ if (res.list) {
+ const mappedList = res.list.map((item: any) => ({
+ id: item.id,
+ user_id: item.id,
+ user_avatar: item.avatar_url || "",
+ user_nickname: item.nickname || "匿名用户",
+ user_signature: item.personal_profile || "",
+ city: item.city || "",
+ ntrp_level: item.ntrp_level || "",
+ time: item.follow_time,
+ is_mutual: item.is_mutual || false,
+ }));
+
+ setFollowList(mappedList);
+ }
+ } catch (e) {
+ Taro.showToast({
+ title: "刷新失败",
+ icon: "none",
+ duration: 2000,
+ });
+ } finally {
+ setRefreshing(false);
+ }
+ };
+
+ // 渲染关注项
+ const renderFollowItem = (item: FollowItem) => {
+ return (
+
+ handleUserClick(item.user_id)}>
+
+
+
+ {item.user_nickname}
+
+ {item.user_signature ? (
+ {item.user_signature}
+ ) : (
+
+ {formatTime(item.time)}关注了你
+
+ )}
+
+
+
+ handleFollowBack(item)}
+ >
+ {item.is_mutual ? "互相关注" : "回关"}
+
+
+ );
+ };
+
+ return (
+
+ {/* 顶部导航栏 */}
+
+
+
+
+
+ 新增关注
+
+
+
+ {/* 关注列表 */}
+
+ {followList.length > 0 ? (
+
+ {followList.map(renderFollowItem)}
+
+ {/* 到底了提示 */}
+
+ 到底了
+
+
+ ) : (
+
+ )}
+
+
+ );
+};
+
+export default withAuth(NewFollow);
diff --git a/src/scss/qweather-icons/fonts/qweather-icons.ttf b/src/scss/qweather-icons/fonts/qweather-icons.ttf
new file mode 100644
index 0000000..2a47642
Binary files /dev/null and b/src/scss/qweather-icons/fonts/qweather-icons.ttf differ
diff --git a/src/scss/qweather-icons/fonts/qweather-icons.woff2 b/src/scss/qweather-icons/fonts/qweather-icons.woff2
new file mode 100644
index 0000000..e1f8f48
Binary files /dev/null and b/src/scss/qweather-icons/fonts/qweather-icons.woff2 differ
diff --git a/src/scss/qweather-icons/qweather-icons.css b/src/scss/qweather-icons/qweather-icons.css
new file mode 100644
index 0000000..6dc64d9
--- /dev/null
+++ b/src/scss/qweather-icons/qweather-icons.css
@@ -0,0 +1,92 @@
+
+
+@font-face {
+ font-family: "qweather-icons";
+ src: url("./fonts/qweather-icons.woff2?3696017a726a77099c2617f87a3367ac") format("woff2")
+
+}
+
+[class^="qi-"]::before,
+[class*=" qi-"]::before {
+ display: inline-block;
+ font-family: "qweather-icons" !important;
+ font-style: normal;
+ font-weight: normal !important;
+ font-variant: normal;
+ text-transform: none;
+ line-height: 1;
+ vertical-align: -.125em;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.qi-100::before { content: "\f101"; }
+.qi-101::before { content: "\f102"; }
+.qi-102::before { content: "\f103"; }
+.qi-103::before { content: "\f104"; }
+.qi-104::before { content: "\f105"; }
+.qi-150::before { content: "\f106"; }
+.qi-151::before { content: "\f107"; }
+.qi-152::before { content: "\f108"; }
+.qi-153::before { content: "\f109"; }
+.qi-300::before { content: "\f10a"; }
+.qi-301::before { content: "\f10b"; }
+.qi-302::before { content: "\f10c"; }
+.qi-303::before { content: "\f10d"; }
+.qi-304::before { content: "\f10e"; }
+.qi-305::before { content: "\f10f"; }
+.qi-306::before { content: "\f110"; }
+.qi-307::before { content: "\f111"; }
+.qi-308::before { content: "\f112"; }
+.qi-309::before { content: "\f113"; }
+.qi-310::before { content: "\f114"; }
+.qi-311::before { content: "\f115"; }
+.qi-312::before { content: "\f116"; }
+.qi-313::before { content: "\f117"; }
+.qi-314::before { content: "\f118"; }
+.qi-315::before { content: "\f119"; }
+.qi-316::before { content: "\f11a"; }
+.qi-317::before { content: "\f11b"; }
+.qi-318::before { content: "\f11c"; }
+.qi-350::before { content: "\f11d"; }
+.qi-351::before { content: "\f11e"; }
+.qi-399::before { content: "\f11f"; }
+.qi-400::before { content: "\f120"; }
+.qi-401::before { content: "\f121"; }
+.qi-402::before { content: "\f122"; }
+.qi-403::before { content: "\f123"; }
+.qi-404::before { content: "\f124"; }
+.qi-405::before { content: "\f125"; }
+.qi-406::before { content: "\f126"; }
+.qi-407::before { content: "\f127"; }
+.qi-408::before { content: "\f128"; }
+.qi-409::before { content: "\f129"; }
+.qi-410::before { content: "\f12a"; }
+.qi-456::before { content: "\f12b"; }
+.qi-457::before { content: "\f12c"; }
+.qi-499::before { content: "\f12d"; }
+.qi-500::before { content: "\f12e"; }
+.qi-501::before { content: "\f12f"; }
+.qi-502::before { content: "\f130"; }
+.qi-503::before { content: "\f131"; }
+.qi-504::before { content: "\f132"; }
+.qi-507::before { content: "\f133"; }
+.qi-508::before { content: "\f134"; }
+.qi-509::before { content: "\f135"; }
+.qi-510::before { content: "\f136"; }
+.qi-511::before { content: "\f137"; }
+.qi-512::before { content: "\f138"; }
+.qi-513::before { content: "\f139"; }
+.qi-514::before { content: "\f13a"; }
+.qi-515::before { content: "\f13b"; }
+.qi-800::before { content: "\f13c"; }
+.qi-801::before { content: "\f13d"; }
+.qi-802::before { content: "\f13e"; }
+.qi-803::before { content: "\f13f"; }
+.qi-804::before { content: "\f140"; }
+.qi-805::before { content: "\f141"; }
+.qi-806::before { content: "\f142"; }
+.qi-807::before { content: "\f143"; }
+.qi-900::before { content: "\f144"; }
+.qi-901::before { content: "\f145"; }
+.qi-999::before { content: "\f146"; }
diff --git a/src/services/commentService.ts b/src/services/commentService.ts
new file mode 100644
index 0000000..88913c6
--- /dev/null
+++ b/src/services/commentService.ts
@@ -0,0 +1,94 @@
+import httpService from "./httpService";
+import type { ApiResponse } from "./httpService";
+
+// 评论项接口定义
+export interface CommentActivity {
+ id: number;
+ type: "comment" | "reply";
+ activity_type: string;
+ game_id: number;
+ parent_id: number | null;
+ content: string;
+ like_count: number;
+ reply_count: number;
+ create_time: string;
+ user: {
+ id: number;
+ nickname: string;
+ province: string;
+ avatar_url: string;
+ };
+ game: {
+ id: number;
+ title: string;
+ start_time: string;
+ location_name: string;
+ image_list?: string[];
+ };
+ reply_to_user: {
+ id: number;
+ nickname: string;
+ avatar_url: string;
+ } | null;
+ parent_comment: {
+ id: number;
+ content: string;
+ user_id: number;
+ user_nickname: string;
+ } | null;
+}
+
+// 获取评论动态请求参数
+export interface GetMyActivitiesParams {
+ page?: number;
+ pageSize?: number;
+}
+
+// 评论动态列表响应
+export interface CommentActivitiesResponse {
+ rows: CommentActivity[];
+ count: number;
+ page: number;
+ pageSize: number;
+ totalPages: number;
+}
+
+class CommentService {
+ // 获取当前用户的评论和回复动态
+ async getMyActivities(params: GetMyActivitiesParams = {}): Promise> {
+ const { page = 1, pageSize = 10 } = params;
+ return httpService.post("/comments/my_activities", { page, pageSize }, { showLoading: false });
+ }
+
+ // 发表评论
+ async createComment(game_id: number, content: string): Promise> {
+ return httpService.post("/comments/create", { game_id, content });
+ }
+
+ // 回复评论
+ async replyComment(parent_id: number, reply_to_user_id: number, content: string): Promise> {
+ return httpService.post("/comments/reply", { parent_id, reply_to_user_id, content });
+ }
+
+ // 获取评论列表
+ async getCommentList(game_id: number, page: number = 1, pageSize: number = 10): Promise> {
+ return httpService.post("/comments/list", { game_id, page, pageSize });
+ }
+
+ // 点赞/取消点赞评论
+ async likeComment(comment_id: number): Promise> {
+ return httpService.post("/comments/like", { comment_id });
+ }
+
+ // 删除评论
+ async deleteComment(comment_id: number): Promise> {
+ return httpService.post("/comments/delete", { comment_id });
+ }
+
+ // 获取评论的所有回复
+ async getCommentReplies(comment_id: number, page: number = 1, pageSize: number = 10): Promise> {
+ return httpService.post("/comments/replies", { comment_id, page, pageSize });
+ }
+}
+
+export default new CommentService();
diff --git a/src/services/followService.ts b/src/services/followService.ts
index acf6e3f..0589ee1 100644
--- a/src/services/followService.ts
+++ b/src/services/followService.ts
@@ -84,6 +84,34 @@ export class FollowService {
}
}
+ // 获取新增粉丝列表(新关注的人)
+ static async get_new_fans_list(
+ page: number = 1,
+ page_size: number = 20
+ ): Promise {
+ try {
+ const response = await httpService.post(
+ '/user_follow/new_fans_list',
+ { page, page_size },
+ { showLoading: false }
+ );
+
+ if (response.code === 0) {
+ // 为数据添加 follow_status 标识
+ const list = response.data.list.map(user => ({
+ ...user,
+ follow_status: user.is_mutual ? 'mutual_follow' as const : 'follower' as const
+ }));
+ return { ...response.data, list };
+ } else {
+ throw new Error(response.message || '获取新增粉丝列表失败');
+ }
+ } catch (error) {
+ console.error('获取新增粉丝列表失败:', error);
+ return { list: [], total: 0 };
+ }
+ }
+
// 获取我的关注列表
static async get_following_list(
page: number = 1,
diff --git a/src/static/message/ar-right.svg b/src/static/message/ar-right.svg
new file mode 100644
index 0000000..412901b
--- /dev/null
+++ b/src/static/message/ar-right.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/static/message/comment-icon.svg b/src/static/message/comment-icon.svg
new file mode 100644
index 0000000..69a4659
--- /dev/null
+++ b/src/static/message/comment-icon.svg
@@ -0,0 +1,11 @@
+
diff --git a/src/static/message/emi.svg b/src/static/message/emi.svg
new file mode 100644
index 0000000..6a526d1
--- /dev/null
+++ b/src/static/message/emi.svg
@@ -0,0 +1,38 @@
+
diff --git a/src/static/message/follow-icon.svg b/src/static/message/follow-icon.svg
new file mode 100644
index 0000000..b6fffff
--- /dev/null
+++ b/src/static/message/follow-icon.svg
@@ -0,0 +1,12 @@
+
diff --git a/src/static/message/reply-icon.svg b/src/static/message/reply-icon.svg
new file mode 100644
index 0000000..0d560d2
--- /dev/null
+++ b/src/static/message/reply-icon.svg
@@ -0,0 +1,6 @@
+
diff --git a/yarn.lock b/yarn.lock
index ee50824..7718bc9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -720,6 +720,18 @@
babel-plugin-polyfill-regenerator "^0.6.5"
semver "^6.3.1"
+"@babel/plugin-transform-runtime@^7.28.3":
+ version "7.28.3"
+ resolved "https://registry.npmmirror.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.3.tgz#f5990a1b2d2bde950ed493915e0719841c8d0eaa"
+ integrity sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==
+ dependencies:
+ "@babel/helper-module-imports" "^7.27.1"
+ "@babel/helper-plugin-utils" "^7.27.1"
+ babel-plugin-polyfill-corejs2 "^0.4.14"
+ babel-plugin-polyfill-corejs3 "^0.13.0"
+ babel-plugin-polyfill-regenerator "^0.6.5"
+ semver "^6.3.1"
+
"@babel/plugin-transform-shorthand-properties@^7.27.1":
version "7.27.1"
resolved "https://registry.npmmirror.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz#532abdacdec87bfee1e0ef8e2fcdee543fe32b90"
@@ -3203,6 +3215,11 @@ babel-plugin-transform-imports-api@1.0.0:
dependencies:
is-invalid-path "^1.0.2"
+babel-plugin-transform-remove-console@^6.9.4:
+ version "6.9.4"
+ resolved "https://registry.npmmirror.com/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz#b980360c067384e24b357a588d807d3c83527780"
+ integrity sha512-88blrUrMX3SPiGkT1GnvVY8E/7A+k6oj3MNvUtTIxJflFzXTw1bHkuJ/y039ouhFMp2prRn5cQGzokViYi1dsg==
+
babel-plugin-transform-solid-jsx@4.1.5:
version "4.1.5"
resolved "https://registry.npmmirror.com/babel-plugin-transform-solid-jsx/-/babel-plugin-transform-solid-jsx-4.1.5.tgz#203d07a15f32e65e09f266b6ad2080f346eb453a"