From bc9538a9e071259ca8bbb6de52c60c9d7a55a03b Mon Sep 17 00:00:00 2001
From: WXL (wul) <wl_5969728@163.com>
Date: 星期三, 23 九月 2026 14:02:44 +0800
Subject: [PATCH] 测试完成

---
 src/views/followvisit/tasklist/index.vue                        |  127 ++
 src/api/smartor/patouthosp.js                                   |    5 
 src/views/Satisfaction/sfstatistics/components/DataOverview.vue |   30 
 src/api/AiCentre/SingleTask.js                                  |    8 
 src/views/Satisfaction/sfstatistics/components/ChartsPanel.vue  |   17 
 src/views/Satisfaction/configurationall/index.vue               | 2308 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/views/followvisit/record/detailpage/index.vue               |   80 +
 src/views/patient/patient/hospital.vue                          |   13 
 src/views/patient/patient/behospitalized.vue                    |   13 
 src/views/patient/patient/AwaitingAdmission.vue                 |   13 
 随访通用922.zip                                                     |    0 
 src/api/patient/record.js                                       |    5 
 src/components/CallCenterLs/index.vue                           |    5 
 src/views/followvisit/discharge/index.vue                       |   23 
 14 files changed, 2,622 insertions(+), 25 deletions(-)

diff --git a/src/api/AiCentre/SingleTask.js b/src/api/AiCentre/SingleTask.js
index 0f24aa0..89474da 100644
--- a/src/api/AiCentre/SingleTask.js
+++ b/src/api/AiCentre/SingleTask.js
@@ -86,6 +86,14 @@
     data: data,
   });
 }
+// 鏌ヨ浠诲姟鎵ц鎯呭喌
+export function countByCondition(data) {
+  return request({
+    url: "/smartor/serviceSubtask/countByCondition",
+    method: "post",
+    data: data,
+  });
+}
 // 浠诲姟瑙勫垯鍒犻櫎
 
 export function deltaskrule(taskid) {
diff --git a/src/api/patient/record.js b/src/api/patient/record.js
index 522b134..e318faa 100644
--- a/src/api/patient/record.js
+++ b/src/api/patient/record.js
@@ -3,10 +3,13 @@
 
 // 鏌ヨ浣忛櫌璁板綍鍒楄〃
 export function listpatient(data) {
+  const params = { ...data };
+  // bedSort 涓嶄紶鍗充笉鎸夊簥鍙锋帓搴�
+  if (params.bedSort == null || params.bedSort === "") delete params.bedSort;
   return request({
     url: '/smartor/patinhosp/selectPatMedInhospList',
     method: 'post',
-    data: data
+    data: params
   })
 };
 // 浣忛櫌璁板綍璇︽儏
diff --git a/src/api/smartor/patouthosp.js b/src/api/smartor/patouthosp.js
index 353c440..315c9cc 100644
--- a/src/api/smartor/patouthosp.js
+++ b/src/api/smartor/patouthosp.js
@@ -10,10 +10,13 @@
 }
 // 鏌ヨ寰呭叆闄㈣褰曞垪琛�
 export function listPatMedInhosp(query) {
+  const data = { ...query };
+  // bedSort 涓嶄紶鍗充笉鎸夊簥鍙锋帓搴�
+  if (data.bedSort == null || data.bedSort === "") delete data.bedSort;
   return request({
     url: '/smartor/patinhosp/selectPatMedInhospList',
     method: 'post',
-    data: query
+    data: data
   })
 }
 
