WXL (wul)
2026-06-15 741805d8daa2d2baa0b6b75bc1724488baf9c6bc
src/views/outsideChainwtnew.vue
@@ -1,6 +1,6 @@
<template>
  <div class="questionnaire-optimized">
    <div v-if="loading" class="loading-container">
    <div v-if="loading && !accomplish" class="loading-container">
      <div class="loading-content">
        <i class="el-icon-loading loading-icon"></i>
        <div class="loading-text">问卷加载中,请稍候...</div>
@@ -23,12 +23,163 @@
                  : "亲爱的患者-家属,我们是医院的医护人员,为了更好地了解您的康复情况,请您抽一点宝贵时间,完成这份随访问卷。"
              }}
            </div>
            <div v-if="orgname" class="questionnaire-signature">———{{ orgname }}</div>
          </div>
          <el-divider class="custom-divider"></el-divider>
          <!-- 问卷题目区域 -->
          <div class="questions-section">
          <!-- 按维度分组的题目区域 -->
          <div class="dimension-section" v-if="hasDimension">
            <!-- 遍历维度分组 -->
            <div
              class="dimension-group"
              v-for="(group, dimensionKey) in dimensionGroups"
              :key="dimensionKey"
            >
              <!-- 维度标题 - 只显示有维度标签的 -->
              <div class="dimension-title" v-if="group.dimensionLabel">
                <h3>{{ group.dimensionLabel }}</h3>
              </div>
              <!-- 该维度下的题目 -->
              <div class="questions-section">
                <div
                  class="question-item"
                  v-for="(item, index) in group.questions"
                  :key="item.id"
                  :class="{
                    'has-warning':
                      item.prompt &&
                      item.scriptResult &&
                      (item.scriptType !== 2 || item.scriptResult.length > 0),
                  }"
                >
                  <!-- 题目题干 -->
                  <div class="question-stem">
                    <span class="question-number"
                      >{{ getQuestionNumber(group, index) }}.</span
                    >
                    <span class="question-text">{{ item.scriptContent }}</span>
                    <span class="question-type-tag">
                      {{
                        item.scriptType == 1
                          ? "[单选]"
                          : item.scriptType == 2
                          ? "[多选]"
                          : "[问答]"
                      }}
                    </span>
                  </div>
                  <!-- 单选题目 -->
                  <div
                    class="question-options"
                    v-if="item.scriptType == 1 && !item.ishide"
                  >
                    <el-radio-group
                      class="options-group"
                      v-model="item.scriptResult"
                    >
                      <el-radio
                        v-for="(
                          option, optionIndex
                        ) in item.svyTaskTemplateTargetoptions"
                        :key="optionIndex"
                        :label="option.optioncontent"
                        :class="{
                          'abnormal-option':
                            option.isabnormal &&
                            item.scriptResult == option.optioncontent,
                        }"
                        @click.native.prevent="
                          handleRadioToggle(
                            item,
                            getGlobalIndex(item.dimension, group, index),
                            item.svyTaskTemplateTargetoptions,
                            option.optioncontent
                          )
                        "
                        class="option-radio"
                      >
                        <span class="option-text">{{
                          option.optioncontent
                        }}</span>
                      </el-radio>
                    </el-radio-group>
                  </div>
                  <!-- 多选题目 -->
                  <div class="question-options" v-if="item.scriptType == 2">
                    <el-checkbox-group
                      class="options-group"
                      v-model="item.scriptResult"
                    >
                      <el-checkbox
                        v-for="(
                          option, optionIndex
                        ) in item.svyTaskTemplateTargetoptions"
                        :key="optionIndex"
                        :label="option.optioncontent"
                        :class="{
                          'abnormal-option': option.isabnormal,
                        }"
                        @change="$forceUpdate()"
                        class="option-checkbox"
                      >
                        <span class="option-text">{{
                          option.optioncontent
                        }}</span>
                      </el-checkbox>
                    </el-checkbox-group>
                  </div>
                  <!-- 填空题目 -->
                  <div class="question-input" v-if="item.scriptType == 4">
                    <el-input
                      type="textarea"
                      :rows="3"
                      placeholder="请输入您的回答"
                      v-model="item.scriptResult"
                      clearable
                      class="answer-textarea"
                    ></el-input>
                  </div>
                  <!-- 提示信息 -->
                  <div
                    class="question-warning"
                    v-show="
                      item.prompt &&
                      item.scriptResult &&
                      (item.scriptType !== 2 || item.scriptResult.length > 0)
                    "
                  >
                    <el-alert
                      :title="item.prompt"
                      type="warning"
                      :closable="false"
                      class="warning-alert"
                    ></el-alert>
                  </div>
                  <div
                    v-if="item.showAppendInput || item.answerps"
                    class="append-input-container"
                  >
                    <el-input
                      type="textarea"
                      :rows="1"
                      placeholder="请输入具体信息"
                      v-model="item.answerps"
                      clearable
                    ></el-input>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <!-- 无维度时的题目区域(保持原样) -->
          <div class="questions-section" v-else>
            <div
              class="question-item"
              v-for="(item, index) in visibleQuestions"
