WXL (wul)
10 小时以前 c65b90aaa3477a90ebc325024927d80227c0c841
src/views/Satisfaction/configurationmyd/index.vue
@@ -272,6 +272,45 @@
                </div>
              </div>
              <div class="header-right">
                <!-- 异常选项状态 -->
                <div
                  class="option-status"
                  v-if="
                    templateForm.templateType != 3 &&
                    templateForm.templateType != 4
                  "
                >
                  <el-tooltip
                    :content="
                      checkHasAbnormalOptions(question)
                        ? '已有异常选项'
                        : '暂无异常选项'
                    "
                    placement="top"
                  >
                    <el-tag
                      :type="
                        checkHasAbnormalOptions(question) ? 'success' : 'danger'
                      "
                      size="small"
                      class="status-tag"
                    >
                      <i
                        :class="
                          checkHasAbnormalOptions(question)
                            ? 'el-icon-success'
                            : 'el-icon-warning'
                        "
                      ></i>
                      {{
                        checkHasAbnormalOptions(question)
                          ? "异常选项已配置"
                          : "无异常选项"
                      }}
                    </el-tag>
                  </el-tooltip>
                </div>
                <el-button
                  type="text"
                  icon="el-icon-view"
@@ -279,6 +318,20 @@
                  size="small"
                >
                  预览
                </el-button>
                <!-- 添加配置选项按钮 -->
                <el-button
                  v-if="
                    templateForm.templateType != 3 &&
                    templateForm.templateType != 4
                  "
                  type="text"
                  icon="el-icon-setting"
                  @click="openOptionDialog(question)"
                  size="small"
                >
                  配置选项
                </el-button>
              </div>
            </div>
@@ -428,7 +481,114 @@
        </div>
      </div>
    </div>
    <!-- 选项配置对话框 -->
    <el-dialog
      title="选项异常状态配置"
      :visible.sync="optionDialogVisible"
      width="700px"
      center
      :close-on-click-modal="false"
    >
      <div v-if="editingQuestion" class="option-config-wrapper">
        <div class="dialog-header">
          <h4>{{ editingQuestion.scriptTopic || "无主题" }}</h4>
          <p class="dialog-subtitle">{{ editingQuestion.scriptContent }}</p>
        </div>
        <div class="option-list">
          <el-alert
            v-if="!currentOptions.some((opt) => opt.isabnormal === 1)"
            title="请至少设置一个异常选项(标记为异常)"
            type="warning"
            :closable="false"
            show-icon
            style="margin-bottom: 20px"
          />
          <div
            v-for="(option, index) in currentOptions"
            :key="index"
            class="option-item"
          >
            <el-form
              :model="option"
              :rules="optionRules"
              ref="optionForm"
              size="small"
              class="option-form"
            >
              <el-row :gutter="12" align="middle">
                <el-col :span="2">
                  <div class="option-index">#{{ index + 1 }}</div>
                </el-col>
                <el-col :span="12">
                  <el-form-item prop="targetvalue">
                    <el-input
                      v-model="option.targetvalue"
                      placeholder="请输入选项内容"
                      clearable
                      maxlength="200"
                      show-word-limit
                    />
                  </el-form-item>
                </el-col>
                <el-col :span="6">
                  <el-form-item prop="isabnormal">
                    <el-select
                      v-model="option.isabnormal"
                      placeholder="选择状态"
                      style="width: 100%"
                    >
                      <el-option
                        v-for="status in abnormalOptions"
                        :key="status.value"
                        :label="status.label"
                        :value="status.value"
                      >
                        <el-tag :type="status.type" size="small">{{
                          status.label
                        }}</el-tag>
                      </el-option>
                    </el-select>
                  </el-form-item>
                </el-col>
                <el-col :span="4">
                  <el-button
                    type="danger"
                    icon="el-icon-delete"
                    @click="removeOption(index)"
                    size="small"
                    circle
                    plain
                  />
                </el-col>
              </el-row>
            </el-form>
          </div>
          <!-- <el-button
        type="primary"
        icon="el-icon-plus"
        @click="addNewOption"
        size="small"
        plain
        style="width: 100%; margin-top: 10px;"
      >
        添加选项
      </el-button> -->
        </div>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="optionDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="saveOptions" :loading="savingOptions">
          保存配置
        </el-button>
      </span>
    </el-dialog>
    <!-- 题目预览对话框 -->
    <el-dialog
      title="题目预览"
