Kaynağa Gözat

Merge branch 'master' of http://git.fmode.cn:3000/nkkj/yss-project

徐福静0235668 6 saat önce
ebeveyn
işleme
81530aefdf

+ 295 - 197
src/app/pages/customer-service/project-detail/project-detail.html

@@ -64,18 +64,16 @@
         <div class="record-section">
           <h4>过往咨询记录</h4>
           <div class="consultation-list">
-            @for (record of consultationRecords(); track $index) {
-              <div class="consultation-item">
-                <div class="consultation-date">{{ formatDate(record.date) }}</div>
-                <div class="consultation-content">{{ record.content }}</div>
-                <div class="consultation-status"
-                     [class.status-processed]="record.status === '已解决' || record.status === '成功'"
-                     [class.status-processing]="record.status === '处理中'"
-                     [class.status-pending]="record.status === '待处理'">
-                  {{ record.status }}
-                </div>
+            <div class="consultation-item" *ngFor="let record of consultationRecords()">
+              <div class="consultation-date">{{ formatDate(record.date) }}</div>
+              <div class="consultation-content">{{ record.content }}</div>
+              <div class="consultation-status"
+                   [class.status-processed]="record.status === '已解决' || record.status === '成功'"
+                   [class.status-processing]="record.status === '处理中'"
+                   [class.status-pending]="record.status === '待处理'">
+                {{ record.status }}
               </div>
-            }
+            </div>
           </div>
         </div>
         
@@ -83,14 +81,12 @@
         <div class="record-section">
           <h4>合作项目</h4>
           <div class="projects-list">
-            @for (proj of cooperationProjects(); track $index) {
-              <div class="project-item">
-                <div class="project-name">{{ proj.name }}</div>
-                <div class="project-period">{{ formatDate(proj.startDate) }} - {{ formatDate(proj.endDate) }}</div>
-                <div class="project-description">{{ proj.description }}</div>
-                <div class="project-status">{{ proj.status }}</div>
-              </div>
-            }
+            <div class="project-item" *ngFor="let proj of cooperationProjects()">
+              <div class="project-name">{{ proj.name }}</div>
+              <div class="project-period">{{ formatDate(proj.startDate) }} - {{ formatDate(proj.endDate) }}</div>
+              <div class="project-description">{{ proj.description }}</div>
+              <div class="project-status">{{ proj.status }}</div>
+            </div>
           </div>
         </div>
         
@@ -98,24 +94,18 @@
         <div class="record-section">
           <h4>历史反馈/评价</h4>
           <div class="feedback-list">
-            @for (feedback of historicalFeedbacks(); track $index) {
-              <div class="feedback-item">
-                <div class="feedback-date">{{ formatDate(feedback.date) }}</div>
-                <div class="feedback-rating">
-                  @for (star of [1,2,3,4,5]; track $index) {
-                    <span>
-                      <i class="fa" [ngClass]="{ 'fa-star': star <= feedback.rating, 'fa-star-o': star > feedback.rating }"></i>
-                    </span>
-                  }
-                </div>
-                <div class="feedback-content">{{ feedback.content }}</div>
-                @if (feedback.response) {
-                  <div class="feedback-response">
-                    <strong>回复:</strong>{{ feedback.response }}
-                  </div>
-                }
+            <div class="feedback-item" *ngFor="let feedback of historicalFeedbacks()">
+              <div class="feedback-date">{{ formatDate(feedback.date) }}</div>
+              <div class="feedback-rating">
+                <span *ngFor="let star of [1,2,3,4,5]">
+                  <i class="fa" [ngClass]="{ 'fa-star': star <= feedback.rating, 'fa-star-o': star > feedback.rating }"></i>
+                </span>
               </div>
-            }
+              <div class="feedback-content">{{ feedback.content }}</div>
+              <div class="feedback-response" *ngIf="feedback.response">
+                <strong>回复:</strong>{{ feedback.response }}
+              </div>
+            </div>
           </div>
         </div>
       </div>
@@ -124,44 +114,34 @@
       <div class="card timeline-card">
         <h3 class="card-title">项目阶段时间轴</h3>
         <div class="project-timeline">
-          @for (stage of projectStages; let i = $index; track $index) {
-            <div class="timeline-item" [class.stage-completed]="stage.completed" [class.stage-in-progress]="stage.inProgress">
-              <div class="timeline-icon" [class.icon-completed]="stage.completed" [class.icon-in-progress]="stage.inProgress">
-                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                  <circle cx="12" cy="12" r="9"></circle>
-                  @if (stage.completed) {
-                    <path d="m5 12 5 5 10-10" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
-                  }
-                  @if (stage.inProgress) {
-                    <circle cx="12" cy="12" r="5"></circle>
-                  }
-                </svg>
+          <div *ngFor="let stage of projectStages; index as i" class="timeline-item" [class.stage-completed]="stage.completed" [class.stage-in-progress]="stage.inProgress">
+            <div class="timeline-icon" [class.icon-completed]="stage.completed" [class.icon-in-progress]="stage.inProgress">
+              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                <circle cx="12" cy="12" r="9"></circle>
+                <path *ngIf="stage.completed" d="m5 12 5 5 10-10" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
+                <circle *ngIf="stage.inProgress" cx="12" cy="12" r="5"></circle>
+              </svg>
+            </div>
+            <div class="timeline-line" *ngIf="i < projectStages.length - 1" [class.line-completed]="stage.completed && projectStages[i+1].completed"></div>
+            <div class="timeline-content">
+              <div class="timeline-header">
+                <h4 class="stage-title">{{ stage.name }}</h4>
+                <span class="stage-status">
+                  {{ stage.completed ? '已完成' : stage.inProgress ? '进行中' : '未开始' }}
+                </span>
               </div>
-              @if (i < projectStages.length - 1) {
-                <div class="timeline-line" [class.line-completed]="stage.completed && projectStages[i+1].completed"></div>
-              }
-              <div class="timeline-content">
-                <div class="timeline-header">
-                  <h4 class="stage-title">{{ stage.name }}</h4>
-                  <span class="stage-status">
-                    {{ stage.completed ? '已完成' : stage.inProgress ? '进行中' : '未开始' }}
-                  </span>
-                </div>
-                <div class="timeline-meta">
-                  <div class="stage-dates">
-                    @if (stage.startDate) { <span class="date-item">开始:{{ formatDate(stage.startDate) }}</span> }
-                    @if (stage.endDate) { <span class="date-item">完成:{{ formatDate(stage.endDate) }}</span> }
-                  </div>
-                  <div class="stage-responsible">负责人:{{ stage.responsible || '未分配' }}</div>
+              <div class="timeline-meta">
+                <div class="stage-dates">
+                  <span *ngIf="stage.startDate" class="date-item">开始:{{ formatDate(stage.startDate) }}</span>
+                  <span *ngIf="stage.endDate" class="date-item">完成:{{ formatDate(stage.endDate) }}</span>
                 </div>
-                @if (stage.details) {
-                  <div class="stage-details">
-                    <p>{{ stage.details }}</p>
-                  </div>
-                }
+                <div class="stage-responsible">负责人:{{ stage.responsible || '未分配' }}</div>
+              </div>
+              <div class="stage-details" *ngIf="stage.details">
+                <p>{{ stage.details }}</p>
               </div>
             </div>
-          }
+          </div>
         </div>
       </div>
 
