根据用户反馈的三个核心问题:
HTML修改 (drag-upload-modal.component.html):
<!-- 添加图片错误处理和点击事件 -->
<img
[src]="file.preview"
[alt]="file.name"
class="file-thumbnail"
(click)="viewFullImage(file)"
(error)="onImageError(file)"
loading="eager" />
<!-- 新增图片查看器 -->
@if (viewingImage) {
<div class="image-viewer-overlay" (click)="closeImageViewer()">
<div class="image-viewer-container" (click)="preventDefault($event)">
<button class="close-viewer-btn" (click)="closeImageViewer()">
<svg>...</svg>
</button>
<img [src]="viewingImage.preview" [alt]="viewingImage.name" class="full-image" />
<div class="image-info">
<div class="image-name">{{ viewingImage.name }}</div>
<div class="image-details">
<span>{{ getFileSizeDisplay(viewingImage.size) }}</span>
<span>{{ viewingImage.analysisResult.dimensions.width }} × {{ viewingImage.analysisResult.dimensions.height }}</span>
<span>质量: {{ getQualityLevelText(viewingImage.analysisResult.quality.level) }}</span>
</div>
</div>
</div>
</div>
}
TypeScript方法 (drag-upload-modal.component.ts):
// 图片查看器状态
viewingImage: UploadFile | null = null;
// 查看完整图片
viewFullImage(file: UploadFile): void {
if (file.preview) {
this.viewingImage = file;
this.cdr.markForCheck();
}
}
// 关闭图片查看器
closeImageViewer(): void {
this.viewingImage = null;
this.cdr.markForCheck();
}
// 图片加载错误处理
onImageError(file: UploadFile): void {
console.error('❌ 图片加载失败:', file.name);
// 尝试重新生成预览
if (this.isImageFile(file.file)) {
this.generatePreview(file).catch(err => {
console.error('❌ 重新生成预览失败:', err);
});
}
}
样式 (drag-upload-modal.component.scss):
// 图片查看器
.image-viewer-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.95);
display: flex;
align-items: center;
justify-content: center;
z-index: 3000;
}
.image-viewer-container {
position: relative;
max-width: 95vw;
max-height: 95vh;
.full-image {
max-width: 95vw;
max-height: 80vh;
object-fit: contain;
border-radius: 8px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
}
}
// 缩略图可点击提示
.file-thumbnail {
cursor: pointer;
transition: all 0.2s ease;
&:hover {
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
}
determineSuggestedStage方法判断逻辑过于简单优化后的判断逻辑 (image-analysis.service.ts):
private determineSuggestedStage(
content: ImageAnalysisResult['content'],
quality: ImageAnalysisResult['quality']
): 'white_model' | 'soft_decor' | 'rendering' | 'post_process' {
// 综合判断:像素密度 + 内容精细度 + 质量分数 + 特征
const megapixels = quality.pixelDensity;
const detailLevel = quality.detailLevel;
const qualityScore = quality.score;
const textureQuality = quality.textureQuality;
console.log('🎯 阶段判断依据:', {
像素密度: megapixels,
精细程度: detailLevel,
质量分数: qualityScore,
纹理质量: textureQuality,
有家具: content.hasFurniture,
有灯光: content.hasLighting
});
// 白模阶段:低质量 + 无装饰元素 + 低精细度
if (!content.hasFurniture && !content.hasLighting &&
(detailLevel === 'minimal' || detailLevel === 'basic') &&
qualityScore < 70) {
return 'white_model';
}
// 软装阶段:有家具 + 无灯光 + 中等质量
if (content.hasFurniture && !content.hasLighting &&
qualityScore >= 60 && qualityScore < 80) {
return 'soft_decor';
}
// 渲染阶段:有灯光 + 高质量 + 详细精细度
if (content.hasLighting &&
(detailLevel === 'detailed' || detailLevel === 'ultra_detailed') &&
qualityScore >= 75 && qualityScore < 90) {
return 'rendering';
}
// 后期处理阶段:超高质量 + 超精细 + 高纹理质量
if (qualityScore >= 90 &&
detailLevel === 'ultra_detailed' &&
textureQuality >= 85 &&
(megapixels === 'ultra_high' || megapixels === 'high')) {
return 'post_process';
}
// 渲染阶段:有灯光效果,即使质量不是最高
if (content.hasLighting && qualityScore >= 70) {
return 'rendering';
}
// 软装阶段:有家具但质量一般
if (content.hasFurniture && qualityScore >= 60) {
return 'soft_decor';
}
// 默认:根据质量分数判断
if (qualityScore >= 85) {
return 'post_process';
} else if (qualityScore >= 70) {
return 'rendering';
} else if (qualityScore >= 55) {
return 'soft_decor';
} else {
return 'white_model';
}
}
判断标准:
| 阶段 | 判断条件 |
|---|---|
| 白模 | 无家具 + 无灯光 + 低精细度(minimal/basic) + 质量<70 |
| 软装 | 有家具 + 无灯光 + 质量60-80 |
| 渲染 | 有灯光 + 详细精细度(detailed/ultra_detailed) + 质量75-90 |
| 后期处理 | 质量≥90 + 超精细(ultra_detailed) + 纹理≥85 + 高像素密度 |
默认兜底:
confirmDragUpload方法中,每个文件上传时都会调用uploadDeliveryFileuploadDeliveryFile方法内部有多个alert调用(权限不足、项目ID缺失、上传失败)添加静默模式参数 (stage-delivery.component.ts):
async uploadDeliveryFile(
event: any,
productId: string,
deliveryType: string,
silentMode: boolean = false // 🔥 新增参数
): Promise<void> {
// 权限检查
if (!this.canEdit) {
if (!silentMode) { // 🔥 只在非静默模式下显示alert
window?.fmode?.alert('您没有上传文件的权限,请联系管理员');
}
return;
}
// 项目ID检查
if (!targetProjectId) {
if (!silentMode) { // 🔥 只在非静默模式下显示alert
window?.fmode?.alert('未找到项目ID,无法上传文件');
}
return;
}
try {
// ... 上传逻辑
} catch (error) {
if (!silentMode) { // 🔥 只在非静默模式下显示alert
window?.fmode?.alert('文件上传失败,请重试');
}
}
}
批量上传时启用静默模式 (stage-delivery.component.ts):
async confirmDragUpload(result: UploadResult): Promise<void> {
for (const fileItem of result.files) {
const mockEvent = {
target: { files: [uploadFile.file] }
};
// 🔥 启用静默模式,避免频繁弹窗
await this.uploadDeliveryFile(mockEvent, fileItem.spaceId, fileItem.stageType, true);
}
// 🔥 所有文件上传完成后,只显示一次提示
if (errorCount === 0) {
window?.fmode?.toast?.success?.(`✅ 成功上传 ${successCount} 个文件`);
} else {
window?.fmode?.alert?.(`⚠️ 上传完成:成功 ${successCount} 个,失败 ${errorCount} 个`);
}
}
修复前:
修复后:
修复前:
修复后:
判断维度:
修复前:
修复后:
private generatePreview(uploadFile: UploadFile): Promise<void> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
uploadFile.preview = e.target?.result as string;
this.cdr.markForCheck();
resolve();
};
reader.onerror = reject;
reader.readAsDataURL(uploadFile.file);
});
}
onImageError(file: UploadFile): void {
console.error('❌ 图片加载失败:', file.name);
if (this.isImageFile(file.file)) {
this.generatePreview(file).catch(err => {
console.error('❌ 重新生成预览失败:', err);
});
}
}
(error)="onImageError(file)"(click)="viewFullImage(file)"viewingImage 状态变量viewFullImage() 方法closeImageViewer() 方法onImageError() 方法.image-viewer-overlay 样式.image-viewer-container 样式.file-thumbnail hover效果determineSuggestedStage() 方法silentMode 参数到 uploadDeliveryFile()silentMode 判断silentMode: trueng build yss-project --base-href=/dev/yss/
obsutil sync ./dist/yss-project/ obs://nova-cloud/dev/yss -i=... -k=... -e="obs.cn-south-1.myhuaweicloud.com" -acl=public-read
obsutil chattri obs://nova-cloud/dev/yss -r -f -i=... -k=... -e="obs.cn-south-1.myhuaweicloud.com" -acl=public-read
hcloud CDN CreateRefreshTasks/v2 --cli-region="cn-north-1" --refresh_task.urls.1="https://app.fmode.cn/dev/yss/" --refresh_task.type="directory" --cli-access-key=... --cli-secret-key=...
FileReader.readAsDataURL() 生成base64预览loading="eager" 属性优先加载创建时间:2025-11-28 最后更新:2025-11-28