WXL
9 小时以前 0c03027d7f238bf5beb98e85463f53f0bd92bbaa
青岛维护
已修改5个文件
3790 ■■■■ 文件已修改
src/views/OfficeRelated/conference/index.vue 1007 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/appear/caseDetail.vue 914 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/appear/index.vue 1483 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/transfer/TransportEdit.vue 100 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/transfer/index.vue 286 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/OfficeRelated/conference/index.vue
@@ -128,7 +128,10 @@
          prop="organizerName"
          label="组织者"
          width="120"
        />
          ><template #default="scope">
            <span>{{ scope.organizerName || scope.createBy }}</span>
          </template>
        </el-table-column>
        <el-table-column
          align="center"
          prop="locationName"
@@ -270,35 +273,98 @@
        </div>
        <!-- 会议纪要附件展示 -->
        <div
          class="minutes-attachments"
          v-if="
            currentRecord.recordattachment &&
              parseAttachments(currentRecord.recordattachment).length > 0
          "
        >
          <el-divider content-position="left">会议纪要附件</el-divider>
          <div class="attachment-list">
        <div class="detail-attachments">
          <div
            v-if="parseAttachments(currentRecord.recordattachment).length > 0"
            class="attachment-grid"
          >
            <div
              v-for="file in parseAttachments(currentRecord.recordattachment)"
              :key="file.id || file.name"
              class="attachment-item"
              class="attachment-card"
            >
              <el-link
                type="primary"
                :href="file.url"
                target="_blank"
                class="file-link"
              >
                <i
                  :class="getFileIcon(file.type || file.name)"
                  style="margin-right: 8px;"
                ></i>
                {{ file.name }}
                <span class="file-size" v-if="file.size"
                  >({{ formatFileSize(file.size) }})</span
              <template v-if="isImageFile(file)">
                <!-- 图片使用 el-image 预览 -->
                <el-image
                  class="image-attachment"
                  :src="getFileUrl(file)"
                  :preview-src-list="getImagePreviewList(file)"
                  fit="cover"
                  :style="{ width: imageSize, height: imageSize }"
                  lazy
                >
              </el-link>
                  <div slot="error" class="image-error">
                    <i class="el-icon-picture-outline"></i>
                    <span>加载失败</span>
                  </div>
                  <div slot="placeholder" class="image-loading">
                    <i class="el-icon-loading"></i>
                  </div>
                </el-image>
                <div class="image-info">
                  <div class="file-name" :title="file.name">
                    {{ file.name }}
                  </div>
                  <div class="file-actions">
                    <el-button
                      type="text"
                      size="mini"
                      @click="handleDownload(file)"
                      icon="el-icon-download"
                      title="下载"
                    />
                    <el-button
                      type="text"
                      size="mini"
                      @click="handlePreview(file)"
                      icon="el-icon-view"
                      title="预览"
                    />
                  </div>
                </div>
              </template>
              <template v-else>
                <!-- 非图片文件保持原有样式 -->
                <el-card shadow="hover" class="file-card">
                  <div class="file-content">
                    <i
                      :class="getFileIcon(file.type || file.name)"
                      class="file-icon"
                    ></i>
                    <div class="file-info">
                      <div class="file-name" :title="file.name">
                        {{ file.name }}
                      </div>
                      <div class="file-meta">
                        <span class="file-type">{{
                          getFileTypeText(file)
                        }}</span>
                        <span class="file-size" v-if="file.size">
                          {{ formatFileSize(file.size) }}
                        </span>
                      </div>
                    </div>
                  </div>
                  <div class="file-actions" v-if="file.url">
                    <el-button
                      type="text"
                      size="mini"
                      @click="handleDownload(file)"
                    >
                      下载
                    </el-button>
                    <el-button
                      v-if="canPreview(file)"
                      type="text"
                      size="mini"
                      @click="handlePreview(file)"
                    >
                      预览
                    </el-button>
                  </div>
                </el-card>
              </template>
            </div>
          </div>
        </div>
@@ -393,6 +459,7 @@
        </el-descriptions-item>
        <!-- 会议附件 -->
        我理解您的需求,您希望将会议附件中的图片文件以el-image的预览图形式展示,其他文件类型保持原有的展示方式。让我修改这部分代码:
        <el-descriptions-item label="会议附件" :span="2">
          <div class="detail-attachments">
            <div
@@ -402,37 +469,94 @@
              "
              class="attachment-grid"
            >
              <!-- 图片附件 - 使用 el-image 预览形式 -->
              <div
                v-for="file in parseAttachments(currentRecord.attachment)"
                :key="file.id || file.name"
                class="attachment-card"
              >
                <el-card shadow="hover" class="file-card">
                  <div class="file-content">
                    <i
                      :class="getFileIcon(file.type || file.name)"
                      class="file-icon"
                    ></i>
                    <div class="file-info">
                      <div class="file-name" :title="file.name">
                        {{ file.name }}
                      </div>
                      <div class="file-meta">
                        <span class="file-size" v-if="file.size">{{
                          formatFileSize(file.size)
                        }}</span>
                      </div>
                <template v-if="isImageFile(file)">
                  <!-- 图片使用 el-image 预览 -->
                  <el-image
                    class="image-attachment"
                    :src="getFileUrl(file)"
                    :preview-src-list="getImagePreviewList(file)"
                    fit="cover"
                    :style="{ width: imageSize, height: imageSize }"
                    lazy
                  >
                    <div slot="error" class="image-error">
                      <i class="el-icon-picture-outline"></i>
                      <span>加载失败</span>
                    </div>
                    <div slot="placeholder" class="image-loading">
                      <i class="el-icon-loading"></i>
                    </div>
                  </el-image>
                  <div class="image-info">
                    <div class="file-name" :title="file.name">
                      {{ file.name }}
                    </div>
                    <div class="file-actions">
                      <el-button
                        type="text"
                        size="mini"
                        @click="handleDownload(file)"
                        icon="el-icon-download"
                        title="下载"
                      />
                      <el-button
                        type="text"
                        size="mini"
                        @click="handlePreview(file)"
                        icon="el-icon-view"
                        title="预览"
                      />
                    </div>
                  </div>
                  <div class="file-actions" v-if="file.url">
                    <el-button
                      type="text"
                      size="mini"
                      @click="handleDownload(file)"
                      >下载</el-button
                    >
                  </div>
                </el-card>
                </template>
                <template v-else>
                  <!-- 非图片文件保持原有样式 -->
                  <el-card shadow="hover" class="file-card">
                    <div class="file-content">
                      <i
                        :class="getFileIcon(file.type || file.name)"
                        class="file-icon"
                      ></i>
                      <div class="file-info">
                        <div class="file-name" :title="file.name">
                          {{ file.name }}
                        </div>
                        <div class="file-meta">
                          <span class="file-type">{{
                            getFileTypeText(file)
                          }}</span>
                          <span class="file-size" v-if="file.size">
                            {{ formatFileSize(file.size) }}
                          </span>
                        </div>
                      </div>
                    </div>
                    <div class="file-actions" v-if="file.url">
                      <el-button
                        type="text"
                        size="mini"
                        @click="handleDownload(file)"
                      >
                        下载
                      </el-button>
                      <el-button
                        v-if="canPreview(file)"
                        type="text"
                        size="mini"
                        @click="handlePreview(file)"
                      >
                        预览
                      </el-button>
                    </div>
                  </el-card>
                </template>
              </div>
            </div>
            <div v-else class="no-attachment">
@@ -474,7 +598,7 @@
    <el-dialog
      :title="`${isEditing ? '编辑' : '新增'}会议`"
      :visible.sync="editDialogVisible"
      width="700px"
      width="800px"
      :before-close="handleEditClose"
    >
      <el-form
@@ -502,16 +626,16 @@
          </el-select>
        </el-form-item>
        <el-form-item label="会议地点" prop="locationId">
        <el-form-item label="会议地点" prop="locationName">
          <el-select
            v-model="editForm.locationId"
            v-model="editForm.locationName"
            placeholder="请选择会议地点"
            style="width: 100%"
          >
            <el-option label="第一会议室" :value="1" />
            <el-option label="第二会议室" :value="2" />
            <el-option label="第三会议室" :value="3" />
            <el-option label="线上会议" :value="4" />
            <el-option label="第一会议室" value="第一会议室" />
            <el-option label="第二会议室" value="第二会议室" />
            <el-option label="第三会议室" value="第三会议室" />
            <el-option label="线上会议" value="线上会议" />
          </el-select>
        </el-form-item>