@@ -202,142 +182,262 @@
             </svg>
             <span>文件</span>
           </button>
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'members'" (click)="switchTab('members')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
-              <circle cx="9" cy="7" r="4"></circle>
-              <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
-              <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
-            </svg>
-            <span>组员</span>
-          </button>
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'requirements'" (click)="switchTab('requirements')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M9 11l3 3L22 4"></path>
-              <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
-            </svg>
-            <span>需求</span>
-          </button>
         </div>
 
-        @if (activeTab() === 'requirements') {
-          <div class="tab-content">
-            <div class="requirements-header" style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
-              <h3 style="margin:0;">需求补充</h3>
-              <button class="secondary-btn btn-hover-effect" (click)="saveRequirementDraft()">保存草稿</button>
-            </div>
-            <div class="incomplete-hints" style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:16px;">
-              @if (!hasBlueprint()) {
-                <div class="hint-card" style="border:1px dashed #f39c12;background:#fffaf0;padding:10px 12px;border-radius:8px;display:flex;align-items:center;gap:8px;">
-                  <span style="color:#e67e22;">未上传施工图纸</span>
-                  <label class="primary-btn btn-hover-effect" style="margin-left:4px;">
-                    <input type="file" accept=".pdf,.png,.jpg,.jpeg" style="display:none" (change)="uploadBlueprint($event)">
-                    上传图纸
-                  </label>
+        <!-- 消息标签内容 -->
+        <div *ngIf="activeTab() === 'messages'" class="tab-content">
+          <div class="messages-container">
+            <div class="messages-list">
+              <div *ngFor="let message of messages()" class="message-item">
+                <div class="message-avatar">
+                  {{ message.sender.charAt(0) }}
                 </div>
-              }
-              @if (!hasStylePreference()) {
-                <div class="hint-card" style="border:1px dashed #8e44ad;background:#faf5ff;padding:10px 12px;border-radius:8px;display:flex;align-items:center;gap:8px;">
-                  <span style="color:#8e44ad;">未填写风格偏好</span>
-                  <button class="secondary-btn btn-hover-effect" (click)="showStyleEdit.set(true); tempStylePreference = ''">去填写</button>
+                <div class="message-content">
+                  <div class="message-header">
+                    <span class="message-sender">{{ message.sender }}</span>
+                    <span class="message-time">{{ formatDateTime(message.timestamp) }}</span>
+                  </div>
+                  <div class="message-text">{{ message.content }}</div>
                 </div>
-              }
+              </div>
+            </div>
+            <div class="message-input-area">
+              <textarea 
+                [value]="newMessage()"
+                (input)="onMessageInput($event)"
+                placeholder="输入消息内容..."
+                rows="3"
+                (keydown.enter.shift)="$event.preventDefault()"
+                (keydown.enter)="sendMessage()"
+              ></textarea>
+              <div class="message-actions">
+                <button class="secondary-btn btn-hover-effect">
+                  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                    <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
+                    <polyline points="14 2 14 8 20 8"></polyline>
+                  </svg>
+                  <span>上传文件</span>
+                </button>
+                <button class="primary-btn btn-hover-effect" (click)="sendMessage()" [disabled]="!newMessage().trim()">
+                  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                    <line x1="22" y1="2" x2="11" y2="13"></line>
+                    <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
+                  </svg>
+                  <span>发送</span>
+                </button>
+              </div>
             </div>
-            @if (showStyleEdit()) {
-              <div class="style-editor" style="padding:12px;border:1px solid #eee;border-radius:8px;margin-bottom:16px;background:#fff;">
-                <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
-                  <span>风格选择:</span>
-                  <select [(ngModel)]="tempStylePreference">
-                    <option value="" disabled selected>请选择风格</option>
-                    @for (opt of styleOptions; track $index) {
-                      <option [value]="opt">{{ opt }}</option>
-                    }
-                  </select>
-                  <button class="primary-btn btn-hover-effect" (click)="saveStylePreference()" [disabled]="!tempStylePreference">保存</button>
-                  <button class="secondary-btn btn-hover-effect" (click)="showStyleEdit.set(false)">取消</button>
+          </div>
+        </div>
+
+        <!-- 概览标签内容 -->
+        <div *ngIf="activeTab() === 'overview'" class="tab-content">
+          <div class="overview-grid">
+            <!-- 客户信息卡片 -->
+            <div class="info-card">
+              <h4 class="card-title">客户信息</h4>
+              <div class="customer-info">
+                <div class="info-item">
+                  <label>客户姓名</label>
+                  <span>{{ project()?.customerName || '王先生' }}</span>
+                </div>
+                <div class="info-item">
+                  <label>联系方式</label>
+                  <span>138****5678</span>
+                </div>
+                <div class="info-item">
+                  <label>标签</label>
+                  <div class="tag-container">
+                    <span class="tag">朋友圈</span>
+                    <span class="tag">软装</span>
+                    <span class="tag">现代风格</span>
+                  </div>
+                </div>
+                <div class="info-item">
+                  <label>高优先级需求</label>
+                  <ul class="need-list">
+                    <li *ngFor="let need of project()?.highPriorityNeeds || ['客厅光线充足', '储物空间充足', '环保材料']">
+                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                        <polyline points="20 6 9 17 4 12"></polyline>
+                      </svg>
+                      {{ need }}
+                    </li>
+                  </ul>
                 </div>
               </div>
-            }
-            <div class="requirements-form">
-              <div class="form-row">
-                <label>需求摘要</label>
-                <textarea [formControl]="requirementForm.controls.summary" rows="3" placeholder="例如:偏好现代简约风,客厅以明亮色调为主…"></textarea>
+            </div>
+
+            <!-- 项目团队卡片 -->
+            <div class="info-card">
+              <h4 class="card-title">项目团队</h4>
+              <div class="team-info">
+                <div class="team-member">
+                  <div class="member-avatar" title="客服小李">IMG</div>
+                  <div class="member-details">
+                    <div class="member-name">客服小李</div>
+                    <div class="member-role">客户经理</div>
+                  </div>
+                  <button class="message-btn">
+                    <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>
+                </div>
+                <div class="team-member">
+                  <div class="member-avatar" title="张设计师">IMG</div>
+                  <div class="member-details">
+                    <div class="member-name">张设计师</div>
+                    <div class="member-role">主设计师</div>
+                  </div>
+                  <button class="message-btn">
+                    <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>
+                </div>
               </div>
-              <div class="form-row">
-                <label>重点诉求</label>
-                <textarea [formControl]="requirementForm.controls.priorityPoints" rows="3"></textarea>
+            </div>
+
+            <!-- 最近反馈卡片 -->
+            <div class="info-card">
+              <h4 class="card-title">客户反馈</h4>
+              <div class="feedback-list">
+                <div *ngFor="let feedback of feedbacks()" class="feedback-item">
+                  <div class="feedback-item">
+                    <div class="feedback-header">
+                      <div class="feedback-author">{{ getFeedbackCustomerName(feedback) }}</div>
+                      <div class="feedback-rating">
+                        <span class="rating-stars">★★★★☆</span>
+                        <span class="rating-number">{{ getFeedbackRating(feedback) }}.0</span>
+                      </div>
+                    </div>
+                    <div class="feedback-content">{{ feedback?.content || '' }}</div>
+                    <div class="feedback-response" *ngIf="feedback?.response">
+                      <div class="response-label">客服回复:</div>
+                      <div class="response-text">{{ feedback.response }}</div>
+                    </div>
+                    <div class="feedback-meta">
+                      <span class="feedback-date">{{ formatDate(feedback?.createdAt) }}</span>
+                      <span class="feedback-status" [class.status-processed]="feedback?.status === '已解决'" [class.status-pending]="feedback?.status === '待处理'" [class.status-processing]="feedback?.status === '处理中'">
+                        {{ feedback?.status || '未知状态' }}
+                      </span>
+                    </div>
+                  </div>
+                </div>
+                <button class="view-all-btn btn-hover-effect" *ngIf="feedbacks().length > 0">查看全部反馈</button>
               </div>
-              <div class="form-row">
-                <label>约束条件</label>
-                <textarea [formControl]="requirementForm.controls.constraints" rows="3"></textarea>
+            </div>
+          </div>
+        </div>
+
+        <!-- 里程碑标签内容 -->
+        <div *ngIf="activeTab() === 'milestones'" class="tab-content">
+          <div class="milestones-timeline">
+            <div *ngFor="let milestone of milestones(); index as i" class="milestone-item">
+              <div class="milestone-dot" [class.completed]="milestone.isCompleted"></div>
+              <div class="milestone-line" *ngIf="i < milestones().length - 1" [class.completed]="milestone.isCompleted && milestones()[i+1].isCompleted"></div>
+              <div class="milestone-content">
+                <div class="milestone-header">
+                  <h4 class="milestone-title">{{ milestone.title }}</h4>
+                  <span class="milestone-status" [class.status-completed]="milestone.isCompleted" [class.status-pending]="!milestone.isCompleted">
+                    {{ milestone.isCompleted ? '已完成' : '进行中' }}
+                  </span>
+                </div>
+                <p class="milestone-description">{{ milestone.description }}</p>
+                <div class="milestone-dates">
+                  <div class="date-item">
+                    <label>截止日期</label>
+                    <span>{{ formatDate(milestone.dueDate) }}</span>
+                  </div>
+                  <div class="date-item" *ngIf="milestone.completedDate">
+                    <label>完成日期</label>
+                    <span>{{ formatDate(milestone.completedDate) }}</span>
+                  </div>
+                </div>
+                <div class="milestone-actions" *ngIf="!milestone.isCompleted">
+                  <button class="primary-btn small btn-hover-effect" (click)="completeMilestone(milestone.id)">
+                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                      <polyline points="20 6 9 17 4 12"></polyline>
+                    </svg>
+                    <span>标记完成</span>
+                  </button>
+                </div>
               </div>
-              <div class="form-row">
-                <label>预算</label>
-                <input type="text" [formControl]="requirementForm.controls.budget" placeholder="如:20万以内">
+            </div>
+          </div>
+        </div>
+
+        <!-- 任务标签内容 -->
+        <div *ngIf="activeTab() === 'tasks'" class="tab-content">
+          <div class="tasks-filter">
+            <div class="filter-options">
+              <button class="filter-btn active">全部任务</button>
+              <button class="filter-btn">进行中</button>
+              <button class="filter-btn">已完成</button>
+              <button class="filter-btn">逾期</button>
+            </div>
+            <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="搜索任务...">
+            </div>
+          </div>
+          <div class="tasks-list">
+            <!-- 修复任务列表中的状态显示,确保安全访问 -->
+            <div *ngFor="let task of tasks()" class="task-item">
+              <div class="task-checkbox">
+                <input type="checkbox" [checked]="task.isCompleted" (change)="task.isCompleted ? null : completeTask(task.id)">
               </div>
-              <div class="form-actions">
-                <button class="primary-btn btn-hover-effect" (click)="submitRequirements()">提交需求</button>
+              <div class="task-content">
+                <h4 class="task-title" [class.completed]="task.isCompleted">{{ task.title || '未命名任务' }}</h4>
+                <p class="task-description">{{ task.description || '' }}</p>
+                <div class="task-meta">
+                  <span class="task-assignee">
+                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                      <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
+                      <circle cx="9" cy="7" r="4"></circle>
+                      <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
+                      <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
+                    </svg>
+                    {{ task.assignee || '未分配' }}
+                  </span>
+                  <span class="task-deadline" [class.overdue]="task.isOverdue">
+                    <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>
+                    {{ formatDate(task.deadline) }}
+                  </span>
+                  <span class="task-priority" [class.priority-high]="task.priority === 'high'" [class.priority-medium]="task.priority === 'medium'" [class.priority-low]="task.priority === 'low'">
+                    {{ task.priority === 'high' ? '高' : task.priority === 'medium' ? '中' : '低' }}
+                  </span>
+                </div>
               </div>
             </div>
           </div>
-        }
+        </div>
 
         <!-- 消息标签内容 -->
