WXL (wul)
昨天 66cc734e81fe0497d88e0951b6aee9c9c8342f2c
src/views/satisfaction.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1188 @@
<template>
  <div class="questionnaire" :class="'survey-type-' + surveyType">
    <!-- åŠ è½½çŠ¶æ€ -->
    <div v-if="loading" class="loading-state">
      <div class="loading-spinner">
        <i class="el-icon-loading"></i>
        <p>问卷加载中...</p>
      </div>
    </div>
    <!-- æ— æ•°æ®çŠ¶æ€ -->
    <div v-else-if="isEmptyData" class="empty-state">
      <div class="empty-content">
        <i class="el-icon-document" style="font-size: 64px; color: #909399"></i>
        <h3>暂无问卷数据</h3>
        <p>当前没有可用的问卷,请联系管理员或稍后重试</p>
        <el-button
          type="primary"
          @click="loadSurveyData"
          icon="el-icon-refresh"
        >
          é‡æ–°åŠ è½½
        </el-button>
      </div>
    </div>
    <!-- é”™è¯¯çŠ¶æ€ -->
    <div v-else-if="hasError" class="error-state">
      <div class="error-content">
        <i class="el-icon-warning" style="font-size: 64px; color: #f56c6c"></i>
        <h3>数据加载失败</h3>
        <p>{{ errorMessage }}</p>
        <el-button
          type="primary"
          @click="loadSurveyData"
          icon="el-icon-refresh"
        >
          é‡æ–°å°è¯•
        </el-button>
      </div>
    </div>
    <div class="CONTENT" v-if="!accomplish">
      <div class="preview-left">
        <div class="toptitle">
          <div class="title">{{ surveyTitle }}</div>
          <div style="font-size: 22px; margin-bottom: 20px; line-height: 1.5">
            {{ surveyDescription }}
          </div>
        </div>
        <div v-if="showDeptSelect" class="dept-select-container">
          <el-form>
            <el-form>
              <el-form-item label="选择科室">
                <el-select
                  v-model="selectedDept"
                  filterable
                  clearable
                  placeholder="请选择科室或输入关键词搜索"
                  @change="handleDeptChange"
                  popper-class="dept-select-dropdown"
                >
                  <el-option
                    v-for="dept in filteredDeptList"
                    :key="dept.code"
                    :label="`${dept.name} (${dept.code})`"
                    :value="dept.name"
                  >
                    <span>{{ dept.name }}</span>
                  </el-option>
                </el-select>
              </el-form-item>
            </el-form>
          </el-form>
        </div>
        <el-divider></el-divider>
        <!-- å•选题 -->
        <div
          class="topic-dev"
          v-for="(item, index) in questionList"
          :key="item.scriptId"
        >
          <div class="scriptTopic-dev" :key="index" v-if="item.scriptType == 1">
            <div class="dev-text">
              {{ index + 1 }}、<span style="line-height: 1.5"
                >{{ item.scriptContent }}
                <span style="color: #3ba2f7">[单选]</span></span
              >
            </div>
            <div class="dev-xx">
              <el-radio-group
                class="custom-radio"
                v-model="item.scriptResult"
                @change="handleOptionChange($event, index, item)"
              >
                <el-radio
                  border
                  v-for="(option, optIndex) in item.svyLibTemplateTargetoptions"
                  :class="
                    option.isabnormal &&
                    item.scriptResult == option.optioncontent
                      ? 'red-star'
                      : ''
                  "
                  :key="optIndex"
                  :label="option.optioncontent"
                  >{{ option.optioncontent }}</el-radio
                >
              </el-radio-group>
            </div>
            <div v-show="item.prompt">
              <el-alert :title="item.prompt" type="warning"> </el-alert>
            </div>
          </div>
          <!-- å¤šé€‰é¢˜ -->
          <div class="scriptTopic-dev" :key="index" v-if="item.scriptType == 2">
            <div class="dev-text">
              {{ index + 1 }}、<span style="line-height: 1.5"
                >{{ item.scriptContent }}
                <span style="color: #3ba2f7">[多选]</span></span
              >
            </div>
            <div class="dev-xx">
              <el-checkbox-group
                class="custom-radio"
                v-model="item.scriptResult"
              >
                <el-checkbox
                  border
                  @change="$forceUpdate()"
                  v-for="(option, optIndex) in item.svyLibTemplateTargetoptions"
                  :key="optIndex"
                  :label="option.optioncontent"
                >
                  {{ option.optioncontent }}
                </el-checkbox>
              </el-checkbox-group>
            </div>
            <div v-show="item.prompt && item.scriptResult[0]">
              <el-alert :title="item.prompt" type="warning"> </el-alert>
            </div>
          </div>
          <!-- å¡«ç©ºé¢˜ -->
          <div class="scriptTopic-dev" :key="index" v-if="item.scriptType == 4">
            <div class="dev-text">
              {{ index + 1 }}、<span style="line-height: 1.5"
                >{{ item.scriptContent
                }}<span style="color: #3ba2f7">[问答]</span></span
              >
            </div>
            <div class="dev-xx">
              <el-input
                type="textarea"
                :rows="3"
                placeholder="请输入"
                v-model="item.scriptResult"
                clearable
              >
              </el-input>
            </div>
          </div>
        </div>
        <div class="bottom-fixed">
          <el-button
            type="primary"
            style="width: 80%; font-size: 20px"
            @click="submitSurvey"
            >提交问卷</el-button
          >
        </div>
      </div>
    </div>
    <div class="CONTENT" v-else>
      <div class="preview-lefts">
        <div class="completion-message">
          <div class="thank-you">{{ this.accomplish || "感谢您的配合!" }}</div>
          <div class="feedback-message">{{ completionMessage }}</div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import {
  getScriptByCondition,
  saveMYDQuestionAnswer,
  WLgetDept,
} from "@/api/AiCentre/index";
export default {
  data() {
    return {
      surveyType: null, // 'outpatient', 'inpatient'
      surveyTitle: "",
      surveyDescription: "",
      questionList: [],
      deptList: [],
      completionMessage: "",
      accomplish: false,
      showDeptSelect: false,
      selectedDept: null,
      deptSearchText: "", // ä¿ç•™ç”¨äºŽæœ¬åœ°è¿‡æ»¤
      deptList: [],
      filteredDeptList: [],
      // åŠ å¯†åŽçš„å‚æ•°
      encryptedParams: {
        param1: "",
        param2: "",
        param3: "",
        param4: "",
        param5: "",
        param6: "30001002",
      },
      isEmptyData: false, // æ–°å¢žï¼šæ— æ•°æ®çŠ¶æ€
      hasError: false, // æ–°å¢žï¼šé”™è¯¯çŠ¶æ€
      loading: false, // æ–°å¢žï¼šåŠ è½½çŠ¶æ€
      errorMessage: "", // æ–°å¢žï¼šé”™è¯¯ä¿¡æ¯
      // æµ‹è¯•数据
      testData: {
        1: {
          title: "门诊满意度调查",
          description:
            "亲爱的患者,感谢您选择我们的医疗服务。为了不断提升服务质量,请您花几分钟时间填写此问卷。",
          questions: [
            {
              scriptId: 1,
              scriptType: 1,
              scriptContent: "您对门诊医生的诊疗水平是否满意?",
              scriptResult: null,
              svyLibTemplateTargetoptions: [
                { optioncontent: "非常满意", value: "5", isabnormal: false },
                { optioncontent: "满意", value: "4", isabnormal: false },
                { optioncontent: "一般", value: "3", isabnormal: true },
                { optioncontent: "不满意", value: "2", isabnormal: true },
                { optioncontent: "非常不满意", value: "1", isabnormal: true },
              ],
            },
            {
              scriptId: 2,
              scriptType: 1,
              scriptContent: "您对门诊护士的服务态度是否满意?",
              scriptResult: null,
              svyLibTemplateTargetoptions: [
                { optioncontent: "非常满意", value: "5", isabnormal: false },
                { optioncontent: "满意", value: "4", isabnormal: false },
                { optioncontent: "一般", value: "3", isabnormal: true },
                { optioncontent: "不满意", value: "2", isabnormal: true },
                { optioncontent: "非常不满意", value: "1", isabnormal: true },
              ],
            },
            {
              scriptId: 3,
              scriptType: 2,
              scriptContent: "您认为门诊哪些方面需要改进?(可多选)",
              scriptResult: [],
              svyLibTemplateTargetoptions: [
                {
                  optioncontent: "排队等候时间",
                  value: "waiting_time",
                  isabnormal: false,
                },
                {
                  optioncontent: "医生沟通方式",
                  value: "communication",
                  isabnormal: false,
                },
                {
                  optioncontent: "就诊环境",
                  value: "environment",
                  isabnormal: false,
                },
                {
                  optioncontent: "医疗设备",
                  value: "equipment",
                  isabnormal: false,
                },
                { optioncontent: "其他", value: "other", isabnormal: false },
              ],
            },
            {
              scriptId: 4,
              scriptType: 4,
              scriptContent: "您对门诊服务还有什么其他建议?",
              scriptResult: null,
            },
          ],
          completionMessage:
            "感谢您宝贵的意见!我们将不断改进门诊服务质量,为您提供更好的医疗服务体验。",
        },
        2: {
          title: "住院满意度调查",
          description:
            "亲爱的患者及家属,感谢您选择在我院住院治疗。为了提升住院服务质量,请您填写此问卷。",
          questions: [
            {
              scriptId: 1,
              scriptType: 1,
              scriptContent: "您对住院期间医生的诊疗水平是否满意?",
              scriptResult: null,
              svyLibTemplateTargetoptions: [
                { optioncontent: "非常满意", value: "5", isabnormal: false },
                { optioncontent: "满意", value: "4", isabnormal: false },
                { optioncontent: "一般", value: "3", isabnormal: true },
                { optioncontent: "不满意", value: "2", isabnormal: true },
                { optioncontent: "非常不满意", value: "1", isabnormal: true },
              ],
            },
            {
              scriptId: 2,
              scriptType: 1,
              scriptContent: "您对住院期间护士的护理服务是否满意?",
              scriptResult: null,
              svyLibTemplateTargetoptions: [
                { optioncontent: "非常满意", value: "5", isabnormal: false },
                { optioncontent: "满意", value: "4", isabnormal: false },
                { optioncontent: "一般", value: "3", isabnormal: true },
                { optioncontent: "不满意", value: "2", isabnormal: true },
                { optioncontent: "非常不满意", value: "1", isabnormal: true },
              ],
            },
            {
              scriptId: 3,
              scriptType: 1,
              scriptContent: "您对住院病房的环境和卫生是否满意?",
              scriptResult: null,
              svyLibTemplateTargetoptions: [
                { optioncontent: "非常满意", value: "5", isabnormal: false },
                { optioncontent: "满意", value: "4", isabnormal: false },
                { optioncontent: "一般", value: "3", isabnormal: true },
                { optioncontent: "不满意", value: "2", isabnormal: true },
                { optioncontent: "非常不满意", value: "1", isabnormal: true },
              ],
            },
            {
              scriptId: 4,
              scriptType: 4,
              scriptContent: "您对住院服务还有什么其他建议?",
              scriptResult: null,
            },
          ],
          completionMessage:
            "感谢您对我们工作的支持!我们将根据您的反馈持续改进住院服务质量,祝您早日康复!",
        },
      },
    };
  },
  created() {
    this.initSurveyData();
  },
  methods: {
    // åˆå§‹åŒ–调查数据
    initSurveyData() {
      // ä»Žè·¯ç”±å‚数获取加密后的参数
      this.encryptedParams.param1 =
        this.$route.query.param1 ||
        "WOAq2QZd43E-qg-96SvuIFsn-sdRVxQNH4M82XhpXp_Ux4PFrPaqSFXcKaeA6oxEgNhPisA86LvU9kTAEz4xvQ==";
      this.encryptedParams.param2 =
        this.$route.query.param2 ||
        "XWeBh42RLYlNsMcomgw9UXhUPySkRP5EneWSueSq8F84qwYznU9heXuSx4tUMUtDvRnuJ86moJivy-kWQX12Rg==";
      this.encryptedParams.param5 = this.$route.query.param3 || "2"; //  1=住院, 2=门诊, 3=投诉建议
      this.encryptedParams.param6 = this.$route.query.param4 || "30001002";
      this.surveyType = parseInt(this.encryptedParams.param5) || 2;
      // åŠ è½½é—®å·æ•°æ®
      this.loadSurveyData();
      // èŽ·å–ç§‘å®¤åˆ—è¡¨
      this.WLgetDept();
    },
    WLgetDept() {
      // è°ƒç”¨æŽ¥å£èŽ·å–ç§‘å®¤æ•°æ®
      WLgetDept(this.encryptedParams.param6).then((res) => {
        this.deptList = Object.entries(res.data).map(([code, name]) => ({
          code,
          name,
        }));
        this.filteredDeptList = [...this.deptList];
        if (this.surveyType === 3) {
          this.showDeptSelect = true;
        }
      });
    },
    filterDeptList() {
      if (!this.deptSearchText) {
        this.filteredDeptList = [...this.deptList];
        return;
      }
      const searchText = this.deptSearchText.toLowerCase();
      this.filteredDeptList = this.deptList.filter(
        (dept) =>
          dept.name.toLowerCase().includes(searchText) ||
          dept.code.toLowerCase().includes(searchText)
      );
    },
    // åŠ è½½è°ƒæŸ¥æ•°æ®
    loadSurveyData() {
      this.loading = true;
      this.isEmptyData = false;
      this.hasError = false;
      this.errorMessage = "";
      // è°ƒç”¨æŽ¥å£èŽ·å–é—®å·æ•°æ®
      // æ ¹æ®é—®å·ç±»åž‹è®¾ç½®ä¸åŒçš„参数
      let encryptedParams = {
        param1: this.encryptedParams.param1,
      };
      // æ ¹æ®surveyType设置不同的参数
      switch (this.surveyType) {
        case 1: // ä½é™¢
          encryptedParams.param2 = this.encryptedParams.param2;
          break;
        case 2: // é—¨è¯Š
          encryptedParams.param3 = this.encryptedParams.param2;
          break;
        case 3: // æŠ•诉建议
          encryptedParams.param4 = this.encryptedParams.param2;
          break;
        default:
          encryptedParams.param3 = this.encryptedParams.param2;
      }
      getScriptByCondition(encryptedParams)
        .then((res) => {
          if (res.code === 200) {
            if (res.data.result) {
              this.accomplish = res.data.result;
              return;
            }
            if (
              !res.data.svyLibTemplateScriptVOS ||
              res.data.svyLibTemplateScriptVOS.length === 0
            ) {
              this.isEmptyData = true;
              this.$message.warning("暂无问卷数据");
              return;
            }
            // å¤„理接口返回的数据
            this.questionList = res.data.svyLibTemplateScriptVOS.map((item) => {
              return {
                ...item,
                scriptResult: item.scriptType === 2 ? [] : null,
              };
            });
            // æ ¹æ®surveyType设置标题和描述
            switch (this.surveyType) {
              case 2: // é—¨è¯Š
                this.surveyTitle = "门诊满意度调查";
                this.surveyDescription =
                  "亲爱的患者,感谢您选择我们的医疗服务。为了不断提升服务质量,请您花几分钟时间填写此问卷。";
                this.completionMessage =
                  "感谢您宝贵的意见!我们将不断改进门诊服务质量,为您提供更好的医疗服务体验。";
                break;
              case 1: // ä½é™¢
                this.surveyTitle = "住院满意度调查";
                this.surveyDescription =
                  "亲爱的患者及家属,感谢您选择在我院住院治疗。为了提升住院服务质量,请您填写此问卷。";
                this.completionMessage =
                  "感谢您对我们工作的支持!我们将根据您的反馈持续改进住院服务质量,祝您早日康复!";
                break;
              case 3: // æŠ•诉建议
                this.surveyTitle = "投诉建议反馈";
                this.surveyDescription =
                  "尊敬的客户,感谢您抽出宝贵时间提供反馈。您的意见对我们改进服务非常重要。";
                this.completionMessage =
                  "感谢您的反馈!我们已收到您的投诉/建议,将尽快处理并与您联系。";
                break;
              default:
                this.useTestData(1); // é»˜è®¤ä½¿ç”¨é—¨è¯Šæ•°æ®
            }
          } else {
            // æŽ¥å£æ— æ•°æ®æˆ–失败,使用测试数据
            // this.useTestData(this.surveyType);
          }
        })
        .catch(() => {
          console.error("数据获取失败:", error);
          this.hasError = true;
          this.errorMessage =
            error.message || "问卷数据加载失败,请检查网络连接后重试";
          this.$message.error("数据加载失败");
          // æŽ¥å£è°ƒç”¨å¤±è´¥ï¼Œä½¿ç”¨æµ‹è¯•数据
          // this.useTestData(this.surveyType);
        })
        .finally(() => {
          this.loading = false;
        });
    },
    handleDeptChange(value) {
      this.selectedDept = value;
      // å¯ä»¥åœ¨è¿™é‡Œæ·»åŠ å…¶ä»–å¤„ç†é€»è¾‘
    },
    // ä½¿ç”¨æµ‹è¯•数据
    useTestData(surveyType) {
      const type = [1, 2, 3].includes(surveyType) ? surveyType : 1;
      const testData = this.testData[type];
      this.surveyTitle = testData.title;
      this.surveyDescription = testData.description;
      this.questionList = testData.questions;
      this.completionMessage = testData.completionMessage;
    },
    // æäº¤è°ƒæŸ¥é—®å·ï¼ˆç›´æŽ¥æäº¤ï¼Œä¸ç»è¿‡ç¼“存)
    async submitSurvey() {
      // éªŒè¯å¿…填项
      if (this.hasUnansweredRequiredQuestions()) {
        this.$message.error("请完成所有必填问题后再提交");
        return;
      }
      try {
        const submitData = this.prepareSubmitData();
        const res = await saveMYDQuestionAnswer(submitData);
        if (res.code === 200) {
          this.accomplish = "问卷已提交";
          this.$message.success("提交成功!感谢您的反馈。");
        } else {
          this.$message.error(res.msg || "提交失败,请稍后再试");
        }
      } catch (error) {
        this.$message.error("网络错误,提交失败");
        console.error("提交失败:", error);
      }
    },
    // æ£€æŸ¥æ˜¯å¦æœ‰æœªå›žç­”的必填问题
    hasUnansweredRequiredQuestions() {
      return this.questionList.some((question) => {
        return (
          question.required &&
          (question.scriptResult === null ||
            question.scriptResult === "" ||
            (Array.isArray(question.scriptResult) &&
              question.scriptResult.length === 0))
        );
      });
    },
    // å‡†å¤‡æäº¤æ•°æ®
    prepareSubmitData() {
      // åˆ›å»ºç§‘室选择问题对象
      const deptQuestion = {
        scriptId: "dept_selection", // è‡ªå®šä¹‰ID
        scriptType: 4, // 4表示问答类型
        scriptContent: "选择的科室",
        scriptResult: this.selectedDept || "", // å­˜å‚¨é€‰æ‹©çš„科室名称
        required: false, // éžå¿…å¡«
        sort: 999,
        nextScriptno: "1",
      };
      return {
        taskId: this.encryptedParams.param1,
        serialnum: this.encryptedParams.param2 || this.encryptedParams.param3,
        mzzy: this.surveyType,
        svyLibTemplateScriptVOS: [
          deptQuestion, // å°†ç§‘室选择作为第一个问题
          ...this.questionList.map((item) => {
            return {
              scriptId: item.scriptId,
              scriptType: item.scriptType,
              scriptResult:
                item.scriptType === 2
                  ? (item.scriptResult || []).join("&")
                  : item.scriptResult || "",
              nextScriptno: item.nextScriptno,
              score: item.score,
              prompt: item.prompt,
              ...item,
            };
          }),
        ],
        excep: this.checkAbnormalOptions() ? 1 : 0,
      };
    },
    // æ£€æŸ¥å¼‚常选项
    checkAbnormalOptions() {
      return this.questionList.some((question) => {
        if (!question.scriptResult) return false;
        if (question.scriptType === 1) {
          // å•选题异常检查
          const selectedOption = question.svyLibTemplateTargetoptions.find(
            (opt) => opt.optioncontent === question.scriptResult
          );
          return selectedOption?.isabnormal;
        } else if (question.scriptType === 2) {
          // å¤šé€‰é¢˜å¼‚常检查
          return question.scriptResult.some((answer) => {
            const option = question.svyLibTemplateTargetoptions.find(
              (opt) => opt.optioncontent === answer
            );
            return option?.isabnormal;
          });
        }
        return false;
      });
    },
    // å¤„理单选选项变化
    handleOptionChange(selectedValue, index, question) {
      const selectedOption = question.svyLibTemplateTargetoptions.find(
        (option) => option.optioncontent === selectedValue
      );
      if (selectedOption) {
        this.questionList[index].nextScriptno = selectedOption.nextQuestion;
        this.questionList[index].score = selectedOption.score;
        this.questionList[index].prompt = selectedOption.prompt;
      }
    },
  },
};
</script>
<style lang="scss" scoped>
/* åŸºç¡€æ ·å¼å˜é‡ */
:root {
  --primary-color: #1a73e8; /* é»˜è®¤è“è‰² */
  --secondary-color: #34d399; /* é»˜è®¤ç»¿è‰² */
  --alert-color: #ed8936; /* é»˜è®¤æ©™è‰² */
}
/* é—¨è¯Šæ ·å¼å˜é‡ */
.survey-type-1 {
  --primary-color: #1a73e8; /* åŒ»ç–—蓝 */
  --secondary-color: #34d399; /* å¥åº·ç»¿ */
  --alert-color: #ed8936; /* è­¦ç¤ºæ©™ */
}
/* ä½é™¢æ ·å¼å˜é‡ */
.survey-type-2 {
  --primary-color: #5a67d8; /* æ·±è“ç´« */
  --secondary-color: #667eea; /* æµ…蓝紫 */
  --alert-color: #f56565; /* è­¦ç¤ºçº¢ */
}
/* æŠ•诉建议样式变量 */
.survey-type-3 {
  --primary-color: #e53e3e; /* ç´§æ€¥çº¢ */
  --secondary-color: #f6ad55; /* è­¦ç¤ºé»„ */
  --alert-color: #f56565; /* è­¦ç¤ºçº¢ */
}
.questionnaire {
  font-family: "PingFang SC", "Helvetica Neue", Arial, sans-serif;
  min-height: 100vh;
  margin: 0;
  padding: 0;
  color: #333;
  transition: all 0.3s ease;
  /* æ ¹æ®surveyType应用不同的主题 */
  &.survey-type-1 {
    background-color: #f5f9fc;
    --theme-gradient: linear-gradient(135deg, #1a73e8, #34d399);
  }
  &.survey-type-2 {
    background-color: #f8f9ff;
    --theme-gradient: linear-gradient(135deg, #5a67d8, #667eea);
  }
  &.survey-type-3 {
    background-color: #fff5f5;
    --theme-gradient: linear-gradient(135deg, #e53e3e, #f6ad55);
  }
}
.dept-select-container {
  margin: 20px 0;
  padding: 20px;
  background-color: #f8fafc;
  border-radius: 8px;
  border: 1px solid #e2e8f0;
}
/* è°ƒæ•´ä¸‹æ‹‰é€‰é¡¹æ ·å¼ */
.el-select-dropdown__item {
  display: flex;
  justify-content: space-between;
}
/* ä¸‹æ‹‰æ¡†æ ·å¼è°ƒæ•´ */
::v-deep .dept-select-dropdown {
  max-height: 400px; /* é™åˆ¶æœ€å¤§é«˜åº¦ */
  overflow-y: auto; /* æ·»åŠ æ»šåŠ¨æ¡ */
  .el-select-dropdown__item {
    display: flex;
    justify-content: space-between;
    padding: 0 20px;
    height: auto;
    line-height: 36px;
    span {
      display: inline-block;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    /* åç§°éƒ¨åˆ† */
    span:first-child {
      width: 60%;
      text-align: left;
    }
    /* ç¼–码部分 */
    span:last-child {
      width: 40%;
      text-align: right;
    }
  }
}
/* ç§»åŠ¨ç«¯é€‚é… */
@media (max-width: 768px) {
  ::v-deep .dept-select-dropdown {
    max-width: 100vw; /* é™åˆ¶æœ€å¤§å®½åº¦ä¸ºè§†å£å®½åº¦ */
    width: auto !important;
    left: 10px !important;
    right: 10px !important;
    .el-select-dropdown__item {
      span:first-child {
        width: 50%;
      }
      span:last-child {
        width: 50%;
      }
    }
  }
}
.CONTENT {
  max-width: 900px;
  margin: 0 auto;
  padding: 20px;
  .title {
    color: var(--primary-color);
    font-size: 28px;
    font-weight: 600;
    margin-bottom: 15px;
    text-align: center;
    letter-spacing: 0.5px;
    position: relative;
    &::after {
      content: "";
      position: absolute;
      bottom: -8px;
      left: 50%;
      transform: translateX(-50%);
      width: 60px;
      height: 3px;
      background: var(--theme-gradient);
      border-radius: 3px;
    }
  }
}
.preview-left {
  margin: 20px 0;
  margin-bottom: 100px;
  background-color: #fff;
  border-radius: 12px;
  padding: 30px;
  border: none;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  transition: all 0.3s ease;
  &:hover {
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
  }
  .toptitle {
    margin-bottom: 25px;
    div {
      color: #4a5568;
      font-size: 18px;
      line-height: 1.6;
      text-align: center;
    }
  }
  .el-divider {
    background-color: #e2e8f0;
    margin: 25px 0;
  }
  .topic-dev {
    margin-bottom: 30px;
    font-size: 17px;
    background-color: #f8fafc;
    border-radius: 10px;
    padding: 20px;
    transition: all 0.3s ease;
    position: relative;
    overflow: hidden;
    &:hover {
      background-color: #f1f5f9;
    }
    /* æ·»åŠ ç±»åž‹æ ‡è¯†å°æ ‡ç­¾ */
    &::before {
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      width: 4px;
      height: 100%;
      background: var(--primary-color);
    }
    .dev-text {
      margin-bottom: 18px;
      font-weight: 500;
      color: #2d3748;
      font-size: 18px;
      line-height: 1.6;
      span[style*="color: #3ba2f7"] {
        font-size: 14px;
        margin-left: 8px;
        color: var(--primary-color) !important;
      }
    }
  }
}
.preview-lefts {
  margin: 20px 0;
  background-color: #fff;
  border-radius: 12px;
  padding: 40px;
  min-height: 400px;
  border: none;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  .completion-message {
    padding: 40px;
    max-width: 600px;
    .thank-you {
      font-size: 32px;
      color: var(--primary-color);
      font-weight: 600;
      margin-bottom: 25px;
      position: relative;
      display: inline-block;
      &::after {
        content: "";
        position: absolute;
        bottom: -10px;
        left: 50%;
        transform: translateX(-50%);
        width: 80px;
        height: 3px;
        background: var(--theme-gradient);
        border-radius: 3px;
      }
    }
    .feedback-message {
      font-size: 20px;
      line-height: 1.7;
      color: #4a5568;
      margin: 0 auto;
    }
  }
}
.red-star {
  ::v-deep .el-radio__label,
  ::v-deep .el-checkbox__label {
    position: relative;
    padding-right: 20px;
    &::after {
      content: "*";
      color: #ef4444;
      position: absolute;
      right: 0;
      top: 0;
      font-size: 16px;
    }
  }
}
.loading-state {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 60vh;
  padding: 40px;
  .loading-spinner {
    text-align: center;
    .el-icon-loading {
      font-size: 48px;
      color: var(--primary-color);
      margin-bottom: 16px;
      animation: rotating 2s linear infinite;
    }
    p {
      color: #666;
      font-size: 16px;
    }
  }
}
.empty-state,
.error-state {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 60vh;
  padding: 40px;
  .empty-content,
  .error-content {
    text-align: center;
    max-width: 400px;
    h3 {
      color: #666;
      font-size: 20px;
      margin: 16px 0 12px;
      font-weight: 500;
    }
    p {
      color: #999;
      font-size: 14px;
      margin-bottom: 24px;
      line-height: 1.6;
    }
  }
}
.error-state {
  .error-content {
    h3 {
      color: #f56c6c;
    }
  }
}
@keyframes rotating {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
/* å“åº”式调整 */
@media (max-width: 768px) {
  .loading-state,
  .empty-state,
  .error-state {
    padding: 20px;
    min-height: 50vh;
    .el-icon-loading,
    .el-icon-document,
    .el-icon-warning {
      font-size: 40px !important;
    }
    h3 {
      font-size: 18px !important;
    }
    p {
      font-size: 13px !important;
    }
  }
}
::v-deep {
  .el-checkbox-group {
    display: flex;
    flex-direction: column;
    margin: 15px 0;
    gap: 12px;
  }
  .el-radio-group {
    display: flex;
    flex-direction: column;
    margin: 15px 0;
    gap: 12px;
  }
  .el-radio.is-bordered,
  .el-checkbox.is-bordered {
    width: 100%;
    margin-right: 0;
    margin-bottom: 10px;
    max-width: 400px;
    padding: 14px 20px 14px 15px;
    border-radius: 8px;
    height: auto;
    min-height: 50px;
    border: 1px solid #e2e8f0;
    transition: all 0.3s ease;
    margin-left: 0 !important;
    margin-top: 0 !important;
    .el-radio-group,
    .el-checkbox-group {
      align-items: center;
    }
    &:hover {
      border-color: var(--primary-color);
      box-shadow: 0 2px 8px rgba(var(--primary-color), 0.15);
    }
    &.is-checked {
      border-color: var(--primary-color);
      background-color: rgba(var(--primary-color), 0.05);
    }
  }
  .el-radio__label,
  .el-checkbox__label {
    font-size: 16px;
    color: #2d3748;
  }
  .el-alert--warning.is-light {
    background-color: #fff8f0;
    color: var(--alert-color);
    margin-top: 15px;
    border-radius: 8px;
    border-left: 4px solid var(--alert-color);
    .el-alert__title {
      font-size: 15px;
      line-height: 1.6;
      color: var(--alert-color);
    }
    .el-alert__closebtn {
      color: var(--alert-color);
    }
  }
  .el-textarea__inner {
    font-size: 16px;
    border-radius: 8px;
    border: 1px solid #e2e8f0;
    padding: 12px 15px;
    transition: all 0.3s ease;
    min-height: 100px;
    &:focus {
      border-color: var(--primary-color);
      box-shadow: 0 0 0 2px rgba(var(--primary-color), 0.2);
    }
    &::placeholder {
      color: #a0aec0;
    }
  }
  .el-radio__input.is-checked .el-radio__inner {
    background-color: var(--primary-color);
    border-color: var(--primary-color);
  }
  .el-checkbox__input.is-checked .el-checkbox__inner {
    background-color: var(--primary-color);
    border-color: var(--primary-color);
  }
  .el-radio__inner,
  .el-checkbox__inner {
    width: 18px;
    height: 18px;
  }
}
.bottom-fixed {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  text-align: center;
  padding: 10px 0;
  background: var(--theme-gradient);
  box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.1);
  z-index: 1000;
  transition: all 0.3s ease;
  .el-button {
    height: 56px;
    font-size: 18px;
    font-weight: 500;
    letter-spacing: 0.5px;
    border-radius: 8px;
    background-color: #fff;
    color: var(--primary-color);
    border: none;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    transition: all 0.3s ease;
    width: 80%;
    max-width: 400px;
    position: relative;
    overflow: hidden;
    &::before {
      content: "";
      position: absolute;
      top: 0;
      left: -100%;
      width: 100%;
      height: 100%;
      background: linear-gradient(
        90deg,
        transparent,
        rgba(255, 255, 255, 0.4),
        transparent
      );
      transition: all 0.5s ease;
    }
    &:hover {
      transform: translateY(-2px);
      box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
      &::before {
        left: 100%;
      }
    }
    &:active {
      transform: translateY(0);
    }
  }
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  .questionnaire {
    .CONTENT {
      padding: 15px;
    }
    .preview-left,
    .preview-lefts {
      padding: 20px;
      margin-bottom: 80px;
    }
    .title {
      font-size: 24px !important;
    }
    .dev-text {
      font-size: 16px !important;
    }
    .bottom-fixed .el-button {
      height: 50px;
      font-size: 16px;
      width: 90%;
    }
  }
}
</style>