@@ -573,7 +733,25 @@
          { required: true, message: "请选择模板类型", trigger: "change" },
        ],
      },
      // 选项管理相关
      optionDialogVisible: false,
      currentOptions: [],
      editingQuestion: null,
      optionRules: {
        targetvalue: [
          { required: true, message: "请输入选项内容", trigger: "blur" },
        ],
        isabnormal: [
          { required: true, message: "请选择异常状态", trigger: "change" },
        ],
      },
      // 异常状态选项
      abnormalOptions: [
        { label: "正常", value: 0, type: "success" },
        { label: "异常", value: 1, type: "danger" },
        { label: "警告", value: 2, type: "warning" },
      ],
      // 模板选项
      questionnaireTemplates: [], // 问卷模板列表
      followupTemplates: [], // 语音模板列表
@@ -613,7 +791,8 @@
      // 满意度分类ID
      satisfactionCategoryIds: ["404", "405", "406", "10039", "10041", "10042"],
      questionnaireCategorys: [],
      voiceCategories: [],
      // 表单验证规则
      configRules: {
        responsibilityDept: [
@@ -668,13 +847,14 @@
    satisfactionQuestionsCount() {
      if (this.templateForm.templateType === 1) {
        return this.questionList.filter((q) =>
          this.satisfactionCategoryIds.includes(q.categoryid?.toString())
          this.questionnaireCategorys.includes(q.categoryid)
        ).length;
      } else if (this.templateForm.templateType === 2) {
        return this.questionList.filter((q) =>
          this.satisfactionCategoryIds.includes(q.scriptAssortid?.toString())
          this.voiceCategories.includes(q.scriptAssortid)
        ).length;
      }
      return 0;
    },
    // 筛选后的题目列表
@@ -684,11 +864,11 @@
      // 筛选满意度题目
      if (this.templateForm.templateType === 1) {
        filtered = filtered.filter((q) =>
          this.satisfactionCategoryIds.includes(q.categoryid?.toString())
          this.questionnaireCategorys.includes(q.categoryid)
        );
      } else if (this.templateForm.templateType === 2) {
        filtered = filtered.filter((q) =>
          this.satisfactionCategoryIds.includes(q.scriptAssortid?.toString())
          this.voiceCategories.includes(q.scriptAssortid)
        );
      }
@@ -696,7 +876,7 @@
      if (this.queryParams.scriptTopic) {
        const keyword = this.queryParams.scriptTopic.toLowerCase();
        filtered = filtered.filter(
          (q) => q.scriptTopic && q.scriptTopic.toLowerCase().includes(keyword)
          (q) => q.scriptTopic && q.criptTopic.toLowerCase().includes(keyword)
        );
      }
@@ -712,6 +892,16 @@
    },
  },
  created() {
    if (store.getters.satisfactionCategories) {
      this.questionnaireCategorys =
        store.getters.satisfactionCategories.questionnaireCategorys.map(
          (item) => item.categoryid
        );
      this.voiceCategories =
        store.getters.satisfactionCategories.voiceCategories.map(
          (item) => item.categoryid
        );
    }
    this.getDeptOptions();
    this.loadAllTemplates();
  },
