Ver código fonte

fix: project-detail survey

ryanemax 1 dia atrás
pai
commit
c215ce2a8e

+ 103 - 0
src/modules/project/pages/project-detail/project-detail.component.html

@@ -59,6 +59,109 @@
       (contactSelected)="onContactSelected($event)">
     </app-contact-selector>
 
+    <!-- 项目问卷卡片 -->
+    @if (contact) {
+      <div class="survey-card">
+        <div class="survey-header">
+          <div class="survey-title">
+            <svg class="icon" viewBox="0 0 512 512">
+              <path fill="currentColor" d="M336 64h32a48 48 0 0148 48v320a48 48 0 01-48 48H144a48 48 0 01-48-48V112a48 48 0 0148-48h32" opacity=".3"/>
+              <path fill="currentColor" d="M336 64h-80a48 48 0 00-96 0h-80a48 48 0 00-48 48v320a48 48 0 0048 48h224a48 48 0 0048-48V112a48 48 0 00-48-48zM256 32a16 16 0 11-16 16 16 16 0 0116-16zm112 400H144V112h224z"/>
+              <path fill="currentColor" d="M176 208h160v16H176zm0 64h160v16H176zm0 64h160v16H176z"/>
+            </svg>
+            <span>项目需求调查</span>
+          </div>
+          <div class="survey-badge" [class.filled]="surveyStatus.filled">
+            {{ surveyStatus.filled ? '已填写' : '待填写' }}
+          </div>
+        </div>
+
+        <div class="survey-content">
+          @if (surveyStatus.filled) {
+            <!-- 已填写状态 -->
+            <div class="survey-info">
+              <svg class="icon success-icon" viewBox="0 0 512 512">
+                <path fill="currentColor" d="M256 48C141.31 48 48 141.31 48 256s93.31 208 208 208 208-93.31 208-208S370.69 48 256 48zm108.25 138.29l-134.4 160a16 16 0 01-12 5.71h-.27a16 16 0 01-11.89-5.3l-57.6-64a16 16 0 1123.78-21.4l45.29 50.32 122.59-145.91a16 16 0 0124.5 20.58z"/>
+              </svg>
+              <div class="survey-text">
+                <p class="survey-desc">
+                  {{ contact.get('realname') || contact.get('name') }} 已完成需求调查
+                </p>
+                <p class="survey-meta">
+                  完成时间: {{ surveyStatus.surveyLog?.get('completedAt') | date:'yyyy-MM-dd HH:mm' }}
+                </p>
+              </div>
+            </div>
+
+            <button class="button secondary" (click)="handleSurveyClick($event)">
+              <svg class="icon" viewBox="0 0 512 512">
+                <path fill="currentColor" d="M255.66 112c-77.94 0-157.89 45.11-220.83 135.33a16 16 0 00-.27 17.77C82.92 340.8 161.8 400 255.66 400c92.84 0 173.34-59.38 221.79-135.25a16.14 16.14 0 000-17.47C428.89 172.28 347.8 112 255.66 112z" opacity=".3"/>
+                <circle cx="256" cy="256" r="80" fill="currentColor"/>
+              </svg>
+              <span>查看答卷</span>
+            </button>
+          } @else {
+            <!-- 未填写状态 -->
+            <div class="survey-info">
+              <svg class="icon pending-icon" viewBox="0 0 512 512">
+                <path fill="currentColor" d="M256 48C141.31 48 48 141.31 48 256s93.31 208 208 208 208-93.31 208-208S370.69 48 256 48zm0 319.91a20 20 0 1120-20 20 20 0 01-20 20zm21.72-201.15l-5.74 122a16 16 0 01-32 0l-5.74-121.94v-.05a21.74 21.74 0 1143.44 0z"/>
+              </svg>
+              <div class="survey-text">
+                <p class="survey-desc">
+                  邀请 {{ contact.get('realname') || contact.get('name') }} 填写项目需求调查表
+                </p>
+                <p class="survey-meta">
+                  了解客户需求,提供更精准的服务方案
+                </p>
+              </div>
+            </div>
+
+            @if (canEdit && groupChat) {
+              <button class="button primary" (click)="handleSurveyClick($event)">
+                <svg class="icon" viewBox="0 0 512 512">
+                  <path fill="currentColor" d="M476.59 227.05l-.16-.07L49.35 49.84A23.56 23.56 0 0027.14 52 24.65 24.65 0 0016 72.59v113.29a24 24 0 0019.52 23.57l232.93 43.07a4 4 0 010 7.86L35.53 303.45A24 24 0 0016 327v113.31A23.57 23.57 0 0026.59 460a23.94 23.94 0 0013.22 4 24.55 24.55 0 009.52-1.93L476.4 285.94l.19-.09a32 32 0 000-58.8z"/>
+                </svg>
+                <span>发送问卷</span>
+              </button>
+            } @else {
+              <div class="survey-tips">
+                <svg class="icon" viewBox="0 0 512 512">
+                  <path fill="currentColor" d="M256 56C145.72 56 56 145.72 56 256s89.72 200 200 200 200-89.72 200-200S366.28 56 256 56zm0 82a26 26 0 11-26 26 26 26 0 0126-26zm48 226h-88a16 16 0 010-32h28v-88h-16a16 16 0 010-32h32a16 16 0 0116 16v104h28a16 16 0 010 32z"/>
+                </svg>
+                <span>需要在企微群聊中发送问卷</span>
+              </div>
+            }
+          }
+        </div>
+
+        <!-- 问卷说明 -->
+        <div class="survey-footer">
+          <div class="survey-highlights">
+            <div class="highlight-item">
+              <svg class="icon" viewBox="0 0 512 512">
+                <path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm61.8-104.4l-84.9-61.7c-3.1-2.3-4.9-5.9-4.9-9.7V116c0-6.6 5.4-12 12-12h32c6.6 0 12 5.4 12 12v141.7l66.8 48.6c5.4 3.9 6.5 11.4 2.6 16.8L334.6 349c-3.9 5.3-11.4 6.5-16.8 2.6z"/>
+              </svg>
+              <span>3-5分钟</span>
+            </div>
+            <div class="highlight-item">
+              <svg class="icon" viewBox="0 0 512 512">
+                <path fill="currentColor" d="M144 144v296a8 8 0 008 8h56V144zm144 0v304h56a8 8 0 008-8V144zm144 0v272a24 24 0 01-24 24h-40V144zM64 144v328a24 24 0 0024 24h40V144z" opacity=".3"/>
+                <path fill="currentColor" d="M496 124a12 12 0 00-12-12H432V40a24 24 0 00-24-24H104a24 24 0 00-24 24v72H28a12 12 0 00-12 12v20a12 12 0 0012 12h456a12 12 0 0012-12zm-96-12H112V48h288z"/>
+              </svg>
+              <span>8道题目</span>
+            </div>
+            <div class="highlight-item">
+              <svg class="icon" viewBox="0 0 512 512">
+                <path fill="currentColor" d="M400 192H32L16 448l16 32h448l16-32-16-256zm-280 96h-16a8 8 0 01-8-8v-16a8 8 0 018-8h16a8 8 0 018 8v16a8 8 0 01-8 8z"/>
+                <path fill="currentColor" d="M464 32H48C21.5 32 0 53.5 0 80v96h512V80c0-26.5-21.5-48-48-48zM148 140a12 12 0 01-12 12h-52a12 12 0 01-12-12v-24a12 12 0 0112-12h52a12 12 0 0112 12zm216 0a12 12 0 01-12 12H212a12 12 0 01-12-12v-24a12 12 0 0112-12h140a12 12 0 0112 12z"/>
+              </svg>
+              <span>选择题为主</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    }
+
     <!-- 子路由内容(各阶段组件) -->
     <div class="stage-content">
       <router-outlet></router-outlet>

