123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- import { Component, EventEmitter, Input, Output, OnInit, OnChanges, SimpleChanges } from '@angular/core';
- import { CommonModule } from '@angular/common';
- import { FormsModule } from '@angular/forms';
- import { FmodeObject } from 'fmode-ng/parse';
- import { ProjectIssueService, ProjectIssue, IssueStatus, IssuePriority, IssueType, IssueCounts } from '../../services/project-issue.service';
- @Component({
- selector: 'app-project-issues-modal',
- standalone: true,
- imports: [CommonModule, FormsModule],
- templateUrl: './project-issues-modal.component.html',
- styleUrls: ['./project-issues-modal.component.scss']
- })
- export class ProjectIssuesModalComponent implements OnInit, OnChanges {
- @Input() project: FmodeObject | null = null;
- @Input() currentUser: FmodeObject | null = null;
- @Input() isVisible: boolean = false;
- @Output() close = new EventEmitter<void>();
- issues: ProjectIssue[] = [];
- counts: IssueCounts = { total: 0, open: 0, in_progress: 0, resolved: 0, closed: 0 };
- filterStatus: IssueStatus[] = [];
- searchText: string = '';
- // 创建表单
- creating: boolean = false;
- newTitle: string = '';
- newDescription: string = '';
- newPriority: IssuePriority = 'medium';
- newType: IssueType = 'task';
- newDueDate?: string;
- newTagsText: string = '';
- loading: boolean = false;
- error: string | null = null;
- constructor(private issueService: ProjectIssueService) {}
- ngOnInit() {
- this.refresh();
- }
- ngOnChanges(changes: SimpleChanges): void {
- if (changes['isVisible'] && this.isVisible) {
- this.refresh();
- }
- if (changes['project'] && this.project) {
- this.refresh();
- }
- }
- refresh() {
- if (!this.project?.id) return;
- try {
- this.loading = true;
- // 首次种子数据(仅内存,无副作用)
- this.issueService.seed(this.project.id);
- this.issues = this.issueService.listIssues(this.project.id, {
- status: this.filterStatus,
- text: this.searchText
- });
- this.counts = this.issueService.getCounts(this.project.id);
- this.loading = false;
- } catch (err: any) {
- this.error = err.message || '加载问题列表失败';
- this.loading = false;
- }
- }
- toggleStatusFilter(status: IssueStatus) {
- const idx = this.filterStatus.indexOf(status);
- if (idx >= 0) this.filterStatus.splice(idx, 1); else this.filterStatus.push(status);
- this.refresh();
- }
- onSearchChange() {
- this.refresh();
- }
- startCreate() {
- this.creating = true;
- this.newTitle = '';
- this.newDescription = '';
- this.newPriority = 'medium';
- this.newType = 'task';
- this.newDueDate = undefined;
- this.newTagsText = '';
- }
- cancelCreate() {
- this.creating = false;
- }
- submitCreate() {
- if (!this.project?.id || !this.currentUser?.id) return;
- if (!this.newTitle.trim()) return;
- const tags = this.newTagsText
- .split(',')
- .map(t => t.trim())
- .filter(Boolean);
- const due = this.newDueDate ? new Date(this.newDueDate) : undefined;
- this.issueService.createIssue(this.project.id, {
- title: this.newTitle.trim(),
- description: this.newDescription.trim(),
- priority: this.newPriority,
- type: this.newType,
- creatorId: this.currentUser.id,
- assigneeId: undefined,
- dueDate: due,
- tags
- });
- this.creating = false;
- this.refresh();
- }
- setStatus(issue: ProjectIssue, status: IssueStatus) {
- if (!this.project?.id) return;
- this.issueService.setStatus(this.project.id, issue.id, status);
- this.refresh();
- }
- deleteIssue(issue: ProjectIssue) {
- if (!this.project?.id) return;
- this.issueService.deleteIssue(this.project.id, issue.id);
- this.refresh();
- }
- addComment(issue: ProjectIssue, text: string) {
- if (!this.project?.id || !this.currentUser?.id) return;
- const content = text.trim();
- if (!content) return;
- this.issueService.addComment(this.project.id, issue.id, this.currentUser.id, content);
- this.refresh();
- }
- onClose() {
- this.close.emit();
- }
- }
|