123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584 |
- // 修复 OnDestroy 导入和使用
- import { Component, OnInit, OnDestroy, signal, computed } from '@angular/core';
- import { CommonModule } from '@angular/common';
- import { FormsModule } from '@angular/forms';
- import { RouterModule, Router, ActivatedRoute } from '@angular/router';
- import { ProjectService } from '../../../services/project.service';
- import { Project, Task, CustomerFeedback } from '../../../models/project.model';
- @Component({
- selector: 'app-dashboard',
- standalone: true,
- imports: [CommonModule, FormsModule, RouterModule],
- templateUrl: './dashboard.html',
- styleUrls: ['./dashboard.scss', '../customer-service-styles.scss'],
- providers: [ProjectService]
- })
- export class Dashboard implements OnInit, OnDestroy {
- // 数据看板统计
- stats = {
- newConsultations: signal(12),
- pendingAssignments: signal(5),
- exceptionProjects: signal(2),
- todayRevenue: signal(28500)
- };
- // 紧急待办列表
- urgentTasks = signal<Task[]>([]);
- // 任务处理状态
- taskProcessingState = signal<{[key: string]: {inProgress: boolean, progress: number}}>({});
-
- // 项目动态流
- projectUpdates = signal<(Project | CustomerFeedback)[]>([]);
-
- // 搜索关键词
- searchTerm = signal('');
-
- // 筛选后的项目更新
- filteredUpdates = computed(() => {
- if (!this.searchTerm()) return this.projectUpdates();
-
- return this.projectUpdates().filter(item => {
- if ('name' in item) {
- // 项目
- return item.name.toLowerCase().includes(this.searchTerm().toLowerCase()) ||
- item.customerName.toLowerCase().includes(this.searchTerm().toLowerCase()) ||
- item.status.toLowerCase().includes(this.searchTerm().toLowerCase());
- } else {
- // 反馈
- return 'content' in item && item.content.toLowerCase().includes(this.searchTerm().toLowerCase()) ||
- 'status' in item && item.status.toLowerCase().includes(this.searchTerm().toLowerCase());
- }
- });
- });
- currentDate = new Date();
-
- // 回到顶部按钮可见性信号
- showBackToTopSignal = signal(false);
- // 任务表单可见性
- isTaskFormVisible = signal(false);
-
- // 新任务数据
- newTask: Task = {
- id: '',
- projectId: '',
- projectName: '',
- title: '',
- stage: '需求沟通',
- deadline: new Date(),
- isOverdue: false,
- isCompleted: false,
- priority: 'high',
- assignee: '当前用户',
- description: ''
- };
-
- // 用于日期时间输入的属性
- deadlineInput = '';
-
- // 预设快捷时长选项
- timePresets = [
- { label: '1小时内', hours: 1 },
- { label: '3小时内', hours: 3 },
- { label: '6小时内', hours: 6 },
- { label: '12小时内', hours: 12 },
- { label: '24小时内', hours: 24 }
- ];
-
- // 选中的预设时长
- selectedPreset = '';
-
- // 自定义时间弹窗可见性
- isCustomTimeVisible = false;
-
- // 自定义选择的日期和时间
- customDate = new Date();
- customTime = '';
-
- // 错误提示信息
- deadlineError = '';
-
- // 提交按钮是否禁用
- isSubmitDisabled = false;
-
- // 下拉框可见性
- deadlineDropdownVisible = false;
-
- // 日期范围限制
- get todayDate(): string {
- return new Date().toISOString().split('T')[0];
- }
-
- get sevenDaysLaterDate(): string {
- const date = new Date();
- date.setDate(date.getDate() + 7);
- return date.toISOString().split('T')[0];
- }
- constructor(
- private projectService: ProjectService,
- private router: Router,
- private activatedRoute: ActivatedRoute
- ) {}
- ngOnInit(): void {
- this.loadUrgentTasks();
- this.loadProjectUpdates();
-
- // 添加滚动事件监听
- window.addEventListener('scroll', this.onScroll.bind(this));
- }
- // 添加滚动事件处理方法
- private onScroll(): void {
- this.showBackToTopSignal.set(window.scrollY > 300);
- }
-
- // 添加显示回到顶部按钮的计算属性
- showBackToTop = computed(() => this.showBackToTopSignal());
- // 清理事件监听器
- ngOnDestroy(): void {
- window.removeEventListener('scroll', this.onScroll.bind(this));
- }
- // 添加scrollToTop方法
- scrollToTop(): void {
- window.scrollTo({
- top: 0,
- behavior: 'smooth'
- });
- }
-
- // 修改loadUrgentTasks方法,添加status属性
- loadUrgentTasks(): void {
- // 从服务获取任务数据,筛选出紧急任务
- this.projectService.getTasks().subscribe(tasks => {
- const filteredTasks = tasks.map(task => ({...task, status: task.isOverdue ? '已逾期' : task.isCompleted ? '已完成' : '进行中'}))
- .filter(task => task.isOverdue || task.deadline.toDateString() === new Date().toDateString());
-
- this.urgentTasks.set(filteredTasks.sort((a, b) => {
- // 按紧急程度排序
- if (a.isOverdue && !b.isOverdue) return -1;
- if (!a.isOverdue && b.isOverdue) return 1;
- return a.deadline.getTime() - b.deadline.getTime();
- }));
- });
- }
- loadProjectUpdates(): void {
- // 模拟项目更新数据
- this.projectService.getProjects().subscribe(projects => {
- this.projectService.getCustomerFeedbacks().subscribe(feedbacks => {
- // 合并项目和反馈,按时间倒序排序
- const updates: (Project | CustomerFeedback)[] = [
- ...projects,
- ...feedbacks
- ].sort((a, b) => {
- const dateA = 'createdAt' in a ? a.createdAt : new Date(a['updatedAt'] || a['deadline']);
- const dateB = 'createdAt' in b ? b.createdAt : new Date(b['updatedAt'] || b['deadline']);
- return dateB.getTime() - dateA.getTime();
- }).slice(0, 20); // 限制显示20条
-
- this.projectUpdates.set(updates);
- });
- });
- }
- // 处理任务完成
- markTaskAsCompleted(taskId: string): void {
- this.urgentTasks.set(
- this.urgentTasks().map(task =>
- task.id === taskId ? { ...task, isCompleted: true, status: '已完成' } : task
- )
- );
- }
- // 处理派单操作
- handleAssignment(taskId: string): void {
- // 标记任务为处理中
- const task = this.urgentTasks().find(t => t.id === taskId);
- if (task) {
- // 初始化处理状态
- this.taskProcessingState.update(state => ({
- ...state,
- [task.id]: { inProgress: true, progress: 0 }
- }));
- // 模拟处理进度
- let progress = 0;
- const interval = setInterval(() => {
- progress += 10;
-
- this.taskProcessingState.update(state => ({
- ...state,
- [task.id]: { inProgress: progress < 100, progress }
- }));
- if (progress >= 100) {
- clearInterval(interval);
-
- // 处理完成后从列表中移除该任务
- this.urgentTasks.set(
- this.urgentTasks().filter(t => t.id !== task.id)
- );
-
- // 清除处理状态
- this.taskProcessingState.update(state => {
- const newState = { ...state };
- delete newState[task.id];
- return newState;
- });
- }
- }, 300);
- }
- // 更新统计数据
- this.stats.pendingAssignments.set(this.stats.pendingAssignments() - 1);
- }
- // 显示任务表单
- showTaskForm(): void {
- // 重置表单数据
- this.newTask = {
- id: '',
- projectId: '',
- projectName: '',
- title: '',
- stage: '需求沟通',
- deadline: new Date(),
- isOverdue: false,
- isCompleted: false,
- priority: 'high',
- assignee: '当前用户',
- description: ''
- };
-
- // 重置相关状态
- this.deadlineError = '';
- this.isSubmitDisabled = false;
-
- // 计算并设置默认预设时长
- this.setDefaultPreset();
-
- // 显示表单
- this.isTaskFormVisible.set(true);
-
- // 添加iOS风格的面板显示动画
- setTimeout(() => {
- document.querySelector('.ios-panel')?.classList.add('ios-panel-visible');
- }, 10);
- }
-
- // 设置默认预设时长
- private setDefaultPreset(): void {
- const now = new Date();
- const todayEnd = new Date(now);
- todayEnd.setHours(23, 59, 59, 999);
-
- // 检查3小时后是否超过当天24:00
- const threeHoursLater = new Date(now.getTime() + 3 * 60 * 60 * 1000);
-
- if (threeHoursLater <= todayEnd) {
- // 3小时后未超过当天24:00,默认选中3小时内
- this.selectedPreset = '3';
- this.updatePresetDeadline(3);
- } else {
- // 3小时后超过当天24:00,默认选中当天24:00前
- this.selectedPreset = 'today';
- this.deadlineInput = todayEnd.toISOString().slice(0, 16);
- this.newTask.deadline = todayEnd;
- }
- }
-
- // 处理预设时长选择
- handlePresetSelection(preset: string): void {
- this.selectedPreset = preset;
- this.deadlineError = '';
-
- if (preset === 'custom') {
- // 打开自定义时间选择器
- this.openCustomTimePicker();
- } else if (preset === 'today') {
- // 设置为当天24:00前
- const now = new Date();
- const todayEnd = new Date(now);
- todayEnd.setHours(23, 59, 59, 999);
-
- this.deadlineInput = todayEnd.toISOString().slice(0, 16);
- this.newTask.deadline = todayEnd;
- } else {
- // 计算预设时长的截止时间
- const hours = parseInt(preset);
- this.updatePresetDeadline(hours);
- }
- }
-
- // 更新预设时长的截止时间
- private updatePresetDeadline(hours: number): void {
- const now = new Date();
- const deadline = new Date(now.getTime() + hours * 60 * 60 * 1000);
-
- this.deadlineInput = deadline.toISOString().slice(0, 16);
- this.newTask.deadline = deadline;
- }
-
- // 打开自定义时间选择器
- openCustomTimePicker(): void {
- // 重置自定义时间
- this.customDate = new Date();
- const now = new Date();
- this.customTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
-
- // 显示自定义时间弹窗
- this.isCustomTimeVisible = true;
-
- // 添加iOS风格的弹窗动画
- setTimeout(() => {
- document.querySelector('.custom-time-modal')?.classList.add('modal-visible');
- }, 10);
- }
-
- // 关闭自定义时间选择器
- closeCustomTimePicker(): void {
- // 添加iOS风格的弹窗关闭动画
- const modal = document.querySelector('.custom-time-modal');
- if (modal) {
- modal.classList.remove('modal-visible');
- setTimeout(() => {
- this.isCustomTimeVisible = false;
- }, 300);
- } else {
- this.isCustomTimeVisible = false;
- }
- }
-
- // 处理自定义时间选择
- handleCustomTimeSelection(): void {
- const [hours, minutes] = this.customTime.split(':').map(Number);
- const selectedDateTime = new Date(this.customDate);
- selectedDateTime.setHours(hours, minutes, 0, 0);
-
- // 验证选择的时间是否有效
- if (this.validateDeadline(selectedDateTime)) {
- this.deadlineInput = selectedDateTime.toISOString().slice(0, 16);
- this.newTask.deadline = selectedDateTime;
- this.closeCustomTimePicker();
- }
- }
-
- // 验证截止时间是否有效
- validateDeadline(deadline: Date): boolean {
- const now = new Date();
-
- if (deadline < now) {
- this.deadlineError = '截止时间不能早于当前时间,请重新选择';
- this.isSubmitDisabled = true;
- return false;
- }
-
- this.deadlineError = '';
- this.isSubmitDisabled = false;
- return true;
- }
-
- // 获取显示的截止时间文本
- getDisplayDeadline(): string {
- if (!this.deadlineInput) return '';
-
- try {
- const date = new Date(this.deadlineInput);
- return date.toLocaleString('zh-CN', {
- year: 'numeric',
- month: '2-digit',
- day: '2-digit',
- hour: '2-digit',
- minute: '2-digit'
- });
- } catch (error) {
- return '';
- }
- }
-
- // 隐藏任务表单
- hideTaskForm(): void {
- // 添加iOS风格的面板隐藏动画
- const panel = document.querySelector('.ios-panel');
- if (panel) {
- panel.classList.remove('ios-panel-visible');
- setTimeout(() => {
- this.isTaskFormVisible.set(false);
- }, 300);
- } else {
- this.isTaskFormVisible.set(false);
- }
- }
-
- // 处理添加任务表单提交
- handleAddTaskSubmit(): void {
- // 验证表单数据
- if (!this.newTask.title.trim() || !this.newTask.projectName.trim() || !this.deadlineInput || this.isSubmitDisabled) {
- // 在实际应用中,这里应该显示错误提示
- alert('请填写必填字段(任务标题、项目名称、截止时间)');
- return;
- }
-
- // 创建新任务
- const taskToAdd: Task = {
- ...this.newTask,
- id: `task-${Date.now()}`,
- projectId: `project-${Math.floor(Math.random() * 1000)}`,
- deadline: new Date(this.deadlineInput),
- isOverdue: new Date(this.deadlineInput) < new Date()
- };
-
- // 添加到任务列表
- this.urgentTasks.set([taskToAdd, ...this.urgentTasks()]);
-
- // 更新统计数据
- this.stats.pendingAssignments.set(this.stats.pendingAssignments() + 1);
-
- // 隐藏表单
- this.hideTaskForm();
- }
-
- // 添加新的紧急事项
- addUrgentTask(): void {
- // 调用显示表单方法
- this.showTaskForm();
- }
- // 新咨询数图标点击处理
- handleNewConsultationsClick(): void {
- this.navigateToDetail('consultations');
- }
- // 待派单数图标点击处理
- handlePendingAssignmentsClick(): void {
- this.navigateToDetail('assignments');
- }
- // 异常项目图标点击处理
- handleExceptionProjectsClick(): void {
- this.navigateToDetail('exceptions');
- }
- // 今日成交额图标点击处理
- handleTodayRevenueClick(): void {
- this.navigateToDetail('revenue');
- }
- // 导航到详情页
- private navigateToDetail(type: 'consultations' | 'assignments' | 'exceptions' | 'revenue'): void {
- const routeMap = {
- consultations: '/customer-service/consultation-list',
- assignments: '/customer-service/assignment-list',
- exceptions: '/customer-service/exception-list',
- revenue: '/customer-service/revenue-detail'
- };
-
- console.log('导航到:', routeMap[type]);
- console.log('当前路由:', this.router.url);
-
- // 添加iOS风格页面过渡动画
- document.body.classList.add('ios-page-transition');
- setTimeout(() => {
- this.router.navigateByUrl(routeMap[type])
- .then(navResult => {
- console.log('导航结果:', navResult);
- if (!navResult) {
- console.error('导航失败,检查路由配置');
- }
- })
- .catch(err => {
- console.error('导航错误:', err);
- });
-
- setTimeout(() => {
- document.body.classList.remove('ios-page-transition');
- }, 300);
- }, 100);
- }
- // 格式化日期
- formatDate(date: Date | string): string {
- if (!date) return '';
- try {
- return new Date(date).toLocaleString('zh-CN', {
- month: '2-digit',
- day: '2-digit',
- hour: '2-digit',
- minute: '2-digit'
- });
- } catch (error) {
- console.error('日期格式化错误:', error);
- return '';
- }
- }
- // 添加安全获取客户名称的方法
- getCustomerName(update: Project | CustomerFeedback): string {
- if ('customerName' in update && update.customerName) {
- return update.customerName;
- } else if ('projectId' in update) {
- // 查找相关项目获取客户名称
- return '客户反馈';
- }
- return '未知客户';
- }
- // 优化的日期格式化方法
- getFormattedDate(update: Project | CustomerFeedback): string {
- if (!update) return '';
-
- if ('createdAt' in update && update.createdAt) {
- return this.formatDate(update.createdAt);
- } else if ('updatedAt' in update && update.updatedAt) {
- return this.formatDate(update.updatedAt);
- } else if ('deadline' in update && update.deadline) {
- return this.formatDate(update.deadline);
- }
- return '';
- }
- // 添加获取状态的安全方法
- getUpdateStatus(update: Project | CustomerFeedback): string {
- if ('status' in update && update.status) {
- return update.status;
- }
- return '已更新';
- }
- // 添加getTaskStatus方法的正确实现
- getTaskStatus(task: Task): string {
- if (!task) return '未知状态';
- if (task.isCompleted) return '已完成';
- if (task.isOverdue) return '已逾期';
- return '进行中';
- }
- // 添加getUpdateStatusClass方法的正确实现
- getUpdateStatusClass(update: Project | CustomerFeedback): string {
- if (!update || !('status' in update) || !update.status) return '';
-
- switch (update.status) {
- case '进行中':
- return 'status-active';
- case '已完成':
- return 'status-completed';
- case '已延期':
- case '已暂停':
- return 'status-warning';
- case '已解决':
- return 'status-success';
- case '待处理':
- case '处理中':
- return 'status-info';
- default:
- return '';
- }
- }
- }
|