Spaces:
Sleeping
Sleeping
Update web/src/components/ChatArea.tsx
Browse files- web/src/components/ChatArea.tsx +56 -18
web/src/components/ChatArea.tsx
CHANGED
|
@@ -1,4 +1,8 @@
|
|
| 1 |
// web/src/components/ChatArea.tsx
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
import React, { useRef, useLayoutEffect } from "react";
|
| 3 |
import React, { useEffect, useMemo, useState } from "react";
|
| 4 |
|
|
@@ -130,6 +134,29 @@ interface PendingFile {
|
|
| 130 |
type: FileType;
|
| 131 |
}
|
| 132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
export function ChatArea({
|
| 134 |
messages,
|
| 135 |
onSendMessage,
|
|
@@ -971,21 +998,29 @@ export function ChatArea({
|
|
| 971 |
{uploadedFiles.map((uf, i) => {
|
| 972 |
const key = `${uf.file.name}::${uf.file.size}::${uf.file.lastModified}`;
|
| 973 |
|
| 974 |
-
const
|
|
|
|
| 975 |
const isImage = [".jpg", ".jpeg", ".png", ".gif", ".webp"].some((e) =>
|
| 976 |
-
|
| 977 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 978 |
const thumbUrl = isImage ? getOrCreate(uf.file) : null;
|
| 979 |
-
|
|
|
|
|
|
|
|
|
|
| 980 |
return (
|
| 981 |
<div
|
| 982 |
key={key}
|
| 983 |
className="flex items-center justify-between gap-2 rounded-md border px-3 py-2"
|
| 984 |
>
|
| 985 |
-
{/* ✅ Thumbnail (
|
| 986 |
-
|
| 987 |
-
|
| 988 |
-
|
| 989 |
<img
|
| 990 |
src={thumbUrl}
|
| 991 |
alt={uf.file.name}
|
|
@@ -996,19 +1031,22 @@ export function ChatArea({
|
|
| 996 |
<div className="h-full w-full flex items-center justify-center">
|
| 997 |
<ImageIcon className="h-4 w-4 text-muted-foreground" />
|
| 998 |
</div>
|
| 999 |
-
)
|
| 1000 |
-
|
| 1001 |
-
|
| 1002 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1003 |
<div className="min-w-0 flex-1">
|
| 1004 |
-
<div className="truncate text-sm font-medium">
|
| 1005 |
-
|
| 1006 |
-
</div>
|
| 1007 |
-
<div className="text-xs text-muted-foreground">
|
| 1008 |
-
{uf.type}
|
| 1009 |
-
</div>
|
| 1010 |
</div>
|
| 1011 |
-
|
| 1012 |
<Button
|
| 1013 |
variant="ghost"
|
| 1014 |
size="icon"
|
|
|
|
| 1 |
// web/src/components/ChatArea.tsx
|
| 2 |
+
import pdfIcon from "../assets/file-icons/pdf.png";
|
| 3 |
+
import pptIcon from "../assets/file-icons/ppt.png";
|
| 4 |
+
import otherIcon from "../assets/file-icons/other_format.png";
|
| 5 |
+
|
| 6 |
import React, { useRef, useLayoutEffect } from "react";
|
| 7 |
import React, { useEffect, useMemo, useState } from "react";
|
| 8 |
|
|
|
|
| 134 |
type: FileType;
|
| 135 |
}
|
| 136 |
|
| 137 |
+
function getExt(name: string) {
|
| 138 |
+
const i = name.lastIndexOf(".");
|
| 139 |
+
return i >= 0 ? name.slice(i + 1).toLowerCase() : "";
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
function pickAttachmentThumb(file: { name: string; type?: string; url?: string; previewUrl?: string }) {
|
| 143 |
+
// 1) 图片:保持图片缩略图(优先 previewUrl,其次 url)
|
| 144 |
+
const ext = getExt(file.name);
|
| 145 |
+
const isImageByExt = ["png", "jpg", "jpeg", "gif", "webp", "bmp", "svg"].includes(ext);
|
| 146 |
+
if (isImageByExt) {
|
| 147 |
+
return file.previewUrl || file.url || ""; // 为空就让外层走 fallback
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
// 2) PDF
|
| 151 |
+
if (ext === "pdf") return pdfIcon;
|
| 152 |
+
|
| 153 |
+
// 3) PPT / PPTX
|
| 154 |
+
if (ext === "ppt" || ext === "pptx") return pptIcon;
|
| 155 |
+
|
| 156 |
+
// 4) 其它
|
| 157 |
+
return otherIcon;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
export function ChatArea({
|
| 161 |
messages,
|
| 162 |
onSendMessage,
|
|
|
|
| 998 |
{uploadedFiles.map((uf, i) => {
|
| 999 |
const key = `${uf.file.name}::${uf.file.size}::${uf.file.lastModified}`;
|
| 1000 |
|
| 1001 |
+
const nameLower = uf.file.name.toLowerCase();
|
| 1002 |
+
|
| 1003 |
const isImage = [".jpg", ".jpeg", ".png", ".gif", ".webp"].some((e) =>
|
| 1004 |
+
nameLower.endsWith(e)
|
| 1005 |
);
|
| 1006 |
+
|
| 1007 |
+
const isPdf = nameLower.endsWith(".pdf");
|
| 1008 |
+
const isPpt = nameLower.endsWith(".ppt") || nameLower.endsWith(".pptx");
|
| 1009 |
+
|
| 1010 |
const thumbUrl = isImage ? getOrCreate(uf.file) : null;
|
| 1011 |
+
|
| 1012 |
+
// 非图片:选择对应 icon
|
| 1013 |
+
const fileIcon = isPdf ? pdfIcon : isPpt ? pptIcon : otherIcon;
|
| 1014 |
+
|
| 1015 |
return (
|
| 1016 |
<div
|
| 1017 |
key={key}
|
| 1018 |
className="flex items-center justify-between gap-2 rounded-md border px-3 py-2"
|
| 1019 |
>
|
| 1020 |
+
{/* ✅ Thumbnail (image preview or file icon) */}
|
| 1021 |
+
<div className="h-10 w-10 flex-shrink-0 rounded-lg overflow-hidden border border-border bg-muted">
|
| 1022 |
+
{isImage ? (
|
| 1023 |
+
thumbUrl ? (
|
| 1024 |
<img
|
| 1025 |
src={thumbUrl}
|
| 1026 |
alt={uf.file.name}
|
|
|
|
| 1031 |
<div className="h-full w-full flex items-center justify-center">
|
| 1032 |
<ImageIcon className="h-4 w-4 text-muted-foreground" />
|
| 1033 |
</div>
|
| 1034 |
+
)
|
| 1035 |
+
) : (
|
| 1036 |
+
<img
|
| 1037 |
+
src={fileIcon}
|
| 1038 |
+
alt={uf.file.name}
|
| 1039 |
+
className="h-full w-full object-contain p-1"
|
| 1040 |
+
draggable={false}
|
| 1041 |
+
/>
|
| 1042 |
+
)}
|
| 1043 |
+
</div>
|
| 1044 |
+
|
| 1045 |
<div className="min-w-0 flex-1">
|
| 1046 |
+
<div className="truncate text-sm font-medium">{uf.file.name}</div>
|
| 1047 |
+
<div className="text-xs text-muted-foreground">{uf.type}</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1048 |
</div>
|
| 1049 |
+
|
| 1050 |
<Button
|
| 1051 |
variant="ghost"
|
| 1052 |
size="icon"
|