@@ -573,47 +697,84 @@
          />
        </el-form-item>
        <!-- 修改会议附件部分 -->
        <el-form-item label="会议附件">
          <div class="attachment-upload-section">
            <div class="upload-tip">上传会议相关资料文件</div>
            <el-upload
              class="attachment-upload"
              action="#"
              :auto-upload="false"
              :on-change="handleAttachmentChange"
              :file-list="editForm.attachmentFiles"
              :limit="10"
              multiple
            >
              <el-button size="small" type="primary" icon="el-icon-upload">
                上传会议附件
              </el-button>
              <div slot="tip" class="el-upload__tip">
                支持文档、图片等格式,单个文件不超过20MB
              </div>
            </el-upload>
            <div
              v-if="
                editForm.attachmentFiles && editForm.attachmentFiles.length > 0
              "
              class="uploaded-files"
            >
              <div
                v-for="(file, index) in editForm.attachmentFiles"
                :key="file.uid || index"
                class="file-item"
          <div class="attachment-section">
            <div class="attachment-header">
              <i class="el-icon-paperclip"></i>
              <span class="attachment-title">会议附件上传</span>
              <span class="attachment-tip"
                >支持上传文档、图片等文件 (最多{{ attachmentLimit }}个)</span
              >
                <span class="file-name">{{ file.name }}</span>
                <el-button
                  type="text"
                  size="mini"
                  @click="handleRemoveAttachmentFile(index)"
                  style="color: #F56C6C;"
                >
                  删除
                </el-button>
              </div>
            </div>
            <!-- 使用 UploadAttachment 组件 -->
            <UploadAttachment
              ref="uploadAttachment"
              :file-list="attachmentFileList"
              :limit="attachmentLimit"
              :accept="attachmentAccept"
              @change="handleAttachmentChange"
              @upload-success="handleAttachmentUploadSuccess"
              @upload-error="handleAttachmentUploadError"
              @remove="handleAttachmentRemove"
            />
          </div>
          <!-- 会议附件列表 -->
          <div
            class="attachment-list"
            v-if="editForm.attachment && editForm.attachment.length > 0"
          >
            <div class="list-title">
              已上传附件 ({{ editForm.attachment.length }})
            </div>
            <el-table
              :data="editForm.attachment"
              style="width: 100%"
              size="small"
              border
            >
              <el-table-column label="文件名" min-width="200">
                <template #default="scope">
                  <i
                    class="el-icon-document"
                    style="margin-right: 8px; color: #409EFF;"
                  ></i>
                  <span class="file-name">{{ scope.row.name }}</span>
                </template>
              </el-table-column>
              <el-table-column label="文件类型" width="100">
                <template #default="scope">
                  <el-tag size="small">{{
                    getFileType(scope.row.name)
                  }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="文件大小" width="100">
                <template #default="scope">
                  <span>{{ formatFileSize(scope.row.size) }}</span>
                </template>
              </el-table-column>
              <el-table-column label="操作" width="200">
                <template #default="scope">
                  <el-button
                    size="mini"
                    type="primary"
                    @click="handlePreviewAttachment(scope.row)"
                  >
                    预览
                  </el-button>
                  <el-button
                    size="mini"
                    type="danger"
                    @click="handleRemoveMeetingAttachment(scope.$index)"
                  >
                    删除
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </el-form-item>
@@ -633,25 +794,86 @@
          </div>
        </el-form-item>
        <!-- 修改会议纪要附件部分 -->
        <el-form-item label="纪要附件">
          <div class="attachment-upload-section">
            <div class="upload-tip">上传与会议纪要相关的补充材料</div>
            <el-upload
              class="minutes-upload"
              action="#"
              :auto-upload="false"
              :on-change="handleMinutesAttachmentChange"
              :file-list="editForm.recordattachmentFiles"
              :limit="5"
              multiple
          <div class="attachment-section">
            <div class="attachment-header">
              <i class="el-icon-paperclip"></i>
              <span class="attachment-title">纪要附件上传</span>
              <span class="attachment-tip"
                >支持上传与纪要相关的补充材料 (最多{{
                  minutesAttachmentLimit
                }}个)</span
              >
            </div>
            <UploadAttachment
              ref="uploadMinutesAttachment"
              :file-list="minutesAttachmentFileList"
              :limit="minutesAttachmentLimit"
              :accept="minutesAttachmentAccept"
              @change="handleMinutesAttachmentChange"
              @upload-success="handleMinutesUploadSuccess"
              @upload-error="handleMinutesUploadError"
              @remove="handleMinutesAttachmentRemove"
            />
          </div>
          <div
            class="attachment-list"
            v-if="
              editForm.recordattachment && editForm.recordattachment.length > 0
            "
          >
            <div class="list-title">
              已上传纪要附件 ({{ editForm.recordattachment.length }})
            </div>
            <el-table
              :data="editForm.recordattachment"
              style="width: 100%"
              size="small"
              border
            >
              <el-button size="small" type="primary" icon="el-icon-upload">
                上传纪要附件
              </el-button>
              <div slot="tip" class="el-upload__tip">
                支持图片、文档等格式,单个文件不超过10MB
              </div>
            </el-upload>
              <el-table-column label="文件名" min-width="200">
                <template #default="scope">
                  <i
                    class="el-icon-document"
                    style="margin-right: 8px; color: #409EFF;"
                  ></i>
                  <span class="file-name">{{ scope.row.name }}</span>
                </template>
              </el-table-column>
              <el-table-column label="文件类型" width="100">
                <template #default="scope">
                  <el-tag size="small">{{
                    getFileType(scope.row.name)
                  }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="文件大小" width="100">
                <template #default="scope">
                  <span>{{ formatFileSize(scope.row.size) }}</span>
                </template>
              </el-table-column>
              <el-table-column label="操作" width="200">
                <template #default="scope">
                  <el-button
                    size="mini"
                    type="primary"
                    @click="handlePreviewMinutesAttachment(scope.row)"
                  >
                    预览
                  </el-button>
                  <el-button
                    size="mini"
                    type="danger"
                    @click="handleRemoveMinutesAttachment(scope.$index)"
                  >
                    删除
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </el-form-item>
@@ -672,6 +894,13 @@
        </el-button>
      </span>
    </el-dialog>
    <!-- 文件预览弹窗 -->
    <FilePreviewDialog
      :visible="previewVisible"
      :file="currentPreviewFile"
      @close="previewVisible = false"
      @download="handleDownload"
    />
  </div>
</template>
@@ -684,9 +913,15 @@
  meetingDel,
  exporremeeting
} from "@/api/officeManagementApi";
import UploadAttachment from "@/components/UploadAttachment";
import FilePreviewDialog from "@/components/FilePreviewDialog";
import dayjs from "dayjs";
export default {
  name: "MeetingManagement",
  components: {
    UploadAttachment,
    FilePreviewDialog
  },
  data() {
    return {
      // 查询参数
@@ -716,6 +951,7 @@
      isEditing: false,
      // 表格数据
      tableData: [],
      imageSize: "100px", // 图片显示大小
      // 编辑表单数据
      editForm: this.getDefaultFormData(),
      // 表单验证规则
@@ -742,7 +978,24 @@
        reminderMinutes: [
          { required: true, message: "请输入提前提醒时间", trigger: "blur" }
        ]
      }
      },
      // 附件相关配置
      attachmentLimit: 10,
      minutesAttachmentLimit: 5,
      attachmentAccept:
        ".pdf,.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.zip,.rar",
      minutesAttachmentAccept: ".pdf,.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx",
      // 文件预览相关
      previewVisible: false,
      currentPreviewFile: null,
      // 附件文件列表
      attachmentFileList: [],
      minutesAttachmentFileList: [],
      // 编辑表单数据 - 修改默认值
      editForm: this.getDefaultFormData()
    };
  },
  mounted() {
@@ -783,7 +1036,111 @@
        this.loading = false;
      }
    },
    // 修改获取文件类型方法
    getFileType(fileName) {
      if (!fileName) return "other";
      const extension = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"];
      const pdfTypes = ["pdf"];
      const officeTypes = ["doc", "docx", "xls", "xlsx", "ppt", "pptx"];
      const zipTypes = ["zip", "rar"];
      if (imageTypes.includes(extension)) return "image";
      if (pdfTypes.includes(extension)) return "pdf";
      if (officeTypes.includes(extension)) return "office";
      if (zipTypes.includes(extension)) return "archive";
      return "other";
    },
    // 判断是否为图片文件
    isImageFile(file) {
      if (!file || !file.name) return false;
      const imageExtensions = [
        "jpg",
        "jpeg",
        "png",
        "gif",
        "bmp",
        "webp",
        "svg"
      ];
      const extension = file.name
        .split(".")
        .pop()
        .toLowerCase();
      return imageExtensions.includes(extension);
    },
    // 获取文件URL
    getFileUrl(file) {
      return file.url || file.path || file.fileUrl || "";
    },
    // 获取图片预览列表
    getImagePreviewList(file) {
      const url = this.getFileUrl(file);
      if (!url) return [];
      return [url];
    },
    // 获取文件类型文本
    getFileTypeText(file) {
      if (!file.name) return "文件";
      const ext = file.name
        .split(".")
        .pop()
        .toLowerCase();
      const typeMap = {
        pdf: "PDF文档",
        doc: "Word文档",
        docx: "Word文档",
        xls: "Excel表格",
        xlsx: "Excel表格",
        ppt: "PPT演示",
        pptx: "PPT演示",
        zip: "压缩包",
        rar: "压缩包",
        txt: "文本文档",
        jpg: "图片",
        jpeg: "图片",
        png: "图片",
        gif: "图片"
      };
      return typeMap[ext] || "文件";
    },
    // 判断文件是否可以预览
    canPreview(file) {
      if (!file.name) return false;
      const previewableExtensions = ["pdf", "jpg", "jpeg", "png", "gif", "txt"];
      const extension = file.name
        .split(".")
        .pop()
        .toLowerCase();
      return previewableExtensions.includes(extension);
    },
    // 文件预览
    handlePreview(file) {
      this.currentPreviewFile = {
        fileName: file.name,
        fileUrl: this.getFileUrl(file),
        fileType: this.getFileType(file.name)
      };
      this.previewVisible = true;
    },
    // 附件转换为上传文件列表
    parseAttachmentToFileList(attachments) {
      if (!attachments || !Array.isArray(attachments)) return [];
      return attachments.map((item, index) => ({
        uid: item.id || `attachment-${index}-${Date.now()}`,
        name: item.name || item.fileName,
        url: item.url || item.path || item.fileUrl,
        size: item.size,
        status: "success"
      }));
    },
    // 获取文件图标
    getFileIcon(fileName) {
      if (!fileName) return "el-icon-document";
@@ -917,9 +1274,9 @@
      }
    },
    // 格式化文件大小
    // 文件大小格式化
    formatFileSize(bytes) {
      if (!bytes) return "0 B";
      if (!bytes || bytes === 0) return "0 B";
      const k = 1024;
      const sizes = ["B", "KB", "MB", "GB"];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
@@ -950,7 +1307,20 @@
      try {
        const response = await meetingInfo(id);
        if (response.code === 200) {
          this.currentRecord = response.data || {};
          const data = response.data || {};
          // 解析附件
          if (data.attachment && typeof data.attachment === "string") {
            data.attachment = this.parseAttachments(data.attachment);
          }
          if (
            data.recordattachment &&
            typeof data.recordattachment === "string"
          ) {
            data.recordattachment = this.parseAttachments(
              data.recordattachment
            );
          }
          this.currentRecord = data;
          this.detailDialogVisible = true;
        } else {
          this.$message.error(response.msg || "获取详情失败");
@@ -965,6 +1335,8 @@
    handleAdd() {
      this.isEditing = false;
      this.editForm = this.getDefaultFormData();
      this.attachmentFileList = [];
      this.minutesAttachmentFileList = [];
      this.editDialogVisible = true;
      this.$nextTick(() => {
        this.$refs.editForm && this.$refs.editForm.clearValidate();
@@ -977,21 +1349,26 @@
        const response = await meetingInfo(id);
        if (response.code === 200) {
          this.isEditing = true;
          this.currentRecord = response.data || {};
          this.editForm = { ...response.data };
          const data = response.data || {};
          // 解析附件
          if (this.editForm.attachment) {
            this.editForm.attachmentFiles = this.parseAttachments(
              this.editForm.attachment
          if (data.attachment) {
            data.attachment = this.parseAttachments(data.attachment);
            this.attachmentFileList = this.parseAttachmentToFileList(
              data.attachment
            );
          }
          if (this.editForm.recordattachment) {
            this.editForm.recordattachmentFiles = this.parseAttachments(
              this.editForm.recordattachment
          if (data.recordattachment) {
            data.recordattachment = this.parseAttachments(
              data.recordattachment
            );
            this.minutesAttachmentFileList = this.parseAttachmentToFileList(
              data.recordattachment
            );
          }
          this.currentRecord = data;
          this.editForm = { ...data };
          this.editDialogVisible = true;
          this.detailDialogVisible = false;
          this.$nextTick(() => {
@@ -1070,33 +1447,156 @@
      }
    },
    // 文件上传处理
    handleAttachmentChange(file, fileList) {
      this.editForm.attachmentFiles = fileList;
    // 附件变化处理
    handleAttachmentChange(fileList) {
      this.attachmentFileList = fileList;
    },
    handleMinutesAttachmentChange(file, fileList) {
      this.editForm.recordattachmentFiles = fileList;
    handleMinutesAttachmentChange(fileList) {
      this.minutesAttachmentFileList = fileList;
    },
    // 附件上传成功处理
    handleAttachmentUploadSuccess({ file, fileList, response }) {
      if (response && response.code === 200) {
        if (!this.editForm.attachment) {
          this.editForm.attachment = [];
        }
        console.log(file, "file");
        console.log(response, "response");
        const attachmentObj = {
          name: file.name,
          url: response.data || file.url,
          fileName: file.name,
          size: file.size || 0,
          type: this.getFileExtension(file.name),
          createTime: dayjs().format("YYYY-MM-DD HH:mm:ss")
        };
        console.log(this.editForm.attachment, "this.editForm.attachment");
        this.editForm.attachment.push(attachmentObj);
        console.log(this.editForm.attachment, "this.editForm.attachment");
        this.$message.success("会议附件上传成功");
      }
    },
    handleMinutesUploadSuccess({ file, fileList, response }) {
      if (response && response.code === 200) {
        if (!this.editForm.recordattachment) {
          this.editForm.recordattachment = [];
        }
        const attachmentObj = {
          name: file.name,
          url: response.data || file.url,
          fileName: file.name,
          size: file.size || 0,
          type: this.getFileExtension(file.name),
          createTime: dayjs().format("YYYY-MM-DD HH:mm:ss")
        };
        this.editForm.recordattachment.push(attachmentObj);
        this.$message.success("纪要附件上传成功");
      }
    },
    // 附件上传错误处理
    handleAttachmentUploadError({ file, fileList, error }) {
      console.error("会议附件上传失败:", error);
      this.$message.error("文件上传失败,请重试");
    },
    // 删除已上传的文件
    handleRemoveAttachmentFile(index) {
      this.editForm.attachmentFiles.splice(index, 1);
    handleMinutesUploadError({ file, fileList, error }) {
      console.error("纪要附件上传失败:", error);
      this.$message.error("文件上传失败,请重试");
    },
    // 附件移除处理
    handleAttachmentRemove(file) {
      if (file.url && this.editForm.attachment) {
        const index = this.editForm.attachment.findIndex(
          item =>
            item.url === file.url ||
            item.path === file.url ||
            item.fileUrl === file.url
        );
        if (index > -1) {
          this.editForm.attachment.splice(index, 1);
        }
      }
    },
    handleMinutesAttachmentRemove(file) {
      if (file.url && this.editForm.recordattachment) {
        const index = this.editForm.recordattachment.findIndex(
          item =>
            item.url === file.url ||
            item.path === file.url ||
            item.fileUrl === file.url
        );
        if (index > -1) {
          this.editForm.recordattachment.splice(index, 1);
        }
      }
    },
    // 手动删除附件
    handleRemoveMeetingAttachment(index) {
      if (this.editForm.attachment && this.editForm.attachment[index]) {
        this.editForm.attachment.splice(index, 1);
        this.attachmentFileList.splice(index, 1);
        this.$message.success("会议附件删除成功");
      }
    },
    handleRemoveMinutesAttachment(index) {
      if (
        this.editForm.recordattachment &&
        this.editForm.recordattachment[index]
      ) {
        this.editForm.recordattachment.splice(index, 1);
        this.minutesAttachmentFileList.splice(index, 1);
        this.$message.success("纪要附件删除成功");
      }
    },
    // 文件预览
    handlePreviewAttachment(file) {
      this.currentPreviewFile = {
        fileName: file.name || file.fileName,
        fileUrl: file.url || file.path || file.fileUrl,
        fileType: this.getFileType(file.name)
      };
      this.previewVisible = true;
    },
    handlePreviewMinutesAttachment(file) {
      this.currentPreviewFile = {
        fileName: file.name || file.fileName,
        fileUrl: file.url || file.path || file.fileUrl,
        fileType: this.getFileType(file.name)
      };
      this.previewVisible = true;
    },
    // 文件下载
    handleDownload(file) {
      if (file.url) {
        const link = document.createElement("a");
        link.href = file.url;
        link.download = file.name;
        link.download = file.name || file.fileName || "download";
        link.style.display = "none";
        document.body.appendChild(link);
        link.click();
        this.$message.success(`开始下载: ${file.name}`);
        document.body.removeChild(link);
        this.$message.success("开始下载文件");
      } else {
        this.$message.warning("文件地址不存在");
        this.$message.warning("文件地址不存在,无法下载");
      }
    },
    // 获取文件扩展名
    getFileExtension(filename) {
      if (!filename) return "";
      return filename
        .split(".")
        .pop()
        .toLowerCase();
    },
    // 保存记录
    async handleSave() {
      try {
@@ -1113,34 +1613,55 @@
          }
        }
        // 检查是否有未上传完成的文件
        const pendingFiles = [
          ...this.attachmentFileList.filter(item => item.status !== "success"),
          ...this.minutesAttachmentFileList.filter(
            item => item.status !== "success"
          )
        ];
        if (pendingFiles.length > 0) {
          this.$message.warning(
            "还有文件未上传完成,请先上传所有文件或移除未上传的文件"
          );
          return;
        }
        this.saveLoading = true;
        // 处理附件
        // 准备数据
        const formData = { ...this.editForm };
        if (formData.attachmentFiles) {
        // 处理附件为JSON字符串
        if (formData.attachment && Array.isArray(formData.attachment)) {
          formData.attachment = JSON.stringify(
            formData.attachmentFiles.map(file => ({
              name: file.name,
              url: file.url || "",
              size: file.size || 0,
              type: file.type || file.name.split(".").pop()
            formData.attachment.map(item => ({
              name: item.name || item.fileName,
              url: item.url || item.path || item.fileUrl,
              size: item.size || 0,
              type:
                item.type || this.getFileExtension(item.name || item.fileName)
            }))
          );
        }
        if (formData.recordattachmentFiles) {
        if (
          formData.recordattachment &&
          Array.isArray(formData.recordattachment)
        ) {
          formData.recordattachment = JSON.stringify(
            formData.recordattachmentFiles.map(file => ({
              name: file.name,
              url: file.url || "",
              size: file.size || 0,
              type: file.type || file.name.split(".").pop()
            formData.recordattachment.map(item => ({
              name: item.name || item.fileName,
              url: item.url || item.path || item.fileUrl,
              size: item.size || 0,
              type:
                item.type || this.getFileExtension(item.name || item.fileName)
            }))
          );
        }
        // 清理文件列表字段
        // 清理临时字段
        delete formData.attachmentFiles;
        delete formData.recordattachmentFiles;
@@ -1179,6 +1700,8 @@
      this.editDialogVisible = false;
      this.currentRecord = {};
      this.editForm = this.getDefaultFormData();
      this.attachmentFileList = [];
      this.minutesAttachmentFileList = [];
      this.$nextTick(() => {
        this.$refs.editForm && this.$refs.editForm.clearValidate();
      });
@@ -1216,7 +1739,7 @@
      console.log("排序变化:", sort);
    },
    // 获取默认表单数据
    // 默认表单数据
    getDefaultFormData() {
      return {
        id: null,
@@ -1227,19 +1750,14 @@
        endTime: "",
        summary: "",
        content: "",
        attachment: null,
        attachmentFiles: [],
        status: 1, // 默认待开始
        isRecurring: 0,
        recurringPattern: null,
        parentMeetingId: null,
        reminderMinutes: 30,
        attachment: [], // 改为数组
        recordcontent: "",
        recordattachment: null,
        recordattachmentFiles: [],
        recordattachment: [], // 改为数组
        recorderBy: "",
        remark: "",
        approvalStatus: 0 // 默认待审核
        status: 1,
        reminderMinutes: 30,
        approvalStatus: 0
      };
    }
  }
@@ -1247,6 +1765,170 @@
</script>
<style scoped>
.meeting-management {
  padding: 20px;
}
.attachment-section {
  margin-bottom: 16px;
}
.attachment-header {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
  padding: 8px 0;
  border-bottom: 1px solid #ebeef5;
}
.attachment-title {
  font-weight: bold;
  margin: 0 8px;
}
.attachment-tip {
  font-size: 12px;
  color: #909399;
}
.attachment-list {
  margin-top: 16px;
}
.list-title {
  font-weight: bold;
  margin-bottom: 12px;
  color: #303133;
}
/* 图片附件样式 */
.image-attachment {
  border-radius: 4px;
  border: 1px solid #ebeef5;
  cursor: pointer;
  transition: all 0.3s;
  margin-bottom: 8px;
}
.image-attachment:hover {
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  transform: translateY(-2px);
}
.image-info {
  text-align: center;
  padding: 4px 0;
}
.image-info .file-name {
  font-size: 12px;
  color: #606266;
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100px;
}
.image-info .file-actions {
  display: flex;
  justify-content: center;
  gap: 8px;
}
.image-loading,
.image-error {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100%;
  color: #909399;
  font-size: 12px;
}
.image-loading i,
.image-error i {
  font-size: 20px;
  margin-bottom: 4px;
}
/* 文件卡片样式 */
.file-card {
  width: 100%;
  min-height: 60px;
  margin-bottom: 8px;
}
.file-content {
  display: flex;
  align-items: center;
}
.file-icon {
  font-size: 24px;
  margin-right: 12px;
  color: #409eff;
  flex-shrink: 0;
}
.file-info {
  flex: 1;
  min-width: 0;
}
.file-name {
  font-size: 13px;
  font-weight: 500;
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.file-meta {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: #909399;
}
.file-actions {
  margin-top: 8px;
  text-align: right;
}
/* 附件网格布局 */
.attachment-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 16px;
}
.attachment-card {
  display: flex;
  flex-direction: column;
}
/* 响应式调整 */
@media (max-width: 768px) {
  .attachment-grid {
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
    gap: 12px;
  }
  .image-attachment {
    width: 80px !important;
    height: 80px !important;
  }
}
.file-name {
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 其他样式保持不变 */
.meeting-management {
  padding: 20px;
}
@@ -1276,6 +1958,7 @@
.attachment-item {
  margin-bottom: 8px;
}
.minutes-content {
  margin-bottom: 16px;
}
@@ -1291,6 +1974,7 @@
    margin: 0 -20px;
  }
}
/* 响应式设计 */
@media (max-width: 768px) {
  .page-header {
@@ -1304,6 +1988,7 @@
    justify-content: space-between;
  }
}
/* 新增附件相关样式 */
.attachment-upload-section {
  border: 1px solid #ebeef5;
src/views/business/appear/caseDetail.vue
@@ -13,10 +13,7 @@
          caseData.name || "-"
        }}</el-descriptions-item>
        <el-descriptions-item label="性别">
          <dict-tag
            :options="dict.type.sys_user_sex"
            :value="caseData.sex ? parseInt(caseData.sex) : ''"
          />
          <dict-tag :options="dict.type.sys_user_sex" :value="caseData.sex" />
        </el-descriptions-item>
        <el-descriptions-item label="年龄">
          {{ caseData.age || "-"
@@ -130,6 +127,12 @@
        <el-descriptions-item label="部门名称">{{
          caseData.deptName || "-"
        }}</el-descriptions-item>
        <el-descriptions-item label="部门编号">{{
          caseData.deptNo || "-"
        }}</el-descriptions-item>
        <el-descriptions-item label="上报医院">{{
          caseData.toHospital || "-"
        }}</el-descriptions-item>
      </el-descriptions>
    </el-card>
@@ -160,7 +163,7 @@
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="报告时间">{{
          formatDateTime(caseData.reporttime)
          formatDateTime(caseData.createTime)
        }}</el-descriptions-item>
        <el-descriptions-item label="是否终止案例">
          {{ caseData.terminationCase === "1" ? "已终止" : "进行中" }}
@@ -169,46 +172,112 @@
    </el-card>
    <!-- 附件信息模块 -->
    <el-card class="detail-section" v-if="attachmentList.length > 0">
    <el-card
      class="detail-section"
      v-if="caseData.annexfilesList && caseData.annexfilesList.length > 0"
    >
      <div slot="header" class="section-header">
        <span class="section-title">附件信息</span>
      </div>
      <el-table :data="attachmentList" style="width: 100%">
        <el-table-column label="文件名" width="300">
          <template slot-scope="scope">
            <i class="el-icon-document" style="margin-right: 8px;"></i>
            <span>{{ scope.row.fileName }}</span>
          </template>
        </el-table-column>
        <el-table-column label="文件类型" width="120">
          <template slot-scope="scope">
            <el-tag size="small">{{ scope.row.fileType }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="大小" width="100">
          <template slot-scope="scope">
            <span>{{ formatFileSize(scope.row.fileSize) }}</span>
          </template>
        </el-table-column>
        <el-table-column label="上传时间" width="180">
          <template slot-scope="scope">
            <span>{{ scope.row.uploadTime }}</span>
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template slot-scope="scope">
            <el-button size="mini" @click="handlePreview(scope.row)"
              >预览</el-button
            >
            <el-button
              size="mini"
              type="success"
              @click="handleDownload(scope.row)"
              >下载</el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <div class="detail-attachments">
        <div class="attachment-grid">
          <div
            v-for="file in caseData.annexfilesList"
            :key="file.id || file.fileName"
            class="attachment-card"
          >
            <template v-if="isImageFile(file.fileName || file.path)">
              <!-- 图片使用 el-image 预览 -->
              <el-image
                class="image-attachment"
                :src="getFileUrl(file)"
                :preview-src-list="getImagePreviewList(file)"
                fit="cover"
                :style="{ width: '120px', height: '120px' }"
                lazy
              >
                <div slot="error" class="image-error">
                  <i class="el-icon-picture-outline"></i>
                  <span>加载失败</span>
                </div>
                <div slot="placeholder" class="image-loading">
                  <i class="el-icon-loading"></i>
                </div>
              </el-image>
              <div class="image-info">
                <div class="file-name" :title="file.fileName">
                  {{ file.fileName }}
                </div>
                <div class="file-actions">
                  <el-button
                    type="text"
                    size="mini"
                    @click="handleDownload(file)"
                    icon="el-icon-download"
                    title="下载"
                  />
                  <el-button
                    type="text"
                    size="mini"
                    @click="handlePreview(file)"
                    icon="el-icon-view"
                    title="预览"
                  />
                </div>
              </div>
            </template>
            <template v-else>
              <!-- 非图片文件使用卡片样式 -->
              <el-card shadow="hover" class="file-card">
                <div class="file-content">
                  <i :class="getFileIcon(file.fileName)" class="file-icon"></i>
                  <div class="file-info">
                    <div class="file-name" :title="file.fileName">
                      {{ file.fileName }}
                    </div>
                    <div class="file-meta">
                      <span class="file-type">{{
                        getFileTypeText(file.fileName)
                      }}</span>
                      <span v-if="file.createTime" class="file-time">
                        {{ formatDateTime(file.createTime) }}
                      </span>
                    </div>
                  </div>
                </div>
                <div class="file-actions">
                  <el-button
                    type="text"
                    size="mini"
                    @click="handleDownload(file)"
                  >
                    下载
                  </el-button>
                  <el-button
                    v-if="canPreview(file.fileName)"
                    type="text"
                    size="mini"
                    @click="handlePreview(file)"
                  >
                    预览
                  </el-button>
                </div>
              </el-card>
            </template>
          </div>
        </div>
      </div>
    </el-card>
    <!-- 备注信息 -->
    <el-card class="detail-section" v-if="caseData.remark">
      <div slot="header" class="section-header">
        <span class="section-title">备注信息</span>
      </div>
      <div class="remark-content">
        {{ caseData.remark }}
      </div>
    </el-card>
    <!-- 审批信息模块(状态为已同意或已驳回时显示) -->
@@ -225,133 +294,108 @@
            {{ caseData.reportStatus === "3" ? "已同意" : "已驳回" }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="审批时间">{{
          formatDateTime(caseData.updateTime)
        }}</el-descriptions-item>
        <el-descriptions-item label="审批人">{{
          caseData.updateBy || "-"
        }}</el-descriptions-item>
        <el-descriptions-item label="审批时间">{{
          formatDateTime(caseData.updateTime)
        }}</el-descriptions-item>
        <el-descriptions-item label="审批意见">{{
          caseData.remark || "无"
        }}</el-descriptions-item>
      </el-descriptions>
    </el-card>
    <!-- PDF预览弹窗 -->
    <el-dialog
      :title="previewTitle"
      :append-to-body="true"
      :visible.sync="pdfPreviewVisible"
      width="90%"
      top="5vh"
      :close-on-click-modal="true"
      class="pdf-preview-dialog"
      @close="handlePdfDialogClose"
    <el-card
      class="detail-section"
      v-if="
        caseData.isTransport == '2' &&
          caseData.serviceTransport &&
          caseData.serviceTransport.length > 0
      "
    >
      <div class="pdf-preview-container" v-loading="pdfLoading">
        <!-- PDF控制工具栏 -->
        <div class="pdf-toolbar">
          <el-button-group>
            <el-button
              size="mini"
              @click="changePage(currentPage - 1)"
              :disabled="currentPage <= 1"
              icon="el-icon-arrow-left"
            >
              上一页
            </el-button>
            <el-button size="mini" disabled>
              第 {{ currentPage }} 页 / 共 {{ pageCount }} 页
            </el-button>
            <el-button
              size="mini"
              @click="changePage(currentPage + 1)"
              :disabled="currentPage >= pageCount"
              icon="el-icon-arrow-right"
            >
              下一页
            </el-button>
          </el-button-group>
          <el-button-group class="zoom-controls">
            <el-button size="mini" @click="zoomOut" :disabled="scale <= 50">
              <i class="el-icon-zoom-out"></i> 缩小
            </el-button>
            <el-button size="mini" disabled> {{ scale }}% </el-button>
            <el-button size="mini" @click="zoomIn" :disabled="scale >= 200">
              <i class="el-icon-zoom-in"></i> 放大
            </el-button>
            <el-button size="mini" @click="resetZoom">
              <i class="el-icon-refresh-left"></i> 重置
            </el-button>
          </el-button-group>
          <el-button
            size="mini"
            type="success"
            @click="downloadPdf(currentFile)"
            icon="el-icon-download"
          >
            下载
          </el-button>
        </div>
        <!-- PDF渲染区域 -->
        <div class="pdf-viewport">
          <pdf
            ref="pdf"
            :src="pdfUrl"
            :page="currentPage"
            :rotate="pageRotate"
            @num-pages="pageCount = $event"
            @page-loaded="currentPage = $event"
            @loaded="loadPdfHandler"
            @error="pdfErrorHandler"
            :style="{
              width: scale + '%',
              transform: 'scale(' + scale / 100 + ')',
              transformOrigin: '0 0'
            }"
          ></pdf>
        </div>
      <div slot="header" class="section-header">
        <span class="section-title">转运信息</span>
        <el-button
          v-if="caseData.reportStatus === '3'"
          type="primary"
          size="small"
          icon="el-icon-truck"
          @click="handleViewTransport"
        >
          查看转运详情
        </el-button>
      </div>
    </el-dialog>
    <!-- 图片预览弹窗 -->
    <el-dialog
      :append-to-body="true"
      :title="previewTitle"
      :visible.sync="imagePreviewVisible"
      width="60%"
      top="10vh"
      :close-on-click-modal="true"
      <el-descriptions :column="2" border>
        <el-descriptions-item
          v-for="transport in caseData.serviceTransport"
          :key="transport.id"
        >
          <!-- 转运单列表 -->
          <div class="transport-info">
            <div class="transport-item">
              <strong>转运单号:</strong> {{ transport.reportId || "-" }}
            </div>
            <div class="transport-item">
              <strong>患者姓名:</strong> {{ transport.patName || "-" }}
            </div>
            <div class="transport-item">
              <strong>出发地点:</strong>
              {{ transport.transportStartPlace || "-" }}
            </div>
            <div class="transport-item">
              <strong>出发时间:</strong>
              {{ formatDateTime(transport.transportStartTime) }}
            </div>
            <div class="transport-item">
              <strong>转运状态:</strong>
              <el-tag :type="getTransportStatusTag(transport)" size="small">
                {{ getTransportStatusText(transport) }}
              </el-tag>
            </div>
            <div class="transport-item">
              <strong>负责协调员:</strong> {{ transport.contactPerson || "-" }}
            </div>
            <div class="transport-item" v-if="transport.remark">
              <strong>备注:</strong> {{ transport.remark }}
            </div>
          </div>
        </el-descriptions-item>
      </el-descriptions>
    </el-card>
    <!-- 无转运信息但需要转运的提示 -->
    <el-card
      class="detail-section"
      v-else-if="caseData.isTransport === '2' && caseData.reportStatus === '3'"
    >
      <div class="image-preview-container">
        <img :src="previewUrl" alt="预览图片" class="preview-image" />
      <div slot="header" class="section-header">
        <span class="section-title">转运信息</span>
        <el-button
          type="primary"
          size="small"
          icon="el-icon-truck"
          @click="handleCreateTransport"
        >
          创建转运单
        </el-button>
      </div>
    </el-dialog>
    <!-- 不支持预览的文件类型 -->
    <el-dialog
      :title="previewTitle"
      :visible.sync="unsupportedPreviewVisible"
      width="400px"
      :close-on-click-modal="true"
    >
      <div class="unsupported-preview">
        <el-alert
          title="该文件格式不支持在线预览,请下载后查看"
          type="warning"
          show-icon
          :closable="false"
        />
        <div style="text-align: center; margin-top: 20px;">
          <el-button type="primary" @click="handleDownload(currentFile)">
            <i class="el-icon-download"></i> 下载文件
          </el-button>
        </div>
      </div>
    </el-dialog>
      <el-alert
        title="该案例需要转运,但尚未创建转运单"
        type="warning"
        description="请点击上方按钮创建转运单"
        show-icon
        :closable="false"
      />
    </el-card>
    <!-- 文件预览弹窗 -->
    <FilePreviewDialog
      :visible="previewVisible"
      :file="currentPreviewFile"
      @close="previewVisible = false"
      @download="handleDownload"
    />
    <div class="detail-footer" v-if="showtitle">
      <el-button @click="handleClose">关闭</el-button>
@@ -361,11 +405,13 @@
<script>
import pdf from "vue-pdf";
import FilePreviewDialog from "@/components/FilePreviewDialog";
export default {
  name: "CaseDetail",
  components: {
    pdf
    pdf,
    FilePreviewDialog
  },
  props: {
    caseData: {
@@ -378,52 +424,29 @@
    }
  },
  dicts: ["sys_user_sex", "sys_BloodType"],
  filters: {
    statusFilter(status) {
      const statusMap = {
        "0": "warning",
        "1": "success",
        "2": "danger"
      };
      return statusMap[status];
    },
    statusTextFilter(status) {
      const statusMap = {
        "0": "待审批",
        "1": "已通过",
        "2": "已驳回"
      };
      return statusMap[status];
    }
  },
  data() {
    return {
      // 预览相关数据(保持原有代码)
      pdfPreviewVisible: false,
      imagePreviewVisible: false,
      unsupportedPreviewVisible: false,
      pdfLoading: false,
      pdfUrl: "",
      currentPage: 1,
      pageCount: 0,
      scale: 100,
      pageRotate: 0,
      previewTitle: "",
      previewUrl: "",
      currentFile: null,
      // 预览相关
      previewVisible: false,
      currentPreviewFile: null,
      // 附件列表
      attachmentList: []
      // 图片预览相关
      imagePreviewUrls: []
    };
  },
  watch: {
    caseData: {
      immediate: true,
      handler(newVal) {
        if (newVal && newVal.annexfilesList) {
          this.loadAttachments(newVal.annexfilesList);
        }
  computed: {
    // 收集所有图片URL用于预览
    allImageUrls() {
      if (
        !this.caseData.annexfilesList ||
        !Array.isArray(this.caseData.annexfilesList)
      ) {
        return [];
      }
      return this.caseData.annexfilesList
        .filter(file => this.isImageFile(file.fileName || file.path))
        .map(file => this.getFileUrl(file))
        .filter(url => url);
    }
  },
  methods: {
@@ -431,37 +454,10 @@
      this.$emit("close");
    },
    // 加载附件
    loadAttachments(annexfilesList) {
      if (!annexfilesList || !Array.isArray(annexfilesList)) {
        this.attachmentList = [];
        return;
      }
      this.attachmentList = annexfilesList.map((file, index) => ({
        id: index + 1,
        fileName: file.fileName || `附件${index + 1}`,
        fileType: this.getFileExtension(file.fileUrl || ""),
        fileSize: file.fileSize || 0,
        uploadTime: file.uploadTime || this.formatDateTime(new Date()),
        fileUrl: file.fileUrl || ""
      }));
    },
    // 获取文件扩展名
    getFileExtension(filename) {
      return (
        filename
          .split(".")
          .pop()
          ?.toLowerCase() || "unknown"
      );
    },
    // 格式化日期时间
    formatDateTime(dateString) {
      if (!dateString) return "-";
      return dateString.replace("T", " ").substring(0, 19);
    formatDateTime(dateTime) {
      if (!dateTime) return "-";
      return dateTime.replace("T", " ").substring(0, 19);
    },
    // 格式化日期
@@ -469,7 +465,75 @@
      if (!dateString) return "-";
      return dateString.split("T")[0];
    },
    getTransportStatusTag(transport) {
      if (!transport.transitStatus) return "info";
      switch (transport.transitStatus.toString()) {
        case "1":
          return "warning"; // 待转运
        case "2":
          return "primary"; // 转运中
        case "3":
          return "success"; // 转运完成
        case "4":
          return "danger"; // 转运取消
        case "5":
          return "info"; // 暂存
        default:
          return "info";
      }
    },
    /** 获取转运状态文本 */
    getTransportStatusText(transport) {
      if (!transport.transitStatus) return "未知";
      switch (transport.transitStatus.toString()) {
        case "1":
          return "待转运";
        case "2":
          return "转运中";
        case "3":
          return "转运完成";
        case "4":
          return "转运取消";
        case "5":
          return "暂存";
        default:
          return "未知";
      }
    },
    /** 查看转运详情 */
    handleViewTransport() {
      if (
        this.caseData.serviceTransport &&
        this.caseData.serviceTransport.length > 0
      ) {
        const transport = this.caseData.serviceTransport[0];
        this.$router.push({
          path: "/business/transport/detail",
          query: {
            id: transport.id,
            caseNo: this.caseData.caseNo
          }
        });
      }
    },
    /** 创建转运单 */
    handleCreateTransport() {
      this.$router.push({
        path: "/business/transport/create",
        query: {
          caseId: this.caseData.id,
          caseNo: this.caseData.caseNo,
          patName: this.caseData.name,
          age: this.caseData.age,
          sex: this.caseData.sex,
          diagnosisname: this.caseData.diagnosisname,
          treatmentHospitalName: this.caseData.treatmenthospitalname
        }
      });
    },
    // 获取完整户籍地址
    getFullRegisterAddress() {
      const {
@@ -550,113 +614,127 @@
      return typeMap[type] || type || "-";
    },
    // 文件预览相关方法(保持原有代码)
    // 文件处理相关方法
    isImageFile(fileName) {
      if (!fileName) return false;
      const imageExtensions = [
        "jpg",
        "jpeg",
        "png",
        "gif",
        "bmp",
        "webp",
        "svg"
      ];
      const extension = fileName
        .split(".")
        .pop()
        .toLowerCase();
      return imageExtensions.includes(extension);
    },
    getFileUrl(file) {
      return file.path || file.fileUrl || "";
    },
    getImagePreviewList(file) {
      // 返回所有图片的URL列表,实现点击任意图片查看所有图片
      return this.allImageUrls;
    },
    getFileIcon(fileName) {
      if (!fileName) return "el-icon-document";
      const ext = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const iconMap = {
        pdf: "el-icon-document",
        doc: "el-icon-document",
        docx: "el-icon-document",
        xls: "el-icon-document",
        xlsx: "el-icon-document",
        jpg: "el-icon-picture",
        jpeg: "el-icon-picture",
        png: "el-icon-picture",
        gif: "el-icon-picture"
      };
      return iconMap[ext] || "el-icon-document";
    },
    getFileTypeText(fileName) {
      if (!fileName) return "文件";
      const ext = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const typeMap = {
        pdf: "PDF文档",
        doc: "Word文档",
        docx: "Word文档",
        xls: "Excel表格",
        xlsx: "Excel表格",
        jpg: "图片",
        jpeg: "图片",
        png: "图片",
        gif: "图片"
      };
      return typeMap[ext] || "文件";
    },
    canPreview(fileName) {
      if (!fileName) return false;
      const previewableExtensions = ["pdf", "jpg", "jpeg", "png", "gif"];
      const extension = fileName
        .split(".")
        .pop()
        .toLowerCase();
      return previewableExtensions.includes(extension);
    },
    // 文件预览
    handlePreview(file) {
      this.currentPreviewFile = {
        fileName: file.fileName,
        fileUrl: this.getFileUrl(file),
        fileType: this.getFileType(file.fileName)
      };
      this.previewVisible = true;
    },
    getFileType(fileName) {
      if (!fileName) return "other";
      const extension = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"];
      const pdfTypes = ["pdf"];
      const officeTypes = ["doc", "docx", "xls", "xlsx"];
      if (imageTypes.includes(extension)) return "image";
      if (pdfTypes.includes(extension)) return "pdf";
      if (officeTypes.includes(extension)) return "office";
      return "other";
    },
    handlePreview(file) {
      this.currentFile = file;
      this.previewTitle = `预览 - ${file.fileName}`;
      this.previewUrl = file.fileUrl;
      const fileType = this.getFileType(file.fileName);
      switch (fileType) {
        case "pdf":
          this.previewPdf(file);
          break;
        case "image":
          this.previewImage(file);
          break;
        default:
          this.previewUnsupported(file);
          break;
      }
    },
    previewPdf(file) {
      this.pdfPreviewVisible = true;
      this.pdfLoading = true;
      this.currentPage = 1;
      this.scale = 100;
      this.pageRotate = 0;
      this.pdfUrl = file.fileUrl;
    },
    loadPdfHandler() {
      this.pdfLoading = false;
      this.currentPage = 1;
    },
    pdfErrorHandler(error) {
      console.error("PDF加载失败:", error);
      this.pdfLoading = false;
      this.$message.error("PDF文件加载失败,请尝试下载后查看");
      this.pdfPreviewVisible = false;
    },
    changePage(newPage) {
      if (newPage < 1 || newPage > this.pageCount) return;
      this.currentPage = newPage;
    },
    zoomIn() {
      if (this.scale >= 200) return;
      this.scale += 10;
    },
    zoomOut() {
      if (this.scale <= 50) return;
      this.scale -= 10;
    },
    resetZoom() {
      this.scale = 100;
    },
    previewImage(file) {
      this.imagePreviewVisible = true;
    },
    previewUnsupported(file) {
      this.unsupportedPreviewVisible = true;
    },
    handlePdfDialogClose() {
      this.pdfUrl = "";
      this.currentPage = 1;
      this.pageCount = 0;
    },
    // 文件下载
    handleDownload(file) {
      const link = document.createElement("a");
      link.href = file.fileUrl;
      link.download = file.fileName;
      link.style.display = "none";
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      this.$message.success("开始下载文件");
    },
      const fileUrl = this.getFileUrl(file);
      const fileName = file.fileName;
    downloadPdf(file) {
      this.handleDownload(file);
    },
    formatFileSize(bytes) {
      if (bytes === 0) return "0 B";
      const k = 1024;
      const sizes = ["B", "KB", "MB", "GB"];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
      if (fileUrl) {
        const link = document.createElement("a");
        link.href = fileUrl;
        link.download = fileName;
        link.style.display = "none";
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        this.$message.success("开始下载文件");
      } else {
        this.$message.warning("文件路径不存在,无法下载");
      }
    }
  }
};
@@ -667,83 +745,144 @@
  padding: 0 20px;
}
/* PDF预览对话框样式 */
.pdf-preview-dialog {
  margin-top: 5vh !important;
.detail-section {
  margin-bottom: 16px;
}
.pdf-preview-dialog >>> .el-dialog {
  min-height: 80vh;
  display: flex;
  flex-direction: column;
}
.pdf-preview-dialog >>> .el-dialog__body {
  flex: 1;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.pdf-preview-container {
  display: flex;
  flex-direction: column;
  height: 100%;
}
/* PDF工具栏样式 */
.pdf-toolbar {
  padding: 15px 20px;
  background: #f5f7fa;
  border-bottom: 1px solid #ebeef5;
.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}
.zoom-controls {
  margin: 0 15px;
.section-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
}
/* PDF视图区域样式 */
.pdf-viewport {
  flex: 1;
  overflow: auto;
  padding: 20px;
  background: #f8f9fa;
/* 附件样式 */
.detail-attachments {
  padding: 10px 0;
}
.attachment-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 16px;
}
.attachment-card {
  display: flex;
  flex-direction: column;
  align-items: center;
}
/* 图片附件样式 */
.image-attachment {
  border-radius: 4px;
  border: 1px solid #ebeef5;
  cursor: pointer;
  transition: all 0.3s;
  margin-bottom: 8px;
}
.image-attachment:hover {
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  transform: translateY(-2px);
}
.image-info {
  text-align: center;
  width: 100%;
}
.image-info .file-name {
  font-size: 12px;
  color: #606266;
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 120px;
}
.image-info .file-actions {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: 8px;
}
/* 图片预览样式 */
.image-preview-container {
.image-loading,
.image-error {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100%;
  color: #909399;
  font-size: 12px;
}
.image-loading i,
.image-error i {
  font-size: 20px;
  margin-bottom: 4px;
}
/* 文件卡片样式 */
.file-card {
  width: 100%;
  min-height: 60px;
  margin-bottom: 8px;
}
.file-content {
  display: flex;
  align-items: center;
  padding: 8px;
}
.file-icon {
  font-size: 24px;
  margin-right: 12px;
  color: #409eff;
  flex-shrink: 0;
}
.file-info {
  flex: 1;
  min-width: 0;
}
.file-name {
  font-size: 13px;
  font-weight: 500;
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.file-meta {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: #909399;
}
.file-actions {
  text-align: center;
  padding: 20px;
  padding: 8px;
  border-top: 1px solid #f0f0f0;
}
.preview-image {
  max-width: 100%;
  max-height: 70vh;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
/* 响应式设计 */
@media (max-width: 768px) {
  .pdf-toolbar {
    flex-direction: column;
    gap: 10px;
  }
  .zoom-controls {
    margin: 10px 0;
  }
  .pdf-preview-dialog {
    width: 95% !important;
  }
/* 备注样式 */
.remark-content {
  padding: 12px;
  line-height: 1.6;
  color: #606266;
  white-space: pre-line;
}
.detail-footer {
@@ -758,4 +897,19 @@
  background-color: #f5f7fa;
  font-weight: bold;
}
/* 转运信息样式 */
.transport-info {
  padding: 10px;
}
.transport-item {
  margin-bottom: 8px;
  line-height: 1.6;
}
.transport-item strong {
  display: inline-block;
  width: 100px;
  color: #606266;
}
</style>
src/views/business/appear/index.vue
@@ -67,6 +67,7 @@
    </el-row>
    <!-- 数据表格 -->
    <!-- 数据表格 -->
    <el-table
      v-loading="loading"
      :data="caseList"
@@ -74,12 +75,18 @@
    >
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column
        label="案例编号"
        align="center"
        prop="caseNo"
        width="150"
        fixed
      />
      <el-table-column
        label="上报时间"
        align="center"
        prop="reporttime"
        width="160"
      />
      <el-table-column
        label="捐献者姓名"
        align="center"
@@ -87,27 +94,23 @@
        width="100"
      />
      <el-table-column label="性别" align="center" prop="sex" width="80">
        <template slot-scope="scope">
          <dict-tag
            :options="dict.type.sys_user_sex"
            :value="parseInt(scope.row.sex)"
          />
        <template #default="scope">
          <dict-tag :options="dict.type.sys_user_sex" :value="scope.row.sex" />
        </template>
      </el-table-column>
      <el-table-column label="年龄" align="center" prop="age" width="80" />
      <el-table-column label="血型" align="center" prop="bloodType" width="80">
        <template slot-scope="scope">
        <template #default="scope">
          <dict-tag
            :options="dict.type.sys_BloodType"
            :value="scope.row.bloodtype"
            :value="scope.row.bloodType"
          />
        </template>
      </el-table-column>
      <el-table-column
        label="GCS评分"
        align="center"
        prop="gscScore"
        prop="gcsScore"
        width="80"
        show-overflow-tooltip
      />
@@ -130,9 +133,16 @@
        prop="reportStatus"
        width="100"
      >
        <template slot-scope="scope">
        <template #default="scope">
          <el-tag :type="scope.row.reportStatus | statusFilter">
            {{ scope.row.reportStatus | statusTextFilter }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column label="转运状态" align="center" width="100">
        <template #default="scope">
          <el-tag :type="getTransportStatusTag(scope.row)">
            {{ getTransportStatusText(scope.row) }}
          </el-tag>
        </template>
      </el-table-column>
@@ -141,9 +151,9 @@
        align="center"
        fixed="right"
        class-name="small-padding fixed-width"
        width="200"
        width="350"
      >
        <template slot-scope="scope">
        <template #default="scope">
          <el-button
            size="mini"
            type="text"
@@ -151,15 +161,37 @@
            @click="handleDetail(scope.row)"
            >详情</el-button
          >
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleEdit(scope.row)"
            v-if="
              scope.row.reportStatus !== '3' &&
                scope.row.reportStatus !== '4' &&
                scope.row.delFlag === 0
            "
            >编辑</el-button
          >
          <el-button
            size="mini"
            type="text"
            icon="el-icon-check"
            @click="handleApprove(scope.row)"
            v-if="scope.row.reportStatus === '2'"
            v-if="scope.row.reportStatus === '2' && scope.row.delFlag === 0"
            >审批</el-button
          >
          <el-button
            size="mini"
            type="text"
            icon="el-icon-truck"
            :type="shouldShowTransportButton(scope.row) ? 'primary' : 'text'"
            @click="handleTransport(scope.row)"
            v-if="shouldShowTransportButton(scope.row)"
            :disabled="!canGoToTransport(scope.row)"
          >
            {{ getTransportButtonText(scope.row) }}
          </el-button>
        </template>
      </el-table-column>
    </el-table>
@@ -237,26 +269,709 @@
        <el-button type="primary" @click="submitApprove">确定</el-button>
      </div>
    </el-dialog>
    <!-- 新增/编辑案例弹框 -->
    <el-dialog
      :title="`${isEditing ? '编辑' : '新增'}案例`"
      :visible.sync="editOpen"
      width="68%"
      append-to-body
      :close-on-click-modal="false"
      @close="handleEditClose"
    >
      <el-form
        ref="editForm"
        :model="editForm"
        :rules="editRules"
        label-width="120px"
      >
        <!-- 基本信息 -->
        <el-card class="form-section" shadow="never">
          <div slot="header" class="section-header">
            <i
              class="el-icon-user"
              style="color: #409EFF; margin-right: 8px;"
            ></i>
            <span>基本信息</span>
          </div>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="案例编号" prop="caseNo">
                <el-input
                  v-model="editForm.caseNo"
                  placeholder="请输入案例编号"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="捐献者姓名" prop="name">
                <el-input
                  v-model="editForm.name"
                  placeholder="请输入捐献者姓名"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="性别" prop="sex">
                <el-select
                  v-model="editForm.sex"
                  placeholder="请选择性别"
                  style="width: 100%"
                >
                  <el-option label="男" value="0" />
                  <el-option label="女" value="1" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="年龄" prop="age">
                <el-input-number
                  v-model="editForm.age"
                  :min="0"
                  :max="120"
                  placeholder="请输入年龄"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="年龄单位" prop="ageunit">
                <el-select
                  v-model="editForm.ageunit"
                  placeholder="请选择年龄单位"
                  style="width: 100%"
                >
                  <el-option label="岁" value="year" />
                  <el-option label="月" value="month" />
                  <el-option label="天" value="day" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="血型" prop="bloodType">
                <el-select
                  v-model="editForm.bloodType"
                  placeholder="请选择血型"
                  style="width: 100%"
                >
                  <el-option label="A型" value="A" />
                  <el-option label="B型" value="B" />
                  <el-option label="O型" value="O" />
                  <el-option label="AB型" value="AB" />
                </el-select>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="Rh阴性" prop="rhYin">
                <el-radio-group v-model="editForm.rhYin">
                  <el-radio label="0">否</el-radio>
                  <el-radio label="1">是</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="出生日期" prop="birthday">
                <el-date-picker
                  v-model="editForm.birthday"
                  type="date"
                  placeholder="选择出生日期"
                  value-format="yyyy-MM-dd"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="证件类型" prop="idcardtype">
                <el-select
                  v-model="editForm.idcardtype"
                  placeholder="请选择证件类型"
                  style="width: 100%"
                >
                  <el-option label="身份证" value="1" />
                  <el-option label="护照" value="2" />
                  <el-option label="军官证" value="3" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="证件号码" prop="idcardno">
                <el-input
                  v-model="editForm.idcardno"
                  placeholder="请输入证件号码"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="民族" prop="nation">
                <el-input v-model="editForm.nation" placeholder="请输入民族" />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="国籍" prop="nationality">
                <el-input
                  v-model="editForm.nationality"
                  placeholder="请输入国籍"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="籍贯" prop="nativeplace">
                <el-input
                  v-model="editForm.nativeplace"
                  placeholder="请输入籍贯"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="学历" prop="education">
                <el-input
                  v-model="editForm.education"
                  placeholder="请输入学历"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="职业" prop="occupation">
                <el-input
                  v-model="editForm.occupation"
                  placeholder="请输入职业"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="联系电话" prop="phone">
                <el-input
                  v-model="editForm.phone"
                  placeholder="请输入联系电话"
                />
              </el-form-item>
            </el-col>
          </el-row>
        </el-card>
        <!-- 地址信息 -->
        <el-card class="form-section" shadow="never">
          <div slot="header" class="section-header">
            <i
              class="el-icon-location-information"
              style="color: #67C23A; margin-right: 8px;"
            ></i>
            <span>地址信息</span>
          </div>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="户籍地址省">
                <el-input
                  v-model="editForm.registerprovincename"
                  placeholder="请输入户籍地址省"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="户籍地址市">
                <el-input
                  v-model="editForm.registercityname"
                  placeholder="请输入户籍地址市"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="户籍街道">
                <el-input
                  v-model="editForm.registertownname"
                  placeholder="请输入户籍街道"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="户籍社区">
                <el-input
                  v-model="editForm.registercommunityname"
                  placeholder="请输入户籍社区"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-form-item label="户籍详细地址" prop="registeraddress">
            <el-input
              v-model="editForm.registeraddress"
              type="textarea"
              :rows="2"
              placeholder="请输入详细的户籍地址"
            />
          </el-form-item>
          <el-divider>现住地址</el-divider>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="现住地址省">
                <el-input
                  v-model="editForm.residenceprovincename"
                  placeholder="请输入现住地址省"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="现住地址市/区">
                <el-input
                  v-model="editForm.residencecountyname"
                  placeholder="请输入现住地址市/区"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="现住地址街道">
                <el-input
                  v-model="editForm.residencetownname"
                  placeholder="请输入现住地址街道"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="现住地址社区">
                <el-input
                  v-model="editForm.residencecommunityname"
                  placeholder="请输入现住地址社区"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-form-item label="现住详细地址" prop="residenceaddress">
            <el-input
              v-model="editForm.residenceaddress"
              type="textarea"
              :rows="2"
              placeholder="请输入详细的现住地址"
            />
          </el-form-item>
        </el-card>
        <!-- 医疗信息 -->
        <el-card class="form-section" shadow="never">
          <div slot="header" class="section-header">
            <i
              class="el-icon-first-aid-kit"
              style="color: #E6A23C; margin-right: 8px;"
            ></i>
            <span>医疗信息</span>
          </div>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="疾病诊断" prop="diagnosisname">
                <el-input
                  v-model="editForm.diagnosisname"
                  placeholder="请输入疾病诊断名称"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="GCS评分" prop="gcsScore">
                <el-input
                  v-model="editForm.gcsScore"
                  placeholder="请输入GCS评分"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-form-item label="病情概况" prop="illnessoverview">
            <el-input
              v-model="editForm.illnessoverview"
              type="textarea"
              :rows="3"
              placeholder="请输入病情概况"
              maxlength="500"
              show-word-limit
            />
          </el-form-item>
          <el-form-item label="病人状况" prop="patientstate">
            <el-input
              v-model="editForm.patientstate"
              type="textarea"
              :rows="2"
              placeholder="请输入病人状况"
              maxlength="200"
              show-word-limit
            />
          </el-form-item>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="传染病情况" prop="infectious">
                <el-input
                  v-model="editForm.infectious"
                  placeholder="请输入传染病情况"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="传染病其他说明">
                <el-input
                  v-model="editForm.infectiousOther"
                  placeholder="请输入传染病其他说明"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-form-item label="是否需要转运" prop="isTransport">
            <el-radio-group v-model="editForm.isTransport">
              <el-radio label="1">不需要</el-radio>
              <el-radio label="2">需要</el-radio>
            </el-radio-group>
          </el-form-item>
        </el-card>
        <!-- 医院信息 -->
        <el-card class="form-section" shadow="never">
          <div slot="header" class="section-header">
            <i
              class="el-icon-office-building"
              style="color: #909399; margin-right: 8px;"
            ></i>
            <span>医院信息</span>
          </div>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="治疗医院" prop="treatmenthospitalname">
                <el-input
                  v-model="editForm.treatmenthospitalname"
                  placeholder="请输入治疗医院名称"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="治疗科室" prop="treatmentdeptname">
                <el-input
                  v-model="editForm.treatmentdeptname"
                  placeholder="请输入治疗科室名称"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="住院号" prop="inpatientno">
                <el-input
                  v-model="editForm.inpatientno"
                  placeholder="请输入住院号"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="部门名称" prop="deptName">
                <el-input
                  v-model="editForm.deptName"
                  placeholder="请输入部门名称"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="部门编号" prop="deptNo">
                <el-input
                  v-model="editForm.deptNo"
                  placeholder="请输入部门编号"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="上报医院" prop="toHospital">
                <el-input
                  v-model="editForm.toHospital"
                  placeholder="请输入上报的医院"
                />
              </el-form-item>
            </el-col>
          </el-row>
        </el-card>
        <!-- 上报信息 -->
        <el-card class="form-section" shadow="never">
          <div slot="header" class="section-header">
            <i
              class="el-icon-s-data"
              style="color: #F56C6C; margin-right: 8px;"
            ></i>
            <span>上报信息</span>
          </div>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="报告者姓名" prop="infoName">
                <el-input
                  v-model="editForm.infoName"
                  placeholder="请输入报告者姓名"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="报告者编号" prop="infoNo">
                <el-input
                  v-model="editForm.infoNo"
                  placeholder="请输入报告者编号"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="报告者电话" prop="reporterphone">
                <el-input
                  v-model="editForm.reporterphone"
                  placeholder="请输入报告者联系电话"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="协调员姓名" prop="coordinatorName">
                <el-input
                  v-model="editForm.coordinatorName"
                  placeholder="请输入协调员姓名"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="协调员编号" prop="coordinatorNo">
                <el-input
                  v-model="editForm.coordinatorNo"
                  placeholder="请输入协调员编号"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="是否终止案例" prop="terminationCase">
                <el-radio-group v-model="editForm.terminationCase">
                  <el-radio label="0">进行中</el-radio>
                  <el-radio label="1">已终止</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
          </el-row>
          <el-form-item label="上报状态" prop="reportStatus">
            <el-select
              v-model="editForm.reportStatus"
              placeholder="请选择上报状态"
              style="width: 100%"
              :disabled="isEditing"
            >
              <el-option label="已上报" value="1" />
              <el-option label="已阅读" value="2" />
              <el-option label="已同意" value="3" />
              <el-option label="已驳回" value="4" />
            </el-select>
          </el-form-item>
        </el-card>
        <!-- 附件信息 -->
        <el-card class="form-section" shadow="never">
          <div slot="header" class="section-header">
            <i
              class="el-icon-folder"
              style="color: #409EFF; margin-right: 8px;"
            ></i>
            <span>附件信息</span>
          </div>
          <div class="attachment-section">
            <div class="attachment-header">
              <i class="el-icon-paperclip"></i>
              <span class="attachment-title">附件上传</span>
              <span class="attachment-tip"
                >支持上传病历、检验报告等文件 (最多{{
                  attachmentLimit
                }}个)</span
              >
            </div>
            <!-- 使用 UploadAttachment 组件 -->
            <UploadAttachment
              ref="uploadAttachment"
              :file-list="attachmentFileList"
              :limit="attachmentLimit"
              :accept="attachmentAccept"
              @change="handleAttachmentChange"
              @upload-success="handleUploadSuccess"
              @upload-error="handleUploadError"
              @remove="handleAttachmentRemove"
            />
          </div>
          <!-- 附件列表 -->
          <div
            class="attachment-list"
            v-if="editForm.annexfilesList && editForm.annexfilesList.length > 0"
          >
            <div class="list-title">
              已上传附件 ({{ editForm.annexfilesList.length }})
            </div>
            <el-table
              :data="editForm.annexfilesList"
              style="width: 100%"
              size="small"
              border
            >
              <el-table-column label="文件名" min-width="200">
                <template #default="scope">
                  <i
                    class="el-icon-document"
                    style="margin-right: 8px; color: #409EFF;"
                  ></i>
                  <span class="file-name">{{ scope.row.fileName }}</span>
                </template>
              </el-table-column>
              <el-table-column label="文件类型" width="100">
                <template #default="scope">
                  <el-tag size="small">{{
                    getFileType(scope.row.fileName)
                  }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="创建时间" width="160">
                <template #default="scope">
                  <span>{{ formatDateTime(scope.row.createTime) }}</span>
                </template>
              </el-table-column>
              <el-table-column
                label="备注"
                min-width="150"
                show-overflow-tooltip
              >
                <template #default="scope">
                  <span>{{ scope.row.remart || "无" }}</span>
                </template>
              </el-table-column>
              <el-table-column label="操作" width="200" fixed="right">
                <template #default="scope">
                  <el-button
                    size="mini"
                    type="primary"
                    @click="handlePreview(scope.row)"
                  >
                    预览
                  </el-button>
                  <el-button
                    size="mini"
                    type="danger"
                    @click="handleRemoveAttachment(scope.$index)"
                  >
                    删除
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </el-card>
        <!-- 备注信息 -->
        <el-card class="form-section" shadow="never">
          <div slot="header" class="section-header">
            <i
              class="el-icon-edit"
              style="color: #67C23A; margin-right: 8px;"
            ></i>
            <span>备注信息</span>
          </div>
          <el-form-item prop="remark">
            <el-input
              v-model="editForm.remark"
              type="textarea"
              :rows="4"
              placeholder="请输入备注信息"
              maxlength="500"
              show-word-limit
            />
          </el-form-item>
        </el-card>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="handleEditClose">取消</el-button>
        <el-button type="primary" :loading="saveLoading" @click="handleSave"
          >保存</el-button
        >
      </div>
    </el-dialog>
    <!-- 文件预览弹窗 -->
    <FilePreviewDialog
      :visible="previewVisible"
      :file="currentPreviewFile"
      @close="previewVisible = false"
      @download="handleDownload"
    />
  </div>
</template>
<script>
import CaseDetail from "./caseDetail";
import UploadAttachment from "@/components/UploadAttachment";
import FilePreviewDialog from "@/components/FilePreviewDialog";
import {
  donateList,
  donateInfo,
  donateDel,
  donateEdit
  donateEdit,
  donateAdd
} from "@/api/businessApi/index";
import dayjs from "dayjs";
export default {
  name: "CaseList",
  components: { CaseDetail },
  components: {
    CaseDetail,
    UploadAttachment,
    FilePreviewDialog
  },
  dicts: ["sys_user_sex", "sys_BloodType"],
  data() {
    return {
      // 遮罩层
      loading: false,
      saveLoading: false,
      // 选中数组
      ids: [],
      // 非单个禁用
@@ -271,22 +986,14 @@
      detailOpen: false,
      // 审批弹框是否显示
      approveOpen: false,
      // 编辑弹框是否显示
      editOpen: false,
      // 编辑状态
      isEditing: false,
      // 详情弹框标题
      detailTitle: "",
      // 当前操作的案例
      currentCase: {},
      // 性别选项
      genderOptions: [
        { value: "0", label: "男" },
        { value: "1", label: "女" }
      ],
      // 血型选项
      bloodTypeOptions: [
        { value: "A", label: "A型" },
        { value: "B", label: "B型" },
        { value: "O", label: "O型" },
        { value: "AB", label: "AB型" }
      ],
      // 查询参数
      queryParams: {
        pageNum: 1,
@@ -309,7 +1016,36 @@
        approveOpinion: [
          { required: true, message: "请输入审批意见", trigger: "blur" }
        ]
      }
      },
      // 编辑表单
      editForm: this.getDefaultFormData(),
      // 表单验证规则
      editRules: {
        caseNo: [
          { required: true, message: "请输入案例编号", trigger: "blur" }
        ],
        name: [
          { required: true, message: "请输入捐献者姓名", trigger: "blur" }
        ],
        sex: [{ required: true, message: "请选择性别", trigger: "change" }],
        age: [{ required: true, message: "请输入年龄", trigger: "blur" }],
        diagnosisname: [
          { required: true, message: "请输入疾病诊断", trigger: "blur" }
        ],
        treatmenthospitalname: [
          { required: true, message: "请输入治疗医院", trigger: "blur" }
        ],
        reportStatus: [
          { required: true, message: "请选择上报状态", trigger: "change" }
        ]
      },
      // 附件相关配置
      attachmentLimit: 10,
      attachmentAccept: ".pdf,.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx",
      attachmentFileList: [],
      // 预览相关
      previewVisible: false,
      currentPreviewFile: null
    };
  },
  filters: {
@@ -346,66 +1082,199 @@
      } catch (error) {
        console.error("获取案例列表失败:", error);
        this.$modal.msgError("获取案例列表失败");
        // 模拟数据
        this.caseList = this.getMockData();
        this.total = this.caseList.length;
        this.caseList = [];
        this.total = 0;
      } finally {
        this.loading = false;
      }
    },
    /** 模拟数据 */
    getMockData() {
      return [
        {
          id: 1,
          caseNo: "DON20241219001",
          name: "张三",
          sex: "0",
          age: 38,
          bloodType: "A",
          gscScore: "1",
          diagnosisname:
            "脑外伤导致脑死亡,经抢救无效宣布脑死亡。家属同意器官捐献。",
          treatmenthospitalname: "青岛大学附属医院",
          reportStatus: "2",
          reporttime: "2024-12-19 09:30:00",
          reportername: "李医生",
          idcardno: "370203198510123456",
          nation: "汉族",
          phone: "13800138000",
          registeraddress: "山东省青岛市市南区香港中路100号",
          inpatientno: "ZY20241219001",
          treatmentdeptname: "神经外科",
          doctorname: "王主任",
          infectious: "无",
          illnessoverview:
            "患者因交通事故导致严重脑外伤,经抢救无效宣布脑死亡。",
          hospitalLevel: "三级甲等",
          contactperson: "张护士",
          contactphone: "13900139000",
          hospitalAddress: "山东省青岛市市南区江苏路1号"
        },
        {
          id: 2,
          caseNo: "DON20241218001",
          name: "李四",
          sex: "0",
          age: 45,
          bloodType: "O",
          gscScore: "3",
          diagnosisname: "急性心肌梗死,心脏功能衰竭",
          treatmenthospitalname: "青岛市立医院",
          reportStatus: "3",
          reporttime: "2024-12-18 14:20:00",
          approvetime: "2024-12-18 16:30:00",
          reportername: "刘医生",
          approvername: "审核专员A",
          approveopinion: "资料齐全,符合捐献条件,同意通过。"
        }
      ];
    // 转运------------------
    /** 判断是否需要显示转运按钮 */
    shouldShowTransportButton(row) {
      // 只有已同意的案例才能进行转运操作
      // return row.reportStatus == "3" && row.delFlag == 0;
      return row.reportStatus != "4";
    },
    /** 判断是否可以跳转到转运单 */
    canGoToTransport(row) {
      // 需要转运(isTransport === "2")且没有转运信息(serviceTransport不存在或为空数组)
      return row.isTransport == "2" && row.serviceTransport;
    },
    /** 获取转运按钮文本 */
    getTransportButtonText(row) {
      if (row.isTransport == "2") {
        if (
          row.serviceTransport &&
          Array.isArray(row.serviceTransport) &&
          row.serviceTransport.length > 0
        ) {
          return "查看转运单";
        } else {
          return "前往转运单";
        }
      }
      return "转运";
    },
    /** 获取转运状态标签样式 */
    getTransportStatusTag(row) {
      if (row.isTransport === "1") {
        return "info"; // 不需要转运
      }
      if (
        row.serviceTransport &&
        Array.isArray(row.serviceTransport) &&
        row.serviceTransport.length > 0
      ) {
        // 根据转运单状态显示不同颜色
        const transport = row.serviceTransport[0];
        if (transport.transitStatus) {
          switch (transport.transitStatus.toString()) {
            case "1":
              return "warning"; // 待转运
            case "2":
              return "primary"; // 转运中
            case "3":
              return "success"; // 转运完成
            case "4":
              return "danger"; // 转运取消
            case "5":
              return "info"; // 暂存
            default:
              return "info";
          }
        }
        return "primary"; // 有转运信息但无状态
      }
      return "danger"; // 需要转运但无转运信息
    },
    /** 获取转运状态文本 */
    getTransportStatusText(row) {
      if (row.isTransport === "1") {
        return "无需转运";
      }
      if (
        row.serviceTransport &&
        Array.isArray(row.serviceTransport) &&
        row.serviceTransport.length > 0
      ) {
        const transport = row.serviceTransport[0];
        if (transport.transitStatus) {
          switch (transport.transitStatus.toString()) {
            case "1":
              return "待转运";
            case "2":
              return "转运中";
            case "3":
              return "转运完成";
            case "4":
              return "转运取消";
            case "5":
              return "暂存";
            default:
              return "有转运单";
          }
        }
        return "有转运单";
      }
      return "需转运";
    },
    /** 处理转运操作 */
    async handleTransport(row) {
      try {
        // 先获取最新的案例信息
        const caseData = row;
        if (caseData.isTransport == "2") {
          if (
            caseData.serviceTransport &&
            Array.isArray(caseData.serviceTransport) &&
            caseData.serviceTransport.length > 0
          ) {
            // 已有转运单,跳转到转运单详情页
            const transport = caseData.serviceTransport[0];
            this.goToTransportDetail(transport.id, row.caseNo);
          } else {
            // 没有转运单,跳转到创建转运单页面
            this.goToCreateTransport(row);
          }
        } else {
          this.$message.warning("该案例不需要转运");
        }
      } catch (error) {
        console.error("获取案例信息失败:", error);
        this.$message.error("获取案例信息失败");
      }
    },
    /** 跳转到创建转运单页面 */
    goToCreateTransport(caseData) {
      // 这里可以跳转到创建转运单的页面
      // 方式1:在新页面打开
      this.$router.push({
        path: "/report/transfer",
        query: {
          autoCreate: "true",
          caseId: caseData.id,
          caseNo: caseData.caseNo,
          patName: caseData.name,
          age: caseData.age,
          sex: caseData.sex,
          diagnosisname: caseData.diagnosisname,
          treatmentHospitalName: caseData.treatmenthospitalname
        }
      });
      // 方式2:在当前页面打开弹框(推荐)
      // this.openTransportDialog(caseData);
    },
    /** 打开转运单弹框 */
    openTransportDialog(caseData) {
      this.$modal.confirm({
        title: "创建转运单",
        message: `您将创建案例【${caseData.caseNo} - ${caseData.name}】的转运单,是否继续?`,
        confirmButtonText: "前往创建",
        cancelButtonText: "取消",
        type: "warning",
        beforeClose: (action, instance, done) => {
          if (action === "confirm") {
            // 跳转到转运单创建页面
            this.$router.push({
              path: "/report/transfer",
              query: {
                caseId: caseData.id,
                caseNo: caseData.caseNo,
                patName: caseData.name,
                age: caseData.age,
                sex: caseData.sex,
                diagnosisname: caseData.diagnosisname,
                treatmentHospitalName: caseData.treatmenthospitalname
              }
            });
            done();
          } else {
            done();
          }
        }
      });
    },
    /** 跳转到转运单详情页 */
    goToTransportDetail(transportId, caseNo) {
      this.$router.push({
        path: "/report/transfer",
        query: {
          id: transportId,
          caseNo: caseNo
        }
      });
    },
    // ---------------------------------end
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.id);
@@ -421,85 +1290,99 @@
    /** 重置按钮操作 */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
      this.queryParams = {
        pageNum: 1,
        pageSize: 10,
        caseNo: undefined,
        name: undefined,
        reportStatus: undefined
      };
      this.getList();
    },
    /** 详情按钮操作 */
    async handleDetail(row) {
      try {
        // 先获取案例详情
        const response = await donateInfo(row.id);
        this.currentCase = response.data || response || row;
        this.currentCase = row;
        // 如果状态是"已上报"(1),则使用完整数据更新为"已阅读"(2)
        // 如果状态是"已上报"(1),则更新为"已阅读"(2)
        if (this.currentCase.reportStatus === "1") {
          try {
            // 使用完整的案例数据作为更新基础,确保所有字段都被保留
            const updateData = {
              ...this.currentCase, // 展开所有现有字段
              reportStatus: "2", // 更新状态为已阅读
              updateTime: new Date()
                .toISOString()
                .replace("T", " ")
                .substring(0, 19),
              updateBy: "当前用户" // 添加更新人信息
              ...this.currentCase,
              reportStatus: "2",
              updateTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
              updateBy: this.$store.state.user?.name || "当前用户"
            };
            await donateEdit(updateData);
            // 更新本地数据和当前显示的数据
            this.currentCase.reportStatus = "2";
            this.currentCase.updateTime = updateData.updateTime;
            row.reportStatus = "2"; // 更新列表中的状态
            row.reportStatus = "2";
            this.$modal.msgSuccess("状态已更新为已阅读");
          } catch (updateError) {
            console.error("状态更新失败:", updateError);
            this.$modal.msgError("状态更新失败,但将继续显示详情");
            // 更新失败时,继续使用原始状态显示详情
          }
        }
        this.detailTitle = `案例详情 - ${this.currentCase.caseNo ||
          row.caseNo}`;
        this.detailOpen = true;
        console.log(this.currentCase, "this.currentCase");
      } catch (error) {
        console.error("获取案例详情失败:", error);
        // 如果获取详情失败,使用行数据作为后备
        this.currentCase = row;
        this.detailTitle = `案例详情 - ${row.caseNo}`;
        this.detailOpen = true;
        // 即使获取详情失败,也尝试更新状态(使用行数据)
        if (row.reportStatus === "1") {
          try {
            const updateData = {
              id: row.id,
              reportStatus: "2",
              updateTime: new Date()
                .toISOString()
                .replace("T", " ")
                .substring(0, 19),
              updateBy: "当前用户"
              // 注意:这里只能传递部分字段,因为详情获取失败了
            };
            await donateEdit(updateData);
            row.reportStatus = "2";
            this.currentCase.reportStatus = "2";
            this.$modal.msgSuccess("状态已更新为已阅读");
          } catch (updateError) {
            console.error("状态更新失败:", updateError);
          }
        }
      }
    },
    /** 编辑按钮操作 */
    async handleEdit(row) {
      try {
        const response = await donateInfo(row.id);
        if (response.code === 200 || response.data) {
          this.isEditing = true;
          const data = response.data || response;
          // 处理附件
          if (data.annexfilesList) {
            this.attachmentFileList = this.parseAttachmentToFileList(
              data.annexfilesList
            );
          } else {
            this.attachmentFileList = [];
          }
          this.currentCase = data;
          this.editForm = { ...data };
          this.editOpen = true;
          this.$nextTick(() => {
            this.$refs.editForm && this.$refs.editForm.clearValidate();
          });
        } else {
          this.$message.error(response.msg || "获取记录失败");
        }
      } catch (error) {
        console.error("获取记录失败:", error);
        this.$modal.msgError("获取记录失败");
      }
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.isEditing = false;
      this.editForm = this.getDefaultFormData();
      this.attachmentFileList = [];
      this.editOpen = true;
      this.$nextTick(() => {
        this.$refs.editForm && this.$refs.editForm.clearValidate();
      });
    },
    /** 审批按钮操作 */
    async handleApprove(row) {
      try {
        // 先获取案例详情数据,确保有完整数据
        const response = await donateInfo(row.id);
        this.currentCase = response.data || response || row;
@@ -516,7 +1399,6 @@
        this.approveOpen = true;
      } catch (error) {
        console.error("获取案例详情失败:", error);
        // 如果获取详情失败,使用行数据作为后备
        this.currentCase = row;
        this.approveForm.id = row.id;
        this.approveForm.approveResult = "3";
@@ -531,31 +1413,20 @@
      try {
        const valid = await this.$refs.approveForm.validate();
        if (valid) {
          // 使用完整的案例数据作为基础,确保所有字段都被保留
          const approveData = {
            ...this.currentCase, // 展开所有现有字段
            ...this.currentCase,
            reportStatus: this.approveForm.approveResult,
            approveOpinion: this.approveForm.approveOpinion,
            approvername: "当前用户", // 实际项目中应该获取当前登录用户
            approvetime: new Date()
              .toISOString()
              .replace("T", " ")
              .substring(0, 19),
            updateTime: new Date()
              .toISOString()
              .replace("T", " ")
              .substring(0, 19),
            updateBy: "当前用户"
            remark: this.approveForm.approveOpinion,
            approvername: this.$store.state.user?.name || "当前用户",
            approvetime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
            updateTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
            updateBy: this.$store.state.user?.name || "当前用户"
          };
          // 移除可能不需要的字段(根据实际API需求调整)
          delete approveData.createTime; // 创建时间不应被更新
          delete approveData.createBy; // 创建人不应变
          await donateEdit(approveData);
          this.$modal.msgSuccess("审批成功");
          this.approveOpen = false;
          this.getList(); // 重新加载列表
          this.getList();
        }
      } catch (error) {
        console.error("审批失败:", error);
@@ -563,11 +1434,6 @@
          this.$modal.msgError("审批失败");
        }
      }
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.$router.push("/case/add");
    },
    /** 删除按钮操作 */
@@ -586,41 +1452,328 @@
          this.$modal.msgError("删除失败");
        }
      }
    },
    /** 获取默认表单数据 */
    getDefaultFormData() {
      return {
        id: undefined,
        caseNo: undefined,
        name: undefined,
        sex: undefined,
        age: undefined,
        ageunit: "year",
        bloodType: undefined,
        rhYin: "0",
        birthday: undefined,
        idcardtype: "1",
        idcardno: undefined,
        nation: undefined,
        nationality: undefined,
        nativeplace: undefined,
        education: undefined,
        occupation: undefined,
        phone: undefined,
        // 户籍地址
        registerprovincename: undefined,
        registercityname: undefined,
        registertownname: undefined,
        registercommunityname: undefined,
        registeraddress: undefined,
        // 现住地址
        residenceprovincename: undefined,
        residencecountyname: undefined,
        residencetownname: undefined,
        residencecommunityname: undefined,
        residenceaddress: undefined,
        // 医疗信息
        diagnosisname: undefined,
        illnessoverview: undefined,
        patientstate: undefined,
        gcsScore: undefined,
        infectious: undefined,
        infectiousOther: undefined,
        isTransport: "1",
        // 医院信息
        treatmenthospitalname: undefined,
        treatmentdeptname: undefined,
        inpatientno: undefined,
        deptName: undefined,
        deptNo: undefined,
        toHospital: undefined,
        // 上报信息
        infoName: undefined,
        infoNo: undefined,
        reporterphone: undefined,
        coordinatorName: undefined,
        coordinatorNo: undefined,
        terminationCase: "0",
        reportStatus: "1", // 默认已上报
        // 附件
        annexfilesList: [],
        // 其他
        remark: undefined,
        createBy: undefined,
        createTime: undefined,
        updateBy: undefined,
        updateTime: undefined,
        delFlag: 0
      };
    },
    /** 附件转换 */
    parseAttachmentToFileList(attachments) {
      if (!attachments || !Array.isArray(attachments)) return [];
      return attachments.map((item, index) => ({
        uid: item.id || `attachment-${index}-${Date.now()}`,
        name: item.fileName,
        url: item.path || item.fileUrl,
        status: "success",
        raw: item
      }));
    },
    /** 附件变化处理 */
    handleAttachmentChange(fileList) {
      this.attachmentFileList = fileList;
    },
    /** 附件上传成功处理 */
    handleUploadSuccess({ file, fileList, response }) {
      if (response && response.code === 200) {
        if (!this.editForm.annexfilesList) {
          this.editForm.annexfilesList = [];
        }
        const attachmentObj = {
          fileName: file.name,
          path: response.data || file.url,
          fileUrl: response.data || file.url,
          type: this.getFileExtension(file.name),
          createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
          remart: file.raw?.remart || "",
          caseNo: this.editForm.caseNo
        };
        this.editForm.annexfilesList.push(attachmentObj);
        this.$message.success("文件上传成功");
      }
    },
    /** 附件上传错误处理 */
    handleUploadError({ file, fileList, error }) {
      console.error("附件上传失败:", error);
      this.$message.error("文件上传失败,请重试");
    },
    /** 附件移除处理 */
    handleAttachmentRemove(file) {
      if (file.url && this.editForm.annexfilesList) {
        const index = this.editForm.annexfilesList.findIndex(
          item => item.path === file.url || item.fileUrl === file.url
        );
        if (index > -1) {
          this.editForm.annexfilesList.splice(index, 1);
        }
      }
    },
    /** 手动删除附件 */
    handleRemoveAttachment(index) {
      this.editForm.annexfilesList.splice(index, 1);
      this.attachmentFileList.splice(index, 1);
      this.$message.success("附件删除成功");
    },
    /** 文件预览 */
    handlePreview(file) {
      this.currentPreviewFile = {
        fileName: file.fileName,
        fileUrl: file.path || file.fileUrl,
        fileType: this.getFileType(file.fileName)
      };
      this.previewVisible = true;
    },
    /** 文件下载 */
    handleDownload(file) {
      const fileUrl = file.path || file.fileUrl;
      const fileName = file.fileName;
      if (fileUrl) {
        const link = document.createElement("a");
        link.href = fileUrl;
        link.download = fileName;
        link.style.display = "none";
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        this.$message.success("开始下载文件");
      } else {
        this.$message.warning("文件路径不存在,无法下载");
      }
    },
    /** 获取文件类型 */
    getFileType(fileName) {
      if (!fileName) return "other";
      const extension = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"];
      const pdfTypes = ["pdf"];
      const officeTypes = ["doc", "docx", "xls", "xlsx"];
      if (imageTypes.includes(extension)) return "image";
      if (pdfTypes.includes(extension)) return "pdf";
      if (officeTypes.includes(extension)) return "office";
      return "other";
    },
    /** 获取文件扩展名 */
    getFileExtension(filename) {
      if (!filename) return "";
      return filename
        .split(".")
        .pop()
        .toLowerCase();
    },
    /** 日期时间格式化 */
    formatDateTime(dateTime) {
      if (!dateTime) return "";
      return dayjs(dateTime).format("YYYY-MM-DD HH:mm:ss");
    },
    /** 关闭编辑对话框 */
    handleEditClose() {
      this.editOpen = false;
      this.currentCase = {};
      this.editForm = this.getDefaultFormData();
      this.attachmentFileList = [];
      this.$nextTick(() => {
        this.$refs.editForm && this.$refs.editForm.clearValidate();
      });
    },
    /** 保存表单 */
    async handleSave() {
      try {
        const valid = await this.$refs.editForm.validate();
        if (!valid) return;
        const pendingFiles = this.attachmentFileList.filter(
          item => item.status !== "success"
        );
        if (pendingFiles.length > 0) {
          this.$message.warning(
            "还有文件未上传完成,请先上传所有文件或移除未上传的文件"
          );
          return;
        }
        this.saveLoading = true;
        const requestData = { ...this.editForm };
        let response;
        if (this.isEditing) {
          response = await donateEdit(requestData);
        } else {
          // 新增时设置创建信息
          requestData.createTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
          requestData.createBy = this.$store.state.user?.name || "当前用户";
          response = await donateAdd(requestData);
        }
        if (response.code === 200) {
          this.$message.success(this.isEditing ? "修改成功" : "新增成功");
          this.handleEditClose();
          this.getList();
        } else {
          this.$message.error(response.msg || "操作失败");
        }
      } catch (error) {
        console.error("保存失败:", error);
        this.$message.error("操作失败,请稍后重试");
      } finally {
        this.saveLoading = false;
      }
    }
  }
};
</script>
<style scoped>
.app-container {
  padding: 20px;
}
.filter-card {
  margin-bottom: 20px;
}
.mb8 {
  margin-bottom: 8px;
}
/* 详情页面样式优化 */
.case-detail-container {
  max-height: 70vh;
  overflow-y: auto;
  padding: 0 10px;
}
.detail-section {
/* 表单区域样式 */
.form-section {
  margin-bottom: 16px;
}
.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.section-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
}
.dialog-footer {
  text-align: right;
  padding: 20px 0 0;
}
/* 附件样式 */
.attachment-section {
  margin-bottom: 16px;
}
.attachment-header {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
  padding: 8px 0;
  border-bottom: 1px solid #ebeef5;
}
.attachment-title {
  font-weight: bold;
  margin: 0 8px;
}
.attachment-tip {
  font-size: 12px;
  color: #909399;
}
.attachment-list {
  margin-top: 16px;
}
.list-title {
  font-weight: bold;
  margin-bottom: 12px;
  color: #303133;
}
.file-name {
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 审批弹框样式 */
.approve-dialog >>> .el-dialog__body {
  padding: 0;
src/views/business/transfer/TransportEdit.vue
@@ -7,6 +7,31 @@
    :close-on-click-modal="false"
    @close="handleClose"
  >
    <!-- 案例信息展示区域 -->
    <div v-if="selectedCase && !isEdit" class="selected-case-info">
      <el-alert
        title="关联案例信息"
        type="success"
        :closable="false"
        style="margin-bottom: 16px;"
      />
      <el-card shadow="never" class="case-info-card">
        <el-descriptions :column="2" border>
          <el-descriptions-item label="案例编号">{{ selectedCase.caseNo }}</el-descriptions-item>
          <el-descriptions-item label="患者姓名">{{ selectedCase.name }}</el-descriptions-item>
          <el-descriptions-item label="性别">
            <dict-tag
              :options="dict.type.sys_user_sex"
              :value="selectedCase.sex"
            />
          </el-descriptions-item>
          <el-descriptions-item label="年龄">{{ selectedCase.age }}</el-descriptions-item>
          <el-descriptions-item label="疾病诊断">{{ selectedCase.diagnosisname }}</el-descriptions-item>
          <el-descriptions-item label="治疗医院">{{ selectedCase.treatmenthospitalname }}</el-descriptions-item>
        </el-descriptions>
      </el-card>
    </div>
    <el-form
      ref="editForm"
      :model="formData"
@@ -34,7 +59,8 @@
            <el-form-item label="案例编号" prop="caseNo">
              <el-input
                v-model="formData.caseNo"
                placeholder="请输入案例编号"
                :disabled="true"
                placeholder="自动从案例获取"
              />
            </el-form-item>
          </el-col>
@@ -45,7 +71,8 @@
            <el-form-item label="患者姓名" prop="patName">
              <el-input
                v-model="formData.patName"
                placeholder="请输入患者姓名"
                :disabled="true"
                placeholder="自动从案例获取"
              />
            </el-form-item>
          </el-col>
@@ -53,6 +80,7 @@
            <el-form-item label="性别" prop="sex">
              <el-select
                v-model="formData.sex"
                :disabled="true"
                placeholder="请选择性别"
                style="width: 100%"
              >
@@ -70,7 +98,8 @@
                v-model="formData.age"
                :min="0"
                :max="120"
                placeholder="请输入年龄"
                :disabled="true"
                placeholder="自动从案例获取"
                style="width: 100%"
              />
            </el-form-item>
@@ -79,7 +108,8 @@
            <el-form-item label="疾病诊断" prop="diagnosisname">
              <el-input
                v-model="formData.diagnosisname"
                placeholder="请输入疾病诊断名称"
                :disabled="true"
                placeholder="自动从案例获取"
              />
            </el-form-item>
          </el-col>
@@ -98,7 +128,8 @@
            <el-form-item label="治疗医院" prop="treatmentHospitalName">
              <el-input
                v-model="formData.treatmentHospitalName"
                placeholder="请输入治疗医院名称"
                :disabled="true"
                placeholder="自动从案例获取"
              />
            </el-form-item>
          </el-col>
@@ -372,6 +403,7 @@
    UploadAttachment,
    FilePreviewDialog
  },
  dicts: ["sys_user_sex", "sys_BloodType"],
  props: {
    editOpen: {
      type: Boolean,
@@ -384,6 +416,10 @@
    isEdit: {
      type: Boolean,
      default: false
    },
    selectedCase: {
      type: Object,
      default: null
    }
  },
  data() {
@@ -428,9 +464,18 @@
  watch: {
    editOpen(val) {
      if (val) {
        this.formData = this.isEdit
          ? { ...this.getDefaultFormData(), ...this.transportData }
          : this.getDefaultFormData();
        if (this.isEdit) {
          // 编辑模式,使用传入的转运单数据
          this.formData = { ...this.getDefaultFormData(), ...this.transportData };
        } else {
          // 新增模式
          this.formData = this.getDefaultFormData();
          // 如果有选中的案例,用案例信息填充表单
          if (this.selectedCase) {
            this.fillFormWithCaseData(this.selectedCase);
          }
        }
        this.initAttachmentList();
@@ -438,6 +483,15 @@
          this.$refs.editForm && this.$refs.editForm.clearValidate();
        });
      }
    },
    selectedCase: {
      handler(newVal) {
        if (newVal && !this.isEdit) {
          this.fillFormWithCaseData(newVal);
        }
      },
      deep: true
    }
  },
  methods: {
@@ -472,6 +526,23 @@
        updateBy: undefined,
        updateTime: undefined,
        delFlag: 0
      };
    },
    /** 用案例数据填充表单 */
    fillFormWithCaseData(caseData) {
      this.formData = {
        ...this.formData,
        caseNo: caseData.caseNo || '',
        patName: caseData.name || '',
        sex: caseData.sex || '',
        age: caseData.age || '',
        diagnosisname: caseData.diagnosisname || '',
        treatmentHospitalName: caseData.treatmenthospitalname || '',
        treatmentDeptName: caseData.treatmentdeptname || '',
        // 可以设置一些默认值
        transportStartPlace: caseData.treatmenthospitalname || '',
        contactPerson: caseData.coordinatorName || ''
      };
    },
@@ -526,7 +597,7 @@
          createTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
          transportId: this.formData.id,
          delFlag: 0,
          caseNo:this.formData.caseNo
          caseNo: this.formData.caseNo
        };
        this.formData.annexfilesList.push(attachmentObj);
@@ -709,13 +780,12 @@
  font-size: 13px;
}
.file-path {
  font-size: 12px;
  color: #909399;
/* 案例信息展示样式 */
.selected-case-info {
  margin-bottom: 20px;
}
::v-deep .el-card__header {
  background: #f5f7fa;
  border-bottom: 1px solid #ebeef5;
.case-info-card {
  border-left: 4px solid #67c23a;
}
</style>
src/views/business/transfer/index.vue
@@ -281,11 +281,120 @@
        @close="detailOpen = false"
      />
    </el-dialog>
    <!-- 案例选择对话框 -->
    <el-dialog
      title="选择转运案例"
      :visible.sync="selectCaseOpen"
      width="900px"
      append-to-body
      :close-on-click-modal="false"
    >
      <div class="case-select-container">
        <el-card class="search-card">
          <el-form :model="caseQueryParams" inline>
            <el-form-item label="案例编号">
              <el-input
                v-model="caseQueryParams.caseNo"
                placeholder="请输入案例编号"
                clearable
                style="width: 180px"
              />
            </el-form-item>
            <el-form-item label="患者姓名">
              <el-input
                v-model="caseQueryParams.patName"
                placeholder="请输入患者姓名"
                clearable
                style="width: 150px"
              />
            </el-form-item>
            <el-form-item>
              <el-button type="primary" icon="el-icon-search" @click="searchCaseList"
                >搜索</el-button
              >
              <el-button icon="el-icon-refresh" @click="resetCaseSearch"
                >重置</el-button
              >
            </el-form-item>
          </el-form>
        </el-card>
        <el-card>
          <el-table
            v-loading="caseLoading"
            :data="availableCaseList"
            @row-click="handleCaseSelect"
            highlight-current-row
            style="width: 100%"
          >
            <el-table-column label="案例编号" prop="caseNo" width="150" align="center" />
            <el-table-column label="患者姓名" prop="name" width="120" align="center" />
            <el-table-column label="性别" width="80" align="center">
              <template slot-scope="scope">
                <dict-tag
                  :options="dict.type.sys_user_sex"
                  :value="scope.row.sex"
                />
              </template>
            </el-table-column>
            <el-table-column label="年龄" prop="age" width="80" align="center" />
            <el-table-column label="血型" width="80" align="center">
              <template slot-scope="scope">
                <dict-tag
                  :options="dict.type.sys_BloodType"
                  :value="scope.row.bloodType"
                />
              </template>
            </el-table-column>
            <el-table-column label="疾病诊断" prop="diagnosisname" min-width="200" show-overflow-tooltip />
            <el-table-column label="治疗医院" prop="treatmenthospitalname" width="150" />
            <el-table-column label="是否已转运" width="100" align="center">
              <template slot-scope="scope">
                <el-tag
                  :type="hasTransport(scope.row) ? 'danger' : 'success'"
                  size="small"
                >
                  {{ hasTransport(scope.row) ? '已转运' : '可转运' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column label="操作" width="80" align="center">
              <template slot-scope="scope">
                <el-button
                  type="text"
                  size="mini"
                  @click="selectCase(scope.row)"
                  :disabled="hasTransport(scope.row)"
                >
                  选择
                </el-button>
              </template>
            </el-table-column>
          </el-table>
          <!-- 分页 -->
          <pagination
            v-show="caseTotal > 0"
            :total="caseTotal"
            :page.sync="caseQueryParams.pageNum"
            :limit.sync="caseQueryParams.pageSize"
            @pagination="searchCaseList"
          />
        </el-card>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="selectCaseOpen = false">取消</el-button>
      </div>
    </el-dialog>
    <!-- 新增:修改弹框 -->
    <transport-edit
      :edit-open="editOpen"
      :transport-data="currentTransport"
      :is-edit="isEditing"
      :selected-case="selectedCase"
      @update:edit-open="editOpen = $event"
      @close="handleEditClose"
      @save-success="handleSaveSuccess"
@@ -318,7 +427,8 @@
  transportAdd,
  transportEdit,
  transportDelete,
  transportInfo
  transportInfo,
  donateList
} from "@/api/businessApi/index";
import TransportDetail from "./transportDetail";
import TransportEdit from "./TransportEdit";
@@ -329,6 +439,7 @@
  dicts: ["sys_user_sex", "sys_BloodType"],
  data() {
    return {
      // 遮罩层
      loading: false,
      // 选中数组
@@ -341,10 +452,12 @@
      showSearch: true,
      // 总条数
      total: 0,
      // 转运单表格数据 - 直接使用后端返回的数据结构
      // 转运单表格数据
      transportList: [],
      // 详情弹框是否显示
      detailOpen: false,
      // 案例选择弹框是否显示
      selectCaseOpen: false,
      // 操作确认弹框是否显示
      actionOpen: false,
      // 新增:修改弹框相关数据
@@ -366,28 +479,42 @@
        pendingTransports: 0,
        completedTransports: 0
      },
      // 查询参数(完全适配后端接口)
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        caseNo: undefined, // 案例编号
        patName: undefined, // 患者姓名
        transitStatus: undefined, // 转运状态:1待转运 2转运中 3转运完成 4转运取消 5暂存
        searchValue: undefined, // 搜索值
        params: {} // 其他参数对象
        caseNo: undefined,
        patName: undefined,
        transitStatus: undefined,
        searchValue: undefined,
        params: {}
      },
      // 案例选择相关
      caseLoading: false,
      availableCaseList: [],
      caseTotal: 0,
      selectedCase: null,
      caseQueryParams: {
        pageNum: 1,
        pageSize: 10,
        caseNo: undefined,
        patName: undefined,
        // 只查询已同意且需要转运的案例
        // reportStatus: "3", // 已同意
        isTransport: "2"   // 需要转运
      }
    };
  },
  created() {
    this.getList();
     this.checkAutoCreate();
  },
  methods: {
    /** 查询转运单列表 - 直接使用后端数据结构 */
    /** 查询转运单列表 */
    async getList() {
      this.loading = true;
      try {
        // 构建符合后端接口的请求参数 [1,2](@ref)
        const requestParams = {
          pageNum: this.queryParams.pageNum,
          pageSize: this.queryParams.pageSize,
@@ -397,20 +524,15 @@
          searchValue: this.queryParams.searchValue
        };
        // 处理时间范围参数 [5](@ref)
        if (this.dateRange && this.dateRange.length === 2) {
          requestParams.transportStartTime = this.dateRange.join(",");
        }
        // 调用正式接口 [3](@ref)
        const response = await transportList(requestParams);
        if (response.code === 200) {
          // 直接使用后端返回的数据结构,不进行转换 [8](@ref)
          this.transportList = response.rows || response.data || [];
          this.total = response.total || 0;
          // 更新统计数据
          this.updateStats();
        } else {
          this.$modal.msgError(response.msg || "获取转运单列表失败");
@@ -421,6 +543,109 @@
      } finally {
        this.loading = false;
      }
    },
  checkAutoCreate() {
    const query = this.$route.query;
    if (query.autoCreate === 'true') {
      // 自动打开案例选择弹框
      this.selectCaseOpen = true;
      this.resetCaseSearch();
      // 如果有特定的案例编号,可以预先搜索
      if (query.caseNo) {
        this.caseQueryParams.caseNo = query.caseNo;
        this.searchCaseList();
      }
    }
  },
    /** 搜索可用案例 */
    async searchCaseList() {
      this.caseLoading = true;
      try {
        const response = await donateList(this.caseQueryParams);
        if (response.code === 200) {
          this.availableCaseList = response.rows || response.data || [];
          this.caseTotal = response.total || 0;
        } else {
          this.$modal.msgError(response.msg || "获取案例列表失败");
        }
      } catch (error) {
        console.error("获取案例列表失败:", error);
        this.$modal.msgError("获取案例列表失败");
      } finally {
        this.caseLoading = false;
      }
    },
    /** 重置案例搜索 */
    resetCaseSearch() {
      this.caseQueryParams = {
        pageNum: 1,
        pageSize: 10,
        caseNo: undefined,
        patName: undefined,
        // reportStatus: "3",
        isTransport: "2"
      };
      this.searchCaseList();
    },
    /** 判断案例是否已有转运单 */
    hasTransport(caseData) {
      // 检查案例是否有转运信息
      if (caseData.serviceTransport &&
          Array.isArray(caseData.serviceTransport) &&
          caseData.serviceTransport.length > 0) {
        return true;
      }
      return false;
    },
    /** 选择案例 */
    selectCase(caseData) {
      if (this.hasTransport(caseData)) {
        this.$message.warning("该案例已有转运单,请选择其他案例");
        return;
      }
      this.selectedCase = caseData;
      this.selectCaseOpen = false;
      // 打开转运单编辑页面,并传入选中的案例
      this.currentTransport = this.convertCaseToTransport(caseData);
      this.isEditing = false;
      this.editOpen = true;
    },
    /** 将案例信息转换为转运单格式 */
    convertCaseToTransport(caseData) {
      return {
        caseNo: caseData.caseNo,
        patName: caseData.name,
        sex: caseData.sex,
        age: caseData.age,
        diagnosisname: caseData.diagnosisname,
        treatmentHospitalName: caseData.treatmenthospitalname,
        treatmentDeptName: caseData.treatmentdeptname,
        // 其他字段可以根据需要从案例中获取
        transportStartPlace: caseData.treatmenthospitalname || '',
        contactPerson: caseData.coordinatorName || '',
        transitStatus: 1, // 默认待转运
        // 清空其他字段
        id: undefined,
        reportId: undefined,
        transportStartTime: undefined,
        doctor: undefined,
        doctorPhone: undefined,
        nurse: undefined,
        nursePhone: undefined,
        driver: undefined,
        driverPhone: undefined,
        icuDoctor: undefined,
        icuDoctorPhone: undefined,
        annexfilesList: [],
        remark: undefined
      };
    },
    /** 获取状态标签类型 */
@@ -496,20 +721,24 @@
    handleUpdate(row) {
      this.currentTransport = row || {};
      this.isEditing = true;
      this.selectedCase = null; // 编辑时不传入案例信息
      this.editOpen = true;
    },
    /** 新建转运单 */
    handleAdd() {
      // 打开案例选择弹框
      this.selectCaseOpen = true;
      this.selectedCase = null;
      this.currentTransport = {};
      this.isEditing = false;
      this.editOpen = true;
      this.resetCaseSearch(); // 重置并搜索案例
    },
    /** 修改弹框关闭处理 */
    handleEditClose() {
      this.editOpen = false;
      this.currentTransport = {};
      this.selectedCase = null;
    },
    /** 保存成功处理 */
@@ -533,7 +762,7 @@
      this.actionOpen = true;
    },
    /** 确认操作 - 使用后端原始数据结构 [4](@ref) */
    /** 确认操作 */
    async confirmAction() {
      try {
        let requestData = {
@@ -546,12 +775,11 @@
          requestData.transitStatus = 3; // 设置为转运完成
        }
        // 直接传递后端需要的参数 [8](@ref)
        const response = await transportEdit(requestData);
        if (response.code === 200) {
          this.$modal.msgSuccess(`${this.actionText}转运成功`);
          this.getList(); // 刷新列表
          this.getList();
        } else {
          this.$modal.msgError(response.msg || `${this.actionText}转运失败`);
        }
@@ -562,7 +790,7 @@
      this.actionOpen = false;
    },
    /** 删除按钮操作 - 使用后端原始ID [1](@ref) */
    /** 删除按钮操作 */
    async handleDelete(row) {
      const ids = row.reportId || row.id || this.ids;
@@ -575,7 +803,7 @@
        if (response.code === 200) {
          this.$modal.msgSuccess("删除成功");
          this.getList(); // 刷新列表
          this.getList();
        } else {
          this.$modal.msgError(response.msg || "删除失败");
        }
@@ -589,7 +817,6 @@
    /** 导出按钮操作 */
    handleExport() {
      // 直接使用后端参数结构进行导出 [5](@ref)
      this.download(
        "system/transport/export",
        {
@@ -597,12 +824,25 @@
        },
        `transport_${new Date().getTime()}.xlsx`
      );
    },
    // 点击行选择案例
    handleCaseSelect(row) {
      this.selectCase(row);
    }
  }
};
</script>
<style scoped>
.case-select-container {
  max-height: 600px;
  overflow: auto;
}
.search-card {
  margin-bottom: 20px;
}
.filter-card {
  margin-bottom: 20px;
}