WXL (wul)
12 小时以前 e9f35f9782f3d99d742c294fe503fc1294f203c4
src/views/outsideChainwtnew.vue
@@ -1,5 +1,15 @@
<template>
  <div class="questionnaire-optimized">
    <!-- 提交中的全屏加载遮罩(大字体,适老化) -->
    <div v-if="submitting" class="submit-loading-mask">
      <div class="submit-loading-box">
        <div class="submit-loading-spinner"></div>
        <div class="submit-loading-title">问卷提交中</div>
        <div class="submit-loading-tip">请稍候,正在上传您的答案...</div>
        <div class="submit-loading-warn">请不要关闭或刷新页面</div>
      </div>
    </div>
    <div v-if="loading && !accomplish" class="loading-container">
      <div class="loading-content">
        <i class="el-icon-loading loading-icon"></i>
@@ -22,6 +32,9 @@
                  ? kcb
                  : "亲爱的患者-家属,我们是医院的医护人员,为了更好地了解您的康复情况,请您抽一点宝贵时间,完成这份随访问卷。"
              }}
            </div>
            <div v-if="orgname" class="questionnaire-signature">
              ———{{ orgname }}
            </div>
          </div>
@@ -51,6 +64,7 @@
                      item.prompt &&
                      item.scriptResult &&
                      (item.scriptType !== 2 || item.scriptResult.length > 0),
                    'required-error': item.requiredError,
                  }"
                >
                  <!-- 题目题干 -->