-        @if (activeTab() === 'messages') {
-          <div class="tab-content">
-            <div class="messages-toolbar" style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;gap:8px;">
-              <div style="display:flex;align-items:center;gap:8px;">
-                <button class="primary-btn btn-hover-effect" (click)="createGroup()" [disabled]="creatingGroup() || !!chatGroup()">{{ creatingGroup() ? '拉群中…' : (chatGroup() ? '已创建群' : '拉群') }}</button>
-                @if (chatGroup()) { <a class="secondary-btn btn-hover-effect" [href]="chatGroup()!.link" target="_blank">群聊入口</a> }
-              </div>
-              <div class="tips" style="color:#888;font-size:12px;">在这里与客户与设计师进行项目沟通</div>
-            </div>
-            <div class="messages-container">
-              <div class="messages-list">
-                @for (message of messages(); track $index) {
-                  <div class="message-item">
-                    <div class="message-avatar">
-                      {{ message.sender.charAt(0) }}
-                    </div>
-                    <div class="message-content">
-                      <div class="message-header">
-                        <span class="message-sender">{{ message.sender }}</span>
-                        <span class="message-time">{{ formatDateTime(message.timestamp) }}</span>
-                      </div>
-                      <div class="message-text">{{ message.content }}</div>
-                    </div>
-                  </div>
-                }
-                <div class="message-input-area">
-                  <textarea 
-                    [value]="newMessage()"
-                    (input)="onMessageInput($event)"
-                    placeholder="输入消息内容..."
-                    rows="3"
-                    (keydown.enter.shift)="$event.preventDefault()"
-                    (keydown.enter)="sendMessage()"
-                  ></textarea>
-                  <div class="message-actions">
-                    <button class="secondary-btn btn-hover-effect">
-                      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                        <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
-                        <polyline points="14 2 14 8 20 8"></polyline>
-                      </svg>
-                      <span>上传文件</span>
-                    </button>
-                    <button class="primary-btn btn-hover-effect" (click)="sendMessage()" [disabled]="!newMessage().trim()">
-                      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                        <line x1="22" y1="2" x2="11" y2="13"></line>
-                        <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
-                      </svg>
-                      <span>发送</span>
-                    </button>
+        <div *ngIf="activeTab() === 'messages'" class="tab-content">
+          <div class="messages-container">
+            <div class="messages-list">
+              <div *ngFor="let message of messages()" class="message-item">
+                <div class="message-avatar">
+                  {{ message.sender.charAt(0) }}
+                </div>
+                <div class="message-content">
+                  <div class="message-header">
+                    <span class="message-sender">{{ message.sender }}</span>
+                    <span class="message-time">{{ formatDateTime(message.timestamp) }}</span>
                   </div>
+                  <div class="message-text">{{ message.content }}</div>
                 </div>
               </div>
             </div>
       </div>
-        }
+    </div>
 
     <!-- 右侧边栏 - 企业微信聊天集成 -->
     <div class="wechat-sidebar ios-sidebar">
