| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 | <div class="project-list-container">  <!-- 右侧主要内容 -->  <div class="project-content">      <!-- 页面标题和操作 -->      <div class="page-header">        <h2>项目看板</h2>        <div class="header-actions">          <div class="search-container">            <div class="search-box">              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">                <circle cx="11" cy="11" r="8"></circle>                <line x1="21" y1="21" x2="16.65" y2="16.65"></line>              </svg>              <input                 type="text"                   placeholder="搜索项目名称或客户..."                  [value]="searchTerm()"                  (input)="searchTerm.set($any($event.target).value)"                  (keyup.enter)="onSearch()"              >              <button class="search-btn" (click)="onSearch()">                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">                  <line x1="5" y1="12" x2="19" y2="12"></line>                  <polyline points="12 5 19 12 12 19"></polyline>                </svg>              </button>            </div>          </div>          <div class="view-toggle">            <button [class.active]="viewMode() === 'card'" (click)="toggleView('card')">卡片</button>            <button [class.active]="viewMode() === 'list'" (click)="toggleView('list')">列表</button>            <button [class.active]="viewMode() === 'dashboard'" (click)="toggleView('dashboard')">监控大盘</button>          </div>          <button class="add-project-btn" (click)="navigateToCreateOrder()">            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">              <line x1="12" y1="5" x2="12" y2="19"></line>              <line x1="5" y1="12" x2="19" y2="12"></line>            </svg>            添加项目          </button>        </div>      </div>      <!-- 筛选和排序工具栏 -->      <div class="filter-toolbar">        <div class="filter-group">          <label>状态筛选</label>          <select (change)="onStatusChange($event)" [value]="statusFilter()">            @for (option of statusOptions; track option.value) {              <option [value]="option.value">{{ option.label }}</option>            }          </select>        </div>        <div class="filter-group">          <label>阶段筛选</label>          <select (change)="onStageChange($event)" [value]="stageFilter()">            @for (option of stageOptions; track option.value) {              <option [value]="option.value">{{ option.label }}</option>            }          </select>        </div>        <div class="filter-group">          <label>排序方式</label>          <select (change)="onSortChange($event)" [value]="sortBy()">            @for (option of sortOptions; track option.value) {              <option [value]="option.value">{{ option.label }}</option>            }          </select>        </div>        <div class="filter-results">          <span>共 {{ projects().length }} 个项目</span>        </div>      </div>      <!-- 加载状态 -->      @if (isLoading()) {        <div class="loading-container">          <div class="loading-spinner"></div>          <p>正在加载项目数据...</p>        </div>      }      <!-- 错误状态 -->      @if (loadError()) {        <div class="error-container">          <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor">            <circle cx="12" cy="12" r="10"></circle>            <line x1="12" y1="8" x2="12" y2="12"></line>            <line x1="12" y1="16" x2="12.01" y2="16"></line>          </svg>          <p>{{ loadError() }}</p>          <button class="retry-btn" (click)="loadProjects()">重试</button>        </div>      }      <!-- 视图:卡片模式(看板) -->      @if (viewMode() === 'card' && !isLoading() && !loadError()) {        <div class="kanban-container">          <div class="kanban-scroll">            <!-- 列头 -->            <div class="kanban-header">              @for (col of columns; track col.id) {                <div class="kanban-column-header" [attr.data-col]="col.id">                  <h3 class="column-title">{{ col.name }}</h3>                  <span class="stage-count">{{ getProjectsByColumn(col.id).length }}</span>                </div>              }            </div>            <!-- 列体 -->            <div class="kanban-body">              @for (col of columns; track col.id) {                <div class="kanban-column" [attr.data-col]="col.id">                  @for (project of getProjectsByColumn(col.id); track project.id) {                    <div class="kanban-card" (click)="navigateToProject(project, col.id)">                      <div class="kanban-card-header">                        <div class="left">                          <h4 class="project-name">{{ project.name }}</h4>                          <span class="project-id">#{{ project.id }}</span>                        </div>                        <div class="right">                          @if (col.id === 'order') {                            <span class="pending-badge">待分配</span>                          }                          <span class="project-tag">{{ project.tagDisplayText }}</span>                          @if (project.isUrgent) {                            <span class="urgent-tag">紧急</span>                          }                        </div>                      </div>                      <div class="kanban-card-content">                        <p class="customer">客户:{{ project.customerName }}</p>                        <p class="assignee">设计师:{{ project.assigneeName || '未分配' }}</p>                        <p class="stage">阶段:<span class="stage-badge" [class]="getStageClass(project.currentStage)">{{ project.currentStage }}</span></p>                        <div class="progress-line">                          <div class="progress-bar">                            <div class="progress-fill" [style.width.percent]="project.progress"></div>                          </div>                          <span class="progress-text">{{ project.progress }}%</span>                        </div>                        <p class="deadline" [class.overdue]="project.daysUntilDeadline < 0" [class.urgent]="project.isUrgent">                          截止:{{ formatDate(project.deadline) }}                        </p>                      </div>                      <div class="kanban-card-footer">                        <button class="btn-link" (click)="$event.stopPropagation(); navigateToProject(project, col.id)">进入</button>                        <button class="btn-link" (click)="$event.stopPropagation(); navigateToMessages(project)">沟通管理</button>                      </div>                    </div>                  }                  @if (getProjectsByColumn(col.id).length === 0) {                    <div class="empty-column">                      <span class="empty-icon">📦</span>                      <p>暂无项目</p>                    </div>                  }                 </div>               }            </div>          </div>        </div>      }      <!-- 视图:列表模式(沿用原卡片列表 + 分页) -->      @if (viewMode() === 'list') {        <div class="project-grid">          @for (project of paginatedProjects(); track project.id) {            <div class="project-card">              <div class="card-header">                <div class="card-title-section">                  <h3 class="project-name">{{ project.name }}</h3>                  <span class="project-id">#{{ project.id }}</span>                </div>                <div class="card-tags">                  <span class="project-tag">{{ project.tagDisplayText }}</span>                  @if (project.isUrgent) { <span class="urgent-tag">紧急</span> }                </div>              </div>              <div class="card-content">                <div class="info-item">                  <span class="info-label">客户</span>                  <span class="info-value">{{ project.customerName }}</span>                </div>                <div class="info-item">                  <span class="info-label">设计师</span>                  <span class="info-value">{{ project.assigneeName || '未分配' }}</span>                </div>                <div class="info-item">                  <span class="info-label">状态</span>                  <span class="info-value status-badge" [class]="getStatusClass(project.status)">{{ project.status }}</span>                </div>                <div class="info-item">                  <span class="info-label">阶段</span>                  <span class="info-value stage-badge" [class]="getStageClass(project.currentStage)">{{ project.currentStage }}</span>                </div>                <div class="progress-section">                  <div class="progress-header">                    <span class="progress-label">进度</span>                    <span class="progress-percentage">{{ project.progress }}%</span>                  </div>                  <div class="progress-bar">                    <div class="progress-fill" [style.width.percent]="project.progress" [style.backgroundColor]="project.status === '进行中' ? '#1976d2' : '#757575'"></div>                  </div>                </div>                <div class="timeline-info">                  <div class="time-item">                    <span class="time-label">创建时间</span>                    <span class="time-value">{{ formatDate(project.createdAt) }}</span>                  </div>                  <div class="time-item">                    <span class="time-label">截止日期</span>                    <span class="time-value deadline" [class.overdue]="project.daysUntilDeadline < 0" [class.urgent]="project.isUrgent">                      {{ formatDate(project.deadline) }} ({{ project.daysUntilDeadline >= 0 ? '还有' + project.daysUntilDeadline + '天' : '已逾期' + getAbsValue(project.daysUntilDeadline) + '天' }})                    </span>                  </div>                </div>                @if (project.highPriorityNeeds && project.highPriorityNeeds.length > 0) {                  <div class="needs-section">                    <span class="needs-label">高优先级需求:</span>                    <ul class="needs-list">                      @for (need of project.highPriorityNeeds; track $index) {                        <li>                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">                            <circle cx="12" cy="12" r="10"></circle>                            <line x1="12" y1="8" x2="12" y2="12"></line>                            <line x1="12" y1="16" x2="12.01" y2="16"></line>                          </svg>                          {{ need }}                        </li>                      }                    </ul>                  </div>                }              </div>              <div class="card-footer">                <button class="secondary-btn card-action" (click)="navigateToMessages(project)">                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">                    <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>                  </svg>                  <span>沟通管理</span>                </button>                <button class="primary-btn card-action" (click)="navigateToProject(project, getColumnIdForProject(project))">                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">                    <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>                  </svg>                  <span>查看详情</span>                </button>              </div>            </div>          }        </div>        <!-- 分页控件 -->        @if (totalPages() > 1) {          <div class="pagination">            <button class="pagination-btn" (click)="prevPage()" [disabled]="currentPage() === 1">              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">                <line x1="15" y1="18" x2="9" y2="12"></line>                <line x1="9" y1="18" x2="15" y2="12"></line>              </svg>            </button>            @for (page of pageNumbers(); track page) {              <button class="pagination-btn" [class.active]="page === currentPage()" (click)="goToPage(page)">{{ page }}</button>            }            @if (totalPages() > 5) { <span class="pagination-ellipsis">...</span> }            @if (totalPages() > 5) {              <button class="pagination-btn" (click)="goToPage(totalPages())">{{ totalPages() }}</button>            }            <button class="pagination-btn" (click)="nextPage()" [disabled]="currentPage() === totalPages()">              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">                <line x1="9" y1="18" x2="15" y2="12"></line>                <line x1="15" y1="6" x2="9" y2="12"></line>              </svg>            </button>          </div>        }      }      <!-- 视图:监控大盘模式 -->      @if (viewMode() === 'dashboard') {        <div class="dashboard-container">          <!-- 使用iframe嵌入组长端监控大盘,通过CSS隐藏待办任务栏 -->          <iframe             src="/team-leader/dashboard"             frameborder="0"             width="100%"             height="100%"            style="min-height: 800px;"            title="项目监控大盘"            onload="const doc = this.contentDocument; doc.querySelector('.todo-section').style.display = 'none'; const header = doc.querySelector('.dashboard-header h1'); if (header && header.textContent.includes('设计组长工作台')) { header.style.display = 'none'; doc.querySelector('.dashboard-metrics').style.marginTop = '20px'; }">          </iframe>        </div>      }    </div>
 |