mirror of
https://github.com/MatrixSeven/file-transfer-go.git
synced 2026-02-04 03:25:03 +08:00
feat: 优化图片传输功能,添加发送和接收图片的状态管理
This commit is contained in:
@@ -250,29 +250,43 @@ build_frontend() {
|
||||
print_verbose "执行 SSG 构建..."
|
||||
|
||||
# 临时移除 API 目录
|
||||
api_backup_name=""
|
||||
if [ -d "src/app/api" ]; then
|
||||
mv src/app/api /tmp/next-api-backup-$(date +%s) 2>/dev/null || true
|
||||
api_backup_name="next-api-backup-$(date +%s)-$$"
|
||||
mv src/app/api "/tmp/$api_backup_name" 2>/dev/null || true
|
||||
print_verbose "API 目录已备份到: /tmp/$api_backup_name"
|
||||
fi
|
||||
|
||||
# 构建
|
||||
build_success=true
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
NEXT_EXPORT=true yarn build
|
||||
NEXT_EXPORT=true yarn build || build_success=false
|
||||
else
|
||||
NEXT_EXPORT=true yarn build > build.log 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
NEXT_EXPORT=true yarn build > build.log 2>&1 || build_success=false
|
||||
if [ "$build_success" = false ]; then
|
||||
print_error "前端构建失败,查看 $FRONTEND_DIR/build.log"
|
||||
cat build.log
|
||||
# 恢复 API 目录后再退出
|
||||
if [ -n "$api_backup_name" ] && [ -d "/tmp/$api_backup_name" ]; then
|
||||
mv "/tmp/$api_backup_name" src/app/api 2>/dev/null || true
|
||||
print_verbose "已恢复 API 目录"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
rm -f build.log
|
||||
fi
|
||||
|
||||
# 恢复 API 目录
|
||||
api_backup=$(ls /tmp/next-api-backup-* 2>/dev/null | head -1)
|
||||
if [ -n "$api_backup" ]; then
|
||||
mv "$api_backup" src/app/api 2>/dev/null || true
|
||||
if [ -n "$api_backup_name" ] && [ -d "/tmp/$api_backup_name" ]; then
|
||||
mv "/tmp/$api_backup_name" src/app/api 2>/dev/null || true
|
||||
print_verbose "已恢复 API 目录"
|
||||
elif [ -n "$api_backup_name" ]; then
|
||||
print_warning "API 目录备份丢失,无法恢复: /tmp/$api_backup_name"
|
||||
fi
|
||||
|
||||
# 清理历史备份文件(保留最近1小时的)
|
||||
find /tmp -name "next-api-backup-*" -mmin +60 -exec rm -rf {} \; 2>/dev/null || true
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# 验证构建结果
|
||||
@@ -483,11 +497,22 @@ show_summary() {
|
||||
error_cleanup() {
|
||||
print_error "构建过程中发生错误"
|
||||
|
||||
# 尝试恢复 API 目录
|
||||
api_backup=$(ls /tmp/next-api-backup-* 2>/dev/null | head -1)
|
||||
if [ -n "$api_backup" ] && [ -d "$FRONTEND_DIR" ]; then
|
||||
mv "$api_backup" "$FRONTEND_DIR/src/app/api" 2>/dev/null || true
|
||||
print_verbose "已恢复 API 目录"
|
||||
# 尝试恢复 API 目录 - 查找所有可能的备份
|
||||
local current_process_backups=$(ls /tmp/next-api-backup-*-$$ 2>/dev/null || true)
|
||||
local other_backups=$(ls /tmp/next-api-backup-* 2>/dev/null | grep -v "\-$$" | head -1 || true)
|
||||
|
||||
# 优先恢复当前进程的备份
|
||||
if [ -n "$current_process_backups" ]; then
|
||||
for backup in $current_process_backups; do
|
||||
if [ -d "$backup" ] && [ -d "$FRONTEND_DIR" ]; then
|
||||
mv "$backup" "$FRONTEND_DIR/src/app/api" 2>/dev/null || true
|
||||
print_verbose "已恢复 API 目录: $backup"
|
||||
break
|
||||
fi
|
||||
done
|
||||
elif [ -n "$other_backups" ] && [ -d "$FRONTEND_DIR" ]; then
|
||||
mv "$other_backups" "$FRONTEND_DIR/src/app/api" 2>/dev/null || true
|
||||
print_verbose "已恢复 API 目录: $other_backups"
|
||||
fi
|
||||
|
||||
exit 1
|
||||
|
||||
@@ -35,7 +35,8 @@ export default function TextTransfer({
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isRoomCreated, setIsRoomCreated] = useState(false);
|
||||
const [connectedUsers, setConnectedUsers] = useState(0);
|
||||
const [images, setImages] = useState<string[]>([]);
|
||||
const [sentImages, setSentImages] = useState<string[]>([]); // 发送的图片
|
||||
const [receivedImages, setReceivedImages] = useState<string[]>([]); // 接收的图片
|
||||
const [imagePreview, setImagePreview] = useState<string | null>(null); // 图片预览状态
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null); // 图片预览弹窗状态
|
||||
const [hasShownJoinSuccess, setHasShownJoinSuccess] = useState(false); // 防止重复显示加入成功消息
|
||||
@@ -112,8 +113,15 @@ export default function TextTransfer({
|
||||
case 'image-send':
|
||||
// 接收到发送的图片
|
||||
if (message.payload?.imageData) {
|
||||
setImages(prev => [...prev, message.payload.imageData]);
|
||||
showToast('收到新的图片!', 'success');
|
||||
console.log('接收到图片数据:', message.payload.imageData.substring(0, 100) + '...');
|
||||
// 验证图片数据格式
|
||||
if (message.payload.imageData.startsWith('data:image/')) {
|
||||
setReceivedImages(prev => [...prev, message.payload.imageData]);
|
||||
showToast('收到新的图片!', 'success');
|
||||
} else {
|
||||
console.error('无效的图片数据格式:', message.payload.imageData.substring(0, 50));
|
||||
showToast('收到的图片格式不正确', 'error');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -335,6 +343,7 @@ export default function TextTransfer({
|
||||
|
||||
// 转为base64,质量为0.8
|
||||
const compressedDataUrl = canvas.toDataURL('image/jpeg', 0.8);
|
||||
console.log('图片压缩完成,数据长度:', compressedDataUrl.length, '前100字符:', compressedDataUrl.substring(0, 100));
|
||||
resolve(compressedDataUrl);
|
||||
} catch (error) {
|
||||
reject(new Error('图片压缩失败: ' + error));
|
||||
@@ -370,7 +379,7 @@ export default function TextTransfer({
|
||||
try {
|
||||
showToast('正在处理图片...', 'info');
|
||||
const compressedImageData = await compressImage(file);
|
||||
setImages(prev => [...prev, compressedImageData]);
|
||||
setSentImages(prev => [...prev, compressedImageData]);
|
||||
|
||||
// 发送图片给其他用户
|
||||
if (websocket && isConnected) {
|
||||
@@ -442,8 +451,12 @@ export default function TextTransfer({
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const index = images.indexOf(src);
|
||||
downloadImage(src, index);
|
||||
// 尝试在发送和接收的图片中查找
|
||||
let index = sentImages.indexOf(src);
|
||||
if (index === -1) {
|
||||
index = receivedImages.indexOf(src);
|
||||
}
|
||||
downloadImage(src, index >= 0 ? index : 0);
|
||||
}}
|
||||
className="bg-white/20 hover:bg-white/30 backdrop-blur-sm text-white p-2 rounded-lg shadow-lg transition-all hover:scale-105"
|
||||
title="下载图片"
|
||||
@@ -619,15 +632,15 @@ export default function TextTransfer({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 图片展示区域 */}
|
||||
{images.length > 0 && (
|
||||
{/* 发送方显示已发送的图片 */}
|
||||
{mode === 'send' && sentImages.length > 0 && (
|
||||
<div className="mt-6">
|
||||
<h3 className="text-lg font-medium text-slate-800 mb-3 flex items-center">
|
||||
<Image className="w-5 h-5 mr-2" />
|
||||
已发送的图片 ({images.length})
|
||||
已发送的图片 ({sentImages.length})
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||
{images.map((img, index) => (
|
||||
{sentImages.map((img, index) => (
|
||||
<div key={index} className="relative group overflow-hidden">
|
||||
<img
|
||||
src={img}
|
||||
@@ -770,21 +783,32 @@ export default function TextTransfer({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 接收到的图片展示 */}
|
||||
{images.length > 0 && (
|
||||
{/* 接收方显示接收到的图片 */}
|
||||
{mode === 'receive' && receivedImages.length > 0 && (
|
||||
<div className="mt-6">
|
||||
<h3 className="text-lg font-medium text-slate-800 mb-3 flex items-center">
|
||||
<Image className="w-5 h-5 mr-2" />
|
||||
接收到的图片 ({images.length})
|
||||
接收到的图片 ({receivedImages.length})
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||
{images.map((img, index) => (
|
||||
{receivedImages.map((img, index) => (
|
||||
<div key={index} className="relative group overflow-hidden">
|
||||
<img
|
||||
src={img}
|
||||
alt={`图片 ${index + 1}`}
|
||||
className="w-full h-24 object-cover rounded-lg border-2 border-slate-200 hover:border-emerald-400 transition-all duration-200 cursor-pointer bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50"
|
||||
onClick={() => setPreviewImage(img)}
|
||||
onLoad={(e) => {
|
||||
console.log(`图片 ${index + 1} 加载成功`);
|
||||
}}
|
||||
onError={(e) => {
|
||||
console.error(`图片 ${index + 1} 加载失败:`, img.substring(0, 100));
|
||||
e.currentTarget.style.backgroundColor = '#f1f5f9';
|
||||
e.currentTarget.style.display = 'flex';
|
||||
e.currentTarget.style.alignItems = 'center';
|
||||
e.currentTarget.style.justifyContent = 'center';
|
||||
e.currentTarget.innerHTML = `<span style="color: #64748b; font-size: 12px;">图片加载失败</span>`;
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-20 transition-opacity rounded-lg"></div>
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
||||
2:I[6801,["177","static/chunks/app/layout-d0f2a5cbcfca20f5.js"],"ToastProvider"]
|
||||
3:I[7555,[],""]
|
||||
4:I[1295,[],""]
|
||||
5:I[5055,["660","static/chunks/660-236c74c3e8221aec.js","974","static/chunks/app/page-28117649abd2bba1.js"],"default"]
|
||||
5:I[5055,["984","static/chunks/984-39bc34483f07a61c.js","974","static/chunks/app/page-f438e315cafde810.js"],"default"]
|
||||
6:I[9665,[],"OutletBoundary"]
|
||||
8:I[4911,[],"AsyncMetadataOutlet"]
|
||||
a:I[9665,[],"ViewportBoundary"]
|
||||
@@ -11,8 +11,8 @@ d:"$Sreact.suspense"
|
||||
f:I[8393,[],""]
|
||||
:HL["/_next/static/media/569ce4b8f30dc480-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
||||
:HL["/_next/static/media/93f479601ee12b01-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
||||
:HL["/_next/static/css/e6f72b7992dea2ee.css","style"]
|
||||
0:{"P":null,"b":"C3SLXyByF2YDtLzyWwMjW","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/e6f72b7992dea2ee.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"zh-CN","children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L5",null,{}],null,["$","$L6",null,{"children":["$L7",["$","$L8",null,{"promise":"$@9"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$La",null,{"children":"$Lb"}],["$","meta",null,{"name":"next-size-adjust","content":""}]],["$","$Lc",null,{"children":["$","div",null,{"hidden":true,"children":["$","$d",null,{"fallback":null,"children":"$Le"}]}]}]]}],false]],"m":"$undefined","G":["$f",[]],"s":false,"S":true}
|
||||
:HL["/_next/static/css/7908b4c934e87974.css","style"]
|
||||
0:{"P":null,"b":"dIaE9ChLg6gKLX3iG0ErS","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/7908b4c934e87974.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"zh-CN","children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L5",null,{}],null,["$","$L6",null,{"children":["$L7",["$","$L8",null,{"promise":"$@9"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$La",null,{"children":"$Lb"}],["$","meta",null,{"name":"next-size-adjust","content":""}]],["$","$Lc",null,{"children":["$","div",null,{"hidden":true,"children":["$","$d",null,{"fallback":null,"children":"$Le"}]}]}]]}],false]],"m":"$undefined","G":["$f",[]],"s":false,"S":true}
|
||||
b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
|
||||
7:null
|
||||
10:I[8175,[],"IconMark"]
|
||||
|
||||
Reference in New Issue
Block a user