diff --git a/src/components/CallCenterLs/index.vue b/src/components/CallCenterLs/index.vue
index 1ffc805..f5fb0fb 100644
--- a/src/components/CallCenterLs/index.vue
+++ b/src/components/CallCenterLs/index.vue
@@ -267,9 +267,14 @@
     },
 
     onCallStatusChange(status) {
+      const wasConnected = this.callStatus === "connected";
       this.callStatus = status.status;
       if (status.status == "connected") {
         this.startCallTimer();
+        // 浠呭湪浠庨潪鎺ラ�氱姸鎬佸垏鎹㈠埌鎺ラ�氭椂閫氱煡鐖剁粍浠讹紙閬垮厤閲嶅璁℃暟锛�
+        if (!wasConnected) {
+          this.$emit("call-connected");
+        }
       } else if (status.status == "idle") {
         this.stopCallTimer();
       }
diff --git a/src/views/Satisfaction/configurationall/index.vue b/src/views/Satisfaction/configurationall/index.vue
new file mode 100644
index 0000000..8208233
--- /dev/null
+++ b/src/views/Satisfaction/configurationall/index.vue
@@ -0,0 +1,2308 @@
+<template>
+  <div class="question-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>
+            </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-show="questionList.length > 0" class="search-section">
+      <el-card shadow="never" class="search-container">
+        <el-form :model="queryParams" size="medium">
+          <el-form-item label="闂涓婚">
+            <el-input
+              v-model="queryParams.scriptTopic"
+              placeholder="璇疯緭鍏ラ棶棰樹富棰�"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+          <el-form-item label="闂鍐呭">
+            <el-input
+              v-model="queryParams.scriptContent"
+              placeholder="璇疯緭鍏ラ棶棰樺唴瀹�"
+              @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">
+            璇烽�夋嫨鍏朵粬妯℃澘鎴栨鏌ユā鏉夸腑鏄惁鍖呭惈棰樼洰
+          </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">
+                  <!-- 璐d换绉戝锛堝閫夛級 -->
+                  <div class="config-field">
+                    <el-form-item
+                      label="璐d换绉戝"
+                      prop="responsibilityDept"
+                      class="config-item"
+                    >
+                      <el-select
+                        v-model="question.exceptionConfig.responsibilityDept"
+                        placeholder="璇烽�夋嫨璐d换绉戝"
+                        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="璐d换浜哄伐鍙�">
+                      <el-input
+                        v-model="question.exceptionConfig.dutyDeptPersonCode"
+                        placeholder="璇疯緭鍏ヨ矗浠讳汉宸ュ彿"
+                        @input="handleConfigChange(question)"
+                      />
+                    </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">璐d换绉戝锛�</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"> 淇濆瓨閰嶇疆 </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 {
+  compileQtemplate,
+  compileFollowup,
+  getQtemplatelist,
+  getFollowuplist,
+  getvFollowup,
+  getQtemplateobj,
+} from "@/api/AiCentre/index";
+import { deptTreeSelect } from "@/api/system/user";
+import store from "@/store";
+import Pagination from "@/components/Pagination";
+
+export default {
+  name: "QuestionExceptionConfig",
+  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: "姝e父", value: 0, type: "success" },
+        { label: "寮傚父", value: 1, type: "danger" },
+        { label: "璀﹀憡", value: 2, type: "warning" },
+      ],
+      // 妯℃澘閫夐」
+      questionnaireTemplates: [], // 闂嵎妯℃澘鍒楄〃
+      followupTemplates: [], // 璇煶妯℃澘鍒楄〃
+      templateOptionsLoading: false,
+
+      // 褰撳墠妯℃澘淇℃伅
+      currentTemplateInfo: null,
+      templateLoading: false,
+
+      // 鏌ヨ鍙傛暟
+      queryParams: {
+        scriptTopic: null,
+        scriptContent: null,
+      },
+
+      // 鏁版嵁鍒楄〃
+      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,
+
+      // 琛ㄥ崟楠岃瘉瑙勫垯
+      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 [];
+    },
+
+    // 绛涢�夊悗鐨勯鐩垪琛�
+    filteredQuestionList() {
+      let filtered = this.questionList;
+
+      // 搴旂敤鎼滅储鏉′欢
+      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.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) => {
+            console.log(res.rows, "res.rows");
+            if (res.code == 200) {
+              console.log(res.rows, 2);
+              this.questionnaireTemplates = (res.rows || []).map((item) => ({
+                id: item.svyid,
+                templateName: item.svyname,
+                isavailable: item.isavailable,
+              }));
+              console.log(this.followupTemplates, 3);
+            } 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) => {
+            console.log(res.rows, "res.rows");
+            if (res.code == 200) {
+              console.log(res.rows, 2);
+
+              this.followupTemplates = (res.rows || []).map((item) => ({
+                id: item.id,
+                templateName: item.templateName,
+                isavailable: item.isavailable,
+              }));
+              console.log(this.followupTemplates, 3);
+            } 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) => {
+        // 瑙f瀽璐d换绉戝鍜屾姤澶囩瀹�
+        let exceptionConfig = {
+          responsibilityDept: [], // 璐d换绉戝缂栫爜鏁扮粍
+          reportDept: [], // 鎶ュ绉戝缂栫爜鏁扮粍
+          dutyDeptPersonCode: question.dutyDeptPersonCode || "", // 璐d换浜哄伐鍙�
+        };
+
+        // 浠庨鐩《灞傚瓧娈佃鍙栨暟鎹�
+        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) {
+      // 鐩存帴瀵规瘮閰嶇疆鏄惁鍙樺寲锛屼笉渚濊禆琛ㄥ崟鏍¢獙锛堥伩鍏嶅彧鏀逛竴涓瓧娈垫椂鏍¢獙涓嶉�氳繃瀵艰嚧 hasChanges 鏃犳硶鏇存柊锛�
+      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();
+      const person1 = (config1.dutyDeptPersonCode || "").trim();
+      const person2 = (config2.dutyDeptPersonCode || "").trim();
+
+      return responsibility1 == responsibility2
+        && report1 == report2
+        && person1 == person2;
+    },
+
+    /** 鏇存柊鍙樻洿鐘舵�� */
+    updateChangedStatus() {
+      const changedItems = this.questionList.filter((q) => q.hasChanges);
+      this.changedCount = changedItems.length;
+      this.hasChanges = changedItems.length > 0;
+
+      // 寮哄埗鏇存柊瑙嗗浘
+      this.$forceUpdate();
+    },
+
+    /** 妫�鏌ラ鐩槸鍚︽湁寮傚父閫夐」 */
+    checkHasAbnormalOptions(question) {
+      if (this.templateForm.templateType == 1) {
+        return (question.svyLibTemplateTargetoptions || []).some(
+          (opt) => opt.isabnormal == 1
+        );
+      } else if (this.templateForm.templateType == 2) {
+        return (question.ivrLibaScriptTargetoptionList || []).some(
+          (opt) => opt.isabnormal == 1
+        );
+      }
+      return false;
+    },
+
+    /** 淇濆瓨鍗曚釜棰樼洰閰嶇疆 */
+    async saveSingleConfig(question) {
+      // 妫�鏌ユ槸鍚︽湁鍙樻洿
+      if (!question.hasChanges) {
+        this.$message.info("褰撳墠閰嶇疆鏃犲彉鍖�");
+        return;
+      }
+
+      const index = this.questionList.findIndex((q) => q.id == question.id);
+      if (index == -1) return;
+
+      const formRef = this.$refs.configForm && this.$refs.configForm[index];
+      if (!formRef) return;
+
+      // 楠岃瘉琛ㄥ崟
+      try {
+        await formRef.validate();
+      } catch (error) {
+        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(","),
+          dutyDeptPersonCode: question.exceptionConfig.dutyDeptPersonCode || "",
+        };
+
+        // 濡傛灉闇�瑕侊紝涔熸洿鏂伴�夐」鏁版嵁
+        if (question.hasChanges && this.templateForm.templateType == 1) {
+          questions[questionIndex].svyLibTemplateTargetoptions =
+            question.svyLibTemplateTargetoptions || [];
+        } else if (question.hasChanges && this.templateForm.templateType == 2) {
+          questions[questionIndex].ivrLibaScriptTargetoptionList =
+            question.ivrLibaScriptTargetoptionList || [];
+        }
+
+        // 鏇存柊妯℃澘
+        updatedTemplateDetail[questionsField] = questions;
+
+        // 淇濆瓨妯℃澘
+        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.dutyDeptPersonCode = question.exceptionConfig.dutyDeptPersonCode || "";
+
+      // 鏇存柊鍘熷閰嶇疆
+      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;
+
+      // 鑾峰彇鏈夊彉鏇寸殑棰樼洰
+      const changedQuestions = this.questionList.filter((q) => q.hasChanges);
+      if (changedQuestions.length == 0) {
+        this.$message.info("娌℃湁闇�瑕佷繚瀛樼殑閰嶇疆鍙樻洿");
+        return;
+      }
+
+      this.$confirm("纭畾瑕佷繚瀛樻墍鏈変慨鏀硅繃鐨勯厤缃悧锛�", "鎵归噺淇濆瓨", {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      })
+        .then(async () => {
+          this.batchSaving = true;
+
+          const results = [];
+          for (const question of changedQuestions) {
+            try {
+              await this.saveSingleConfig(question);
+              results.push({
+                id: question.id,
+                success: !question.hasChanges,
+              });
+            } catch (error) {
+              results.push({
+                id: question.id,
+                success: false,
+                error: error.message,
+              });
+            }
+          }
+
+          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} 涓猔
+            );
+            // 鍙互鏄剧ず鍏蜂綋鍝簺澶辫触浜�
+            const failedQuestions = results
+              .filter((r) => !r.success)
+              .map((r) => r.id);
+            console.error("淇濆瓨澶辫触鐨勯鐩甀D:", failedQuestions);
+          }
+        })
+        .catch(() => {
+          this.batchSaving = false;
+        });
+    },
+
+    /** 棰勮棰樼洰 */
+    previewQuestion(question) {
+      this.currentPreview = { ...question };
+      this.previewAnswer = "";
+      this.previewVisible = true;
+    },
+
+    /** 淇敼閫夐」绠$悊瀵硅瘽妗嗙殑鎵撳紑鏂规硶锛屼繚瀛樺師濮嬮�夐」 */
+    openOptionDialog(question) {
+      this.editingQuestion = question;
+
+      // 淇濆瓨鍘熷閫夐」鐨勫揩鐓�
+      if (this.templateForm.templateType == 1) {
+        this.editingQuestion.originalOptions = JSON.parse(
+          JSON.stringify(question.svyLibTemplateTargetoptions || [])
+        );
+      } else if (this.templateForm.templateType == 2) {
+        this.editingQuestion.originalOptions = JSON.parse(
+          JSON.stringify(question.ivrLibaScriptTargetoptionList || [])
+        );
+      }
+
+      // 澶嶅埗閫夐」鏁版嵁
+      if (this.templateForm.templateType == 1) {
+        this.currentOptions = JSON.parse(
+          JSON.stringify(question.svyLibTemplateTargetoptions || [])
+        ).map((opt, index) => ({
+          ...opt,
+          id: opt.id || `temp_${Date.now()}_${index}`,
+          targetvalue: opt.optioncontent || "",
+          isabnormal: opt.isabnormal || 0,
+        }));
+      } else if (this.templateForm.templateType == 2) {
+        this.currentOptions = JSON.parse(
+          JSON.stringify(question.ivrLibaScriptTargetoptionList || [])
+        ).map((opt, index) => ({
+          ...opt,
+          id: opt.id || `temp_${Date.now()}_${index}`,
+          targetvalue: opt.targetvalue || "",
+          isabnormal: opt.isabnormal || 0,
+        }));
+      }
+
+      this.optionDialogVisible = true;
+    },
+
+    /** 娣诲姞鏂伴�夐」 */
+    addNewOption() {
+      this.currentOptions.push({
+        id: `temp_${Date.now()}_${this.currentOptions.length}`,
+        targetvalue: "",
+        isabnormal: 0,
+        isNew: true,
+      });
+    },
+
+    /** 鍒犻櫎閫夐」 */
+    removeOption(index) {
+      this.$confirm("纭畾瑕佸垹闄よ繖涓�夐」鍚楋紵", "鎻愮ず", {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      })
+        .then(() => {
+          this.currentOptions.splice(index, 1);
+        })
+        .catch(() => {});
+    },
+
+    /** 淇濆瓨閫夐」閰嶇疆 */
+    async saveOptions() {
+      try {
+        // 楠岃瘉蹇呭~椤�
+        for (let i = 0; i < this.currentOptions.length; i++) {
+          const option = this.currentOptions[i];
+          if (!option.targetvalue || option.targetvalue.trim() == "") {
+            this.$message.warning(`绗� ${i + 1} 涓�夐」鍐呭涓嶈兘涓虹┖`);
+            return;
+          }
+        }
+
+        // 妫�鏌ユ槸鍚︽湁寮傚父閫夐」
+        const hasAbnormal = this.currentOptions.some(
+          (opt) => opt.isabnormal == 1
+        );
+
+        if (!hasAbnormal) {
+          this.$message.warning("璇疯嚦灏戣缃竴涓紓甯搁�夐」锛坕sabnormal=1锛�");
+          return;
+        }
+
+        // 鍒ゆ柇閫夐」鏄惁鍙戠敓鍙樺寲
+        let isOptionsChanged = false;
+
+        if (this.templateForm.templateType == 1) {
+          const originalOptions =
+            this.editingQuestion.svyLibTemplateTargetoptions || [];
+          isOptionsChanged = this.checkOptionsChanged(
+            originalOptions,
+            this.currentOptions,
+            "questionnaire"
+          );
+        } else if (this.templateForm.templateType == 2) {
+          const originalOptions =
+            this.editingQuestion.ivrLibaScriptTargetoptionList || [];
+          isOptionsChanged = this.checkOptionsChanged(
+            originalOptions,
+            this.currentOptions,
+            "voice"
+          );
+        }
+
+        // 淇濆瓨閫昏緫 - 鏇存柊棰樼洰瀵硅薄鐨勯�夐」鏁版嵁
+        if (this.templateForm.templateType == 1) {
+          this.editingQuestion.svyLibTemplateTargetoptions =
+            this.currentOptions.map((opt) => ({
+              ...opt,
+              optioncontent: opt.targetvalue,
+              isabnormal: opt.isabnormal,
+              // 娓呴櫎涓存椂瀛楁
+              targetvalue: undefined,
+              isNew: undefined,
+            }));
+        } else if (this.templateForm.templateType == 2) {
+          this.editingQuestion.ivrLibaScriptTargetoptionList =
+            this.currentOptions.map((opt) => ({
+              ...opt,
+              // 娓呴櫎涓存椂瀛楁
+              isNew: undefined,
+            }));
+        }
+
+        // 濡傛灉閫夐」鏈夊彉鍖栵紝鍒欒缃鐩负鏈夊彉鏇寸姸鎬�
+        if (isOptionsChanged) {
+          this.editingQuestion.hasChanges = true;
+          this.updateChangedStatus();
+        }
+
+        this.$message.success("閫夐」閰嶇疆淇濆瓨鎴愬姛");
+        this.optionDialogVisible = false;
+      } catch (error) {
+        console.error("淇濆瓨閫夐」澶辫触:", error);
+        this.$message.error("淇濆瓨閫夐」澶辫触");
+      }
+    },
+    /** 妫�鏌ラ�夐」鏄惁鍙戠敓鍙樺寲 */
+    checkOptionsChanged(originalOptions, newOptions, templateType) {
+      // 濡傛灉鏁伴噺涓嶅悓锛屽垯涓�瀹氬彉鍖栦簡
+      if (originalOptions.length !== newOptions.length) {
+        return true;
+      }
+
+      // 姣旇緝姣忎釜閫夐」鐨勫唴瀹瑰拰寮傚父鐘舵��
+      for (let i = 0; i < originalOptions.length; i++) {
+        const original = originalOptions[i];
+        const current = newOptions[i];
+
+        if (templateType == "questionnaire") {
+          // 闂嵎妯℃澘姣旇緝
+          if (
+            original.optioncontent !== current.targetvalue ||
+            original.isabnormal !== current.isabnormal
+          ) {
+            return true;
+          }
+        } else if (templateType == "voice") {
+          // 璇煶妯℃澘姣旇緝
+          if (
+            original.targetvalue !== current.targetvalue ||
+            original.isabnormal !== current.isabnormal
+          ) {
+            return true;
+          }
+        }
+      }
+
+      return false;
+    },
+    /** 鑾峰彇寮傚父閫夐」缁熻 */
+    getAbnormalStats(question) {
+      if (this.templateForm.templateType == 1) {
+        const options = question.svyLibTemplateTargetoptions || [];
+        return {
+          total: options.length,
+          abnormal: options.filter((opt) => opt.isabnormal == 1).length,
+          warning: options.filter((opt) => opt.isabnormal == 2).length,
+          normal: options.filter((opt) => opt.isabnormal == 0).length,
+        };
+      } else if (this.templateForm.templateType == 2) {
+        const options = question.ivrLibaScriptTargetoptionList || [];
+        return {
+          total: options.length,
+          abnormal: options.filter((opt) => opt.isabnormal == 1).length,
+          warning: options.filter((opt) => opt.isabnormal == 2).length,
+          normal: options.filter((opt) => opt.isabnormal == 0).length,
+        };
+      }
+      return { total: 0, abnormal: 0, warning: 0, normal: 0 };
+    },
+
+    /** 鎼滅储 */
+    handleQuery() {
+      // 浠呯瓫閫夋樉绀猴紝涓嶉渶瑕侀噸鏂板姞杞�
+    },
+
+    /** 閲嶇疆鎼滅储 */
+    resetQuery() {
+      this.queryParams = {
+        scriptTopic: "",
+        scriptContent: "",
+      };
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.question-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) {
+  .question-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>
diff --git a/src/views/Satisfaction/sfstatistics/components/ChartsPanel.vue b/src/views/Satisfaction/sfstatistics/components/ChartsPanel.vue
index cc1be67..93f0164 100644
--- a/src/views/Satisfaction/sfstatistics/components/ChartsPanel.vue
+++ b/src/views/Satisfaction/sfstatistics/components/ChartsPanel.vue
@@ -371,6 +371,11 @@
       if (!den) return '0.00';
       return (num / den).toFixed(2);
     },
+    // 缁煎悎寰楀垎灞曠ず锛氱洿鎺ヤ娇鐢ㄥ悗绔� avgScore锛屾牸寮忓寲涓轰袱浣嶅皬鏁�
+    formatScore(val) {
+      const n = Number(val);
+      return isNaN(n) ? '0.00' : n.toFixed(2);
+    },
     // 瑙f瀽姣斾緥瀛楃涓诧細"14.29%"銆�"0"銆�"0.5" 鈫� 鏁板�硷紙鐧惧垎姣旓級锛涙棤鏁堣繑鍥� null
     parsePercent(value) {
       if (value == null || value === '') return null;
@@ -382,7 +387,13 @@
       const arr = Array.isArray(list) ? list : (list && (list.list || list.rows)) || [];
       const options = [];
       let total = 0;
+      let avgScore = null;
       arr.forEach((item) => {
+        // 缁煎悎寰楀垎锛氬悗绔繑鍥炵殑棰樼洰绾� avgScore锛堜笌棰樼洰鍚岀骇锛�
+        if (avgScore == null) {
+          const rawAvg = Number(item && item.avgScore);
+          if (!isNaN(rawAvg)) avgScore = rawAvg;
+        }
         const details = Array.isArray(item && item.subtaskDetailRatioExportList)
           ? item.subtaskDetailRatioExportList
           : [];
@@ -395,7 +406,7 @@
           total += count;
         });
       });
-      return { options, total };
+      return { options, total, avgScore };
     },
     // 鍗曢閫夐」缁熻锛堢湡瀹炴帴鍙� /smartor/serviceSubtaskDetail/sltdMydTotalByScore锛�
     async fetchSingleQuestion() {
@@ -425,7 +436,7 @@
         const res = await sltdMydTotalByScore(payload);
         console.log('[sltdMydTotalByScore 鍗曢] code=', res && res.code, 'data=', JSON.stringify(res && res.data).slice(0, 1500));
         const list = res && res.code === 200 ? res.data : [];
-        const { options, total } = this.normalizeSingleQuestion(list);
+        const { options, total, avgScore } = this.normalizeSingleQuestion(list);
         this.singleQuestionData = options.map((o) => ({
           option: o.option,
           count: o.count,
@@ -435,7 +446,7 @@
         }));
         this.singleQuestionMeta = {
           total: total.toLocaleString(),
-          score: this.scoreOfOptions(options),
+          score: this.formatScore(avgScore),
         };
         if (!options.length) this.notify('褰撳墠鏉′欢涓嬫殏鏃犺棰樼殑閫夐」缁熻鏁版嵁', 'warning');
       } catch (e) {
diff --git a/src/views/Satisfaction/sfstatistics/components/DataOverview.vue b/src/views/Satisfaction/sfstatistics/components/DataOverview.vue
index 2cfa325..d221e0f 100644
--- a/src/views/Satisfaction/sfstatistics/components/DataOverview.vue
+++ b/src/views/Satisfaction/sfstatistics/components/DataOverview.vue
@@ -445,8 +445,13 @@
     const details = pickDetails(item);
     let node = map.get(title);
     if (!node) {
-      node = { title, type: "", options: new Map() };
+      node = { title, type: "", options: new Map(), avgScore: null };
       map.set(title, node);
+    }
+    // 缁煎悎寰楀垎锛氬悗绔繑鍥炵殑棰樼洰绾� avgScore锛堜笌棰樼洰鍚岀骇锛�
+    if (node.avgScore == null) {
+      const rawAvg = Number(item && item.avgScore);
+      node.avgScore = isNaN(rawAvg) ? null : rawAvg;
     }
 
     details.forEach((d) => {
@@ -480,7 +485,7 @@
 
   return Array.from(map.values()).map((n) => {
     const options = Array.from(n.options.values());
-    const item = { title: n.title, type: n.type, options };
+    const item = { title: n.title, type: n.type, options, avgScore: n.avgScore };
     // 澶氶�夐锛氱敤鍚勯�夐」鏁伴噺涔嬪拰浣滀负濉姤鎬婚噺锛堟帴鍙f湭鍗曠嫭鎻愪緵浜烘锛�
     if (n.type === "multiple")
       item.total = options.reduce((s, o) => s + o.count, 0);
@@ -595,7 +600,8 @@
           veryBadRate: this.rateOf(veryBad.count, total),
           naCount: na.count,
           naRate: this.rateOf(na.count, total),
-          score: this.scoreOfItems(items),
+          total,
+          score: this.formatScore(q.avgScore),
         };
       });
 
@@ -609,6 +615,17 @@
         });
       });
       const total = sum.reduce((s, it) => s + it.count, 0);
+      // 鎬昏缁煎悎寰楀垎锛氭寜鍚勯濉姤閲忓姞鏉冨钩鍧囧悇棰� avgScore
+      let totalScoreNum = 0;
+      let totalScoreDen = 0;
+      this.satisfactionQuestions.forEach((q, i) => {
+        const avg = Number(q.avgScore);
+        const qTotal = rows[i] ? rows[i].total : 0;
+        if (!isNaN(avg) && qTotal) {
+          totalScoreNum += avg * qTotal;
+          totalScoreDen += qTotal;
+        }
+      });
       rows.push({
         category: "鎬昏",
         veryGoodCount: sum[0].count,
@@ -623,7 +640,7 @@
         veryBadRate: this.rateOf(sum[4].count, total),
         naCount: sum[5].count,
         naRate: this.rateOf(sum[5].count, total),
-        score: this.scoreOfItems(sum),
+        score: totalScoreDen ? (totalScoreNum / totalScoreDen).toFixed(2) : "0.00",
       });
       return rows;
     },
@@ -772,6 +789,11 @@
       if (!denominator) return "0.00";
       return (numerator / denominator).toFixed(2);
     },
+    // 缁煎悎寰楀垎灞曠ず锛氱洿鎺ヤ娇鐢ㄥ悗绔� avgScore锛屾牸寮忓寲涓轰袱浣嶅皬鏁�
+    formatScore(val) {
+      const n = Number(val);
+      return isNaN(n) ? "0.00" : n.toFixed(2);
+    },
     defaultRange() {
       const end = new Date();
       const start = new Date();
diff --git a/src/views/followvisit/discharge/index.vue b/src/views/followvisit/discharge/index.vue
index 3531957..f99e472 100644
--- a/src/views/followvisit/discharge/index.vue
+++ b/src/views/followvisit/discharge/index.vue
@@ -586,6 +586,29 @@
           prop="phone"
         />
         <el-table-column
+          v-if="
+            orgname == '鏅畞鐣叉棌鑷不鍘夸汉姘戝尰闄�' ||
+            orgname == '涓芥按甯備腑鍖婚櫌'
+          "
+          label="鎷ㄥ彿鐘舵��"
+          width="120"
+          align="center"
+          key="isManuallyCalled"
+          prop="isManuallyCalled"
+        >
+          <template slot-scope="scope">
+            <el-tag
+              v-if="Number(scope.row.isManuallyCalled) > 0"
+              type="success"
+              :disable-transitions="false"
+              >鎷ㄩ�歿{ scope.row.isManuallyCalled }}娆�</el-tag
+            >
+            <el-tag v-else type="info" :disable-transitions="false"
+              >鏈嫧閫�</el-tag
+            >
+          </template>
+        </el-table-column>
+        <el-table-column
           label="韬唤璇佸彿鐮�"
           width="200"
           align="center"
diff --git a/src/views/followvisit/record/detailpage/index.vue b/src/views/followvisit/record/detailpage/index.vue
index cb7e4ef..cb22dd1 100644
--- a/src/views/followvisit/record/detailpage/index.vue
+++ b/src/views/followvisit/record/detailpage/index.vue
@@ -127,6 +127,29 @@
               </el-tooltip>
             </template>
           </el-table-column>
+                <el-table-column
+            v-if="
+              orgname == '鏅畞鐣叉棌鑷不鍘夸汉姘戝尰闄�' ||
+              orgname == '涓芥按甯備腑鍖婚櫌'
+            "
+            label="鎷ㄥ彿鐘舵��"
+            width="120"
+            align="center"
+            key="isManuallyCalled"
+            prop="isManuallyCalled"
+          >
+            <template slot-scope="scope">
+              <el-tag
+                v-if="Number(scope.row.isManuallyCalled) > 0"
+                type="success"
+                :disable-transitions="false"
+                >鎷ㄩ�歿{ scope.row.isManuallyCalled }}娆�</el-tag
+              >
+              <el-tag v-else type="info" :disable-transitions="false"
+                >鏈嫧閫�</el-tag
+              >
+            </template>
+          </el-table-column>
           <el-table-column
             prop="finishtime"
             align="center"
@@ -282,6 +305,8 @@
             show-overflow-tooltip
           >
           </el-table-column>
+
+
 
           <el-table-column
             prop="bankcardno"
@@ -1312,7 +1337,11 @@
         :visible.sync="CaldialogVisible"
         width="60%"
       >
-        <CallCenterLs ref="CallCenterLs" :initial-phone="currentPhoneNumber" />
+        <CallCenterLs
+          ref="CallCenterLs"
+          :initial-phone="currentPhoneNumber"
+          @call-connected="recordCallConnected"
+        />
       </el-dialog>
     </div>
   </div>
@@ -2349,7 +2378,7 @@
         this.CaldialogVisible = true;
         return;
       }
-      // 涓嬫柟鏄腑鍖婚櫌鍛煎彨
+      // 涓嬫柟鏄附姘村競涓尰闄�
       if (this.isSipRegistering) {
         this.$message.warning("绯荤粺姝e湪鍒濆鍖栵紝璇风◢鍊�...");
         return;
@@ -2379,11 +2408,15 @@
       this.callStatus = status.type;
 
       if (status.type === "connected") {
-        this.currentCall = {
-          phone: this.currentPhoneNumber,
-          type: this.callType,
-          startTime: new Date(),
-        };
+        if (!this.currentCall) {
+          this.currentCall = {
+            phone: this.currentPhoneNumber,
+            type: this.callType,
+            startTime: new Date(),
+          };
+          // 涓芥按甯備腑鍖婚櫌锛氱數璇濇帴閫氬悗璁板綍鎷ㄦ墦鎴愬姛娆℃暟
+          this.recordCallConnected();
+        }
       } else if (status.type === "ended" || status.type === "failed") {
         this.currentCall = null;
       }
@@ -2393,6 +2426,39 @@
         this.$message.error(`鍛煎彨澶辫触: ${status.text}`);
       }
     },
+    // 鐢佃瘽鎷ㄦ墦鎴愬姛鍚庯紙鎺ラ�氾級娆℃暟 +1锛屽苟閫氳繃 Editsingletaskson 鏇存柊鏈嶅姟鏁版嵁
+    // 浠呴檺鏅畞鐣叉棌鑷不鍘夸汉姘戝尰闄笌涓芥按甯備腑鍖婚櫌
+    recordCallConnected() {
+      const allowedOrgs = ["鏅畞鐣叉棌鑷不鍘夸汉姘戝尰闄�", "涓芥按甯備腑鍖婚櫌"];
+      if (!allowedOrgs.includes(this.orgname)) return;
+
+      getTaskservelist({
+        patid: this.patid,
+        subId: this.id,
+      }).then((res) => {
+        if (res.code == 200) {
+          const objson = res.rows[0].serviceSubtaskList.find(
+            (item) => item.id == this.id
+          );
+          if (!objson) return;
+          objson.isManuallyCalled = (Number(objson.isManuallyCalled) || 0) + 1;
+          Editsingletaskson(objson).then((r) => {
+            if (r.code == 200) {
+              // 鍙婃椂鏇存柊椤堕儴鏈嶅姟鍒楄〃涓庤〃鍗曚腑鐨勬嫧鍙风姸鎬�
+              const localRow = (this.logsheetlist || []).find(
+                (item) => item.id == this.id
+              );
+              if (localRow) {
+                localRow.isManuallyCalled = objson.isManuallyCalled;
+              }
+              if (this.form && this.form.id == this.id) {
+                this.form.isManuallyCalled = objson.isManuallyCalled;
+              }
+            }
+          });
+        }
+      });
+    },
     // 缁撴潫褰撳墠閫氳瘽
     endCurrentCall() {
       if (!this.currentCall) return;
diff --git a/src/views/followvisit/tasklist/index.vue b/src/views/followvisit/tasklist/index.vue
index cc6c050..069c195 100644
--- a/src/views/followvisit/tasklist/index.vue
+++ b/src/views/followvisit/tasklist/index.vue
@@ -130,19 +130,19 @@
           :show-overflow-tooltip="true"
         />
         <el-table-column
-          label="鎬讳换鍔�/宸查殢璁�"
+          label="鎵ц璇︽儏"
           align="center"
-          key="nickName"
+          key="taskDetail"
           width="120"
-          prop="nickName"
         >
           <template slot-scope="scope">
-            <span
-              >{{
-                scope.row.wfs || scope.row.wfs == 0
-                  ? scope.row.wfs + scope.row.yfs
-                  : ""
-              }}/{{ scope.row.yfs }}</span
+            <el-button
+              size="medium"
+              type="text"
+              @click="showCountDetail(scope.row)"
+              ><span class="button-xq"
+                ><i class="el-icon-s-data"></i>鎵ц璇︽儏</span
+              ></el-button
             >
           </template>
         </el-table-column>
@@ -385,6 +385,26 @@
         >
       </div>
     </el-dialog>
+    <!-- 浠诲姟鎵ц璇︽儏寮圭獥 -->
+    <el-dialog
+      title="浠诲姟鎵ц璇︽儏"
+      :visible.sync="countDialogVisible"
+      width="600px"
+      :before-close="handleCountClose"
+    >
+      <div v-loading="countLoading" class="count-detail">
+        <div class="count-task-name">浠诲姟鍚嶇О锛歿{ countTaskName || "-" }}</div>
+        <div class="count-grid">
+          <div v-for="f in countFields" :key="f.key" class="count-item">
+            <span class="count-label">{{ f.label }}</span>
+            <span class="count-value">{{ countVal(f) }}</span>
+          </div>
+        </div>
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="countDialogVisible = false">鍏� 闂�</el-button>
+      </span>
+    </el-dialog>
     <!-- 閫夋嫨鎮h�呭脊妗� -->
     <Patient-Selection
       ref="Patient"
@@ -407,6 +427,7 @@
 import {
   getTasklist,
   delTaskInfo,
+  countByCondition,
   TaskTemplateSendExecution,
   scanGenerateSubtask,
   goSfRelay,
@@ -450,6 +471,25 @@
       qrDialogVisible: false,
       qrCodeUrl: "", // 涓棿椤甸摼鎺�
       qrCodeCanvas: null, // 瀛樺偍canvas寮曠敤
+      // 浠诲姟鎵ц璇︽儏寮圭獥
+      countDialogVisible: false,
+      countLoading: false,
+      countTaskName: "",
+      countData: {},
+      countFields: [
+        { key: "total", label: "浠诲姟鎬婚噺" },
+        { key: "blq", label: "寰呴鍙�" },
+        { key: "wfs", label: "鏈彂閫�" },
+        { key: "yfs", label: "宸插彂閫�", altKey: "ysf" },
+        { key: "fssb", label: "鍙戦�佸け璐�" },
+        { key: "dsf", label: "寰呴殢璁�" },
+        { key: "xsf", label: "闇�闅忚" },
+        { key: "wxsf", label: "鏃犻渶闅忚" },
+        { key: "wzx", label: "鏈墽琛�" },
+        { key: "ywc", label: "宸插畬鎴�" },
+        { key: "yc", label: "寮傚父" },
+        { key: "jg", label: "璀﹀憡" },
+      ],
       // 鏃ユ湡鑼冨洿
       dateRange: [],
       // 宀椾綅閫夐」
@@ -1196,6 +1236,44 @@
       const container = this.$refs.qrCodeContainer;
       if (container) container.innerHTML = "";
     },
+    // 鏌ョ湅浠诲姟鎵ц璇︽儏锛坈ountByCondition 鐩存帴杩斿洖鑱氬悎 Map锛�
+    showCountDetail(row) {
+      this.countTaskName = row.taskName || "";
+      this.countData = {};
+      this.countDialogVisible = true;
+      this.countLoading = true;
+      countByCondition({ taskid: row.taskid })
+        .then((res) => {
+          this.countData = this.normalizeCount(res || {});
+        })
+        .catch(() => {
+          this.countData = {};
+          this.$modal.msgError("鑾峰彇浠诲姟鎵ц璇︽儏澶辫触");
+        })
+        .finally(() => {
+          this.countLoading = false;
+        });
+    },
+    // 褰掍竴閿悕锛堣仛鍚� SQL 鍒悕鍙兘甯︾┖鏍硷級
+    normalizeCount(data) {
+      const out = {};
+      Object.keys(data || {}).forEach((k) => {
+        out[String(k).trim()] = data[k];
+      });
+      return out;
+    },
+    // 璇诲彇缁熻鍊硷紙key 浼樺厛锛宎ltKey 鍏滃簳锛岀己澶辨樉绀� 0锛�
+    countVal(f) {
+      const d = this.countData || {};
+      if (d[f.key] != null && d[f.key] !== "") return d[f.key];
+      if (f.altKey && d[f.altKey] != null && d[f.altKey] !== "")
+        return d[f.altKey];
+      return 0;
+    },
+    handleCountClose() {
+      this.countDialogVisible = false;
+      this.countData = {};
+    },
   },
 };
 </script>
@@ -1358,4 +1436,35 @@
     font-size: 24px;
   }
 }
+
+.count-detail {
+  .count-task-name {
+    font-size: 14px;
+    color: #303133;
+    font-weight: 600;
+    margin-bottom: 16px;
+  }
+  .count-grid {
+    display: grid;
+    grid-template-columns: repeat(3, 1fr);
+    gap: 12px;
+  }
+  .count-item {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 12px 14px;
+    background: #f5f7fa;
+    border-radius: 4px;
+    .count-label {
+      font-size: 13px;
+      color: #606266;
+    }
+    .count-value {
+      font-size: 16px;
+      font-weight: 600;
+      color: #303133;
+    }
+  }
+}
 </style>
diff --git a/src/views/patient/patient/AwaitingAdmission.vue b/src/views/patient/patient/AwaitingAdmission.vue
index d2e414b..64fb662 100644
--- a/src/views/patient/patient/AwaitingAdmission.vue
+++ b/src/views/patient/patient/AwaitingAdmission.vue
@@ -54,6 +54,17 @@
               <el-option label="鏈畨鎺�" :value="2"> </el-option>
             </el-select>
           </el-form-item>
+          <el-form-item label="搴婂彿鎺掑簭" prop="bedSort">
+            <el-select
+              v-model="queryParams.bedSort"
+              placeholder="涓嶆寜搴婂彿鎺掑簭"
+              style="width: 160px"
+            >
+              <el-option label="涓嶆寜搴婂彿鎺掑簭" :value="null"></el-option>
+              <el-option label="鎸夊簥鍙锋搴�" :value="1"></el-option>
+              <el-option label="鎸夊簥鍙峰�掑簭" :value="2"></el-option>
+            </el-select>
+          </el-form-item>
           <el-row>
             <el-form-item>
               <el-button
@@ -704,6 +715,7 @@
         pageNum: 1,
         pageSize: 10,
         inhospstate: "3",
+        bedSort: null,
       },
       // 琛ㄥ崟鏍¢獙
       rules: {
@@ -893,6 +905,7 @@
       this.queryParams = {
         pageNum: 1,
         pageSize: 10,
+        bedSort: null,
 
         // leavehospitaldistrictcodes: [],
       };
diff --git a/src/views/patient/patient/behospitalized.vue b/src/views/patient/patient/behospitalized.vue
index e35a72e..b099067 100644
--- a/src/views/patient/patient/behospitalized.vue
+++ b/src/views/patient/patient/behospitalized.vue
@@ -66,6 +66,17 @@
               end-placeholder="缁撴潫鏃ユ湡"
             ></el-date-picker>
           </el-form-item>
+          <el-form-item label="搴婂彿鎺掑簭" prop="bedSort">
+            <el-select
+              v-model="queryParams.bedSort"
+              placeholder="涓嶆寜搴婂彿鎺掑簭"
+              style="width: 160px"
+            >
+              <el-option label="涓嶆寜搴婂彿鎺掑簭" :value="null"></el-option>
+              <el-option label="鎸夊簥鍙锋搴�" :value="1"></el-option>
+              <el-option label="鎸夊簥鍙峰�掑簭" :value="2"></el-option>
+            </el-select>
+          </el-form-item>
 
           <el-row>
             <el-form-item>
@@ -767,6 +778,7 @@
         pageNum: 1,
         pageSize: 10,
         cry: 0,
+        bedSort: null,
         searchscope: 3,
         scopetype: [],
         leaveldeptcodes: [],
@@ -1118,6 +1130,7 @@
         pageNum: 1,
         pageSize: 10,
         cry: 0,
+        bedSort: null,
         searchscope: 3,
         scopetype: [],
         leaveldeptcodes: [],
diff --git a/src/views/patient/patient/hospital.vue b/src/views/patient/patient/hospital.vue
index 9726bee..9058f8a 100644
--- a/src/views/patient/patient/hospital.vue
+++ b/src/views/patient/patient/hospital.vue
@@ -77,6 +77,17 @@
               end-placeholder="缁撴潫鏃ユ湡"
             ></el-date-picker>
           </el-form-item>
+          <el-form-item label="搴婂彿鎺掑簭" prop="bedSort">
+            <el-select
+              v-model="queryParams.bedSort"
+              placeholder="涓嶆寜搴婂彿鎺掑簭"
+              style="width: 160px"
+            >
+              <el-option label="涓嶆寜搴婂彿鎺掑簭" :value="null"></el-option>
+              <el-option label="鎸夊簥鍙锋搴�" :value="1"></el-option>
+              <el-option label="鎸夊簥鍙峰�掑簭" :value="2"></el-option>
+            </el-select>
+          </el-form-item>
 
           <el-row>
             <el-form-item>
@@ -794,6 +805,7 @@
         pageNum: 1,
         pageSize: 10,
         cry: 1,
+        bedSort: null,
         searchscope: 3,
         scopetype: [],
         leaveldeptcodes: [],
@@ -1147,6 +1159,7 @@
         pageNum: 1,
         pageSize: 10,
         cry: 1,
+        bedSort: null,
         searchscope: 3,
         scopetype: [],
         leaveldeptcodes: [],
diff --git "a/\351\232\217\350\256\277\351\200\232\347\224\2509.22.zip" "b/\351\232\217\350\256\277\351\200\232\347\224\250922.zip"
similarity index 93%
rename from "\351\232\217\350\256\277\351\200\232\347\224\2509.22.zip"
rename to "\351\232\217\350\256\277\351\200\232\347\224\250922.zip"
index 08a35f9..ff5a348 100644
--- "a/\351\232\217\350\256\277\351\200\232\347\224\2509.22.zip"
+++ "b/\351\232\217\350\256\277\351\200\232\347\224\250922.zip"
Binary files differ

--
Gitblit v1.9.3