@@ -361,20 +461,18 @@
       
       <!-- 聊天消息列表 -->
       <div class="wechat-messages" #wechatMessages>
-        @for (msg of wechatMessagesList; track $index) {
-          <div class="wechat-message-item">
-            <div class="message-avatar">
-              {{ msg.sender.charAt(0) }}
-            </div>
-            <div class="message-content">
-              <div class="message-header">
-                <span class="message-sender">{{ msg.sender }}</span>
-                <span class="message-time">{{ formatTime(msg.timestamp) }}</span>
-              </div>
-              <div class="message-text">{{ msg.content }}</div>
+        <div *ngFor="let msg of wechatMessagesList" class="wechat-message-item">
+          <div class="message-avatar">
+            {{ msg.sender.charAt(0) }}
+          </div>
+          <div class="message-content">
+            <div class="message-header">
+              <span class="message-sender">{{ msg.sender }}</span>
+              <span class="message-time">{{ formatTime(msg.timestamp) }}</span>
             </div>
+            <div class="message-text">{{ msg.content }}</div>
           </div>
-        }
+        </div>
       </div>
       
       <!-- 消息输入框 -->

+ 48 - 189
src/app/pages/customer-service/project-detail/project-detail.ts

@@ -1,10 +1,10 @@
 import { Component, OnInit, signal, computed, ViewChild, AfterViewChecked } from '@angular/core';
 import { CommonModule } from '@angular/common';
-import { FormsModule, ReactiveFormsModule, FormGroup, FormControl } from '@angular/forms';
-import { RouterModule, ActivatedRoute, Router } from '@angular/router';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { RouterModule, ActivatedRoute } from '@angular/router';
 import { MatDialog, MatDialogModule } from '@angular/material/dialog';
 import { ProjectService } from '../../../services/project.service';
-import { Project, Task, Message, FileItem, CustomerFeedback, Milestone, CustomerTag } from '../../../models/project.model';
+import { Project, Task, Message, FileItem, CustomerFeedback, Milestone } from '../../../models/project.model';
 import { ModificationRequestDialog } from './modification-request-dialog';
 import { ComplaintWarningDialog } from './complaint-warning-dialog';
 import { RefundRequestDialog } from './refund-request-dialog';
@@ -93,49 +93,9 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
   // 当前激活的标签页
   activeTab = signal('overview');
   