@@ -48,9 +199,9 @@
                <span class="question-text">{{ item.scriptContent }}</span>
                <span class="question-type-tag">
                  {{
                    item.scriptType === 1
                    item.scriptType == 1
                      ? "[单选]"
                      : item.scriptType === 2
                      : item.scriptType == 2
                      ? "[多选]"
                      : "[问答]"
                  }}
@@ -143,6 +294,18 @@
                  class="warning-alert"
                ></el-alert>
              </div>
              <div
                v-if="item.showAppendInput || item.answerps"
                class="append-input-container"
              >
                <el-input
                  type="textarea"
                  :rows="1"
                  placeholder="请输入具体信息"
                  v-model="item.answerps"
                  clearable
                ></el-input>
              </div>
            </div>
          </div>
@@ -182,6 +345,7 @@
  getExternalfollowup,
  getCachequestionnaire,
  Cachequestionnaire,
  gettypeout,
  Submitaquestionnaire,
  geturlinfo,
} from "@/api/AiCentre/index";
@@ -196,12 +360,30 @@
      excep: 0,
      isabnormal: 0,
      taskname: "",
      orgname: "",
      questionList: [],
      param6: null,
      jsy: null,
      dialogVisible: false,
      Endornot: true,
      accomplish: false,
      dimensionTypes: [
        {
          value: 1,
          label: "维度1",
          listClass: "primary",
        },
        {
          value: 2,
          label: "维度二",
          listClass: "primary",
        },
        {
          value: 3,
          label: "维度三",
          listClass: "primary",
        },
      ],
      // 前端公钥
      publicKey:
        "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKR0yHv0rbJWQE+Sc7/FwpW66qMd9qX2k6z+SDgkSdxWh/1GbBoAP7bDQQRF6vXmoKsD2ya42H6XRLSDXAoayuMCAwEAAQ== ",
@@ -222,6 +404,7 @@
    // window.removeEventListener("beforeunload", this.cache);
  },
  created() {
    this.gettypeout();
    this.geturlinfo();
  },
  computed: {
@@ -231,6 +414,79 @@
        return [];
      }
      return this.questionList.filter((question) => !question.ishide);
    },
    // 计算属性:检查是否有维度字段
    hasDimension() {
      if (!this.questionList || this.questionList.length == 0) {
        return false;
      }
      // 检查任意一个题目是否有dimension字段
      return this.questionList.some(
        (question) =>
          question.dimension && this.getDimensionLabel(question.dimension)
      );
    },
    // 计算属性:按维度整理题目
    dimensionGroups() {
      if (!this.questionList || this.questionList.length == 0) {
        return {};
      }
      const groups = {};
      let currentGroupKey = null;
      let groupStartIndex = 0; // 记录当前维度组的开始索引
      this.visibleQuestions.forEach((question, index) => {
        const dimensionValue = question.dimension;
        const dimensionLabel = this.getDimensionLabel(dimensionValue);
        // 生成组key:有维度用维度值,无维度用特殊标记+起始索引
        let groupKey;
        if (dimensionValue && dimensionLabel) {
          groupKey = `dimension-${dimensionValue}`;
        } else {
          // 无维度或维度映射不存在的题目单独分组
          groupKey = `no-dimension-${groupStartIndex}`;
        }
        // 创建或获取维度组
        if (!groups[groupKey]) {
          groups[groupKey] = {
            questions: [],
            startIndex: groupStartIndex,
            dimensionValue: dimensionValue, // 保存维度值用于后续处理
            dimensionLabel: dimensionLabel, // 保存维度标签
            isNoDimension: !dimensionValue || !dimensionLabel,
          };
          currentGroupKey = groupKey;
        }
        // 将题目添加到对应的维度组
        groups[groupKey].questions.push(question);
        // 如果下一个题目维度不同,重新开始计数
        const nextQuestion = this.visibleQuestions[index + 1];
        if (nextQuestion) {
          const nextDimensionValue = nextQuestion.dimension;
          const nextDimensionLabel = this.getDimensionLabel(nextDimensionValue);
          const currentDimensionLabel = this.getDimensionLabel(dimensionValue);
          // 判断维度是否相同
          if (
            dimensionValue !== nextDimensionValue ||
            (dimensionValue &&
              nextDimensionValue &&
              ((!dimensionValue && nextDimensionValue) ||
                (dimensionValue && !nextDimensionValue)))
          ) {
            groupStartIndex = index + 1;
          }
        }
      });
      return groups;
    },
  },
  methods: {
@@ -250,9 +506,28 @@
            res.data.param3,
            res.data.param5
          );
          this.orgname = res.data.orgname;
          this.param6 = res.data.param6;
        }
      });
    },
    // 获取字典
    gettypeout() {
      gettypeout("dimensionality_type").then((res) => {
        if (res.code == 200) {
          this.dimensionTypes = res.data;
        }
      });
    },
    // 根据维度值获取维度标签
    getDimensionLabel(dimensionValue) {
      if (!dimensionValue) return "";
      const dimensionType = this.dimensionTypes.find(
        (item) => item.dictValue == Number(dimensionValue)
      );
      return dimensionType ? dimensionType.dictLabel : "";
    },
    //     extractLastSegmentFromUrl(url) {
    //     // 找到最后一个'/'的位置
