Ver Fonte

feat: add six thinking hats activity feature

新增六顶思考帽互动活动功能,包括多语言文案配置、页面组件注册与数据提交逻辑适配
lsc há 5 dias atrás
pai
commit
47f1fe1731

+ 6 - 0
.trae/rules/git-commit-message.md

@@ -0,0 +1,6 @@
+---
+alwaysApply: true
+scene: git_message
+---
+中文
+在此处编写规则,自定义 AI 生成提交信息的风格。

+ 2 - 0
src/components/pages/pptEasy/addCourse3.vue

@@ -2768,6 +2768,8 @@ export default {
       let title = ""
       if (tool == 831){
         title = this.lang.ssKWLTitle
+      } else if (tool == 832){
+        title = this.lang.ssSHTitle
       }
       this.dialogVisibleChoice = false;
       let _data = { id: new Date().getTime(), tool: tool, title: title, json: data, url: "" }

+ 2152 - 0
src/components/pages/workPage/components/sixHatPage.vue

@@ -0,0 +1,2152 @@
+<template>
+  <div class="shPage">
+    <!-- ============ 成员选择阶段 ============ -->
+    <div class="shMemberStage" v-if="work.stage === 'member'">
+      <div class="shMemberCard">
+        <div class="shMemberIconWrap">
+          <div class="shMemberIcon">
+            <svg viewBox="0 0 24 24" width="40" height="40" fill="none" stroke="#F5A623" stroke-width="2">
+              <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>
+          </div>
+        </div>
+        <h2 class="shMemberTitle">{{ lang.ssSHMemberTitle }}</h2>
+        <p class="shMemberSubtitle">{{ lang.ssSHMemberSubtitle }}</p>
+
+        <div class="shMemberBox">
+          <el-select v-model="work.members" multiple filterable :filter-method="onMemberFilter" collapse-tags
+            placeholder="" :popper-class="'shMemberSelectPopper'" :no-data-text="' '" @change="onMemberChange">
+            <el-option v-for="stu in filteredStudents" :key="stu.id" :label="stu.name" :value="stu.id">
+              <span>{{ stu.name }}</span>
+            </el-option>
+          </el-select>
+        </div>
+
+        <button class="shMemberConfirmBtn"
+          :class="{ shMemberConfirmDisabled: !work.members || work.members.length === 0 }"
+          :disabled="!work.members || work.members.length === 0" @click="confirmMembers">
+          {{ lang.ssSHMemberConfirmN.replace('{n}', work.members ? work.members.length : 0) }}
+        </button>
+      </div>
+    </div>
+
+    <!-- ============ 设置内容阶段 ============ -->
+    <div class="shSetupStage" v-else-if="work.stage === 'setup'">
+      <!-- 顶部 tab -->
+      <div class="shTopTabs">
+        <div class="shTab shTabActive">
+          <div class="shTabNum">①</div>
+          <div class="shTabLabel">{{ lang.ssSHSetupContent }}</div>
+        </div>
+        <div class="shTabDivider"></div>
+        <div class="shTab shTabDisabled">
+          <div class="shTabNum">②</div>
+          <div class="shTabLabel">{{ lang.ssSHStartActivity }}</div>
+        </div>
+      </div>
+
+      <div class="shSetupHeader">
+        <h2 class="shSetupTitle">{{ lang.ssSHBeforeStart }}</h2>
+        <p class="shSetupDesc">{{ lang.ssSHSetupDesc }}</p>
+        <div class="shStepIndicator">
+          <div class="shStepIndicator" v-for="(label, idx) in stageLabels" :key="label">
+            <span class="shStepDot"
+              :class="{ shStepDotActive: idx <= setupStep, shStepDotCurrent: idx === setupStep }"></span>
+            <span class="shStepLabel" :class="{ shStepLabelActive: idx === setupStep }">{{ label }}</span>
+            <span v-if="idx < stageLabels.length - 1" class="shStepLine"></span>
+          </div>
+        </div>
+      </div>
+
+      <!-- 步骤 1:情境 -->
+      <div class="shStepCard" v-if="setupStep === 0">
+        <div class="shStepRow">
+          <div class="shStepLeft">
+            <div class="shStepTitleRow">
+              <div class="shStepIcon shScenarioIcon">
+                <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="#F5A623" stroke-width="2">
+                  <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
+                </svg>
+              </div>
+              <div>
+                <h3 class="shStepTitle">{{ lang.ssSHScenarioTitle }}</h3>
+                <p class="shStepSub">{{ lang.ssSHScenarioSubtitle }}</p>
+              </div>
+            </div>
+            <div class="shExampleLabel">{{ lang.ssSHScenarioExamples }}</div>
+            <ul class="shExampleList">
+              <li class="shExampleItem"><span class="shExampleDot">•</span>{{ lang.ssSHScenarioEx1 }}</li>
+              <li class="shExampleItem"><span class="shExampleDot">•</span>{{ lang.ssSHScenarioEx2 }}</li>
+              <li class="shExampleItem"><span class="shExampleDot">•</span>{{ lang.ssSHScenarioEx3 }}</li>
+            </ul>
+          </div>
+          <div class="shStepRight">
+            <div class="shInputTitle">{{ lang.ssSHScenarioInputTitle }}</div>
+            <div class="shTextareaWrap">
+              <textarea v-model="work.scenario" class="shTextarea" :placeholder="lang.ssSHScenarioInputPH"></textarea>
+              <div class="shMicBtn" :class="{ shMicBtnActive: recordObj.status == '1' }"
+                :title="recordObj.status == '1' ? lang.ssSHStopRec : lang.ssSHVoiceInput"
+                @click="recordObj.status == '1' ? stopRecord() : startRecord('scenario')">
+                <svg v-if="recordObj.status == '1'" width="18" height="18" viewBox="0 0 120 20">
+                  <rect x="5" y="5" width="8" :height="10" rx="3" fill="currentColor">
+                    <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0s" />
+                    <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0s" />
+                  </rect>
+                  <rect x="29" y="5" width="8" :height="10" rx="3" fill="currentColor">
+                    <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.2s" />
+                    <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.2s" />
+                  </rect>
+                  <rect x="53" y="5" width="8" :height="10" rx="3" fill="currentColor">
+                    <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.4s" />
+                    <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.4s" />
+                  </rect>
+                  <rect x="77" y="5" width="8" :height="10" rx="3" fill="currentColor">
+                    <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.6s" />
+                    <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.6s" />
+                  </rect>
+                  <rect x="101" y="5" width="8" :height="10" rx="3" fill="currentColor">
+                    <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.8s" />
+                    <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.8s" />
+                  </rect>
+                </svg>
+                <svg v-else viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor"
+                  stroke-width="2">
+                  <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path>
+                  <path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
+                  <line x1="12" y1="19" x2="12" y2="23"></line>
+                  <line x1="8" y1="23" x2="16" y2="23"></line>
+                </svg>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="shStepFooter shStepFooterSingle">
+          <button class="shPrimaryBtn" :class="{ shPrimaryBtnDisabled: !work.scenario || !work.scenario.trim() }"
+            :disabled="!work.scenario || !work.scenario.trim()" @click="setupStep = 1">{{ lang.ssSHStepNext }}</button>
+        </div>
+      </div>
+
+      <!-- 步骤 2:计时 -->
+      <div class="shStepCard" v-if="setupStep === 1">
+        <div class="shTimerWrap">
+          <div class="shTimerTopRow">
+            <div class="shStepTitleRow">
+              <div class="shStepIcon shTimerIcon">
+                <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="#F5A623" stroke-width="2">
+                  <circle cx="12" cy="12" r="10"></circle>
+                  <polyline points="12 6 12 12 16 14"></polyline>
+                </svg>
+              </div>
+              <div>
+                <h3 class="shStepTitle">{{ lang.ssSHTimerTitle }}</h3>
+                <p class="shStepSub">{{ lang.ssSHTimerSubtitle }}</p>
+              </div>
+            </div>
+            <div class="shSwitch" :class="{ shSwitchOn: work.timerEnabled }"
+              @click="work.timerEnabled = !work.timerEnabled">
+              <div class="shSwitchThumb"></div>
+            </div>
+          </div>
+          <template v-if="work.timerEnabled">
+            <div class="shTimerQuestion">{{ lang.ssSHTimerQuestion }}</div>
+            <div class="shTimerOpts">
+              <button v-for="(opt, idx) in timerPresets" :key="idx" class="shTimerOpt"
+                :class="{ shTimerOptActive: work.timerDuration === opt.value }"
+                @click="work.timerDuration = opt.value">{{ opt.label }}</button>
+              <button class="shTimerOpt" :class="{ shTimerOptActive: timerCustomFocused }" @click="focusTimerCustom">{{ lang.ssSHTimerCustom }}</button>
+              <input v-if="timerCustomFocused" type="number" min="1" class="shTimerCustomInput"
+                v-model.number="work.timerCustomValue" @blur="onTimerCustomBlur" @keyup.enter="onTimerCustomBlur" />
+            </div>
+            <div class="shTimerHint">
+              <div class="shTimerHintMain">
+                {{ lang.ssSHTimerPerHat.replace('{n}', work.timerDuration) }}
+              </div>
+              <div class="shTimerHintSub">{{ currentTimerHint }}</div>
+            </div>
+          </template>
+          <template v-else>
+            <div class="shTimerOffHint">
+              <div class="shTimerOffMain">{{ lang.ssSHTimerOff }}</div>
+              <div class="shTimerOffSub">{{ lang.ssSHTimerOffSub }}</div>
+            </div>
+          </template>
+        </div>
+        <div class="shStepFooter">
+          <button class="shSecondaryBtn" @click="setupStep = 0">{{ lang.ssSHStepPrev }}</button>
+          <button class="shPrimaryBtn" @click="setupStep = 2">{{ lang.ssSHStepNext }}</button>
+        </div>
+      </div>
+
+      <!-- 步骤 3:帽子 -->
+      <div class="shStepCard" v-if="setupStep === 2">
+        <h3 class="shHatsTitle">{{ lang.ssSHHatsTitle }}</h3>
+        <p class="shHatsSub">{{ lang.ssSHHatsSubtitle }}</p>
+        <div class="shHatsGrid">
+          <div v-for="hat in hatList" :key="hat.key" class="shHatCard"
+            :class="{ ['shHatSelected shHat' + hat.key + 'Sel']: work.hats.indexOf(hat.key) > -1 }"
+            @click="toggleHat(hat.key)">
+            <div v-if="work.hats.indexOf(hat.key) > -1" class="shHatCheck" :class="'shHatCheck' + hat.key">
+              <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="3">
+                <polyline points="20 6 9 17 4 12"></polyline>
+              </svg>
+            </div>
+            <div class="shHatIcon" :style="{ background: hat.bg }">
+              <svg v-if="hat.key === 'white'" viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="#666"
+                stroke-width="2">
+                <rect x="3" y="12" width="4" height="9"></rect>
+                <rect x="9" y="6" width="4" height="15"></rect>
+                <rect x="15" y="9" width="4" height="12"></rect>
+              </svg>
+              <svg v-else-if="hat.key === 'red'" viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="#fda4af"
+                stroke-width="2">
+                <path
+                  d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z">
+                </path>
+              </svg>
+              <svg v-else-if="hat.key === 'black'" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-alert w-4 h-4 text-white" aria-hidden="true"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path></svg>
+              <svg v-else-if="hat.key === 'yellow'" viewBox="0 0 24 24" width="28" height="28" fill="none"
+                stroke="#ca8a04" stroke-width="2">
+                <polygon
+                  points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2">
+                </polygon>
+              </svg>
+              <svg v-else-if="hat.key === 'green'" viewBox="0 0 24 24" width="28" height="28" fill="none"
+                stroke="#16a34a" stroke-width="2">
+                <path d="M9 18h6"></path>
+                <path d="M10 22h4"></path>
+                <path
+                  d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14">
+                </path>
+              </svg>
+              <svg v-else-if="hat.key === 'blue'" viewBox="0 0 24 24" width="28" height="28" fill="none"
+                stroke="#2563eb" stroke-width="2">
+                <circle cx="12" cy="12" r="10"></circle>
+                <circle cx="12" cy="12" r="4"></circle>
+              </svg>
+            </div>
+            <div class="shHatName">{{ hat.name }}</div>
+            <div class="shHatDesc">{{ hat.desc }}</div>
+          </div>
+        </div>
+        <div class="shStepFooter">
+          <button class="shSecondaryBtn" @click="setupStep = 1">{{ lang.ssSHStepPrev }}</button>
+          <button class="shPrimaryBtn shPrimaryBtnStart"
+            :class="{ shPrimaryBtnDisabled: !work.hats || work.hats.length === 0 }"
+            :disabled="!work.hats || work.hats.length === 0" @click="startActivity">{{ lang.ssSHHatsStart }}</button>
+        </div>
+      </div>
+    </div>
+
+    <!-- ============ 开始活动阶段 ============ -->
+    <div class="shActivityStage" v-else-if="work.stage === 'activity'">
+      <!-- 顶部 tab -->
+      <div class="shTopTabs" v-if="work.isSetting">
+        <div class="shTab shTabClickable" @click="backToSetup">
+          <div class="shTabNum">①</div>
+          <div class="shTabLabel">{{ lang.ssSHSetupContent }} {{ lang.ssSHClickBack }}</div>
+        </div>
+        <div class="shTabDivider"></div>
+        <div class="shTab shTabActive">
+          <div class="shTabNum">②</div>
+          <div class="shTabLabel">{{ lang.ssSHStartActivity }}</div>
+        </div>
+      </div>
+
+      <!-- 标题卡 -->
+      <div class="shHeaderCard" v-if="work.stage === 'activity'">
+        <div class="shHeaderCardLeft">
+          <div class="shHeaderCardTitle">{{ lang.ssSHTitle }}</div>
+          <div class="shHeaderCardSub">{{ lang.ssSHHowToDesc }}</div>
+        </div>
+        <div class="shHeaderCardRight" @click="showHowTo = !showHowTo">
+          <span>{{ lang.ssSHHowTo }}</span>
+          <i :class="['kwlArrow', { 'kwlArrowUp': showHowTo }]">
+            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
+              <polyline points="6 9 12 15 18 9"></polyline>
+            </svg>
+          </i>
+        </div>
+      </div>
+      <div class="shHowToContent" v-show="showHowTo">
+        <div class="shHowToList">
+          <div class="shHowToItem" v-for="(step, idx) in effectiveHowToSteps" :key="idx">{{ step }}</div>
+        </div>
+      </div>
+
+      <!-- 帽子圆点导航 -->
+      <div class="shHatDots">
+        <div class="shHatDots" v-for="(hatKey, idx) in selectedHats" :key="hatKey">
+          <div class="shHatDot"
+            :class="['shHatDot' + hatKey, { shHatDotDone: !!work.hatContent[hatKey], shHatDotCurrent: idx === currentHatIdx }]"
+            @click="jumpToHat(idx)"></div>
+          <span v-if="idx < selectedHats.length - 1" class="shHatDotLine"></span>
+        </div>
+      </div>
+
+      <!-- 主区域:左帽子卡 + 右输入卡 -->
+      <div class="shActivityRow">
+        <!-- 左侧帽子展示 -->
+        <div class="shActivityHatCard">
+          <div class="shActivityHatIcon" :class="'shActivityHatIcon' + currentHat.key"
+            :style="{ background: currentHat.bg }">
+            <svg v-if="currentHat.key === 'white'" viewBox="0 0 24 24" width="44" height="44" fill="none" stroke="#666"
+              stroke-width="2">
+              <rect x="3" y="12" width="4" height="9"></rect>
+              <rect x="9" y="6" width="4" height="15"></rect>
+              <rect x="15" y="9" width="4" height="12"></rect>
+            </svg>
+            <svg v-else-if="currentHat.key === 'red'" viewBox="0 0 24 24" width="44" height="44" fill="none"
+              stroke="#fda4af" stroke-width="2">
+              <path
+                d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z">
+              </path>
+            </svg>
+            <svg v-else-if="currentHat.key === 'black'" xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-alert w-4 h-4 text-white" aria-hidden="true"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path></svg>
+            <svg v-else-if="currentHat.key === 'yellow'" viewBox="0 0 24 24" width="44" height="44" fill="none"
+              stroke="#ca8a04" stroke-width="2">
+              <polygon
+                points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2">
+              </polygon>
+            </svg>
+            <svg v-else-if="currentHat.key === 'green'" viewBox="0 0 24 24" width="44" height="44" fill="none"
+              stroke="#16a34a" stroke-width="2">
+              <path d="M9 18h6"></path>
+              <path d="M10 22h4"></path>
+              <path
+                d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14">
+              </path>
+            </svg>
+            <svg v-else-if="currentHat.key === 'blue'" viewBox="0 0 24 24" width="44" height="44" fill="none"
+              stroke="#2563eb" stroke-width="2">
+              <circle cx="12" cy="12" r="10"></circle>
+              <circle cx="12" cy="12" r="4"></circle>
+            </svg>
+          </div>
+          <div class="shActivityHatName">{{ currentHat.name }}</div>
+          <div class="shActivityHatDesc">{{ currentHat.desc }}</div>
+          <div class="shActivityHatProgress">{{ lang.ssSHHatProgress.replace('{n}', currentHatIdx +
+            1).replace('{total}',
+            selectedHats.length) }}</div>
+          <!-- 引导问题 -->
+          <div class="shActivityHatPrompt">{{ currentHatPrompt }}</div>
+        </div>
+
+        <!-- 右侧输入 -->
+        <div class="shActivityInputCard">
+          <div class="shInputTitle">{{ lang.ssSHYourThoughts }}</div>
+          <div class="shTextareaWrap">
+            <textarea v-model="work.hatContent[currentHat.key]" class="shTextarea"
+              :placeholder="lang.ssSHYourThoughtsPH.replace('{hatName}', currentHat.name)"></textarea>
+            <div class="shMicBtn" :class="{ shMicBtnActive: recordObj.status == '1' }"
+              :title="recordObj.status == '1' ? lang.ssSHStopRec : lang.ssSHVoiceInput"
+              @click="recordObj.status == '1' ? stopRecord() : startRecord(currentHat.key)">
+              <svg v-if="recordObj.status == '1'" width="18" height="18" viewBox="0 0 120 20">
+                <rect x="5" y="5" width="8" :height="10" rx="3" fill="currentColor">
+                  <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0s" />
+                  <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0s" />
+                </rect>
+                <rect x="29" y="5" width="8" :height="10" rx="3" fill="currentColor">
+                  <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.2s" />
+                  <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.2s" />
+                </rect>
+                <rect x="53" y="5" width="8" :height="10" rx="3" fill="currentColor">
+                  <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.4s" />
+                  <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.4s" />
+                </rect>
+                <rect x="77" y="5" width="8" :height="10" rx="3" fill="currentColor">
+                  <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.6s" />
+                  <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.6s" />
+                </rect>
+                <rect x="101" y="5" width="8" :height="10" rx="3" fill="currentColor">
+                  <animate attributeName="height" values="10;20;10" dur="1s" repeatCount="indefinite" begin="0.8s" />
+                  <animate attributeName="y" values="10;0;10" dur="1s" repeatCount="indefinite" begin="0.8s" />
+                </rect>
+              </svg>
+              <svg v-else viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2">
+                <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path>
+                <path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
+                <line x1="12" y1="19" x2="12" y2="23"></line>
+                <line x1="8" y1="23" x2="16" y2="23"></line>
+              </svg>
+            </div>
+          </div>
+          <!-- 负责人(mock 显示) -->
+          <div class="shAssignee" v-if="memberNames.length">
+            <span class="shAssigneeLabel">{{ lang.ssOwner }}:</span>
+            <template v-if="memberNames.length">
+              <span v-for="n in memberNames" :key="n" class="shAssigneeTag">{{ n }}</span>
+            </template>
+            <template v-else>
+              <span class="shAssigneeTagEmpty">—</span>
+            </template>
+          </div>
+        </div>
+      </div>
+
+      <!-- 底部栏 -->
+      <div class="shActivityFooter" v-if="work.timerEnabled">
+        <div class="shAutoSwitchBtn" :class="{ shAutoSwitchOn: autoSwitch }" @click="toggleAutoSwitch">
+          <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2">
+            <circle cx="12" cy="12" r="10"></circle>
+            <polyline points="12 6 12 12 16 14"></polyline>
+          </svg>
+          <span>{{ lang.ssSHAutoSwitchOn }}</span>
+        </div>
+        <div class="shCountdown" v-if="autoSwitch">
+          {{ lang.ssSHCountdownSec.replace('{n}', countdownRemaining) }}
+        </div>
+      </div>
+      <div class="shFooterBtns">
+        <button class="shSecondaryBtn" :class="{ shSecondaryBtnDisabled: currentHatIdx === 0 }"
+          :disabled="currentHatIdx === 0" @click="prevHat">{{ lang.ssSHPrevHat }}</button>
+        <button class="shPrimaryBtn" @click="nextOrFinish">{{ isLastHat ? lang.ssSHFinish : lang.ssSHNextHat
+          }}</button>
+      </div>
+    </div>
+
+    <!-- ============ 总结回顾阶段 ============ -->
+    <div class="shReviewStage" v-else-if="work.stage === 'review'">
+      <div class="shReviewHeader">
+        <h2 class="shReviewTitle">{{ lang.ssSHSummaryTitle }}</h2>
+        <p class="shReviewSub">{{ lang.ssSHSummarySub }}</p>
+      </div>
+
+      <div class="shReviewList">
+        <div v-for="hat in selectedHatsDetail" :key="hat.key" class="shReviewCard" :class="['shReviewCard' + hat.key]"
+          @click="copyHatContent(hat)">
+          <div class="shReviewIcon" :style="{ background: hat.bg }">
+            <svg v-if="hat.key === 'white'" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="#666"
+              stroke-width="2">
+              <rect x="3" y="12" width="4" height="9"></rect>
+              <rect x="9" y="6" width="4" height="15"></rect>
+              <rect x="15" y="9" width="4" height="12"></rect>
+            </svg>
+            <svg v-else-if="hat.key === 'red'" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="#ef4444"
+              stroke-width="2">
+              <path
+                d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z">
+              </path>
+            </svg>
+            <svg v-else-if="hat.key === 'black'" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-alert w-4 h-4 text-white" aria-hidden="true"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path></svg>
+            <svg v-else-if="hat.key === 'yellow'" viewBox="0 0 24 24" width="22" height="22" fill="none"
+              stroke="#ca8a04" stroke-width="2">
+              <polygon
+                points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2">
+              </polygon>
+            </svg>
+            <svg v-else-if="hat.key === 'green'" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="#16a34a"
+              stroke-width="2">
+              <path d="M9 18h6"></path>
+              <path d="M10 22h4"></path>
+              <path
+                d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14">
+              </path>
+            </svg>
+            <svg v-else-if="hat.key === 'blue'" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="#2563eb"
+              stroke-width="2">
+              <circle cx="12" cy="12" r="10"></circle>
+              <circle cx="12" cy="12" r="4"></circle>
+            </svg>
+          </div>
+          <div class="shReviewInfo">
+            <div class="shReviewName">{{ hat.name }} · {{ hat.desc }}</div>
+            <div class="shReviewContent">
+              <template v-if="work.hatContent[hat.key] && work.hatContent[hat.key].trim()">
+                {{ work.hatContent[hat.key] }}
+              </template>
+              <template v-else>
+                <span class="shReviewEmpty">{{ lang.ssSHNoAnswer }}</span>
+              </template>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="shReviewFooter">
+        <!-- <button class="shSecondaryBtn" @click="backToSetupFromReview">{{ lang.ssSHBackToSetup }}</button> -->
+        <button class="shPrimaryBtn" @click="restart">{{ lang.ssSHRestart }}</button>
+      </div>
+    </div>
+
+    <!-- 录音 iframe -->
+    <iframe allow="camera *; microphone *;display-capture;midi;encrypted-media;" :src="iframeUrl" ref="iframeRef"
+      v-show="false"></iframe>
+  </div>
+</template>
+
+<script>
+// 帽子配置
+const HAT_LIST = [
+  { key: 'white', nameKey: 'ssSHWhite', descKey: 'ssSHWhiteDesc', promptKey: 'ssSHHatPromptWhite', bg: '#fafafa' },
+  { key: 'red', nameKey: 'ssSHRed', descKey: 'ssSHRedDesc', promptKey: 'ssSHHatPromptRed', bg: '#fef2f2' },
+  { key: 'black', nameKey: 'ssSHBlack', descKey: 'ssSHBlackDesc', promptKey: 'ssSHHatPromptBlack', bg: '#1f1f1f' },
+  { key: 'yellow', nameKey: 'ssSHYellow', descKey: 'ssSHYellowDesc', promptKey: 'ssSHHatPromptYellow', bg: '#fffbeb' },
+  { key: 'green', nameKey: 'ssSHGreen', descKey: 'ssSHGreenDesc', promptKey: 'ssSHHatPromptGreen', bg: '#f0fdf4' },
+  { key: 'blue', nameKey: 'ssSHBlue', descKey: 'ssSHBlueDesc', promptKey: 'ssSHHatPromptBlue', bg: '#eff6ff' },
+];
+
+export default {
+  props: {
+    workData: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data() {
+    return {
+      // 持久化数据(含后台配置字段)
+      work: {
+        stage: 'member',
+        isSetting: true,
+        members: [],
+        scenario: '',
+        timerEnabled: true,
+        timerDuration: 60,
+        timerCustomValue: 60,
+        hats: ['white', 'red', 'black', 'yellow', 'green', 'blue'],
+        hatContent: {}, // { white: '', red: '', ... }
+        // —— 后台配置字段 ——
+        enableMemberSelect: true,
+        enableContentConfigurable: true,
+        autoExpandHowTo: false,
+        customScenario: '',
+        customTimerEnabled: true,
+        customTimerDuration: 60,
+        customHats: null,
+        howToSteps: null, // null 或 [] → 用默认 i18n 6 步;可由后台覆盖自定义
+      },
+      // 纯 UI 状态
+      setupStep: 0,
+      timerCustomFocused: false,
+      showHowTo: false,
+      currentHatIdx: 0,
+      autoSwitch: true,
+      countdownRemaining: 0,
+      _countdownTimer: null,
+
+      // mock 学生
+      allStudents: [
+        { id: 's1', name: '同学A' },
+        { id: 's2', name: '同学B' },
+        { id: 's3', name: '同学C' },
+        { id: 's4', name: '同学D' },
+        { id: 's5', name: '同学E' },
+        { id: 's6', name: '同学F' },
+        { id: 's7', name: '同学G' },
+        { id: 's8', name: '同学H' },
+      ],
+      recordObj: { status: '0', loading: false, startTime: 0 },
+      memberSearchText: '',
+      _initialized: false,
+    };
+  },
+  computed: {
+    // 完整帽子列表(带 i18n 文案)
+    hatList() {
+      return HAT_LIST.map(h => ({
+        ...h,
+        name: this.lang[h.nameKey] || h.key,
+        desc: this.lang[h.descKey] || '',
+        prompt: this.lang[h.promptKey] || '',
+      }));
+    },
+    stageLabels() {
+      return [
+        this.lang.ssSHStageScenario || '情境',
+        this.lang.ssSHStageTimer || '计时',
+        this.lang.ssSHStageHats || '帽子',
+      ];
+    },
+    timerPresets() {
+      return [
+        { value: 10, label: '10秒' },
+        { value: 30, label: '30秒' },
+        { value: 60, label: '60秒' },
+      ];
+    },
+    currentTimerHint() {
+      const n = this.work.timerDuration;
+      const l = this.lang;
+      if (n <= 45) return l.ssSHTimerHintFast || '';
+      if (n <= 90) return l.ssSHTimerHintNormal || '';
+      return l.ssSHTimerHintDeep || '';
+    },
+    filteredStudents() {
+      const q = (this.memberSearchText || '').trim();
+      if (!q) return this.allStudents;
+      return this.allStudents.filter(s => s.name.indexOf(q) > -1);
+    },
+    iframeUrl() {
+      if (this.lang && this.lang.lang === 'com') return 'https://cloud.cocorobo.com/browser/public/index.html';
+      if (this.lang && this.lang.lang === 'hk') return 'https://cloud.cocorobo.hk/browser/public/index.html';
+      return 'https://beta.cloud.cocorobo.cn/browser/public/index.html';
+    },
+
+    // —— 活动阶段辅助 ——
+    selectedHats() {
+      // 按 HAT_LIST 的 canonical 顺序过滤出选中的
+      const selected = this.work.hats || [];
+      return HAT_LIST.filter(h => selected.indexOf(h.key) > -1).map(h => h.key);
+    },
+    selectedHatsDetail() {
+      const selected = this.work.hats || [];
+      return HAT_LIST.filter(h => selected.indexOf(h.key) > -1).map(h => ({
+        ...h,
+        name: this.lang[h.nameKey] || h.key,
+        desc: this.lang[h.descKey] || '',
+      }));
+    },
+    currentHat() {
+      const key = this.selectedHats[this.currentHatIdx] || 'white';
+      return this.selectedHatsDetail.find(h => h.key === key) || HAT_LIST[0];
+    },
+    currentHatPrompt() {
+      const key = this.selectedHats[this.currentHatIdx];
+      if (!key) return '';
+      const map = {
+        white: this.lang.ssSHHatPromptWhite,
+        red: this.lang.ssSHHatPromptRed,
+        black: this.lang.ssSHHatPromptBlack,
+        yellow: this.lang.ssSHHatPromptYellow,
+        green: this.lang.ssSHHatPromptGreen,
+        blue: this.lang.ssSHHatPromptBlue,
+      };
+      return map[key] || '';
+    },
+    isLastHat() {
+      return this.currentHatIdx === this.selectedHats.length - 1;
+    },
+    memberNames() {
+      const ids = this.work.members || [];
+      return this.allStudents.filter(s => ids.indexOf(s.id) > -1).map(s => s.name);
+    },
+    // 最终渲染的"怎么做"步骤 —— 固定显示 6 个帽子思考方法(i18n 定义)
+    effectiveHowToSteps() {
+      const l = this.lang;
+      return [
+        l.ssSHHowToStep1, l.ssSHHowToStep2, l.ssSHHowToStep3,
+        l.ssSHHowToStep4, l.ssSHHowToStep5, l.ssSHHowToStep6,
+      ].filter(Boolean);
+    },
+  },
+  watch: {
+    // 初始化 hatContent,确保每个选中的帽子都有 key(只在设置/帽子列表真正变化时才做)
+    selectedHats: {
+      handler() {
+        if (!this._initialized) { this._initialized = true; return; }
+        const target = this.work.hatContent || {};
+        const next = {};
+        this.selectedHats.forEach(k => {
+          next[k] = target[k] !== undefined ? target[k] : '';
+        });
+        this.work.hatContent = next;
+      },
+    },
+
+    work: {
+      deep: true,
+      handler(newVal) {
+        const str = JSON.stringify(newVal);
+        if (str !== JSON.stringify(this.workData.json || {})) {
+          this.$emit('changeWorkData', str);
+        }
+      },
+    },
+
+    // 设置阶段保存触发
+    setupStep() {
+      if (this.work.stage === 'setup') this.$emit('setTestJson', this.work);
+    },
+
+    // 帽子切换 → 重置倒计时
+    currentHatIdx() {
+      this.restartCountdown();
+    },
+
+    // 阶段切换 → 清理倒计时
+    'work.stage'(val, oldVal) {
+      if (val !== 'activity') this.clearCountdown();
+      // 第一次进入 activity 启动倒计时
+      if (val === 'activity' && oldVal !== 'activity') {
+        this.currentHatIdx = 0;
+        this.restartCountdown();
+      }
+    },
+
+    // 计时开关 / 时长 变化
+    'work.timerEnabled'() { this.restartCountdown(); },
+    'work.timerDuration'() { this.restartCountdown(); },
+
+    workData: {
+      handler(newVal, oldVal) {
+        const _data = JSON.parse(JSON.stringify((newVal && newVal.json) || {}));
+        // 构造默认 work 值(含所有配置字段)
+        const base = {
+          stage: 'member',
+          isSetting: true,
+          members: [],
+          scenario: '',
+          timerEnabled: true,
+          timerDuration: 60,
+          timerCustomValue: 60,
+          hats: ['white', 'red', 'black', 'yellow', 'green', 'blue'],
+          hatContent: {},
+          enableMemberSelect: true,
+          enableContentConfigurable: true,
+          autoExpandHowTo: false,
+          customScenario: '',
+          customTimerEnabled: true,
+          customTimerDuration: 60,
+          customHats: null,
+          howToSteps: null,
+        };
+        const merged = Object.assign(base, _data || {});
+
+        // 仅当没有任何业务数据时(首次进入)才根据配置决定初始 stage
+        const isFresh = !(merged.members && merged.members.length)
+          && !(merged.scenario && merged.scenario.trim())
+          && !(merged.hatContent && Object.keys(merged.hatContent).some(k => merged.hatContent[k]));
+
+        if (isFresh) {
+          // 应用后台预设值(当 enableContentConfigurable=false 时)
+          if (!merged.enableContentConfigurable) {
+            if (merged.customScenario) merged.scenario = merged.customScenario;
+            merged.timerEnabled = merged.customTimerEnabled;
+            merged.timerDuration = merged.customTimerDuration;
+            if (merged.customHats && merged.customHats.length) merged.hats = merged.customHats;
+          }
+          // 决定初始 stage
+          if (!merged.enableMemberSelect && !merged.enableContentConfigurable) {
+            merged.stage = 'activity';
+          } else if (!merged.enableMemberSelect) {
+            merged.stage = 'setup';
+          } else if (!merged.enableContentConfigurable) {
+            merged.stage = 'member';
+          }
+          // 初始化 UI 状态
+          this.showHowTo = !!merged.autoExpandHowTo;
+          this.setupStep = 0;
+          this.currentHatIdx = 0;
+
+          // 同步父组件保存,避免下次加载重复 isFresh
+          this.$nextTick(() => this.$emit('setTestJson', merged));
+        }
+
+        this.work = merged;
+      },
+      deep: true,
+      immediate: true,
+    },
+  },
+  methods: {
+    // —— 成员选择 ——
+    onMemberFilter(query) { this.memberSearchText = query || ''; },
+    onMemberChange(val) { this.work.members = val || []; },
+    confirmMembers() {
+      if (!this.work.members || this.work.members.length === 0) return;
+      if (this.work.enableContentConfigurable) {
+        this.work.stage = 'setup';
+        this.setupStep = 0;
+      } else {
+        this.startActivity();
+      }
+      this.$emit('setTestJson', this.work);
+    },
+
+    // —— 计时设置 ——
+    focusTimerCustom() {
+      this.timerCustomFocused = true;
+      this.$nextTick(() => {
+        const inp = document.querySelector('.shPage .shTimerCustomInput');
+        if (inp) inp.focus();
+      });
+    },
+    onTimerCustomBlur() {
+      const v = parseInt(this.work.timerCustomValue, 10);
+      if (!isNaN(v) && v > 0) this.work.timerDuration = v;
+      this.timerCustomFocused = false;
+    },
+
+    // —— 帽子 ——
+    toggleHat(key) {
+      const idx = (this.work.hats || []).indexOf(key);
+      if (idx > -1) this.work.hats.splice(idx, 1);
+      else this.work.hats.push(key);
+    },
+
+    // —— 活动控制 ——
+    startActivity() {
+      // 确保 hatContent 有初始 key
+      const content = {};
+      this.selectedHats.forEach(k => { content[k] = this.work.hatContent[k] || ''; });
+      this.work.hatContent = content;
+      this.work.stage = 'activity';
+      this.currentHatIdx = 0;
+      this.$emit('setTestJson', this.work);
+    },
+    backToSetup() {
+      this.clearCountdown();
+      // 如果后台禁用了设置阶段,跳到 member(如果开了)或保持在 activity
+      if (!this.work.enableContentConfigurable) {
+        if (this.work.enableMemberSelect) this.work.stage = 'member';
+        return;
+      }
+      this.work.stage = 'setup';
+      this.$emit('setTestJson', this.work);
+    },
+    prevHat() {
+      if (this.currentHatIdx > 0) this.currentHatIdx--;
+    },
+    nextOrFinish() {
+      if (this.isLastHat) {
+        this.work.stage = 'review';
+        this.clearCountdown();
+        this.$emit('setTestJson', this.work);
+      } else {
+        this.currentHatIdx++;
+      }
+    },
+    jumpToHat(idx) {
+      if (idx >= 0 && idx < this.selectedHats.length) this.currentHatIdx = idx;
+    },
+    toggleAutoSwitch() {
+      this.autoSwitch = !this.autoSwitch;
+      if (this.autoSwitch) this.restartCountdown();
+      else this.clearCountdown();
+    },
+
+    // —— 倒计时 ——
+    restartCountdown() {
+      this.clearCountdown();
+      if (!this.work.timerEnabled || !this.autoSwitch || this.work.stage !== 'activity') return;
+      if (!this.selectedHats.length) return;
+      this.countdownRemaining = this.work.timerDuration;
+      this._countdownTimer = setInterval(() => {
+        if (this.countdownRemaining > 0) {
+          this.countdownRemaining--;
+        } else {
+          // 时间到 → 自动切换到下一顶
+          clearInterval(this._countdownTimer);
+          this._countdownTimer = null;
+          if (!this.isLastHat) {
+            this.currentHatIdx++;
+          } else {
+            // 最后一顶时间到 → 直接进总结
+            this.work.stage = 'review';
+          }
+        }
+      }, 1000);
+    },
+    clearCountdown() {
+      if (this._countdownTimer) {
+        clearInterval(this._countdownTimer);
+        this._countdownTimer = null;
+      }
+      this.countdownRemaining = 0;
+    },
+
+    // —— 总结回顾 ——
+    copyHatContent(hat) {
+      const text = (this.work.hatContent[hat.key] || '').trim();
+      if (!text) return; // 空内容不复制
+      if (navigator.clipboard && navigator.clipboard.writeText) {
+        navigator.clipboard.writeText(text).then(() => {
+          this.$message && this.$message.success(this.lang.ssSHAddedToClipboard);
+        }).catch(() => {
+          this.fallbackCopy(text);
+        });
+      } else {
+        this.fallbackCopy(text);
+      }
+    },
+    fallbackCopy(text) {
+      const ta = document.createElement('textarea');
+      ta.value = text;
+      document.body.appendChild(ta);
+      ta.select();
+      try { document.execCommand('copy'); this.$message && this.$message.success(this.lang.ssSHAddedToClipboard); } catch (e) { }
+      document.body.removeChild(ta);
+    },
+    backToSetupFromReview() {
+      this.clearCountdown();
+      if (this.work.enableContentConfigurable) {
+        this.work.stage = 'setup';
+      } else if (this.work.enableMemberSelect) {
+        this.work.stage = 'member';
+      } else {
+        // 两个都禁用了 — 回到 activity 第 1 顶
+        this.work.stage = 'activity';
+        this.currentHatIdx = 0;
+      }
+      this.$emit('setTestJson', this.work);
+    },
+    restart() {
+      if (this.$confirm) {
+        this.$confirm(this.lang.ssSHRestartConfirm, this.lang.ssSHRestart, {
+          confirmButtonText: this.lang.ssSHRestart,
+          cancelButtonText: this.lang.Cancel,
+          type: 'warning',
+        }).then(() => {
+          this.doRestart();
+        }).catch(() => { });
+      } else {
+        if (confirm(this.lang.ssSHRestartConfirm)) this.doRestart();
+      }
+    },
+    doRestart() {
+      const empty = {};
+      this.selectedHats.forEach(k => { empty[k] = ''; });
+      this.work.hatContent = empty;
+      this.currentHatIdx = 0;
+      this.work.stage = 'activity';
+      this.clearCountdown();
+      this.restartCountdown();
+      this.$emit('setTestJson', this.work);
+    },
+
+    // —— 语音输入 ——
+    startRecord(targetKey) {
+      const iframe = this.$refs['iframeRef'];
+      if (!iframe || !iframe.contentWindow) {
+        this.$message.error(this.lang.ssSHRecServiceNotReady);
+        return;
+      }
+      try {
+        if (this.lang.lang === 'com') iframe.contentWindow.window.document.getElementById('languageOptions').selectedIndex = 8;
+        else if (this.lang.lang === 'hk') iframe.contentWindow.window.document.getElementById('languageOptions').selectedIndex = 4;
+        else iframe.contentWindow.window.document.getElementById('languageOptions').selectedIndex = 2;
+        iframe.contentWindow.window.onRecognizedResult = (e) => {
+          const privText = e.privText;
+          if (!privText) return;
+          if (targetKey === 'scenario') {
+            this.work.scenario = (this.work.scenario ? this.work.scenario + ' ' : '') + privText;
+          } else {
+            const existing = this.work.hatContent[targetKey] || '';
+            this.$set(this.work.hatContent, targetKey, (existing ? existing + ' ' : '') + privText);
+          }
+        };
+        iframe.contentWindow.ConversationTranscriber();
+        this.recordObj.status = '1';
+        this.recordObj.startTime = new Date().getTime();
+        setTimeout(() => {
+          if (!navigator.permissions) return;
+          navigator.permissions.query({ name: 'microphone' }).then(ps => {
+            if (ps.state !== 'granted') {
+              this.recordObj.status = '0';
+              this.recordObj.loading = false;
+              iframe.contentWindow.onSessionStopped = null;
+              iframe.contentWindow.window.onRecognizedResult = null;
+              this.$message.warning(this.lang.ssSHNoPermStop);
+            }
+          }).catch(() => { });
+        }, 2000);
+      } catch (err) {
+        console.error('startRecord error:', err);
+        this.$message.error(this.lang.ssSHRecStartFail);
+        this.recordObj.status = '0';
+      }
+    },
+    stopRecord() {
+      if (new Date().getTime() - this.recordObj.startTime < 2000) {
+        this.$message.info(this.lang.ssSHRecordShort);
+        return;
+      }
+      const iframe = this.$refs['iframeRef'];
+      if (!iframe || !iframe.contentWindow) {
+        this.recordObj.status = '0';
+        this.recordObj.loading = false;
+        return;
+      }
+      this.recordObj.loading = true;
+      try {
+        iframe.contentWindow.window.document.getElementById('scenarioStopButton').click();
+      } catch (err) {
+        console.error('stop click error:', err);
+        this.recordObj.status = '0';
+        this.recordObj.loading = false;
+        return;
+      }
+      iframe.contentWindow.onSessionStopped = () => {
+        this.recordObj.status = '0';
+        this.recordObj.loading = false;
+        this.$message.success(this.lang.ssSHRecStopped);
+        iframe.contentWindow.onSessionStopped = null;
+        iframe.contentWindow.window.onRecognizedResult = null;
+      };
+    },
+  },
+  beforeUnmount() {
+    this.clearCountdown();
+  },
+};
+</script>
+
+<style scoped>
+/* ========= 基础 ========= */
+.shPage {
+  width: 100%;
+  min-height: 100%;
+  height: 100vh;
+  box-sizing: border-box;
+  padding: 1rem;
+  background: #fdf8f0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+  overflow-y: auto;
+}
+
+/* ========= 成员选择 ========= */
+.shMemberStage {
+  width: 100%;
+  max-width: 680px;
+  padding-top: 2rem;
+}
+
+.shMemberCard {
+  width: 100%;
+  background: #fff;
+  border-radius: 1rem;
+  padding: 2.5rem 2rem 2rem;
+  text-align: center;
+  box-shadow: 0 4px 20px rgba(245, 166, 35, 0.08);
+}
+
+.shMemberIconWrap {
+  margin-bottom: 1.25rem;
+}
+
+.shMemberIcon {
+  width: 5rem;
+  height: 5rem;
+  border-radius: 1rem;
+  background: #fff6e5;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.shMemberTitle {
+  font-size: 1.5rem;
+  font-weight: 700;
+  color: #1f1f1f;
+  margin: 0 0 0.5rem;
+}
+
+.shMemberSubtitle {
+  font-size: 0.9375rem;
+  color: #666;
+  margin: 0 0 2rem;
+  line-height: 1.5;
+}
+
+.shMemberBox {
+  text-align: left;
+  margin-bottom: 2.5rem;
+}
+
+.shMemberBox>>>.el-select {
+  width: 100%;
+}
+
+.shMemberBox>>>.el-input__inner {
+  height: 3rem;
+  border-radius: 0.75rem;
+  border: 1px solid #eee;
+  background: #fafafa;
+  font-size: 0.9375rem;
+  padding-left: 1rem;
+  transition: all 0.2s;
+}
+
+.shMemberBox>>>.el-input.is-focus .el-input__inner,
+.shMemberBox>>>.el-input__inner:focus {
+  border-color: #F5A623;
+  background: #fff;
+}
+
+.shMemberBox>>>.el-tag {
+  background: #fff6e5;
+  color: #F5A623;
+  border: none;
+  border-radius: 1rem;
+  height: 1.75rem;
+  line-height: 1.5rem;
+  padding: 0 0.75rem;
+  font-size: 0.875rem;
+}
+
+.shMemberBox>>>.el-tag .el-tag__close {
+  color: #F5A623;
+  margin-left: 0.375rem;
+}
+
+.shMemberBox>>>.el-tag .el-tag__close:hover {
+  background: #fff0d8;
+  border-radius: 50%;
+}
+
+.shMemberConfirmBtn {
+  width: 100%;
+  height: 3.25rem;
+  background: linear-gradient(135deg, #F5A623 0%, #f59e0b 100%);
+  border: none;
+  border-radius: 0.75rem;
+  color: #fff;
+  font-size: 1rem;
+  font-weight: 600;
+  cursor: pointer;
+  transition: all 0.2s;
+  box-shadow: 0 4px 12px rgba(245, 166, 35, 0.25);
+}
+
+.shMemberConfirmBtn:hover:not(.shMemberConfirmDisabled) {
+  transform: translateY(-1px);
+  box-shadow: 0 6px 16px rgba(245, 166, 35, 0.35);
+}
+
+.shMemberConfirmDisabled {
+  background: #f5deb3 !important;
+  box-shadow: none !important;
+  cursor: not-allowed !important;
+  opacity: 0.7;
+}
+
+/* ========= 设置内容 ========= */
+.shSetupStage,
+.shActivityStage,
+.shReviewStage {
+  width: 100%;
+  max-width: 1000px;
+  padding-top: 0.5rem;
+}
+
+.shTopTabs {
+  display: flex;
+  align-items: center;
+  margin-bottom: 1.5rem;
+}
+
+.shTab {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 0.5rem;
+  padding: 1rem 1.5rem;
+  border-radius: 0.75rem;
+  font-size: 1rem;
+  font-weight: 600;
+  transition: all 0.2s;
+}
+
+.shTabActive {
+  background: linear-gradient(135deg, #F5A623 0%, #f59e0b 100%);
+  color: #fff;
+  box-shadow: 0 4px 12px rgba(245, 166, 35, 0.25);
+}
+
+.shTabDisabled {
+  background: #f0f0f0;
+  color: #999;
+}
+
+.shTabClickable {
+  background: #fff6e5;
+  color: #F5A623;
+  cursor: pointer;
+}
+
+.shTabClickable:hover {
+  background: #ffecd1;
+}
+
+.shTabDivider {
+  width: 1.5rem;
+  flex-shrink: 0;
+}
+
+.shSetupHeader {
+  text-align: center;
+  margin-bottom: 1.5rem;
+}
+
+.shSetupTitle {
+  font-size: 1.5rem;
+  font-weight: 700;
+  color: #1f1f1f;
+  margin: 0 0 0.375rem;
+}
+
+.shSetupDesc {
+  font-size: 0.9375rem;
+  color: #666;
+  margin: 0 0 1.25rem;
+}
+
+.shStepIndicator {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.shStepDot {
+  width: 0.5rem;
+  height: 0.5rem;
+  border-radius: 50%;
+  background: #ddd;
+  transition: all 0.2s;
+}
+
+.shStepDotActive {
+  background: #F5A623;
+}
+
+.shStepDotCurrent {
+  box-shadow: 0 0 0 4px rgba(245, 166, 35, 0.2);
+}
+
+.shStepLabel {
+  font-size: 0.875rem;
+  color: #999;
+  margin: 0 0.5rem;
+}
+
+.shStepLabelActive {
+  color: #F5A623;
+  font-weight: 600;
+}
+
+.shStepLine {
+  width: 2rem;
+  height: 1px;
+  background: #ddd;
+}
+
+.shStepCard {
+  background: #fff;
+  border-radius: 1rem;
+  padding: 1.75rem 2rem;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
+}
+
+.shStepRow {
+  display: flex;
+  gap: 1.5rem;
+}
+
+.shStepLeft {
+  flex: 0 0 45%;
+}
+
+.shStepRight {
+  flex: 1;
+}
+
+.shStepTitleRow {
+  display: flex;
+  align-items: flex-start;
+  gap: 0.75rem;
+  margin-bottom: 1.5rem;
+}
+
+.shStepIcon {
+  width: 2.5rem;
+  height: 2.5rem;
+  border-radius: 0.625rem;
+  background: #fff6e5;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+}
+
+.shStepTitle {
+  font-size: 1.125rem;
+  font-weight: 700;
+  color: #1f1f1f;
+  margin: 0 0 0.25rem;
+}
+
+.shStepSub {
+  font-size: 0.875rem;
+  color: #666;
+  margin: 0;
+  line-height: 1.5;
+}
+
+.shExampleLabel {
+  font-size: 0.9375rem;
+  font-weight: 600;
+  color: #333;
+  margin-bottom: 0.75rem;
+}
+
+.shExampleList {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 0.625rem;
+}
+
+.shExampleItem {
+  background: #fafafa;
+  border-radius: 0.5rem;
+  padding: 0.875rem 1rem;
+  font-size: 0.875rem;
+  color: #555;
+  line-height: 1.5;
+}
+
+.shExampleDot {
+  color: #F5A623;
+  margin-right: 0.5rem;
+  font-weight: bold;
+}
+
+.shInputTitle {
+  font-size: 1rem;
+  font-weight: 600;
+  color: #1f1f1f;
+  margin-bottom: 0.75rem;
+}
+
+.shTextareaWrap {
+  position: relative;
+}
+
+.shTextarea {
+  width: 100%;
+  min-height: 14rem;
+  padding: 1rem 3rem 1rem 1rem;
+  border: 1px solid #eee;
+  border-radius: 0.75rem;
+  background: #fafafa;
+  font-size: 0.9375rem;
+  line-height: 1.6;
+  resize: vertical;
+  outline: none;
+  box-sizing: border-box;
+  font-family: inherit;
+  transition: all 0.2s;
+}
+
+.shTextarea:focus {
+  border-color: #F5A623;
+  background: #fff;
+}
+
+.shTextarea::placeholder {
+  color: #bbb;
+}
+
+.shMicBtn {
+  position: absolute;
+  right: 0.75rem;
+  bottom: 0.75rem;
+  width: 2.25rem;
+  height: 2.25rem;
+  border-radius: 50%;
+  background: #fff6e5;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  color: #F5A623;
+  transition: all 0.2s;
+}
+
+.shMicBtn:hover {
+  background: #ffe7c2;
+}
+
+.shMicBtnActive {
+  background: #F5A623;
+  color: #fff;
+}
+
+/* 计时 */
+.shTimerWrap {
+  padding: 0.5rem 0;
+}
+
+.shTimerTopRow {
+  display: flex;
+  align-items: flex-start;
+  justify-content: space-between;
+  margin-bottom: 1.5rem;
+}
+
+.shSwitch {
+  width: 3.25rem;
+  height: 1.75rem;
+  border-radius: 1rem;
+  background: #ddd;
+  position: relative;
+  cursor: pointer;
+  transition: background 0.2s;
+  flex-shrink: 0;
+  margin-top: 0.25rem;
+}
+
+.shSwitchOn {
+  background: #F5A623;
+}
+
+.shSwitchThumb {
+  width: 1.5rem;
+  height: 1.5rem;
+  border-radius: 50%;
+  background: #fff;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
+  position: absolute;
+  top: 0.125rem;
+  left: 0.125rem;
+  transition: left 0.2s;
+}
+
+.shSwitchOn .shSwitchThumb {
+  left: 1.625rem;
+}
+
+.shTimerQuestion {
+  font-size: 1rem;
+  font-weight: 600;
+  color: #1f1f1f;
+  margin-bottom: 0.75rem;
+}
+
+.shTimerOpts {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 0.625rem;
+  margin-bottom: 1.25rem;
+}
+
+.shTimerOpt {
+  min-width: 3.5rem;
+  height: 2.25rem;
+  padding: 0 1rem;
+  border-radius: 0.5rem;
+  border: 1px solid #eee;
+  background: #fafafa;
+  color: #555;
+  font-size: 0.875rem;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+
+.shTimerOpt:hover {
+  border-color: #F5A623;
+  color: #F5A623;
+}
+
+.shTimerOptActive {
+  background: #F5A623;
+  border-color: #F5A623;
+  color: #fff;
+  font-weight: 600;
+}
+
+.shTimerCustomInput {
+  width: 5rem;
+  height: 2.25rem;
+  padding: 0 0.75rem;
+  border-radius: 0.5rem;
+  border: 1px solid #F5A623;
+  background: #fff;
+  color: #1f1f1f;
+  font-size: 0.875rem;
+  outline: none;
+  box-sizing: border-box;
+}
+
+.shTimerHint {
+  background: #fff6e5;
+  border-radius: 0.75rem;
+  padding: 1rem;
+  text-align: center;
+}
+
+.shTimerHintMain {
+  font-size: 1.125rem;
+  font-weight: 700;
+  color: #1f1f1f;
+  margin-bottom: 0.375rem;
+}
+
+.shTimerHintSub {
+  font-size: 0.875rem;
+  color: #666;
+}
+
+.shTimerOffHint {
+  background: #f5f5f5;
+  border-radius: 0.75rem;
+  padding: 1.5rem 1rem;
+  text-align: center;
+}
+
+.shTimerOffMain {
+  font-size: 1.125rem;
+  font-weight: 700;
+  color: #1f1f1f;
+  margin-bottom: 0.375rem;
+}
+
+.shTimerOffSub {
+  font-size: 0.875rem;
+  color: #666;
+}
+
+/* 帽子选择 */
+.shHatsTitle {
+  font-size: 1.125rem;
+  font-weight: 700;
+  color: #1f1f1f;
+  margin: 0 0 0.375rem;
+}
+
+.shHatsSub {
+  font-size: 0.875rem;
+  color: #666;
+  margin: 0 0 1.5rem;
+}
+
+.shHatsGrid {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 1rem;
+}
+
+.shHatCard {
+  position: relative;
+  background: #fafafa;
+  border: 2px solid #e8e8e8;
+  border-radius: 0.75rem;
+  padding: 1.5rem 1rem 1rem;
+  text-align: center;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+
+.shHatCard:hover {
+  border-color: #ccc;
+}
+
+.shHatSelected {
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+}
+
+.shHatwhiteSel {
+  border-color: #d0d0d0;
+  background: #fafafa;
+}
+
+.shHatredSel {
+  border-color: #fda4af;
+  background: #fef2f2;
+}
+
+.shHatblackSel {
+  border-color: #1f1f1f;
+  background: #cecece;
+}
+
+.shHatyellowSel {
+  border-color: #fcd34d;
+  background: #fffbeb;
+}
+
+.shHatgreenSel {
+  border-color: #86efac;
+  background: #f0fdf4;
+}
+
+.shHatblueSel {
+  border-color: #93c5fd;
+  background: #eff6ff;
+}
+
+.shHatIcon {
+  width: 3.5rem;
+  height: 3.5rem;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin: 0 auto 0.75rem;
+}
+
+.shHatblackSel .shHatName {
+  color: #000;
+}
+
+.shHatblackSel .shHatDesc {
+  color: #999;
+}
+
+.shHatName {
+  font-size: 1rem;
+  font-weight: 700;
+  color: #1f1f1f;
+  margin-bottom: 0.25rem;
+}
+
+.shHatDesc {
+  font-size: 0.8125rem;
+  color: #999;
+  margin: 0;
+}
+
+.shHatCheck {
+  position: absolute;
+  top: 0.5rem;
+  right: 0.5rem;
+  width: 1.5rem;
+  height: 1.5rem;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: #fff;
+}
+
+.shHatCheckwhite {
+  background: #666;
+}
+
+.shHatCheckred {
+  background: #ef4444;
+}
+
+.shHatCheckblack {
+  background: #fff;
+  color: #1f1f1f !important;
+}
+
+.shHatCheckyellow {
+  background: #eab308;
+}
+
+.shHatCheckgreen {
+  background: #16a34a;
+}
+
+.shHatCheckblue {
+  background: #2563eb;
+}
+
+/* 底部按钮 */
+.shStepFooter {
+  display: flex;
+  gap: 1rem;
+  margin-top: 1.5rem;
+  padding-top: 1.25rem;
+  border-top: 1px solid #f0f0f0;
+}
+
+.shStepFooterSingle {
+  justify-content: flex-end;
+}
+
+.shPrimaryBtn {
+  height: 3rem;
+  padding: 0 2.5rem;
+  background: linear-gradient(135deg, #F5A623 0%, #f59e0b 100%);
+  border: none;
+  border-radius: 0.75rem;
+  color: #fff;
+  font-size: 1rem;
+  font-weight: 600;
+  cursor: pointer;
+  transition: all 0.2s;
+  box-shadow: 0 4px 12px rgba(245, 166, 35, 0.25);
+  flex: 1;
+}
+
+.shPrimaryBtn:hover:not(.shPrimaryBtnDisabled) {
+  transform: translateY(-1px);
+  box-shadow: 0 6px 16px rgba(245, 166, 35, 0.35);
+}
+
+.shPrimaryBtnDisabled {
+  background: #f5deb3 !important;
+  box-shadow: none !important;
+  cursor: not-allowed !important;
+  opacity: 0.7;
+}
+
+.shPrimaryBtnStart {
+  flex: 1;
+}
+
+.shSecondaryBtn {
+  height: 3rem;
+  padding: 0 2rem;
+  background: #fff;
+  border: 1px solid #e8e8e8;
+  border-radius: 0.75rem;
+  color: #666;
+  font-size: 1rem;
+  font-weight: 500;
+  cursor: pointer;
+  transition: all 0.2s;
+  flex: 1;
+}
+
+.shSecondaryBtn:hover {
+  border-color: #F5A623;
+  color: #F5A623;
+}
+
+.shSecondaryBtnDisabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+}
+
+.shSecondaryBtnDisabled:hover {
+  border-color: #e8e8e8;
+  color: #666;
+}
+
+/* ========= 活动阶段 ========= */
+.shHeaderCard {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  background: #fff;
+  border-radius: 0.75rem;
+  padding: 1rem 1.5rem;
+  margin-bottom: 1rem;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+}
+
+.shHeaderCardTitle {
+  font-size: 1.125rem;
+  font-weight: 700;
+  color: #1f1f1f;
+}
+
+.shHeaderCardSub {
+  font-size: 0.875rem;
+  color: #666;
+  margin-top: 0.25rem;
+}
+
+.shHeaderCardRight {
+  display: flex;
+  align-items: center;
+  gap: 0.375rem;
+  color: #F5A623;
+  font-size: 0.875rem;
+  cursor: pointer;
+}
+
+.kwlArrow {
+  display: inline-flex;
+  align-items: center;
+  transition: transform 0.2s;
+}
+
+.kwlArrowUp {
+  transform: rotate(180deg);
+}
+
+.shHowToContent {
+  background: #fff;
+  border-radius: 0.5rem;
+  padding: 1rem 1.5rem;
+  margin-bottom: 1rem;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+}
+
+.shHowToList {
+  display: flex;
+  flex-direction: column;
+  gap: 0.25rem;
+}
+
+.shHowToItem {
+  font-size: 0.9375rem;
+  color: #555;
+  line-height: 1.6;
+  padding: 0.375rem 0;
+}
+
+.shScenarioBadge {
+  display: inline-block;
+  background: #fff6e5;
+  color: #F5A623;
+  padding: 0.375rem 0.875rem;
+  border-radius: 1rem;
+  font-size: 0.8125rem;
+  font-weight: 500;
+}
+
+.shHowToDesc {
+  font-size: 0.9375rem;
+  color: #666;
+}
+
+.shHatDots {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 1.5rem;
+}
+
+.shHatDot {
+  width: 1rem;
+  height: 1rem;
+  border-radius: 50%;
+  border: 2px solid #e0e0e0;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+
+.shHatDotLine {
+  width: 0.75rem;
+  height: 2px;
+  background: #e0e0e0;
+  margin: 0 0.25rem;
+}
+
+.shHatDotDone {
+  border-color: currentColor;
+  background: currentColor;
+  opacity: 0.8;
+}
+
+.shHatDotCurrent {
+  width: 1.25rem;
+  height: 1.25rem;
+  box-shadow: 0 0 0 3px rgba(245, 166, 35, 0.25);
+  transform: scale(1.05);
+}
+
+.shHatDotwhite {
+  background: #FFF;
+}
+
+.shHatDotred {
+  background: #fda4af;
+}
+
+.shHatDotblack {
+  background: #333;
+}
+
+.shHatDotyellow {
+  background: #fcd34d;
+}
+
+.shHatDotgreen {
+  background: #86efac;
+}
+
+.shHatDotblue {
+  background: #93c5fd;
+}
+
+.shActivityRow {
+  display: flex;
+  gap: 1rem;
+  margin-bottom: 1rem;
+}
+
+/* 左:帽子展示卡 */
+.shActivityHatCard {
+  flex: 0 0 45%;
+  background: #fff;
+  border-radius: 0.75rem;
+  padding: 1.5rem;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  text-align: center;
+}
+
+.shActivityHatIcon {
+  width: 6rem;
+  height: 6rem;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 1rem;
+  transition: background 0.2s;
+}
+
+.shActivityHatIconwhite {
+  background: #fafafa;
+}
+
+.shActivityHatIconred {
+  background: #fef2f2;
+}
+
+.shActivityHatIconblack {
+  background: #1f1f1f;
+}
+
+.shActivityHatIconyellow {
+  background: #fffbeb;
+}
+
+.shActivityHatIcongreen {
+  background: #f0fdf4;
+}
+
+.shActivityHatIconblue {
+  background: #eff6ff;
+}
+
+.shActivityHatName {
+  font-size: 1.25rem;
+  font-weight: 700;
+  color: #1f1f1f;
+  margin-bottom: 0.25rem;
+}
+
+.shActivityHatDesc {
+  font-size: 0.875rem;
+  color: #666;
+  margin-bottom: 0.5rem;
+}
+
+.shActivityHatProgress {
+  font-size: 0.8125rem;
+  color: #bbb;
+  margin-bottom: 1.25rem;
+}
+
+.shActivityHatPrompt {
+  background: #fff6e5;
+  border-radius: 0.5rem;
+  padding: 0.875rem 1rem;
+  font-size: 0.875rem;
+  color: #666;
+  line-height: 1.5;
+  width: 100%;
+}
+
+/* 右:输入卡 */
+.shActivityInputCard {
+  flex: 1;
+  background: #fff;
+  border-radius: 0.75rem;
+  padding: 1.5rem;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
+  display: flex;
+  flex-direction: column;
+}
+
+.shActivityInputCard .shTextarea {
+  min-height: 11rem;
+}
+
+.shAssignee {
+  display: flex;
+  align-items: center;
+  gap: 0.5rem;
+  margin-top: 1rem;
+  flex-wrap: wrap;
+}
+
+.shAssigneeLabel {
+  font-size: 0.875rem;
+  color: #999;
+}
+
+.shAssigneeTag {
+  background: #fff6e5;
+  color: #F5A623;
+  border-radius: 1rem;
+  padding: 0.25rem 0.75rem;
+  font-size: 0.8125rem;
+}
+
+.shAssigneeTagEmpty {
+  color: #ccc;
+}
+
+/* 底部栏 */
+.shActivityFooter {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 1rem;
+  background: #fff;
+  border-radius: 0.75rem;
+  padding: 1rem 1.5rem;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
+}
+
+.shAutoSwitchBtn {
+  display: flex;
+  align-items: center;
+  gap: 0.375rem;
+  background: #fafafa;
+  padding: 0.5rem 0.875rem;
+  border-radius: 0.5rem;
+  font-size: 0.875rem;
+  color: #999;
+  cursor: pointer;
+  border: 1px solid transparent;
+  transition: all 0.2s;
+}
+
+.shAutoSwitchBtn:hover {
+  border-color: #eee;
+}
+
+.shAutoSwitchOn {
+  background: #fff6e5;
+  color: #F5A623;
+  border-color: #ffe7c2;
+}
+
+.shCountdown {
+  font-size: 1.75rem;
+  font-weight: 700;
+  color: #F5A623;
+  flex: 1;
+  text-align: center;
+  font-variant-numeric: tabular-nums;
+}
+
+.shFooterBtns {
+  display: flex;
+  gap: 1rem;
+  margin-top: 1rem;
+}
+
+/* ========= 总结回顾 ========= */
+.shReviewStage {
+  padding-top: 1rem;
+}
+
+.shReviewHeader {
+  text-align: center;
+  margin-bottom: 2rem;
+}
+
+.shReviewTitle {
+  font-size: 1.5rem;
+  font-weight: 700;
+  color: #1f1f1f;
+  margin: 0 0 0.375rem;
+}
+
+.shReviewSub {
+  font-size: 0.9375rem;
+  color: #666;
+  margin: 0;
+}
+
+.shReviewList {
+  display: flex;
+  flex-direction: column;
+  gap: 0.75rem;
+  margin-bottom: 2rem;
+}
+
+.shReviewCard {
+  display: flex;
+  align-items: flex-start;
+  gap: 1rem;
+  padding: 1rem 1.25rem;
+  background: #fff;
+  border-radius: 0.75rem;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
+  cursor: pointer;
+  transition: all 0.2s;
+  border: 2px solid transparent;
+}
+
+.shReviewCard:hover {
+  border-color: #F5A623;
+}
+
+.shReviewIcon {
+  width: 2.5rem;
+  height: 2.5rem;
+  border-radius: 50%;
+  flex-shrink: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.shReviewInfo {
+  flex: 1;
+  min-width: 0;
+}
+
+.shReviewName {
+  font-size: 0.9375rem;
+  font-weight: 600;
+  color: #1f1f1f;
+  margin-bottom: 0.375rem;
+}
+
+.shReviewContent {
+  font-size: 0.875rem;
+  color: #333;
+  line-height: 1.6;
+  word-break: break-all;
+}
+
+.shReviewEmpty {
+  color: #bbb;
+}
+
+.shReviewFooter {
+  display: flex;
+  gap: 1rem;
+  justify-content: center;
+}
+
+/* 响应式 */
+@media (max-width: 768px) {
+
+  .shSetupStage,
+  .shActivityStage,
+  .shReviewStage {
+    padding-top: 0;
+  }
+
+  .shStepRow {
+    flex-direction: column;
+  }
+
+  .shStepLeft {
+    flex: 1;
+  }
+
+  .shActivityRow {
+    flex-direction: column;
+  }
+
+  .shActivityHatCard,
+  .shActivityInputCard {
+    flex: 1;
+  }
+
+  .shHatsGrid {
+    grid-template-columns: repeat(2, 1fr);
+  }
+
+  .shCountdown {
+    font-size: 1.25rem;
+  }
+}
+</style>

+ 20 - 2
src/components/pages/workPage/index_new.vue

@@ -10,6 +10,8 @@
       @changeWorkData="changeWorkData" />
     <kwlPage ref="kwlPageRef" v-if="workData.type === '831'" :workData="workData"
       @changeWorkData="changeWorkData" />
+    <sixHatPage ref="sixHatPageRef" v-if="workData.type === '832'" :workData="workData"
+      @changeWorkData="changeWorkData" />
     <file ref="fileRef" v-if="workData.type === '84'" :workData="workData"
       @changeWorkData="changeWorkData" />
   </div>
@@ -23,6 +25,7 @@ import choiceQuestion from "./components/choiceQuestion.vue";
 import voteQuestion from "./components/voteQuestion.vue";
 import photo from "./components/photo.vue";
 import kwlPage from "./components/kwlPage.vue";
+import sixHatPage from "./components/sixHatPage.vue";
 import file from "./components/file.vue";
 export default {
   components: {
@@ -31,6 +34,7 @@ export default {
     voteQuestion,
     photo,
     kwlPage,
+    sixHatPage,
     file,
   },
   data() {
@@ -301,6 +305,18 @@ export default {
             content: encodeURIComponent(JSON.stringify(this.workData.json)),
             type: "831"
           });
+        } else if (this.workData.type == "832") {
+          //跨学科kwl题
+          params.push({
+            uid: this.userid,
+            cid: this.courseid,
+            stage: 0,
+            task: task,
+            tool: 0,
+            atool: "832",
+            content: encodeURIComponent(JSON.stringify(this.workData.json)),
+            type: "832"
+          });
         } else if (this.workData.type == "84") {
           //文件题
           params.push({
@@ -316,7 +332,7 @@ export default {
         }
 
         if (
-          ["3", "8", "22", "23", "831", "84"].includes(params[0].type) && 
+          ["3", "8", "22", "23", "831", "832", "84"].includes(params[0].type) && 
           params[0].uid &&
           params[0].cid
         ) {
@@ -383,7 +399,7 @@ export default {
             this.loading = false;
             if (_data.length) {
               _data = _data[0];
-              if (["15", "45", "78", "79", "831", "84"].includes(_data.atool)) {
+              if (["15", "45", "78", "79", "831", "832", "84"].includes(_data.atool)) {
                 let _work = JSON.parse(decodeURIComponent(_data.content));
                 this.studentWork = _work;
                 this.studentWorkToWorkData();
@@ -436,6 +452,8 @@ export default {
           this.workData.json.fileList = _work.fileList;
         }else if(this.workData.type == "831"){
           this.workData.json = _work;
+        }else if(this.workData.type == "832"){
+          this.workData.json = _work;
         }else if(this.workData.type == "84"){
           //文件题
           this.workData.json.fileList = _work.fileList;

+ 3 - 0
src/components/pages/workPage/setIndex.vue

@@ -12,6 +12,7 @@
     <setVoteQuestion :workData="workData" v-if="workData.type == 78" @setTestJson="setTestJson" @save="saveTest"/>
     <setPhoto :workData="workData" v-if="workData.type == 79" @setTestJson="setTestJson" @save="saveTest"/>
     <kwlPage :workData="workData" v-if="workData.type == 831" @setTestJson="setTestJson" @save="saveTest"/>
+    <sixHatPage :workData="workData" v-if="workData.type == 832" @setTestJson="setTestJson" @save="saveTest"/>
     <setFile :workData="workData" v-if="workData.type == 84" @setTestJson="setTestJson" @save="saveTest"/>
   </div>
 </template>
@@ -22,6 +23,7 @@ import setQuestionsAndAnswers from './components/setQuestionsAndAnswers.vue'
 import setVoteQuestion from './components/setVoteQuestion.vue'
 import setPhoto from './components/setPhoto.vue'
 import kwlPage from './components/kwlPage.vue'
+import sixHatPage from './components/sixHatPage.vue'
 import setFile from './components/setFile.vue'
 
 function debounce(func, wait) {
@@ -41,6 +43,7 @@ export default {
     setVoteQuestion,
     setPhoto,
     kwlPage,
+    sixHatPage,
     setFile,
   },
   data() {

+ 89 - 0
src/lang/cn.json

@@ -2102,6 +2102,95 @@
   "ssKWLRecServiceNotReady":"录音服务未就绪",
   "ssKWLRecStartFail":"启动录音失败",
   "ssKWLRecStopped":"录音已停止",
+  "ssSHTitle":"六顶思考帽",
+  "ssSHSubtitle":"用六顶不同颜色的帽子,从不同角度思考问题",
+  "ssSHHowTo":"怎么做",
+  "ssSHSetupContent":"设置内容",
+  "ssSHStartActivity":"开始活动",
+  "ssSHBeforeStart":"开始之前,先设置一下",
+  "ssSHSetupDesc":"这一轮的六顶思考帽由你自己来定",
+  "ssSHStageScenario":"情境",
+  "ssSHStageTimer":"计时",
+  "ssSHStageHats":"帽子",
+  "ssSHStepNext":"下一步",
+  "ssSHStepPrev":"上一步",
+  "ssSHMemberTitle":"先选出这次的小组成员",
+  "ssSHMemberSubtitle":"输入姓名搜索,或从名单里选择和你一起完成这次活动的同学",
+  "ssSHMemberSearchPH":"输入姓名搜索或选择...",
+  "ssSHMemberConfirm":"确认小组成员",
+  "ssSHMemberConfirmN":"确认小组成员 ({n}人)",
+  "ssSHScenarioTitle":"这一轮要思考什么?",
+  "ssSHScenarioSubtitle":"先确定一个问题或议题,后面的所有步骤都会围绕它展开。",
+  "ssSHScenarioInputTitle":"请输入本轮讨论的主题",
+  "ssSHScenarioInputPH":"写下这一轮要讨论的议题或情境...",
+  "ssSHScenarioExamples":"示例:",
+  "ssSHScenarioEx1":"是否应该延长午休时间",
+  "ssSHScenarioEx2":"怎样减少校园塑料使用",
+  "ssSHScenarioEx3":"如何让课堂更有趣",
+  "ssSHTimerTitle":"限时切换",
+  "ssSHTimerSubtitle":"让每个思考角度保持同样的专注时间",
+  "ssSHTimerQuestion":"每顶帽子思考多久?",
+  "ssSHTimerPerHat":"每顶帽子 {n} 秒",
+  "ssSHTimerHintFast":"适合快速发散和轮流表达",
+  "ssSHTimerHintNormal":"适合深入思考和充分讨论",
+  "ssSHTimerHintDeep":"适合深度分析和细致探讨",
+  "ssSHTimerCustom":"自定义",
+  "ssSHTimerOff":"自由控制节奏",
+  "ssSHTimerOffSub":"你可以自己决定每顶帽子思考多久",
+  "ssSHHatsTitle":"选择要用哪几顶帽子",
+  "ssSHHatsSubtitle":"可以保留全部,也可以只选择适合本次讨论的角度",
+  "ssSHHatsStart":"开始活动",
+  "ssSHWhite":"白帽",
+  "ssSHWhiteDesc":"事实与数据",
+  "ssSHRed":"红帽",
+  "ssSHRedDesc":"情感与直觉",
+  "ssSHBlack":"黑帽",
+  "ssSHBlackDesc":"风险与问题",
+  "ssSHYellow":"黄帽",
+  "ssSHYellowDesc":"优点与价值",
+  "ssSHGreen":"绿帽",
+  "ssSHGreenDesc":"创意与方案",
+  "ssSHBlue":"蓝帽",
+  "ssSHBlueDesc":"控制与总结",
+  "ssSHVoiceInput":"语音输入",
+  "ssSHStartRec":"开始录音",
+  "ssSHStopRec":"停止录音",
+  "ssSHRecordShort":"录音时间过短",
+  "ssSHNoPermStop":"未获得麦克风权限,录音已停止",
+  "ssSHNoMicPerm":"未获得麦克风权限",
+  "ssSHRecServiceNotReady":"录音服务未就绪",
+  "ssSHRecStartFail":"启动录音失败",
+  "ssSHRecStopped":"录音已停止",
+  "ssSHClickBack":"(点击返回)",
+  "ssSHHowToDesc":"轮流戴上六顶帽子,从六个不同角度说说你的想法",
+  "ssSHYourThoughts":"你的想法",
+  "ssSHYourThoughtsPH":"从{hatName}的角度说说你的想法...",
+  "ssSHHatProgress":"第 {n}/{total} 顶",
+  "ssSHAutoSwitch":"自动切换",
+  "ssSHAutoSwitchOn":"自动切换",
+  "ssSHCountdownSec":"{n}秒",
+  "ssSHPrevHat":"上一顶",
+  "ssSHNextHat":"下一顶",
+  "ssSHFinish":"完成",
+  "ssSHSummaryTitle":"六顶思考帽·总结回顾",
+  "ssSHSummarySub":"蓝帽视角:整合六个角度的思考",
+  "ssSHNoAnswer":"(未作答)",
+  "ssSHRestart":"重新开始",
+  "ssSHRestartConfirm":"确定要重新开始填写吗?之前的内容将被清空。",
+  "ssSHBackToSetup":"返回设置",
+  "ssSHHatPromptWhite":"关于这个议题,我们已知的客观事实、数据和信息有哪些?(不加入个人观点)",
+  "ssSHHatPromptRed":"凭直觉和第一感受,你对这件事有什么情绪反应?喜欢还是不喜欢?为什么?",
+  "ssSHHatPromptBlack":"这个方案可能有什么问题、风险或不足之处?哪些地方需要谨慎考虑?",
+  "ssSHHatPromptYellow":"这个方案有哪些好处?最理想的情况会是什么样?能带来什么价值?",
+  "ssSHHatPromptGreen":"有什么新的创意、替代方案或更好的解决办法?大胆想象,不必受限于常规。",
+  "ssSHHatPromptBlue":"整体来看,这个议题最重要的是什么?我们的结论或下一步行动是什么?",
+  "ssSHAddedToClipboard":"已添加到剪贴板",
+  "ssSHHowToStep1":"1. 白帽思考:收集客观事实和数据",
+  "ssSHHowToStep2":"2. 红帽思考:表达直觉和情感反应",
+  "ssSHHowToStep3":"3. 黑帽思考:识别风险和潜在问题",
+  "ssSHHowToStep4":"4. 黄帽思考:寻找优点和积极面",
+  "ssSHHowToStep5":"5. 绿帽思考:提出创新想法和方案",
+  "ssSHHowToStep6":"6. 蓝帽思考:总结思考过程和结论",
   "ssFileTip": "文件上传指引",
   "ssFile": "文件上传",
   "ssUpload": "上传",

+ 89 - 0
src/lang/en.json

@@ -2101,6 +2101,95 @@
   "ssKWLRecServiceNotReady":"Recording service is not ready",
   "ssKWLRecStartFail":"Failed to start recording",
   "ssKWLRecStopped":"Recording stopped",
+  "ssSHTitle":"Six Thinking Hats",
+  "ssSHSubtitle":"Use six different colored hats to think from different angles",
+  "ssSHHowTo":"How to use",
+  "ssSHSetupContent":"Setup",
+  "ssSHStartActivity":"Start Activity",
+  "ssSHBeforeStart":"Let's get ready before we start",
+  "ssSHSetupDesc":"You decide this round of Six Thinking Hats",
+  "ssSHStageScenario":"Topic",
+  "ssSHStageTimer":"Timer",
+  "ssSHStageHats":"Hats",
+  "ssSHStepNext":"Next",
+  "ssSHStepPrev":"Back",
+  "ssSHMemberTitle":"Pick your team members first",
+  "ssSHMemberSubtitle":"Search by name, or pick classmates from the list to join this activity",
+  "ssSHMemberSearchPH":"Search or pick a name...",
+  "ssSHMemberConfirm":"Confirm members",
+  "ssSHMemberConfirmN":"Confirm members ({n})",
+  "ssSHScenarioTitle":"What should we think about this round?",
+  "ssSHScenarioSubtitle":"Start with a clear question or topic — everything else will revolve around it.",
+  "ssSHScenarioInputTitle":"Enter the discussion topic for this round",
+  "ssSHScenarioInputPH":"Write down the topic or scenario for this round...",
+  "ssSHScenarioExamples":"Examples:",
+  "ssSHScenarioEx1":"Should recess be longer?",
+  "ssSHScenarioEx2":"How can we reduce plastic in the school?",
+  "ssSHScenarioEx3":"How can we make class more fun?",
+  "ssSHTimerTitle":"Timed rotation",
+  "ssSHTimerSubtitle":"Give each perspective the same focused time",
+  "ssSHTimerQuestion":"How long per hat?",
+  "ssSHTimerPerHat":"{n} seconds per hat",
+  "ssSHTimerHintFast":"Great for quick brainstorming and quick turns",
+  "ssSHTimerHintNormal":"Great for deeper thinking and open discussion",
+  "ssSHTimerHintDeep":"Great for thorough analysis and careful discussion",
+  "ssSHTimerCustom":"Custom",
+  "ssSHTimerOff":"Free your pace",
+  "ssSHTimerOffSub":"You decide how long to spend on each hat",
+  "ssSHHatsTitle":"Choose which hats to use",
+  "ssSHHatsSubtitle":"Use all six, or just pick the ones that fit this round",
+  "ssSHHatsStart":"Start Activity",
+  "ssSHWhite":"White Hat",
+  "ssSHWhiteDesc":"Facts & Data",
+  "ssSHRed":"Red Hat",
+  "ssSHRedDesc":"Feelings & Intuition",
+  "ssSHBlack":"Black Hat",
+  "ssSHBlackDesc":"Risks & Problems",
+  "ssSHYellow":"Yellow Hat",
+  "ssSHYellowDesc":"Benefits & Values",
+  "ssSHGreen":"Green Hat",
+  "ssSHGreenDesc":"Ideas & Solutions",
+  "ssSHBlue":"Blue Hat",
+  "ssSHBlueDesc":"Control & Summary",
+  "ssSHVoiceInput":"Voice input",
+  "ssSHStartRec":"Start recording",
+  "ssSHStopRec":"Stop recording",
+  "ssSHRecordShort":"Recording is too short",
+  "ssSHNoPermStop":"Microphone permission not granted, recording stopped",
+  "ssSHNoMicPerm":"Microphone permission not granted",
+  "ssSHRecServiceNotReady":"Recording service not ready",
+  "ssSHRecStartFail":"Failed to start recording",
+  "ssSHRecStopped":"Recording stopped",
+  "ssSHClickBack":"(click to go back)",
+  "ssSHHowToDesc":"Wear each hat in turn and share your thoughts from six different angles",
+  "ssSHYourThoughts":"Your thoughts",
+  "ssSHYourThoughtsPH":"Share your thoughts from the {hatName} angle...",
+  "ssSHHatProgress":"Hat {n} / {total}",
+  "ssSHAutoSwitch":"Auto",
+  "ssSHAutoSwitchOn":"Auto switch",
+  "ssSHCountdownSec":"{n}s",
+  "ssSHPrevHat":"Previous",
+  "ssSHNextHat":"Next",
+  "ssSHFinish":"Finish",
+  "ssSHSummaryTitle":"Six Thinking Hats · Summary",
+  "ssSHSummarySub":"Blue hat view: integrate all six angles",
+  "ssSHNoAnswer":"(No answer yet)",
+  "ssSHRestart":"Restart",
+  "ssSHRestartConfirm":"Restart? All previous answers will be cleared.",
+  "ssSHBackToSetup":"Back to setup",
+  "ssSHHatPromptWhite":"What objective facts, data and information do we know about this topic? (no personal opinions)",
+  "ssSHHatPromptRed":"What is your gut reaction? Do you like it or dislike it? Why?",
+  "ssSHHatPromptBlack":"What are the problems, risks or downsides? What needs careful consideration?",
+  "ssSHHatPromptYellow":"What are the benefits? What would the ideal outcome look like? What value does it bring?",
+  "ssSHHatPromptGreen":"What new ideas, alternatives or better solutions can you think of? Be bold and creative.",
+  "ssSHHatPromptBlue":"Overall, what matters most here? What is our conclusion or next step?",
+  "ssSHAddedToClipboard":"Added to clipboard",
+  "ssSHHowToStep1":"1. White hat: collect objective facts and data",
+  "ssSHHowToStep2":"2. Red hat: express intuition and emotional reactions",
+  "ssSHHowToStep3":"3. Black hat: identify risks and potential problems",
+  "ssSHHowToStep4":"4. Yellow hat: look for positives and benefits",
+  "ssSHHowToStep5":"5. Green hat: propose new ideas and creative solutions",
+  "ssSHHowToStep6":"6. Blue hat: summarize the process and conclusions",
   "ssFileTip": "File upload guidance",
   "ssFile": "File upload",
   "ssUpload": "Upload",

+ 89 - 0
src/lang/hk.json

@@ -2102,6 +2102,95 @@
   "ssKWLRecServiceNotReady":"錄音服務未就緒",
   "ssKWLRecStartFail":"啟動錄音失敗",
   "ssKWLRecStopped":"錄音已停止",
+  "ssSHTitle":"六頂思考帽",
+  "ssSHSubtitle":"用六頂不同顏色的帽子,從不同角度思考問題",
+  "ssSHHowTo":"怎麼做",
+  "ssSHSetupContent":"設置內容",
+  "ssSHStartActivity":"開始活動",
+  "ssSHBeforeStart":"開始之前,先設置一下",
+  "ssSHSetupDesc":"這一輪的六頂思考帽由你自己來定",
+  "ssSHStageScenario":"情境",
+  "ssSHStageTimer":"計時",
+  "ssSHStageHats":"帽子",
+  "ssSHStepNext":"下一步",
+  "ssSHStepPrev":"上一步",
+  "ssSHMemberTitle":"先選出這次的小組成員",
+  "ssSHMemberSubtitle":"輸入姓名搜索,或從名單裡選擇和你一起完成這次活動的同學",
+  "ssSHMemberSearchPH":"輸入姓名搜索或選擇...",
+  "ssSHMemberConfirm":"確認小組成員",
+  "ssSHMemberConfirmN":"確認小組成員 ({n}人)",
+  "ssSHScenarioTitle":"這一輪要思考什麼?",
+  "ssSHScenarioSubtitle":"先確定一個問題或議題,後面的所有步驟都會圍繞它展開。",
+  "ssSHScenarioInputTitle":"請輸入本輪討論的主題",
+  "ssSHScenarioInputPH":"寫下這一輪要討論的議題或情境...",
+  "ssSHScenarioExamples":"示例:",
+  "ssSHScenarioEx1":"是否應該延長午休時間",
+  "ssSHScenarioEx2":"怎樣減少校園塑料使用",
+  "ssSHScenarioEx3":"如何讓課堂更有趣",
+  "ssSHTimerTitle":"限時切換",
+  "ssSHTimerSubtitle":"讓每個思考角度保持同樣的專注時間",
+  "ssSHTimerQuestion":"每頂帽子思考多久?",
+  "ssSHTimerPerHat":"每頂帽子 {n} 秒",
+  "ssSHTimerHintFast":"適合快速發散和輪流表達",
+  "ssSHTimerHintNormal":"適合深入思考和充分討論",
+  "ssSHTimerHintDeep":"適合深度分析和細緻探討",
+  "ssSHTimerCustom":"自定義",
+  "ssSHTimerOff":"自由控制節奏",
+  "ssSHTimerOffSub":"你可以自己決定每頂帽子思考多久",
+  "ssSHHatsTitle":"選擇要用哪幾頂帽子",
+  "ssSHHatsSubtitle":"可以保留全部,也可以只選擇適合本次討論的角度",
+  "ssSHHatsStart":"開始活動",
+  "ssSHWhite":"白帽",
+  "ssSHWhiteDesc":"事實與數據",
+  "ssSHRed":"紅帽",
+  "ssSHRedDesc":"情感與直覺",
+  "ssSHBlack":"黑帽",
+  "ssSHBlackDesc":"風險與問題",
+  "ssSHYellow":"黃帽",
+  "ssSHYellowDesc":"優點與價值",
+  "ssSHGreen":"綠帽",
+  "ssSHGreenDesc":"創意與方案",
+  "ssSHBlue":"藍帽",
+  "ssSHBlueDesc":"控制與總結",
+  "ssSHVoiceInput":"語音輸入",
+  "ssSHStartRec":"開始錄音",
+  "ssSHStopRec":"停止錄音",
+  "ssSHRecordShort":"錄音時間過短",
+  "ssSHNoPermStop":"未獲得麥克風權限,錄音已停止",
+  "ssSHNoMicPerm":"未獲得麥克風權限",
+  "ssSHRecServiceNotReady":"錄音服務未就緒",
+  "ssSHRecStartFail":"啟動錄音失敗",
+  "ssSHRecStopped":"錄音已停止",
+  "ssSHClickBack":"(點擊返回)",
+  "ssSHHowToDesc":"輪流戴上六頂帽子,從六個不同角度說說你的想法",
+  "ssSHYourThoughts":"你的想法",
+  "ssSHYourThoughtsPH":"從{hatName}的角度說說你的想法...",
+  "ssSHHatProgress":"第 {n}/{total} 頂",
+  "ssSHAutoSwitch":"自動切換",
+  "ssSHAutoSwitchOn":"自動切換",
+  "ssSHCountdownSec":"{n}秒",
+  "ssSHPrevHat":"上一頂",
+  "ssSHNextHat":"下一頂",
+  "ssSHFinish":"完成",
+  "ssSHSummaryTitle":"六頂思考帽·總結回顧",
+  "ssSHSummarySub":"藍帽視角:整合六個角度的思考",
+  "ssSHNoAnswer":"(未作答)",
+  "ssSHRestart":"重新開始",
+  "ssSHRestartConfirm":"確定要重新開始填寫嗎?之前的內容將被清空。",
+  "ssSHBackToSetup":"返回設置",
+  "ssSHHatPromptWhite":"關於這個議題,我們已知的客觀事實、數據和信息有哪些?(不加入個人觀點)",
+  "ssSHHatPromptRed":"憑直覺和第一感受,你對這件事有什麼情緒反應?喜歡還是不喜歡?為什麼?",
+  "ssSHHatPromptBlack":"這個方案可能有什麼問題、風險或不足之處?哪些地方需要謹慎考慮?",
+  "ssSHHatPromptYellow":"這個方案有哪些好處?最理想的情況會是什麼樣?能帶來什麼價值?",
+  "ssSHHatPromptGreen":"有什麼新的創意、替代方案或更好的解決辦法?大膽想象,不必受限於常規。",
+  "ssSHHatPromptBlue":"整體來看,這個議題最重要的是什麼?我們的結論或下一步行動是什麼?",
+  "ssSHAddedToClipboard":"已添加到剪貼板",
+  "ssSHHowToStep1":"1. 白帽思考:收集客觀事實和數據",
+  "ssSHHowToStep2":"2. 紅帽思考:表達直覺和情感反應",
+  "ssSHHowToStep3":"3. 黑帽思考:識別風險和潛在問題",
+  "ssSHHowToStep4":"4. 黃帽思考:尋找優點和積極面",
+  "ssSHHowToStep5":"5. 綠帽思考:提出創新想法和方案",
+  "ssSHHowToStep6":"6. 藍帽思考:總結思考過程和結論",
   "ssFileTip": "文件上傳指引",
   "ssFile": "文件上傳",
   "ssUpload": "上傳",