@@ -1011,6 +1201,8 @@
    handleConfigChange(question) {
      this.$nextTick(() => {
        const index = this.filteredQuestionList.findIndex((q) => q.id === question.id);
        console.log(index,'index');
        if (index !== -1) {
          const formRef = this.$refs.configForm && this.$refs.configForm[index];
          if (formRef) {
@@ -1057,22 +1249,61 @@
      const changedItems = this.questionList.filter((q) => q.hasChanges);
      this.changedCount = changedItems.length;
      this.hasChanges = changedItems.length > 0;
      // 强制更新视图
      this.$forceUpdate();
    },
    /** 检查题目是否有异常选项 */
    checkHasAbnormalOptions(question) {
      if (this.templateForm.templateType === 1) {
        return (question.svyLibTemplateTargetoptions || []).some(
          (opt) => opt.isabnormal === 1
        );
      } else if (this.templateForm.templateType === 2) {
        return (question.ivrLibaScriptTargetoptionList || []).some(
          (opt) => opt.isabnormal === 1
        );
      }
      return false;
    },
    /** 保存单个题目配置 */
    async saveSingleConfig(question) {
      if (!question.hasChanges) return;
    async saveSingleConfig(question, skipAbnormalCheck = false) {
      // 检查是否有变更
      if (!question.hasChanges && !skipAbnormalCheck) {
        this.$message.info("当前配置无变化");
        return;
      }
      const index = this.filteredQuestionList.findIndex((q) => q.id === question.id);
      console.log(index,'filteredQuestionList');
      // 检查是否有异常选项
      if (!skipAbnormalCheck && !this.checkHasAbnormalOptions(question)) {
        this.$confirm(
          "该题目没有设置异常选项,必须先配置异常选项才能保存。是否立即配置?",
          "提示",
          {
            confirmButtonText: "去配置",
            cancelButtonText: "取消",
            type: "warning",
          }
        )
          .then(() => {
            this.openOptionDialog(question);
          })
          .catch(() => {});
        return;
      }
      const index = this.questionList.findIndex((q) => q.id === question.id);
      if (index === -1) return;
      const formRef = this.$refs.configForm && this.$refs.configForm[index];
      if (!formRef) return;
      const valid = await formRef.validate();
      if (!valid) {
      // 验证表单
      try {
        await formRef.validate();
      } catch (error) {
        this.$message.warning("请先完成必填项");
        return;
      }
@@ -1133,6 +1364,18 @@
          reportDeptName: reportDeptNames.join(","),
        };
        // 如果需要,也更新选项数据
        if (question.hasChanges && this.templateForm.templateType === 1) {
          questions[questionIndex].svyLibTemplateTargetoptions =
            question.svyLibTemplateTargetoptions || [];
        } else if (
          question.hasChanges &&
          this.templateForm.templateType === 2
        ) {
          questions[questionIndex].ivrLibaScriptTargetoptionList =
            question.ivrLibaScriptTargetoptionList || [];
        }
        // 更新模板
        updatedTemplateDetail[questionsField] = questions;
@@ -1170,7 +1413,6 @@
    },
    /** 处理保存成功 */
    /** 处理保存成功 */
    handleSaveSuccess(question) {
      // 同时更新题目顶层字段
      const responsibilityDeptNames = this.getDeptNames(
@@ -1206,7 +1448,6 @@
      }, 5000);
    },
    /** 重置单个题目配置 */
    /** 重置单个题目配置 */
    resetSingleConfig(question) {
      this.$confirm("确定要重置当前题目的配置吗?", "提示", {
@@ -1245,6 +1486,36 @@
    async handleBatchSave() {
      if (!this.hasChanges || this.batchSaving) return;
      // 获取有变更的题目
      const changedQuestions = this.questionList.filter((q) => q.hasChanges);
      if (changedQuestions.length === 0) {
        this.$message.info("没有需要保存的配置变更");
        return;
      }
      // 检查是否有题目缺少异常选项
      const questionsWithoutAbnormal = changedQuestions.filter(
        (q) => !this.checkHasAbnormalOptions(q)
      );
      if (questionsWithoutAbnormal.length > 0) {
        this.$confirm(
          `有 ${questionsWithoutAbnormal.length} 个题目没有设置异常选项,必须配置异常选项后才能保存。是否先去配置?`,
          "提示",
          {
            confirmButtonText: "去配置",
            cancelButtonText: "取消",
            type: "warning",
          }
        )
          .then(() => {
            // 打开第一个没有异常选项的题目的配置对话框
            this.openOptionDialog(questionsWithoutAbnormal[0]);
          })
          .catch(() => {});
        return;
      }
      this.$confirm("确定要保存所有修改过的配置吗?", "批量保存", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
@@ -1253,24 +1524,20 @@
        .then(async () => {
          this.batchSaving = true;
          const changedQuestions = this.questionList.filter(
            (q) => q.hasChanges
          );
          const results = [];
          for (const question of changedQuestions) {
            try {
              await this.saveSingleConfig(question);
              // 跳过异常检查,因为在上面已经检查过了
              await this.saveSingleConfig(question, true);
              results.push({
                id: question.id,
                success:
                  !question.hasChanges &&
                  question.saveStatus?.type === "success",
                success: !question.hasChanges,
              });
            } catch (error) {
              results.push({
                id: question.id,
                success: false,
                error: error.message,
              });
            }
          }
@@ -1287,6 +1554,11 @@
            this.$message.warning(
              `成功保存 ${successCount} 个,失败 ${failCount} 个`
            );
            // 可以显示具体哪些失败了
            const failedQuestions = results
              .filter((r) => !r.success)
              .map((r) => r.id);
            console.error("保存失败的题目ID:", failedQuestions);
          }
        })
        .catch(() => {
@@ -1299,6 +1571,199 @@
      this.currentPreview = { ...question };
      this.previewAnswer = "";
      this.previewVisible = true;
    },
    /** 修改选项管理对话框的打开方法,保存原始选项 */
    openOptionDialog(question) {
      this.editingQuestion = question;
      // 保存原始选项的快照
      if (this.templateForm.templateType === 1) {
        this.editingQuestion.originalOptions = JSON.parse(
          JSON.stringify(question.svyLibTemplateTargetoptions || [])
        );
      } else if (this.templateForm.templateType === 2) {
        this.editingQuestion.originalOptions = JSON.parse(
          JSON.stringify(question.ivrLibaScriptTargetoptionList || [])
        );
      }
      // 复制选项数据
      if (this.templateForm.templateType === 1) {
        this.currentOptions = JSON.parse(
          JSON.stringify(question.svyLibTemplateTargetoptions || [])
        ).map((opt, index) => ({
          ...opt,
          id: opt.id || `temp_${Date.now()}_${index}`,
          targetvalue: opt.optioncontent || "",
          isabnormal: opt.isabnormal || 0,
        }));
      } else if (this.templateForm.templateType === 2) {
        this.currentOptions = JSON.parse(
          JSON.stringify(question.ivrLibaScriptTargetoptionList || [])
        ).map((opt, index) => ({
          ...opt,
          id: opt.id || `temp_${Date.now()}_${index}`,
          targetvalue: opt.targetvalue || "",
          isabnormal: opt.isabnormal || 0,
        }));
      }
      this.optionDialogVisible = true;
    },
    /** 添加新选项 */
    addNewOption() {
      this.currentOptions.push({
        id: `temp_${Date.now()}_${this.currentOptions.length}`,
        targetvalue: "",
        isabnormal: 0,
        isNew: true,
      });
    },
    /** 删除选项 */
    removeOption(index) {
      this.$confirm("确定要删除这个选项吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.currentOptions.splice(index, 1);
        })
        .catch(() => {});
    },
    /** 保存选项配置 */
    async saveOptions() {
      try {
        // 验证必填项
        for (let i = 0; i < this.currentOptions.length; i++) {
          const option = this.currentOptions[i];
          if (!option.targetvalue || option.targetvalue.trim() === "") {
            this.$message.warning(`第 ${i + 1} 个选项内容不能为空`);
            return;
          }
        }
        // 检查是否有异常选项
        const hasAbnormal = this.currentOptions.some(
          (opt) => opt.isabnormal === 1
        );
        if (!hasAbnormal) {
          this.$message.warning("请至少设置一个异常选项(isabnormal=1)");
          return;
        }
        // 判断选项是否发生变化
        let isOptionsChanged = false;
        if (this.templateForm.templateType === 1) {
          const originalOptions =
            this.editingQuestion.svyLibTemplateTargetoptions || [];
          isOptionsChanged = this.checkOptionsChanged(
            originalOptions,
            this.currentOptions,
            "questionnaire"
          );
        } else if (this.templateForm.templateType === 2) {
          const originalOptions =
            this.editingQuestion.ivrLibaScriptTargetoptionList || [];
          isOptionsChanged = this.checkOptionsChanged(
            originalOptions,
            this.currentOptions,
            "voice"
          );
        }
        // 保存逻辑 - 更新题目对象的选项数据
        if (this.templateForm.templateType === 1) {
          this.editingQuestion.svyLibTemplateTargetoptions =
            this.currentOptions.map((opt) => ({
              ...opt,
              optioncontent: opt.targetvalue,
              isabnormal: opt.isabnormal,
              // 清除临时字段
              targetvalue: undefined,
              isNew: undefined,
            }));
        } else if (this.templateForm.templateType === 2) {
          this.editingQuestion.ivrLibaScriptTargetoptionList =
            this.currentOptions.map((opt) => ({
              ...opt,
              // 清除临时字段
              isNew: undefined,
            }));
        }
        // 如果选项有变化,则设置题目为有变更状态
        if (isOptionsChanged) {
          this.editingQuestion.hasChanges = true;
          this.updateChangedStatus();
        }
        this.$message.success("选项配置保存成功");
        this.optionDialogVisible = false;
      } catch (error) {
        console.error("保存选项失败:", error);
        this.$message.error("保存选项失败");
      }
    },
    /** 检查选项是否发生变化 */
    checkOptionsChanged(originalOptions, newOptions, templateType) {
      // 如果数量不同,则一定变化了
      if (originalOptions.length !== newOptions.length) {
        return true;
      }
      // 比较每个选项的内容和异常状态
      for (let i = 0; i < originalOptions.length; i++) {
        const original = originalOptions[i];
        const current = newOptions[i];
        if (templateType === "questionnaire") {
          // 问卷模板比较
          if (
            original.optioncontent !== current.targetvalue ||
            original.isabnormal !== current.isabnormal
          ) {
            return true;
          }
        } else if (templateType === "voice") {
          // 语音模板比较
          if (
            original.targetvalue !== current.targetvalue ||
            original.isabnormal !== current.isabnormal
          ) {
            return true;
          }
        }
      }
      return false;
    },
    /** 获取异常选项统计 */
    getAbnormalStats(question) {
      if (this.templateForm.templateType === 1) {
        const options = question.svyLibTemplateTargetoptions || [];
        return {
          total: options.length,
          abnormal: options.filter((opt) => opt.isabnormal === 1).length,
          warning: options.filter((opt) => opt.isabnormal === 2).length,
          normal: options.filter((opt) => opt.isabnormal === 0).length,
        };
      } else if (this.templateForm.templateType === 2) {
        const options = question.ivrLibaScriptTargetoptionList || [];
        return {
          total: options.length,
          abnormal: options.filter((opt) => opt.isabnormal === 1).length,
          warning: options.filter((opt) => opt.isabnormal === 2).length,
          normal: options.filter((opt) => opt.isabnormal === 0).length,
        };
      }
      return { total: 0, abnormal: 0, warning: 0, normal: 0 };
    },
    /** 搜索 */
@@ -1595,7 +2060,20 @@
          }
          .header-right {
            flex-shrink: 0;
            display: flex;
            flex-direction: column;
            align-items: flex-end;
            gap: 8px;
            .option-status {
              .status-tag {
                cursor: default;
                i {
                  margin-right: 4px;
                }
              }
            }
          }
        }
@@ -1789,7 +2267,52 @@
    }
  }
}
.option-config-wrapper {
  .dialog-header {
    margin-bottom: 20px;
    padding-bottom: 15px;
    border-bottom: 1px solid #ebeef5;
    h4 {
      margin: 0 0 8px 0;
      color: #303133;
      font-size: 16px;
      font-weight: 600;
    }
    .dialog-subtitle {
      margin: 0;
      color: #606266;
      font-size: 13px;
      line-height: 1.4;
    }
  }
  .option-list {
    .option-item {
      margin-bottom: 12px;
      padding: 12px;
      background: #f8f9fa;
      border-radius: 4px;
      border: 1px solid #ebeef5;
      &:hover {
        border-color: #dcdfe6;
      }
      .option-form {
        .option-index {
          display: flex;
          align-items: center;
          justify-content: center;
          height: 100%;
          color: #909399;
          font-weight: 500;
        }
      }
    }
  }
}
@media (max-width: 768px) {
  .satisfaction-exception-config {
    padding: 12px;