WXL (wul)
16 小时以前 55f9876e876c7ddfcc2a7a2b870dfc224c9467a6
src/views/Satisfaction/configurationmyd/index.vue
@@ -5,13 +5,149 @@
      <div class="header-content">
        <h2 class="page-title">满意度题目异常处理配置</h2>
        <p class="page-description">
          为满意度题目配置责任科室和报备科室,优化异常反馈流程
          基于模板配置满意度题目的责任科室和报备科室
        </p>
      </div>
    </div>
    <!-- 搜索区域 -->
    <div class="search-card">
    <!-- 模板选择区域 -->
    <div class="template-section">
      <el-card shadow="never">
        <div class="template-header">
          <h3 class="template-title">模板选择</h3>
          <p class="template-tip">请先选择模板类型和具体模板</p>
        </div>
        <el-form
          :model="templateForm"
          :rules="templateRules"
          ref="templateForm"
          label-width="120px"
          size="medium"
        >
          <el-row :gutter="20">
            <el-col :span="8">
              <el-form-item label="模板类型" prop="templateType">
                <el-select
                  v-model="templateForm.templateType"
                  placeholder="请选择模板类型"
                  clearable
                  @change="handleTemplateTypeChange"
                  style="width: 100%"
                >
                  <el-option label="问卷模板" :value="1" />
                  <el-option label="语音模板" :value="2" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item
                label="选择模板"
                prop="templateId"
                :rules="
                  templateForm.templateType
                    ? [
                        {
                          required: true,
                          message: '请选择模板',
                          trigger: 'change',
                        },
                      ]
                    : []
                "
              >
                <el-select
                  v-model="templateForm.templateId"
                  placeholder="请选择模板"
                  clearable
                  filterable
                  :disabled="
                    !templateForm.templateType || templateOptionsLoading
                  "
                  @change="handleTemplateChange"
                  style="width: 100%"
                >
                  <el-option
                    v-for="template in filteredTemplateOptions"
                    :key="template.id"
                    :label="template.templateName"
                    :value="template.id"
                  />
                  <div
                    v-if="templateOptionsLoading"
                    slot="empty"
                    class="select-loading"
                  >
                    <i class="el-icon-loading"></i>
                    <span>加载中...</span>
                  </div>
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item>
                <el-button
                  type="primary"
                  icon="el-icon-search"
                  @click="handleLoadTemplate"
                  :loading="templateLoading"
                  :disabled="!templateForm.templateId"
                >
                  加载模板题目
                </el-button>
                <el-button icon="el-icon-refresh" @click="handleResetTemplate">
                  重置
                </el-button>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </el-card>
    </div>
    <!-- 模板信息 -->
    <div v-if="currentTemplateInfo" class="template-info-section">
      <el-card shadow="never">
        <div class="template-info">
          <div class="info-left">
            <h3 class="template-name">
              {{ currentTemplateInfo.templateName }}
            </h3>
            <div class="template-meta">
              <span class="meta-item">
                <i class="el-icon-s-order"></i>
                模板类型:{{
                  templateForm.templateType === 1 ? "问卷模板" : "语音模板"
                }}
              </span>
              <span class="meta-item">
                <i class="el-icon-s-management"></i>
                题目总数:{{ currentTemplateInfo.questionCount || 0 }}
              </span>
              <span class="meta-item">
                <i class="el-icon-star-on"></i>
                满意度题目:{{ satisfactionQuestionsCount }}
              </span>
            </div>
          </div>
          <div class="info-right">
            <el-tag
              :type="
                currentTemplateInfo.templateStatus === 1 ? 'success' : 'info'
              "
              size="medium"
            >
              {{ currentTemplateInfo.templateStatus === 1 ? "启用" : "停用" }}
            </el-tag>
          </div>
        </div>
      </el-card>
    </div>
    <!-- 搜索区域(题目筛选) -->
    <div v-if="questionList.length > 0" class="search-section">
      <el-card shadow="never" class="search-container">
        <el-form :model="queryParams" :inline="true" size="medium">
          <el-form-item label="问题主题">
@@ -30,30 +166,16 @@
              @keyup.enter.native="handleQuery"
            />
          </el-form-item>
          <el-form-item label="是否可用">
            <el-select
              v-model="queryParams.isavailable"
              placeholder="请选择"
              clearable
            >
              <el-option
                v-for="item in qyoptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button
              type="primary"
              icon="el-icon-search"
              @click="handleQuery"
            >
              搜索
              筛选题目
            </el-button>
            <el-button icon="el-icon-refresh" @click="resetQuery">
              重置
              重置筛选
            </el-button>
          </el-form-item>
        </el-form>
