| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646 |
- <!-- 欢迎区域 -->
- <section class="welcome-section">
- <div class="welcome-header">
- <div>
- <h2>您好,{{currentUser?.name}} 👋</h2>
- <p>今天是 {{ currentDate.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long' }) }},祝您工作顺利!</p>
- </div>
- <button class="attendance-view-btn" (click)="viewAttendance()" title="查看人员考勤">
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
- <line x1="16" y1="2" x2="16" y2="6"></line>
- <line x1="8" y1="2" x2="8" y2="6"></line>
- <line x1="3" y1="10" x2="21" y2="10"></line>
- </svg>
- 查看考勤
- </button>
- </div>
- </section>
- <!-- 数据看板 -->
- <section class="stats-dashboard">
- <div class="stats-grid">
- <!-- 项目总数 -->
- <div class="stat-card" (click)="handleTotalProjectsClick()" title="点击查看所有项目">
- <div class="stat-icon primary">
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <path d="M3 3h7v7H3z"></path>
- <path d="M14 3h7v7h-7z"></path>
- <path d="M14 14h7v7h-7z"></path>
- <path d="M3 14h7v7H3z"></path>
- </svg>
- </div>
- <div class="stat-content">
- <div class="stat-value">{{ stats.totalProjects() }}</div>
- <div class="stat-label">项目总数</div>
- </div>
- </div>
- <!-- 新咨询数 - 已隐藏 -->
- <!-- <div class="stat-card" (click)="handleNewConsultationsClick()" title="点击查看新咨询详情">
- <div class="stat-icon secondary">
- <svg width="24" height="24" 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>
- </div>
- <div class="stat-content">
- <div class="stat-value">{{ stats.newConsultations() }}</div>
- <div class="stat-label">新咨询数</div>
- </div>
- </div> -->
- <!-- 待分配项目数 -->
- <div class="stat-card" (click)="handlePendingAssignmentsClick()" title="点击查看待分配项目详情">
- <div class="stat-icon warning">
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
- <polyline points="22 4 12 14.01 9 11.01"></polyline>
- </svg>
- </div>
- <div class="stat-content">
- <div class="stat-value">{{ stats.pendingAssignments() }}</div>
- <div class="stat-label">待分配项目数</div>
- </div>
- </div>
- <!-- 异常项目 -->
- <div class="stat-card" (click)="handleExceptionProjectsClick()" title="点击查看异常项目详情">
- <div class="stat-icon danger">
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
- <line x1="12" y1="9" x2="12" y2="13"></line>
- <line x1="12" y1="17" x2="12.01" y2="17"></line>
- </svg>
- </div>
- <div class="stat-content">
- <div class="stat-value">{{ stats.exceptionProjects() }}</div>
- <div class="stat-label">异常项目</div>
- </div>
- </div>
- <!-- 售后服务 -->
- <div class="stat-card" (click)="handleAfterSalesClick()" title="点击查看售后服务详情">
- <div class="stat-icon success">
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
- </svg>
- </div>
- <div class="stat-content">
- <div class="stat-value">{{ stats.afterSalesCount() }}</div>
- <div class="stat-label">售后服务</div>
- </div>
- </div>
- </div>
- </section>
- <!-- 新客户触达 与 老客户回访 - 暂时隐藏,等待后续功能开发 -->
- <!-- <section class="crm-queues">
- <div class="crm-grid">
- <div class="crm-card">
- <div class="crm-header">
- <div class="crm-title-section">
- <h3>新客户触达</h3>
- <div class="crm-stats">
- <div class="stat-item">
- <span class="stat-number">0</span>
- <span class="stat-label">待触达</span>
- </div>
- <div class="stat-item">
- <span class="stat-number success">0%</span>
- <span class="stat-label">转化率</span>
- </div>
- </div>
- </div>
- <a class="view-all-link" (click)="goToConsultationList()">查看全部</a>
- </div>
- <div class="crm-list">
- <div class="empty-state small">暂无待触达客户</div>
- </div>
- </div>
- <div class="crm-card">
- <div class="crm-header">
- <div class="crm-title-section">
- <h3>老客户回访</h3>
- <div class="crm-stats">
- <div class="stat-item">
- <span class="stat-number">0</span>
- <span class="stat-label">待回访</span>
- </div>
- <div class="stat-item">
- <span class="stat-number warning">0%</span>
- <span class="stat-label">留存率</span>
- </div>
- </div>
- </div>
- <a class="view-all-link" (click)="goToConsultationList()">查看全部</a>
- </div>
- <div class="crm-list">
- <div class="empty-state small">暂无待回访客户</div>
- </div>
- </div>
- </div>
- </section> -->
- <!-- 新增:待跟进尾款项目列表 -->
- <section class="pending-final-payment-section">
- <div class="section-header">
- <h3>待跟进尾款项目</h3>
- <div class="section-stats">
- <span class="stat-badge urgent">{{ pendingFinalPaymentProjects().length }}</span>
- <span class="stat-label">个项目待跟进</span>
- </div>
- </div>
-
- <div class="final-payment-list">
- @if (pendingFinalPaymentProjects().length === 0) {
- <div class="empty-state">
- <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <circle cx="12" cy="12" r="10"></circle>
- <path d="M12 6v6l4 2"></path>
- </svg>
- <p>暂无待跟进尾款项目</p>
- </div>
- }
-
- @for (project of pendingFinalPaymentProjects(); track project.id) {
- <div class="final-payment-item" [class.overdue]="project.status === '已逾期'">
- <div class="project-info">
- <div class="project-header">
- <h4 class="project-name">{{ project.projectName }}</h4>
- <span class="payment-amount highlight">¥{{ project.finalPaymentAmount | number:'1.0-0' }}</span>
- </div>
- <div class="customer-info">
- <div class="customer-details">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" class="icon-user">
- <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
- <circle cx="12" cy="7" r="4"></circle>
- </svg>
- <span class="customer-name">{{ project.customerName }}</span>
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" class="icon-phone">
- <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>
- </svg>
- <span class="customer-phone">{{ project.customerPhone }}</span>
- </div>
- <div class="project-meta">
- <span class="due-date">
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <circle cx="12" cy="12" r="10"></circle>
- <polyline points="12 6 12 12 16 14"></polyline>
- </svg>
- 应付时间:{{ project.dueDate | date:'yyyy-MM-dd' }}
- </span>
- <span class="status-badge" [ngClass]="{'overdue': project.status === '已逾期', 'pending': project.status === '待付款'}">
- {{ project.status }}
- @if (project.overdueDay > 0) {
- <span class="overdue-days">(逾期{{ project.overdueDay }}天)</span>
- }
- </span>
- </div>
- </div>
- </div>
- <div class="payment-actions">
- <button
- class="btn-primary mini"
- (click)="followUpFinalPayment(project.projectId)"
- title="开始跟进客户尾款"
- >
- <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>
- 开始跟进
- </button>
- <button
- class="btn-secondary mini"
- (click)="viewProjectDetail(project.projectId)"
- title="查看项目详情"
- >
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
- <circle cx="12" cy="12" r="3"></circle>
- </svg>
- 查看详情
- </button>
- </div>
- </div>
- }
- </div>
- </section>
- <!-- 紧急待办和项目动态流 -->
- <div class="content-grid">
- <!-- 紧急待办列表 -->
- <section class="urgent-tasks-section">
- <div class="section-header">
- <h3>紧急待办</h3>
- <div style="display: flex; gap: 12px; align-items: center;">
- <button
- class="btn-primary"
- (click)="showTaskForm()"
- style="font-size: 14px; padding: 6px 16px;"
- >
- 添加紧急事项
- </button>
- <a href="/customer-service/project-list" class="view-all-link">查看全部</a>
- </div>
- </div>
-
- <div class="tasks-list">
- @if (urgentTasks().length === 0) {
- <div class="empty-state">
- <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <circle cx="12" cy="12" r="10"></circle>
- <polyline points="12 6 12 12 16 14"></polyline>
- </svg>
- <p>暂无紧急待办事项</p>
- </div>
- }
-
- @for (task of urgentTasks(); track task.id) {
- <div class="task-item-enhanced" [class.completed]="task.isCompleted" [class.overdue]="task.isOverdue">
- <div class="task-priority-indicator" [class.high]="task.priority === 'high'" [class.medium]="task.priority === 'medium'" [class.low]="task.priority === 'low'"></div>
-
- <div class="task-main-content">
- <div class="task-header-row">
- <div class="task-checkbox">
- <input type="checkbox" [checked]="task.isCompleted" (change)="markTaskAsCompleted(task.id)">
- </div>
- <div class="task-info">
- <h4 class="task-title">{{ task.title || '未命名任务' }}</h4>
- <div class="task-tags">
- <span class="tag tag-project">📋 {{ task.projectName || '未知项目' }}</span>
- <span class="tag tag-stage">🔄 {{ task.stage }}</span>
- @if (task.assignee && task.assignee !== '未分配') {
- <span class="tag tag-assignee">👤 {{ task.assignee }}</span>
- }
- </div>
- </div>
- </div>
-
- @if (task.description) {
- <div class="task-description">
- <p>{{ task.description }}</p>
- </div>
- }
-
- <div class="task-meta-row">
- <div class="task-meta-info">
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <circle cx="12" cy="12" r="10"></circle>
- <polyline points="12 6 12 12 16 14"></polyline>
- </svg>
- <span class="task-time" [class.overdue]="task.isOverdue">
- {{ formatDate(task.deadline) }}
- @if (task.isOverdue) {
- <span class="overdue-badge">已逾期</span>
- }
- </span>
- </div>
- <div class="task-priority-badge" [class.high]="task.priority === 'high'" [class.medium]="task.priority === 'medium'" [class.low]="task.priority === 'low'">
- {{ task.priority === 'high' ? '高优先级' : task.priority === 'medium' ? '中优先级' : '低优先级' }}
- </div>
- </div>
-
- <!-- 任务处理状态(使用本地变量 s 消除可选链告警) -->
- <ng-container *ngIf="taskProcessingState()[task.id] as s">
- <ng-container *ngIf="s.inProgress === true">
- <div class="task-progress-container">
- <div class="task-progress-bar" [style.width]="s.progress + '%'">
- <span class="task-progress-text">处理中 {{ s.progress }}%</span>
- </div>
- </div>
- </ng-container>
- </ng-container>
- </div>
-
- <div class="task-actions-row">
- <ng-container *ngIf="taskProcessingState()[task.id] as s; else noTaskActionState">
- <button
- class="btn-action btn-process"
- [class.processing]="s.inProgress === true"
- (click)="handleAssignment(task.id)"
- [disabled]="(s.inProgress === true) || task.isCompleted"
- >
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <polyline points="9 11 12 14 22 4"></polyline>
- <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
- </svg>
- {{ s.inProgress === true ? '处理中' : '处理' }}
- </button>
- </ng-container>
- <ng-template #noTaskActionState>
- <button class="btn-action btn-process" (click)="handleAssignment(task.id)" [disabled]="task.isCompleted">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <polyline points="9 11 12 14 22 4"></polyline>
- <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
- </svg>
- 处理
- </button>
- </ng-template>
-
- <button class="btn-action btn-delete" (click)="deleteTask(task.id)" title="删除任务">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <polyline points="3 6 5 6 21 6"></polyline>
- <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
- </svg>
- </button>
- </div>
- </div>
- }
- </div>
- </section>
- <!-- iOS风格的添加紧急事项面板 -->
- @if (isTaskFormVisible()) {
- <div class="ios-modal-overlay" (click)="hideTaskForm()">
- <div class="ios-panel" (click)="$event.stopPropagation()">
- <div class="ios-panel-header">
- <h3>添加紧急事项</h3>
- <button class="ios-close-button" (click)="hideTaskForm()">
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <line x1="18" y1="6" x2="6" y2="18"></line>
- <line x1="6" y1="6" x2="18" y2="18"></line>
- </svg>
- </button>
- </div>
-
- <div class="ios-panel-content">
- <form (ngSubmit)="handleAddTaskSubmit()">
- <div class="form-group">
- <label for="taskTitle">任务标题 *</label>
- <input
- type="text"
- id="taskTitle"
- [(ngModel)]="newTask.title"
- [ngModelOptions]="{standalone: true}"
- placeholder="请输入任务标题"
- required
- class="ios-input"
- >
- </div>
-
- <div class="form-group">
- <label for="projectSelect">选择项目 *</label>
- <select
- id="projectSelect"
- [(ngModel)]="newTask.projectId"
- [ngModelOptions]="{standalone: true}"
- (change)="onProjectChange($any($event.target).value)"
- required
- class="ios-select"
- >
- <option value="">-- 请选择项目 --</option>
- @for (project of projectList(); track project.id) {
- <option [value]="project.id">{{ project.title }}</option>
- }
- </select>
- </div>
-
- <div class="form-group">
- <label for="spaceSelect">选择空间(可选)</label>
- <select
- id="spaceSelect"
- [(ngModel)]="newTask.spaceId"
- [ngModelOptions]="{standalone: true}"
- class="ios-select"
- [disabled]="!newTask.projectId"
- >
- <option value="">-- 请选择空间 --</option>
- @for (space of spaceList(); track space.id) {
- <option [value]="space.id">{{ space.title }}</option>
- }
- </select>
- </div>
-
- <div class="form-group">
- <label for="projectStage">项目阶段 *</label>
- <select
- id="projectStage"
- [(ngModel)]="newTask.stage"
- [ngModelOptions]="{standalone: true}"
- required
- class="ios-select"
- >
- <option value="订单分配">订单分配</option>
- <option value="慎设需求">慎设需求</option>
- <option value="交付执行">交付执行</option>
- <option value="售后">售后</option>
- </select>
- </div>
-
- <div class="form-group">
- <label for="region">区域/位置(可选)</label>
- <input
- type="text"
- id="region"
- [(ngModel)]="newTask.region"
- [ngModelOptions]="{standalone: true}"
- placeholder="例如:客厅、主卧、厨房"
- class="ios-input"
- >
- </div>
-
- <div class="form-group">
- <label for="taskDeadline">截止时间 *</label>
-
- <!-- 显示当前选择的截止时间 -->
- <div class="deadline-display ios-input" (click)="deadlineDropdownVisible = !deadlineDropdownVisible">
- {{ getDisplayDeadline() || '请选择截止时间' }}
- <span class="dropdown-arrow" [class.rotate]="deadlineDropdownVisible">▼</span>
- </div>
-
- <!-- 预设时长下拉选择框 -->
- <div class="deadline-dropdown" [class.visible]="deadlineDropdownVisible">
- @for (preset of timePresets; track preset.hours) {
- <div class="dropdown-option"
- [class.selected]="selectedPreset === preset.hours.toString()"
- (click)="handlePresetSelection(preset.hours.toString())">
- {{ preset.label }}
- </div>
- }
-
- <!-- 当天24:00前选项 -->
- <div class="dropdown-option"
- [class.selected]="selectedPreset === 'today'"
- (click)="handlePresetSelection('today')">
- 今日24:00前
- </div>
-
- <!-- 自定义时间选项 -->
- <div class="dropdown-divider"></div>
- <div class="dropdown-option custom-option" (click)="handlePresetSelection('custom')">
- 自定义时间
- </div>
- </div>
-
- <!-- 错误提示信息 -->
- @if (deadlineError) {
- <div class="error-message">{{ deadlineError }}</div>
- }
- </div>
-
- <!-- 自定义时间选择弹窗 -->
- @if (isCustomTimeVisible) {
- <div class="custom-time-modal">
- <div class="modal-backdrop"></div>
- <div class="modal-content">
- <div class="modal-header">
- <h3>选择自定义时间</h3>
- <button class="close-button" (click)="closeCustomTimePicker()">×</button>
- </div>
-
- <div class="modal-body">
- <!-- 日期选择 -->
- <div class="date-picker">
- <label>日期</label>
- <input
- type="date"
- [(ngModel)]="customDate"
- min="{{ todayDate }}"
- max="{{ sevenDaysLaterDate }}"
- class="ios-input"
- />
- </div>
-
- <!-- 时间选择 -->
- <div class="time-picker">
- <label>时间</label>
- <input
- type="time"
- [(ngModel)]="customTime"
- class="ios-input"
- />
- </div>
-
- <!-- 错误提示信息 -->
- @if (deadlineError) {
- <div class="error-message">{{ deadlineError }}</div>
- }
- </div>
-
- <div class="modal-footer">
- <button class="cancel-button" (click)="closeCustomTimePicker()">取消</button>
- <button class="confirm-button" (click)="handleCustomTimeSelection()">确定</button>
- </div>
- </div>
- </div>
- }
-
- <div class="form-group">
- <label for="assigneeSelect">指派给(可选)</label>
- <select
- id="assigneeSelect"
- [(ngModel)]="newTask.assigneeId"
- [ngModelOptions]="{standalone: true}"
- class="ios-select"
- >
- <option value="">-- 暂不指派 --</option>
- @for (member of teamMembers(); track member.id) {
- <option [value]="member.id">{{ member.name }}{{ member.roleName ? ' (' + member.roleName + ')' : '' }}</option>
- }
- </select>
- </div>
-
- <div class="form-group">
- <label for="taskPriority">优先级 *</label>
- <select
- id="taskPriority"
- [(ngModel)]="newTask.priority"
- [ngModelOptions]="{standalone: true}"
- class="ios-select"
- >
- <option value="high">🔴 高优先级(紧急)</option>
- <option value="medium">🟡 中优先级(普通)</option>
- <option value="low">🟢 低优先级</option>
- </select>
- </div>
-
- <div class="form-group">
- <label for="taskDescription">详细描述(可选)</label>
- <textarea
- id="taskDescription"
- [(ngModel)]="newTask.description"
- [ngModelOptions]="{standalone: true}"
- placeholder="请详细描述需要紧急处理的问题..."
- rows="4"
- class="ios-textarea"
- ></textarea>
- </div>
- </form>
- </div>
-
- <div class="ios-panel-footer">
- <button type="button" class="ios-cancel-button" (click)="hideTaskForm()">取消</button>
- <button type="button" class="ios-submit-button" (click)="handleAddTaskSubmit()" [disabled]="isSubmitDisabled">确定</button>
- </div>
- </div>
- </div>
- }
- <!-- 项目动态流 -->
- <section class="project-updates-section">
- <div class="section-header">
- <h3>项目动态</h3>
- <div class="search-box">
- <input
- type="text"
- [value]="searchTerm()"
- (input)="searchTerm.set($any($event.target).value)"
- placeholder="搜索动态..."
- class="search-input"
- />
- </div>
- </div>
-
- <div class="updates-list">
- @if (filteredUpdates().length === 0) {
- <div class="empty-state">
- <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <circle cx="12" cy="12" r="10"></circle>
- <line x1="2" y1="12" x2="22" y2="12"></line>
- <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>
- </svg>
- <p>暂无项目动态</p>
- </div>
- }
-
- @for (update of filteredUpdates(); track update) {
- <div class="update-item">
- <div class="update-icon">
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <path d="M22 12h-4l-3 9L9 3l-3 9H2"></path>
- </svg>
- </div>
- <div class="update-content">
- @if (isProjectUpdate(update) && update.name && update.status) {
- <div class="update-title">
- 项目 <strong>{{ update.name }}</strong> 状态更新为 {{ update.status }}
- </div>
- }
- @if (hasContent(update)) {
- <div class="update-title">
- <strong>{{ getCustomerName(update) }}</strong> 提交了反馈
- </div>
- }
- @if (hasContent(update) && getUpdateContent(update)) {
- <p class="update-text">{{ getUpdateContent(update) }}</p>
- }
- <div class="update-meta">
- <span class="update-time">{{ getFormattedDate(update) }}</span>
- <span class="update-status {{ getUpdateStatusClass(update) }}">
- {{ getUpdateStatus(update) }}
- </span>
- </div>
- </div>
- </div>
- }
- </div>
- </section>
- <!-- 回到顶部按钮 -->
- <button class="back-to-top" (click)="scrollToTop()" [class.visible]="showBackToTop()">
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <polyline points="18 15 12 9 6 15"></polyline>
- </svg>
- </button>
|