WXL (wul)
3 天以前 dac4393e8af2646f544f6e1ca24dab11b40c8492
src/views/Satisfaction/configurationmyd/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2376 @@
<template>
  <div class="satisfaction-exception-config">
    <!-- é¡µé¢æ ‡é¢˜ -->
    <div class="page-header">
      <div class="header-content">
        <h2 class="page-title">满意度题目异常处理配置</h2>
        <p class="page-description">
          åŸºäºŽæ¨¡æ¿é…ç½®æ»¡æ„åº¦é¢˜ç›®çš„责任科室和报备科室
        </p>
      </div>
    </div>
    <!-- æ¨¡æ¿é€‰æ‹©åŒºåŸŸ -->
    <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
                  @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="问题主题">
            <el-input
              v-model="queryParams.scriptTopic"
              placeholder="请输入问题主题"
              clearable
              @keyup.enter.native="handleQuery"
            />
          </el-form-item>
          <el-form-item label="问题内容">
            <el-input
              v-model="queryParams.scriptContent"
              placeholder="请输入问题内容"
              clearable
              @keyup.enter.native="handleQuery"
            />
          </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>
      </el-card>
    </div>
    <!-- é…ç½®åˆ—表 -->
    <div class="config-content">
      <!-- æ‰¹é‡æ“ä½œæ  -->
      <div v-if="questionList.length > 0" class="batch-actions-card">
        <el-card shadow="never">
          <div class="batch-actions">
            <el-button
              type="success"
              icon="el-icon-check"
              :loading="batchSaving"
              :disabled="!hasChanges || batchSaving"
              @click="handleBatchSave"
              size="medium"
            >
              {{ batchSaving ? "批量保存中..." : "批量保存配置" }}
            </el-button>
            <span v-if="changedCount > 0" class="change-count">
              æœ‰ {{ changedCount }} é¡¹é…ç½®éœ€è¦ä¿å­˜
            </span>
            <div class="total-count">
              å…± {{ filteredQuestionList.length }} æ¡è®°å½•
            </div>
          </div>
        </el-card>
      </div>
      <div v-if="loading" class="loading-wrapper">
        <div class="loading-spinner">
          <i class="el-icon-loading"></i>
          <span>加载中...</span>
        </div>
      </div>
      <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-if="filteredQuestionList.length > 0" class="question-list">
        <div
          v-for="(question, index) in filteredQuestionList"
          :key="question.id"
          class="question-item"
        >
          <el-card
            shadow="hover"
            class="question-card"
            :class="{ 'has-changes': question.hasChanges }"
          >
            <!-- å¡ç‰‡å¤´éƒ¨ -->
            <div class="card-header">
              <div class="header-left">
                <div class="question-index">
                  <span class="index-number">{{ index + 1 }}</span>
                  <div class="index-line"></div>
                </div>
                <div class="question-basic-info">
                  <div class="question-title-section">
                    <h3 class="question-topic" :title="question.scriptTopic">
                      {{ question.scriptTopic || "无主题" }}
                    </h3>
                    <div class="question-tags">
                      <dict-tag
                        :options="askvaluetype"
                        :value="question.scriptType"
                        size="small"
                      />
                      <el-tag
                        v-if="question.targetname"
                        size="small"
                        type="info"
                      >
                        {{ question.targetname }}
                      </el-tag>
                    </div>
                  </div>
                  <div class="question-content-section">
                    <span class="content-label">题目内容:</span>
                    <span class="content-text">{{
                      question.scriptContent
                    }}</span>
                  </div>
                </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"
                  @click="previewQuestion(question)"
                  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>
            <!-- å¼‚常处理配置 -->
            <div class="config-section">
              <div class="config-title">
                <i class="el-icon-setting"></i>
                <span>异常处理配置</span>
              </div>
              <el-form
                :model="question.exceptionConfig"
                :rules="configRules"
                ref="configForm"
                label-width="100px"
                size="small"
                class="config-form"
              >
                <div class="config-fields">
                  <!-- è´£ä»»ç§‘室(多选) -->
                  <div class="config-field">
                    <el-form-item
                      label="责任科室"
                      prop="responsibilityDept"
                      class="config-item"
                    >
                      <el-select
                        v-model="question.exceptionConfig.responsibilityDept"
                        placeholder="请选择责任科室"
                        filterable
                        clearable
                        multiple
                        style="width: 100%"
                        @change="handleConfigChange(question)"
                      >
                        <el-option
                          v-for="dept in deptOptions"
                          :key="dept.id"
                          :label="dept.label"
                          :value="dept.deptCode"
                        />
                      </el-select>
                      <div class="config-tip">
                        è´Ÿè´£å¤„理该题目反馈的科室,可多选
                      </div>
                    </el-form-item>
                  </div>
                  <!-- æŠ¥å¤‡ç§‘室(多选) -->
                  <div class="config-field">
                    <el-form-item
                      label="报备科室"
                      prop="reportDept"
                      class="config-item"
                    >
                      <el-select
                        v-model="question.exceptionConfig.reportDept"
                        placeholder="请选择报备科室"
                        filterable
                        clearable
                        multiple
                        style="width: 100%"
                        @change="handleConfigChange(question)"
                      >
                        <el-option
                          v-for="dept in deptOptions"
                          :key="dept.id"
                          :label="dept.label"
                          :value="dept.deptCode"
                        />
                      </el-select>
                      <div class="config-tip">
                        éœ€è¦æŽ¥æ”¶å¼‚常反馈的科室,可多选
                      </div>
                    </el-form-item>
                  </div>
                </div>
                <!-- å½“前配置信息 -->
                <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>
                <!-- é…ç½®çŠ¶æ€å’Œæ“ä½œæŒ‰é’® -->
                <div class="config-footer">
                  <div v-if="question.saveStatus" class="save-status">
                    <el-alert
                      :type="question.saveStatus.type"
                      :title="question.saveStatus.message"
                      :closable="false"
                      show-icon
                      :effect="
                        question.saveStatus.type === 'success'
                          ? 'dark'
                          : 'light'
                      "
                      size="small"
                    />
                  </div>
                  <div class="config-actions">
                    <el-button
                      type="primary"
                      :loading="question.saving"
                      :disabled="!question.hasChanges"
                      @click="saveSingleConfig(question)"
                      size="small"
                      icon="el-icon-check"
                    >
                      {{ question.saving ? "保存中..." : "保存配置" }}
                    </el-button>
                    <el-button
                      v-if="question.hasChanges"
                      type="text"
                      @click="resetSingleConfig(question)"
                      size="small"
                    >
                      é‡ç½®
                    </el-button>
                  </div>
                </div>
              </el-form>
            </div>
          </el-card>
        </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="题目预览"
      :visible.sync="previewVisible"
      width="600px"
      center
    >
      <div v-if="currentPreview" class="preview-wrapper">
        <div class="preview-header">
          <h4>{{ currentPreview.scriptTopic || "无主题" }}</h4>
          <div class="preview-tags">
            <dict-tag
              :options="askvaluetype"
              :value="currentPreview.scriptType"
              size="small"
            />
            <el-tag v-if="currentPreview.targetname" size="small" type="info">
              {{ currentPreview.targetname }}
            </el-tag>
          </div>
        </div>
        <!-- æ¨¡æ¿é¢˜ç›®å±•示 -->
        <div class="preview-content" v-if="templateForm.templateType == 1">
          <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.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>
          <div v-else class="preview-textarea">
            <el-input
              type="textarea"
              placeholder="请输入回答"
              v-model="previewAnswer"
              :rows="4"
            />
          </div>
        </div>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="previewVisible = false">关闭</el-button>
      </span>
    </el-dialog>
    <!-- ä¿å­˜æˆåŠŸæç¤º -->
    <el-dialog
      title="保存成功"
      :visible.sync="saveSuccessVisible"
      width="400px"
      center
    >
      <div class="success-content">
        <i class="el-icon-success success-icon"></i>
        <p class="success-text">配置已成功保存!</p>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button type="primary" @click="saveSuccessVisible = false"
          >确定</el-button
        >
      </span>
    </el-dialog>
  </div>
