|
@@ -1,243 +1,292 @@
|
|
|
-<ion-header>
|
|
|
|
|
- <ion-toolbar>
|
|
|
|
|
- <ion-buttons slot="start">
|
|
|
|
|
- <ion-button (click)="goBack()">
|
|
|
|
|
- <ion-icon name="arrow-back"></ion-icon>
|
|
|
|
|
- </ion-button>
|
|
|
|
|
- </ion-buttons>
|
|
|
|
|
- <ion-title>项目需求调查</ion-title>
|
|
|
|
|
- </ion-toolbar>
|
|
|
|
|
-</ion-header>
|
|
|
|
|
-
|
|
|
|
|
-<ion-content class="survey-content">
|
|
|
|
|
|
|
+<!-- 顶部导航栏 -->
|
|
|
|
|
+<div class="survey-header">
|
|
|
|
|
+ <div class="header-content">
|
|
|
|
|
+ <button class="back-button" (click)="goBack()">
|
|
|
|
|
+ <svg class="icon" viewBox="0 0 512 512">
|
|
|
|
|
+ <path fill="currentColor" d="M244 400L100 256l144-144M120 256h292"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <h1 class="header-title">项目需求调查</h1>
|
|
|
|
|
+ <div class="header-spacer"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</div>
|
|
|
|
|
+
|
|
|
|
|
+<!-- 主内容区 -->
|
|
|
|
|
+<div class="survey-container">
|
|
|
<!-- 加载状态 -->
|
|
<!-- 加载状态 -->
|
|
|
@if (loading) {
|
|
@if (loading) {
|
|
|
- <div class="loading-container">
|
|
|
|
|
- <ion-spinner name="crescent"></ion-spinner>
|
|
|
|
|
- <p>加载中...</p>
|
|
|
|
|
|
|
+ <div class="status-view loading-view">
|
|
|
|
|
+ <div class="spinner">
|
|
|
|
|
+ <div class="spinner-circle"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <p class="status-text">加载中...</p>
|
|
|
</div>
|
|
</div>
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
<!-- 错误状态 -->
|
|
<!-- 错误状态 -->
|
|
|
@if (error && !loading) {
|
|
@if (error && !loading) {
|
|
|
- <div class="error-container">
|
|
|
|
|
- <ion-icon name="alert-circle-outline"></ion-icon>
|
|
|
|
|
- <p>{{ error }}</p>
|
|
|
|
|
- <ion-button (click)="goBack()">返回</ion-button>
|
|
|
|
|
|
|
+ <div class="status-view error-view">
|
|
|
|
|
+ <div class="error-icon-wrapper">
|
|
|
|
|
+ <svg class="icon error-icon" viewBox="0 0 512 512">
|
|
|
|
|
+ @if (isCustomerOnly) {
|
|
|
|
|
+ <!-- 仅限客户图标 -->
|
|
|
|
|
+ <path fill="currentColor" d="M336 256c-20.56 0-40.44-9.18-56-25.84-15.13-16.25-24.37-37.92-26-61-1.74-24.62 5.77-47.26 21.14-63.76S312 80 336 80c23.83 0 45.38 9.06 60.7 25.52 15.47 16.62 23 39.22 21.26 63.63-1.67 23.11-10.9 44.77-26 61C376.44 246.82 356.57 256 336 256zm66-88c0-51.18-42.82-92-94-92s-94 40.82-94 92 42.82 92 94 92 94-40.82 94-92z" opacity=".3"/>
|
|
|
|
|
+ <path fill="currentColor" d="M336 256c-20.56 0-40.44-9.18-56-25.84-15.13-16.25-24.37-37.92-26-61-1.74-24.62 5.77-47.26 21.14-63.76S312 80 336 80c23.83 0 45.38 9.06 60.7 25.52 15.47 16.62 23 39.22 21.26 63.63-1.67 23.11-10.9 44.77-26 61C376.44 246.82 356.57 256 336 256zM467.83 432H204.18a27.71 27.71 0 01-22-10.67 30.22 30.22 0 01-5.26-25.79c8.42-33.81 29.28-61.85 60.32-81.08C264.79 297.4 299.86 288 336 288c36.85 0 71 9.23 98.83 26.73 31.45 19.86 52.3 48 60.38 81.55a30.27 30.27 0 01-5.32 25.78A27.68 27.68 0 01467.83 432z"/>
|
|
|
|
|
+ } @else {
|
|
|
|
|
+ <!-- 常规错误图标 -->
|
|
|
|
|
+ <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>
|
|
|
|
|
+ <h2 class="error-title">{{ isCustomerOnly ? '仅限客户填写' : '加载失败' }}</h2>
|
|
|
|
|
+ <p class="error-message">{{ error }}</p>
|
|
|
|
|
+ @if (isCustomerOnly) {
|
|
|
|
|
+ <div class="info-box">
|
|
|
|
|
+ <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>
|
|
|
|
|
+ <div class="info-text">
|
|
|
|
|
+ <p class="info-title">客户填写入口</p>
|
|
|
|
|
+ <p class="info-desc">请通过企微群聊中收到的问卷链接进入</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+ <button class="btn-primary" (click)="goBack()">返回</button>
|
|
|
</div>
|
|
</div>
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
<!-- 欢迎页 -->
|
|
<!-- 欢迎页 -->
|
|
|
@if (currentState === 'welcome' && !loading && !error) {
|
|
@if (currentState === 'welcome' && !loading && !error) {
|
|
|
- <div class="welcome-page">
|
|
|
|
|
- <div class="welcome-header">
|
|
|
|
|
- @if (currentContact) {
|
|
|
|
|
- <div class="user-avatar">
|
|
|
|
|
- <img [src]="currentContact.get('data')?.avatar || 'assets/default-avatar.png'" alt="头像" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <h2>您好,{{ currentContact.get('realname') || currentContact.get('name') }}</h2>
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ <div class="welcome-view">
|
|
|
|
|
+ <!-- 用户信息卡片 -->
|
|
|
|
|
+ <div class="user-card">
|
|
|
|
|
+ <div class="user-avatar">
|
|
|
|
|
+ <img [src]="currentContact?.get('data')?.avatar || 'assets/default-avatar.png'" alt="头像" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <h2 class="user-greeting">您好, {{ currentContact?.get('realname') || currentContact?.get('name') }}</h2>
|
|
|
|
|
+ <p class="user-subtitle">欢迎参与需求调查</p>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="welcome-content">
|
|
|
|
|
- <h1>《家装效果图服务初次合作需求调查表》</h1>
|
|
|
|
|
|
|
+ <!-- 问卷介绍 -->
|
|
|
|
|
+ <div class="intro-card">
|
|
|
|
|
+ <div class="intro-header">
|
|
|
|
|
+ <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"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ <h3>家装效果图服务需求调查</h3>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- <div class="welcome-intro">
|
|
|
|
|
- <p>尊敬的伙伴:</p>
|
|
|
|
|
- <p>为让本次效果图服务更贴合您的工作节奏与核心需求,我们准备了简短选择式问卷,您的偏好将直接帮我们校准服务方向,感谢支持!</p>
|
|
|
|
|
|
|
+ <div class="intro-body">
|
|
|
|
|
+ <p class="intro-text">
|
|
|
|
|
+ 尊敬的伙伴,为让本次效果图服务更贴合您的工作节奏与核心需求,我们准备了简短选择式问卷。
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p class="intro-text">
|
|
|
|
|
+ 您的偏好将直接帮我们校准服务方向,感谢支持!
|
|
|
|
|
+ </p>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="survey-info">
|
|
|
|
|
- <div class="info-item">
|
|
|
|
|
- <ion-icon name="time-outline"></ion-icon>
|
|
|
|
|
- <span>预计用时: 3-5分钟</span>
|
|
|
|
|
|
|
+ <!-- 问卷信息标签 -->
|
|
|
|
|
+ <div class="info-tags">
|
|
|
|
|
+ <div class="info-tag">
|
|
|
|
|
+ <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>
|
|
|
- <div class="info-item">
|
|
|
|
|
- <ion-icon name="list-outline"></ion-icon>
|
|
|
|
|
- <span>题目数量: {{ effectiveQuestions.length }}题</span>
|
|
|
|
|
|
|
+ <div class="info-tag">
|
|
|
|
|
+ <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"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ <span>{{ effectiveQuestions.length }}道题</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="info-item">
|
|
|
|
|
- <ion-icon name="checkmark-circle-outline"></ion-icon>
|
|
|
|
|
- <span>题型: 选择题为主</span>
|
|
|
|
|
|
|
+ <div class="info-tag">
|
|
|
|
|
+ <svg class="icon" viewBox="0 0 512 512">
|
|
|
|
|
+ <path fill="currentColor" d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ <span>选择为主</span>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
- <ion-button expand="block" size="large" (click)="startSurvey()" class="start-button">
|
|
|
|
|
- 开始填写
|
|
|
|
|
- </ion-button>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 开始按钮 -->
|
|
|
|
|
+ <button class="btn-start" (click)="startSurvey()">
|
|
|
|
|
+ <span>开始填写</span>
|
|
|
|
|
+ <svg class="icon" viewBox="0 0 512 512">
|
|
|
|
|
+ <path fill="currentColor" d="M294.1 256L167 129c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.3 34 0L345 239c9.1 9.1 9.3 23.7.7 33.1L201.1 417c-4.7 4.7-10.9 7-17 7s-12.3-2.3-17-7c-9.4-9.4-9.4-24.6 0-33.9l127-127.1z"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </button>
|
|
|
</div>
|
|
</div>
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
<!-- 答题页 -->
|
|
<!-- 答题页 -->
|
|
|
@if (currentState === 'questionnaire' && !loading && !error) {
|
|
@if (currentState === 'questionnaire' && !loading && !error) {
|
|
|
- <div class="questionnaire-page">
|
|
|
|
|
- <!-- 进度条 -->
|
|
|
|
|
- <div class="progress-bar">
|
|
|
|
|
- <div class="progress-fill" [style.width.%]="getProgress()"></div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="progress-text">
|
|
|
|
|
- {{ currentQuestionIndex + 1 }} / {{ effectiveQuestions.length }}
|
|
|
|
|
|
|
+ <div class="questionnaire-view">
|
|
|
|
|
+ <!-- 进度指示器 -->
|
|
|
|
|
+ <div class="progress-section">
|
|
|
|
|
+ <div class="progress-bar-wrapper">
|
|
|
|
|
+ <div class="progress-bar">
|
|
|
|
|
+ <div class="progress-fill" [style.width.%]="getProgress()"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="progress-info">
|
|
|
|
|
+ <span class="progress-current">{{ currentQuestionIndex + 1 }}</span>
|
|
|
|
|
+ <span class="progress-separator">/</span>
|
|
|
|
|
+ <span class="progress-total">{{ effectiveQuestions.length }}</span>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
@if (getCurrentQuestion(); as question) {
|
|
@if (getCurrentQuestion(); as question) {
|
|
|
- <div class="question-container">
|
|
|
|
|
- <!-- 章节标题 -->
|
|
|
|
|
- <div class="section-title">{{ question.section }}</div>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 题目 -->
|
|
|
|
|
- <div class="question-title">
|
|
|
|
|
- <span class="question-number">{{ currentQuestionIndex + 1 }}.</span>
|
|
|
|
|
- {{ question.title }}
|
|
|
|
|
- @if (question.required) {
|
|
|
|
|
- <span class="required-mark">*</span>
|
|
|
|
|
- }
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 单选题 -->
|
|
|
|
|
- @if (question.type === 'single') {
|
|
|
|
|
- <div class="options-container">
|
|
|
|
|
- @for (option of question.options; track option) {
|
|
|
|
|
- <div
|
|
|
|
|
- class="option-item"
|
|
|
|
|
- [class.selected]="answers[question.id] === option"
|
|
|
|
|
- (click)="selectSingleOption(option)"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="option-radio">
|
|
|
|
|
- @if (answers[question.id] === option) {
|
|
|
|
|
- <ion-icon name="radio-button-on"></ion-icon>
|
|
|
|
|
- } @else {
|
|
|
|
|
- <ion-icon name="radio-button-off"></ion-icon>
|
|
|
|
|
- }
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="option-text">{{ option }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <!-- 题目卡片 -->
|
|
|
|
|
+ <div class="question-card">
|
|
|
|
|
+ <!-- 章节标签 -->
|
|
|
|
|
+ <div class="section-badge">{{ question.section }}</div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 题目内容 -->
|
|
|
|
|
+ <div class="question-content">
|
|
|
|
|
+ <h3 class="question-title">
|
|
|
|
|
+ <span class="question-number">{{ currentQuestionIndex + 1 }}.</span>
|
|
|
|
|
+ <span class="question-text">{{ question.title }}</span>
|
|
|
|
|
+ @if (question.required) {
|
|
|
|
|
+ <span class="required-star">*</span>
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- @if (question.hasOther) {
|
|
|
|
|
- <div
|
|
|
|
|
- class="option-item"
|
|
|
|
|
- [class.selected]="answers[question.id]?.startsWith('其他')"
|
|
|
|
|
- (click)="selectSingleOption('其他')"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="option-radio">
|
|
|
|
|
- @if (answers[question.id]?.startsWith('其他')) {
|
|
|
|
|
- <ion-icon name="radio-button-on"></ion-icon>
|
|
|
|
|
- } @else {
|
|
|
|
|
- <ion-icon name="radio-button-off"></ion-icon>
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ </h3>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 单选题 -->
|
|
|
|
|
+ @if (question.type === 'single') {
|
|
|
|
|
+ <div class="options-list">
|
|
|
|
|
+ @for (option of question.options; track option) {
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="option-item"
|
|
|
|
|
+ [class.selected]="answers[question.id] === option"
|
|
|
|
|
+ (click)="selectSingleOption(option)">
|
|
|
|
|
+ <div class="option-radio">
|
|
|
|
|
+ <div class="radio-outer">
|
|
|
|
|
+ <div class="radio-inner"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <span class="option-label">{{ option }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="option-text">其他</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- }
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- @if (showOtherInput) {
|
|
|
|
|
- <div class="other-input-container">
|
|
|
|
|
- <ion-input
|
|
|
|
|
- [(ngModel)]="otherInput"
|
|
|
|
|
- placeholder="请输入其他内容..."
|
|
|
|
|
- class="other-input"
|
|
|
|
|
- ></ion-input>
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @if (question.hasOther) {
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="option-item"
|
|
|
|
|
+ [class.selected]="answers[question.id]?.startsWith('其他')"
|
|
|
|
|
+ (click)="selectSingleOption('其他')">
|
|
|
|
|
+ <div class="option-radio">
|
|
|
|
|
+ <div class="radio-outer">
|
|
|
|
|
+ <div class="radio-inner"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <span class="option-label">其他</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
</div>
|
|
</div>
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- <!-- 多选题 -->
|
|
|
|
|
- @if (question.type === 'multiple') {
|
|
|
|
|
- <div class="options-container">
|
|
|
|
|
- @for (option of question.options; track option) {
|
|
|
|
|
- <div
|
|
|
|
|
- class="option-item"
|
|
|
|
|
- [class.selected]="hasMultipleOption(question.id, option)"
|
|
|
|
|
- (click)="toggleMultipleOption(option)"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="option-checkbox">
|
|
|
|
|
- @if (hasMultipleOption(question.id, option)) {
|
|
|
|
|
- <ion-icon name="checkbox"></ion-icon>
|
|
|
|
|
- } @else {
|
|
|
|
|
- <ion-icon name="square-outline"></ion-icon>
|
|
|
|
|
- }
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="option-text">{{ option }}</div>
|
|
|
|
|
|
|
+ @if (showOtherInput) {
|
|
|
|
|
+ <div class="input-wrapper">
|
|
|
|
|
+ <input
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ class="text-input"
|
|
|
|
|
+ [(ngModel)]="otherInput"
|
|
|
|
|
+ placeholder="请输入其他内容..."
|
|
|
|
|
+ autofocus />
|
|
|
</div>
|
|
</div>
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- @if (question.hasOther) {
|
|
|
|
|
- <div
|
|
|
|
|
- class="option-item"
|
|
|
|
|
- [class.selected]="hasMultipleOptionStartsWith(question.id, '其他')"
|
|
|
|
|
- (click)="toggleMultipleOption('其他')"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="option-checkbox">
|
|
|
|
|
- @if (hasMultipleOptionStartsWith(question.id, '其他')) {
|
|
|
|
|
- <ion-icon name="checkbox"></ion-icon>
|
|
|
|
|
- } @else {
|
|
|
|
|
- <ion-icon name="square-outline"></ion-icon>
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ <!-- 多选题 -->
|
|
|
|
|
+ @if (question.type === 'multiple') {
|
|
|
|
|
+ <div class="options-list">
|
|
|
|
|
+ @for (option of question.options; track option) {
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="option-item"
|
|
|
|
|
+ [class.selected]="hasMultipleOption(question.id, option)"
|
|
|
|
|
+ (click)="toggleMultipleOption(option)">
|
|
|
|
|
+ <div class="option-checkbox">
|
|
|
|
|
+ <div class="checkbox-box">
|
|
|
|
|
+ <svg class="icon checkmark" viewBox="0 0 512 512">
|
|
|
|
|
+ <path fill="currentColor" d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <span class="option-label">{{ option }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @if (question.hasOther) {
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="option-item"
|
|
|
|
|
+ [class.selected]="hasMultipleOptionStartsWith(question.id, '其他')"
|
|
|
|
|
+ (click)="toggleMultipleOption('其他')">
|
|
|
|
|
+ <div class="option-checkbox">
|
|
|
|
|
+ <div class="checkbox-box">
|
|
|
|
|
+ <svg class="icon checkmark" viewBox="0 0 512 512">
|
|
|
|
|
+ <path fill="currentColor" d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <span class="option-label">其他</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="option-text">其他</div>
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ @if (showOtherInput) {
|
|
|
|
|
+ <div class="input-wrapper">
|
|
|
|
|
+ <input
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ class="text-input"
|
|
|
|
|
+ [(ngModel)]="otherInput"
|
|
|
|
|
+ placeholder="请输入其他内容..." />
|
|
|
</div>
|
|
</div>
|
|
|
}
|
|
}
|
|
|
- </div>
|
|
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- @if (showOtherInput) {
|
|
|
|
|
- <div class="other-input-container">
|
|
|
|
|
- <ion-input
|
|
|
|
|
- [(ngModel)]="otherInput"
|
|
|
|
|
- placeholder="请输入其他内容..."
|
|
|
|
|
- class="other-input"
|
|
|
|
|
- ></ion-input>
|
|
|
|
|
|
|
+ <!-- 文本题 -->
|
|
|
|
|
+ @if (question.type === 'text') {
|
|
|
|
|
+ <div class="input-wrapper">
|
|
|
|
|
+ <textarea
|
|
|
|
|
+ class="textarea-input"
|
|
|
|
|
+ [(ngModel)]="answers[question.id]"
|
|
|
|
|
+ [placeholder]="question.placeholder || '请输入...'"
|
|
|
|
|
+ rows="4"></textarea>
|
|
|
</div>
|
|
</div>
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- <!-- 文本题 -->
|
|
|
|
|
- @if (question.type === 'text') {
|
|
|
|
|
- <div class="text-input-container">
|
|
|
|
|
- <ion-textarea
|
|
|
|
|
- [(ngModel)]="answers[question.id]"
|
|
|
|
|
- [placeholder]="question.placeholder || '请输入...'"
|
|
|
|
|
- rows="4"
|
|
|
|
|
- class="text-input"
|
|
|
|
|
- ></ion-textarea>
|
|
|
|
|
- </div>
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- <!-- 数字题 -->
|
|
|
|
|
- @if (question.type === 'number') {
|
|
|
|
|
- <div class="number-input-container">
|
|
|
|
|
- <ion-input
|
|
|
|
|
- type="number"
|
|
|
|
|
- [(ngModel)]="answers[question.id]"
|
|
|
|
|
- [placeholder]="question.placeholder || '请输入数字...'"
|
|
|
|
|
- class="number-input"
|
|
|
|
|
- ></ion-input>
|
|
|
|
|
- </div>
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ <!-- 数字题 -->
|
|
|
|
|
+ @if (question.type === 'number') {
|
|
|
|
|
+ <div class="input-wrapper">
|
|
|
|
|
+ <input
|
|
|
|
|
+ type="number"
|
|
|
|
|
+ class="text-input"
|
|
|
|
|
+ [(ngModel)]="answers[question.id]"
|
|
|
|
|
+ [placeholder]="question.placeholder || '请输入数字...'" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 导航按钮 -->
|
|
<!-- 导航按钮 -->
|
|
|
<div class="nav-buttons">
|
|
<div class="nav-buttons">
|
|
|
- <ion-button
|
|
|
|
|
- fill="outline"
|
|
|
|
|
- (click)="previousQuestion()"
|
|
|
|
|
|
|
+ <button
|
|
|
|
|
+ class="btn-nav btn-prev"
|
|
|
[disabled]="currentQuestionIndex === 0"
|
|
[disabled]="currentQuestionIndex === 0"
|
|
|
- >
|
|
|
|
|
- <ion-icon name="chevron-back" slot="start"></ion-icon>
|
|
|
|
|
- 上一题
|
|
|
|
|
- </ion-button>
|
|
|
|
|
-
|
|
|
|
|
- <ion-button
|
|
|
|
|
- (click)="nextQuestion()"
|
|
|
|
|
- >
|
|
|
|
|
- @if (currentQuestionIndex >= effectiveQuestions.length - 1) {
|
|
|
|
|
- 提交
|
|
|
|
|
- } @else {
|
|
|
|
|
- 下一题
|
|
|
|
|
- }
|
|
|
|
|
- <ion-icon name="chevron-forward" slot="end"></ion-icon>
|
|
|
|
|
- </ion-button>
|
|
|
|
|
|
|
+ (click)="previousQuestion()">
|
|
|
|
|
+ <svg class="icon" viewBox="0 0 512 512">
|
|
|
|
|
+ <path fill="currentColor" d="M217.9 256L345 129c9.4-9.4 9.4-24.6 0-33.9-9.4-9.4-24.6-9.3-34 0L167 239c-9.1 9.1-9.3 23.7-.7 33.1L310.9 417c4.7 4.7 10.9 7 17 7s12.3-2.3 17-7c9.4-9.4 9.4-24.6 0-33.9L217.9 256z"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ <span>上一题</span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+
|
|
|
|
|
+ <button
|
|
|
|
|
+ class="btn-nav btn-next"
|
|
|
|
|
+ (click)="nextQuestion()">
|
|
|
|
|
+ <span>{{ currentQuestionIndex >= effectiveQuestions.length - 1 ? '提交' : '下一题' }}</span>
|
|
|
|
|
+ <svg class="icon" viewBox="0 0 512 512">
|
|
|
|
|
+ <path fill="currentColor" d="M294.1 256L167 129c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.3 34 0L345 239c9.1 9.1 9.3 23.7.7 33.1L201.1 417c-4.7 4.7-10.9 7-17 7s-12.3-2.3-17-7c-9.4-9.4-9.4-24.6 0-33.9l127-127.1z"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </button>
|
|
|
</div>
|
|
</div>
|
|
|
}
|
|
}
|
|
|
</div>
|
|
</div>
|
|
@@ -245,81 +294,89 @@
|
|
|
|
|
|
|
|
<!-- 结果页 -->
|
|
<!-- 结果页 -->
|
|
|
@if (currentState === 'result' && !loading && !error) {
|
|
@if (currentState === 'result' && !loading && !error) {
|
|
|
- <div class="result-page">
|
|
|
|
|
- <div class="result-header">
|
|
|
|
|
- <ion-icon name="checkmark-circle" color="success"></ion-icon>
|
|
|
|
|
- <h2>问卷提交成功</h2>
|
|
|
|
|
- <p>感谢您的反馈!</p>
|
|
|
|
|
- <p>我们将根据您的选择制定服务方案</p>
|
|
|
|
|
|
|
+ <div class="result-view">
|
|
|
|
|
+ <!-- 成功图标 -->
|
|
|
|
|
+ <div class="success-icon-wrapper">
|
|
|
|
|
+ <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>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="result-content">
|
|
|
|
|
- <h3>【您的答卷】</h3>
|
|
|
|
|
|
|
+ <h2 class="result-title">问卷提交成功</h2>
|
|
|
|
|
+ <p class="result-subtitle">感谢您的反馈!</p>
|
|
|
|
|
+ <p class="result-desc">我们将根据您的选择制定服务方案</p>
|
|
|
|
|
|
|
|
- <div class="result-item">
|
|
|
|
|
- <div class="result-label">核心服务:</div>
|
|
|
|
|
- <div class="result-value">{{ getFormattedAnswer('q1') }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <!-- 答卷内容 -->
|
|
|
|
|
+ <div class="result-card">
|
|
|
|
|
+ <h3 class="result-card-title">您的答卷</h3>
|
|
|
|
|
|
|
|
- <div class="result-item">
|
|
|
|
|
- <div class="result-label">空间数量:</div>
|
|
|
|
|
- <div class="result-value">{{ getFormattedAnswer('q2') }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="result-item">
|
|
|
|
|
- <div class="result-label">价值侧重:</div>
|
|
|
|
|
- <div class="result-value">{{ getFormattedAnswer('q3') }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="result-item">
|
|
|
|
|
- <div class="result-label">技术配合:</div>
|
|
|
|
|
- <div class="result-value">{{ getFormattedAnswer('q4') }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="result-list">
|
|
|
|
|
+ <div class="result-item">
|
|
|
|
|
+ <div class="result-label">核心服务</div>
|
|
|
|
|
+ <div class="result-value">{{ getFormattedAnswer('q1') }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- <div class="result-item">
|
|
|
|
|
- <div class="result-label">协作方式:</div>
|
|
|
|
|
- <div class="result-value">{{ getFormattedAnswer('q5') }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="result-item">
|
|
|
|
|
+ <div class="result-label">空间数量</div>
|
|
|
|
|
+ <div class="result-value">{{ getFormattedAnswer('q2') }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- @if (answers['q6']) {
|
|
|
|
|
<div class="result-item">
|
|
<div class="result-item">
|
|
|
- <div class="result-label">注意事项:</div>
|
|
|
|
|
- <div class="result-value">{{ getFormattedAnswer('q6') }}</div>
|
|
|
|
|
|
|
+ <div class="result-label">价值侧重</div>
|
|
|
|
|
+ <div class="result-value">{{ getFormattedAnswer('q3') }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- @if (answers['q7']) {
|
|
|
|
|
<div class="result-item">
|
|
<div class="result-item">
|
|
|
- <div class="result-label">特殊要求:</div>
|
|
|
|
|
- <div class="result-value">{{ getFormattedAnswer('q7') }}</div>
|
|
|
|
|
|
|
+ <div class="result-label">技术配合</div>
|
|
|
|
|
+ <div class="result-value">{{ getFormattedAnswer('q4') }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- @if (answers['q8']) {
|
|
|
|
|
<div class="result-item">
|
|
<div class="result-item">
|
|
|
- <div class="result-label">参考素材:</div>
|
|
|
|
|
- <div class="result-value">{{ getFormattedAnswer('q8') }}</div>
|
|
|
|
|
|
|
+ <div class="result-label">协作方式</div>
|
|
|
|
|
+ <div class="result-value">{{ getFormattedAnswer('q5') }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- <div class="result-divider"></div>
|
|
|
|
|
|
|
+ @if (answers['q6']) {
|
|
|
|
|
+ <div class="result-item">
|
|
|
|
|
+ <div class="result-label">注意事项</div>
|
|
|
|
|
+ <div class="result-value">{{ getFormattedAnswer('q6') }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @if (answers['q7']) {
|
|
|
|
|
+ <div class="result-item">
|
|
|
|
|
+ <div class="result-label">特殊要求</div>
|
|
|
|
|
+ <div class="result-value">{{ getFormattedAnswer('q7') }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- <div class="result-item">
|
|
|
|
|
- <div class="result-label">对接人:</div>
|
|
|
|
|
- <div class="result-value">{{ answers['contact_name'] || currentContact?.get('realname') || '-' }}</div>
|
|
|
|
|
|
|
+ @if (answers['q8']) {
|
|
|
|
|
+ <div class="result-item">
|
|
|
|
|
+ <div class="result-label">参考素材</div>
|
|
|
|
|
+ <div class="result-value">{{ getFormattedAnswer('q8') }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="result-item">
|
|
|
|
|
- <div class="result-label">电话:</div>
|
|
|
|
|
- <div class="result-value">
|
|
|
|
|
- {{ maskPhone(answers['contact_phone'] || currentContact?.get('mobile') || '') }}
|
|
|
|
|
|
|
+ <div class="result-divider"></div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="result-list">
|
|
|
|
|
+ <div class="result-item">
|
|
|
|
|
+ <div class="result-label">对接人</div>
|
|
|
|
|
+ <div class="result-value">{{ answers['contact_name'] || currentContact?.get('realname') || '-' }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="result-item">
|
|
|
|
|
+ <div class="result-label">电话</div>
|
|
|
|
|
+ <div class="result-value">{{ maskPhone(answers['contact_phone'] || currentContact?.get('mobile') || '') }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <ion-button expand="block" size="large" (click)="goBack()" class="back-button">
|
|
|
|
|
- 返回项目
|
|
|
|
|
- </ion-button>
|
|
|
|
|
|
|
+ <!-- 返回按钮 -->
|
|
|
|
|
+ <button class="btn-primary" (click)="goBack()">
|
|
|
|
|
+ <span>返回项目</span>
|
|
|
|
|
+ </button>
|
|
|
</div>
|
|
</div>
|
|
|
}
|
|
}
|
|
|
-</ion-content>
|
|
|
|
|
|
|
+</div>
|