提交 faf0bfab 作者: ZhangLingKun

功能:贴子详情

上级 0c0ee289
流水线 #8162 已通过 于阶段
in 5 分 44 秒
......@@ -989,7 +989,7 @@ export type AllCommentListType = InterListFunction<
* 用户id
*/
userAccountId?: number;
status: number;
status: boolean;
replyCount: number;
userAccountVO: {
userImg: string;
......@@ -999,3 +999,97 @@ export type AllCommentListType = InterListFunction<
};
}
>;
// 话题-评论下的回复
export type ReplyListType = InterListFunction<
{
/**
* 动态id
*/
dynamicId: number;
/**
* 评论id
*/
id: number;
},
{
/**
* 评论内容
*/
content?: string;
/**
* 创建时间
*/
createTime?: string;
/**
* 动态id
*/
dynamicId?: number;
/**
* id
*/
id: number;
/**
* 评论点赞数
*/
likeCount?: number;
/**
* pid
*/
pid: number;
/**
* 回复id
*/
reviewId?: number;
/**
* 更新时间
*/
updateTime?: string;
/**
* 用户id
*/
userAccountId?: number;
status: number;
userAccountVO: {
userImg: string;
userName: string;
nickName: string;
id: number;
};
}
>;
// 发表评论
export type PublishCommentType = InterFunction<
{
content: string;
dynamicId: number;
rootPath?: string;
/**
* pid(注意:单纯评论就为0,有回复就上级id)
*/
pid?: number;
/**
* 评论id(注意:单纯评论就为0 ,有回复就评论id)
*/
reviewId?: number;
},
{}
>;
// 话题-评论/回复点赞,取消
export type ReviewLikesType = InterFunction<
{
/**
* id
*/
id: number;
/**
* status
*/
status: boolean;
},
{}
>;
// 论坛点赞或取消点赞
export type LikeOrCancelType = InterFunction<
{ dynamicId: number; userId?: number },
null
>;
......@@ -12,14 +12,18 @@ import {
GetSecondDistrictInfo,
IndustryListPagesType,
LeaseGoodsListType,
LikeOrCancelType,
ListBannerImgType,
ListBrandInfoType,
ListCompanyInfoByCoopIdType,
ListNewsType,
ListTenderInfoType,
NewDetailsType,
PublishCommentType,
RecommendGoodsType,
ReplyListType,
RequirementsListType,
ReviewLikesType,
} from '@/api/interface/home';
import request from '@/api/request';
......@@ -111,4 +115,20 @@ export class HomeAPI {
// 话题-所有评论
static getAllCommentList: AllCommentListType = (params) =>
request.post('/release/gambit/allCommentList', params);
// 话题-评论下的回复
static getReplyList: ReplyListType = (data) =>
request.post('/release/gambit/replyList', data);
// 论坛-评论-发表
static publishComment: PublishCommentType = (data) =>
request.post('/release/dynamic/comment', data);
// 话题-评论/回复点赞,取消
static reviewLikes: ReviewLikesType = (params) =>
request.get('/release/dynamic/reviewLikes', { params });
// 论坛-点赞或取消点赞
static likeOrCancel: LikeOrCancelType = (params) =>
request.get('/release/dynamic/likeOrCancel', { params });
}
import React from 'react';
import { LikeOutlined, MessageOutlined } from '@ant-design/icons';
import { Button } from 'antd';
import React, { useState } from 'react';
import {
DownOutlined,
LikeOutlined,
MessageOutlined,
UpOutlined,
} from '@ant-design/icons';
import { App, Button, Input } from 'antd';
import styled from 'styled-components';
import { HomeAPI } from '@/api';
import { InterListType } from '@/api/interface';
import { AllCommentListType } from '@/api/interface/home';
import { AllCommentListType, ReplyListType } from '@/api/interface/home';
// 详情类型
type DetailType = InterListType<AllCommentListType>[0];
// 列表类型
type ListType = InterListType<ReplyListType>;
const ForumCommentItem: React.FC<{ detail: DetailType }> = ({ detail }) => {
const ForumCommentItem: React.FC<{
detail: DetailType;
reply?: ListType;
origin?: DetailType;
onRefresh?: () => void;
}> = ({ detail, reply, origin, onRefresh }) => {
// 静态组件
const { message } = App.useApp();
// 是否显示回复输入
const [isInput, setIsInput] = useState(false);
// 是否展开评论列表
const [isReply, setIsReply] = useState(false);
// 回复列表
const [replyList, setReplyList] = useState<ListType>([]);
// 获取回复列表
const getReplyList = async () => {
const res = await HomeAPI.getReplyList({
dynamicId: detail?.dynamicId,
id: detail?.id,
pageNo: 1,
pageSize: 999,
});
if (res && res.code === '200') {
// console.log('回复列表 ===>', res.result?.list);
setReplyList(res.result?.list || []);
}
};
// 展开评论列表
const handleReply = async (e: Boolean) => {
setIsReply(!e);
if (!e) {
getReplyList().then();
} else {
setReplyList([]);
}
};
// 获取@人的名字
const getReplyName = () => {
if (!reply) return undefined;
const item = reply?.find((i) => i?.id === detail?.pid);
return item ? (
<>
回复&nbsp;
<Button type={'link'} className="px-0">
@{item?.userAccountVO?.nickName}
</Button>
&nbsp;
</>
) : undefined;
};
// 获取@人的名字
const getReplyNameStr = () => {
if (!reply) return `回复 @${detail?.userAccountVO?.nickName} `;
const item = reply?.find((i) => i?.id === detail?.pid);
return item
? `回复 @${item?.userAccountVO?.nickName} `
: `回复 @${detail?.userAccountVO?.nickName} `;
};
// 回复数据
const [replyText, setReplyText] = useState<string>();
// 提交数据
const handleSubmit = async () => {
if (!replyText) {
await message.warning('请输入内容');
return;
}
// 提交数据
const res = await HomeAPI.publishComment({
content: replyText,
dynamicId: detail?.dynamicId,
pid: detail?.id,
reviewId: origin?.id || detail?.id,
});
if (res && res.code === '200') {
message.success('回复成功');
setReplyText(undefined);
setIsInput(false);
onRefresh?.();
}
};
// 点赞
const handleReviewLikes = async () => {
const res = await HomeAPI.reviewLikes({
id: detail?.id,
status: !detail?.status,
});
if (res && res.code === '200') {
message.success(!detail?.status ? '点赞成功' : '取消点赞');
onRefresh?.();
}
};
return (
<ForumCommentWrap>
<img
......@@ -21,18 +119,68 @@ const ForumCommentItem: React.FC<{ detail: DetailType }> = ({ detail }) => {
{detail?.userAccountVO?.nickName ||
`云享飞用户_${detail?.userAccountId}`}
</div>
<div className="text">{detail?.content}</div>
<div className="text">
{getReplyName()}
{detail?.content}
</div>
<div className="action">
<div className="date">{detail?.createTime}</div>
<div className="button">
<Button type={'link'} icon={<MessageOutlined />}>
回复
<Button
type={'text'}
icon={<MessageOutlined />}
onClick={() => setIsInput(!isInput)}
>
{isInput ? '取消回复' : '回复'}
</Button>
<Button type={'link'} icon={<LikeOutlined />}>
<Button
type={'text'}
icon={<LikeOutlined />}
className={!detail?.status ? 'text-333' : 'text-primary'}
onClick={handleReviewLikes}
>
&nbsp;{detail?.likeCount || 0}
</Button>
</div>
</div>
{isInput && (
<div className="textarea">
<Input.TextArea
placeholder={getReplyNameStr()}
showCount
style={{ height: 86 }}
maxLength={140}
onChange={(e) => setReplyText(e.target.value)}
/>
<div className="action">
<Button type="primary" shape="round" onClick={handleSubmit}>
发布
</Button>
</div>
</div>
)}
{!!replyList?.length &&
replyList?.map((n, m) => (
<ForumCommentItem
detail={n as any}
key={m}
reply={replyList}
origin={detail}
onRefresh={getReplyList}
/>
))}
{!!detail?.replyCount && (
<div className="flex">
<div className="reply" onClick={() => handleReply(isReply)}>
{!isReply ? `展开${detail?.replyCount}条回复` : `收起`}
</div>
{!isReply ? (
<DownOutlined className="ml-1 text-xs text-999" />
) : (
<UpOutlined className="ml-1 text-xs text-999" />
)}
</div>
)}
</div>
</ForumCommentWrap>
);
......@@ -47,10 +195,12 @@ const ForumCommentWrap = styled.div`
display: flex;
align-items: flex-start;
justify-content: flex-start;
margin-bottom: 1.5rem;
&:not(:first-child) {
margin-top: 1rem;
}
.image {
width: 2.5rem;
height: 2.5rem;
width: 2.25rem;
height: 2.25rem;
border-radius: 50%;
margin-right: 1rem;
}
......@@ -76,5 +226,23 @@ const ForumCommentWrap = styled.div`
color: #999999;
}
}
.textarea {
position: relative;
width: 100%;
box-sizing: border-box;
margin-top: 0.5rem;
.action {
margin-top: 1.5rem;
width: 100%;
display: flex;
justify-content: flex-end;
}
}
.reply {
cursor: pointer;
&:hover {
color: #ff392b;
}
}
}
`;
import React from 'react';
import React, { useEffect, useState } from 'react';
import {
ExportOutlined,
LikeOutlined,
MessageOutlined,
} from '@ant-design/icons';
import { Image } from 'antd';
import { App, Button, Image } from 'antd';
import { useRouter } from 'next/router';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { HomeAPI } from '@/api';
import { InterListType } from '@/api/interface';
import { ForumListType } from '@/api/interface/home';
import { RootState } from '@/store';
import { setGlobalData } from '@/store/module/globalData';
import { SystemState } from '@/store/module/system';
// 详情类型
type DetailType = InterListType<ForumListType>[0];
const ForumItemView: React.FC<{ detail: DetailType; isDetail?: Boolean }> = ({
detail,
isDetail = false,
}) => {
const ForumItemView: React.FC<{
detail: DetailType;
isDetail?: Boolean;
hiddenMedia?: Boolean;
onRefresh?: () => void;
}> = ({ detail, isDetail = false, onRefresh, hiddenMedia = false }) => {
// 静态组件
const { message } = App.useApp();
// 路由钩子
const router = useRouter();
// system
const system = useSelector((state: RootState) => state.system) as SystemState;
// store
const dispatch = useDispatch();
// 缓存贴子的数据
const [forumDetail, setForumDetail] = useState<DetailType>();
// 获取帖子的媒体信息 0图片 1视频
const getForumMedia = (type: number) => {
return detail?.mediaVO?.filter((i) => i?.type === type)?.slice(0, 4);
......@@ -38,6 +49,32 @@ const ForumItemView: React.FC<{ detail: DetailType; isDetail?: Boolean }> = ({
router.push(`/forum/detail/${detail?.id}`).then();
}
};
// 点赞
const handleReviewLikes = async () => {
// 如果未登录,则弹出登录框
if (!system?.token) {
dispatch(setGlobalData({ loginModalVisible: true }));
return;
}
const res = await HomeAPI.likeOrCancel({
dynamicId: detail?.id,
});
if (res && res.code === '200') {
message.success(!forumDetail?.likes ? '点赞成功' : '取消点赞');
setForumDetail({
...detail,
likes: !forumDetail?.likes,
likesCount: !forumDetail?.likes
? Number(forumDetail?.likesCount) + 1
: Number(forumDetail?.likesCount) - 1,
});
}
};
// 监听变化
useEffect(() => {
if (!detail) return;
setForumDetail(detail);
}, [detail]);
return (
<ForumItemWrap>
<div className="relative mb-2 flex w-full items-center justify-start">
......@@ -64,43 +101,53 @@ const ForumItemView: React.FC<{ detail: DetailType; isDetail?: Boolean }> = ({
))}
<span>{detail?.description}</span>
</div>
<div className="forum-media flex w-full flex-wrap items-start justify-start">
{getForumMedia(0)?.length
? getForumMedia(0)?.map((i, j) => (
<Image
src={`${i?.url}?x-oss-process=image/quality,Q_50`}
alt="图片"
key={j}
fallback={
'https://pad-video-x.oss-cn-shenzhen.aliyuncs.com/file/identity-bg.png'
}
/>
))
: undefined}
{getForumMedia(1)?.length
? getForumMedia(1)?.map((i, j) => (
<div
className="media-video relative box-border overflow-hidden rounded-lg"
key={j}
>
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
<video src={i.url} controls={true} />
</div>
))
: undefined}
</div>
{!hiddenMedia && (
<div className="forum-media flex w-full flex-wrap items-start justify-start">
{getForumMedia(0)?.length
? getForumMedia(0)?.map((i, j) => (
<Image
src={`${i?.url}?x-oss-process=image/quality,Q_50`}
alt="图片"
key={j}
fallback={
'https://pad-video-x.oss-cn-shenzhen.aliyuncs.com/file/identity-bg.png'
}
/>
))
: undefined}
{getForumMedia(1)?.length
? getForumMedia(1)?.map((i, j) => (
<div
className="media-video relative box-border overflow-hidden rounded-lg"
key={j}
>
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
<video src={i.url} controls={true} />
</div>
))
: undefined}
</div>
)}
<div className="forum-action flex pb-2 text-777">
<div className="action-item mr-8 flex" onClick={() => handleAction(1)}>
<LikeOutlined />
<div className="ml-1">{detail?.likesCount}</div>
<div className="action-item mr-6" onClick={() => handleAction(1)}>
<Button
type={'text'}
icon={<LikeOutlined />}
className={!forumDetail?.likes ? 'text-333' : 'text-primary'}
onClick={handleReviewLikes}
>
&nbsp;{forumDetail?.likesCount}
</Button>
</div>
<div className="action-item mr-8 flex" onClick={() => handleAction(2)}>
<MessageOutlined />
<div className="ml-1">{detail?.commentCount}</div>
<div className="action-item mr-6" onClick={() => handleAction(2)}>
<Button type={'text'} icon={<MessageOutlined />}>
&nbsp;{forumDetail?.commentCount}
</Button>
</div>
<div className="action-item mr-8 flex" onClick={() => handleAction(3)}>
<ExportOutlined />
<div className="ml-1">{detail?.transpond}</div>
<div className="action-item mr-6" onClick={() => handleAction(3)}>
<Button type={'text'} icon={<ExportOutlined />}>
&nbsp;{forumDetail?.transpond || 0}
</Button>
</div>
</div>
</ForumItemWrap>
......
import React from 'react';
import styled from 'styled-components';
import { InterListType } from '@/api/interface';
import { AllCommentListType } from '@/api/interface/home';
// 详情类型
type DetailType = InterListType<AllCommentListType>[0];
const ForumReplyItem = () => {
return (
<ForumReplyWrap>
<div>ForumReplyItem</div>
</ForumReplyWrap>
);
};
export default ForumReplyItem;
// 样式
const ForumReplyWrap = styled.div`
position: relative;
width: 100%;
box-sizing: border-box;
display: flex;
align-items: flex-start;
justify-content: flex-start;
margin-bottom: 1.5rem;
.image {
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
margin-right: 1rem;
}
.content {
position: relative;
width: calc(100% - 3.5rem);
box-sizing: border-box;
}
`;
......@@ -8,6 +8,7 @@ import { AllCommentListType, ForumDetailType } from '@/api/interface/home';
import ForumCommentItem from '@/components/forumCommentItem';
import ForumItemView from '@/components/forumItem';
import LayoutView from '@/components/layout';
import DetailForumList from '@/pages/forum/detail/comp/_detailForumList';
import { wrapper } from '@/store';
import { setGlobalData } from '@/store/module/globalData';
import { UserInfoState } from '@/store/module/userInfo';
......@@ -58,7 +59,7 @@ const ForumDetailPage: React.FC<{ forumDetail: DetailType }> = ({
{ totalPage: number; totalCount: number } & PaginationProps
>({
pageNo: 1,
pageSize: 10,
pageSize: 999,
totalPage: 0,
totalCount: 0,
});
......@@ -75,8 +76,9 @@ const ForumDetailPage: React.FC<{ forumDetail: DetailType }> = ({
if (res && res.code === '200') {
const { list, totalPage, pageNo, pageSize, totalCount } =
res.result || {};
console.log('评论列表 ===>', list);
setCommentList((prevList) => [...prevList, ...(list || [])]);
// console.log('评论列表 ===>', list);
setCommentList(list || []);
// setCommentList((prevList) => [...prevList, ...(list || [])]);
pagination.totalPage = totalPage;
setPagination({ pageNo, pageSize, totalPage, totalCount });
}
......@@ -115,11 +117,17 @@ const ForumDetailPage: React.FC<{ forumDetail: DetailType }> = ({
<div className="detail-list">
<div className="list-title">评论列表</div>
{commentList?.map((i, j) => (
<ForumCommentItem key={j} detail={i} />
<ForumCommentItem
key={j}
detail={i}
onRefresh={getAllCommentList}
/>
))}
</div>
</div>
<div className="forum-order">45345</div>
<div className="forum-order">
<DetailForumList />
</div>
</div>
</ForumDetailWrap>
</LayoutView>
......@@ -183,6 +191,7 @@ const ForumDetailWrap = styled.div`
.detail-list {
position: relative;
width: 100%;
margin-bottom: 2rem;
.list-title {
font-size: 14px;
font-weight: bold;
......@@ -193,7 +202,6 @@ const ForumDetailWrap = styled.div`
.forum-order {
position: relative;
width: 30%;
background: lightgreen;
}
}
`;
import React, { useEffect, useState } from 'react';
import { Affix } from 'antd';
import styled from 'styled-components';
import { HomeAPI } from '@/api';
import { InterListType, PaginationProps } from '@/api/interface';
import { ForumListType } from '@/api/interface/home';
import ForumItemView from '@/components/forumItem';
// 列表类型
type ListType = InterListType<ForumListType>;
const DetailForumList = () => {
// 翻页数据
const [pagination, setPagination] = useState<
{ totalPage: number } & PaginationProps
>({
pageNo: 1,
pageSize: 5,
totalPage: 0,
});
// 论坛列表
const [forumList, setForumList] = useState<ListType>([]);
// 获取论坛列表
const getForumList = async () => {
const res = await HomeAPI.getForumList({
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
});
if (res && res.code === '200') {
const { list, totalPage, pageNo, pageSize } = res.result || {};
setForumList((prevList) => [...prevList, ...(list || [])]);
pagination.totalPage = totalPage;
setPagination({ pageNo, pageSize, totalPage });
}
};
// 组件挂载
useEffect(() => {
getForumList().then();
}, []);
return (
<Affix offsetTop={58}>
<DetailForumWrap>
<div className="mb-4 text-base font-bold">最新动态</div>
<div className="list scroll-view w-full">
{forumList?.map((i, j) => (
<ForumItemView key={j} detail={i} hiddenMedia={true} />
))}
</div>
</DetailForumWrap>
</Affix>
);
};
export default DetailForumList;
// 样式
const DetailForumWrap = styled.div`
position: relative;
width: 100%;
box-sizing: border-box;
.list {
position: relative;
box-sizing: border-box;
height: 70vh;
overflow: hidden;
overflow-y: scroll;
}
`;
......@@ -12,6 +12,9 @@ const themeConfig: ThemeConfig = {
colorLinkHover: '#e1251b',
contentFontSize: 12,
},
Input: {
colorBgContainer: '#F5F5F6',
},
},
};
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论