From 0a9622cedd83ab461cf538cf039ffd6f35af618e Mon Sep 17 00:00:00 2001 From: cph999 Date: Wed, 16 Oct 2024 16:04:08 +0800 Subject: [PATCH] =?UTF-8?q?=E2=80=9C=E5=8F=91=E7=8E=B0=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/cph/musicbackend/a.html | 40 ------ .../musicbackend/config/WebSocketConfig.java | 3 +- .../controller/PostsController.java | 42 +++++++ .../com/cph/musicbackend/entity/Posts.java | 33 +++++ .../cph/musicbackend/mapper/PostsMapper.java | 17 +++ .../main/resources/mybatis/PostsMapper.xml | 41 +++++++ package-lock.json | 9 ++ package.json | 1 + src/App.js | 6 +- src/components/Chat.css | 90 ++++++++++++-- src/components/Chat.tsx | 23 ++-- src/components/Discovery.css | 93 ++++++++++++++ src/components/Discovery.tsx | 114 ++++++++++++++++++ src/components/SearchPage.tsx | 10 -- src/utils/api.js | 6 +- 15 files changed, 451 insertions(+), 77 deletions(-) delete mode 100644 musicBackend/src/main/java/com/cph/musicbackend/a.html create mode 100644 musicBackend/src/main/java/com/cph/musicbackend/controller/PostsController.java create mode 100644 musicBackend/src/main/java/com/cph/musicbackend/entity/Posts.java create mode 100644 musicBackend/src/main/java/com/cph/musicbackend/mapper/PostsMapper.java create mode 100644 musicBackend/src/main/resources/mybatis/PostsMapper.xml create mode 100644 src/components/Discovery.css create mode 100644 src/components/Discovery.tsx delete mode 100644 src/components/SearchPage.tsx diff --git a/musicBackend/src/main/java/com/cph/musicbackend/a.html b/musicBackend/src/main/java/com/cph/musicbackend/a.html deleted file mode 100644 index 775f53b..0000000 --- a/musicBackend/src/main/java/com/cph/musicbackend/a.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - WebSocket Chat - - -
- - -
-
- - - - diff --git a/musicBackend/src/main/java/com/cph/musicbackend/config/WebSocketConfig.java b/musicBackend/src/main/java/com/cph/musicbackend/config/WebSocketConfig.java index c3a6f11..9fd964c 100644 --- a/musicBackend/src/main/java/com/cph/musicbackend/config/WebSocketConfig.java +++ b/musicBackend/src/main/java/com/cph/musicbackend/config/WebSocketConfig.java @@ -12,7 +12,8 @@ public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { - registry.addHandler(new ChatWebSocketHandler(), "/api/chat") + registry.addHandler(new ChatWebSocketHandler(), "/chat") .setAllowedOrigins("*"); // 允许跨域请求,可以根据需要配置 + } } \ No newline at end of file diff --git a/musicBackend/src/main/java/com/cph/musicbackend/controller/PostsController.java b/musicBackend/src/main/java/com/cph/musicbackend/controller/PostsController.java new file mode 100644 index 0000000..442bd05 --- /dev/null +++ b/musicBackend/src/main/java/com/cph/musicbackend/controller/PostsController.java @@ -0,0 +1,42 @@ +package com.cph.musicbackend.controller; + +import com.cph.musicbackend.aspect.RecognizeAddress; +import com.cph.musicbackend.aspect.UserContext; +import com.cph.musicbackend.common.CommonResult; +import com.cph.musicbackend.entity.Posts; +import com.cph.musicbackend.entity.User; +import com.cph.musicbackend.mapper.PostsMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +public class PostsController { + + @Autowired + PostsMapper postMapper; + + @PostMapping("/api/posts") + @RecognizeAddress + public CommonResult getPosts(){ + User currentUser = UserContext.getCurrentUser(); + List posts = postMapper.getUserPosts(currentUser); + posts.stream().forEach(p->{ + p.setImages(Arrays.asList(p.getMedia().split(","))); + }); + return new CommonResult(200,"查询成功",posts); + } + + @PostMapping("/api/likePost") + @RecognizeAddress + public CommonResult like(@RequestBody Posts p){ + User currentUser = UserContext.getCurrentUser(); + postMapper.like(currentUser,p); + return new CommonResult(200,"操作成功",null); + } +} \ No newline at end of file diff --git a/musicBackend/src/main/java/com/cph/musicbackend/entity/Posts.java b/musicBackend/src/main/java/com/cph/musicbackend/entity/Posts.java new file mode 100644 index 0000000..f03ecf1 --- /dev/null +++ b/musicBackend/src/main/java/com/cph/musicbackend/entity/Posts.java @@ -0,0 +1,33 @@ +package com.cph.musicbackend.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; +import java.util.List; + +@Data +@Accessors(chain = true) +@TableName("posts") +public class Posts { + + @TableId(type = IdType.AUTO, value = "id") + private Integer id; + private Integer userId; + private Date createdTime; + private String media; + private String content; + + private String userIcon; + private String userNickname; + + @TableField(exist = false) + private Integer isLike; + @TableField(exist = false) + private List images; + +} \ No newline at end of file diff --git a/musicBackend/src/main/java/com/cph/musicbackend/mapper/PostsMapper.java b/musicBackend/src/main/java/com/cph/musicbackend/mapper/PostsMapper.java new file mode 100644 index 0000000..1b3473e --- /dev/null +++ b/musicBackend/src/main/java/com/cph/musicbackend/mapper/PostsMapper.java @@ -0,0 +1,17 @@ +package com.cph.musicbackend.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.cph.musicbackend.entity.Music; +import com.cph.musicbackend.entity.Posts; +import com.cph.musicbackend.entity.User; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface PostsMapper extends BaseMapper { + public List getUserPosts(@Param("u") User user); + + public void like(@Param("u") User user, @Param("p") Posts p); +} diff --git a/musicBackend/src/main/resources/mybatis/PostsMapper.xml b/musicBackend/src/main/resources/mybatis/PostsMapper.xml new file mode 100644 index 0000000..d3aec0d --- /dev/null +++ b/musicBackend/src/main/resources/mybatis/PostsMapper.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + update user_posts + set is_like = #{p.isLike} + where user_id = #{u.id} + and post_id = #{p.id} + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1a2a5d1..46562a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "react-audio-player": "^0.17.0", "react-dom": "^18.3.1", "react-scripts": "5.0.1", + "react-swipeable": "^7.0.1", "react-vant": "^3.3.5", "web-vitals": "^2.1.4" }, @@ -15293,6 +15294,14 @@ } } }, + "node_modules/react-swipeable": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/react-swipeable/-/react-swipeable-7.0.1.tgz", + "integrity": "sha512-RKB17JdQzvECfnVj9yDZsiYn3vH0eyva/ZbrCZXZR0qp66PBRhtg4F9yJcJTWYT5Adadi+x4NoG53BxKHwIYLQ==", + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18" + } + }, "node_modules/react-transition-group": { "version": "4.4.2", "resolved": "https://registry.npmmirror.com/react-transition-group/-/react-transition-group-4.4.2.tgz", diff --git a/package.json b/package.json index 8e07cee..1414161 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "react-audio-player": "^0.17.0", "react-dom": "^18.3.1", "react-scripts": "5.0.1", + "react-swipeable": "^7.0.1", "react-vant": "^3.3.5", "web-vitals": "^2.1.4" }, diff --git a/src/App.js b/src/App.js index f6c305d..6de2ee6 100644 --- a/src/App.js +++ b/src/App.js @@ -3,7 +3,7 @@ import './App.css'; import 'react-vant/es/styles'; import Home from './components/Home.tsx'; -import SearchPage from './components/SearchPage.tsx'; +import Discovery from './components/Discovery.tsx'; import Chat from './components/Chat.tsx'; import Profile from './components/Profile.tsx'; import React, { useState, useEffect } from 'react'; @@ -80,7 +80,7 @@ function App() { case 'home': return ; case 'search': - return ; + return ; case 'chat': return ; case 'profile': @@ -175,7 +175,7 @@ function App() { 首页 } name="search" badge={{ dot: true }}> - 搜索 + 发现 } name="chat" badge={{ content: 5 }}> 聊天 diff --git a/src/components/Chat.css b/src/components/Chat.css index 67af6dd..5a14927 100644 --- a/src/components/Chat.css +++ b/src/components/Chat.css @@ -1,18 +1,82 @@ -.chat-box { - width: 100%; - height: 100%; - background: linear-gradient(to bottom, #DDA0DD, #FFB6C1); +.chat-input-container { + display: flex; + align-items: center; + position: relative; + padding: 10px; + background-color: #fff; + border-top: 1px solid #eaeaea; } -html, -body { - height: 100%; - margin: 0; - padding: 0; +.chat-input { + flex: 1; + margin-right: 10px; + border-radius: 20px; + border: 1px solid #eaeaea; + padding: 8px 12px; + font-size: 14px; } -.chat-header{ - padding: 2px; - /* margin-top: 2%; */ + +.send-button { + margin-left: 10px; + background-color: #007bff; + border-color: #007bff; + color: #fff; + border-radius: 20px; +} + +.emoji-picker-wrapper { + position: absolute; + bottom: 60px; + /* 确保不会挡住输入框 */ + right: 10px; + z-index: 10; + background-color: #fff; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + border-radius: 10px; +} + +.emoji-icon { + margin-right: 10px; + cursor: pointer; + color: #888; +} + +.emoji-icon:hover { + color: #007bff; } -.list-container { + +.chat-header { + background-color: #f0f0f0; + /* 顶部背景色 */ + padding: 15px; + /* 增加内边距,调整顶部区域的高度 */ + min-height: 60px; + /* 设置最小高度 */ + border-bottom: 1px solid #eaeaea; + /* 添加下边框 */ + display: flex; + /* 使用 flex 布局 */ + align-items: center; + /* 垂直居中对齐 */ +} + +.chat-header .badge { + margin-right: 10px; + /* Badge 和 nickname 之间的间距 */ +} + +.chat-header .nickname { + font-size: 18px; + /* 增加昵称字体大小 */ + color: #333; + /* 字体颜色 */ + font-weight: bold; + /* 加粗字体 */ +} + +.chat-header .nickname-container { + display: flex; + /* 使用 flex 布局 */ + flex-direction: column; + /* 纵向排列 */ } \ No newline at end of file diff --git a/src/components/Chat.tsx b/src/components/Chat.tsx index cd2f0b4..ca2d684 100644 --- a/src/components/Chat.tsx +++ b/src/components/Chat.tsx @@ -22,7 +22,11 @@ function Chat({ userinfo }) { useEffect(() => { if (!userinfo || !userinfo.id) return; - const ws = new WebSocket(`ws://localhost:8809/api/chat?userId=${userinfo.id}`); + const ws = new WebSocket(`wss://app102.acapp.acwing.com.cn/chat?userId=${userinfo.id}`); + // wss://app102.acapp.acwing.com.cn/chat?userId=49 + // const ws = new WebSocket(`ws://39.100.90.48:8809/chat?userId=${userinfo.id}`); + // const ws = new WebSocket(`ws://localhost:8809/chat?userId=${userinfo.id}`); + ws.onopen = () => { console.log('WebSocket connected'); @@ -116,16 +120,18 @@ function Chat({ userinfo }) { {!chatState && (
- - - - + + + + - {userinfo.nickname} - + + {userinfo.nickname} +
+
@@ -134,8 +140,9 @@ function Chat({ userinfo }) { key={message[message.length - 1].id} title={userinfo.id === message[message.length - 1].fromId ? message[message.length - 1].toNickname : message[message.length - 1].fromNickname} label={message[message.length - 1].message} - icon={from} + icon={from} value={message[message.length - 1].showTime} + className={userinfo.id === message[message.length - 1].fromId ? "cell-sent" : "cell-received"} onClick={() => handleClickMessage(message)} /> ))} diff --git a/src/components/Discovery.css b/src/components/Discovery.css new file mode 100644 index 0000000..316ffa8 --- /dev/null +++ b/src/components/Discovery.css @@ -0,0 +1,93 @@ +html, +body { + height: 100%; + margin: 0; + padding: 0; +} + +#root { + height: 100%; +} + +.discovery-container { + height: 100%; + padding: 20px 10px; + background: linear-gradient(to bottom, #DDA0DD, #FFB6C1); + box-sizing: border-box; + position: relative; + +} + + +.post-content { + margin-bottom: 10px; + font-size: 16px; + line-height: 1.5; + background: white; + padding: 10px; + border-radius: 8px; +} + +.single-image { + border-radius: 8px; + max-width: 100%; + height: auto; + margin-bottom: 10px; +} + +.image-grid { + display: flex; + gap: 5px; +} + +.grid-image { + border-radius: 8px; +} + +.grid-image-2 { + width: 49%; + height: 200px; +} + +.grid-image-3, +.grid-image-4 { + width: 32%; + height: 150px; +} + +.grid-image-5, +.grid-image-6 { + width: 49%; + height: 150px; +} + +.grid-image-7, +.grid-image-8, +.grid-image-9 { + width: 32%; + height: 100px; +} + +.right-operate-container { + position: absolute; + bottom: 15%; + right: 0; + display: flex; + flex-direction: column; + align-items: center; + gap: 15px; + width: 20%; + z-index: 999; +} + +.right-operate-container img { + border-radius: 50%; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.2); +} + +.main-content-container { + display: flex; + justify-content: center; + flex-direction: column; + margin-top: 35%; +} \ No newline at end of file diff --git a/src/components/Discovery.tsx b/src/components/Discovery.tsx new file mode 100644 index 0000000..91649ea --- /dev/null +++ b/src/components/Discovery.tsx @@ -0,0 +1,114 @@ +import React, { useEffect, useState } from "react"; +import { instance } from '../utils/api'; +import { Flex, Image, Typography } from 'react-vant'; +import './Discovery.css'; // 引入自定义样式 +import { Like, LikeO, ShareO } from "@react-vant/icons"; +import { useSwipeable } from 'react-swipeable'; + +function Discovery() { + const [posts, setPosts] = useState([]); + const [postIndex, setPostIndex] = useState(0); + const [likeState, setLikeState] = useState(false); + + useEffect(() => { + fetchPosts(); + }, []); + + useEffect(() => { + if (posts.length > 0) { + setLikeState(posts[postIndex]?.isLike); + } + }, [postIndex, posts]); + + const fetchPosts = async () => { + try { + const res = await instance.post("/posts"); + setPosts(prevPosts => [...prevPosts, ...res.data.datas]); + } catch (err) { + console.log(err); + } + }; + + const handleClickLike = async (state) => { + const res = await instance.post("/likePost", { + id: posts[postIndex]?.id, + isLike: state + }); + setLikeState(state); + }; + + const handleSwipeUp = () => { + console.log("handleSwipeUp") + if (postIndex < posts.length - 1) { + setPostIndex(prevIndex => prevIndex + 1); + } else { + fetchPosts(); // 加载更多帖子 + } + }; + + const handleSwipeDown = () => { + console.log("handleSwipeDown") + if (postIndex > 0) { + setPostIndex(prevIndex => prevIndex - 1); + } + }; + + const swipeHandlers = useSwipeable({ + onSwipedUp: handleSwipeUp, + onSwipedDown: handleSwipeDown, + preventScrollOnSwipe: true, + trackMouse: true // 允许在电脑上用鼠标拖动测试 + }); + + const images = posts[postIndex]?.images || []; + + return ( +
+
+ + {posts[postIndex]?.content} + + + {images.length === 1 ? ( + + ) : images.length > 1 ? ( + + {images.map((image, idx) => ( + + ))} + + ) : null} +
+ + +
+ + {likeState ? ( + { handleClickLike(0) }} color="red" /> + ) : ( + { handleClickLike(1) }} /> + )} + +
+
+ ); +} + +export default Discovery; diff --git a/src/components/SearchPage.tsx b/src/components/SearchPage.tsx deleted file mode 100644 index 315ed39..0000000 --- a/src/components/SearchPage.tsx +++ /dev/null @@ -1,10 +0,0 @@ - -function SearchPage() { - return ( -
- SearchPage -
- ) -} - -export default SearchPage; \ No newline at end of file diff --git a/src/utils/api.js b/src/utils/api.js index 087125d..608913b 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -2,8 +2,10 @@ import axios from 'axios'; import LocalStorageUtil from './LocalStorageUtil'; export const instance = axios.create({ - // baseURL: 'https://app102.acapp.acwing.com.cn/api', - baseURL: 'http://localhost:8809/api', + baseURL: 'https://app102.acapp.acwing.com.cn/api', + // baseURL: 'http://localhost:8809/api', + // baseURL: 'http://39.100.90.48:8809/api', + headers: { 'Content-Type': 'application/json', }