project-issues-modal.component.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import { Component, EventEmitter, Input, Output, OnInit, OnChanges, SimpleChanges } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { FormsModule } from '@angular/forms';
  4. import { FmodeObject } from 'fmode-ng/parse';
  5. import { ProjectIssueService, ProjectIssue, IssueStatus, IssuePriority, IssueType, IssueCounts } from '../../services/project-issue.service';
  6. @Component({
  7. selector: 'app-project-issues-modal',
  8. standalone: true,
  9. imports: [CommonModule, FormsModule],
  10. templateUrl: './project-issues-modal.component.html',
  11. styleUrls: ['./project-issues-modal.component.scss']
  12. })
  13. export class ProjectIssuesModalComponent implements OnInit, OnChanges {
  14. @Input() project: FmodeObject | null = null;
  15. @Input() currentUser: FmodeObject | null = null;
  16. @Input() isVisible: boolean = false;
  17. @Output() close = new EventEmitter<void>();
  18. issues: ProjectIssue[] = [];
  19. counts: IssueCounts = { total: 0, open: 0, in_progress: 0, resolved: 0, closed: 0 };
  20. filterStatus: IssueStatus[] = [];
  21. searchText: string = '';
  22. // 创建表单
  23. creating: boolean = false;
  24. newTitle: string = '';
  25. newDescription: string = '';
  26. newPriority: IssuePriority = 'medium';
  27. newType: IssueType = 'task';
  28. newDueDate?: string;
  29. newTagsText: string = '';
  30. loading: boolean = false;
  31. error: string | null = null;
  32. constructor(private issueService: ProjectIssueService) {}
  33. ngOnInit() {
  34. this.refresh();
  35. }
  36. ngOnChanges(changes: SimpleChanges): void {
  37. if (changes['isVisible'] && this.isVisible) {
  38. this.refresh();
  39. }
  40. if (changes['project'] && this.project) {
  41. this.refresh();
  42. }
  43. }
  44. refresh() {
  45. if (!this.project?.id) return;
  46. try {
  47. this.loading = true;
  48. // 首次种子数据(仅内存,无副作用)
  49. this.issueService.seed(this.project.id);
  50. this.issues = this.issueService.listIssues(this.project.id, {
  51. status: this.filterStatus,
  52. text: this.searchText
  53. });
  54. this.counts = this.issueService.getCounts(this.project.id);
  55. this.loading = false;
  56. } catch (err: any) {
  57. this.error = err.message || '加载问题列表失败';
  58. this.loading = false;
  59. }
  60. }
  61. toggleStatusFilter(status: IssueStatus) {
  62. const idx = this.filterStatus.indexOf(status);
  63. if (idx >= 0) this.filterStatus.splice(idx, 1); else this.filterStatus.push(status);
  64. this.refresh();
  65. }
  66. onSearchChange() {
  67. this.refresh();
  68. }
  69. startCreate() {
  70. this.creating = true;
  71. this.newTitle = '';
  72. this.newDescription = '';
  73. this.newPriority = 'medium';
  74. this.newType = 'task';
  75. this.newDueDate = undefined;
  76. this.newTagsText = '';
  77. }
  78. cancelCreate() {
  79. this.creating = false;
  80. }
  81. submitCreate() {
  82. if (!this.project?.id || !this.currentUser?.id) return;
  83. if (!this.newTitle.trim()) return;
  84. const tags = this.newTagsText
  85. .split(',')
  86. .map(t => t.trim())
  87. .filter(Boolean);
  88. const due = this.newDueDate ? new Date(this.newDueDate) : undefined;
  89. this.issueService.createIssue(this.project.id, {
  90. title: this.newTitle.trim(),
  91. description: this.newDescription.trim(),
  92. priority: this.newPriority,
  93. type: this.newType,
  94. creatorId: this.currentUser.id,
  95. assigneeId: undefined,
  96. dueDate: due,
  97. tags
  98. });
  99. this.creating = false;
  100. this.refresh();
  101. }
  102. setStatus(issue: ProjectIssue, status: IssueStatus) {
  103. if (!this.project?.id) return;
  104. this.issueService.setStatus(this.project.id, issue.id, status);
  105. this.refresh();
  106. }
  107. deleteIssue(issue: ProjectIssue) {
  108. if (!this.project?.id) return;
  109. this.issueService.deleteIssue(this.project.id, issue.id);
  110. this.refresh();
  111. }
  112. addComment(issue: ProjectIssue, text: string) {
  113. if (!this.project?.id || !this.currentUser?.id) return;
  114. const content = text.trim();
  115. if (!content) return;
  116. this.issueService.addComment(this.project.id, issue.id, this.currentUser.id, content);
  117. this.refresh();
  118. }
  119. onClose() {
  120. this.close.emit();
  121. }
  122. }