+ 303 - 0
src/modules/project/pages/project-detail/project-detail.component.scss

@@ -1001,3 +1001,306 @@
     font-size: 13px;
   }
 }
+
+// 问卷卡片样式
+.survey-card {
+  background: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+  margin: 16px;
+  overflow: hidden;
+  transition: all 0.3s ease;
+
+  &:hover {
+    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
+  }
+
+  // 头部
+  .survey-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 16px;
+    border-bottom: 1px solid var(--light-shade);
+    background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
+
+    .survey-title {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+
+      .icon {
+        width: 24px;
+        height: 24px;
+        color: var(--primary-color);
+      }
+
+      span {
+        font-size: 16px;
+        font-weight: 600;
+        color: var(--dark-color);
+      }
+    }
+
+    .survey-badge {
+      padding: 4px 12px;
+      border-radius: 12px;
+      font-size: 12px;
+      font-weight: 600;
+      background: rgba(255, 196, 9, 0.15);
+      color: var(--warning-color);
+      transition: all 0.3s ease;
+
+      &.filled {
+        background: rgba(45, 211, 111, 0.15);
+        color: var(--success-color);
+      }
+    }
+  }
+
+  // 内容区
+  .survey-content {
+    padding: 20px 16px;
+
+    .survey-info {
+      display: flex;
+      align-items: flex-start;
+      gap: 12px;
+      margin-bottom: 16px;
+
+      .icon {
+        width: 40px;
+        height: 40px;
+        flex-shrink: 0;
+        margin-top: 2px;
+
+        &.success-icon {
+          color: var(--success-color);
+        }
+
+        &.pending-icon {
+          color: var(--warning-color);
+        }
+      }
+
+      .survey-text {
+        flex: 1;
+
+        .survey-desc {
+          margin: 0 0 6px;
+          font-size: 15px;
+          font-weight: 500;
+          color: var(--dark-color);
+          line-height: 1.5;
+        }
+
+        .survey-meta {
+          margin: 0;
+          font-size: 13px;
+          color: var(--medium-color);
+          line-height: 1.4;
+        }
+      }
+    }
+
+    .button {
+      width: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 8px;
+      padding: 12px 20px;
+      border-radius: 8px;
+      font-size: 14px;
+      font-weight: 600;
+      border: none;
+      cursor: pointer;
+      transition: all 0.3s ease;
+
+      .icon {
+        width: 18px;
+        height: 18px;
+      }
+
+      &.primary {
+        background: var(--primary-color);
+        color: white;
+        box-shadow: 0 2px 8px rgba(56, 128, 255, 0.2);
+
+        &:hover {
+          background: #2f6ce5;
+          transform: translateY(-2px);
+          box-shadow: 0 4px 12px rgba(56, 128, 255, 0.3);
+        }
+
+        &:active {
+          transform: translateY(0);
+        }
+      }
+
+      &.secondary {
+        background: white;
+        color: var(--primary-color);
+        border: 2px solid var(--primary-color);
+
+        &:hover {
+          background: rgba(56, 128, 255, 0.05);
+        }
+
+        &:active {
+          transform: scale(0.98);
+        }
+      }
+    }
+
+    .survey-tips {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 8px;
+      padding: 12px 16px;
+      background: rgba(255, 196, 9, 0.1);
+      border-radius: 8px;
+      color: var(--warning-color);
+      font-size: 13px;
+      font-weight: 500;
+
+      .icon {
+        width: 20px;
+        height: 20px;
+        flex-shrink: 0;
+      }
+    }
+  }
+
+  // 底部说明
+  .survey-footer {
+    padding: 12px 16px;
+    background: #f9fafb;
+    border-top: 1px solid var(--light-shade);
+
+    .survey-highlights {
+      display: flex;
+      justify-content: space-around;
+      gap: 8px;
+
+      .highlight-item {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        gap: 6px;
+        flex: 1;
+        min-width: 0;
+
+        .icon {
+          width: 24px;
+          height: 24px;
+          color: var(--primary-color);
+          opacity: 0.7;
+        }
+
+        span {
+          font-size: 12px;
+          font-weight: 500;
+          color: var(--medium-color);
+          text-align: center;
+          white-space: nowrap;
+        }
+      }
+    }
+  }
+}
+
+// 移动端适配
+@media (max-width: 480px) {
+  .survey-card {
+    margin: 12px 8px;
+    border-radius: 10px;
+
+    .survey-header {
+      padding: 12px;
+
+      .survey-title {
+        gap: 8px;
+
+        .icon {
+          width: 20px;
+          height: 20px;
+        }
+
+        span {
+          font-size: 15px;
+        }
+      }
+
+      .survey-badge {
+        font-size: 11px;
+        padding: 3px 10px;
+      }
+    }
+
+    .survey-content {
+      padding: 16px 12px;
+
+      .survey-info {
+        gap: 10px;
+
+        .icon {
+          width: 36px;
+          height: 36px;
+        }
+
+        .survey-text {
+          .survey-desc {
+            font-size: 14px;
+          }
+
+          .survey-meta {
+            font-size: 12px;
+          }
+        }
+      }
+
+      .button {
+        padding: 10px 16px;
+        font-size: 13px;
+
+        .icon {
+          width: 16px;
+          height: 16px;
+        }
+      }
+
+      .survey-tips {
+        padding: 10px 12px;
+        font-size: 12px;
+
+        .icon {
+          width: 18px;
+          height: 18px;
+        }
+      }
+    }
+
+    .survey-footer {
+      padding: 10px 12px;
+
+      .survey-highlights {
+        gap: 6px;
+
+        .highlight-item {
+          gap: 4px;
+
+          .icon {
+            width: 20px;
+            height: 20px;
+          }
+
+          span {
+            font-size: 11px;
+          }
+        }
+      }
+    }
+  }
+}

+ 11 - 1
src/modules/project/pages/project-detail/project-detail.component.ts

@@ -30,7 +30,17 @@ const Parse = FmodeParse.with('nova');
 @Component({
   selector: 'app-project-detail',
   standalone: true,
-  imports: [CommonModule, IonicModule, RouterModule, ProjectBottomCardComponent, ProjectFilesModalComponent, ProjectMembersModalComponent, ProjectIssuesModalComponent, CustomerProfileComponent, CustomerSelectorComponent],
+  imports: [
+    CommonModule,
+    IonicModule,
+    RouterModule,
+    ProjectBottomCardComponent,
+    ProjectFilesModalComponent,
+    ProjectMembersModalComponent,
+    ProjectIssuesModalComponent,
+    CustomerProfileComponent,
+    CustomerSelectorComponent
+  ],
   templateUrl: './project-detail.component.html',
   styleUrls: ['./project-detail.component.scss']
 })