@@ -79,7 +201,9 @@
            <span v-if="changedCount > 0" class="change-count">
              有 {{ changedCount }} 项配置需要保存
            </span>
            <div class="total-count">共 {{ total }} 条记录</div>
            <div class="total-count">
              共 {{ filteredQuestionList.length }} 条记录
            </div>
          </div>
        </el-card>
      </div>
@@ -91,18 +215,22 @@
        </div>
      </div>
      <div v-else-if="questionList.length === 0" class="empty-wrapper">
        <el-empty description="暂无满意度题目数据">
          <el-button type="primary" @click="getQuestionList"
            >刷新数据</el-button
          >
      <div
        v-else-if="questionList.length === 0 && templateForm.templateId"
        class="empty-wrapper"
      >
        <el-empty description="该模板中暂无满意度题目">
          <p class="empty-tip">
            请选择其他模板或检查模板中是否包含满意度类型题目(分类ID:
            404,405,406)
          </p>
        </el-empty>
      </div>
      <!-- 一行一行的卡片列表 -->
      <div v-else class="question-list">
      <div v-else-if="filteredQuestionList.length > 0" class="question-list">
        <div
          v-for="(question, index) in questionList"
          v-for="(question, index) in filteredQuestionList"
          :key="question.id"
          class="question-item"
        >
@@ -124,12 +252,6 @@
                      {{ question.scriptTopic || "无主题" }}
                    </h3>
                    <div class="question-tags">
                      <el-tag
                        :type="question.isavailable == 1 ? 'danger' : 'success'"
                        size="small"
                      >
                        {{ question.isavailable == 1 ? "不可用" : "可用" }}
                      </el-tag>
                      <dict-tag
                        :options="askvaluetype"
                        :value="question.scriptType"
@@ -180,7 +302,7 @@
                class="config-form"
              >
                <div class="config-fields">
                  <!-- 责任科室 -->
                  <!-- 责任科室(多选) -->
                  <div class="config-field">
                    <el-form-item
                      label="责任科室"
@@ -192,21 +314,25 @@
                        placeholder="请选择责任科室"
                        filterable
                        clearable
                        multiple
                        collapse-tags
                        style="width: 100%"
                        @change="handleConfigChange(question)"
                      >
                        <el-option
                          v-for="dept in deptOptions"
                          :key="dept.id"
                          :label="dept.name"
                          :value="dept.id"
                          :label="dept.label"
                          :value="dept.deptCode"
                        />
                      </el-select>
                      <div class="config-tip">负责处理该题目反馈的科室</div>
                      <div class="config-tip">
                        负责处理该题目反馈的科室,可多选
                      </div>
                    </el-form-item>
                  </div>
                  <!-- 报备科室 -->
                  <!-- 报备科室(多选) -->
                  <div class="config-field">
                    <el-form-item
                      label="报备科室"
@@ -219,15 +345,14 @@
                        filterable
                        clearable
                        multiple
                        collapse-tags
                        style="width: 100%"
                        @change="handleConfigChange(question)"
                      >
                        <el-option
                          v-for="dept in deptOptions"
                          :key="dept.id"
                          :label="dept.name"
                          :value="dept.id"
                          :label="dept.label"
                          :value="dept.deptCode"
                        />
                      </el-select>
                      <div class="config-tip">
@@ -235,24 +360,31 @@
                      </div>
                    </el-form-item>
                  </div>
                </div>
                  <!-- 通知方式 -->
                  <div class="config-field">
                    <el-form-item
                      label="通知方式"
                      prop="notifyTypes"
                      class="config-item"
                    >
                      <el-checkbox-group
                        v-model="question.exceptionConfig.notifyTypes"
                        @change="handleConfigChange(question)"
                      >
                        <el-checkbox label="system">系统消息</el-checkbox>
                        <el-checkbox label="sms">短信</el-checkbox>
                        <el-checkbox label="email">邮件</el-checkbox>
                        <el-checkbox label="wechat">企业微信</el-checkbox>
                      </el-checkbox-group>
                    </el-form-item>
                <!-- 当前配置信息 -->
                <div v-if="question.hasChanges" class="current-config">
                  <div class="config-preview">
                    <div class="preview-item">
                      <span class="preview-label">责任科室:</span>
                      <span class="preview-value">
                        {{
                          getDeptNames(
                            question.exceptionConfig.responsibilityDept || []
                          ).join(", ")
                        }}
                      </span>
                    </div>
                    <div class="preview-item">
                      <span class="preview-label">报备科室:</span>
                      <span class="preview-value">
                        {{
                          getDeptNames(
                            question.exceptionConfig.reportDept || []
                          ).join(", ")
                        }}
                      </span>
                    </div>
                  </div>
                </div>
