project-management.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. import { Component, OnInit, signal } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { RouterModule } from '@angular/router';
  4. import { FormsModule } from '@angular/forms';
  5. import { MatButtonModule } from '@angular/material/button';
  6. import { MatIconModule } from '@angular/material/icon';
  7. import { MatTableModule } from '@angular/material/table';
  8. import { MatInputModule } from '@angular/material/input';
  9. import { MatSelectModule } from '@angular/material/select';
  10. import { MatPaginatorModule } from '@angular/material/paginator';
  11. import { MatDialogModule, MatDialog } from '@angular/material/dialog';
  12. import { MatSortModule } from '@angular/material/sort';
  13. import { ProjectDialogComponent } from './project-dialog/project-dialog';
  14. interface Project {
  15. id: string;
  16. name: string;
  17. customer: string;
  18. status: 'pending' | 'in-progress' | 'completed' | 'on-hold';
  19. designer: string;
  20. startDate: string;
  21. endDate: string;
  22. budget: number;
  23. progress: number;
  24. }
  25. @Component({
  26. selector: 'app-project-management',
  27. standalone: true,
  28. imports: [
  29. CommonModule,
  30. RouterModule,
  31. FormsModule,
  32. MatButtonModule,
  33. MatIconModule,
  34. MatTableModule,
  35. MatInputModule,
  36. MatSelectModule,
  37. MatPaginatorModule,
  38. MatDialogModule,
  39. MatSortModule,
  40. ProjectDialogComponent
  41. ],
  42. templateUrl: './project-management.html',
  43. styleUrl: './project-management.scss'
  44. })
  45. export class ProjectManagement implements OnInit {
  46. projects = signal<Project[]>([]);
  47. filteredProjects = signal<Project[]>([]);
  48. searchTerm = '';
  49. statusFilter = '';
  50. sortColumn = 'startDate';
  51. sortDirection = 'desc';
  52. pageSize = 10;
  53. currentPage = 0;
  54. // 状态颜色映射
  55. statusColors: Record<string, string> = {
  56. 'pending': '#FFAA00',
  57. 'in-progress': '#165DFF',
  58. 'completed': '#00B42A',
  59. 'on-hold': '#F53F3F'
  60. };
  61. // 状态文本映射
  62. statusTexts: Record<string, string> = {
  63. 'pending': '待开始',
  64. 'in-progress': '进行中',
  65. 'completed': '已完成',
  66. 'on-hold': '暂停中'
  67. };
  68. constructor(private dialog: MatDialog) {}
  69. ngOnInit(): void {
  70. this.loadProjects();
  71. }
  72. loadProjects(): void {
  73. // 模拟项目数据
  74. this.projects.set([
  75. {
  76. id: '1',
  77. name: '现代简约风格三居室设计',
  78. customer: '王先生',
  79. status: 'in-progress',
  80. designer: '张设计师',
  81. startDate: '2025-09-01',
  82. endDate: '2025-10-15',
  83. budget: 85000,
  84. progress: 65
  85. },
  86. {
  87. id: '2',
  88. name: '北欧风格两居室设计',
  89. customer: '李女士',
  90. status: 'completed',
  91. designer: '刘设计师',
  92. startDate: '2025-08-15',
  93. endDate: '2025-09-30',
  94. budget: 68000,
  95. progress: 100
  96. },
  97. {
  98. id: '3',
  99. name: '工业风办公室设计',
  100. customer: '赵先生',
  101. status: 'pending',
  102. designer: '王设计师',
  103. startDate: '2025-09-15',
  104. endDate: '2025-11-05',
  105. budget: 120000,
  106. progress: 0
  107. },
  108. {
  109. id: '4',
  110. name: '新中式别墅设计',
  111. customer: '钱女士',
  112. status: 'on-hold',
  113. designer: '张设计师',
  114. startDate: '2025-08-20',
  115. endDate: '2025-11-20',
  116. budget: 250000,
  117. progress: 40
  118. },
  119. {
  120. id: '5',
  121. name: '日式小户型设计',
  122. customer: '孙先生',
  123. status: 'in-progress',
  124. designer: '刘设计师',
  125. startDate: '2025-09-05',
  126. endDate: '2025-10-20',
  127. budget: 55000,
  128. progress: 35
  129. },
  130. {
  131. id: '6',
  132. name: '美式乡村风格设计',
  133. customer: '周女士',
  134. status: 'in-progress',
  135. designer: '王设计师',
  136. startDate: '2025-09-08',
  137. endDate: '2025-10-30',
  138. budget: 75000,
  139. progress: 25
  140. },
  141. {
  142. id: '7',
  143. name: '极简主义公寓设计',
  144. customer: '吴先生',
  145. status: 'pending',
  146. designer: '张设计师',
  147. startDate: '2025-09-20',
  148. endDate: '2025-11-10',
  149. budget: 60000,
  150. progress: 0
  151. },
  152. {
  153. id: '8',
  154. name: '地中海风格别墅设计',
  155. customer: '郑女士',
  156. status: 'completed',
  157. designer: '刘设计师',
  158. startDate: '2025-08-01',
  159. endDate: '2025-09-15',
  160. budget: 180000,
  161. progress: 100
  162. },
  163. {
  164. id: '9',
  165. name: '后现代风格办公室设计',
  166. customer: '王总',
  167. status: 'in-progress',
  168. designer: '王设计师',
  169. startDate: '2025-08-25',
  170. endDate: '2025-10-15',
  171. budget: 95000,
  172. progress: 50
  173. },
  174. {
  175. id: '10',
  176. name: '新古典主义住宅设计',
  177. customer: '陈女士',
  178. status: 'on-hold',
  179. designer: '张设计师',
  180. startDate: '2025-08-10',
  181. endDate: '2025-11-01',
  182. budget: 130000,
  183. progress: 30
  184. }
  185. ]);
  186. this.applyFilters();
  187. }
  188. applyFilters(): void {
  189. let result = [...this.projects()];
  190. // 搜索过滤
  191. if (this.searchTerm) {
  192. const term = this.searchTerm.toLowerCase();
  193. result = result.filter(project =>
  194. project.name.toLowerCase().includes(term) ||
  195. project.customer.toLowerCase().includes(term) ||
  196. project.designer.toLowerCase().includes(term)
  197. );
  198. }
  199. // 状态过滤
  200. if (this.statusFilter) {
  201. result = result.filter(project => project.status === this.statusFilter);
  202. }
  203. // 排序
  204. result.sort((a, b) => {
  205. if (this.sortColumn === 'startDate' || this.sortColumn === 'endDate') {
  206. const dateA = new Date(a[this.sortColumn]).getTime();
  207. const dateB = new Date(b[this.sortColumn]).getTime();
  208. return this.sortDirection === 'asc' ? dateA - dateB : dateB - dateA;
  209. } else if (this.sortColumn === 'budget' || this.sortColumn === 'progress') {
  210. return this.sortDirection === 'asc' ? a[this.sortColumn] - b[this.sortColumn] : b[this.sortColumn] - a[this.sortColumn];
  211. } else {
  212. const valueA = a[this.sortColumn as keyof Project]?.toString().toLowerCase() || '';
  213. const valueB = b[this.sortColumn as keyof Project]?.toString().toLowerCase() || '';
  214. return this.sortDirection === 'asc' ? valueA.localeCompare(valueB) : valueB.localeCompare(valueA);
  215. }
  216. });
  217. this.filteredProjects.set(result);
  218. }
  219. onSearch(): void {
  220. this.applyFilters();
  221. }
  222. onStatusFilterChange(): void {
  223. this.applyFilters();
  224. }
  225. onSort(column: string): void {
  226. if (this.sortColumn === column) {
  227. this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
  228. } else {
  229. this.sortColumn = column;
  230. this.sortDirection = 'asc';
  231. }
  232. this.applyFilters();
  233. }
  234. openProjectDialog(project?: Project): void {
  235. const dialogRef = this.dialog.open(ProjectDialogComponent, {
  236. width: '600px',
  237. data: project ? { ...project } : {
  238. id: '',
  239. name: '',
  240. client: '',
  241. designer: '',
  242. status: 'pending',
  243. priority: 'medium',
  244. startDate: new Date().toISOString().split('T')[0],
  245. endDate: '',
  246. budget: 0,
  247. progress: 0,
  248. description: '',
  249. tasks: []
  250. }
  251. });
  252. dialogRef.afterClosed().subscribe(result => {
  253. if (result) {
  254. if (result.id) {
  255. // 更新项目
  256. this.updateProject(result);
  257. } else {
  258. // 创建新项目
  259. this.createProject(result);
  260. }
  261. }
  262. });
  263. }
  264. createProject(projectData: Omit<Project, 'id'>): void {
  265. const newProject: Project = {
  266. ...projectData,
  267. id: (this.projects().length + 1).toString()
  268. };
  269. this.projects.set([newProject, ...this.projects()]);
  270. this.applyFilters();
  271. }
  272. updateProject(updatedProject: Project): void {
  273. this.projects.set(this.projects().map(project =>
  274. project.id === updatedProject.id ? updatedProject : project
  275. ));
  276. this.applyFilters();
  277. }
  278. deleteProject(id: string): void {
  279. if (confirm('确定要删除这个项目吗?')) {
  280. this.projects.set(this.projects().filter(project => project.id !== id));
  281. this.applyFilters();
  282. }
  283. }
  284. formatCurrency(amount: number): string {
  285. return new Intl.NumberFormat('zh-CN', {
  286. style: 'currency',
  287. currency: 'CNY',
  288. minimumFractionDigits: 0
  289. }).format(amount);
  290. }
  291. get paginatedProjects(): Project[] {
  292. const startIndex = this.currentPage * this.pageSize;
  293. return this.filteredProjects().slice(startIndex, startIndex + this.pageSize);
  294. }
  295. get totalPages(): number {
  296. return Math.ceil(this.filteredProjects().length / this.pageSize);
  297. }
  298. onPageChange(page: number): void {
  299. this.currentPage = page;
  300. }
  301. }