|
@@ -84,7 +84,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
showDropdown: boolean = false;
|
|
|
currentStage: string = '';
|
|
|
// 新增:10阶段顺序(串式流程)
|
|
|
- stageOrder: ProjectStage[] = ['订单创建', '需求沟通', '方案确认', '建模', '软装', '渲染', '尾款结算', '客户评价', '投诉处理'];
|
|
|
+ stageOrder: ProjectStage[] = ['订单创建', '需求沟通', '方案确认', '建模', '软装', '渲染', '后期', '尾款结算', '客户评价', '投诉处理'];
|
|
|
// 新增:阶段展开状态(默认全部收起,当前阶段在数据加载后自动展开)
|
|
|
expandedStages: Partial<Record<ProjectStage, boolean>> = {
|
|
|
'订单创建': false,
|
|
@@ -103,7 +103,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
sections: Array<{ key: SectionKey; label: string; stages: ProjectStage[] }> = [
|
|
|
{ key: 'order', label: '订单创建', stages: ['订单创建'] },
|
|
|
{ key: 'requirements', label: '确认需求', stages: ['需求沟通', '方案确认'] },
|
|
|
- { key: 'delivery', label: '交付执行', stages: ['建模', '软装', '渲染'] },
|
|
|
+ { key: 'delivery', label: '交付执行', stages: ['建模', '软装', '渲染', '后期'] },
|
|
|
{ key: 'aftercare', label: '售后', stages: ['尾款结算', '客户评价', '投诉处理'] }
|
|
|
];
|
|
|
expandedSection: SectionKey | null = null;
|
|
@@ -145,6 +145,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
whiteModelImages: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = [];
|
|
|
softDecorImages: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = [];
|
|
|
renderLargeImages: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = [];
|
|
|
+ postProcessImages: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = [];
|
|
|
showRenderUploadModal: boolean = false;
|
|
|
pendingRenderLargeItems: Array<{ id: string; name: string; url: string; file: File }> = [];
|
|
|
|
|
@@ -342,7 +343,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
}
|
|
|
|
|
|
// ============ 组长:同步上传与审核(新增,模拟实现) ============
|
|
|
- syncUploadedImages(phase: 'white' | 'soft' | 'render'): void {
|
|
|
+ syncUploadedImages(phase: 'white' | 'soft' | 'render' | 'postProcess'): void {
|
|
|
if (!this.isTeamLeaderView()) return;
|
|
|
const markSynced = (arr: Array<{ reviewStatus?: 'pending'|'approved'|'rejected'; synced?: boolean }>) => {
|
|
|
arr.forEach(img => {
|
|
@@ -353,10 +354,11 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
if (phase === 'white') markSynced(this.whiteModelImages);
|
|
|
if (phase === 'soft') markSynced(this.softDecorImages);
|
|
|
if (phase === 'render') markSynced(this.renderLargeImages);
|
|
|
+ if (phase === 'postProcess') markSynced(this.postProcessImages);
|
|
|
alert('已同步该阶段的图片信息(模拟)');
|
|
|
}
|
|
|
|
|
|
- reviewImage(imageId: string, phase: 'white' | 'soft' | 'render', status: 'approved' | 'rejected'): void {
|
|
|
+ reviewImage(imageId: string, phase: 'white' | 'soft' | 'render' | 'postProcess', status: 'approved' | 'rejected'): void {
|
|
|
if (!this.isTeamLeaderView()) return;
|
|
|
const setStatus = (arr: Array<{ id: string; reviewStatus?: 'pending'|'approved'|'rejected'; synced?: boolean }>) => {
|
|
|
const target = arr.find(i => i.id === imageId);
|
|
@@ -368,6 +370,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
if (phase === 'white') setStatus(this.whiteModelImages);
|
|
|
if (phase === 'soft') setStatus(this.softDecorImages);
|
|
|
if (phase === 'render') setStatus(this.renderLargeImages);
|
|
|
+ if (phase === 'postProcess') setStatus(this.postProcessImages);
|
|
|
}
|
|
|
|
|
|
getImageReviewStatusText(img: { reviewStatus?: 'pending'|'approved'|'rejected'; synced?: boolean }): string {
|
|
@@ -1183,6 +1186,8 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
this.removeSoftDecorImage(this.previewImageData.id);
|
|
|
} else if (this.renderLargeImages.find(i => i.id === this.previewImageData.id)) {
|
|
|
this.removeRenderLargeImage(this.previewImageData.id);
|
|
|
+ } else if (this.postProcessImages.find(i => i.id === this.previewImageData.id)) {
|
|
|
+ this.removePostProcessImage(this.previewImageData.id);
|
|
|
}
|
|
|
this.closeImagePreview();
|
|
|
}
|
|
@@ -1201,7 +1206,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
this.isDragOver = false;
|
|
|
}
|
|
|
|
|
|
- onFileDrop(event: DragEvent, type: 'whiteModel' | 'softDecor' | 'render'): void {
|
|
|
+ onFileDrop(event: DragEvent, type: 'whiteModel' | 'softDecor' | 'render' | 'postProcess'): void {
|
|
|
event.preventDefault();
|
|
|
event.stopPropagation();
|
|
|
this.isDragOver = false;
|
|
@@ -1227,11 +1232,14 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
case 'render':
|
|
|
this.onRenderLargePicsSelected(mockEvent);
|
|
|
break;
|
|
|
+ case 'postProcess':
|
|
|
+ this.onPostProcessPicsSelected(mockEvent);
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 触发文件输入框
|
|
|
- triggerFileInput(type: 'whiteModel' | 'softDecor' | 'render'): void {
|
|
|
+ triggerFileInput(type: 'whiteModel' | 'softDecor' | 'render' | 'postProcess'): void {
|
|
|
let inputId: string;
|
|
|
switch (type) {
|
|
|
case 'whiteModel':
|
|
@@ -1243,6 +1251,9 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
case 'render':
|
|
|
inputId = 'renderFileInput';
|
|
|
break;
|
|
|
+ case 'postProcess':
|
|
|
+ inputId = 'postProcessFileInput';
|
|
|
+ break;
|
|
|
}
|
|
|
const input = document.querySelector(`#${inputId}`) as HTMLInputElement;
|
|
|
if (input) {
|
|
@@ -1256,12 +1267,48 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
this.softDecorImages = this.softDecorImages.filter(i => i.id !== id);
|
|
|
}
|
|
|
|
|
|
+ // 新增:后期阶段图片上传处理
|
|
|
+ async onPostProcessPicsSelected(event: Event): Promise<void> {
|
|
|
+ const input = event.target as HTMLInputElement;
|
|
|
+ if (!input.files || input.files.length === 0) return;
|
|
|
+ const files = Array.from(input.files).filter(f => /\.(jpg|jpeg|png)$/i.test(f.name));
|
|
|
+
|
|
|
+ for (const f of files) {
|
|
|
+ const item = this.makeImageItem(f);
|
|
|
+ this.postProcessImages.unshift({
|
|
|
+ id: item.id,
|
|
|
+ name: item.name,
|
|
|
+ url: item.url,
|
|
|
+ size: this.formatFileSize(f.size)
|
|
|
+ });
|
|
|
+ }
|
|
|
+ input.value = '';
|
|
|
+ }
|
|
|
+
|
|
|
+ removePostProcessImage(id: string): void {
|
|
|
+ const target = this.postProcessImages.find(i => i.id === id);
|
|
|
+ if (target) this.revokeUrl(target.url);
|
|
|
+ this.postProcessImages = this.postProcessImages.filter(i => i.id !== id);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 新增:后期阶段确认上传并自动进入下一阶段(尾款结算)
|
|
|
+ confirmPostProcessUpload(): void {
|
|
|
+ if (this.postProcessImages.length === 0) return;
|
|
|
+ this.advanceToNextStage('后期');
|
|
|
+ }
|
|
|
+
|
|
|
// 新增:软装阶段 确认上传并自动进入下一阶段(渲染)
|
|
|
confirmSoftDecorUpload(): void {
|
|
|
if (this.softDecorImages.length === 0) return;
|
|
|
this.advanceToNextStage('软装');
|
|
|
}
|
|
|
|
|
|
+ // 新增:渲染阶段 确认上传并自动进入下一阶段(后期)
|
|
|
+ confirmRenderUpload(): void {
|
|
|
+ if (this.renderLargeImages.length === 0) return;
|
|
|
+ this.advanceToNextStage('渲染');
|
|
|
+ }
|
|
|
+
|
|
|
// =========== 渲染阶段:大图上传(弹窗 + 4K校验) ===========
|
|
|
openRenderUploadModal(): void {
|
|
|
this.showRenderUploadModal = true;
|
|
@@ -1298,15 +1345,6 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
input.value = '';
|
|
|
}
|
|
|
|
|
|
- confirmRenderUpload(): void {
|
|
|
- // 将待确认的图片加入正式列表(此处模拟上传成功)
|
|
|
- const toAdd = this.pendingRenderLargeItems.map(i => ({ id: i.id, name: i.name, url: i.url, size: this.formatFileSize(i.file.size) }));
|
|
|
- this.renderLargeImages.unshift(...toAdd);
|
|
|
- this.closeRenderUploadModal();
|
|
|
- // 新增:渲染阶段确认后,自动进入下一阶段(后期)
|
|
|
- this.advanceToNextStage('渲染');
|
|
|
- }
|
|
|
-
|
|
|
removeRenderLargeImage(id: string): void {
|
|
|
const target = this.renderLargeImages.find(i => i.id === id);
|
|
|
if (target) this.revokeUrl(target.url);
|