-  // 允许的标签集合
-  private readonly allowedTabs = new Set(['overview','milestones','tasks','messages','files','members','requirements'])
-  
-  // 群聊相关状态
-  chatGroup = signal<{ name: string; link: string; createdAt: Date } | null>(null)
-  creatingGroup = signal(false)
-
-  // 需求补充相关(风格偏好与施工图纸)
-  styleOptions: string[] = ['现代简约','北欧','新中式','美式','工业风','法式','日式','混搭']
-  tempStylePreference: string = ''
-  showStyleEdit = signal(false)
-
-  hasStylePreference = computed(() => {
-    const p = this.project();
-    if (!p || !p.customerTags || p.customerTags.length === 0) return false
-    return p.customerTags.some(t => !!t.preference)
-  })
-
-  hasBlueprint = computed(() => {
-    return (this.files() || []).some(f => /施工图|blueprint|图纸/i.test(f.name))
-  })
   // 新消息内容
   newMessage = signal('');
   
-  // 组员管理
-  teamRoles: string[] = ['订单客服','主设计师','协助设计','渲染设计师']
-  teamMembers = signal<{ role: string; name: string }[]>([
-    { role: '订单客服', name: '客服小李' },
-    { role: '主设计师', name: '张设计师' },
-    { role: '协助设计', name: '王设计师' },
-    { role: '渲染设计师', name: '李设计师' }
-  ])
-  isEditingMembers = signal(false)
-  editedMembers = signal<{ role: string; name: string }[]>([])
-
-  // 需求补充表单(Reactive Forms)
-  requirementForm = new FormGroup({
-    summary: new FormControl<string>('', { nonNullable: true }),
-    priorityPoints: new FormControl<string>('', { nonNullable: true }),
-    constraints: new FormControl<string>('', { nonNullable: true }),
-    budget: new FormControl<string>('', { nonNullable: true })
-  })
-  
   // 项目阶段数据 - 进度时间轴
   projectStages: ProjectStage[] = [
     {      
@@ -189,23 +149,23 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
       responsible: '客服小李',
       details: '项目验收、交付和投诉处理'
     }
-  ]
+  ];
   
   // 企业微信聊天相关
-  @ViewChild('wechatMessages') wechatMessagesContainer: any
-  wechatMessagesList: WechatMessage[] = []
-  wechatInput = ''
-  scrollToBottom = false
+  @ViewChild('wechatMessages') wechatMessagesContainer: any;
+  wechatMessagesList: WechatMessage[] = [];
+  wechatInput = '';
+  scrollToBottom = false;
   
   // 历史服务记录相关
-  consultationRecords = signal<ConsultationRecord[]>([])
-  cooperationProjects = signal<CooperationProject[]>([])
-  historicalFeedbacks = signal<HistoricalFeedback[]>([])
+  consultationRecords = signal<ConsultationRecord[]>([]);
+  cooperationProjects = signal<CooperationProject[]>([]);
+  historicalFeedbacks = signal<HistoricalFeedback[]>([]);
   
   // 售后处理弹窗状态
-  showModificationRequest = false
-  showComplaintWarning = false
-  showRefundRequest = false
+  showModificationRequest = false;
+  showComplaintWarning = false;
+  showRefundRequest = false;
   
   // 渲染图只读预览弹窗状态与数据
   showRenderPreviewModal = false;
@@ -218,37 +178,28 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
     '已完成': 'success',
     '已暂停': 'secondary',
     '已取消': 'danger'
-  }
+  };
   
   // 计算完成进度
   completionProgress = computed(() => {
-    if (!this.tasks().length) return 0
-    const completedTasks = this.tasks().filter(task => task.isCompleted).length
-    return Math.round((completedTasks / this.tasks().length) * 100)
-  })
+    if (!this.tasks().length) return 0;
+    const completedTasks = this.tasks().filter(task => task.isCompleted).length;
+    return Math.round((completedTasks / this.tasks().length) * 100);
+  });
   
   constructor(
     private route: ActivatedRoute,
     private projectService: ProjectService,
-    private dialog: MatDialog,
-    private router: Router
+    private dialog: MatDialog
   ) {
     // 获取路由参数中的项目ID
     this.route.paramMap.subscribe(params => {
-      this.projectId = params.get('id') || ''
-    })
+      this.projectId = params.get('id') || '';
+    });
   }
   
   ngOnInit(): void {
-    // 解析 queryParams 中的 activeTab
-    this.route.queryParamMap.subscribe(q => {
-      const tab = (q.get('activeTab') || '').trim()
-      if (tab && this.allowedTabs.has(tab)) {
-        this.activeTab.set(tab)
-      }
-    })
-
-    this.loadProjectDetails()
+    this.loadProjectDetails();
   }
   
   // 加载项目详情
@@ -256,22 +207,22 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
     // 模拟从服务获取项目数据
     this.projectService.getProjectById(this.projectId).subscribe(project => {
       if (project) {
-        this.project.set(project)
+        this.project.set(project);
       }
-    })
+    });
     
     // 加载模拟数据