@@ -431,19 +706,42 @@
    getVisibleQuestionIndex(index) {
      return index + 1;
    },
    // 获取维度内题目的序号
    getQuestionNumber(group, index) {
      return group.startIndex + index + 1;
    },
    // 获取题目在全量数组中的索引
    getGlobalIndex(dimension, group, localIndex) {
      return group.startIndex + localIndex;
    },
    // 在模板中使用
    getDimensionLabelForDisplay(dimensionKey) {
      if (dimensionKey.startsWith("no-dimension-")) {
        return "";
      }
      const dimensionValue = dimensionKey.replace("dimension-", "");
      return this.getDimensionLabel(dimensionValue);
    },
    // 新增的切换选中/取消选中方法
    handleRadioToggle(questionItem, index, options, optionValue) {
    handleRadioToggle(questionItem, globalIndex, options, optionValue) {
      // 保存当前状态以便后续比较
      const previousState = JSON.parse(JSON.stringify(this.questionList));
      // 原有的处理逻辑
      if (questionItem.scriptResult === optionValue) {
      if (questionItem.scriptResult == optionValue) {
        questionItem.scriptResult = "";
        questionItem.isabnormal = 0;
        questionItem.showAppendInput = false;
      } else {
        questionItem.scriptResult = optionValue;
        this.handleOptionChange(optionValue, index, options, questionItem);
        this.handleOptionChange(
          optionValue,
          globalIndex,
          options,
          questionItem
        );
      }
      // 处理完成后,确保重新计算可见题目的序号
@@ -476,6 +774,7 @@
      this.questionList[questionIndex].showAppendInput =
        selectedOptionObj.appendflag == 1;
      console.log(this.questionList);
      console.log(selectedOptionObj.appendflag);
      // if (!this.questionList[questionIndex].showAppendInput) {
      //   this.questionList[questionIndex].answerps = ""; // 清除附加信息
@@ -517,12 +816,12 @@
            }
            // 当前题目总是可见
            if (index === questionIndex) {
            if (index == questionIndex) {
              return { ...item, ishide: 0, hiddenByEnd: false };
            }
            // 显示目标下一题
            if (index === nextQuestionIndex) {
            if (index == nextQuestionIndex) {
              return { ...item, ishide: 0, hiddenByEnd: false };
            }
@@ -544,42 +843,17 @@
        // 如果没有跳转,只需确保下一题可见
        this.questionList = this.questionList.map((item, index) => ({
          ...item,
          ishide: index === questionIndex + 1 ? 0 : item.ishide,
          hiddenByEnd: index === questionIndex + 1 ? false : item.hiddenByEnd,
          ishide: index == questionIndex + 1 ? 0 : item.ishide,
          hiddenByEnd: index == questionIndex + 1 ? false : item.hiddenByEnd,
        }));
      }
      this.$forceUpdate();
      // 在处理完题目显示/隐藏后,强制更新视图以确保序号正确
      this.$nextTick(() => {
        this.$forceUpdate();
      });
    },
    // 处理单选选项
    // handleOptionChange(selectedvalue, index, arr) {
    //   // 查找选中的选项对象
    //   const selectedOption = arr.svyTaskTemplateTargetoptions.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;
    //   }
    // },
    // 处理多选选项
    // updateScore(selectedvalues, index, arr) {
    //   // ��加分数
    //   let score = 0;
    //   selectedvalues.forEach((value) => {
    //     const selectedOption = arr.svyTaskTemplateTargetoptions.find(
    //       (option) => option.optioncontent == value
    //     );
    //     if (selectedOption) {
    //       score += Number(selectedOption.score);
    //     }
    //   });
    //   this.questionList[index].score = score;
    // },
  },
};
</script>
@@ -620,24 +894,63 @@
}
.questionnaire-description {
  font-size: 18px;
  font-size: 16px;
  color: #5a6c84;
  line-height: 1.6;
  max-width: 700px;
  margin: 0 auto;
  line-height: 1.8;
  max-width: 720px;
  margin: 0;
  padding: 0 16px;
  text-align: justify; /* 两端对齐,更美观 */
  text-indent: 2em; /* 首行缩进 */
}
.questionnaire-signature {
  font-size: 15px;
  color: #8a9bb5; /* 比正文颜色稍浅,体现附属感 */
  text-align: right; /* 右对齐 */
  max-width: 720px;
  margin: 8px 0 0 auto; /* 上边距8px,右边靠齐 */
  padding: 0 16px;
  letter-spacing: 1px; /* 字间距稍宽,更有落款感 */
}
.custom-divider {
  margin: 25px 0;
  background-color: #eaeef2;
}
/* 维度分组样式 */
.dimension-section {
  margin-bottom: 20px;
}
.dimension-group {
  margin-bottom: 30px;
  &:last-child {
    margin-bottom: 0;
  }
}
.dimension-title {
  background: linear-gradient(135deg, #e8f4ff 0%, #d1e7ff 100%);
  padding: 12px 20px;
  border-radius: 8px;
  margin-bottom: 20px;
  border-left: 4px solid #175997;
  h3 {
    color: #175997;
    font-size: 20px;
    font-weight: 600;
    margin: 0;
  }
}
.questions-section {
  margin-bottom: 40px;
  margin-bottom: 10px;
}
.question-item {
  margin-bottom: 35px;
  margin-bottom: 25px;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #eaeef2;
@@ -651,11 +964,17 @@
  &.has-warning {
    border-left: 4px solid #e6a23c;
  }
  &:last-child {
    margin-bottom: 0;
  }
}
.question-stem {
  display: flex;
  align-items: flex-start;
  display: grid;
  grid-template-columns: auto 1fr auto; /* 题号自适应,文本占满剩余空间,题型标签自适应 */
  gap: 3px;
  align-items: center;
  margin-bottom: 20px;
  font-size: 18px;
}
@@ -663,22 +982,23 @@
.question-number {
  font-weight: 600;
  color: #175997;
  margin-right: 8px;
  min-width: 24px;
  min-width: 16px;
}
.question-text {
  flex: 1;
  line-height: 1.5;
  color: #2c3e50;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.question-type-tag {
  color: #3ba2f7;
  font-size: 14px;
  margin-left: 10px;
  font-weight: 500;
  text-align: right;
}
.question-options {
@@ -745,7 +1065,13 @@
.question-warning {
  margin-top: 15px;
}
.append-input-container {
  margin-top: 15px;
  padding: 10px;
  background-color: #f5f7fa;
  border-radius: 4px;
  border: 1px solid #dcdfe6;
}
.warning-alert {
  :deep(.el-alert__title) {
    font-size: 15px;
@@ -756,6 +1082,8 @@
.submit-section {
  text-align: center;
  padding: 20px 0 10px;
  border-top: 1px solid #eaeef2;
  margin-top: 20px;
}
.submit-button {
@@ -849,6 +1177,14 @@
    padding: 15px;
  }
  .dimension-title {
    padding: 10px 15px;
    h3 {
      font-size: 18px;
    }
  }
  .completion-content {
    padding: 30px 20px;
  }
@@ -874,6 +1210,10 @@
  .completion-icon {
    font-size: 60px;
  }
  .dimension-title h3 {
    font-size: 16px;
  }
}
.loading-container {
  display: flex;