|
@@ -26,14 +26,86 @@ type ChatComponentProps = {
|
|
|
const ChatComponent: React.FC<ChatComponentProps> = ({ messageList, onClose }) => {
|
|
const ChatComponent: React.FC<ChatComponentProps> = ({ messageList, onClose }) => {
|
|
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
|
|
- // 自动滚动到最新消息
|
|
|
|
|
|
|
+ const baseMessageStyle = {
|
|
|
|
|
+ display: "flex",
|
|
|
|
|
+ marginBottom: "12px",
|
|
|
|
|
+ width: "100%",
|
|
|
|
|
+ } as React.CSSProperties;
|
|
|
|
|
+
|
|
|
|
|
+ const senderInfoStyle = {
|
|
|
|
|
+ display: "flex",
|
|
|
|
|
+ alignItems: "center",
|
|
|
|
|
+ marginBottom: "4px",
|
|
|
|
|
+ } as React.CSSProperties;
|
|
|
|
|
+
|
|
|
|
|
+ const iconStyle = {
|
|
|
|
|
+ width: "20px",
|
|
|
|
|
+ height: "20px",
|
|
|
|
|
+ marginRight: "4px",
|
|
|
|
|
+ } as React.CSSProperties;
|
|
|
|
|
+
|
|
|
|
|
+ const senderNameStyle = {
|
|
|
|
|
+ fontSize: "14px",
|
|
|
|
|
+ fontWeight: "bold",
|
|
|
|
|
+ } as React.CSSProperties;
|
|
|
|
|
+
|
|
|
|
|
+ const bubbleStyle = {
|
|
|
|
|
+ padding: "10px 14px",
|
|
|
|
|
+ borderRadius: "8px",
|
|
|
|
|
+ boxShadow: "0 1px 2px rgba(0,0,0,0.1)",
|
|
|
|
|
+ maxWidth: "70%",
|
|
|
|
|
+ wordWrap: "break-word",
|
|
|
|
|
+ } as React.CSSProperties;
|
|
|
|
|
+
|
|
|
|
|
+ const contentStyle = {
|
|
|
|
|
+ marginBottom: "0",
|
|
|
|
|
+ lineHeight: "1.5",
|
|
|
|
|
+ fontSize: "14px",
|
|
|
|
|
+ textAlign: "left",
|
|
|
|
|
+ } as React.CSSProperties;
|
|
|
|
|
+
|
|
|
|
|
+ const attachmentContainerStyle = {
|
|
|
|
|
+ display: "flex",
|
|
|
|
|
+ alignItems: "center",
|
|
|
|
|
+ marginBottom: "4px",
|
|
|
|
|
+ gap: "8px",
|
|
|
|
|
+ } as React.CSSProperties;
|
|
|
|
|
+
|
|
|
|
|
+ const fileNameWrapStyle = {
|
|
|
|
|
+ border: "1px solid #ccc",
|
|
|
|
|
+ borderRadius: "4px",
|
|
|
|
|
+ padding: "6px 8px",
|
|
|
|
|
+ display: "inline-block",
|
|
|
|
|
+ } as React.CSSProperties;
|
|
|
|
|
+
|
|
|
|
|
+ const attachmentNameStyle = {
|
|
|
|
|
+ fontSize: "14px",
|
|
|
|
|
+ color: "#000",
|
|
|
|
|
+ whiteSpace: "nowrap",
|
|
|
|
|
+ overflow: "hidden",
|
|
|
|
|
+ textOverflow: "ellipsis",
|
|
|
|
|
+ maxWidth: "200px",
|
|
|
|
|
+ } as React.CSSProperties;
|
|
|
|
|
+
|
|
|
|
|
+ const downloadLinkStyle = {
|
|
|
|
|
+ fontSize: "14px",
|
|
|
|
|
+ color: "#0066cc",
|
|
|
|
|
+ textDecoration: "underline",
|
|
|
|
|
+ cursor: "pointer",
|
|
|
|
|
+ whiteSpace: "nowrap",
|
|
|
|
|
+ } as React.CSSProperties;
|
|
|
|
|
+
|
|
|
|
|
+ const timestampStyle = {
|
|
|
|
|
+ fontSize: "14px",
|
|
|
|
|
+ color: "#000",
|
|
|
|
|
+ } as React.CSSProperties;
|
|
|
|
|
+
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
|
}, [messageList]);
|
|
}, [messageList]);
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<div>
|
|
<div>
|
|
|
- {/* 返回按钮区域 */}
|
|
|
|
|
<div
|
|
<div
|
|
|
style={{
|
|
style={{
|
|
|
marginBottom: "10px",
|
|
marginBottom: "10px",
|
|
@@ -53,24 +125,23 @@ const ChatComponent: React.FC<ChatComponentProps> = ({ messageList, onClose }) =
|
|
|
> 戻る </span>
|
|
> 戻る </span>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- {/* 消息列表 */}
|
|
|
|
|
{messageList.map((msg) => (
|
|
{messageList.map((msg) => (
|
|
|
<div
|
|
<div
|
|
|
key={msg.id}
|
|
key={msg.id}
|
|
|
style={{
|
|
style={{
|
|
|
- marginBottom: "12px",
|
|
|
|
|
- width: "100%",
|
|
|
|
|
|
|
+ ...baseMessageStyle,
|
|
|
|
|
+ justifyContent: msg.sender === "other"? "flex-start" : "flex-end",
|
|
|
display: "flex",
|
|
display: "flex",
|
|
|
flexDirection: "column",
|
|
flexDirection: "column",
|
|
|
- alignItems: msg.sender === "other" ? "flex-start" : "flex-end",
|
|
|
|
|
|
|
+ alignItems: msg.sender === "other"? "flex-start" : "flex-end",
|
|
|
paddingLeft: "40px",
|
|
paddingLeft: "40px",
|
|
|
}}
|
|
}}
|
|
|
>
|
|
>
|
|
|
- {/* 发送者信息 */}
|
|
|
|
|
|
|
+ {/* 根据发送方显示对应的名称和图标 */}
|
|
|
{msg.sender === "other" && (
|
|
{msg.sender === "other" && (
|
|
|
<div style={senderInfoStyle}>
|
|
<div style={senderInfoStyle}>
|
|
|
<img
|
|
<img
|
|
|
- src="/partner.png"
|
|
|
|
|
|
|
+ src="/usr.png"
|
|
|
alt="AAA株式会社图标"
|
|
alt="AAA株式会社图标"
|
|
|
style={iconStyle}
|
|
style={iconStyle}
|
|
|
/>
|
|
/>
|
|
@@ -81,39 +152,30 @@ const ChatComponent: React.FC<ChatComponentProps> = ({ messageList, onClose }) =
|
|
|
<div style={senderInfoStyle}>
|
|
<div style={senderInfoStyle}>
|
|
|
<span style={senderNameStyle}>羽田ベース</span>
|
|
<span style={senderNameStyle}>羽田ベース</span>
|
|
|
<img
|
|
<img
|
|
|
- src="/base.png"
|
|
|
|
|
|
|
+ src="/cat.png"
|
|
|
alt="羽田ベース图标"
|
|
alt="羽田ベース图标"
|
|
|
- style={{ ...iconStyle, marginLeft: "4px", marginRight: "0" }}
|
|
|
|
|
|
|
+ style={{...iconStyle, marginLeft: "4px", marginRight: "0" }}
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
- {/* 聊天内容气泡 - 使用div包裹以便添加伪元素 */}
|
|
|
|
|
- {msg.content.trim() !== "" && (
|
|
|
|
|
- <div
|
|
|
|
|
- style={{
|
|
|
|
|
- position: "relative",
|
|
|
|
|
- marginBottom: "4px"
|
|
|
|
|
|
|
+ {/*聊天内容气泡:内容非空时才显示 */}
|
|
|
|
|
+ {msg.content.trim()!== "" && (
|
|
|
|
|
+ <div
|
|
|
|
|
+ style={{
|
|
|
|
|
+ ...bubbleStyle,
|
|
|
|
|
+ backgroundColor: msg.sender === "other"? "#d9d9d9" : "#deebf7",
|
|
|
|
|
+ marginBottom: "4px",
|
|
|
}}
|
|
}}
|
|
|
- className={msg.sender === "other" ? "other-bubble" : "my-bubble"}
|
|
|
|
|
>
|
|
>
|
|
|
- <div
|
|
|
|
|
- style={{
|
|
|
|
|
- ...bubbleStyle,
|
|
|
|
|
- backgroundColor: msg.sender === "other" ? "#d9d9d9" : "#deebf7",
|
|
|
|
|
- }}
|
|
|
|
|
- >
|
|
|
|
|
- <div style={contentStyle}>
|
|
|
|
|
- {msg.content.split("\n").map((line, index) => (
|
|
|
|
|
- <React.Fragment key={index}>
|
|
|
|
|
- {line}
|
|
|
|
|
- {index < msg.content.split("\n").length - 1 && <br />}
|
|
|
|
|
- </React.Fragment>
|
|
|
|
|
- ))}
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div style={contentStyle}>
|
|
|
|
|
+ {msg.content.split("\n").map((line, index) => (
|
|
|
|
|
+ <React.Fragment key={index}>
|
|
|
|
|
+ {line}
|
|
|
|
|
+ {index < msg.content.split("\n").length - 1 && <br />}
|
|
|
|
|
+ </React.Fragment>
|
|
|
|
|
+ ))}
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
- {/* 三角指向通过CSS伪元素实现,见下方style标签 */}
|
|
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
@@ -137,9 +199,9 @@ const ChatComponent: React.FC<ChatComponentProps> = ({ messageList, onClose }) =
|
|
|
<div
|
|
<div
|
|
|
style={{
|
|
style={{
|
|
|
...timestampStyle,
|
|
...timestampStyle,
|
|
|
- marginLeft: msg.sender === "other" ? "10px" : "0",
|
|
|
|
|
- marginRight: msg.sender === "me" ? "10px" : "0",
|
|
|
|
|
- textAlign: msg.sender === "other" ? "left" : "right",
|
|
|
|
|
|
|
+ marginLeft: msg.sender === "other"? "10px" : "0",
|
|
|
|
|
+ marginRight: msg.sender === "me"? "10px" : "0",
|
|
|
|
|
+ textAlign: msg.sender === "other"? "left" : "right",
|
|
|
}}
|
|
}}
|
|
|
>
|
|
>
|
|
|
{msg.timestamp}
|
|
{msg.timestamp}
|
|
@@ -149,35 +211,6 @@ const ChatComponent: React.FC<ChatComponentProps> = ({ messageList, onClose }) =
|
|
|
|
|
|
|
|
{/* 用于自动滚动的参考点 */}
|
|
{/* 用于自动滚动的参考点 */}
|
|
|
<div ref={messagesEndRef} />
|
|
<div ref={messagesEndRef} />
|
|
|
-
|
|
|
|
|
- {/* 气泡三角样式 - 使用CSS伪元素实现 */}
|
|
|
|
|
- <style jsx global>{`
|
|
|
|
|
- /* 对方消息气泡的三角指向(左侧) */
|
|
|
|
|
- .other-bubble::before {
|
|
|
|
|
- content: "";
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: 10px;
|
|
|
|
|
- left: -8px;
|
|
|
|
|
- width: 0;
|
|
|
|
|
- height: 0;
|
|
|
|
|
- border-top: 8px solid transparent;
|
|
|
|
|
- border-right: 8px solid #d9d9d9; /* 与对方气泡背景色一致 */
|
|
|
|
|
- border-bottom: 8px solid transparent;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* 自己消息气泡的三角指向(右侧) */
|
|
|
|
|
- .my-bubble::before {
|
|
|
|
|
- content: "";
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: 10px;
|
|
|
|
|
- right: -8px;
|
|
|
|
|
- width: 0;
|
|
|
|
|
- height: 0;
|
|
|
|
|
- border-top: 8px solid transparent;
|
|
|
|
|
- border-left: 8px solid #deebf7; /* 与自己气泡背景色一致 */
|
|
|
|
|
- border-bottom: 8px solid transparent;
|
|
|
|
|
- }
|
|
|
|
|
- `}</style>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|
|
@@ -206,7 +239,7 @@ const InputArea: React.FC<{ onSendMessage: (content: string, file: File | null)
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
|
- if (e.key === 'Enter' && !e.shiftKey) {
|
|
|
|
|
|
|
+ if (e.key === 'Enter' &&!e.shiftKey) {
|
|
|
e.preventDefault();
|
|
e.preventDefault();
|
|
|
handleSend();
|
|
handleSend();
|
|
|
}
|
|
}
|
|
@@ -311,81 +344,6 @@ const InputArea: React.FC<{ onSendMessage: (content: string, file: File | null)
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 样式定义
|
|
|
|
|
-const baseMessageStyle = {
|
|
|
|
|
- display: "flex",
|
|
|
|
|
- marginBottom: "12px",
|
|
|
|
|
- width: "100%",
|
|
|
|
|
-} as React.CSSProperties;
|
|
|
|
|
-
|
|
|
|
|
-const senderInfoStyle = {
|
|
|
|
|
- display: "flex",
|
|
|
|
|
- alignItems: "center",
|
|
|
|
|
- marginBottom: "4px",
|
|
|
|
|
-} as React.CSSProperties;
|
|
|
|
|
-
|
|
|
|
|
-const iconStyle = {
|
|
|
|
|
- width: "20px",
|
|
|
|
|
- height: "20px",
|
|
|
|
|
- marginRight: "4px",
|
|
|
|
|
-} as React.CSSProperties;
|
|
|
|
|
-
|
|
|
|
|
-const senderNameStyle = {
|
|
|
|
|
- fontSize: "14px",
|
|
|
|
|
- fontWeight: "bold",
|
|
|
|
|
-} as React.CSSProperties;
|
|
|
|
|
-
|
|
|
|
|
-const bubbleStyle = {
|
|
|
|
|
- padding: "10px 14px",
|
|
|
|
|
- borderRadius: "8px",
|
|
|
|
|
- boxShadow: "0 1px 2px rgba(0,0,0,0.1)",
|
|
|
|
|
- maxWidth: "70%",
|
|
|
|
|
- wordWrap: "break-word",
|
|
|
|
|
-} as React.CSSProperties;
|
|
|
|
|
-
|
|
|
|
|
-const contentStyle = {
|
|
|
|
|
- marginBottom: "0",
|
|
|
|
|
- lineHeight: "1.5",
|
|
|
|
|
- fontSize: "14px",
|
|
|
|
|
- textAlign: "left",
|
|
|
|
|
-} as React.CSSProperties;
|
|
|
|
|
-
|
|
|
|
|
-const attachmentContainerStyle = {
|
|
|
|
|
- display: "flex",
|
|
|
|
|
- alignItems: "center",
|
|
|
|
|
- marginBottom: "4px",
|
|
|
|
|
- gap: "8px",
|
|
|
|
|
-} as React.CSSProperties;
|
|
|
|
|
-
|
|
|
|
|
-const fileNameWrapStyle = {
|
|
|
|
|
- border: "1px solid #ccc",
|
|
|
|
|
- borderRadius: "4px",
|
|
|
|
|
- padding: "6px 8px",
|
|
|
|
|
- display: "inline-block",
|
|
|
|
|
-} as React.CSSProperties;
|
|
|
|
|
-
|
|
|
|
|
-const attachmentNameStyle = {
|
|
|
|
|
- fontSize: "14px",
|
|
|
|
|
- color: "#000",
|
|
|
|
|
- whiteSpace: "nowrap",
|
|
|
|
|
- overflow: "hidden",
|
|
|
|
|
- textOverflow: "ellipsis",
|
|
|
|
|
- maxWidth: "200px",
|
|
|
|
|
-} as React.CSSProperties;
|
|
|
|
|
-
|
|
|
|
|
-const downloadLinkStyle = {
|
|
|
|
|
- fontSize: "14px",
|
|
|
|
|
- color: "#0066cc",
|
|
|
|
|
- textDecoration: "underline",
|
|
|
|
|
- cursor: "pointer",
|
|
|
|
|
- whiteSpace: "nowrap",
|
|
|
|
|
-} as React.CSSProperties;
|
|
|
|
|
-
|
|
|
|
|
-const timestampStyle = {
|
|
|
|
|
- fontSize: "14px",
|
|
|
|
|
- color: "#000",
|
|
|
|
|
-} as React.CSSProperties;
|
|
|
|
|
-
|
|
|
|
|
// 示例数据
|
|
// 示例数据
|
|
|
const initialMessages: ChatMessage[] = [
|
|
const initialMessages: ChatMessage[] = [
|
|
|
{
|
|
{
|
|
@@ -459,3 +417,4 @@ const Chat: React.FC<ChatProps> = ({ onClose }) => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
export default Chat;
|
|
export default Chat;
|
|
|
|
|
+
|