-    this.loadMockData()
+    this.loadMockData();
   }
   
   // 加载模拟数据
   // 修复 loadMockData 方法中的任务对象,确保类型一致性
   loadMockData(): void {
     // 初始化企业微信聊天
-    this.initWechatMessages()
+    this.initWechatMessages();
     
     // 初始化历史服务记录
-    this.initHistoricalServiceRecords()
+    this.initHistoricalServiceRecords();
     
     // 模拟里程碑数据
     this.milestones.set([
@@ -315,7 +266,7 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
         completedDate: undefined,
         isCompleted: false
       }
-    ])
+    ]);
     
     // 模拟任务数据 - 确保所有任务对象都有完整的必填属性
     this.tasks.set([
@@ -389,7 +340,7 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
         priority: 'medium',
         stage: '渲染'
       }
-    ])
+    ]);
     
     // 模拟消息数据
     this.messages.set([
@@ -433,7 +384,7 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
         isRead: true,
         type: 'text'
       }
-    ])
+    ]);
     
     // 模拟文件数据
     this.files.set([
@@ -487,7 +438,7 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
         uploadedAt: new Date('2023-06-04'),
         downloadCount: 7
       }
-    ])
+    ]);
     
     // 模拟客户反馈
     this.feedbacks.set([
@@ -507,57 +458,17 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
   
   // 切换标签页
   switchTab(tab: string): void {
-    this.activeTab.set(tab)
-    // 同步到 URL,便于分享/刷新保持状态
-    this.router.navigate([], {
-      relativeTo: this.route,
-      queryParams: { activeTab: tab },
-      queryParamsHandling: 'merge'
-    })
-  }
-
-  // 组员编辑相关
-  startEditMembers(): void {
-    this.editedMembers.set(this.teamMembers().map(m => ({ ...m })))
-    this.isEditingMembers.set(true)
-  }
-  cancelEditMembers(): void {
-    this.isEditingMembers.set(false)
-  }
-  saveMembers(): void {
-    this.teamMembers.set(this.editedMembers().map(m => ({ ...m })))
-    this.isEditingMembers.set(false)
-  }
-
-  // 组员编辑:模板辅助方法
-  getEditedMemberName(role: string): string {
-    const found = this.editedMembers().find(m => m.role === role)
-    return found?.name || ''
-  }
-  updateEditedMember(role: string, name: string): void {
-    const others = this.editedMembers().filter(m => m.role !== role)
-    this.editedMembers.set([...others, { role, name }])
-  }
-  
-  // 需求表单相关
-  saveRequirementDraft(): void {
-    console.log('保存草稿:', this.requirementForm.getRawValue())
-  }
-  submitRequirements(): void {
-    const data = this.requirementForm.getRawValue()
-    console.log('提交需求:', data)
-    // 简单反馈
-    alert('需求已提交,客服与设计师会尽快处理。')
+    this.activeTab.set(tab);
   }
   
   // 增强版发送消息功能
   sendMessage(): void {
     if (this.newMessage().trim()) {
       // 添加发送动画效果
-      const sendBtn = document.querySelector('.message-actions .primary-btn')
+      const sendBtn = document.querySelector('.message-actions .primary-btn');
       if (sendBtn) {
-        sendBtn.classList.add('sending')
-        setTimeout(() => sendBtn.classList.remove('sending'), 300)
+        sendBtn.classList.add('sending');
+        setTimeout(() => sendBtn.classList.remove('sending'), 300);
       }
 
       const newMsg: Message = {
@@ -567,28 +478,28 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
         timestamp: new Date(),
         isRead: true,
         type: 'text'
-      }
+      };
       
-      this.messages.set([...this.messages(), newMsg])
-      this.newMessage.set('')
+      this.messages.set([...this.messages(), newMsg]);
+      this.newMessage.set('');
       
       // 自动滚动到底部
       setTimeout(() => {
-        const container = document.querySelector('.messages-list') as HTMLElement | null
+        const container = document.querySelector('.messages-list');
         if (container) {
-          container.scrollTop = container.scrollHeight
+          container.scrollTop = container.scrollHeight;
         }
-      }, 100)
+      }, 100);
     }
   }
   
   // 增强版完成任务功能
   completeTask(taskId: string): void {
     // 添加完成动画效果
-    const taskElement = document.querySelector(`.task-item[data-id="${taskId}"]`)
+    const taskElement = document.querySelector(`.task-item[data-id="${taskId}"]`);
     if (taskElement) {
-      taskElement.classList.add('completing')
-      setTimeout(() => taskElement.classList.remove('completing'), 500)
+      taskElement.classList.add('completing');
+      setTimeout(() => taskElement.classList.remove('completing'), 500);
     }
 
     this.tasks.set(
@@ -597,10 +508,10 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
           ? { ...task, isCompleted: true, completedDate: new Date(), isOverdue: false }
           : task
       )
-    )
+    );
     
     // 播放完成音效
-    this.playSound('complete')
+    this.playSound('complete');
   }
   
   // 修复 completeMilestone 方法中的类型问题
@@ -611,7 +522,7 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
           ? { ...milestone, isCompleted: true, completedDate: new Date() }
           : milestone
       )
-    )
+    );
   }
   
   // 增强 formatDate 和 formatDateTime 方法的类型安全
@@ -937,56 +848,4 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
       }
     });
   }
-  
-  // 创建项目群聊(模拟)
-  createGroup(): void {
-    if (this.chatGroup()) return
-    this.creatingGroup.set(true)
-    const name = `项目群 - ${this.project()?.name || '未命名项目'}`
-    // 模拟异步创建
-    setTimeout(() => {
-      const link = `https://work.weixin.qq.com/grouplink/${this.projectId}-${Math.random().toString(36).slice(2, 8)}`
-      this.chatGroup.set({ name, link, createdAt: new Date() })
-      // 推送系统消息
-      this.wechatMessagesList.push({ sender: '系统', content: `已创建群聊「${name}」,群聊入口已生成。`, timestamp: new Date() })
-      this.creatingGroup.set(false)
-    }, 600)
-  }
-
-  // 上传施工图纸(模拟)
-  uploadBlueprint(event: Event): void {
-    const input = event.target as HTMLInputElement
-    const file = input?.files && input.files[0]
-    if (!file) return
-    const newItem: FileItem = {
-      id: 'bp-' + Date.now(),
-      name: file.name || `施工图-${Date.now()}.pdf`,
-      type: 'document',
-      size: `${Math.max(1, Math.round((file.size || 500000) / 1024))} KB`,
-      url: URL.createObjectURL(file),
-      uploadedBy: '客服',
-      uploadedAt: new Date(),
-      downloadCount: 0
-    }
-    const arr = [...this.files()]
-    arr.unshift(newItem)
-    this.files.set(arr)
-  }
-
-  // 保存风格偏好(写入 Project.customerTags)
-  saveStylePreference(): void {
-    const pref = (this.tempStylePreference || '').trim()
-    if (!pref) return
-    const p = this.project()
-    if (!p) return
-    const tags = [...(p.customerTags || [])]
-    if (tags.length === 0) {
-      tags.push({ source: '朋友圈', needType: '软装', preference: pref as CustomerTag['preference'], colorAtmosphere: '' })
-    } else {
-      // 替换第一个标签的偏好
-      tags[0] = { ...tags[0], preference: pref as CustomerTag['preference'] }
-    }
-    this.project.set({ ...p, customerTags: tags })
-    this.showStyleEdit.set(false)
-  }
 }

+ 1 - 1
src/app/pages/designer/dashboard/skill-radar/skill-radar.component.scss