</template>
<script>
import {
  compileissue,
  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";
export default {
  name: "SatisfactionExceptionConfig",
  components: { Pagination },
  data() {
    return {
      // æ¨¡æ¿è¡¨å•
      templateForm: {
        templateType: "",
        templateId: "",
      },
      templateRules: {
        templateType: [
          { 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: [], // è¯­éŸ³æ¨¡æ¿åˆ—表
      templateOptionsLoading: false,
      // å½“前模板信息
      currentTemplateInfo: null,
      templateLoading: false,
      // æŸ¥è¯¢å‚æ•°
      queryParams: {
        scriptTopic: "",
        scriptContent: "",
      },
      // æ•°æ®åˆ—表
      questionList: [],
      loading: false,
      batchSaving: false,
      // å­—典数据
      askvaluetype: store.getters.askvaluetype || [],
      qyoptions: store.getters.usable || [],
      // ç§‘室选项
      deptOptions: [],
      // é¢„览相关
      previewVisible: false,
      currentPreview: null,
      previewAnswer: "",
      // ä¿å­˜ç›¸å…³
      saveSuccessVisible: false,
      hasChanges: false,
      changedCount: 0,
      // æ»¡æ„åº¦åˆ†ç±»ID
      satisfactionCategoryIds: ["404", "405", "406", "10039", "10041", "10042"],
      questionnaireCategorys: [],
      voiceCategories: [],
      // è¡¨å•验证规则
      configRules: {
        responsibilityDept: [
          {
            required: true,
            message: "请至少选择一个责任科室",
            trigger: "change",
          },
          {
            validator: (rule, value, callback) => {
              if (!value || value.length === 0) {
                callback(new Error("请至少选择一个责任科室"));
              } else {
                callback();
              }
            },
            trigger: "change",
          },
        ],
        reportDept: [
          {
            required: true,
            message: "请至少选择一个报备科室",
            trigger: "change",
          },
          {
            validator: (rule, value, callback) => {
              if (!value || value.length === 0) {
                callback(new Error("请至少选择一个报备科室"));
              } else {
                callback();
              }
            },
            trigger: "change",
          },
        ],
      },
    };
  },
  computed: {
    // æ ¹æ®æ¨¡æ¿ç±»åž‹è¿‡æ»¤æ¨¡æ¿é€‰é¡¹
    filteredTemplateOptions() {
      if (this.templateForm.templateType === 1) {
        return this.questionnaireTemplates;
      } else if (this.templateForm.templateType === 2) {
        return this.followupTemplates;
      }
      return [];
    },
    // æ»¡æ„åº¦é¢˜ç›®æ•°é‡
    satisfactionQuestionsCount() {
      if (this.templateForm.templateType === 1) {
        return this.questionList.filter((q) =>
          this.questionnaireCategorys.includes(q.categoryid)
        ).length;
      } else if (this.templateForm.templateType === 2) {
        return this.questionList.filter((q) =>
          this.voiceCategories.includes(q.scriptAssortid)
        ).length;
      }
    },
    // æ£€æŸ¥é¢˜ç›®æ˜¯å¦æœ‰å¼‚常选项
    hasAbnormalOption(question) {
      return (question) => {
        if (!question) return false;
        // é—®å·æ¨¡æ¿
        if (this.templateForm.templateType === 1) {
          const options = question.svyLibTemplateTargetoptions || [];
          return options.some((opt) => opt.isabnormal === 1);
        }
        // è¯­éŸ³æ¨¡æ¿
        else if (this.templateForm.templateType === 2) {
          const options = question.ivrLibaScriptTargetoptionList || [];
          return options.some((opt) => opt.isabnormal === 1);
        }
        return false;
      };
    },
    // ç­›é€‰åŽçš„题目列表
    filteredQuestionList() {
      let filtered = this.questionList;
      console.log(this.questionnaireCategorys);
      // ç­›é€‰æ»¡æ„åº¦é¢˜ç›®
      if (this.templateForm.templateType === 1) {
        filtered = filtered.filter((q) =>
          this.questionnaireCategorys.includes(q.categoryid)
        );
      } else if (this.templateForm.templateType === 2) {
        filtered = filtered.filter((q) =>
          this.voiceCategories.includes(q.scriptAssortid)
        );
      }
      // åº”用搜索条件
      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() {
    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();
  },
  methods: {
    /** åŠ è½½æ‰€æœ‰æ¨¡æ¿åˆ—è¡¨ */
    loadAllTemplates() {
      this.templateOptionsLoading = true;
      // å¹¶è¡ŒåŠ è½½é—®å·æ¨¡æ¿å’Œè¯­éŸ³æ¨¡æ¿
      Promise.all([
        this.loadQuestionnaireTemplates(),
        this.loadFollowupTemplates(),
      ]).finally(() => {
        this.templateOptionsLoading = false;
      });
    },
    /** æŸ¥è¯¢ç§‘室列表 */
    getDeptOptions() {
      deptTreeSelect()
        .then((res) => {
          if (res.code == 200) {
            this.deptOptions = this.flattenArray(res.data) || [];
          }
        })
        .catch((error) => {
          console.error("获取科室列表失败:", error);
          this.$message.error("获取科室列表失败");
        });
    },
    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 = [];
    },
    /** åŠ è½½é—®å·æ¨¡æ¿åˆ—è¡¨ */
    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,
          };
        }
        // åŠ è½½æ¨¡æ¿è¯¦æƒ…æ•°æ®
        this.templateLoading = true;
        this.loading = true;
        this.questionList = [];
        if (this.templateForm.templateType === 1) {
          this.loadQuestionnaireTemplateDetail();
        } else if (this.templateForm.templateType === 2) {
          this.loadFollowupTemplateDetail();
        }
      } 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 && res.rows && res.rows.length > 0) {
            const templateDetail = res.rows[0];
            // æ›´æ–°æ¨¡æ¿ä¿¡æ¯
            this.currentTemplateInfo = {
              ...templateDetail,
              templateName: templateDetail.svyname,
              templateStatus: templateDetail.isavailable,
              questionCount: templateDetail.svyTemplateLibScripts?.length || 0,
            };
            // æå–题目列表
            const questions = templateDetail.svyTemplateLibScripts || [];
            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("加载模板详情失败");
        });
    },
    /** åŠ è½½è¯­éŸ³æ¨¡æ¿è¯¦æƒ… */
    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();
    },
    /** é…ç½®å˜æ›´å¤„理 */
    handleConfigChange(question) {
      this.$nextTick(() => {
        const index = this.filteredQuestionList.findIndex(
          (q) => q.id === question.id
        );
        if (index !== -1) {
          const formRef = this.$refs.configForm && this.$refs.configForm[index];
          if (formRef) {
            formRef.validate((valid) => {
              if (valid) {
                question.hasChanges = !this.isConfigEqual(
                  question.exceptionConfig,
                  question.originalConfig
                );
                this.updateChangedStatus();
              }
            });
          }
        }
      });
    },
    /** æ¯”较配置是否改变 */
    isConfigEqual(config1, config2) {
      if (!config1 || !config2) return false;
      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 responsibility1 === responsibility2 && report1 === report2;
    },
    /** æ›´æ–°å˜æ›´çŠ¶æ€ */
    updateChangedStatus() {
      const changedItems = this.questionList.filter((q) => q.hasChanges);
      this.changedCount = changedItems.length;
      this.hasChanges = changedItems.length > 0;
    },
    /** ä¿å­˜å•个题目配置 */
    async saveSingleConfig(question) {
      if (!question.hasChanges) return;
      const index = this.filteredQuestionList.findIndex(
        (q) => q.id === question.id
      );
      console.log(index, "filteredQuestionList");
      if (index === -1) return;
      const formRef = this.$refs.configForm && this.$refs.configForm[index];
      if (!formRef) return;
      const valid = await formRef.validate();
      if (!valid) {
        this.$message.warning("请先完成必填项");
        return;
      }
      question.saving = true;
      question.saveStatus = null;
      try {
        // èŽ·å–å½“å‰æ¨¡æ¿è¯¦æƒ…
        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(","),
        };
        // æ›´æ–°æ¨¡æ¿
        updatedTemplateDetail[questionsField] = questions;
        // ä¿å­˜æ¨¡æ¿
        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) {
          this.handleSaveSuccess(question);
        } else {
          throw new Error(response.msg || "保存失败");
        }
      } catch (error) {
        console.error("保存失败:", error);
        question.saveStatus = {
          type: "error",
          message: error.message || "保存失败,请稍后重试",
        };
        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("确定要重置当前题目的配置吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          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();
          this.$message.success("配置已重置");
        })
        .catch(() => {});
    },
    /** æ‰¹é‡ä¿å­˜é…ç½® */
    async handleBatchSave() {
      if (!this.hasChanges || this.batchSaving) return;
      this.$confirm("确定要保存所有修改过的配置吗?", "批量保存", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .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);
              results.push({
                id: question.id,
                success:
                  !question.hasChanges &&
                  question.saveStatus?.type === "success",
              });
            } catch (error) {
              results.push({
                id: question.id,
                success: false,
              });
            }
          }
          this.batchSaving = false;
          const successCount = results.filter((r) => r.success).length;
          const failCount = results.length - successCount;
          if (failCount === 0) {
            this.saveSuccessVisible = true;
            this.$message.success(`成功保存 ${successCount} ä¸ªé…ç½®`);
          } else {
            this.$message.warning(
              `成功保存 ${successCount} ä¸ªï¼Œå¤±è´¥ ${failCount} ä¸ª`
            );
          }
        })
        .catch(() => {
          this.batchSaving = false;
        });
    },
    /** é¢„览题目 */
    previewQuestion(question) {
      this.currentPreview = { ...question };
      this.previewAnswer = "";
      this.previewVisible = true;
    },
    /** æ£€æŸ¥é¢˜ç›®æ˜¯å¦æœ‰å¼‚常选项 */
    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;
    },
    /** æ‰“开选项管理对话框 */
    openOptionDialog(question) {
      this.editingQuestion = question;
      // å¤åˆ¶é€‰é¡¹æ•°æ®
      if (this.templateForm.templateType === 1) {
        this.currentOptions = JSON.parse(
          JSON.stringify(question.svyLibTemplateTargetoptions || [])
        ).map((opt) => ({
          ...opt,
          id: opt.id,
          targetvalue: opt.optioncontent || "",
          isabnormal: opt.isabnormal || 0,
        }));
      } else if (this.templateForm.templateType === 2) {
        this.currentOptions = JSON.parse(
          JSON.stringify(question.ivrLibaScriptTargetoptionList || [])
        ).map((opt) => ({
          ...opt,
          targetvalue: opt.targetvalue || "",
          isabnormal: opt.isabnormal || 0,
        }));
      }
      this.optionDialogVisible = true;
    },
    /** æ·»åŠ æ–°é€‰é¡¹ */
    addNewOption() {
      this.currentOptions.push({
        id: Date.now(), // ä¸´æ—¶ID
        targetvalue: "",
        isabnormal: 0,
        isNew: true,
      });
    },
    /** åˆ é™¤é€‰é¡¹ */
    removeOption(index) {
      this.currentOptions.splice(index, 1);
    },
    /** ä¿å­˜é€‰é¡¹é…ç½® */
    async saveOptions() {
      try {
        // éªŒè¯å¿…填项
        for (const option of this.currentOptions) {
          if (!option.targetvalue || option.targetvalue.trim() === "") {
            this.$message.warning("请填写所有选项内容");
            return;
          }
        }
        // æ£€æŸ¥æ˜¯å¦æœ‰å¼‚常选项
        const hasAbnormal = this.currentOptions.some(
          (opt) => opt.isabnormal === 1
        );
        if (!hasAbnormal) {
          this.$message.warning("请至少设置一个异常选项(isabnormal=1)");
          return;
        }
        // ä¿å­˜é€»è¾‘ - æ›´æ–°é¢˜ç›®å¯¹è±¡çš„选项数据
        if (this.templateForm.templateType === 1) {
          this.editingQuestion.svyLibTemplateTargetoptions =
            this.currentOptions.map((opt) => ({
              ...opt,
              optioncontent: opt.targetvalue,
              isabnormal: opt.isabnormal,
            }));
        } else if (this.templateForm.templateType === 2) {
          this.editingQuestion.ivrLibaScriptTargetoptionList =
            this.currentOptions;
        }
        // è§¦å‘配置变更检查
        this.handleConfigChange(this.editingQuestion);
        this.$message.success("选项配置保存成功");
        this.optionDialogVisible = false;
      } catch (error) {
        console.error("保存选项失败:", error);
        this.$message.error("保存选项失败");
      }
    },
    /** ä¿®æ”¹ä¿å­˜å•个题目配置方法,添加异常选项检查 */
    async saveSingleConfig(question) {
      // æ£€æŸ¥æ˜¯å¦æœ‰å¼‚常选项
      if (!this.checkHasAbnormalOptions(question)) {
        this.$confirm("该题目没有设置异常选项,是否先配置选项?", "提示", {
          confirmButtonText: "去配置",
          cancelButtonText: "取消",
          type: "warning",
        })
          .then(() => {
            this.openOptionDialog(question);
          })
          .catch(() => {});
        return;
      }
      // åŽŸæœ‰çš„ä¿å­˜é€»è¾‘...
      if (!question.hasChanges) return;
      const index = this.filteredQuestionList.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) {
        this.$message.warning("请先完成必填项");
        return;
      }
      // ç»§ç»­åŽŸæœ‰çš„ä¿å­˜é€»è¾‘...
      question.saving = true;
      question.saveStatus = null;
      try {
        // ... åŽŸæœ‰çš„ä¿å­˜é€»è¾‘ä¸å˜
      } catch (error) {
        // ... é”™è¯¯å¤„理不变
      } finally {
        question.saving = false;
      }
    },
    /** æ‰¹é‡ä¿å­˜æ—¶ä¹Ÿè¦æ£€æŸ¥ */
    async handleBatchSave() {
      if (!this.hasChanges || this.batchSaving) return;
      // æ£€æŸ¥æ‰€æœ‰æœ‰å˜æ›´çš„题目是否都有异常选项
      const changedQuestions = this.questionList.filter((q) => q.hasChanges);
      const questionsWithoutAbnormal = changedQuestions.filter(
        (q) => !this.checkHasAbnormalOptions(q)
      );
      if (questionsWithoutAbnormal.length > 0) {
        this.$confirm(
          `有 ${questionsWithoutAbnormal.length} ä¸ªé¢˜ç›®æ²¡æœ‰è®¾ç½®å¼‚常选项,请先配置选项。是否继续?`,
          "提示",
          {
            confirmButtonText: "ç»§ç»­",
            cancelButtonText: "去配置",
            type: "warning",
          }
        )
          .then(() => {
            // ç»§ç»­æ‰§è¡Œæ‰¹é‡ä¿å­˜
            this.executeBatchSave(changedQuestions);
          })
          .catch(() => {
            // å¯ä»¥åœ¨è¿™é‡Œè·³è½¬åˆ°ç¬¬ä¸€ä¸ªæ²¡æœ‰å¼‚常选项的题目
            if (questionsWithoutAbnormal.length > 0) {
              this.openOptionDialog(questionsWithoutAbnormal[0]);
            }
          });
      } else {
        this.executeBatchSave(changedQuestions);
      }
    },
    /** æ‰§è¡Œæ‰¹é‡ä¿å­˜ */
    async executeBatchSave(changedQuestions) {
      this.$confirm("确定要保存所有修改过的配置吗?", "批量保存", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(async () => {
          this.batchSaving = true;
          const results = [];
          for (const question of changedQuestions) {
            try {
              // è¿™é‡Œè°ƒç”¨ä¿®æ”¹åŽçš„saveSingleConfig方法
              await this.saveSingleConfig(question);
              results.push({
                id: question.id,
                success:
                  !question.hasChanges &&
                  question.saveStatus?.type === "success",
              });
            } catch (error) {
              results.push({
                id: question.id,
                success: false,
              });
            }
          }
          this.batchSaving = false;
          // ... åŽç»­å¤„理不变
        })
        .catch(() => {
          this.batchSaving = 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 };
    },
    /** æœç´¢ */
    handleQuery() {
      // ä»…筛选显示,不需要重新加载
    },
    /** é‡ç½®æœç´¢ */
    resetQuery() {
      this.queryParams = {
        scriptTopic: "",
        scriptContent: "",
      };
    },
  },
};
</script>
<style lang="scss" scoped>
.satisfaction-exception-config {
  min-height: 100%;
  background-color: #f5f7fa;
  padding: 20px;
  .page-header {
    margin-bottom: 20px;
    padding: 20px;
    background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
    border-radius: 8px;
    color: white;
    .header-content {
      .page-title {
        margin: 0 0 8px 0;
        font-size: 20px;
        font-weight: 600;
      }
      .page-description {
        margin: 0;
        opacity: 0.9;
        font-size: 14px;
      }
    }
  }
  .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 {
      border-radius: 8px;
      .el-form {
        display: flex;
        flex-wrap: wrap;
        gap: 16px;
        align-items: center;
      }
    }
  }
  .config-content {
    .batch-actions-card {
      margin-bottom: 20px;
      .batch-actions {
        display: flex;
        align-items: center;
        gap: 20px;
        padding: 8px 0;
        .change-count {
          color: #e6a23c;
          font-size: 14px;
          font-weight: 500;
        }
        .total-count {
          margin-left: auto;
          color: #909399;
          font-size: 14px;
        }
      }
    }
    .loading-wrapper {
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 400px;
      .loading-spinner {
        text-align: center;
        color: #409eff;
        i {
          font-size: 24px;
          margin-right: 8px;
        }
        span {
          font-size: 16px;
        }
      }
    }
    .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 {
      display: flex;
      flex-direction: column;
      gap: 16px;
    }
    .question-item {
      .question-card {
        border-radius: 8px;
        border: 1px solid #ebeef5;
        transition: all 0.3s ease;
        &.has-changes {
          border-color: #409eff;
          box-shadow: 0 2px 12px 0 rgba(64, 158, 255, 0.1);
        }
        &:hover {
          box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
        }
        .card-header {
          display: flex;
          justify-content: space-between;
          align-items: flex-start;
          margin-bottom: 20px;
          padding-bottom: 20px;
          border-bottom: 1px solid #f0f0f0;
          .header-left {
            display: flex;
            gap: 20px;
            flex: 1;
            .question-index {
              display: flex;
              flex-direction: column;
              align-items: center;
              min-width: 40px;
              .index-number {
                display: flex;
                align-items: center;
                justify-content: center;
                width: 32px;
                height: 32px;
                background: #409eff;
                color: white;
                border-radius: 50%;
                font-size: 14px;
                font-weight: 600;
                margin-bottom: 8px;
              }
              .index-line {
                width: 2px;
                height: 100%;
                background: #e0e0e0;
                border-radius: 1px;
              }
            }
            .question-basic-info {
              flex: 1;
              .question-title-section {
                margin-bottom: 12px;
                .question-topic {
                  margin: 0 0 8px 0;
                  font-size: 16px;
                  font-weight: 600;
                  color: #303133;
                  line-height: 1.4;
                }
                .question-tags {
                  display: flex;
                  gap: 8px;
                  flex-wrap: wrap;
                }
              }
              .question-content-section {
                display: flex;
                align-items: flex-start;
                gap: 8px;
                .content-label {
                  color: #606266;
                  font-size: 13px;
                  font-weight: 500;
                  min-width: 80px;
                }
                .content-text {
                  color: #303133;
                  font-size: 13px;
                  line-height: 1.6;
                  flex: 1;
                }
              }
            }
          }
          .header-right {
            display: flex;
            flex-direction: column;
            align-items: flex-end;
            gap: 8px;
            .option-status {
              .status-tag {
                cursor: default;
                i {
                  margin-right: 4px;
                }
              }
            }
          }
        }
        .config-section {
          .config-title {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 20px;
            padding: 8px 12px;
            background: #f8f9fa;
            border-radius: 4px;
            i {
              color: #409eff;
              font-size: 16px;
            }
            span {
              color: #303133;
              font-weight: 600;
              font-size: 14px;
            }
          }
          .config-form {
            .config-fields {
              display: grid;
              grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
              gap: 20px;
              margin-bottom: 20px;
              .config-field {
                .config-item {
                  margin-bottom: 0;
                  :deep(.el-form-item__label) {
                    font-weight: 500;
                    color: #606266;
                    padding-right: 12px;
                  }
                  .config-tip {
                    font-size: 12px;
                    color: #909399;
                    margin-top: 4px;
                    line-height: 1.4;
                  }
                }
              }
            }
            .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;
              align-items: center;
              padding-top: 20px;
              border-top: 1px dashed #dcdfe6;
              .save-status {
                flex: 1;
                margin-right: 20px;
                .el-alert {
                  padding: 8px 16px;
                  border-radius: 4px;
                }
              }
              .config-actions {
                display: flex;
                align-items: center;
                gap: 12px;
                flex-shrink: 0;
                .el-button {
                  min-width: 100px;
                }
              }
            }
          }
        }
      }
    }
  }
  .preview-wrapper {
    .preview-header {
      margin-bottom: 20px;
      h4 {
        margin: 0 0 12px 0;
        color: #303133;
        font-size: 18px;
        font-weight: 600;
      }
      .preview-tags {
        display: flex;
        gap: 8px;
        flex-wrap: wrap;
      }
    }
    .preview-content {
      .preview-question {
        margin-bottom: 20px;
        padding: 16px;
        background: #f8f9fa;
        border-radius: 4px;
        color: #606266;
        line-height: 1.6;
      }
      .preview-options {
        .option-item {
          display: block;
          margin-bottom: 12px;
          padding: 12px;
          border-radius: 4px;
          border: 1px solid #ebeef5;
          transition: all 0.3s;
          &:hover {
            background: #f5f7fa;
            border-color: #409eff;
          }
          &:last-child {
            margin-bottom: 0;
          }
        }
      }
      .preview-textarea {
        .el-textarea__inner {
          resize: none;
        }
      }
    }
  }
  .success-content {
    text-align: center;
    padding: 20px 0;
    .success-icon {
      color: #67c23a;
      font-size: 48px;
      margin-bottom: 20px;
    }
    .success-text {
      font-size: 16px;
      color: #606266;
      margin: 0;
    }
  }
}
.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;
    .page-header {
      padding: 16px;
      margin-bottom: 16px;
    }
    .template-info {
      flex-direction: column;
      align-items: flex-start;
      gap: 10px;
    }
    .search-card {
      margin-bottom: 16px;
    }
    .config-content {
      .batch-actions-card {
        margin-bottom: 16px;
      }
      .question-item {
        .question-card {
          .card-header {
            flex-direction: column;
            gap: 12px;
            .header-left {
              flex-direction: column;
              gap: 12px;
              .question-index {
                flex-direction: row;
                align-items: center;
                min-width: auto;
                .index-number {
                  margin-bottom: 0;
                  margin-right: 12px;
                }
                .index-line {
                  width: 100%;
                  height: 2px;
                }
              }
            }
          }
          .config-section {
            .config-form {
              .config-fields {
                grid-template-columns: 1fr;
                gap: 16px;
              }
              .current-config {
                padding: 12px;
              }
              .config-footer {
                flex-direction: column;
                align-items: stretch;
                gap: 12px;
                .save-status {
                  margin-right: 0;
                  margin-bottom: 8px;
                }
              }
            }
          }
        }
      }
    }
  }
}
</style>