@@ -299,17 +431,6 @@
          </el-card>
        </div>
      </div>
      <!-- 分页 -->
      <div v-if="questionList.length > 0" class="pagination-wrapper">
        <pagination
          v-show="total > 0"
          :total="total"
          :page.sync="queryParams.pageNum"
          :limit.sync="queryParams.pageSize"
          @pagination="getQuestionList"
        />
      </div>
    </div>
    <!-- 题目预览对话框 -->
@@ -328,19 +449,14 @@
              :value="currentPreview.scriptType"
              size="small"
            />
            <el-tag
              :type="currentPreview.isavailable === 1 ? 'success' : 'danger'"
              size="small"
            >
              {{ currentPreview.isavailable === 1 ? "可用" : "不可用" }}
            </el-tag>
            <el-tag v-if="currentPreview.targetname" size="small" type="info">
              {{ currentPreview.targetname }}
            </el-tag>
          </div>
        </div>
        <!-- 模板题目展示 -->
        <div class="preview-content">
        <div class="preview-content" v-if="templateForm.templateType == 1">
          <p class="preview-question">{{ currentPreview.scriptContent }}</p>
          <div
@@ -351,13 +467,47 @@
          >
            <el-radio-group v-model="previewAnswer">
              <el-radio
                v-for="(option, idx) in currentPreview.svyLibScriptOptions ||
                []"
                v-for="(
                  option, idx
                ) in currentPreview.svyLibTemplateTargetoptions || []"
                :key="idx"
                :label="option.optioncontent"
                class="option-item"
              >
                {{ option.optioncontent }}
              </el-radio>
            </el-radio-group>
          </div>
          <div v-else class="preview-textarea">
            <el-input
              type="textarea"
              placeholder="请输入回答"
              v-model="previewAnswer"
              :rows="4"
            />
          </div>
        </div>
        <!-- 语音题目展示 -->
        <div class="preview-content" v-else>
          <p class="preview-question">{{ currentPreview.scriptContent }}</p>
          <div
            v-if="
              currentPreview.scriptType != 3 && currentPreview.scriptType != 4
            "
            class="preview-options"
          >
            <el-radio-group v-model="previewAnswer">
              <el-radio
                v-for="(
                  option, idx
                ) in currentPreview.ivrLibaScriptTargetoptionList || []"
                :key="idx"
                :label="option.targetvalue"
                class="option-item"
              >
                {{ option.targetvalue }}
              </el-radio>
            </el-radio-group>
          </div>