@@ -1,4 +1,4 @@
-@use '../../ios-theme.scss' as *;
+  @use '../../ios-theme.scss' as *;
 
 .skill-radar-container {
   background-color: $ios-card-background;

+ 112 - 1
src/app/pages/designer/project-detail/project-detail.html

@@ -354,7 +354,118 @@
                                 @if (stage === '投诉处理') {
                                   <div class="empty">此处可扩展投诉处理流程/记录</div>
                                 }
-                                @if (stage === '订单创建' || stage === '需求沟通' || stage === '方案确认') {
+                                @if (stage === '订单创建') {
+                                  <div>
+                                    <div style="display:flex; gap:12px; align-items:center; margin-bottom:12px; flex-wrap: wrap;">
+                                      <div style="display:flex; gap:8px; align-items:center;">
+                                        <button class="secondary-btn" [class.active]="orderCreationMethod === 'miniprogram'" (click)="orderCreationMethod = 'miniprogram'">小程序同步</button>
+                                        <button class="secondary-btn" [class.active]="orderCreationMethod === 'manual'" (click)="orderCreationMethod = 'manual'">手动录入</button>
+                                      </div>
+                                      <span class="hint">下单时间:{{ orderTime }}</span>
+                                    </div>
+
+                                    @if (orderCreationMethod === 'miniprogram') {
+                                      <div style="display:flex; gap:12px; align-items:center; margin-bottom:12px;">
+                                        <button class="primary-btn" [disabled]="isSyncing" (click)="syncMiniprogramCustomerInfo()">{{ isSyncing ? '同步中...' : '从小程序同步客户信息' }}</button>
+                                        <span class="hint">点击同步后将自动填充客户姓名、手机号、微信等信息</span>
+                                      </div>
+                                    }
+
+                                    <div style="border-top:1px solid #e5e7eb; padding-top:12px; margin-top:4px;"></div>
+
+                                    <div style="margin-bottom:12px; display:flex; gap:8px; align-items:center; flex-wrap: wrap;">
+                                      <input type="text" placeholder="搜索客户姓名或手机号" [(ngModel)]="customerSearchKeyword" (ngModelChange)="searchCustomer()" style="flex:1 1 260px; padding:8px 10px; border:1px solid #d1d5db; border-radius:6px;" />
+                                      <button class="secondary-btn" (click)="searchCustomer()">搜索</button>
+                                      @if (selectedOrderCustomer) {
+                                        <div style="display:flex; gap:8px; align-items:center; background:#f3f4f6; padding:6px 10px; border-radius:9999px;">
+                                          <span>已选客户:{{ selectedOrderCustomer?.name }}({{ selectedOrderCustomer?.phone }})</span>
+                                          <button class="link danger" (click)="clearSelectedCustomer()">清除</button>
+                                        </div>
+                                      }
+                                    </div>
+
+                                    @if (customerSearchResults.length > 0) {
+                                      <div style="border:1px solid #e5e7eb; border-radius:8px; padding:8px; margin-bottom:12px; max-height:200px; overflow:auto;">
+                                        @for (c of customerSearchResults; track c.id) {
+                                          <div style="display:flex; justify-content:space-between; align-items:center; padding:6px 4px; border-bottom:1px dashed #f3f4f6;">
+                                            <div style="display:flex; gap:8px; align-items:center;">
+                                              <span style="font-weight:500;">{{ c.name }}</span>
+                                              <span style="color:#6b7280;">{{ c.phone }}</span>
+                                              @if (c.wechat) { <span style="color:#9ca3af;">wx: {{ c.wechat }}</span> }
+                                            </div>
+                                            <button class="link" (click)="selectCustomer(c)">选择</button>
+                                          </div>
+                                        }
+                                      </div>
+                                    }
+
+                                    <form [formGroup]="customerForm" novalidate>
+                                      <div class="form-group">
+                                        <label>客户姓名</label>
+                                        <input type="text" formControlName="name" placeholder="请输入客户姓名" />
+                                        @if (customerForm.get('name')?.touched && customerForm.get('name')?.invalid) {
+                                          <div style="color:#ef4444; font-size:12px; margin-top:4px;">请填写客户姓名</div>
+                                        }
+                                      </div>
+
+                                      <div class="form-group">
+                                        <label>手机号</label>
+                                        <input type="text" formControlName="phone" placeholder="请输入11位手机号" />
+                                        @if (customerForm.get('phone')?.touched && customerForm.get('phone')?.errors?.['required']) {
+                                          <div style="color:#ef4444; font-size:12px; margin-top:4px;">请填写手机号</div>
+                                        }
+                                        @if (customerForm.get('phone')?.touched && customerForm.get('phone')?.errors?.['pattern']) {
+                                          <div style="color:#ef4444; font-size:12px; margin-top:4px;">手机号格式不正确</div>
+                                        }
+                                      </div>
+
+                                      <div class="form-group">
+                                        <label>微信号</label>
+                                        <input type="text" formControlName="wechat" placeholder="可选" />
+                                      </div>
+
+                                      <div class="form-group">
+                                        <label>客户类型</label>
+                                        <select formControlName="customerType">
+                                          <option value="新客户">新客户</option>
+                                          <option value="老客户">老客户</option>
+                                          <option value="VIP客户">VIP客户</option>
+                                        </select>
+                                      </div>
+
+                                      <div class="form-group">
+                                        <label>需求类型</label>
+                                        <select formControlName="demandType">
+                                          <option value="">未指定</option>
+                                          @for (d of demandTypes; track d.value) {
+                                            <option [value]="d.value">{{ d.label }}</option>
+                                          }
+                                        </select>
+                                      </div>
+
+                                      <div class="form-group">
+                                        <label>跟进状态</label>
+                                        <select formControlName="followUpStatus">
+                                          <option value="">未指定</option>
+                                          @for (s of followUpStatus; track s.value) {
+                                            <option [value]="s.value">{{ s.label }}</option>
+                                          }
+                                        </select>
+                                      </div>
+
+                                      <div class="form-group">
+                                        <label>来源渠道</label>
+                                        <input type="text" formControlName="source" placeholder="如:抖音/官网/转介绍/线下" />
+                                      </div>
+
+                                      <div class="form-group">
+                                        <label>备注</label>
+                                        <textarea formControlName="remark" rows="3" placeholder="可填写特殊要求或沟通记录"></textarea>
+                                      </div>
+                                    </form>
+                                  </div>
+                                }
+                                @if (stage === '需求沟通' || stage === '方案确认') {
                                   <div class="empty">该阶段的详细表单与内容保持与现有功能一致,后续可按需接入</div>
                                 }
                               </div>

+ 100 - 4
src/app/pages/designer/project-detail/project-detail.ts

@@ -1,7 +1,7 @@
 import { Component, OnInit, OnDestroy } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { ActivatedRoute, Router } from '@angular/router';
-import { FormsModule } from '@angular/forms';
+import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
 import { ProjectService } from '../../../services/project.service';
 import {
   Project,
@@ -54,7 +54,7 @@ type SectionKey = 'order' | 'requirements' | 'delivery' | 'aftercare';
 
 @Component({
   selector: 'app-project-detail',
-  imports: [CommonModule, FormsModule],
+  imports: [CommonModule, FormsModule, ReactiveFormsModule],
   templateUrl: './project-detail.html',
   styleUrls: ['./project-detail.scss', './debug-styles.scss']
 })
@@ -146,7 +146,8 @@ export class ProjectDetail implements OnInit, OnDestroy {
   constructor(
     private route: ActivatedRoute,
     private projectService: ProjectService,
-    private router: Router
+    private router: Router,
+    private fb: FormBuilder,
   ) {}
 
   // 切换标签页
@@ -255,6 +256,24 @@ export class ProjectDetail implements OnInit, OnDestroy {
     
     // 添加点击事件监听器,当点击页面其他位置时关闭下拉菜单
     document.addEventListener('click', this.closeDropdownOnClickOutside);
+  
+    // 初始化客户表单(与客服端保持一致)
+    this.customerForm = this.fb.group({
+      name: ['', Validators.required],
+      phone: ['', [Validators.required, Validators.pattern(/^1[3-9]\d{9}$/)]],
+      wechat: [''],
+      customerType: ['新客户'],
+      source: [''],
+      remark: [''],
+      demandType: [''],
+      followUpStatus: ['']
+    });
+  
+    // 自动生成下单时间
+    this.orderTime = new Date().toLocaleString('zh-CN', {
+      year: 'numeric', month: '2-digit', day: '2-digit',
+      hour: '2-digit', minute: '2-digit', second: '2-digit'
+    });
   }
 
   // 在组件销毁时移除事件监听器和清理资源
@@ -1174,7 +1193,7 @@ return 'order';
 }
 }
 
-// 获取板块状态:completed/active/pending
+// 获取板块状态:completed | 'active' | 'pending'
 getSectionStatus(key: SectionKey): 'completed' | 'active' | 'pending' {
   const current = this.project?.currentStage as ProjectStage | undefined;
   if (!current) return 'pending';
@@ -1194,4 +1213,81 @@ getSectionStatus(key: SectionKey): 'completed' | 'active' | 'pending' {
 toggleSection(key: SectionKey): void {
   this.expandedSection = key;
 }
+
+// 订单创建阶段:客户信息(迁移自客服端“客户信息”卡片)
+orderCreationMethod: 'miniprogram' | 'manual' = 'miniprogram'
+isSyncing: boolean = false
+orderTime: string = ''
+
+customerForm!: FormGroup
+customerSearchKeyword: string = ''
+customerSearchResults: Array<{ id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string }> = []
+selectedOrderCustomer: { id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string } | null = null
+
+demandTypes = [
+{ value: 'price', label: '价格敏感' },
+{ value: 'quality', label: '质量敏感' },
+{ value: 'comprehensive', label: '综合要求' }
+]
+followUpStatus = [
+{ value: 'quotation', label: '待报价' },
+{ value: 'confirm', label: '待确认需求' },
+{ value: 'lost', label: '已失联' }
+]
+// 客户信息:搜索/选择/清空/同步/快速填写 逻辑
+searchCustomer(): void {
+if (this.customerSearchKeyword.trim().length >= 2) {
+this.customerSearchResults = [
+{ id: '1', name: '张先生', phone: '138****5678', customerType: '老客户', source: '官网咨询', avatar: "data:image/svg+xml,%3Csvg width='64' height='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='%23E6E6E6'/%3E%3Ctext x='50%25' y='50%25' font-family='Arial' font-size='13.333333333333334' font-weight='bold' text-anchor='middle' fill='%23555555' dy='0.3em'%3EIMG%3C/text%3E%3C/svg%3E" },
+{ id: '2', name: '李女士', phone: '139****1234', customerType: 'VIP客户', source: '推荐介绍', avatar: "data:image/svg+xml,%3Csvg width='65' height='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='%23DCDCDC'/%3E%3Ctext x='50%25' y='50%25' font-family='Arial' font-size='13.333333333333334' font-weight='bold' text-anchor='middle' fill='%23555555' dy='0.3em'%3EIMG%3C/text%3E%3C/svg%3E" }
+]
+} else {
+this.customerSearchResults = []
+}
+}
+
+selectCustomer(customer: { id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string }): void {
+this.selectedOrderCustomer = customer
+this.customerForm.patchValue({
+name: customer.name,
+phone: customer.phone,
+wechat: customer.wechat || '',
+customerType: customer.customerType || '新客户',
+source: customer.source || '',
+remark: customer.remark || ''
+})
+this.customerSearchResults = []
+this.customerSearchKeyword = ''
+}
+
+clearSelectedCustomer(): void {
+this.selectedOrderCustomer = null
+this.customerForm.reset({ customerType: '新客户' })
+}
+
+quickFillCustomerInfo(keyword: string): void {
+const k = (keyword || '').trim()
+if (!k) return
+// 模拟:若有搜索结果,选择第一条
+if (this.customerSearchResults.length === 0) this.searchCustomer()
+if (this.customerSearchResults.length > 0) {
+this.selectCustomer(this.customerSearchResults[0])
+}
+}
+
+syncMiniprogramCustomerInfo(): void {
+if (this.isSyncing) return
+this.isSyncing = true
+setTimeout(() => {
+// 模拟从小程序同步到客户表单
+this.customerForm.patchValue({
+name: '小程序用户',
+phone: '13800001234',
+wechat: 'wx_user_001',
+customerType: '新客户',
+source: '小程序下单'
+})
+this.isSyncing = false
+}, 1000)
+}
 }