dashboard.ts 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. // 修复 OnDestroy 导入和使用
  2. import { Component, OnInit, OnDestroy, signal, computed } from '@angular/core';
  3. import { CommonModule } from '@angular/common';
  4. import { FormsModule } from '@angular/forms';
  5. import { RouterModule } from '@angular/router';
  6. import { ProjectService } from '../../../services/project.service';
  7. import { Project, Task, CustomerFeedback } from '../../../models/project.model';
  8. @Component({
  9. selector: 'app-dashboard',
  10. standalone: true,
  11. imports: [CommonModule, FormsModule, RouterModule],
  12. templateUrl: './dashboard.html',
  13. styleUrls: ['./dashboard.scss', '../customer-service-styles.scss']
  14. })
  15. export class Dashboard implements OnInit, OnDestroy {
  16. // 数据看板统计
  17. stats = {
  18. newConsultations: signal(12),
  19. pendingAssignments: signal(5),
  20. exceptionProjects: signal(2),
  21. todayRevenue: signal(28500)
  22. };
  23. // 紧急待办列表
  24. urgentTasks = signal<Task[]>([]);
  25. // 任务处理状态
  26. taskProcessingState = signal<{[key: string]: {inProgress: boolean, progress: number}}>({});
  27. // 项目动态流
  28. projectUpdates = signal<(Project | CustomerFeedback)[]>([]);
  29. // 搜索关键词
  30. searchTerm = signal('');
  31. // 筛选后的项目更新
  32. filteredUpdates = computed(() => {
  33. if (!this.searchTerm()) return this.projectUpdates();
  34. return this.projectUpdates().filter(item => {
  35. if ('name' in item) {
  36. // 项目
  37. return item.name.toLowerCase().includes(this.searchTerm().toLowerCase()) ||
  38. item.customerName.toLowerCase().includes(this.searchTerm().toLowerCase()) ||
  39. item.status.toLowerCase().includes(this.searchTerm().toLowerCase());
  40. } else {
  41. // 反馈
  42. return 'content' in item && item.content.toLowerCase().includes(this.searchTerm().toLowerCase()) ||
  43. 'status' in item && item.status.toLowerCase().includes(this.searchTerm().toLowerCase());
  44. }
  45. });
  46. });
  47. currentDate = new Date();
  48. // 回到顶部按钮可见性信号
  49. showBackToTopSignal = signal(false);
  50. constructor(private projectService: ProjectService) {}
  51. ngOnInit(): void {
  52. this.loadUrgentTasks();
  53. this.loadProjectUpdates();
  54. // 添加滚动事件监听
  55. window.addEventListener('scroll', this.onScroll.bind(this));
  56. }
  57. // 添加滚动事件处理方法
  58. private onScroll(): void {
  59. this.showBackToTopSignal.set(window.scrollY > 300);
  60. }
  61. // 添加显示回到顶部按钮的计算属性
  62. showBackToTop = computed(() => this.showBackToTopSignal());
  63. // 清理事件监听器
  64. ngOnDestroy(): void {
  65. window.removeEventListener('scroll', this.onScroll.bind(this));
  66. }
  67. // 添加scrollToTop方法
  68. scrollToTop(): void {
  69. window.scrollTo({
  70. top: 0,
  71. behavior: 'smooth'
  72. });
  73. }
  74. // 修改loadUrgentTasks方法,添加status属性
  75. loadUrgentTasks(): void {
  76. // 从服务获取任务数据,筛选出紧急任务
  77. this.projectService.getTasks().subscribe(tasks => {
  78. const filteredTasks = tasks.map(task => ({...task, status: task.isOverdue ? '已逾期' : task.isCompleted ? '已完成' : '进行中'}))
  79. .filter(task => task.isOverdue || task.deadline.toDateString() === new Date().toDateString());
  80. this.urgentTasks.set(filteredTasks.sort((a, b) => {
  81. // 按紧急程度排序
  82. if (a.isOverdue && !b.isOverdue) return -1;
  83. if (!a.isOverdue && b.isOverdue) return 1;
  84. return a.deadline.getTime() - b.deadline.getTime();
  85. }));
  86. });
  87. }
  88. loadProjectUpdates(): void {
  89. // 模拟项目更新数据
  90. this.projectService.getProjects().subscribe(projects => {
  91. this.projectService.getCustomerFeedbacks().subscribe(feedbacks => {
  92. // 合并项目和反馈,按时间倒序排序
  93. const updates: (Project | CustomerFeedback)[] = [
  94. ...projects,
  95. ...feedbacks
  96. ].sort((a, b) => {
  97. const dateA = 'createdAt' in a ? a.createdAt : new Date(a['updatedAt'] || a['deadline']);
  98. const dateB = 'createdAt' in b ? b.createdAt : new Date(b['updatedAt'] || b['deadline']);
  99. return dateB.getTime() - dateA.getTime();
  100. }).slice(0, 20); // 限制显示20条
  101. this.projectUpdates.set(updates);
  102. });
  103. });
  104. }
  105. // 处理任务完成
  106. markTaskAsCompleted(taskId: string): void {
  107. this.urgentTasks.set(
  108. this.urgentTasks().map(task =>
  109. task.id === taskId ? { ...task, isCompleted: true, status: '已完成' } : task
  110. )
  111. );
  112. }
  113. // 处理派单操作
  114. handleAssignment(taskId: string): void {
  115. // 标记任务为处理中
  116. const task = this.urgentTasks().find(t => t.id === taskId);
  117. if (task) {
  118. // 初始化处理状态
  119. this.taskProcessingState.update(state => ({
  120. ...state,
  121. [task.id]: { inProgress: true, progress: 0 }
  122. }));
  123. // 模拟处理进度
  124. let progress = 0;
  125. const interval = setInterval(() => {
  126. progress += 10;
  127. this.taskProcessingState.update(state => ({
  128. ...state,
  129. [task.id]: { inProgress: progress < 100, progress }
  130. }));
  131. if (progress >= 100) {
  132. clearInterval(interval);
  133. // 处理完成后从列表中移除该任务
  134. this.urgentTasks.set(
  135. this.urgentTasks().filter(t => t.id !== task.id)
  136. );
  137. // 清除处理状态
  138. this.taskProcessingState.update(state => {
  139. const newState = { ...state };
  140. delete newState[task.id];
  141. return newState;
  142. });
  143. }
  144. }, 300);
  145. }
  146. // 更新统计数据
  147. this.stats.pendingAssignments.set(this.stats.pendingAssignments() - 1);
  148. }
  149. // 添加新的紧急事项
  150. addUrgentTask(): void {
  151. // 在实际应用中,这里可能会打开一个表单模态框
  152. // 这里使用模拟数据直接添加
  153. const newTask: Task = {
  154. id: `task-${Date.now()}`,
  155. projectId: `project-${Math.floor(Math.random() * 1000)}`,
  156. title: '新增紧急任务',
  157. projectName: '新项目',
  158. stage: '前期沟通', // 设置一个有效的ProjectStage值
  159. deadline: new Date(),
  160. isOverdue: false,
  161. isCompleted: false,
  162. // Task接口中没有status属性,移除它
  163. // 为了符合Task接口要求,添加required的stage字段
  164. priority: 'high', // 模拟值
  165. assignee: '当前用户', // 模拟值
  166. description: '紧急任务描述' // 模拟值
  167. };
  168. this.urgentTasks.set([newTask, ...this.urgentTasks()]);
  169. this.stats.pendingAssignments.set(this.stats.pendingAssignments() + 1);
  170. }
  171. // 新咨询数图标点击处理
  172. handleNewConsultationsClick(): void {
  173. console.log('点击查看新咨询详情');
  174. // 在实际应用中,这里会跳转到新咨询列表页面或打开新咨询模态框
  175. }
  176. // 待派单数图标点击处理
  177. handlePendingAssignmentsClick(): void {
  178. console.log('点击查看待派单详情');
  179. // 在实际应用中,这里会跳转到待派单列表页面或打开待派单模态框
  180. }
  181. // 异常项目图标点击处理
  182. handleExceptionProjectsClick(): void {
  183. console.log('点击查看异常项目详情');
  184. // 在实际应用中,这里会跳转到异常项目列表页面或打开异常项目模态框
  185. }
  186. // 今日成交额图标点击处理
  187. handleTodayRevenueClick(): void {
  188. console.log('点击查看今日成交额详情');
  189. // 在实际应用中,这里会跳转到今日成交额详情页面或打开今日成交额模态框
  190. }
  191. // 格式化日期
  192. formatDate(date: Date | string): string {
  193. if (!date) return '';
  194. try {
  195. return new Date(date).toLocaleString('zh-CN', {
  196. month: '2-digit',
  197. day: '2-digit',
  198. hour: '2-digit',
  199. minute: '2-digit'
  200. });
  201. } catch (error) {
  202. console.error('日期格式化错误:', error);
  203. return '';
  204. }
  205. }
  206. // 添加安全获取客户名称的方法
  207. getCustomerName(update: Project | CustomerFeedback): string {
  208. if ('customerName' in update && update.customerName) {
  209. return update.customerName;
  210. } else if ('projectId' in update) {
  211. // 查找相关项目获取客户名称
  212. return '客户反馈';
  213. }
  214. return '未知客户';
  215. }
  216. // 优化的日期格式化方法
  217. getFormattedDate(update: Project | CustomerFeedback): string {
  218. if (!update) return '';
  219. if ('createdAt' in update && update.createdAt) {
  220. return this.formatDate(update.createdAt);
  221. } else if ('updatedAt' in update && update.updatedAt) {
  222. return this.formatDate(update.updatedAt);
  223. } else if ('deadline' in update && update.deadline) {
  224. return this.formatDate(update.deadline);
  225. }
  226. return '';
  227. }
  228. // 添加获取状态的安全方法
  229. getUpdateStatus(update: Project | CustomerFeedback): string {
  230. if ('status' in update && update.status) {
  231. return update.status;
  232. }
  233. return '已更新';
  234. }
  235. // 添加getTaskStatus方法的正确实现
  236. getTaskStatus(task: Task): string {
  237. if (!task) return '未知状态';
  238. if (task.isCompleted) return '已完成';
  239. if (task.isOverdue) return '已逾期';
  240. return '进行中';
  241. }
  242. // 添加getUpdateStatusClass方法的正确实现
  243. getUpdateStatusClass(update: Project | CustomerFeedback): string {
  244. if (!update || !('status' in update) || !update.status) return '';
  245. switch (update.status) {
  246. case '进行中':
  247. return 'status-active';
  248. case '已完成':
  249. return 'status-completed';
  250. case '已延期':
  251. case '已暂停':
  252. return 'status-warning';
  253. case '已解决':
  254. return 'status-success';
  255. case '待处理':
  256. case '处理中':
  257. return 'status-info';
  258. default:
  259. return '';
  260. }
  261. }
  262. }