parent
0f21aa3586
commit
f5332c270b
After Width: | Height: | Size: 887 B |
After Width: | Height: | Size: 905 B |
@ -0,0 +1,66 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
interface Props {
|
||||
commentCount: number;
|
||||
thumbupCount: number;
|
||||
haveThumbup: boolean;
|
||||
className?: string;
|
||||
onShowInput: () => void;
|
||||
onShowAll: () => Promise<boolean>;
|
||||
onThumbup: () => Promise<boolean>;
|
||||
}
|
||||
export default function ButtonBar({
|
||||
commentCount,
|
||||
thumbupCount,
|
||||
haveThumbup,
|
||||
className,
|
||||
onShowAll,
|
||||
onShowInput,
|
||||
onThumbup,
|
||||
}: Props) {
|
||||
const [showAll, setShowAll] = useState<boolean>(false);
|
||||
const [thumbup, setThumbup] = useState<boolean>(false);
|
||||
|
||||
const handleShowAll = async () => {
|
||||
const res = await onShowAll();
|
||||
setShowAll(res);
|
||||
};
|
||||
|
||||
const handleThumbup = async () => {
|
||||
const res = await onThumbup();
|
||||
if (res) setThumbup(!thumbup);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setThumbup(haveThumbup);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`flex flex-row items-start gap-[18px] ${className}`}>
|
||||
{/* 回复 */}
|
||||
<div
|
||||
className="pt-[3px] text-[14px] leading-[19.6px] text-[rgba(0,0,0,0.4)] hover:text-theme cursor-pointer"
|
||||
onClick={onShowInput}
|
||||
>
|
||||
回复
|
||||
</div>
|
||||
{/* 展开回复 */}
|
||||
{commentCount > 1 && !showAll && (
|
||||
<div className="pt-[3px] text-theme text-[14px] leading-[19.6px] cursor-pointer" onClick={handleShowAll}>
|
||||
展开{commentCount}条回复
|
||||
</div>
|
||||
)}
|
||||
{/* 点赞 */}
|
||||
<div className="flex flex-row items-center w-[49px] h-[24px] group cursor-pointer" onClick={handleThumbup}>
|
||||
{thumbupCount > 0 && (
|
||||
<div className="mr-[2px] pt-[5px] text-[rgba(0,0,0,0.7)] text-[12px] leading-[16.8px] group-hover:text-theme">
|
||||
{thumbupCount}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={`w-[24px] h-[24px] ${thumbup ? 'bg-[url(/img/icon/thumb-active.svg)]' : 'bg-[url(/img/icon/thumb.svg)]'} group-hover:bg-[bg-[url(/img/icon/thumb-active.svg)]]`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,3 +1,42 @@
|
||||
export default function CommentItem() {
|
||||
return <div></div>;
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Button } from '@/components';
|
||||
|
||||
interface Props {
|
||||
onSubmit: (value: string) => Promise<boolean>;
|
||||
}
|
||||
|
||||
export default function CommentItem({ onSubmit }: Props) {
|
||||
const [value, setValue] = useState<string>('');
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
setLoading(true);
|
||||
await onSubmit(value);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<textarea
|
||||
value={value}
|
||||
cols={30}
|
||||
rows={10}
|
||||
disabled={loading}
|
||||
placeholder="发布评论"
|
||||
className="w-[712px] h-[89px] p-[15px] radounded-[3px] bg-[#F2F3F7] text-[15px] leading-[21px] resize-none"
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={loading || !value}
|
||||
className="w-[80px] h-[35px] my-[15px] rounded-[16px] text-[13px] leading-[18.2px]"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
确定
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
'use client';
|
||||
|
||||
/** 评论输入框 */
|
||||
import { useState, useRef, useMemo, useEffect } from 'react';
|
||||
|
||||
import { Avatar, Button, Input } from '@/components';
|
||||
|
||||
interface Props {
|
||||
nickName: string;
|
||||
avatar: string;
|
||||
className?: string;
|
||||
onSubmit: (value: string) => Promise<boolean>;
|
||||
}
|
||||
|
||||
export default function CommentInput({ nickName, avatar, className, onSubmit }: Props) {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [value, setValue] = useState<string>('');
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
const btnDisabled = useMemo(() => {
|
||||
return loading && !value;
|
||||
}, [loading, value]);
|
||||
|
||||
const handleReplySubmit = async () => {
|
||||
if (!value) return;
|
||||
setLoading(true);
|
||||
await onSubmit(value);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
inputRef.current && inputRef.current.focus();
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={`${className}`}>
|
||||
<div className={`flex flex-row items-center`}>
|
||||
<Avatar size={40} src={avatar} className="mr-[6px]" />
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
disabled={loading}
|
||||
value={value}
|
||||
placeholder={` 回复 ${nickName}`}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
className="w-[600px] h-[48px] px-[14px] rounded-[3px] bg-[#F2F3F7] text-[rgba(0,0,0,0.4)] text-[14px] leading-[19.6px]"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={btnDisabled}
|
||||
className="w-[80px] h-[35px] mt-[15px] rounded-[16px] text-[13px] leading-[18.2px]"
|
||||
onClick={handleReplySubmit}
|
||||
>
|
||||
确定
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,3 +1,102 @@
|
||||
export default function CommentItem() {
|
||||
return <div></div>;
|
||||
'use client';
|
||||
|
||||
import { useState, useRef } from 'react';
|
||||
|
||||
import ButtonBar from './ButtonBar';
|
||||
import CommentInput from './CommentInput';
|
||||
import SubCommentItem from './CommentSubItem';
|
||||
|
||||
import { Avatar } from '@/components';
|
||||
import { apiCommentThumbup, apiCommentSave, apiGetSubComment } from '@/services';
|
||||
|
||||
// 回复
|
||||
const handleReply = async (params: { content: string; journalId: string; parentId: string }) => {
|
||||
await apiCommentSave(params);
|
||||
};
|
||||
|
||||
export default function CommentItem({
|
||||
_id,
|
||||
avatar,
|
||||
nickName,
|
||||
publishTime,
|
||||
location,
|
||||
content,
|
||||
haveThumbup,
|
||||
thumbupCount,
|
||||
commentCount,
|
||||
topChildrenComment,
|
||||
journalId,
|
||||
className,
|
||||
}: Comment & { className?: string }) {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [showAll, setShowAll] = useState<boolean>(false);
|
||||
const [showInput, setShowInput] = useState<boolean>(false);
|
||||
const [subCommentList, setSubCommentList] = useState<Comment[]>(topChildrenComment ? [topChildrenComment] : []);
|
||||
|
||||
// 展示全部评论
|
||||
const handleShowAll = async () => {
|
||||
setShowAll(true);
|
||||
const res = await apiGetSubComment({ parentId: _id, size: commentCount, page: 1 });
|
||||
if (res.code === 200) setSubCommentList(res.data.rows);
|
||||
return res.code === 200;
|
||||
};
|
||||
|
||||
// 展示回复框
|
||||
const handleShowInput = () => {
|
||||
setShowInput(true);
|
||||
inputRef.current && inputRef.current.focus();
|
||||
};
|
||||
|
||||
// 提交回复
|
||||
const handleReplySubmit = async (value: string) => {
|
||||
const res = await apiCommentSave({ content: value, journalId: journalId, parentId: _id });
|
||||
if (res.code === 200) setShowInput(false);
|
||||
return res.code === 200;
|
||||
};
|
||||
|
||||
// 点赞
|
||||
const handleThumbup = async () => {
|
||||
const res = await apiCommentThumbup(_id);
|
||||
return res.code === 200;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${className}`}>
|
||||
{/* 一级评论 */}
|
||||
<div className={`flex flex-row flex-shrink`}>
|
||||
<Avatar size={40} src={avatar} />
|
||||
<div className="ml-[15px]">
|
||||
<div className="text-[rgba(0,0,0,0.7)] text-[13px] leading-[18.2px]">{nickName}</div>
|
||||
<div className="mt-[4px] text-[rgba(0,0,0,0.4)] text-[10px] leading-[14px]">{`${publishTime} ${location}`}</div>
|
||||
<div className="mt-[6px] w-[657px] text-base text-[14px] leading-[19.6spx] text-overflow">{content}</div>
|
||||
{/* 子评论 */}
|
||||
<div>
|
||||
{subCommentList.length > 0 &&
|
||||
subCommentList.map((subComment) => (
|
||||
<SubCommentItem key={subComment._id} className="mt-[12px]" {...subComment} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 按钮 */}
|
||||
<ButtonBar
|
||||
className={`mt-[15px] ${topChildrenComment ? 'ml-[79px]' : 'ml-[54px]'}`}
|
||||
commentCount={commentCount}
|
||||
thumbupCount={thumbupCount}
|
||||
haveThumbup={haveThumbup}
|
||||
onShowInput={handleShowInput}
|
||||
onShowAll={handleShowAll}
|
||||
onThumbup={handleThumbup}
|
||||
/>
|
||||
{/* 回复 */}
|
||||
{showInput && (
|
||||
<CommentInput
|
||||
nickName={nickName}
|
||||
avatar={avatar}
|
||||
className="mt-[25px] ml-[55px]"
|
||||
onSubmit={handleReplySubmit}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,10 @@
|
||||
import CommentItem from './CommentItem';
|
||||
|
||||
export default function CommentList({ commentList }: { commentList: Comment[] }) {
|
||||
return <div></div>;
|
||||
return (
|
||||
<div>
|
||||
{commentList.length > 0 &&
|
||||
commentList.map((comment) => <CommentItem key="_id" {...comment} className="py-[20px]" />)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
/** 子评论输入框 */
|
||||
import { Avatar } from '@/components';
|
||||
|
||||
export default function CommentSubInput() {
|
||||
return (
|
||||
<div>
|
||||
<Avatar />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { Avatar } from '@/components';
|
||||
|
||||
export default function SubCommentItem({
|
||||
avatar,
|
||||
nickName,
|
||||
publishTime,
|
||||
location,
|
||||
content,
|
||||
className,
|
||||
}: Comment & { className?: string }) {
|
||||
return (
|
||||
<div className={`w-[655px] ${className}`}>
|
||||
<div className="flex flex-row">
|
||||
<Avatar size={20} src={avatar} />
|
||||
<div className="ml-[5px]">
|
||||
<div className="w-[630px] text-[13px] leading-[20px] text-overflow">
|
||||
<span className="text-[rgba(0,0,0,0.4)]">{`${nickName}: `}</span>
|
||||
<span className="text-base">{content}</span>
|
||||
{/* 归属地 */}
|
||||
</div>
|
||||
<div className="mt-[4px] text-[rgba(0,0,0,0.4)] text-[10px] leading-[14px]">{`${publishTime} ${location}`}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Reference in new issue