|
@@ -616,6 +616,9 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
// 重置方案分析状态,确保需求信息展示区域能够显示
|
|
|
this.resetProposalAnalysis();
|
|
|
|
|
|
+ // 初始化售后模块示例数据
|
|
|
+ this.initializeAftercareData();
|
|
|
+
|
|
|
this.route.paramMap.subscribe(params => {
|
|
|
this.projectId = params.get('id') || '';
|
|
|
// 根据当前URL检测视图上下文
|
|
@@ -1953,17 +1956,8 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
}, 1500);
|
|
|
}
|
|
|
|
|
|
- // 新增:客户评价阶段确认并自动进入下一阶段(投诉处理)
|
|
|
- confirmCustomerReview(): void {
|
|
|
- console.log('确认客户评价完成');
|
|
|
- // 可以在这里添加更多逻辑,比如更新项目状态等
|
|
|
- // 调用服务更新后端数据
|
|
|
- // this.projectService.confirmCustomerReview(this.projectId);
|
|
|
- this.advanceToNextStage('客户评价');
|
|
|
- }
|
|
|
-
|
|
|
- // 新增:投诉处理阶段确认并完成项目
|
|
|
- confirmComplaint(): void {
|
|
|
+ // 新增:投诉处理阶段确认并完成项目(基础版本,详细版本在售后模块中)
|
|
|
+ confirmComplaintBasic(): void {
|
|
|
console.log('确认投诉处理完成');
|
|
|
// 可以在这里添加更多逻辑,比如标记项目完成等
|
|
|
// 调用服务更新后端数据
|
|
@@ -3058,36 +3052,6 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
window.open(url, '_blank');
|
|
|
}
|
|
|
|
|
|
- // 新增:上传支付凭证功能
|
|
|
- uploadPaymentProof(): void {
|
|
|
- const input = document.createElement('input');
|
|
|
- input.type = 'file';
|
|
|
- input.accept = 'image/*,.pdf';
|
|
|
- input.multiple = false;
|
|
|
-
|
|
|
- input.onchange = (event: any) => {
|
|
|
- const file = event.target.files[0];
|
|
|
- if (file) {
|
|
|
- // 验证文件大小(限制为5MB)
|
|
|
- if (file.size > 5 * 1024 * 1024) {
|
|
|
- alert('文件大小不能超过5MB');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 验证文件类型
|
|
|
- const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
|
|
|
- if (!allowedTypes.includes(file.type)) {
|
|
|
- alert('只支持图片文件(JPG、PNG、GIF)和PDF文件');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 模拟上传过程
|
|
|
- this.handlePaymentProofUpload(file);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- input.click();
|
|
|
- }
|
|
|
|
|
|
// 切换客户信息卡片展开状态
|
|
|
toggleCustomerInfo(): void {
|
|
@@ -3764,16 +3728,35 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
|
|
|
// ==================== 自动结算相关 ====================
|
|
|
|
|
|
- // 启动自动结算
|
|
|
+ // 检查项目是否已完成验收
|
|
|
+ isProjectAccepted(): boolean {
|
|
|
+ // 检查所有交付阶段是否完成
|
|
|
+ const deliveryStages: ProjectStage[] = ['建模', '软装', '渲染', '后期'];
|
|
|
+ return deliveryStages.every(stage => this.getStageStatus(stage) === 'completed');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 启动自动结算(只有技术人员可触发)
|
|
|
initiateAutoSettlement(): void {
|
|
|
if (this.isAutoSettling) return;
|
|
|
|
|
|
+ // 权限检查
|
|
|
+ if (!this.isTechnicalView()) {
|
|
|
+ alert('⚠️ 仅技术人员可以启动自动化结算流程');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验收状态检查
|
|
|
+ if (!this.isProjectAccepted()) {
|
|
|
+ const confirmStart = confirm('项目尚未完成全部验收,确定要启动结算流程吗?');
|
|
|
+ if (!confirmStart) return;
|
|
|
+ }
|
|
|
+
|
|
|
this.isAutoSettling = true;
|
|
|
console.log('启动自动化结算流程...');
|
|
|
|
|
|
// 模拟启动各个自动化功能
|
|
|
setTimeout(() => {
|
|
|
- // 启动小程序支付监听
|
|
|
+ // 1. 启动小程序支付监听
|
|
|
this.miniprogramPaymentStatus = 'active';
|
|
|
this.isSettlementInitiated = true;
|
|
|
|
|
@@ -3782,9 +3765,15 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
console.log('🔍 支付凭证智能识别已就绪');
|
|
|
console.log('📱 自动通知系统已就绪');
|
|
|
|
|
|
- // 技术人员确认项目完成后,自动通知客服跟进尾款
|
|
|
+ // 2. 自动生成尾款结算记录
|
|
|
+ this.createFinalPaymentRecord();
|
|
|
+
|
|
|
+ // 3. 通知客服跟进尾款
|
|
|
this.notifyCustomerServiceForFinalPayment();
|
|
|
|
|
|
+ // 4. 设置支付监听和自动化流程
|
|
|
+ this.setupPaymentAutomation();
|
|
|
+
|
|
|
this.isAutoSettling = false;
|
|
|
|
|
|
// 显示启动成功消息
|
|
@@ -3802,7 +3791,33 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
}, 2000);
|
|
|
}
|
|
|
|
|
|
- // 新增:通知客服跟进尾款的方法
|
|
|
+ // 创建尾款结算记录
|
|
|
+ private createFinalPaymentRecord(): void {
|
|
|
+ const finalPaymentRecord: Settlement = {
|
|
|
+ id: `settlement_${Date.now()}`,
|
|
|
+ projectId: this.projectId,
|
|
|
+ projectName: this.project?.name || '',
|
|
|
+ type: 'final_payment',
|
|
|
+ amount: this.project?.finalPaymentAmount || 0,
|
|
|
+ percentage: 30,
|
|
|
+ status: 'pending',
|
|
|
+ createdAt: new Date(),
|
|
|
+ initiatedBy: 'technical',
|
|
|
+ initiatedAt: new Date(),
|
|
|
+ notifiedCustomerService: false,
|
|
|
+ paymentReceived: false,
|
|
|
+ imagesUnlocked: false
|
|
|
+ };
|
|
|
+
|
|
|
+ // 添加到结算列表
|
|
|
+ if (!this.settlements.find(s => s.type === 'final_payment')) {
|
|
|
+ this.settlements.unshift(finalPaymentRecord);
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('📝 已创建尾款结算记录:', finalPaymentRecord);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通知客服跟进尾款
|
|
|
private notifyCustomerServiceForFinalPayment(): void {
|
|
|
const projectInfo = {
|
|
|
projectId: this.projectId,
|
|
@@ -3811,28 +3826,217 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
customerPhone: this.project?.customerPhone || '',
|
|
|
finalPaymentAmount: this.project?.finalPaymentAmount || 0,
|
|
|
notificationTime: new Date(),
|
|
|
- status: 'pending_followup'
|
|
|
+ status: 'pending_followup',
|
|
|
+ priority: 'high',
|
|
|
+ autoGenerated: true,
|
|
|
+ message: `项目【${this.project?.name}】已完成技术验收,请及时跟进客户尾款支付。`
|
|
|
};
|
|
|
|
|
|
- // 模拟发送通知到客服系统
|
|
|
+ // 发送通知到客服系统
|
|
|
console.log('📢 正在通知客服跟进尾款...', projectInfo);
|
|
|
|
|
|
- // 这里应该调用实际的API来通知客服系统
|
|
|
- // 例如:this.customerServiceNotificationService.addPendingFinalPaymentProject(projectInfo);
|
|
|
+ // 模拟API调用到客服通知系统
|
|
|
+ // this.customerServiceAPI.addPendingTask(projectInfo).subscribe(...)
|
|
|
|
|
|
- // 模拟API调用
|
|
|
setTimeout(() => {
|
|
|
console.log('✅ 客服通知已发送成功');
|
|
|
+
|
|
|
+ // 更新结算记录状态
|
|
|
+ const settlement = this.settlements.find(s => s.type === 'final_payment');
|
|
|
+ if (settlement) {
|
|
|
+ settlement.notifiedCustomerService = true;
|
|
|
+ }
|
|
|
}, 500);
|
|
|
}
|
|
|
+
|
|
|
+ // 设置支付自动化流程
|
|
|
+ private setupPaymentAutomation(): void {
|
|
|
+ console.log('⚙️ 设置支付自动化监听...');
|
|
|
+
|
|
|
+ // 模拟支付监听(实际应使用WebSocket或轮询)
|
|
|
+ // 这里仅作演示
|
|
|
+ this.monitorPaymentStatus();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 监听支付状态
|
|
|
+ private monitorPaymentStatus(): void {
|
|
|
+ // 实际应该使用WebSocket连接或定时轮询API
|
|
|
+ // 这里仅作演示用setTimeout模拟
|
|
|
+ console.log('👀 开始监听支付状态...');
|
|
|
+
|
|
|
+ // 当检测到支付完成时,自动触发后续流程
|
|
|
+ // this.onPaymentReceived();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 支付到账回调
|
|
|
+ onPaymentReceived(paymentInfo?: any): void {
|
|
|
+ console.log('💰 检测到支付到账:', paymentInfo);
|
|
|
+
|
|
|
+ const settlement = this.settlements.find(s => s.type === 'final_payment');
|
|
|
+ if (!settlement) return;
|
|
|
+
|
|
|
+ // 更新结算状态
|
|
|
+ settlement.status = '已结算';
|
|
|
+ settlement.settledAt = new Date();
|
|
|
+ settlement.paymentReceived = true;
|
|
|
+ settlement.paymentReceivedAt = new Date();
|
|
|
+
|
|
|
+ // 自动解锁并发送大图
|
|
|
+ this.autoUnlockAndSendImages();
|
|
|
+
|
|
|
+ // 通知客户和客服
|
|
|
+ this.sendPaymentConfirmationNotifications();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 自动解锁并发送大图
|
|
|
+ private autoUnlockAndSendImages(): void {
|
|
|
+ console.log('🔓 自动解锁大图...');
|
|
|
+
|
|
|
+ // 更新结算记录
|
|
|
+ const settlement = this.settlements.find(s => s.type === 'final_payment');
|
|
|
+ if (settlement) {
|
|
|
+ settlement.imagesUnlocked = true;
|
|
|
+ settlement.imagesUnlockedAt = new Date();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 自动发送大图给客户(通过客服)
|
|
|
+ this.autoSendImagesToCustomer();
|
|
|
+
|
|
|
+ console.log('✅ 大图已解锁并准备发送');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 自动发送图片给客户
|
|
|
+ private autoSendImagesToCustomer(): void {
|
|
|
+ console.log('📤 自动发送大图给客户...');
|
|
|
+
|
|
|
+ // 收集所有渲染大图
|
|
|
+ const renderProcess = this.deliveryProcesses.find(p => p.id === 'rendering');
|
|
|
+ const images: string[] = [];
|
|
|
+
|
|
|
+ if (renderProcess) {
|
|
|
+ Object.keys(renderProcess.content).forEach(spaceId => {
|
|
|
+ const content = renderProcess.content[spaceId];
|
|
|
+ if (content.images) {
|
|
|
+ content.images.forEach(img => {
|
|
|
+ images.push(img.url);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ const sendInfo = {
|
|
|
+ projectId: this.projectId,
|
|
|
+ customerName: this.project?.customerName || '',
|
|
|
+ images: images,
|
|
|
+ sendMethod: 'wechat',
|
|
|
+ autoGenerated: true
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log('📨 准备发送的大图信息:', sendInfo);
|
|
|
+
|
|
|
+ // 调用客服系统API一键发图
|
|
|
+ // this.customerServiceAPI.sendImagesToCustomer(sendInfo).subscribe(...)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 发送支付确认通知
|
|
|
+ private sendPaymentConfirmationNotifications(): void {
|
|
|
+ console.log('📱 发送支付确认通知...');
|
|
|
+
|
|
|
+ // 通知客户
|
|
|
+ const customerMessage = `尊敬的${this.project?.customerName || '客户'},您的尾款支付已确认,大图已自动解锁并发送,请查收。感谢您的信任!`;
|
|
|
+
|
|
|
+ // 通知客服
|
|
|
+ const csMessage = `项目【${this.project?.name}】尾款已到账,大图已自动解锁,请一键发送给客户。`;
|
|
|
+
|
|
|
+ console.log('📧 客户通知:', customerMessage);
|
|
|
+ console.log('📧 客服通知:', csMessage);
|
|
|
+
|
|
|
+ // 实际发送通知
|
|
|
+ // this.notificationService.send({ to: 'customer', message: customerMessage });
|
|
|
+ // this.notificationService.send({ to: 'customer_service', message: csMessage });
|
|
|
+ }
|
|
|
|
|
|
// ==================== 全景图合成相关 ====================
|
|
|
|
|
|
// 全景图合成数据
|
|
|
panoramicSyntheses: PanoramicSynthesis[] = [];
|
|
|
+ isUploadingPanoramicImages: boolean = false;
|
|
|
+ panoramicUploadProgress: number = 0;
|
|
|
|
|
|
- // 启动全景图合成
|
|
|
+ // 启动全景图合成流程
|
|
|
+ // 上传支付凭证
|
|
|
+ uploadPaymentProof(): void {
|
|
|
+ console.log('📎 打开支付凭证上传...');
|
|
|
+
|
|
|
+ const fileInput = document.createElement('input');
|
|
|
+ fileInput.type = 'file';
|
|
|
+ fileInput.accept = 'image/*';
|
|
|
+ fileInput.onchange = (event: any) => {
|
|
|
+ const file = (event.target as HTMLInputElement).files?.[0];
|
|
|
+ if (!file) return;
|
|
|
+
|
|
|
+ console.log('📄 上传的凭证文件:', file.name);
|
|
|
+ alert(`📎 支付凭证已上传:${file.name}\n\n系统将自动识别支付金额和支付方式。`);
|
|
|
+
|
|
|
+ // 模拟凭证识别和处理
|
|
|
+ setTimeout(() => {
|
|
|
+ const mockPaymentInfo = {
|
|
|
+ amount: this.project?.finalPaymentAmount || 5000,
|
|
|
+ method: '微信支付',
|
|
|
+ imageUrl: URL.createObjectURL(file),
|
|
|
+ uploadTime: new Date()
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log('✅ 支付凭证识别完成:', mockPaymentInfo);
|
|
|
+ this.onPaymentReceived(mockPaymentInfo);
|
|
|
+ }, 1500);
|
|
|
+ };
|
|
|
+ fileInput.click();
|
|
|
+ }
|
|
|
+
|
|
|
startPanoramicSynthesis(): void {
|
|
|
+ console.log('🎨 启动全景图合成...');
|
|
|
+
|
|
|
+ // 打开文件选择对话框,支持多文件选择
|
|
|
+ const fileInput = document.createElement('input');
|
|
|
+ fileInput.type = 'file';
|
|
|
+ fileInput.accept = 'image/*';
|
|
|
+ fileInput.multiple = true;
|
|
|
+ fileInput.onchange = (event: any) => {
|
|
|
+ const files = Array.from(event.target.files || []) as File[];
|
|
|
+ if (files.length === 0) return;
|
|
|
+
|
|
|
+ console.log(`📸 选择了 ${files.length} 张图片进行合成`);
|
|
|
+ this.processPanoramicImages(files);
|
|
|
+ };
|
|
|
+ fileInput.click();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理全景图片上传和合成
|
|
|
+ private processPanoramicImages(files: File[]): void {
|
|
|
+ this.isUploadingPanoramicImages = true;
|
|
|
+ this.panoramicUploadProgress = 0;
|
|
|
+
|
|
|
+ console.log(`📸 开始处理 ${files.length} 张全景图片...`);
|
|
|
+
|
|
|
+ // 模拟上传进度
|
|
|
+ const uploadInterval = setInterval(() => {
|
|
|
+ this.panoramicUploadProgress += 10;
|
|
|
+ if (this.panoramicUploadProgress >= 100) {
|
|
|
+ clearInterval(uploadInterval);
|
|
|
+ this.panoramicUploadProgress = 100;
|
|
|
+
|
|
|
+ // 上传完成后开始合成
|
|
|
+ this.synthesizePanoramicView(files);
|
|
|
+ }
|
|
|
+ }, 300);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 合成全景漫游
|
|
|
+ private synthesizePanoramicView(files: File[]): void {
|
|
|
+ console.log('🔄 开始合成全景漫游...');
|
|
|
+
|
|
|
+ // 创建合成记录
|
|
|
const synthesis: PanoramicSynthesis = {
|
|
|
id: 'panoramic-' + Date.now(),
|
|
|
projectId: this.projectId,
|
|
@@ -3841,37 +4045,133 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
status: 'processing',
|
|
|
quality: 'high',
|
|
|
createdAt: new Date(),
|
|
|
- updatedAt: new Date()
|
|
|
+ updatedAt: new Date(),
|
|
|
+ progress: 0
|
|
|
};
|
|
|
|
|
|
- this.panoramicSyntheses.push(synthesis);
|
|
|
- console.log('启动全景图合成:', synthesis);
|
|
|
+ // 根据上传的文件创建空间列表
|
|
|
+ files.forEach((file, index) => {
|
|
|
+ // 从文件名提取空间名称(如"客厅-角度1.jpg")
|
|
|
+ const fileName = file.name.replace(/\.(jpg|jpeg|png|gif)$/i, '');
|
|
|
+ const match = fileName.match(/^(.+?)-/);
|
|
|
+ const spaceName = match ? match[1] : `空间${index + 1}`;
|
|
|
+
|
|
|
+ // 根据空间名称推断类型
|
|
|
+ let spaceType: 'living_room' | 'bedroom' | 'kitchen' | 'bathroom' | 'dining_room' | 'study' | 'balcony' = 'living_room';
|
|
|
+ if (spaceName.includes('客厅')) spaceType = 'living_room';
|
|
|
+ else if (spaceName.includes('卧室')) spaceType = 'bedroom';
|
|
|
+ else if (spaceName.includes('厨房')) spaceType = 'kitchen';
|
|
|
+ else if (spaceName.includes('卫生间') || spaceName.includes('浴室')) spaceType = 'bathroom';
|
|
|
+ else if (spaceName.includes('餐厅')) spaceType = 'dining_room';
|
|
|
+ else if (spaceName.includes('书房')) spaceType = 'study';
|
|
|
+ else if (spaceName.includes('阳台')) spaceType = 'balcony';
|
|
|
+
|
|
|
+ synthesis.spaces.push({
|
|
|
+ id: `space_${Date.now()}_${index}`,
|
|
|
+ name: spaceName,
|
|
|
+ type: spaceType,
|
|
|
+ imageCount: 1,
|
|
|
+ viewAngle: fileName
|
|
|
+ });
|
|
|
+ });
|
|
|
|
|
|
- // 模拟合成进度
|
|
|
- const progressInterval = setInterval(() => {
|
|
|
- const currentSynthesis = this.panoramicSyntheses.find(s => s.id === synthesis.id);
|
|
|
- if (currentSynthesis) {
|
|
|
- // 模拟进度更新
|
|
|
- if (currentSynthesis.status === 'processing') {
|
|
|
- currentSynthesis.status = 'completed';
|
|
|
- currentSynthesis.completedAt = new Date();
|
|
|
- currentSynthesis.updatedAt = new Date();
|
|
|
- currentSynthesis.previewUrl = 'https://via.placeholder.com/1920x1080';
|
|
|
- currentSynthesis.downloadUrl = 'https://via.placeholder.com/1920x1080';
|
|
|
- currentSynthesis.renderTime = 120;
|
|
|
- currentSynthesis.fileSize = 1024 * 1024 * 50; // 50MB
|
|
|
- clearInterval(progressInterval);
|
|
|
- console.log('全景图合成完成:', currentSynthesis);
|
|
|
- }
|
|
|
+ this.panoramicSyntheses.unshift(synthesis);
|
|
|
+
|
|
|
+ // 模拟KR Panel合成进度
|
|
|
+ let progress = 0;
|
|
|
+ const synthesisInterval = setInterval(() => {
|
|
|
+ progress += 15;
|
|
|
+ synthesis.progress = Math.min(progress, 100);
|
|
|
+ synthesis.updatedAt = new Date();
|
|
|
+
|
|
|
+ if (progress >= 100) {
|
|
|
+ clearInterval(synthesisInterval);
|
|
|
+
|
|
|
+ // 合成完成
|
|
|
+ synthesis.status = 'completed';
|
|
|
+ synthesis.completedAt = new Date();
|
|
|
+ synthesis.previewUrl = this.generateMockPanoramicUrl(synthesis.id);
|
|
|
+ synthesis.downloadUrl = this.generateMockDownloadUrl(synthesis.id);
|
|
|
+ synthesis.renderTime = 120 + Math.floor(Math.random() * 60);
|
|
|
+ synthesis.fileSize = files.reduce((sum, f) => sum + f.size, 0);
|
|
|
+
|
|
|
+ this.isUploadingPanoramicImages = false;
|
|
|
+
|
|
|
+ console.log('✅ 全景图合成完成:', synthesis);
|
|
|
+
|
|
|
+ // 自动生成分享链接
|
|
|
+ this.generatePanoramicShareLink(synthesis);
|
|
|
}
|
|
|
- }, 2000);
|
|
|
+ }, 500);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成全景图分享链接
|
|
|
+ private generatePanoramicShareLink(synthesis: PanoramicSynthesis): void {
|
|
|
+ const shareLink = `https://panoramic.example.com/view/${synthesis.id}`;
|
|
|
+ synthesis.shareLink = shareLink;
|
|
|
+
|
|
|
+ console.log('🔗 全景图分享链接:', shareLink);
|
|
|
+
|
|
|
+ // 自动通知客服发送给客户
|
|
|
+ this.notifyCustomerServiceForPanoramicLink(synthesis);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通知客服发送全景图链接
|
|
|
+ private notifyCustomerServiceForPanoramicLink(synthesis: PanoramicSynthesis): void {
|
|
|
+ const notification = {
|
|
|
+ type: 'panoramic_ready',
|
|
|
+ projectId: this.projectId,
|
|
|
+ projectName: synthesis.projectName,
|
|
|
+ shareLink: synthesis.shareLink,
|
|
|
+ message: `项目【${synthesis.projectName}】的全景漫游已生成完成,请发送给客户查看。`,
|
|
|
+ timestamp: new Date()
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log('📢 通知客服发送全景图链接:', notification);
|
|
|
+
|
|
|
+ // 调用客服通知API
|
|
|
+ // this.customerServiceAPI.notifyPanoramicReady(notification).subscribe(...)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成模拟全景图URL
|
|
|
+ private generateMockPanoramicUrl(id: string): string {
|
|
|
+ return `https://panoramic.example.com/preview/${id}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成模拟下载URL
|
|
|
+ private generateMockDownloadUrl(id: string): string {
|
|
|
+ return `https://panoramic.example.com/download/${id}`;
|
|
|
}
|
|
|
|
|
|
// 查看全景图画廊
|
|
|
viewPanoramicGallery(): void {
|
|
|
console.log('打开全景图画廊');
|
|
|
- // 这里可以打开一个模态框或导航到画廊页面
|
|
|
- alert('全景图画廊功能开发中...');
|
|
|
+
|
|
|
+ if (this.panoramicSyntheses.length === 0) {
|
|
|
+ alert('暂无全景图记录');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 显示全景图列表
|
|
|
+ const galleryInfo = this.panoramicSyntheses.map((s, i) =>
|
|
|
+ `${i + 1}. ${s.projectName} - ${s.status === 'completed' ? '已完成' : '处理中'} (${s.spaces.length}个空间)`
|
|
|
+ ).join('\n');
|
|
|
+
|
|
|
+ alert(`全景图画廊\n\n${galleryInfo}\n\n点击查看详情功能开发中...`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 复制全景图链接
|
|
|
+ copyPanoramicLink(synthesis: PanoramicSynthesis): void {
|
|
|
+ if (!synthesis.shareLink) {
|
|
|
+ alert('全景图链接尚未生成');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ navigator.clipboard.writeText(synthesis.shareLink).then(() => {
|
|
|
+ alert(`✅ 全景图链接已复制!\n\n${synthesis.shareLink}`);
|
|
|
+ }).catch(() => {
|
|
|
+ alert(`全景图链接:\n${synthesis.shareLink}`);
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
// ==================== 评价统计相关 ====================
|
|
@@ -3893,29 +4193,285 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
|
|
|
// 生成评价链接
|
|
|
generateReviewLink(): void {
|
|
|
- console.log('生成客户评价链接');
|
|
|
- const reviewLink = `https://review.example.com/project/${this.projectId}`;
|
|
|
+ console.log('📋 生成客户评价链接...');
|
|
|
+
|
|
|
+ // 生成唯一的评价令牌
|
|
|
+ const reviewToken = this.generateReviewToken();
|
|
|
+ const reviewLink = `https://review.yss.com/project/${this.projectId}?token=${reviewToken}`;
|
|
|
+
|
|
|
+ // 保存评价链接记录
|
|
|
+ const reviewRecord = {
|
|
|
+ projectId: this.projectId,
|
|
|
+ projectName: this.project?.name || '',
|
|
|
+ customerName: this.project?.customerName || '',
|
|
|
+ reviewLink: reviewLink,
|
|
|
+ token: reviewToken,
|
|
|
+ createdAt: new Date(),
|
|
|
+ expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30天有效期
|
|
|
+ status: 'active',
|
|
|
+ accessed: false
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log('✅ 评价链接已生成:', reviewRecord);
|
|
|
+
|
|
|
+ // 复制到剪贴板
|
|
|
navigator.clipboard.writeText(reviewLink).then(() => {
|
|
|
- alert('评价链接已复制到剪贴板!');
|
|
|
+ alert(`✅ 评价链接已复制到剪贴板!\n\n链接:${reviewLink}\n\n有效期:30天\n\n请通过企业微信发送给客户`);
|
|
|
}).catch(() => {
|
|
|
- alert(`评价链接:${reviewLink}`);
|
|
|
+ alert(`评价链接:\n\n${reviewLink}\n\n有效期:30天\n\n请通过企业微信发送给客户`);
|
|
|
});
|
|
|
+
|
|
|
+ // 通知客服发送评价链接
|
|
|
+ this.notifyCustomerServiceForReviewLink(reviewRecord);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成评价令牌
|
|
|
+ private generateReviewToken(): string {
|
|
|
+ return `review_${this.projectId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通知客服发送评价链接
|
|
|
+ private notifyCustomerServiceForReviewLink(reviewRecord: any): void {
|
|
|
+ const notification = {
|
|
|
+ type: 'review_link_ready',
|
|
|
+ projectId: this.projectId,
|
|
|
+ projectName: reviewRecord.projectName,
|
|
|
+ customerName: reviewRecord.customerName,
|
|
|
+ reviewLink: reviewRecord.reviewLink,
|
|
|
+ message: `项目【${reviewRecord.projectName}】的客户评价链接已生成,请发送给客户。`,
|
|
|
+ timestamp: new Date()
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log('📢 通知客服发送评价链接:', notification);
|
|
|
+
|
|
|
+ // 调用客服通知API
|
|
|
+ // this.customerServiceAPI.notifyReviewLinkReady(notification).subscribe(...)
|
|
|
+ }
|
|
|
+ // 确认客户评价完成
|
|
|
+ confirmCustomerReview(): void {
|
|
|
+ console.log('✅ 确认客户评价完成');
|
|
|
+
|
|
|
+ // 更新项目状态
|
|
|
+ if (this.project) {
|
|
|
+ this.project.customerReviewCompleted = true;
|
|
|
+ this.project.customerReviewCompletedAt = new Date();
|
|
|
+ }
|
|
|
+
|
|
|
+ alert('✅ 客户评价已确认完成!');
|
|
|
+
|
|
|
+ // 可选:自动进入下一阶段
|
|
|
+ // this.advanceToNextStage('客户评价');
|
|
|
}
|
|
|
|
|
|
// ==================== 投诉管理相关 ====================
|
|
|
|
|
|
+ // 关键词监控配置
|
|
|
+ complaintKeywords: string[] = ['不满意', '投诉', '退款', '差评', '质量问题', '延期', '态度差'];
|
|
|
+ isKeywordMonitoringActive: boolean = false;
|
|
|
+
|
|
|
// 手动创建投诉
|
|
|
createComplaintManually(): void {
|
|
|
- console.log('手动创建投诉');
|
|
|
- // 这里可以打开一个模态框来创建投诉
|
|
|
- alert('手动创建投诉功能开发中...');
|
|
|
+ console.log('📝 手动创建投诉');
|
|
|
+
|
|
|
+ // 弹出创建投诉表单
|
|
|
+ const complaintReason = prompt('请输入投诉原因:');
|
|
|
+ if (!complaintReason || complaintReason.trim() === '') return;
|
|
|
+
|
|
|
+ const complaintStage = prompt('请输入投诉环节(如:需求沟通、建模、渲染等):') || '未指定';
|
|
|
+
|
|
|
+ // 创建投诉记录
|
|
|
+ const complaint: any = {
|
|
|
+ id: `complaint_${Date.now()}`,
|
|
|
+ projectId: this.projectId,
|
|
|
+ projectName: this.project?.name || '',
|
|
|
+ customerName: this.project?.customerName || '',
|
|
|
+ type: '人工创建',
|
|
|
+ stage: complaintStage,
|
|
|
+ reason: complaintReason,
|
|
|
+ severity: 'medium',
|
|
|
+ status: 'pending',
|
|
|
+ createdBy: 'manual',
|
|
|
+ createdAt: new Date(),
|
|
|
+ handler: '',
|
|
|
+ resolution: '',
|
|
|
+ resolvedAt: null
|
|
|
+ };
|
|
|
+
|
|
|
+ // 智能标注核心问题
|
|
|
+ complaint.tags = this.analyzeComplaintTags(complaintReason);
|
|
|
+
|
|
|
+ // 添加到投诉列表
|
|
|
+ this.exceptionHistories.unshift(complaint);
|
|
|
+
|
|
|
+ console.log('✅ 投诉记录已创建:', complaint);
|
|
|
+
|
|
|
+ alert(`✅ 投诉记录已创建!\n\n投诉环节:${complaintStage}\n核心问题:${complaint.tags.join('、')}\n\n系统将自动跟踪处理进度。`);
|
|
|
+
|
|
|
+ // 通知相关人员
|
|
|
+ this.notifyComplaintHandlers(complaint);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 分析投诉标签
|
|
|
+ private analyzeComplaintTags(reason: string): string[] {
|
|
|
+ const tags: string[] = [];
|
|
|
+
|
|
|
+ const tagPatterns = {
|
|
|
+ '需求理解': ['需求', '理解', '沟通', '误解'],
|
|
|
+ '设计质量': ['质量', '效果', '不好', '不满意'],
|
|
|
+ '交付延期': ['延期', '超时', '慢', '着急'],
|
|
|
+ '服务态度': ['态度', '不礼貌', '敷衍', '回复慢'],
|
|
|
+ '价格问题': ['价格', '费用', '贵', '退款']
|
|
|
+ };
|
|
|
+
|
|
|
+ Object.entries(tagPatterns).forEach(([tag, keywords]) => {
|
|
|
+ if (keywords.some(keyword => reason.includes(keyword))) {
|
|
|
+ tags.push(tag);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return tags.length > 0 ? tags : ['其他'];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通知投诉处理人员
|
|
|
+ private notifyComplaintHandlers(complaint: any): void {
|
|
|
+ const notification = {
|
|
|
+ type: 'new_complaint',
|
|
|
+ projectId: this.projectId,
|
|
|
+ complaintId: complaint.id,
|
|
|
+ projectName: complaint.projectName,
|
|
|
+ customerName: complaint.customerName,
|
|
|
+ severity: complaint.severity,
|
|
|
+ tags: complaint.tags,
|
|
|
+ message: `项目【${complaint.projectName}】收到新投诉,请及时处理。`,
|
|
|
+ timestamp: new Date()
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log('📢 通知投诉处理人员:', notification);
|
|
|
+
|
|
|
+ // 调用通知API
|
|
|
+ // this.complaintService.notifyHandlers(notification).subscribe(...)
|
|
|
}
|
|
|
|
|
|
// 设置关键词监控
|
|
|
setupKeywordMonitoring(): void {
|
|
|
- console.log('设置关键词监控');
|
|
|
- // 这里可以打开关键词监控设置界面
|
|
|
- alert('关键词监控设置功能开发中...');
|
|
|
+ console.log('⚙️ 设置关键词监控');
|
|
|
+
|
|
|
+ if (this.isKeywordMonitoringActive) {
|
|
|
+ const confirmStop = confirm('关键词监控已激活,是否停止监控?');
|
|
|
+ if (confirmStop) {
|
|
|
+ this.isKeywordMonitoringActive = false;
|
|
|
+ alert('✅ 关键词监控已停止');
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 显示当前监控关键词
|
|
|
+ const currentKeywords = this.complaintKeywords.join('、');
|
|
|
+ const newKeywords = prompt(`当前监控关键词:\n\n${currentKeywords}\n\n请输入要添加的关键词(多个关键词用逗号分隔):`);
|
|
|
+
|
|
|
+ if (newKeywords && newKeywords.trim()) {
|
|
|
+ const keywords = newKeywords.split(/[,,、]/).map(k => k.trim()).filter(k => k);
|
|
|
+ this.complaintKeywords = [...new Set([...this.complaintKeywords, ...keywords])];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 激活监控
|
|
|
+ this.isKeywordMonitoringActive = true;
|
|
|
+ this.startKeywordMonitoring();
|
|
|
+
|
|
|
+ alert(`✅ 关键词监控已激活!\n\n监控关键词:${this.complaintKeywords.join('、')}\n\n系统将自动检测企业微信群消息中的关键词并创建投诉记录。`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 开始关键词监控
|
|
|
+ private startKeywordMonitoring(): void {
|
|
|
+ console.log('👀 开始关键词监控...');
|
|
|
+ console.log('监控关键词:', this.complaintKeywords);
|
|
|
+
|
|
|
+ // 模拟监控企业微信消息(实际应使用企业微信API或webhook)
|
|
|
+ // 这里仅作演示
|
|
|
+
|
|
|
+ // 监控到关键词后自动创建投诉
|
|
|
+ // this.onKeywordDetected(message, keyword);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关键词检测回调
|
|
|
+ onKeywordDetected(message: string, keyword: string): void {
|
|
|
+ console.log('🚨 检测到关键词:', keyword);
|
|
|
+ console.log('消息内容:', message);
|
|
|
+
|
|
|
+ // 自动创建投诉记录
|
|
|
+ const complaint: any = {
|
|
|
+ id: `complaint_auto_${Date.now()}`,
|
|
|
+ projectId: this.projectId,
|
|
|
+ projectName: this.project?.name || '',
|
|
|
+ customerName: this.project?.customerName || '',
|
|
|
+ type: '关键词自动抓取',
|
|
|
+ keyword: keyword,
|
|
|
+ message: message,
|
|
|
+ severity: this.assessComplaintSeverity(message),
|
|
|
+ status: 'pending',
|
|
|
+ createdBy: 'auto',
|
|
|
+ createdAt: new Date(),
|
|
|
+ handler: '',
|
|
|
+ resolution: '',
|
|
|
+ resolvedAt: null
|
|
|
+ };
|
|
|
+
|
|
|
+ // 智能标注问题环节和核心问题
|
|
|
+ complaint.stage = this.identifyComplaintStage(message);
|
|
|
+ complaint.tags = this.analyzeComplaintTags(message);
|
|
|
+
|
|
|
+ // 添加到投诉列表
|
|
|
+ this.exceptionHistories.unshift(complaint);
|
|
|
+
|
|
|
+ console.log('✅ 自动投诉记录已创建:', complaint);
|
|
|
+
|
|
|
+ // 实时通知相关人员
|
|
|
+ this.notifyComplaintHandlers(complaint);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 评估投诉严重程度
|
|
|
+ private assessComplaintSeverity(message: string): 'low' | 'medium' | 'high' {
|
|
|
+ const highSeverityKeywords = ['退款', '投诉', '举报', '律师', '曝光'];
|
|
|
+ const mediumSeverityKeywords = ['不满意', '差评', '质量问题'];
|
|
|
+
|
|
|
+ if (highSeverityKeywords.some(k => message.includes(k))) return 'high';
|
|
|
+ if (mediumSeverityKeywords.some(k => message.includes(k))) return 'medium';
|
|
|
+ return 'low';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 识别投诉环节
|
|
|
+ private identifyComplaintStage(message: string): string {
|
|
|
+ const stageKeywords = {
|
|
|
+ '需求沟通': ['需求', '沟通', '理解'],
|
|
|
+ '方案确认': ['方案', '设计', '效果'],
|
|
|
+ '建模': ['建模', '模型', '白模'],
|
|
|
+ '软装': ['软装', '家具', '配饰'],
|
|
|
+ '渲染': ['渲染', '出图', '大图'],
|
|
|
+ '交付': ['交付', '发送', '收到']
|
|
|
+ };
|
|
|
+
|
|
|
+ for (const [stage, keywords] of Object.entries(stageKeywords)) {
|
|
|
+ if (keywords.some(k => message.includes(k))) {
|
|
|
+ return stage;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return '未指定';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确认投诉处理完成
|
|
|
+ confirmComplaint(): void {
|
|
|
+ console.log('✅ 确认投诉处理完成');
|
|
|
+
|
|
|
+ // 检查是否有未处理的投诉
|
|
|
+ const pendingComplaints = this.exceptionHistories.filter(c => c.status === '待处理');
|
|
|
+
|
|
|
+ if (pendingComplaints.length > 0) {
|
|
|
+ const confirmAnyway = confirm(`还有 ${pendingComplaints.length} 个投诉未处理,确定要标记为已完成吗?`);
|
|
|
+ if (!confirmAnyway) return;
|
|
|
+ }
|
|
|
+
|
|
|
+ alert('✅ 所有投诉已确认处理完成!');
|
|
|
}
|
|
|
|
|
|
// 处理评价表单提交
|
|
@@ -3959,6 +4515,82 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
|
|
|
// ============ 缺少的方法实现 ============
|
|
|
|
|
|
+ // 初始化售后模块数据
|
|
|
+ private initializeAftercareData(): void {
|
|
|
+ // 初始化一些示例全景图合成记录
|
|
|
+ this.panoramicSyntheses = [
|
|
|
+ {
|
|
|
+ id: 'panoramic_001',
|
|
|
+ projectId: this.projectId,
|
|
|
+ projectName: '示例项目',
|
|
|
+ spaces: [
|
|
|
+ { id: 'space_001', name: '客厅', type: 'living_room' as const, imageCount: 3, viewAngle: '客厅-角度1' },
|
|
|
+ { id: 'space_002', name: '卧室', type: 'bedroom' as const, imageCount: 2, viewAngle: '卧室-角度1' }
|
|
|
+ ],
|
|
|
+ status: 'completed' as const,
|
|
|
+ quality: 'high' as const,
|
|
|
+ createdAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000),
|
|
|
+ updatedAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000),
|
|
|
+ completedAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000),
|
|
|
+ previewUrl: 'https://panoramic.example.com/preview/panoramic_001',
|
|
|
+ downloadUrl: 'https://panoramic.example.com/download/panoramic_001',
|
|
|
+ shareLink: 'https://panoramic.example.com/view/panoramic_001',
|
|
|
+ renderTime: 135,
|
|
|
+ fileSize: 52428800,
|
|
|
+ progress: 100
|
|
|
+ }
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 初始化一些示例结算记录
|
|
|
+ if (this.settlements.length === 0) {
|
|
|
+ this.settlements = [
|
|
|
+ {
|
|
|
+ id: 'settlement_001',
|
|
|
+ projectId: this.projectId,
|
|
|
+ projectName: '示例项目',
|
|
|
+ type: 'deposit',
|
|
|
+ amount: 5000,
|
|
|
+ percentage: 30,
|
|
|
+ status: '已结算',
|
|
|
+ createdAt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
|
|
|
+ settledAt: new Date(Date.now() - 28 * 24 * 60 * 60 * 1000)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'settlement_002',
|
|
|
+ projectId: this.projectId,
|
|
|
+ projectName: '示例项目',
|
|
|
+ type: 'progress',
|
|
|
+ amount: 7000,
|
|
|
+ percentage: 40,
|
|
|
+ status: '已结算',
|
|
|
+ createdAt: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000),
|
|
|
+ settledAt: new Date(Date.now() - 13 * 24 * 60 * 60 * 1000)
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化一些示例客户反馈
|
|
|
+ if (this.feedbacks.length === 0) {
|
|
|
+ this.feedbacks = [
|
|
|
+ {
|
|
|
+ id: 'feedback_001',
|
|
|
+ projectId: this.projectId,
|
|
|
+ customerName: '张先生',
|
|
|
+ rating: 5,
|
|
|
+ content: '设计师非常专业,效果图很满意!',
|
|
|
+ isSatisfied: true,
|
|
|
+ status: '已解决',
|
|
|
+ createdAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000)
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化一些示例投诉记录
|
|
|
+ if (this.exceptionHistories.length === 0) {
|
|
|
+ this.exceptionHistories = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 初始化表单
|
|
|
initializeForms(): void {
|
|
|
// 初始化订单创建表单(必填项)
|
|
@@ -4571,4 +5203,18 @@ export class ProjectDetail implements OnInit, OnDestroy {
|
|
|
|
|
|
content.lastUpdated = new Date();
|
|
|
}
|
|
|
+
|
|
|
+ // ==================== 功能卡片点击事件 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 显示功能详情
|
|
|
+ * @param title 功能标题
|
|
|
+ * @param description 功能详细描述
|
|
|
+ */
|
|
|
+ showFeatureDetail(title: string, description: string): void {
|
|
|
+ console.log(`📋 功能详情: ${title}`);
|
|
|
+ console.log(`📝 ${description}`);
|
|
|
+
|
|
|
+ alert(`✨ ${title}\n\n${description}\n\n点击确定关闭`);
|
|
|
+ }
|
|
|
}
|