@@ -58,7 +72,11 @@
                    <span class="question-number"
                      >{{ getQuestionNumber(group, index) }}.</span
                    >
                    <span class="question-text">{{ item.scriptContent }}</span>
                    <span class="question-text"
                      ><span v-if="isRequired(item)" class="required-star"
                        >*</span
                      >{{ item.scriptContent }}</span
                    >
                    <span class="question-type-tag">
                      {{
                        item.scriptType == 1
@@ -93,7 +111,7 @@
                        @click.native.prevent="
                          handleRadioToggle(
                            item,
                            getGlobalIndex(dimension, group, index),
                            getGlobalIndex(item.dimension, group, index),
                            item.svyTaskTemplateTargetoptions,
                            option.optioncontent
                          )
@@ -160,6 +178,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>
            </div>
@@ -176,6 +206,7 @@
                  item.prompt &&
                  item.scriptResult &&
                  (item.scriptType !== 2 || item.scriptResult.length > 0),
                'required-error': item.requiredError,
              }"
            >
              <!-- 题目题干 -->
@@ -183,7 +214,11 @@
                <span class="question-number"
                  >{{ getVisibleQuestionIndex(index) }}.</span
                >
                <span class="question-text">{{ item.scriptContent }}</span>
                <span class="question-text"
                  ><span v-if="isRequired(item)" class="required-star"
                    >*</span
                  >{{ item.scriptContent }}</span
                >
                <span class="question-type-tag">
                  {{
                    item.scriptType == 1
@@ -281,6 +316,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>
@@ -290,8 +337,9 @@
              type="primary"
              @click="cache(true)"
              class="submit-button"
              :loading="submitting"
            >
              提交问卷
              {{ submitting ? "提交中..." : "提交问卷" }}
            </el-button>
          </div>
        </div>
@@ -302,6 +350,7 @@
        <div class="completion-content">
          <div class="completion-icon">✓</div>
          <h2 class="completion-title">感谢您的配合!</h2>
          <h2 class="completion-title">祝您健康快乐!</h2>
          <p class="completion-message">
            {{
              jsy
@@ -332,15 +381,19 @@
      taskid: 355,
      patid: 265823,
      kcb: "",
      subtaskid: null,
      excep: 0,
      isabnormal: 0,
      taskname: "",
      orgname: "",
      questionList: [],
      permanentlyHidden: [], // 存储被永久隐藏的题目索引
      param6: null,
      jsy: null,
      dialogVisible: false,
      Endornot: true,
      accomplish: false,
      submitting: false, // 提交中的加载状态
      dimensionTypes: [
        {
          value: 1,
@@ -471,6 +524,7 @@
      console.log(url, "url");
      // let url = 'http://218.108.11.22:8093/sf/003';
      // let urlid = this.extractLastSegmentFromUrl(url);
      console.log("棒棒2");
      geturlinfo(url).then((res) => {
        if (res.code == 200) {
@@ -478,8 +532,19 @@
            res.data.param1,
            res.data.param2,
            res.data.param3,
            res.data.param5
            res.data.param5,
            res.data.param6
          );
          if (
            res.data.orgname == "省立同德翠苑院区" ||
            res.data.orgname == "省立同德之江院区" ||
            res.data.orgname == "省立同德闲林院区"
          ) {
            this.orgname = "浙江省立同德医院";
          } else {
            this.orgname = res.data.orgname;
          }
          this.param6 = res.data.param6;
        }
      });
@@ -513,10 +578,11 @@
    // },
    // 获取数据
    getQuestionnaire(param1, param2, param3) {
    getQuestionnaire(param1, param2, param3, param5, param6) {
      this.taskid = decodeURIComponent(param1);
      this.patid = decodeURIComponent(param2);
      this.taskname = decodeURIComponent(param3);
      this.subtaskid = decodeURIComponent(param6);
      this.loading = true;
      // let taskid =
@@ -528,7 +594,11 @@
      // let taskids = this.encrypt(this.taskid);
      // let patids = this.encrypt(this.patid);
      // 先取缓存
      getCachequestionnaire({ param1: this.taskid, param2: this.patid })
      getCachequestionnaire({
        param1: this.taskid,
        param2: this.patid,
        param6: param6,
      })
        .then((res) => {
          if (res.code == 200) {
            this.questionList = res.data.result;
@@ -555,12 +625,18 @@
    },
    // 获取数据
    getExternalfollowup() {
      getExternalfollowup({ param1: this.taskid, param2: this.patid })
      getExternalfollowup({
        param1: this.taskid,
        param2: this.patid,
        param6: this.subtaskid,
      })
        .then((res) => {
          if (res.code == 200) {
            this.questionList = res.data.script;
            this.jsy = res.data.jsy;
            this.kcb = res.data.kcb;
            if (res.data.taskName) this.taskname = res.data.taskName;
            // 处理题目收集结果格式
            this.questionList.forEach((item) => {
              item.nextScriptno = Number(item.nextScriptno);
@@ -608,19 +684,35 @@
      //   }
      // });
      form.serviceSubtaskDetailList = arr;
      Submitaquestionnaire(form).then((res) => {
        if (res.code == 200) {
          if (this.jsy) {
            this.dialogVisible = true;
      Submitaquestionnaire(form)
        .then((res) => {
          this.submitting = false;
          if (res.code == 200) {
            if (this.jsy) {
              this.dialogVisible = true;
            }
            this.accomplish = true;
            this.$modal.msgSuccess("提交成功");
          } else {
            this.$modal.msgError(res.msg || "提交失败,请重试");
          }
          this.accomplish = true;
          this.$modal.msgSuccess("提交成功");
        }
      });
        })
        .catch(() => {
          this.submitting = false;
          this.$modal.msgError("提交失败,请重试");
        });
    },
    // 缓存
    cache(subm) {
      console.log("进入缓存");
      // 提交场景:先做必填项校验
      if (subm === true && !this.validateRequired()) {
        return;
      }
      // 提交场景:开启加载状态,防止重复点击
      if (subm === true) {
        this.submitting = true;
      }
      let form = {
        param1: this.taskid,
        param2: this.patid,
@@ -666,13 +758,66 @@
      });
      form.svyTaskTemplateScriptVOS = arr;
      form.type = 2;
      Cachequestionnaire(form).then((res) => {
        if (res.code == 200) {
          if (subm) {
            this.submitForm();
      Cachequestionnaire(form)
        .then((res) => {
          if (res.code == 200) {
            if (subm) {
              this.submitForm();
            }
          } else {
            // 缓存失败:恢复提交状态
            if (subm) {
              this.submitting = false;
              this.$modal.msgError("提交失败,请重试");
            }
          }
        }
        })
        .catch(() => {
          if (subm) {
            this.submitting = false;
            this.$modal.msgError("提交失败,请重试");
          }
        });
    },
    // 是否必填(1必填 2不必填)
    isRequired(item) {
      return item.ismandatory == 1 || item.ismandatory == "1";
    },
    // 判断题目是否未填写
    isQuestionEmpty(item) {
      const val = item.scriptResult;
      if (item.scriptType == 2) {
        return !val || (Array.isArray(val) && val.length === 0);
      }
      return val === undefined || val === null || val === "";
    },
    // 保存前必填项校验
    validateRequired() {
      const list = this.questionList || [];
      list.forEach((item) => {
        if (item.requiredError) this.$set(item, "requiredError", false);
      });
      const unfilled = [];
      list.forEach((item) => {
        if (item.ishide) return;
        if (!this.isRequired(item)) return;
        if (this.isQuestionEmpty(item)) unfilled.push(item);
      });
      if (unfilled.length > 0) {
        unfilled.forEach((item) => this.$set(item, "requiredError", true));
        this.$modal.msgWarning(
          `还有 ${unfilled.length} 道必填题未填写,请补充后再提交`
        );
        this.$nextTick(() => {
          const idx = this.visibleQuestions.indexOf(unfilled[0]);
          const els = this.$el.querySelectorAll(".question-item");
          if (els[idx]) {
            els[idx].scrollIntoView({ behavior: "smooth", block: "center" });
          }
        });
        return false;
      }
      return true;
    },
    // 获取可见题目的正确序号(解决跳题后序号不连续的问题)
    getVisibleQuestionIndex(index) {
@@ -699,9 +844,10 @@
    // 新增的切换选中/取消选中方法
    handleRadioToggle(questionItem, globalIndex, options, optionValue) {
      // 保存当前状态以便后续比较
      const previousState = JSON.parse(JSON.stringify(this.questionList));
      this.permanentlyHidden = this.permanentlyHidden.filter(
        (idx) => idx <= globalIndex
      );
      // 原有的处理逻辑
      if (questionItem.scriptResult == optionValue) {
        questionItem.scriptResult = "";
        questionItem.isabnormal = 0;
@@ -715,20 +861,15 @@
          questionItem
        );
      }
      // 处理完成后,确保重新计算可见题目的序号
      this.$forceUpdate();
    },
    // 在methods部分,修改handleOptionChange方法:
    handleOptionChange(selectedOption, questionIndex, options, a) {
      console.log(selectedOption, questionIndex, options, a, "888");
      if (document.activeElement) {
        document.activeElement.blur();
      }
      // 找到被选中的选项对象
      const selectedOptionObj = options.find(
        (item) => item.optioncontent == selectedOption
      );
@@ -738,88 +879,93 @@
        this.questionList[questionIndex].score = selectedOptionObj.score;
        this.questionList[questionIndex].prompt = selectedOptionObj.prompt;
      }
      // 处理异常状态高亮
      console.log(
        this.questionList[questionIndex].nextScriptno,
        "this.questionList[questionIndex].nextScriptno"
      );
      console.log(questionIndex, "questionIndex");
      this.questionList[questionIndex].isabnormal =
        selectedOptionObj.isabnormal;
      // 处理附加输入框显示
      this.questionList[questionIndex].showAppendInput =
        selectedOptionObj.appendflag == 1;
      console.log(this.questionList);
      // if (!this.questionList[questionIndex].showAppendInput) {
      //   this.questionList[questionIndex].answerps = ""; // 清除附加信息
      // }
      // 保存当前题目之前已经隐藏的题目状态
      // 记录当前题之前已隐藏的题目索引(不改变)
      const previouslyHiddenBeforeCurrent = this.questionList
        .slice(0, questionIndex)
        .map((item, index) => (item.ishide ? index : -1))
        .filter((index) => index !== -1);
        .filter((idx) => idx !== -1);
      // 保存之前因nextQuestion=0而隐藏的题目范围
      const previouslyHiddenByEnd = this.questionList
        .map((item, index) => (item.hiddenByEnd ? index : -1))
        .filter((index) => index !== -1);
      // 如果branchFlag为1,处理题目跳转
      if (a.branchFlag == 1) {
        if (selectedOptionObj.nextQuestion == 0) {
          // 结束问答 - 隐藏后面所有题目并标记
          // 【结束问答】隐藏所有后续题目,并记录永久隐藏
          this.questionList = this.questionList.map((item, index) => ({
            ...item,
            ishide: index > questionIndex,
            hiddenByEnd: index > questionIndex, // 标记这些题目是被结束问答隐藏的
            hiddenByEnd: index > questionIndex,
          }));
          // 将后续隐藏的题目加入永久隐藏
          this.questionList.forEach((item, idx) => {
            if (idx > questionIndex && !this.permanentlyHidden.includes(idx)) {
              this.permanentlyHidden.push(idx);
            }
          });
        } else {
          // 正常跳转逻辑
          // 【正常跳转】
          const nextQuestionIndex = selectedOptionObj.nextQuestion - 1;
          console.log(nextQuestionIndex, 4);
          console.log(selectedOptionObj);
          this.questionList = this.questionList.map((item, index) => {
            // 保留当前题目之前的隐藏状态
            if (index < questionIndex) {
              return {
                ...item,
                ishide: previouslyHiddenBeforeCurrent.includes(index),
                hiddenByEnd: false, // 清除结束标记
                hiddenByEnd: false,
              };
            }
            // 当前题目总是可见
            if (index == questionIndex) {
              return { ...item, ishide: 0, hiddenByEnd: false };
            }
            // 显示目标下一题
            // 目标题:若在永久隐藏列表中,强制隐藏
            if (index == nextQuestionIndex) {
              return { ...item, ishide: 0, hiddenByEnd: false };
              const shouldShow = !this.permanentlyHidden.includes(index);
              return {
                ...item,
                ishide: shouldShow ? 0 : 1,
                hiddenByEnd: false,
              };
            }
            // 如果是之前被结束问答隐藏的题目,现在应该恢复显示
            // 之前因结束问答隐藏的题:恢复显示但检查永久隐藏
            if (item.hiddenByEnd) {
              return { ...item, ishide: 0, hiddenByEnd: false };
              const shouldShow = !this.permanentlyHidden.includes(index);
              return {
                ...item,
                ishide: shouldShow ? 0 : 1,
                hiddenByEnd: false,
              };
            }
            // 隐藏当前题和目标题之间的题目
            // 隐藏当前题和目标题之间的题目,并记录永久隐藏
            if (index > questionIndex && index < nextQuestionIndex) {
              if (!this.permanentlyHidden.includes(index)) {
                this.permanentlyHidden.push(index);
              }
              return { ...item, ishide: 1, hiddenByEnd: false };
            }
            // 其他情况保持原状
            return item;
          });
        }
      } else {
        // 如果没有跳转,只需确保下一题可见
        this.questionList = this.questionList.map((item, index) => ({
          ...item,
          ishide: index == questionIndex + 1 ? 0 : item.ishide,
          hiddenByEnd: index == questionIndex + 1 ? false : item.hiddenByEnd,
        }));
        // 【无跳转】仅显示下一题,但需检查永久隐藏
        // 如果没有跳转,只需确保下一题可见,但需检查永久隐藏
        this.questionList = this.questionList.map((item, index) => {
          if (index == questionIndex + 1) {
            const shouldShow = !this.permanentlyHidden.includes(index);
            return { ...item, ishide: shouldShow ? 0 : 1, hiddenByEnd: false };
          }
          // 其他题目保持不变
          return item;
        });
      }
      // 在处理完题目显示/隐藏后,强制更新视图以确保序号正确
      this.$forceUpdate();
      this.$nextTick(() => {
        this.$forceUpdate();
      });
@@ -856,19 +1002,44 @@
}
.questionnaire-title {
  color: #175997;
  color: #0d3b6b; // 基础深蓝色
  font-size: 28px;
  font-weight: 700;
  margin-bottom: 15px;
  line-height: 1.3;
  text-shadow: 0 1px 3px rgba(255, 255, 255, 0.4);
  // 针对夜间模式的优化
  @media (prefers-color-scheme: dark) {
    color: #4a90d9; // 夜间模式使用更亮的蓝色
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
  }
}
.questionnaire-description {
  font-size: 18px;
  color: #5a6c84;
  line-height: 1.6;
  max-width: 700px;
  margin: 0 auto;
  font-size: 16px;
  color: #303a47;
  line-height: 1.8;
  max-width: 720px;
  margin: 0;
  padding: 0 16px;
  text-align: justify;
  /* 两端对齐,更美观 */
  text-indent: 2em;
  /* 首行缩进 */
}
.questionnaire-signature {
  font-size: 16px;
  color: #303a47;
  text-align: right;
  /* 右对齐 */
  max-width: 720px;
  margin: 8px 0 0 auto;
  /* 上边距8px,右边靠齐 */
  padding: 0 16px;
  letter-spacing: 1px;
  /* 字间距稍宽,更有落款感 */
}
.custom-divider {
@@ -924,72 +1095,139 @@
    border-left: 4px solid #e6a23c;
  }
  &.required-error {
    border-color: #f56c6c;
    border-left: 4px solid #f56c6c;
    background: #fef0f0;
  }
  &: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: start;
  /* 改为 start 避免居中导致的视觉问题 */
  margin-bottom: 20px;
  font-size: 18px;
}
.question-text {
  line-height: 1.5;
  color: #2c3e50;
  font-weight: 500;
  word-wrap: break-word;
  overflow-wrap: break-word;
  white-space: normal;
  /* 确保允许换行 */
  min-width: 0;
  /* 防止 grid 项溢出 */
}
.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;
.required-star {
  color: #f56c6c;
  margin-right: 2px;
  font-weight: 600;
}
// .question-text {
//   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 {
  margin: 15px 0;
  margin: 12px 0;
  padding: 0 4px;
}
.options-group {
  display: flex;
  flex-direction: column;
  gap: 12px;
::v-deep .el-radio-group.options-group,
::v-deep .el-checkbox-group.options-group {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  width: 100%;
}
// 同样处理多选的情况
::v-deep .el-checkbox-group {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  width: 100%;
  @media (max-width: 420px) {
    grid-template-columns: 1fr;
  }
}
// 修改选项的样式
.option-radio,
.option-checkbox {
  margin: 0;
  padding: 12px 15px;
  padding: 10px 12px;
  border-radius: 6px;
  border: 1px solid #e0e0e0;
  transition: all 0.2s;
  display: flex;
  align-items: center;
  width: 100%;
  box-sizing: border-box;
  &:hover {
    border-color: #c0c4cc;
    background-color: #f8fafc;
  }
  // 确保内部内容自适应
  :deep(.el-radio__label),
  :deep(.el-checkbox__label) {
    display: inline;
    font-size: 15px;
    line-height: 1.4;
    word-break: break-all; // 允许在任意字符处换行
    white-space: normal; // 允许自动换行
    padding-left: 6px;
  }
  // 确保 radio/checkbox 图标不压缩
  :deep(.el-radio__input),
  :deep(.el-checkbox__input) {
    flex-shrink: 0;
  }
}
:deep(.option-radio .el-radio__label),
:deep(.option-checkbox .el-checkbox__label) {
  display: flex;
  align-items: center;
  font-size: 16px;
}
// :deep(.option-radio .el-radio__label),
// :deep(.option-checkbox .el-checkbox__label) {
//   display: flex;
//   align-items: center;
//   font-size: 16px;
// }
.option-text {
  margin-right: 5px;
  word-break: break-all;
  white-space: normal;
  line-height: 1.4;
}
.abnormal-indicator {
@@ -1020,6 +1258,14 @@
.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 {
@@ -1112,11 +1358,11 @@
    font-size: 16px;
  }
  .question-stem {
    font-size: 16px;
    flex-direction: column;
    align-items: flex-start;
  }
  // .question-stem {
  //   font-size: 16px;
  //   flex-direction: column;
  //   align-items: flex-start;
  // }
  .question-type-tag {
    margin-left: 0;
@@ -1165,11 +1411,74 @@
    font-size: 16px;
  }
}
.loading-container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 70vh;
}
/* ===== 提交中:适老化全屏加载遮罩 ===== */
.submit-loading-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 3000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.submit-loading-box {
  background: #ffffff;
  border-radius: 20px;
  padding: 48px 56px;
  text-align: center;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
  max-width: 85vw;
  box-sizing: border-box;
}
.submit-loading-spinner {
  width: 72px;
  height: 72px;
  margin: 0 auto 24px;
  border: 8px solid #e8eef5;
  border-top-color: #175997;
  border-right-color: #2a77c9;
  border-radius: 50%;
  animation: submit-spin 1s linear infinite;
}
@keyframes submit-spin {
  to {
    transform: rotate(360deg);
  }
}
.submit-loading-title {
  font-size: 28px;
  font-weight: 700;
  color: #175997;
  margin-bottom: 14px;
}
.submit-loading-tip {
  font-size: 20px;
  color: #303a47;
  line-height: 1.6;
}
.submit-loading-warn {
  margin-top: 18px;
  font-size: 17px;
  color: #e6a23c;
  font-weight: 500;
}
.loading-content {
@@ -1196,6 +1505,7 @@
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }