WXL (wul)
10 小时以前 c65b90aaa3477a90ebc325024927d80227c0c841
src/views/Satisfaction/configurationmyd/components/DetailsAnomaly.vue
@@ -13,147 +13,280 @@
      <el-row :gutter="20">
        <el-col :span="8">
          <div class="info-item">
            <span class="label">患者姓名:</span>
            <span class="value">{{ currentRecord.patientName }}</span>
            <span class="label">问题内容:</span>
            <span class="value">{{ currentRecord.questiontext || '无' }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">性别:</span>
            <span class="value">{{ currentRecord.gender === 1 ? '男' : '女' }}</span>
            <span class="label">回答内容:</span>
            <span class="value">{{ currentRecord.asrtext || '无回答' }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">年龄:</span>
            <span class="value">{{ currentRecord.age }}岁</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">联系方式:</span>
            <span class="value">{{ currentRecord.phone }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">出院科室:</span>
            <span class="value">{{ currentRecord.dischargeDept }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">出院病区:</span>
            <span class="value">{{ currentRecord.dischargeWard }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">填写时间:</span>
            <span class="value">{{ currentRecord.fillTime }}</span>
            <span class="label">解析值:</span>
            <span class="value">{{ currentRecord.matchedtext || '无' }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">负责科室:</span>
            <el-tag type="primary">{{ currentRecord.responsibilityDept }}</el-tag>
            <el-tag v-if="currentRecord.todeptname" type="primary">{{ currentRecord.todeptname }}</el-tag>
            <span v-else class="value">未分配</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">处理状态:</span>
            <el-tag
              :type="getStatusTagType(currentRecord.processStatus)"
              :type="getStatusTagType(currentRecord.handleFlag)"
              effect="dark"
            >
              {{ getStatusText(currentRecord.processStatus) }}
              {{ getStatusText(currentRecord.handleFlag) }}
            </el-tag>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">模板类型:</span>
            <el-tag :type="currentRecord.templateType === 1 ? 'primary' : 'success'">
              {{ currentRecord.templateType === 1 ? '语音模板' : '问卷模板' }}
            </el-tag>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">创建时间:</span>
            <span class="value">{{ formatDateTime(currentRecord.createTime) }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">处理时间:</span>
            <span class="value">{{ currentRecord.handleTime ? formatDateTime(currentRecord.handleTime) : '未处理' }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <span class="label">处理人:</span>
            <span class="value">{{ currentRecord.handleBy || '未处理' }}</span>
          </div>
        </el-col>
        <el-col :span="8" v-if="currentRecord.handleresult">
          <div class="info-item">
            <span class="label">处理结果:</span>
            <span class="value">{{ getHandleresultText(currentRecord.handleresult) }}</span>
          </div>
        </el-col>
        <el-col :span="16" v-if="currentRecord.handledesc">
          <div class="info-item">
            <span class="label">处理说明:</span>
            <span class="value">{{ currentRecord.handledesc }}</span>
          </div>
        </el-col>
        <el-col :span="24" v-if="currentRecord.finaloption">
          <div class="info-item">
            <span class="label">最终意见:</span>
            <span class="value">{{ currentRecord.finaloption }}</span>
          </div>
        </el-col>
        <el-col :span="8" v-if="currentRecord.recordurl">
          <div class="info-item">
            <span class="label">录音地址:</span>
            <el-button
              type="text"
              size="small"
              icon="el-icon-headset"
              @click="handlePlayAudio(currentRecord.recordurl)"
            >
              播放录音
            </el-button>
          </div>
        </el-col>
        <el-col :span="8" v-if="currentRecord.ccdepts">
          <div class="info-item">
            <span class="label">抄送科室:</span>
            <span class="value">{{ currentRecord.ccdepts }}</span>
          </div>
        </el-col>
      </el-row>
    </div>
    <!-- 问卷详情 -->
    <div class="questionnaire-section">
      <div class="section-title">问卷填写详情</div>
      <div class="questionnaire-content">
        <div class="question-item" v-for="(question, index) in questionnaireData" :key="index">
          <div class="question-header">
            <span class="question-index">{{ index + 1 }}.</span>
            <span class="question-text">{{ question.question }}</span>
            <el-tag
              size="mini"
              :type="question.type === 1 ? 'primary' : 'success'"
              class="question-type"
            >
              {{ question.type === 1 ? '单选题' : '多选题' }}
            </el-tag>
          </div>
          <div class="question-options">
            <el-radio-group
              v-model="question.answer"
              v-if="question.type === 1"
              disabled
            >
              <el-radio
                v-for="option in question.options"
                :key="option.value"
                :label="option.value"
                :class="{ 'unsatisfactory-option': isUnsatisfactoryOption(option.value) }"
    <!-- 问卷/语音详情 -->
    <div class="content-container" v-if="templateData.length > 0">
      <el-tabs v-model="activeName" type="border-card">
        <!-- 问卷随访详情 -->
        <el-tab-pane name="wj" v-if="currentRecord.templateType === 2">
          <span slot="label"><i class="el-icon-notebook-1"></i> 问卷随访详情</span>
          <div class="CONTENT">
            <div class="title">{{ currentRecord.questiontext || '问卷详情' }}</div>
            <div class="preview-left" v-if="!isVoiceTemplate">
              <div
                class="topic-dev"
                v-for="(item, index) in templateData"
                :key="item.id"
              >
                {{ option.text }}
              </el-radio>
            </el-radio-group>
            <el-checkbox-group
              v-model="question.answer"
              v-else
              disabled
            >
              <el-checkbox
                v-for="option in question.options"
                :key="option.value"
                :label="option.value"
                :class="{ 'unsatisfactory-option': isUnsatisfactoryOption(option.value) }"
              >
                {{ option.text }}
              </el-checkbox>
            </el-checkbox-group>
                <!-- 单选 -->
                <div
                  :class="getTopicClass(item)"
                  :key="index"
                  v-if="item.scriptType == 1 && !item.astrict"
                >
                  <div class="dev-text">
                    {{ index + 1 }}、[单选]<span>{{ item.scriptContent }}</span>
                  </div>
                  <div class="dev-xx">
                    <el-radio-group v-model="item.scriptResult" disabled>
                      <el-radio
                        v-for="(option, optionIndex) in item.svyTaskTemplateTargetoptions"
                        :class="getOptionClass(option)"
                        :key="optionIndex"
                        :label="option.optioncontent"
                      >{{ option.optioncontent }}</el-radio>
                    </el-radio-group>
                  </div>
                  <div
                    v-if="item.showAppendInput || item.answerps"
                    class="append-input-container"
                  >
                    <el-input
                      type="textarea"
                      :rows="2"
                      placeholder="请输入具体信息"
                      v-model="item.answerps"
                      readonly
                    ></el-input>
                  </div>
                  <div v-show="item.prompt">
                    <el-alert :title="item.prompt" type="warning"></el-alert>
                  </div>
                </div>
                <!-- 多选 -->
                <div
                  :class="item.isabnormal ? 'scriptTopic-isabnormal' : 'scriptTopic-dev'"
                  :key="index"
                  v-if="item.scriptType == 2 && !item.astrict"
                >
                  <div class="dev-text">
                    {{ index + 1 }}、[多选]<span>{{ item.scriptContent }}</span>
                  </div>
                  <div class="dev-xx">
                    <el-checkbox-group v-model="item.scriptResult" disabled>
                      <el-checkbox
                        :class="option.isabnormal ? 'red-star' : ''"
                        v-for="(option, optionIndex) in item.svyTaskTemplateTargetoptions"
                        :key="optionIndex"
                        :label="option.optioncontent"
                      >
                        {{ option.optioncontent }}
                      </el-checkbox>
                    </el-checkbox-group>
                  </div>
                  <div v-show="item.prompt && item.scriptResult[0]">
                    <el-alert :title="item.prompt" type="warning"></el-alert>
                  </div>
                </div>
                <!-- 填空 -->
                <div
                  class="scriptTopic-dev"
                  :key="index"
                  v-if="item.scriptType == 4 && !item.astrict"
                >
                  <div class="dev-text">
                    {{ index + 1 }}、[问答]<span>{{ item.scriptContent }}</span>
                    <span v-if="item.valueType == 3">(只能输入数字)</span>
                  </div>
                  <div class="dev-xx" v-if="item.valueType == 3">
                    <el-input
                      type="text"
                      placeholder="请输入答案"
                      v-model="item.scriptResult"
                      readonly
                    ></el-input>
                  </div>
                  <div class="dev-xx" v-else>
                    <el-input
                      type="textarea"
                      :rows="2"
                      placeholder="请输入答案"
                      v-model="item.scriptResult"
                      readonly
                    ></el-input>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div v-if="question.additional" class="additional-remark">
            <div class="remark-label">补充说明:</div>
            <div class="remark-content">{{ question.additional }}</div>
        </el-tab-pane>
        <!-- 语音随访详情 -->
        <el-tab-pane name="yy" v-if="currentRecord.templateType === 1">
          <span slot="label"><i class="el-icon-headset"></i> 语音随访详情</span>
          <div class="borderdiv">
            <div class="title">{{ taskName }}</div>
            <div class="voice-audio" v-if="voiceAudioUrl">
              完整语音:
              <audio-player
                :audio-source="voiceAudioUrl"
              ></audio-player>
            </div>
            <div class="preview-left" v-if="voiceData.length > 0">
              <div v-for="(item, index) in voiceData" :key="index">
                <div class="leftside">
                  <i class="el-icon-phone-outline"></i>
                  <span>{{ item.questiontext || '问题内容' }}</span>
                </div>
                <div class="offside">
                  <i class="el-icon-user"></i>
                  <div class="offside-value">
                    <el-input
                      type="textarea"
                      :autosize="{ minRows: 1 }"
                      v-model="item.asrtext"
                      readonly
                    ></el-input>
                    <div v-if="item.questionvoice">
                      <audio-player
                        :audio-source="item.questionvoice"
                      ></audio-player>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
        </el-tab-pane>
      </el-tabs>
    </div>
    <!-- 处理记录 -->
    <div class="process-section">
    <div class="process-section" v-if="processRecords.length > 0">
      <div class="section-title">处理记录</div>
      <div class="process-timeline" v-if="processRecords.length > 0">
      <div class="process-timeline">
        <el-timeline>
          <el-timeline-item
            v-for="(record, index) in processRecords"
            :key="index"
            :timestamp="record.time"
            :timestamp="formatDateTime(record.handleTime || record.createTime)"
            placement="top"
          >
            <el-card>
              <div class="process-item">
                <div class="process-header">
                  <span class="process-user">{{ record.user }}</span>
                  <span class="process-user">{{ record.handleBy || '系统' }}</span>
                  <el-tag
                    size="small"
                    :type="getStatusTagType(record.status)"
                    :type="getStatusTagType(record.handleFlag)"
                  >
                    {{ getStatusText(record.status) }}
                    {{ getStatusText(record.handleFlag) }}
                  </el-tag>
                </div>
                <div class="process-content">
                  <div v-if="record.reportDepts && record.reportDepts.length > 0" class="process-depts">
                    <span class="label">报备科室:</span>
                  <div v-if="record.ccdepts" class="process-depts">
                    <span class="label">抄送科室:</span>
                    <el-tag
                      v-for="dept in record.reportDepts"
                      v-for="dept in getDeptArray(record.ccdepts)"
                      :key="dept"
                      size="small"
                      type="info"
@@ -162,31 +295,23 @@
                      {{ dept }}
                    </el-tag>
                  </div>
                  <div v-if="record.remark" class="process-remark">
                    <span class="label">处理备注:</span>
                    <span class="content">{{ record.remark }}</span>
                  <div v-if="record.handleresult" class="process-remark">
                    <span class="label">处理结果:</span>
                    <span class="content">{{ getHandleresultText(record.handleresult) }}</span>
                  </div>
                  <div v-if="record.attachments && record.attachments.length > 0" class="process-attachments">
                    <span class="label">附件:</span>
                    <el-button
                      v-for="file in record.attachments"
                      :key="file.id"
                      type="text"
                      size="small"
                      icon="el-icon-document"
                      @click="handlePreviewFile(file)"
                    >
                      {{ file.name }}
                    </el-button>
                  <div v-if="record.handledesc" class="process-remark">
                    <span class="label">处理说明:</span>
                    <span class="content">{{ record.handledesc }}</span>
                  </div>
                  <div v-if="record.finaloption" class="process-remark">
                    <span class="label">最终意见:</span>
                    <span class="content">{{ record.finaloption }}</span>
                  </div>
                </div>
              </div>
            </el-card>
          </el-timeline-item>
        </el-timeline>
      </div>
      <div v-else class="no-record">
        暂无处理记录
      </div>
    </div>
@@ -195,7 +320,7 @@
        type="primary"
        icon="el-icon-edit"
        @click="handleProcess"
        v-if="currentRecord.processStatus !== 2"
        v-if="currentRecord.handleFlag !== '1'"
      >
        处理异常
      </el-button>
@@ -217,61 +342,72 @@
        label-width="100px"
        size="medium"
      >
        <el-form-item label="处理状态" prop="status">
        <el-form-item label="处理状态" prop="handleFlag">
          <el-select
            v-model="processForm.status"
            v-model="processForm.handleFlag"
            placeholder="请选择处理状态"
            style="width: 100%"
          >
            <el-option label="处理中" :value="1" />
            <el-option label="已处理" :value="2" />
            <el-option label="已驳回" :value="3" />
            <el-option label="已处理" :value="'1'" />
            <el-option label="取消处理" :value="'0'" />
          </el-select>
        </el-form-item>
        <el-form-item label="报备科室" prop="reportDepts">
        <el-form-item label="抄送科室" prop="ccdepts">
          <el-select
            v-model="processForm.reportDepts"
            placeholder="请选择报备科室"
            v-model="processForm.ccdepts"
            placeholder="请选择抄送科室"
            multiple
            filterable
            collapse-tags
            style="width: 100%"
            :disabled="processForm.handleFlag !== '1'"
          >
            <el-option
              v-for="dept in deptList"
              :key="dept.id"
              :label="dept.name"
              :value="dept.id"
              :key="dept.deptCode"
              :label="dept.label"
              :value="dept.deptCode"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="处理备注" prop="remark">
        <el-form-item label="处理结果" prop="handleresult">
          <el-select
            v-model="processForm.handleresult"
            placeholder="请选择处理结果"
            style="width: 100%"
            :disabled="processForm.handleFlag !== '1'"
          >
            <el-option label="已解决" value="resolved" />
            <el-option label="已解释" value="explained" />
            <el-option label="已转交" value="transferred" />
            <el-option label="需改进" value="improvement" />
            <el-option label="已驳回" value="rejected" />
          </el-select>
        </el-form-item>
        <el-form-item label="处理说明" prop="handledesc">
          <el-input
            v-model="processForm.remark"
            v-model="processForm.handledesc"
            type="textarea"
            :rows="4"
            placeholder="请输入处理备注(最多500字)"
            placeholder="请输入处理说明(最多500字)"
            maxlength="500"
            show-word-limit
            :disabled="processForm.handleFlag !== '1'"
          />
        </el-form-item>
        <el-form-item label="附件上传">
          <el-upload
            class="upload-demo"
            action="#"
            :on-preview="handleFilePreview"
            :on-remove="handleFileRemove"
            :before-remove="beforeFileRemove"
            :limit="3"
            :on-exceed="handleFileExceed"
            :file-list="fileList"
          >
            <el-button size="small" type="primary">点击上传</el-button>
            <div slot="tip" class="el-upload__tip">支持上传图片、文档等附件,单个文件不超过10MB</div>
          </el-upload>
        <el-form-item label="最终意见" prop="finaloption" v-if="hasQualityPermission">
          <el-input
            v-model="processForm.finaloption"
            type="textarea"
            :rows="3"
            placeholder="请输入最终处理意见(最多300字)"
            maxlength="300"
            show-word-limit
          />
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
@@ -285,24 +421,42 @@
        </el-button>
      </span>
    </el-dialog>
    <!-- 录音播放器 -->
    <audio
      v-if="audioUrl"
      :src="audioUrl"
      ref="audioPlayer"
      controls
      style="display: none"
    />
  </el-dialog>
</template>
<script>
import { traceedit } from "@/api/AiCentre/index";
import { getsearchrResults, getPersonVoices, getTaskservelist } from "@/api/AiCentre/index";
import { deptTreeSelect } from "@/api/system/user";
import AudioPlayer from "@/components/AudioPlayer"; // 需要创建这个音频播放组件
export default {
  name: 'ExceptionDetailDialog',
  components: {
    AudioPlayer
  },
  props: {
    // 是否显示对话框
    visible: {
      type: Boolean,
      default: false
    },
    // 记录ID
    recordId: {
      type: [Number, String],
      default: null
    },
    // 对话框标题
    recordData: {
      type: Object,
      default: () => ({})
    },
    title: {
      type: String,
      default: '异常反馈详情'
@@ -313,45 +467,71 @@
      // 当前记录
      currentRecord: {},
      // 问卷数据
      questionnaireData: [],
      // 问卷/语音数据
      activeName: 'wj',
      taskName: '',
      templateData: [],
      voiceData: [],
      voiceAudioUrl: '',
      // 处理记录
      processRecords: [],
      // 科室列表
      deptList: [
        { id: 1, name: '心血管内科' },
        { id: 2, name: '神经内科' },
        { id: 3, name: '普外科' },
        { id: 4, name: '骨科' },
        { id: 5, name: '妇产科' },
        { id: 6, name: '儿科' },
        { id: 7, name: '急诊科' },
        { id: 8, name: '呼吸内科' }
      ],
      deptList: [],
      // 处理对话框
      processDialogVisible: false,
      processing: false,
      processForm: {
        status: '',
        reportDepts: [],
        remark: ''
        handleFlag: '',
        ccdepts: [],
        handleresult: '',
        handledesc: '',
        finaloption: ''
      },
      processRules: {
        status: [
        handleFlag: [
          { required: true, message: '请选择处理状态', trigger: 'change' }
        ],
        remark: [
          { required: true, message: '请输入处理备注', trigger: 'blur' },
          { min: 5, max: 500, message: '备注长度在 5 到 500 个字符', trigger: 'blur' }
        handleresult: [
          {
            required: true,
            message: '请选择处理结果',
            trigger: 'change',
            validator: (rule, value, callback) => {
              if (this.processForm.handleFlag === '1' && !value) {
                callback(new Error('请选择处理结果'));
              } else {
                callback();
              }
            }
          }
        ],
        handledesc: [
          {
            required: true,
            message: '请输入处理说明',
            trigger: 'blur',
            validator: (rule, value, callback) => {
              if (this.processForm.handleFlag === '1' && (!value || value.trim().length < 3)) {
                callback(new Error('处理说明至少3个字符'));
              } else {
                callback();
              }
            }
          }
        ]
      },
      fileList: [],
      // 音频播放
      audioUrl: '',
      // 加载状态
      loading: false
      loading: false,
      // 权限控制
      hasQualityPermission: false
    };
  },
@@ -363,6 +543,10 @@
      set(val) {
        this.$emit('update:visible', val);
      }
    },
    isVoiceTemplate() {
      return this.currentRecord.templateType === 1;
    }
  },
@@ -370,237 +554,293 @@
    visible: {
      immediate: true,
      handler(val) {
        if (val && this.recordId) {
        if (val) {
          this.loadData();
        } else {
          this.resetData();
        }
      }
    },
    recordData: {
      immediate: true,
      handler(val) {
        if (val && Object.keys(val).length > 0) {
          this.currentRecord = { ...val };
        }
      }
    }
  },
  methods: {
    // 加载数据
    async loadData() {
      this.loading = true;
    // 格式化日期时间
    formatDateTime(dateTime) {
      if (!dateTime) return '';
      try {
        await Promise.all([
          this.loadRecordDetail(),
          this.loadQuestionnaireData(),
          this.loadProcessRecords()
        ]);
      } finally {
        this.loading = false;
        const date = new Date(dateTime);
        if (isNaN(date.getTime())) {
          return dateTime;
        }
        return date.toLocaleDateString().replace(/\//g, '-') + ' ' +
               date.toTimeString().split(' ')[0];
      } catch (error) {
        console.error('日期格式化错误:', error);
        return dateTime;
      }
    },
    // 加载记录详情
    async loadRecordDetail() {
      return new Promise(resolve => {
        setTimeout(() => {
          // 根据不同的recordId返回不同的mock数据
          const mockRecords = {
            1: {
              id: 1,
              patientName: '张先生',
              gender: 1,
              age: 45,
              phone: '13800138000',
              dischargeDept: '心血管内科',
              dischargeWard: '内科一病区',
              fillTime: '2024-01-15 10:30:25',
              responsibilityDept: '心血管内科',
              processStatus: 0
            },
            2: {
              id: 2,
              patientName: '李女士',
              gender: 0,
              age: 38,
              phone: '13900139000',
              dischargeDept: '神经内科',
              dischargeWard: '内科二病区',
              fillTime: '2024-01-14 16:20:10',
              responsibilityDept: '神经内科',
              processStatus: 0
            },
            3: {
              id: 3,
              patientName: '王先生',
              gender: 1,
              age: 52,
              phone: '13700137000',
              dischargeDept: '普外科',
              dischargeWard: '外科一病区',
              fillTime: '2024-01-13 09:15:45',
              responsibilityDept: '普外科',
              processStatus: 1
            }
          };
          this.currentRecord = mockRecords[this.recordId] || {
            id: 1,
            patientName: '张先生',
            gender: 1,
            age: 45,
            phone: '13800138000',
            dischargeDept: '心血管内科',
            dischargeWard: '内科一病区',
            fillTime: '2024-01-15 10:30:25',
            responsibilityDept: '心血管内科',
            processStatus: 0
          };
          resolve();
        }, 300);
      });
    // 检查质管权限
    checkQualityPermission() {
      const userRoles = this.$store.getters.roles || [];
      return userRoles.includes('quality_manager') || userRoles.includes('admin');
    },
    // 加载问卷数据
    async loadQuestionnaireData() {
      return new Promise(resolve => {
        setTimeout(() => {
          this.questionnaireData = [
            {
              question: '您对医护人员的服务态度是否满意?',
              type: 1,
              options: [
                { value: '非常满意', text: '非常满意' },
                { value: '满意', text: '满意' },
                { value: '一般', text: '一般' },
                { value: '不满意', text: '不满意' },
                { value: '非常不满意', text: '非常不满意' }
              ],
              answer: '不满意',
              additional: '医生查房时间太短,沟通不够充分,对病情解释不够详细'
            },
            {
              question: '您对医生的诊疗水平和技术能力评价如何?',
              type: 1,
              options: [
                { value: '非常专业', text: '非常专业' },
                { value: '比较专业', text: '比较专业' },
                { value: '一般', text: '一般' },
                { value: '不够专业', text: '不够专业' },
                { value: '非常不专业', text: '非常不专业' }
              ],
              answer: '比较专业',
              additional: ''
            },
            {
              question: '您对医院的环境和卫生状况是否满意?',
              type: 1,
              options: [
                { value: '非常满意', text: '非常满意' },
                { value: '满意', text: '满意' },
                { value: '一般', text: '一般' },
                { value: '不满意', text: '不满意' },
                { value: '非常不满意', text: '非常不满意' }
              ],
              answer: '一般',
              additional: ''
            },
            {
              question: '您认为医护人员与您的沟通是否充分?',
              type: 1,
              options: [
                { value: '非常充分', text: '非常充分' },
                { value: '比较充分', text: '比较充分' },
                { value: '一般', text: '一般' },
                { value: '不够充分', text: '不够充分' },
                { value: '非常不充分', text: '非常不充分' }
              ],
              answer: '不够充分',
              additional: '医生讲解病情时语速太快,没有给足够的时间提问'
            },
            {
              question: '您对等待就诊和治疗的时间是否满意?',
              type: 1,
              options: [
                { value: '非常满意', text: '非常满意' },
                { value: '满意', text: '满意' },
                { value: '一般', text: '一般' },
                { value: '不满意', text: '不满意' },
                { value: '非常不满意', text: '非常不满意' }
              ],
              answer: '不满意',
              additional: '预约的9点,实际10点才见到医生'
            }
          ];
          resolve();
        }, 300);
      });
    // 获取科室列表
    async getDeptOptions() {
      try {
        const res = await deptTreeSelect();
        if (res.code == 200) {
          this.deptList = this.flattenArray(res.data) || [];
        }
      } catch (error) {
        console.error('获取科室列表失败:', error);
      }
    },
    // 加载处理记录
    async loadProcessRecords() {
      return new Promise(resolve => {
        setTimeout(() => {
          this.processRecords = [
            {
              id: 1,
              time: '2024-01-15 14:20:30',
              user: '张医生',
              status: 1, // 处理中
              reportDepts: ['医务科', '护理部'],
              remark: '已收到反馈,正在安排相关人员核查情况',
              attachments: [
                { id: 1, name: '初步调查记录.docx' },
                { id: 2, name: '患者沟通记录.jpg' }
              ]
            },
            {
              id: 2,
              time: '2024-01-15 10:45:12',
              user: '系统',
              status: 0, // 待处理
              remark: '系统自动识别为异常反馈,已分配到责任科室',
              attachments: []
            }
          ];
          resolve();
        }, 300);
      });
    },
    // 展平数组
    flattenArray(multiArray) {
      let result = [];
    // 判断是否为不满意选项
    isUnsatisfactoryOption(value) {
      const unsatisfactoryValues = [
        '不满意',
        '非常不满意',
        '不够专业',
        '非常不专业',
        '不够充分',
        '非常不充分'
      ];
      return unsatisfactoryValues.includes(value);
      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;
    },
    // 获取状态标签类型
    getStatusTagType(status) {
      switch (status) {
        case 0: return 'warning'; // 待处理
        case 1: return 'primary'; // 处理中
        case 2: return 'success'; // 已处理
        case 3: return 'danger';  // 已驳回
    getStatusTagType(handleFlag) {
      switch (handleFlag) {
        case '0': return 'warning'; // 未处理
        case '1': return 'success'; // 已处理
        default: return 'info';
      }
    },
    // 获取状态文本
    getStatusText(status) {
      switch (status) {
        case 0: return '待处理';
        case 1: return '处理中';
        case 2: return '已处理';
        case 3: return '已驳回';
    getStatusText(handleFlag) {
      switch (handleFlag) {
        case '0': return '未处理';
        case '1': return '已处理';
        default: return '未知';
      }
    },
    // 获取处理结果文本
    getHandleresultText(handleresult) {
      const map = {
        'resolved': '已解决',
        'explained': '已解释',
        'transferred': '已转交',
        'improvement': '需改进',
        'rejected': '已驳回'
      };
      return map[handleresult] || handleresult;
    },
    // 获取科室数组
    getDeptArray(ccdepts) {
      if (!ccdepts) return [];
      return ccdepts.split(',');
    },
    // 播放音频
    handlePlayAudio(url) {
      this.audioUrl = url;
      this.$nextTick(() => {
        const audioPlayer = this.$refs.audioPlayer;
        if (audioPlayer) {
          audioPlayer.play().catch(error => {
            console.error('播放失败:', error);
            this.$message.error('音频播放失败');
          });
        }
      });
    },
    // 获取主题样式类
    getTopicClass(item) {
      if (item.isabnormal == 1) {
        return "scriptTopic-isabnormal";
      } else if (item.isabnormal == 2) {
        return "scriptTopic-warning";
      } else {
        return "scriptTopic-dev";
      }
    },
    // 获取选项样式类
    getOptionClass(items) {
      if (items.isabnormal == 1) {
        return "red-star";
      } else if (items.isabnormal == 2) {
        return "yellow-star";
      }
      return "";
    },
    // 加载数据
    async loadData() {
      this.loading = true;
      try {
        this.hasQualityPermission = this.checkQualityPermission();
        await this.getDeptOptions();
        if (Object.keys(this.currentRecord).length === 0) {
          this.currentRecord = this.recordData || {};
        }
        // 如果当前记录是语音模板,加载语音数据
        if (this.currentRecord.templateType === 1) {
          await this.loadVoiceData();
          this.activeName = 'yy';
        } else if (this.currentRecord.templateType === 2) {
          await this.loadQuestionnaireData();
          this.activeName = 'wj';
        }
        await this.loadProcessRecords();
      } catch (error) {
        console.error('加载详情数据失败:', error);
        this.$message.error('加载数据失败');
      } finally {
        this.loading = false;
      }
    },
    // 重置数据
    resetData() {
      this.currentRecord = {};
      this.templateData = [];
      this.voiceData = [];
      this.processRecords = [];
      this.voiceAudioUrl = '';
      this.taskName = '';
      this.activeName = 'wj';
    },
    // 加载问卷数据
    async loadQuestionnaireData() {
      try {
        // 这里需要根据实际情况调用接口获取问卷数据
        // 如果recordData中已经包含了问卷数据,可以直接使用
        if (this.currentRecord.taskid && this.currentRecord.patid) {
          const params = {
            taskid: this.currentRecord.taskid,
            patid: this.currentRecord.patid,
            subId: this.currentRecord.subId || this.currentRecord.id,
            isFinish: true
          };
          const res = await getsearchrResults(params);
          if (res.code === 200 && res.data) {
            this.templateData = res.data.scriptResult || [];
            this.taskName = res.data.taskName || '';
            // 处理数据格式
            this.templateData.forEach((item) => {
              if (item.scriptType == 2) item.scriptResult = [];
              if (item.scriptResultId && item.scriptType != 2) {
                item.isoption = 3;
                item.scriptResult = item.scriptResult;
              } else if (item.scriptResultId && item.scriptType == 2) {
                item.scriptResult = item.scriptResult.split("&");
                item.isoption = 3;
              }
            });
            this.overdata();
          }
        }
      } catch (error) {
        console.error('加载问卷数据失败:', error);
      }
    },
    // 处理异常数据
    overdata() {
      this.templateData.forEach((item, index) => {
        var obj = item.svyTaskTemplateTargetoptions.find(
          (items) => items.optioncontent == item.scriptResult
        );
        if (obj && obj.isabnormal) {
          this.templateData[index].isabnormal = obj.isabnormal;
        }
        this.$forceUpdate();
      });
    },
    // 加载语音数据
    async loadVoiceData() {
      try {
        if (this.currentRecord.taskid && this.currentRecord.patid) {
          const params = {
            taskid: this.currentRecord.taskid,
            patid: this.currentRecord.patid,
            subId: this.currentRecord.subId || this.currentRecord.id
          };
          const res = await getPersonVoices(params);
          if (res.code == 200) {
            this.voiceData = res.data.serviceSubtaskDetails || [];
            this.voiceAudioUrl = res.data.voice || '';
            this.taskName = res.data.taskName || '';
            this.templateData = res.data.filteredDetails || [];
            this.templateData.forEach((item) => {
              if (item.targetvalue) {
                item.scriptResult = item.targetvalue.split("&");
              } else {
                item.scriptResult = [];
              }
            });
          }
        }
      } catch (error) {
        console.error('加载语音数据失败:', error);
      }
    },
    // 加载处理记录
    async loadProcessRecords() {
      try {
        // 这里可以根据recordId加载处理历史
        // 暂时使用当前记录的处理信息
        if (this.currentRecord.handleTime) {
          this.processRecords = [{
            ...this.currentRecord,
            time: this.currentRecord.handleTime
          }];
        }
      } catch (error) {
        console.error('加载处理记录失败:', error);
      }
    },
    // 处理异常
    handleProcess() {
      this.processForm = {
        status: this.currentRecord.processStatus === 0 ? 1 : this.currentRecord.processStatus,
        reportDepts: [],
        remark: ''
        handleFlag: this.currentRecord.handleFlag === '0' ? '1' : '0',
        ccdepts: this.currentRecord.ccdepts ? this.currentRecord.ccdepts.split(',') : [],
        handleresult: this.currentRecord.handleresult || '',
        handledesc: this.currentRecord.handledesc || '',
        finaloption: this.currentRecord.finaloption || ''
      };
      this.processDialogVisible = true;
    },
@@ -608,52 +848,56 @@
    // 提交处理
    async submitProcess() {
      this.$refs.processForm.validate(async (valid) => {
        if (valid) {
          this.processing = true;
          try {
            // Mock API调用
            await new Promise(resolve => setTimeout(resolve, 1000));
        if (!valid) {
          return;
        }
            this.$message.success('处理提交成功');
        this.processing = true;
        try {
          const submitData = {
            id: this.currentRecord.id,
            handleFlag: this.processForm.handleFlag,
            handleresult: this.processForm.handleresult,
            handledesc: this.processForm.handledesc,
            finaloption: this.processForm.finaloption,
            ccdepts: Array.isArray(this.processForm.ccdepts)
              ? this.processForm.ccdepts.join(",")
              : this.processForm.ccdepts
          };
          const res = await traceedit(submitData);
          if (res.code === 200) {
            this.$message.success("处理提交成功");
            this.processDialogVisible = false;
            // 重新加载数据
            await this.loadData();
            // 更新当前记录
            this.currentRecord = {
              ...this.currentRecord,
              ...submitData,
              handleBy: this.$store.getters.name, // 当前用户
              handleTime: new Date().toISOString().replace('T', ' ').substr(0, 19)
            };
            // 重新加载处理记录
            await this.loadProcessRecords();
            // 触发父组件刷新
            this.$emit('processed');
          } finally {
            this.processing = false;
          } else {
            this.$message.error(res.msg || "处理提交失败");
          }
        } catch (error) {
          console.error("处理提交失败:", error);
          this.$message.error("处理提交失败,请稍后重试");
        } finally {
          this.processing = false;
        }
      });
    },
    // 预览文件
    handlePreviewFile(file) {
      this.$message.info(`预览文件: ${file.name}`);
    },
    // 处理对话框关闭
    handleClose() {
      this.$emit('close');
    },
    // 文件上传相关方法
    handleFilePreview(file) {
      console.log('预览文件:', file);
    },
    handleFileRemove(file, fileList) {
      console.log('移除文件:', file, fileList);
    },
    beforeFileRemove(file) {
      return this.$confirm(`确定移除 ${file.name}?`);
    },
    handleFileExceed(files, fileList) {
      this.$message.warning(`当前限制选择 3 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
    }
  }
};
@@ -705,116 +949,137 @@
        font-size: 14px;
        color: #303133;
        font-weight: 500;
        flex: 1;
      }
    }
  }
  .questionnaire-section {
  .content-container {
    margin-bottom: 20px;
    padding: 20px;
    background: #fff;
    border-radius: 8px;
    border: 1px solid #ebeef5;
    .section-title {
      font-size: 16px;
      font-weight: 600;
      color: #303133;
      margin-bottom: 15px;
      padding-bottom: 10px;
      border-bottom: 2px solid #409EFF;
    ::v-deep .el-tabs__content {
      padding: 20px;
      background: #fff;
      border-radius: 0 0 4px 4px;
    }
    .questionnaire-content {
      .question-item {
        margin-bottom: 20px;
        padding: 15px;
        border-radius: 6px;
        border: 1px solid #ebeef5;
        transition: all 0.3s;
    .CONTENT, .borderdiv {
      padding: 20px;
      background: #fff;
      border-radius: 6px;
        &:hover {
          border-color: #409EFF;
          box-shadow: 0 2px 12px 0 rgba(64, 158, 255, 0.1);
      .title {
        font-size: 18px;
        font-weight: 600;
        color: #303133;
        margin-bottom: 20px;
        padding-bottom: 10px;
        border-bottom: 2px solid #409EFF;
      }
      .voice-audio {
        margin-bottom: 20px;
        display: flex;
        align-items: center;
        gap: 10px;
        .audio-player {
          flex: 1;
        }
      }
      .preview-left {
        .topic-dev {
          margin-bottom: 20px;
          padding: 15px;
          border-radius: 6px;
          border: 1px solid #ebeef5;
          background: #fff;
          .dev-text {
            font-size: 15px;
            font-weight: 500;
            color: #303133;
            margin-bottom: 15px;
            line-height: 1.5;
            span {
              color: #606266;
            }
          }
          .dev-xx {
            ::v-deep .el-radio-group,
            ::v-deep .el-checkbox-group {
              display: flex;
              flex-direction: column;
              gap: 10px;
            }
            ::v-deep .el-radio,
            ::v-deep .el-checkbox {
              margin: 0;
              padding: 8px 12px;
              border-radius: 4px;
              border: 1px solid #ebeef5;
              transition: all 0.3s;
              &:hover {
                background: #f5f7fa;
              }
              &.red-star {
                border-color: #f56c6c;
                background: #fef0f0;
              }
              &.yellow-star {
                border-color: #e6a23c;
                background: #fdf6ec;
              }
            }
          }
          .append-input-container {
            margin-top: 15px;
          }
          .el-alert {
            margin-top: 10px;
          }
        }
        .question-header {
        .leftside {
          display: flex;
          align-items: center;
          margin-bottom: 15px;
          padding-bottom: 10px;
          border-bottom: 1px dashed #dcdfe6;
          gap: 10px;
          margin-bottom: 10px;
          font-size: 15px;
          font-weight: 500;
          color: #303133;
          .question-index {
            font-weight: 600;
          i {
            color: #409EFF;
            margin-right: 8px;
            font-size: 15px;
          }
        }
        .offside {
          display: flex;
          align-items: flex-start;
          gap: 10px;
          margin-bottom: 20px;
          i {
            color: #67C23A;
            margin-top: 8px;
          }
          .question-text {
          .offside-value {
            flex: 1;
            font-size: 15px;
            color: #303133;
            font-weight: 500;
            line-height: 1.5;
          }
          .question-type {
            margin-left: 10px;
          }
        }
        .question-options {
          ::v-deep .el-radio-group {
            display: flex;
            flex-direction: column;
            gap: 10px;
          }
          ::v-deep .el-checkbox-group {
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
          }
          ::v-deep .el-radio,
          ::v-deep .el-checkbox {
            margin: 0;
            padding: 8px 12px;
            border-radius: 4px;
            border: 1px solid #ebeef5;
            transition: all 0.3s;
            &:hover {
              background: #f5f7fa;
            .el-textarea {
              margin-bottom: 10px;
            }
            &.unsatisfactory-option {
              border-color: #e6a23c;
              background: #fdf6ec;
            }
          }
        }
        .additional-remark {
          margin-top: 15px;
          padding: 12px;
          background: #f0f9ff;
          border-radius: 6px;
          border-left: 4px solid #409EFF;
          .remark-label {
            font-size: 13px;
            color: #606266;
            font-weight: 500;
            margin-bottom: 5px;
          }
          .remark-content {
            font-size: 14px;
            color: #303133;
            line-height: 1.6;
          }
        }
      }
@@ -886,30 +1151,8 @@
              line-height: 1.5;
            }
          }
          .process-attachments {
            .label {
              font-size: 13px;
              color: #606266;
              margin-right: 5px;
            }
            ::v-deep .el-button {
              margin-right: 8px;
              margin-bottom: 5px;
            }
          }
        }
      }
    }
    .no-record {
      text-align: center;
      padding: 40px 0;
      color: #909399;
      font-style: italic;
      background: #f8f9fa;
      border-radius: 6px;
    }
  }
@@ -920,4 +1163,15 @@
    gap: 10px;
  }
}
// 异常样式
.scriptTopic-isabnormal {
  border-color: #f56c6c !important;
  background: #fef0f0 !important;
}
.scriptTopic-warning {
  border-color: #e6a23c !important;
  background: #fdf6ec !important;
}
</style>