@@ -399,10 +549,16 @@
<script>
import {
  getissuelist,
  compileissue,
  getissueclassify,
  compileQtemplate,
  compileFollowup,
  getQtemplatelist,
  getFollowuplist,
  getvFollowup,
  getQtemplateobj,
  selectInfoByConditiony,
} from "@/api/AiCentre/index";
import { deptTreeSelect } from "@/api/system/user";
import store from "@/store";
import Pagination from "@/components/Pagination";
@@ -411,28 +567,40 @@
  components: { Pagination },
  data() {
    return {
      // 模板表单
      templateForm: {
        templateType: "",
        templateId: "",
      },
      templateRules: {
        templateType: [
          { required: true, message: "请选择模板类型", trigger: "change" },
        ],
      },
      // 模板选项
      questionnaireTemplates: [], // 问卷模板列表
      followupTemplates: [], // 语音模板列表
      templateOptionsLoading: false,
      // 当前模板信息
      currentTemplateInfo: null,
      templateLoading: false,
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        scriptTopic: "",
        scriptContent: "",
        targetname: "",
        isavailable: "",
        categoryids: "404,405,406", // 固定查询满意度类型
      },
      // 数据列表
      questionList: [],
      total: 0,
      loading: false,
      batchSaving: false,
      // 字典数据
      askvaluetype: store.getters.askvaluetype || [],
      qyoptions: store.getters.usable || [],
      mode: store.getters.mode || [],
      languagelist: store.getters.languagelist || [],
      // 科室选项
      deptOptions: [],
@@ -447,21 +615,21 @@
      hasChanges: false,
      changedCount: 0,
      // 满意度分类ID
      satisfactionCategoryIds: ["404", "405", "406"],
      // 表单验证规则
      configRules: {
        responsibilityDept: [
          { required: true, message: "请选择责任科室", trigger: "change" },
        ],
        reportDept: [
          {
            required: true,
            message: "请选择至少一个报备科室",
            message: "请至少选择一个责任科室",
            trigger: "change",
          },
          {
            validator: (rule, value, callback) => {
              if (!value || value.length === 0) {
                callback(new Error("请选择至少一个报备科室"));
                callback(new Error("请至少选择一个责任科室"));
              } else {
                callback();
              }
@@ -469,11 +637,16 @@
            trigger: "change",
          },
        ],
        notifyTypes: [
        reportDept: [
          {
            required: true,
            message: "请至少选择一个报备科室",
            trigger: "change",
          },
          {
            validator: (rule, value, callback) => {
              if (!value || value.length === 0) {
                callback(new Error("请至少选择一种通知方式"));
                callback(new Error("请至少选择一个报备科室"));
              } else {
                callback();
              }
@@ -484,17 +657,76 @@
      },
    };
  },
  computed: {
    // 根据模板类型过滤模板选项
    filteredTemplateOptions() {
      if (this.templateForm.templateType === 1) {
        return this.questionnaireTemplates;
      } else if (this.templateForm.templateType === 2) {
        return this.followupTemplates;
      }
      return [];
    },
    // 满意度题目数量
    satisfactionQuestionsCount() {
      return this.questionList.filter((q) =>
        this.satisfactionCategoryIds.includes(q.categoryid?.toString())
      ).length;
    },
    // 筛选后的题目列表
    filteredQuestionList() {
      let filtered = this.questionList;
      // 筛选满意度题目
      filtered = filtered.filter((q) =>
        this.satisfactionCategoryIds.includes(q.categoryid?.toString())
      );
      // 应用搜索条件
      if (this.queryParams.scriptTopic) {
        const keyword = this.queryParams.scriptTopic.toLowerCase();
        filtered = filtered.filter(
          (q) => q.scriptTopic && q.scriptTopic.toLowerCase().includes(keyword)
        );
      }
      if (this.queryParams.scriptContent) {
        const keyword = this.queryParams.scriptContent.toLowerCase();
        filtered = filtered.filter(
          (q) =>
            q.scriptContent && q.scriptContent.toLowerCase().includes(keyword)
        );
      }
      return filtered;
    },
  },
  created() {
    this.getDeptOptions();
    this.getQuestionList();
    this.loadAllTemplates();
  },
  methods: {
    /** 加载所有模板列表 */
    loadAllTemplates() {
      this.templateOptionsLoading = true;
      // 并行加载问卷模板和语音模板
      Promise.all([
        this.loadQuestionnaireTemplates(),
        this.loadFollowupTemplates(),
      ]).finally(() => {
        this.templateOptionsLoading = false;
      });
    },
    /** 查询科室列表 */
    getDeptOptions() {
      getissueclassify({})
      deptTreeSelect()
        .then((res) => {
          if (res.code === 200) {
            this.deptOptions = res.rows || [];
          if (res.code == 200) {
            this.deptOptions = this.flattenArray(res.data) || [];
          }
        })
        .catch((error) => {
@@ -503,58 +735,258 @@
        });
    },
    /** 查询满意度题目列表 */
    getQuestionList() {
      this.loading = true;
    flattenArray(multiArray) {
      let result = [];
      function flatten(element) {
        if (element.children && element.children.length > 0) {
          element.children.forEach((child) => flatten(child));
        } else {
          let item = JSON.parse(JSON.stringify(element));
          result.push(item);
        }
      }
      multiArray.forEach((element) => flatten(element));
      return result;
    },
    /** 根据科室编码获取科室名称 */
    getDeptName(deptCode) {
      if (!deptCode) return "";
      const dept = this.deptOptions.find((d) => d.deptCode === deptCode);
      return dept ? dept.label : deptCode;
    },
    /** 根据科室编码数组获取科室名称数组 */
    getDeptNames(deptCodes) {
      if (!Array.isArray(deptCodes) || deptCodes.length === 0) return [];
      return deptCodes
        .map((code) => this.getDeptName(code))
        .filter((name) => name && name.trim());
    },
    /** 模板类型变更 */
    handleTemplateTypeChange() {
      this.templateForm.templateId = "";
      this.currentTemplateInfo = null;
      this.questionList = [];
    },
      getissuelist(this.queryParams)
    /** 加载问卷模板列表 */
    loadQuestionnaireTemplates() {
      return new Promise((resolve) => {
        getQtemplatelist({ pageSize: 1000 })
          .then((res) => {
            if (res.code === 200) {
              this.questionnaireTemplates = (res.rows || []).map((item) => ({
                id: item.svyid,
                templateName: item.svyname,
                isavailable: item.isavailable,
              }));
            } else {
              this.$message.error(res.msg || "加载问卷模板失败");
            }
            resolve();
          })
          .catch((error) => {
            console.error("加载问卷模板失败:", error);
            this.$message.error("加载问卷模板失败");
            resolve();
          });
      });
    },
    /** 加载语音模板列表 */
    loadFollowupTemplates() {
      return new Promise((resolve) => {
        getFollowuplist({ pageSize: 1000 })
          .then((res) => {
            if (res.code === 200) {
              this.followupTemplates = (res.rows || []).map((item) => ({
                id: item.id,
                templateName: item.templateName,
                isavailable: item.isavailable,
              }));
            } else {
              this.$message.error(res.msg || "加载语音模板失败");
            }
            resolve();
          })
          .catch((error) => {
            console.error("加载语音模板失败:", error);
            this.$message.error("加载语音模板失败");
            resolve();
          });
      });
    },
    /** 模板选择变更 */
    handleTemplateChange(templateId) {
      if (templateId) {
        const selectedTemplate = this.filteredTemplateOptions.find(
          (t) => t.id === templateId
        );
        if (selectedTemplate) {
          this.currentTemplateInfo = {
            templateName: selectedTemplate.templateName,
            templateStatus: selectedTemplate.isavailable,
            questionCount: 0,
          };
        }
      } else {
        this.currentTemplateInfo = null;
        this.questionList = [];
      }
    },
    /** 加载模板详情和题目 */
    handleLoadTemplate() {
      this.$refs.templateForm.validate((valid) => {
        if (!valid) {
          this.$message.warning("请先选择模板");
          return;
        }
        this.templateLoading = true;
        this.loading = true;
        this.questionList = [];
        if (this.templateForm.templateType === 1) {
          this.loadQuestionnaireTemplateDetail();
        } else if (this.templateForm.templateType === 2) {
          this.loadFollowupTemplateDetail();
        }
      });
    },
    /** 加载问卷模板详情 */
    loadQuestionnaireTemplateDetail() {
      getQtemplateobj({ svyid: this.templateForm.templateId })
        .then((res) => {
          this.templateLoading = false;
          this.loading = false;
          if (res.code === 200) {
            this.questionList = (res.rows || []).map((item) => {
              // 解析异常处理配置
              let exceptionConfig = {
                responsibilityDept: "",
                reportDept: [],
                notifyTypes: ["system"],
              };
              try {
                if (item.otherdata) {
                  const otherData = JSON.parse(item.otherdata);
                  if (otherData.exceptionConfig) {
                    exceptionConfig = {
                      ...exceptionConfig,
                      ...otherData.exceptionConfig,
                    };
                  }
                }
              } catch (error) {
                console.warn("解析异常配置失败:", error);
              }
          if (res.code === 200 && res.rows && res.rows.length > 0) {
            const templateDetail = res.rows[0];
              return {
                ...item,
                originalConfig: JSON.parse(JSON.stringify(exceptionConfig)),
                exceptionConfig: exceptionConfig,
                hasChanges: false,
                saving: false,
                saveStatus: null,
              };
            });
            // 更新模板信息
            this.currentTemplateInfo = {
              ...templateDetail,
              templateName: templateDetail.svyname,
              templateStatus: templateDetail.isavailable,
              questionCount: templateDetail.svyTemplateLibScripts?.length || 0,
            };
            this.total = res.total || 0;
            this.updateChangedStatus();
            // 提取题目列表
            const questions = templateDetail.svyTemplateLibScripts || [];
            this.processQuestions(questions);
            this.$message.success(`成功加载 ${questions.length} 个题目`);
          } else {
            this.$message.error(res.msg || "获取数据失败");
            this.$message.error(res.msg || "加载模板详情失败");
          }
        })
        .catch((error) => {
          this.templateLoading = false;
          this.loading = false;
          console.error("查询失败:", error);
          this.$message.error("获取数据失败");
          console.error("加载问卷模板详情失败:", error);
          this.$message.error("加载模板详情失败");
        });
    },
    /** 加载语音模板详情 */
    loadFollowupTemplateDetail() {
      getvFollowup({ id: this.templateForm.templateId })
        .then((res) => {
          this.templateLoading = false;
          this.loading = false;
          if (res.code === 200) {
            const templateDetail = res.data;
            // 更新模板信息
            this.currentTemplateInfo = {
              ...this.currentTemplateInfo,
              templateName: templateDetail.templateName,
              templateStatus: templateDetail.isavailable,
              questionCount:
                templateDetail.ivrLibaTemplateScriptVOList?.length || 0,
            };
            // 提取题目列表
            const questions = templateDetail.ivrLibaTemplateScriptVOList || [];
            this.processQuestions(questions);
            this.$message.success(`成功加载 ${questions.length} 个题目`);
          } else {
            this.$message.error(res.msg || "加载模板详情失败");
          }
        })
        .catch((error) => {
          this.templateLoading = false;
          this.loading = false;
          console.error("加载语音模板详情失败:", error);
          this.$message.error("加载模板详情失败");
        });
    },
    /** 处理题目数据 */
    processQuestions(questions) {
      this.questionList = questions.map((question) => {
        // 解析责任科室和报备科室
        let exceptionConfig = {
          responsibilityDept: [], // 责任科室编码数组
          reportDept: [], // 报备科室编码数组
        };
        // 从题目顶层字段读取数据
        if (question.dutyDeptCode) {
          // 从逗号分隔的字符串转为数组
          exceptionConfig.responsibilityDept = question.dutyDeptCode
            .split(",")
            .map((code) => code.trim())
            .filter((code) => code);
        }
        if (question.reportDeptCode) {
          exceptionConfig.reportDept = question.reportDeptCode
            .split(",")
            .map((code) => code.trim())
            .filter((code) => code);
        }
        return {
          ...question,
          // 统一字段名
          id: question.id || question.scriptId,
          scriptTopic: question.scriptTopic || question.scriptTopic,
          scriptContent: question.scriptContent || question.scriptContent,
          scriptType: question.scriptType,
          isavailable: question.isavailable,
          targetname: question.targetname,
          categoryid: question.categoryid || question.categoryid,
          originalConfig: JSON.parse(JSON.stringify(exceptionConfig)),
          exceptionConfig: exceptionConfig,
          hasChanges: false,
          saving: false,
          saveStatus: null,
        };
      });
      this.updateChangedStatus();
    },
    /** 重置模板选择 */
    handleResetTemplate() {
      this.templateForm = {
        templateType: "",
        templateId: "",
      };
      this.currentTemplateInfo = null;
      this.questionList = [];
      this.resetQuery();
      this.$refs.templateForm?.clearValidate();
    },
    /** 配置变更处理 */
@@ -582,16 +1014,24 @@
    isConfigEqual(config1, config2) {
      if (!config1 || !config2) return false;
      const report1 = [...(config1.reportDept || [])].sort().join(",");
      const report2 = [...(config2.reportDept || [])].sort().join(",");
      const notify1 = [...(config1.notifyTypes || [])].sort().join(",");
      const notify2 = [...(config2.notifyTypes || [])].sort().join(",");
      const responsibility1 = [...(config1.responsibilityDept || [])]
        .sort()
        .join(",")
        .toLowerCase();
      const responsibility2 = [...(config2.responsibilityDept || [])]
        .sort()
        .join(",")
        .toLowerCase();
      const report1 = [...(config1.reportDept || [])]
        .sort()
        .join(",")
        .toLowerCase();
      const report2 = [...(config2.reportDept || [])]
        .sort()
        .join(",")
        .toLowerCase();
      return (
        config1.responsibilityDept === config2.responsibilityDept &&
        report1 === report2 &&
        notify1 === notify2
      );
      return responsibility1 === responsibility2 && report1 === report2;
    },
    /** 更新变更状态 */
@@ -621,64 +1061,132 @@
      question.saveStatus = null;
      try {
        // 构建保存数据
        const saveData = {
          id: question.id,
          isoperation: 2, // 修改操作
          ...question,
        // 获取当前模板详情
        let templateDetail;
        if (this.templateForm.templateType === 1) {
          // 问卷模板
          const res = await getQtemplateobj({
            svyid: this.templateForm.templateId,
          });
          if (res.code !== 200 || !res.rows || res.rows.length === 0) {
            throw new Error(res.msg || "获取模板详情失败");
          }
          templateDetail = res.rows[0];
        } else if (this.templateForm.templateType === 2) {
          // 语音模板
          const res = await getvFollowup({ id: this.templateForm.templateId });
          if (res.code !== 200) {
            throw new Error(res.msg || "获取模板详情失败");
          }
          templateDetail = res.data;
        }
        // 更新题目配置
        let updatedTemplateDetail = { ...templateDetail };
        let questionsField =
          this.templateForm.templateType === 1
            ? "svyTemplateLibScripts"
            : "ivrLibaTemplateScriptVOList";
        const questions = updatedTemplateDetail[questionsField] || [];
        const questionIndex = questions.findIndex((q) => q.id === question.id);
        if (questionIndex === -1) {
          throw new Error("未找到题目");
        }
        // 获取科室名称
        const responsibilityDeptNames = this.getDeptNames(
          question.exceptionConfig.responsibilityDept
        );
        const reportDeptNames = this.getDeptNames(
          question.exceptionConfig.reportDept
        );
        // 直接更新题目顶层字段
        questions[questionIndex] = {
          ...questions[questionIndex],
          // 设置Excel要求的字段
          dutyDeptCode: question.exceptionConfig.responsibilityDept.join(","),
          dutyDeptName: responsibilityDeptNames.join(","),
          reportDeptCode: question.exceptionConfig.reportDept.join(","),
          reportDeptName: reportDeptNames.join(","),
        };
        // 将异常配置保存到 otherdata
        const otherData = JSON.parse(question.otherdata || "{}");
        otherData.exceptionConfig = question.exceptionConfig;
        saveData.otherdata = JSON.stringify(otherData);
        // 更新模板
        updatedTemplateDetail[questionsField] = questions;
        // 移除不需要的字段
        delete saveData.originalConfig;
        delete saveData.hasChanges;
        delete saveData.saving;
        delete saveData.saveStatus;
        delete saveData.exceptionConfig;
        const response = await compileissue(saveData);
        // 保存模板
        let response;
        if (this.templateForm.templateType === 1) {
          response = await compileQtemplate({
            ...updatedTemplateDetail,
            id: this.templateForm.templateId,
            isoperation: 2,
          });
        } else {
          response = await compileFollowup({
            ...updatedTemplateDetail,
            id: this.templateForm.templateId,
            isoperation: 2,
          });
        }
        if (response.code === 200) {
          // 更新原始配置
          question.originalConfig = JSON.parse(
            JSON.stringify(question.exceptionConfig)
          );
          question.hasChanges = false;
          question.saveStatus = {
            type: "success",
            message: "配置保存成功",
          };
          this.updateChangedStatus();
          this.$message.success("配置保存成功");
          // 5秒后清除成功提示
          setTimeout(() => {
            question.saveStatus = null;
          }, 5000);
          this.handleSaveSuccess(question);
        } else {
          question.saveStatus = {
            type: "error",
            message: response.msg || "保存失败",
          };
          this.$message.error(response.msg || "保存失败");
          throw new Error(response.msg || "保存失败");
        }
      } catch (error) {
        console.error("保存失败:", error);
        question.saveStatus = {
          type: "error",
          message: "保存失败,请稍后重试",
          message: error.message || "保存失败,请稍后重试",
        };
        this.$message.error("保存失败,请稍后重试");
        this.$message.error(error.message || "保存失败,请稍后重试");
      } finally {
        question.saving = false;
      }
    },
    /** 处理保存成功 */
    /** 处理保存成功 */
    handleSaveSuccess(question) {
      // 同时更新题目顶层字段
      const responsibilityDeptNames = this.getDeptNames(
        question.exceptionConfig.responsibilityDept
      );
      const reportDeptNames = this.getDeptNames(
        question.exceptionConfig.reportDept
      );
      // 更新题目本身的字段
      question.dutyDeptCode =
        question.exceptionConfig.responsibilityDept.join(",");
      question.dutyDeptName = responsibilityDeptNames.join(",");
      question.reportDeptCode = question.exceptionConfig.reportDept.join(",");
      question.reportDeptName = reportDeptNames.join(",");
      // 更新原始配置
      question.originalConfig = JSON.parse(
        JSON.stringify(question.exceptionConfig)
      );
      question.hasChanges = false;
      question.saveStatus = {
        type: "success",
        message: "配置保存成功",
      };
      this.updateChangedStatus();
      this.$message.success("配置保存成功");
      // 5秒后清除成功提示
      setTimeout(() => {
        question.saveStatus = null;
      }, 5000);
    },
    /** 重置单个题目配置 */
    /** 重置单个题目配置 */
    resetSingleConfig(question) {
      this.$confirm("确定要重置当前题目的配置吗?", "提示", {
@@ -690,6 +1198,21 @@
          question.exceptionConfig = JSON.parse(
            JSON.stringify(question.originalConfig)
          );
          // 同时重置题目顶层字段
          const responsibilityDeptNames = this.getDeptNames(
            question.exceptionConfig.responsibilityDept
          );
          const reportDeptNames = this.getDeptNames(
            question.exceptionConfig.reportDept
          );
          question.dutyDeptCode =
            question.exceptionConfig.responsibilityDept.join(",");
          question.dutyDeptName = responsibilityDeptNames.join(",");
          question.reportDeptCode =
            question.exceptionConfig.reportDept.join(",");
          question.reportDeptName = reportDeptNames.join(",");
          question.hasChanges = false;
          question.saveStatus = null;
          this.updateChangedStatus();
@@ -760,22 +1283,15 @@
    /** 搜索 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getQuestionList();
      // 仅筛选显示,不需要重新加载
    },
    /** 重置搜索 */
    resetQuery() {
      this.queryParams = {
        pageNum: 1,
        pageSize: 10,
        scriptTopic: "",
        scriptContent: "",
        targetname: "",
        isavailable: "",
        categoryids: "404,405,406",
      };
      this.getQuestionList();
    },
  },
};
@@ -809,7 +1325,76 @@
    }
  }
  .search-card {
  .template-section {
    margin-bottom: 20px;
    .template-header {
      margin-bottom: 20px;
      .template-title {
        margin: 0 0 8px 0;
        font-size: 16px;
        font-weight: 600;
        color: #303133;
      }
      .template-tip {
        margin: 0;
        color: #909399;
        font-size: 13px;
      }
    }
    .select-loading {
      text-align: center;
      padding: 10px;
      color: #909399;
      i {
        margin-right: 8px;
      }
    }
  }
  .template-info-section {
    margin-bottom: 20px;
    .template-info {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 5px 0;
      .info-left {
        .template-name {
          margin: 0 0 10px 0;
          font-size: 18px;
          font-weight: 600;
          color: #303133;
        }
        .template-meta {
          display: flex;
          gap: 20px;
          flex-wrap: wrap;
          .meta-item {
            display: flex;
            align-items: center;
            gap: 5px;
            font-size: 13px;
            color: #606266;
            i {
              font-size: 14px;
            }
          }
        }
      }
    }
  }
  .search-section {
    margin-bottom: 20px;
    .search-container {
@@ -872,8 +1457,16 @@
    .empty-wrapper {
      min-height: 400px;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      .empty-tip {
        margin-top: 10px;
        color: #909399;
        font-size: 13px;
        text-align: center;
      }
    }
    .question-list {
@@ -1035,6 +1628,40 @@
              }
            }
            .current-config {
              margin-bottom: 20px;
              padding: 15px;
              background: #f0f9ff;
              border-radius: 6px;
              border: 1px solid #d0ebff;
              .config-preview {
                .preview-item {
                  display: flex;
                  align-items: flex-start;
                  margin-bottom: 8px;
                  &:last-child {
                    margin-bottom: 0;
                  }
                  .preview-label {
                    font-size: 13px;
                    color: #606266;
                    font-weight: 500;
                    min-width: 80px;
                  }
                  .preview-value {
                    font-size: 13px;
                    color: #303133;
                    line-height: 1.5;
                    flex: 1;
                  }
                }
              }
            }
            .config-footer {
              display: flex;
              justify-content: space-between;
@@ -1066,15 +1693,6 @@
          }
        }
      }
    }
    .pagination-wrapper {
      margin-top: 20px;
      padding: 20px;
      background: white;
      border-radius: 8px;
      display: flex;
      justify-content: center;
    }
  }
@@ -1161,6 +1779,12 @@
      margin-bottom: 16px;
    }
    .template-info {
      flex-direction: column;
      align-items: flex-start;
      gap: 10px;
    }
    .search-card {
      margin-bottom: 16px;
    }
@@ -1205,6 +1829,10 @@
                gap: 16px;
              }
              .current-config {
                padding: 12px;
              }
              .config-footer {
                flex-direction: column;
                align-items: stretch;
@@ -1220,27 +1848,6 @@
        }
      }
    }
  }
}
</style>
<style lang="scss">
.config-form {
  .el-checkbox-group {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
  }
  .el-checkbox {
    margin: 0;
  }
}
.option-item {
  .el-radio__label {
    display: block;
    padding-left: 8px;
  }
}
</style>