| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638 |
- import { Component, OnInit, signal } from '@angular/core';
- import { CommonModule } from '@angular/common';
- import { RouterModule, Router } from '@angular/router';
- import { AuthService } from '../../../services/auth.service';
- import { WorkHourService } from '../../../services/work-hour.service';
- import { WorkHourDashboardData, MonthlyWorkHourStats, PerformanceLevel } from '../../../models/work-hour.model';
- // 项目复杂度类型
- interface ProjectComplexity {
- type: string; // '家装小户型' | '家装大户型' | '工装商铺' | '工装写字楼'
- standardHours: {
- requirementDeepening: number;
- modeling: number;
- rendering: number;
- postProduction: number;
- };
- }
- // 逾期项目信息
- interface OverdueProject {
- id: string;
- name: string;
- stage: string;
- overdueDays: number;
- reason: string;
- designerName: string;
- }
- // 设计师效率统计
- interface DesignerEfficiency {
- id: string;
- name: string;
- avgCompletionTime: number; // 天
- overdueProjectCount: number;
- stageUtilization: {
- requirementDeepening: number; // 百分比
- modeling: number;
- rendering: number;
- postProduction: number;
- };
- idleDays: number; // 闲置天数
- suggestion: string;
- }
- @Component({
- selector: 'app-dashboard',
- imports: [CommonModule, RouterModule],
- templateUrl: './dashboard.html',
- styleUrl: './dashboard.scss'
- })
- export class Dashboard implements OnInit {
- // 添加today属性用于模板中的日期显示
- today = new Date();
-
- // 待办任务数据
- todoTasks = signal([
- { id: 1, title: '临时收款待核定', priority: 'high', source: 'reconciliation', dueTime: '10:30' },
- { id: 2, title: '报价待审核', priority: 'medium', source: 'project-records', dueTime: '14:00' },
- { id: 3, title: '素材成本待录入', priority: 'low', source: 'reconciliation', dueTime: '16:30' },
- { id: 4, title: '临时收款待核定', priority: 'high', source: 'reconciliation', dueTime: '11:00' },
- { id: 5, title: '销售监管提醒', priority: 'high', source: 'project-records', dueTime: '09:30' }
- ]);
- // 数据概览数据
- dashboardStats = signal({
- todayOrders: 12,
- pendingPayment: 156800,
- quotedProjects: 28,
- materialCostSaved: 8900
- });
- // 用户角色
- userRole = signal('teamLead'); // teamLead 或 juniorMember
- // 工时统计数据
- workHourDashboard = signal<WorkHourDashboardData | null>(null);
- monthlyStats = signal<MonthlyWorkHourStats[]>([]);
- showWorkHourModule = signal(false);
- // 新增:项目复杂度预设标准工时
- projectComplexities: ProjectComplexity[] = [
- {
- type: '家装小户型',
- standardHours: { requirementDeepening: 16, modeling: 40, rendering: 24, postProduction: 8 }
- },
- {
- type: '家装大户型',
- standardHours: { requirementDeepening: 24, modeling: 64, rendering: 40, postProduction: 16 }
- },
- {
- type: '工装商铺',
- standardHours: { requirementDeepening: 32, modeling: 80, rendering: 48, postProduction: 16 }
- },
- {
- type: '工装写字楼',
- standardHours: { requirementDeepening: 40, modeling: 120, rendering: 72, postProduction: 24 }
- }
- ];
- // 新增:逾期项目列表
- overdueProjects = signal<OverdueProject[]>([
- { id: 'P001', name: '现代简约三居室', stage: '渲染', overdueDays: 2, reason: '客户需求变更', designerName: '张设计' },
- { id: 'P002', name: '商业办公空间', stage: '建模', overdueDays: 5, reason: '资源不足', designerName: '李设计' },
- { id: 'P003', name: '北欧风格两居室', stage: '后期', overdueDays: 1, reason: '客户需求变更', designerName: '王设计' }
- ]);
- // 新增:设计师效率统计
- designerEfficiencies = signal<DesignerEfficiency[]>([
- {
- id: 'D001',
- name: '张设计',
- avgCompletionTime: 18,
- overdueProjectCount: 1,
- stageUtilization: { requirementDeepening: 85, modeling: 92, rendering: 78, postProduction: 88 },
- idleDays: 3,
- suggestion: '表现优秀,可适当增加项目难度'
- },
- {
- id: 'D002',
- name: '李设计',
- avgCompletionTime: 25,
- overdueProjectCount: 2,
- stageUtilization: { requirementDeepening: 70, modeling: 65, rendering: 72, postProduction: 80 },
- idleDays: 8,
- suggestion: '近30天闲置8天,建议分配更多项目'
- },
- {
- id: 'D003',
- name: '王设计',
- avgCompletionTime: 15,
- overdueProjectCount: 0,
- stageUtilization: { requirementDeepening: 95, modeling: 90, rendering: 93, postProduction: 92 },
- idleDays: 1,
- suggestion: '高效能设计师,建议承接重点项目'
- },
- {
- id: 'D004',
- name: '赵设计',
- avgCompletionTime: 22,
- overdueProjectCount: 1,
- stageUtilization: { requirementDeepening: 80, modeling: 75, rendering: 70, postProduction: 85 },
- idleDays: 10,
- suggestion: '需要提升渲染阶段效率,建议参加培训'
- }
- ]);
- // 时间筛选维度
- timeDimension = signal<'week' | 'month' | 'quarter'>('month');
- // 显示悬浮按钮
- showFloatingButton = signal(true);
- // 当前激活的导航项
- activeNavItem = signal<string>('');
- constructor(private authService: AuthService, private workHourService: WorkHourService, private router: Router) {}
- ngOnInit(): void {
- // 初始化用户角色
- this.initializeUserRole();
-
- // 初始化设计师效率数据(使用默认的month维度)
- this.updateDesignerEfficienciesByDimension('month');
-
- // 加载工时统计数据
- this.loadWorkHourData();
- }
-
- // 初始化用户角色
- initializeUserRole(): void {
- // 从AuthService获取用户角色
- const roles = this.authService.getUserRoles();
- // 默认使用teamLead角色
- let userRole = 'teamLead';
-
- // 如果用户有admin角色,也视为teamLead
- if (roles.includes('admin')) {
- userRole = 'teamLead';
- } else if (roles.length > 0) {
- // 否则使用第一个角色
- userRole = roles[0];
- }
-
- this.userRole.set(userRole);
-
- // 根据用户角色过滤待办任务
- this.filterTasksByRole();
- }
-
- // 根据用户角色过滤待办任务
- filterTasksByRole(): void {
- if (this.userRole() !== 'teamLead') {
- // 初级组员只显示非财务审批类任务
- const filteredTasks = this.todoTasks().filter(task =>
- task.title !== '临时收款待核定' && task.title !== '报价待审核'
- );
- this.todoTasks.set(filteredTasks);
- }
- }
-
- // 检查用户角色
- checkUserRole(requiredRole: string): boolean {
- return this.authService.hasRole(requiredRole);
- }
- // 处理待办任务点击
- handleTaskClick(task: any) {
- // 检查财务相关任务的权限
- if ((task.title === '临时收款待核定' || task.title === '报价待审核') && !this.checkUserRole('teamLead')) {
- alert('⚠️ 权限不足\n\n只有组长及以上权限可以处理此任务\n\n请联系管理员申请权限');
- return;
- }
-
- // 显示任务详情
- const taskDetails = `📋 待办任务详情\n\n任务:${task.title}\n优先级:${task.priority === 'high' ? '高' : task.priority === 'medium' ? '中' : '低'}\n截止时间:${task.time}\n\n是否立即处理此任务?`;
-
- if (confirm(taskDetails)) {
- // 根据任务来源跳转到对应页面
- switch(task.source) {
- case 'reconciliation':
- alert('🔄 正在跳转到对账管理页面...');
- // window.location.href = '/finance/reconciliation';
- break;
- case 'project-records':
- alert('🔄 正在跳转到项目记录页面...');
- // window.location.href = '/finance/project-records';
- break;
- default:
- alert(`📌 任务已标记\n\n任务"${task.title}"已加入处理队列`);
- }
- }
- }
- // 处理快捷操作点击
- handleQuickAction(action: string) {
- // 检查权限
- if ((action === 'recordPayment' || action === 'generateReport') && !this.checkUserRole('teamLead')) {
- alert('⚠️ 权限不足\n\n只有组长及以上权限可以执行此操作\n\n请联系管理员申请权限');
- return;
- }
-
- // 设置当前激活的导航项
- this.activeNavItem.set(action);
-
- switch(action) {
- case 'newQuote':
- this.showNewQuoteDialog();
- break;
- case 'recordPayment':
- this.showRecordPaymentDialog();
- break;
- case 'generateReport':
- this.showGenerateReportDialog();
- break;
- case 'quotationApproval':
- const confirmNavigation = confirm('💼 即将跳转到报价审核页面\n\n是否继续?');
- if (confirmNavigation) {
- this.router.navigate(['/finance/quotation-approval']);
- }
- break;
- }
- }
- // 显示新建报价对话框
- private showNewQuoteDialog(): void {
- const projectName = prompt('📝 新建报价\n\n请输入项目名称:', '示例项目');
- if (projectName) {
- const projectType = prompt('请选择项目类型:\n\n1. 家装-小户型\n2. 家装-大户型\n3. 工装-商铺\n4. 工装-写字楼\n\n请输入数字 1-4:', '1');
-
- if (projectType) {
- alert(`✅ 报价创建成功!\n\n项目名称:${projectName}\n项目类型:${this.getProjectTypeText(projectType)}\n\n系统正在为您准备报价模板...`);
- console.log('创建报价:', { projectName, projectType });
- // window.location.href = '/finance/project-records';
- }
- }
- }
- // 显示记录回款对话框
- private showRecordPaymentDialog(): void {
- const amount = prompt('💰 记录回款\n\n请输入回款金额(元):', '');
- if (amount && !isNaN(Number(amount))) {
- const paymentMethod = prompt('请选择支付方式:\n\n1. 微信支付\n2. 支付宝\n3. 银行转账\n4. 现金\n\n请输入数字 1-4:', '1');
-
- if (paymentMethod) {
- alert(`✅ 回款记录成功!\n\n回款金额:¥${Number(amount).toLocaleString()}\n支付方式:${this.getPaymentMethodText(paymentMethod)}\n记录时间:${new Date().toLocaleString()}\n\n财务数据已更新`);
- console.log('记录回款:', { amount, paymentMethod });
- // window.location.href = '/finance/reconciliation';
- }
- }
- }
- // 显示生成报表对话框
- private showGenerateReportDialog(): void {
- const reportType = prompt('📊 生成财务报表\n\n请选择报表类型:\n\n1. 月度收入报表\n2. 项目利润分析\n3. 回款统计报表\n4. 成本费用报表\n\n请输入数字 1-4:', '1');
-
- if (reportType) {
- const period = prompt('请选择时间周期:\n\n1. 本月\n2. 上月\n3. 本季度\n4. 本年度\n\n请输入数字 1-4:', '1');
-
- if (period) {
- alert(`✅ 报表生成成功!\n\n报表类型:${this.getReportTypeText(reportType)}\n时间周期:${this.getPeriodText(period)}\n生成时间:${new Date().toLocaleString()}\n\n报表正在下载,请稍候...`);
- console.log('生成报表:', { reportType, period });
- // window.location.href = '/finance/reports';
- }
- }
- }
- // 辅助方法:获取项目类型文本
- private getProjectTypeText(type: string): string {
- const types: Record<string, string> = {
- '1': '家装-小户型',
- '2': '家装-大户型',
- '3': '工装-商铺',
- '4': '工装-写字楼'
- };
- return types[type] || '未知类型';
- }
- // 辅助方法:获取支付方式文本
- private getPaymentMethodText(method: string): string {
- const methods: Record<string, string> = {
- '1': '微信支付',
- '2': '支付宝',
- '3': '银行转账',
- '4': '现金'
- };
- return methods[method] || '未知方式';
- }
- // 辅助方法:获取报表类型文本
- private getReportTypeText(type: string): string {
- const types: Record<string, string> = {
- '1': '月度收入报表',
- '2': '项目利润分析',
- '3': '回款统计报表',
- '4': '成本费用报表'
- };
- return types[type] || '未知报表';
- }
- // 辅助方法:获取周期文本
- private getPeriodText(period: string): string {
- const periods: Record<string, string> = {
- '1': '本月',
- '2': '上月',
- '3': '本季度',
- '4': '本年度'
- };
- return periods[period] || '未知周期';
- }
- // 格式化金额显示
- formatAmount(amount: number): string {
- return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' }).format(amount);
- }
- // 加载工时统计数据
- loadWorkHourData(): void {
- this.workHourService.getDashboardData().subscribe(data => {
- this.workHourDashboard.set(data);
- });
- this.workHourService.getMonthlyStats().subscribe(stats => {
- this.monthlyStats.set(stats);
- });
- }
- // 切换工时模块显示
- toggleWorkHourModule(): void {
- this.showWorkHourModule.set(!this.showWorkHourModule());
- }
- // 获取绩效等级颜色
- getPerformanceLevelColor(level: string): string {
- const colors: Record<string, string> = {
- 'S': '#ff6b6b',
- 'A': '#4ecdc4',
- 'B': '#45b7d1',
- 'C': '#96ceb4'
- };
- return colors[level] || '#ccc';
- }
- // 获取绩效等级人数
- getPerformanceLevelCount(level: string): number {
- const dashboard = this.workHourDashboard();
- if (!dashboard) return 0;
-
- const distribution = dashboard.performanceDistribution as Record<string, number>;
- return distribution[level] || 0;
- }
- // 获取绩效等级描述
- getPerformanceLevelDescription(level: string): string {
- const descriptions: Record<string, string> = {
- 'S': '卓越表现',
- 'A': '优秀表现',
- 'B': '良好表现',
- 'C': '待提升'
- };
- return descriptions[level] || '未知';
- }
- // 格式化工时显示
- formatWorkHours(hours: number): string {
- const days = Math.floor(hours / 8);
- const remainingHours = hours % 8;
-
- if (days > 0 && remainingHours > 0) {
- return `${days}天${remainingHours}小时`;
- } else if (days > 0) {
- return `${days}天`;
- } else {
- return `${remainingHours}小时`;
- }
- }
- // 新增:获取标准工时
- getStandardHours(complexityType: string): ProjectComplexity | undefined {
- return this.projectComplexities.find(c => c.type === complexityType);
- }
- // 新增:获取逾期预警样式类
- getOverdueClass(days: number): string {
- if (days >= 5) return 'severe';
- if (days >= 3) return 'warning';
- return 'mild';
- }
- // 新增:切换时间维度
- setTimeDimension(dimension: 'week' | 'month' | 'quarter'): void {
- this.timeDimension.set(dimension);
- // 根据不同维度更新设计师效率数据
- this.updateDesignerEfficienciesByDimension(dimension);
- // 重新加载工时数据
- this.loadWorkHourData();
- }
-
- // 根据时间维度更新设计师效率数据
- private updateDesignerEfficienciesByDimension(dimension: 'week' | 'month' | 'quarter'): void {
- const baseData: DesignerEfficiency[] = [
- {
- id: 'D001',
- name: '张设计',
- avgCompletionTime: dimension === 'week' ? 15 : dimension === 'month' ? 18 : 20,
- overdueProjectCount: dimension === 'week' ? 0 : dimension === 'month' ? 1 : 2,
- stageUtilization: {
- requirementDeepening: dimension === 'week' ? 90 : dimension === 'month' ? 85 : 80,
- modeling: dimension === 'week' ? 95 : dimension === 'month' ? 92 : 88,
- rendering: dimension === 'week' ? 82 : dimension === 'month' ? 78 : 75,
- postProduction: dimension === 'week' ? 92 : dimension === 'month' ? 88 : 85
- },
- idleDays: dimension === 'week' ? 1 : dimension === 'month' ? 3 : 5,
- suggestion: dimension === 'week' ? '本周表现优秀,可适当增加项目难度' : dimension === 'month' ? '表现优秀,可适当增加项目难度' : '本季度整体表现良好,建议继续保持'
- },
- {
- id: 'D002',
- name: '李设计',
- avgCompletionTime: dimension === 'week' ? 22 : dimension === 'month' ? 25 : 28,
- overdueProjectCount: dimension === 'week' ? 1 : dimension === 'month' ? 2 : 4,
- stageUtilization: {
- requirementDeepening: dimension === 'week' ? 75 : dimension === 'month' ? 70 : 65,
- modeling: dimension === 'week' ? 70 : dimension === 'month' ? 65 : 60,
- rendering: dimension === 'week' ? 76 : dimension === 'month' ? 72 : 68,
- postProduction: dimension === 'week' ? 82 : dimension === 'month' ? 80 : 75
- },
- idleDays: dimension === 'week' ? 2 : dimension === 'month' ? 8 : 15,
- suggestion: dimension === 'week' ? '本周闲置2天,建议分配更多项目' : dimension === 'month' ? '近30天闲置8天,建议分配更多项目' : '本季度闲置时间较长,需要加强项目分配'
- },
- {
- id: 'D003',
- name: '王设计',
- avgCompletionTime: dimension === 'week' ? 12 : dimension === 'month' ? 15 : 17,
- overdueProjectCount: 0,
- stageUtilization: {
- requirementDeepening: dimension === 'week' ? 98 : dimension === 'month' ? 95 : 92,
- modeling: dimension === 'week' ? 95 : dimension === 'month' ? 90 : 88,
- rendering: dimension === 'week' ? 96 : dimension === 'month' ? 93 : 90,
- postProduction: dimension === 'week' ? 94 : dimension === 'month' ? 92 : 89
- },
- idleDays: dimension === 'week' ? 0 : dimension === 'month' ? 1 : 2,
- suggestion: dimension === 'week' ? '本周表现卓越,建议承接重点项目' : dimension === 'month' ? '高效能设计师,建议承接重点项目' : '本季度持续高效,可考虑带教新人'
- },
- {
- id: 'D004',
- name: '赵设计',
- avgCompletionTime: dimension === 'week' ? 20 : dimension === 'month' ? 22 : 24,
- overdueProjectCount: dimension === 'week' ? 0 : dimension === 'month' ? 1 : 3,
- stageUtilization: {
- requirementDeepening: dimension === 'week' ? 85 : dimension === 'month' ? 80 : 75,
- modeling: dimension === 'week' ? 78 : dimension === 'month' ? 75 : 70,
- rendering: dimension === 'week' ? 74 : dimension === 'month' ? 70 : 65,
- postProduction: dimension === 'week' ? 88 : dimension === 'month' ? 85 : 80
- },
- idleDays: dimension === 'week' ? 3 : dimension === 'month' ? 10 : 20,
- suggestion: dimension === 'week' ? '本周渲染阶段需提升,建议参加培训' : dimension === 'month' ? '需要提升渲染阶段效率,建议参加培训' : '本季度渲染效率偏低,需要重点关注和培训'
- }
- ];
-
- this.designerEfficiencies.set(baseData);
- }
- // 新增:获取时间维度文本
- getTimeDimensionText(): string {
- const dimension = this.timeDimension();
- const texts = {
- week: '本周',
- month: '本月',
- quarter: '本季度'
- };
- return texts[dimension];
- }
- // 新增:导出逾期分析报表
- exportOverdueReport(): void {
- const dimension = this.getTimeDimensionText();
- const projects = this.overdueProjects();
- const overdueCount = projects.length;
- const totalOverdueDays = projects.reduce((sum: number, p: OverdueProject) => sum + p.overdueDays, 0);
- const avgOverdueDays = overdueCount > 0 ? (totalOverdueDays / overdueCount).toFixed(1) : '0';
-
- // 生成CSV内容
- let csvContent = '\uFEFF'; // UTF-8 BOM for Excel compatibility
- csvContent += '逾期项目分析报表\n\n';
- csvContent += `时间维度,${dimension}\n`;
- csvContent += `统计时间,${new Date().toLocaleDateString()}\n`;
- csvContent += `逾期项目数,${overdueCount}\n`;
- csvContent += `总逾期天数,${totalOverdueDays}\n`;
- csvContent += `平均逾期天数,${avgOverdueDays}\n\n`;
- csvContent += '项目ID,项目名称,当前阶段,逾期天数,逾期原因,负责设计师\n';
-
- projects.forEach((p: OverdueProject) => {
- csvContent += `${p.id},"${p.name}",${p.stage},${p.overdueDays},"${p.reason}",${p.designerName}\n`;
- });
-
- // 创建Blob并下载
- const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
- const link = document.createElement('a');
- const url = URL.createObjectURL(blob);
- link.setAttribute('href', url);
- link.setAttribute('download', `逾期项目分析报表_${dimension}_${new Date().toISOString().split('T')[0]}.csv`);
- link.style.visibility = 'hidden';
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- URL.revokeObjectURL(url);
-
- alert(`✅ 报表导出成功!\n\n时间维度:${dimension}\n逾期项目数:${overdueCount}个\n平均逾期天数:${avgOverdueDays}天\n\n报表已下载到您的下载文件夹。`);
- }
- // 新增:查看项目详情
- viewProjectDetail(projectId: string): void {
- this.router.navigate(['/designer/project-detail', projectId], {
- queryParams: {
- role: 'customer-service',
- activeTab: 'progress',
- currentStage: '客户评价',
- section: 'aftercare',
- view: 'project-review-only'
- }
- });
- }
- // 新增:查看设计师详情
- viewDesignerDetail(designerId: string): void {
- const designers = this.designerEfficiencies();
- const designer = designers.find((d: DesignerEfficiency) => d.id === designerId);
- if (!designer) return;
-
- // 将对象转换为可读的利用率信息
- const utilizationDetails = [
- ` 需求深化:${designer.stageUtilization.requirementDeepening}%`,
- ` 建模阶段:${designer.stageUtilization.modeling}%`,
- ` 渲染阶段:${designer.stageUtilization.rendering}%`,
- ` 后期处理:${designer.stageUtilization.postProduction}%`
- ].join('\n');
-
- // 计算效率等级
- const avgUtilization = (
- designer.stageUtilization.requirementDeepening +
- designer.stageUtilization.modeling +
- designer.stageUtilization.rendering +
- designer.stageUtilization.postProduction
- ) / 4;
-
- const efficiencyLevel = avgUtilization >= 90 ? '优秀' :
- avgUtilization >= 75 ? '良好' :
- avgUtilization >= 60 ? '中等' : '待提升';
-
- alert(`👨💼 设计师效率详情
- 姓名:${designer.name}
- 效率等级:${efficiencyLevel} (平均利用率: ${avgUtilization.toFixed(1)}%)
- 平均完成时长:${designer.avgCompletionTime}天
- 当前闲置天数:${designer.idleDays}天
- 逾期项目数:${designer.overdueProjectCount}个
- 各阶段工时利用率:
- ${utilizationDetails}
- ${designer.suggestion}
- 点击确定查看更多详情...`);
-
- console.log('查看设计师详情:', designer);
- // TODO: 跳转到设计师详情页
- }
- // 新增:跳转到售后复盘(悬浮按钮)
- goToAftercare(): void {
- // 跳转到指定的项目复盘页面
- const projectId = 'mock-1';
- this.router.navigate(['/designer/project-detail', projectId], {
- queryParams: {
- role: 'customer-service',
- activeTab: 'progress',
- currentStage: '客户评价',
- section: 'aftercare', // 定位到售后板块
- view: 'project-review-only' // 只显示项目复盘内容
- }
- });
- }
- // 新增:获取效率等级颜色
- getEfficiencyLevelColor(avgTime: number): string {
- if (avgTime <= 15) return '#34c759'; // 优秀 - 绿色
- if (avgTime <= 20) return '#30b0c7'; // 良好 - 青色
- if (avgTime <= 25) return '#ff9500'; // 一般 - 橙色
- return '#ff3b30'; // 需提升 - 红色
- }
- // 新增:获取阶段利用率颜色
- getUtilizationColor(percentage: number): string {
- if (percentage >= 90) return '#34c759';
- if (percentage >= 75) return '#30b0c7';
- if (percentage >= 60) return '#ff9500';
- return '#ff3b30';
- }
- }
|