| ¶Ô±ÈÐÂÎļþ |
| | |
| | | export * from "./transport"; |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // æ¥è¯¢è½¬è¿åå表 |
| | | export function listTransport(query) { |
| | | return request({ |
| | | url: '/system/transport/list', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢è½¬è¿åè¯¦ç» |
| | | export function getTransport(id) { |
| | | return request({ |
| | | url: '/system/transport/' + id, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢è½¬è¿å |
| | | export function addTransport(data) { |
| | | return request({ |
| | | url: '/system/transport', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹è½¬è¿å |
| | | export function updateTransport(data) { |
| | | return request({ |
| | | url: '/system/transport', |
| | | method: 'put', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // å é¤è½¬è¿å |
| | | export function delTransport(id) { |
| | | return request({ |
| | | url: '/system/transport/' + id, |
| | | method: 'delete' |
| | | }) |
| | | } |
| | | |
| | | // æ´æ°è½¬è¿ç¶æ |
| | | export function updateTransportStatus(data) { |
| | | return request({ |
| | | url: '/system/transport/status', |
| | | method: 'put', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // 导åºè½¬è¿å |
| | | export function exportTransport(query) { |
| | | return request({ |
| | | url: '/system/transport/export', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="case-detail"> |
| | | <el-tabs v-model="activeTab"> |
| | | <el-tab-pane label="åºæ¬ä¿¡æ¯" name="basic"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="æç®ç¼å·">{{ caseData.donorNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="æç®è
å§å">{{ caseData.donorName }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ§å«"> |
| | | <dict-tag :options="genderOptions" :value="caseData.gender"/> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å¹´é¾">{{ caseData.age }}å²</el-descriptions-item> |
| | | <el-descriptions-item label="è¡å"> |
| | | <dict-tag :options="bloodTypeOptions" :value="caseData.bloodType"/> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="è¯ä»¶å·ç ">{{ caseData.idCardNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ°æ">{{ caseData.nation }}</el-descriptions-item> |
| | | <el-descriptions-item label="èç³»çµè¯">{{ caseData.phone }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä½å" :span="2">{{ caseData.address }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-tab-pane> |
| | | |
| | | <el-tab-pane label="å»çä¿¡æ¯" name="medical"> |
| | | <el-descriptions :column="1" border> |
| | | <el-descriptions-item label="ç¾ç
è¯æ">{{ caseData.diagnosis }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä½é¢å·">{{ caseData.inpatientNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="æå¨ç§å®¤">{{ caseData.departmentName }}</el-descriptions-item> |
| | | <el-descriptions-item label="主治å»ç">{{ caseData.doctorName }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¼ æç
æ
åµ">{{ caseData.infectiousDisease || 'æ ' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å»çè®°å½">{{ caseData.medicalRecord }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-tab-pane> |
| | | |
| | | <el-tab-pane label="å»é¢ä¿¡æ¯" name="hospital"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="å»é¢åç§°">{{ caseData.hospitalName }}</el-descriptions-item> |
| | | <el-descriptions-item label="å»é¢çº§å«">{{ caseData.hospitalLevel }}</el-descriptions-item> |
| | | <el-descriptions-item label="è系人">{{ caseData.contactPerson }}</el-descriptions-item> |
| | | <el-descriptions-item label="èç³»çµè¯">{{ caseData.contactPhone }}</el-descriptions-item> |
| | | <el-descriptions-item label="å»é¢å°å" :span="2">{{ caseData.hospitalAddress }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-tab-pane> |
| | | |
| | | <!-- æ°å¢éä»¶ä¿¡æ¯æ ç¾é¡µ --> |
| | | <el-tab-pane label="éä»¶ä¿¡æ¯" name="attachments"> |
| | | <el-card class="attachment-card"> |
| | | <div slot="header" class="clearfix"> |
| | | <span>éä»¶å表</span> |
| | | <el-button |
| | | style="float: right; padding: 3px 0" |
| | | type="text" |
| | | @click="handleUpload" |
| | | > |
| | | ä¸ä¼ éä»¶ |
| | | </el-button> |
| | | </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 |
| | | > |
| | | <el-button |
| | | size="mini" |
| | | type="danger" |
| | | @click="handleDelete(scope.row)" |
| | | >å é¤</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | </el-tab-pane> |
| | | |
| | | <el-tab-pane label="审æ¹ä¿¡æ¯" name="approval" v-if="caseData.status !== '0'"> |
| | | <el-descriptions :column="1" border> |
| | | <el-descriptions-item label="审æ¹ç»æ"> |
| | | <el-tag :type="caseData.status | statusFilter"> |
| | | {{ caseData.status | statusTextFilter }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å®¡æ¹æ¶é´">{{ caseData.approveTime }}</el-descriptions-item> |
| | | <el-descriptions-item label="审æ¹äºº">{{ caseData.approverName }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®¡æ¹æè§">{{ caseData.approveOpinion }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | |
| | | <!-- 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" |
| | | > |
| | | <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> |
| | | </el-dialog> |
| | | |
| | | <!-- å¾çé¢è§å¼¹çª --> |
| | | <el-dialog |
| | | :append-to-body="true" |
| | | :title="previewTitle" |
| | | :visible.sync="imagePreviewVisible" |
| | | width="60%" |
| | | top="10vh" |
| | | :close-on-click-modal="true" |
| | | > |
| | | <div class="image-preview-container"> |
| | | <img :src="previewUrl" alt="é¢è§å¾ç" class="preview-image" /> |
| | | </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> |
| | | |
| | | <div class="detail-footer"> |
| | | <el-button @click="handleClose">å
³é</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import pdf from "vue-pdf"; |
| | | |
| | | export default { |
| | | name: "CaseDetail", |
| | | components: { |
| | | pdf |
| | | }, |
| | | props: { |
| | | caseData: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | } |
| | | }, |
| | | 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 { |
| | | activeTab: 'basic', |
| | | genderOptions: [ |
| | | { value: "0", label: "ç·" }, |
| | | { value: "1", label: "女" } |
| | | ], |
| | | bloodTypeOptions: [ |
| | | { value: "A", label: "Aå" }, |
| | | { value: "B", label: "Bå" }, |
| | | { value: "O", label: "Oå" }, |
| | | { value: "AB", label: "ABå" } |
| | | ], |
| | | |
| | | // éä»¶ç¸å
³æ°æ® |
| | | attachmentList: [ |
| | | { |
| | | id: 1, |
| | | fileName: "æç®è
身份è¯.jpg", |
| | | fileType: "jpg", |
| | | fileSize: 1024000, |
| | | uploadTime: "2024-12-19 10:30:00", |
| | | fileUrl: "https://img95.699pic.com/photo/40142/8262.jpg_wh860.jpg" |
| | | }, |
| | | { |
| | | id: 2, |
| | | fileName: "å»çè¯æè¯æ.pdf", |
| | | fileType: "pdf", |
| | | fileSize: 2048000, |
| | | uploadTime: "2024-12-19 11:20:00", |
| | | fileUrl: |
| | | "http://192.168.100.10:8080/profile/upload/2025/12/19/(å´é¾8.7)æ¯æ¥å·¥ä½æ»ç»1766131266142.pdf" |
| | | }, |
| | | { |
| | | id: 3, |
| | | fileName: "æ£éªæ¥åå.jpg", |
| | | fileType: "docx", |
| | | fileSize: 512000, |
| | | uploadTime: "2024-12-19 14:15:00", |
| | | fileUrl: "https://img95.699pic.com/photo/40019/3490.jpg_wh860.jpg" |
| | | } |
| | | ], |
| | | |
| | | // PDFé¢è§ç¸å
³æ°æ® |
| | | pdfPreviewVisible: false, |
| | | pdfLoading: false, |
| | | pdfUrl: "", |
| | | currentPage: 1, |
| | | pageCount: 0, |
| | | scale: 100, |
| | | pageRotate: 0, |
| | | |
| | | // å¾çé¢è§ç¸å
³ |
| | | imagePreviewVisible: false, |
| | | |
| | | // 䏿¯æé¢è§ç¸å
³ |
| | | unsupportedPreviewVisible: false, |
| | | |
| | | // éç¨é¢è§æ°æ® |
| | | previewTitle: "", |
| | | previewUrl: "", |
| | | currentFile: null |
| | | }; |
| | | }, |
| | | methods: { |
| | | handleClose() { |
| | | this.$emit('close'); |
| | | }, |
| | | |
| | | // è·åæä»¶ç±»å |
| | | getFileType(fileName) { |
| | | const extension = fileName.split('.').pop().toLowerCase(); |
| | | const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"]; |
| | | const pdfTypes = ["pdf"]; |
| | | |
| | | if (imageTypes.includes(extension)) return "image"; |
| | | if (pdfTypes.includes(extension)) return "pdf"; |
| | | 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; |
| | | } |
| | | }, |
| | | |
| | | // PDFé¢è§æ¹æ³ |
| | | previewPdf(file) { |
| | | this.pdfPreviewVisible = true; |
| | | this.pdfLoading = true; |
| | | this.currentPage = 1; |
| | | this.scale = 100; |
| | | this.pageRotate = 0; |
| | | this.pdfUrl = file.fileUrl; |
| | | }, |
| | | |
| | | // PDFå è½½å®æåè° |
| | | loadPdfHandler() { |
| | | this.pdfLoading = false; |
| | | this.currentPage = 1; |
| | | }, |
| | | |
| | | // PDFå è½½é误å¤ç |
| | | 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) { |
| | | this.$message.info("å·²æ¾å¤§å°æå¤§æ¯ä¾"); |
| | | return; |
| | | } |
| | | this.scale += 10; |
| | | }, |
| | | |
| | | zoomOut() { |
| | | if (this.scale <= 50) { |
| | | this.$message.info("已缩å°å°æå°æ¯ä¾"); |
| | | return; |
| | | } |
| | | this.scale -= 10; |
| | | }, |
| | | |
| | | resetZoom() { |
| | | this.scale = 100; |
| | | }, |
| | | |
| | | // å¾çé¢è§æ¹æ³ |
| | | previewImage(file) { |
| | | this.imagePreviewVisible = true; |
| | | }, |
| | | |
| | | // 䏿¯æé¢è§çæä»¶ç±»å |
| | | previewUnsupported(file) { |
| | | this.unsupportedPreviewVisible = true; |
| | | }, |
| | | |
| | | // PDFå¯¹è¯æ¡å
³éå¤ç |
| | | 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("å¼å§ä¸è½½æä»¶"); |
| | | }, |
| | | |
| | | // ä¸ç¨PDFä¸è½½æ¹æ³ |
| | | downloadPdf(file) { |
| | | this.handleDownload(file); |
| | | }, |
| | | |
| | | // æä»¶å é¤ |
| | | handleDelete(file) { |
| | | this.$confirm("ç¡®å®è¦å é¤è¿ä¸ªéä»¶åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning" |
| | | }).then(() => { |
| | | this.attachmentList = this.attachmentList.filter( |
| | | item => item.id !== file.id |
| | | ); |
| | | this.$message.success("å 餿å"); |
| | | }); |
| | | }, |
| | | |
| | | // ä¸ä¼ éä»¶ |
| | | handleUpload() { |
| | | this.$message.info("ä¸ä¼ åè½å¾
å®ç°"); |
| | | }, |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | 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]; |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .case-detail { |
| | | padding: 0 20px; |
| | | } |
| | | |
| | | /* PDFé¢è§å¯¹è¯æ¡æ ·å¼ */ |
| | | .pdf-preview-dialog { |
| | | margin-top: 5vh !important; |
| | | } |
| | | |
| | | .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; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .zoom-controls { |
| | | margin: 0 15px; |
| | | } |
| | | |
| | | /* PDFè§å¾åºåæ ·å¼ */ |
| | | .pdf-viewport { |
| | | flex: 1; |
| | | overflow: auto; |
| | | padding: 20px; |
| | | background: #f8f9fa; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | /* å¾çé¢è§æ ·å¼ */ |
| | | .image-preview-container { |
| | | text-align: center; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | } |
| | | |
| | | .detail-footer { |
| | | text-align: center; |
| | | margin-top: 20px; |
| | | padding-top: 20px; |
| | | border-top: 1px solid #ebeef5; |
| | | } |
| | | |
| | | ::v-deep .el-descriptions__label { |
| | | width: 120px; |
| | | background-color: #f5f7fa; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- æç´¢çéåºå --> |
| | | <el-card class="filter-card"> |
| | | <el-form :model="queryParams" ref="queryForm" :inline="true" class="demo-form-inline"> |
| | | <el-form-item label="æç®ç¼å·" prop="donorNo"> |
| | | <el-input v-model="queryParams.donorNo" placeholder="请è¾å
¥æç®ç¼å·" clearable style="width: 200px"/> |
| | | </el-form-item> |
| | | <el-form-item label="æç®è
å§å" prop="donorName"> |
| | | <el-input v-model="queryParams.donorName" placeholder="请è¾å
¥æç®è
å§å" clearable style="width: 200px"/> |
| | | </el-form-item> |
| | | <el-form-item label="æ¡ä¾ç¶æ" prop="status"> |
| | | <el-select v-model="queryParams.status" placeholder="è¯·éæ©ç¶æ" clearable style="width: 200px"> |
| | | <el-option label="å
¨é¨" value=""/> |
| | | <el-option label="å¾
审æ¹" value="0"/> |
| | | <el-option label="å·²éè¿" value="1"/> |
| | | <el-option label="已驳å" value="2"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" icon="el-icon-search" @click="handleQuery">æç´¢</el-button> |
| | | <el-button icon="el-icon-refresh" @click="resetQuery">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- æä½æé®åºå --> |
| | | <el-row :gutter="10" class="mb8"> |
| | | <el-col :span="1.5"> |
| | | <el-button type="primary" plain icon="el-icon-plus" @click="handleAdd">æ°å¢æ¡ä¾</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button type="success" plain icon="el-icon-edit" :disabled="single" @click="handleUpdate">ä¿®æ¹</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button type="danger" plain icon="el-icon-delete" :disabled="multiple" @click="handleDelete">å é¤</el-button> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- æ°æ®è¡¨æ ¼ --> |
| | | <el-table v-loading="loading" :data="caseList" @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" width="55" align="center"/> |
| | | <el-table-column label="åºå·" type="index" width="60" align="center"/> |
| | | <el-table-column label="æç®ç¼å·" align="center" prop="donorNo" width="140"/> |
| | | <el-table-column label="æç®è
å§å" align="center" prop="donorName" width="100"/> |
| | | <el-table-column label="æ§å«" align="center" prop="gender" width="80"> |
| | | <template slot-scope="scope"> |
| | | <dict-tag :options="genderOptions" :value="scope.row.gender"/> |
| | | </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"> |
| | | <dict-tag :options="bloodTypeOptions" :value="scope.row.bloodType"/> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç¾ç
è¯æ" align="center" prop="diagnosis" min-width="200" show-overflow-tooltip/> |
| | | <el-table-column label="å»é¢åç§°" align="center" prop="hospitalName" width="150"/> |
| | | <el-table-column label="æ¡ä¾ç¶æ" align="center" prop="status" width="100"> |
| | | <template slot-scope="scope"> |
| | | <el-tag :type="scope.row.status | statusFilter"> |
| | | {{ scope.row.status | statusTextFilter }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="䏿¥æ¶é´" align="center" prop="reportTime" width="160"/> |
| | | <el-table-column label="æä½" align="center" class-name="small-padding fixed-width" width="200"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="mini" type="text" icon="el-icon-view" @click="handleDetail(scope.row)">详æ
</el-button> |
| | | <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">ä¿®æ¹</el-button> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-check" |
| | | @click="handleApprove(scope.row)" |
| | | v-if="scope.row.status === '0'" |
| | | >审æ¹</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <pagination |
| | | v-show="total>0" |
| | | :total="total" |
| | | :page.sync="queryParams.pageNum" |
| | | :limit.sync="queryParams.pageSize" |
| | | @pagination="getList" |
| | | /> |
| | | |
| | | <!-- æ¡ä¾è¯¦æ
å¼¹æ¡ --> |
| | | <el-dialog |
| | | :title="detailTitle" |
| | | :visible.sync="detailOpen" |
| | | width="900px" |
| | | append-to-body |
| | | :close-on-click-modal="false" |
| | | > |
| | | <case-detail :caseData="currentCase" @close="detailOpen = false"/> |
| | | </el-dialog> |
| | | |
| | | <!-- 审æ¹å¼¹æ¡ --> |
| | | <el-dialog |
| | | title="æ¡ä¾å®¡æ¹" |
| | | :visible.sync="approveOpen" |
| | | width="500px" |
| | | append-to-body |
| | | > |
| | | <el-form ref="approveForm" :model="approveForm" :rules="approveRules" label-width="80px"> |
| | | <el-form-item label="审æ¹ç»æ" prop="approveResult"> |
| | | <el-radio-group v-model="approveForm.approveResult"> |
| | | <el-radio label="1">éè¿</el-radio> |
| | | <el-radio label="2">驳å</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="å®¡æ¹æè§" prop="approveOpinion"> |
| | | <el-input |
| | | type="textarea" |
| | | v-model="approveForm.approveOpinion" |
| | | placeholder="请è¾å
¥å®¡æ¹æè§" |
| | | :rows="4" |
| | | maxlength="500" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button @click="approveOpen = false">å æ¶</el-button> |
| | | <el-button type="primary" @click="submitApprove">ç¡® å®</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import CaseDetail from './caseDetail'; |
| | | export default { |
| | | name: "CaseList", |
| | | components: { CaseDetail }, |
| | | |
| | | data() { |
| | | return { |
| | | // é®ç½©å± |
| | | loading: false, |
| | | // é䏿°ç» |
| | | ids: [], |
| | | // éå个ç¦ç¨ |
| | | single: true, |
| | | // éå¤ä¸ªç¦ç¨ |
| | | multiple: true, |
| | | // æ»æ¡æ° |
| | | total: 0, |
| | | // æ¡ä¾è¡¨æ ¼æ°æ® |
| | | caseList: [], |
| | | // 详æ
å¼¹æ¡æ¯å¦æ¾ç¤º |
| | | detailOpen: false, |
| | | // 审æ¹å¼¹æ¡æ¯å¦æ¾ç¤º |
| | | approveOpen: 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, |
| | | pageSize: 10, |
| | | donorNo: undefined, |
| | | donorName: undefined, |
| | | status: undefined |
| | | }, |
| | | // 审æ¹è¡¨å |
| | | approveForm: { |
| | | caseId: null, |
| | | approveResult: "1", |
| | | approveOpinion: "" |
| | | }, |
| | | // 审æ¹è¡¨åéªè¯ |
| | | approveRules: { |
| | | approveResult: [ |
| | | { required: true, message: "è¯·éæ©å®¡æ¹ç»æ", trigger: "change" } |
| | | ], |
| | | approveOpinion: [ |
| | | { required: true, message: "请è¾å
¥å®¡æ¹æè§", trigger: "blur" } |
| | | ] |
| | | } |
| | | }; |
| | | }, |
| | | filters: { |
| | | statusFilter(status) { |
| | | const statusMap = { |
| | | '0': 'warning', // å¾
å®¡æ¹ |
| | | '1': 'success', // å·²éè¿ |
| | | '2': 'danger' // 已驳å |
| | | }; |
| | | return statusMap[status]; |
| | | }, |
| | | statusTextFilter(status) { |
| | | const statusMap = { |
| | | '0': 'å¾
审æ¹', |
| | | '1': 'å·²éè¿', |
| | | '2': '已驳å' |
| | | }; |
| | | return statusMap[status]; |
| | | } |
| | | }, |
| | | created() { |
| | | this.getList(); |
| | | }, |
| | | methods: { |
| | | /** æ¥è¯¢æ¡ä¾å表 */ |
| | | getList() { |
| | | this.loading = true; |
| | | // 模æAPIè°ç¨å»¶è¿ |
| | | setTimeout(() => { |
| | | // æµè¯æ°æ® |
| | | this.caseList = [ |
| | | { |
| | | id: 1, |
| | | donorNo: 'DON20241219001', |
| | | donorName: 'å¼ ä¸', |
| | | gender: '0', |
| | | age: 38, |
| | | bloodType: 'A', |
| | | diagnosis: 'èå¤ä¼¤å¯¼è´èæ»äº¡ï¼ç»æ¢ææ æå®£å¸èæ»äº¡ãå®¶å±åæå¨å®æç®ã', |
| | | hospitalName: 'éå²å¤§å¦éå±å»é¢', |
| | | status: '0', |
| | | reportTime: '2024-12-19 09:30:00', |
| | | reporterName: 'æå»ç', |
| | | idCardNo: '370203198510123456', |
| | | nation: 'æ±æ', |
| | | phone: '13800138000', |
| | | address: 'å±±ä¸çéå²å¸å¸ååºé¦æ¸¯ä¸è·¯100å·', |
| | | inpatientNo: 'ZY20241219001', |
| | | departmentName: 'ç¥ç»å¤ç§', |
| | | doctorName: 'ç主任', |
| | | infectiousDisease: 'æ ', |
| | | medicalRecord: 'æ£è
å 交éäºæ
导è´ä¸¥éèå¤ä¼¤ï¼ç»æ¢ææ æå®£å¸èæ»äº¡ã', |
| | | hospitalLevel: 'ä¸çº§ç²ç', |
| | | contactPerson: 'å¼ æ¤å£«', |
| | | contactPhone: '13900139000', |
| | | hospitalAddress: 'å±±ä¸çéå²å¸å¸ååºæ±èè·¯1å·' |
| | | }, |
| | | { |
| | | id: 2, |
| | | donorNo: 'DON20241218001', |
| | | donorName: 'æå', |
| | | gender: '1', |
| | | age: 45, |
| | | bloodType: 'O', |
| | | diagnosis: 'æ¥æ§å¿èæ¢æ»ï¼å¿èåè½è¡°ç«', |
| | | hospitalName: 'éå²å¸ç«å»é¢', |
| | | status: '1', |
| | | reportTime: '2024-12-18 14:20:00', |
| | | approveTime: '2024-12-18 16:30:00', |
| | | reporterName: 'åå»ç', |
| | | approverName: 'å®¡æ ¸ä¸åA', |
| | | approveOpinion: 'èµæé½å
¨ï¼ç¬¦åæç®æ¡ä»¶ï¼åæéè¿ã' |
| | | }, |
| | | { |
| | | id: 3, |
| | | donorNo: 'DON20241217001', |
| | | donorName: 'çäº', |
| | | gender: '0', |
| | | age: 52, |
| | | bloodType: 'B', |
| | | diagnosis: 'é¢
å
åºè¡ï¼èå¹²åè½ä¸§å¤±', |
| | | hospitalName: 'éå²ç¼ç§å»é¢', |
| | | status: '2', |
| | | reportTime: '2024-12-17 10:15:00', |
| | | approveTime: '2024-12-17 14:20:00', |
| | | reporterName: 'éå»ç', |
| | | approverName: 'å®¡æ ¸ä¸åB', |
| | | approveOpinion: 'å®¶å±åæä¹¦ä¸å®æ´ï¼éè¡¥å
ææåéæ°æäº¤ã' |
| | | }, |
| | | { |
| | | id: 4, |
| | | donorNo: 'DON20241216001', |
| | | donorName: 'èµµå
', |
| | | gender: '1', |
| | | age: 28, |
| | | bloodType: 'AB', |
| | | diagnosis: 'éåé¢
èæä¼¤ï¼å¤å¨å®åè½è¡°ç«', |
| | | hospitalName: 'éå²å¿ç«¥å»é¢', |
| | | status: '0', |
| | | reportTime: '2024-12-16 16:45:00', |
| | | reporterName: 'åå»ç' |
| | | } |
| | | ]; |
| | | this.total = this.caseList.length; |
| | | this.loading = false; |
| | | }, 500); |
| | | }, |
| | | // å¤éæ¡é䏿°æ® |
| | | handleSelectionChange(selection) { |
| | | this.ids = selection.map(item => item.id); |
| | | this.single = selection.length !== 1; |
| | | this.multiple = !selection.length; |
| | | }, |
| | | /** æç´¢æé®æä½ */ |
| | | handleQuery() { |
| | | this.queryParams.pageNum = 1; |
| | | this.getList(); |
| | | }, |
| | | /** éç½®æé®æä½ */ |
| | | resetQuery() { |
| | | this.resetForm("queryForm"); |
| | | this.handleQuery(); |
| | | }, |
| | | /** 详æ
æé®æä½ */ |
| | | handleDetail(row) { |
| | | this.currentCase = row; |
| | | this.detailTitle = `æ¡ä¾è¯¦æ
- ${row.donorNo}`; |
| | | this.detailOpen = true; |
| | | }, |
| | | /** å®¡æ¹æé®æä½ */ |
| | | handleApprove(row) { |
| | | this.currentCase = row; |
| | | this.approveForm.caseId = row.id; |
| | | this.approveForm.approveResult = "1"; |
| | | this.approveForm.approveOpinion = ""; |
| | | this.approveOpen = true; |
| | | }, |
| | | /** æäº¤å®¡æ¹ */ |
| | | submitApprove() { |
| | | this.$refs["approveForm"].validate(valid => { |
| | | if (valid) { |
| | | // 模æå®¡æ¹æäº¤ |
| | | this.$modal.msgSuccess("å®¡æ¹æå"); |
| | | this.approveOpen = false; |
| | | // æ´æ°æ¡ä¾ç¶æ |
| | | const caseItem = this.caseList.find(item => item.id === this.approveForm.caseId); |
| | | if (caseItem) { |
| | | caseItem.status = this.approveForm.approveResult; |
| | | caseItem.approveTime = new Date().toLocaleString(); |
| | | caseItem.approverName = 'å½åç¨æ·'; |
| | | caseItem.approveOpinion = this.approveForm.approveOpinion; |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | /** æ°å¢æé®æä½ */ |
| | | handleAdd() { |
| | | this.$router.push('/case/add'); |
| | | }, |
| | | /** ä¿®æ¹æé®æä½ */ |
| | | handleUpdate(row) { |
| | | const id = row.id || this.ids[0]; |
| | | this.$router.push('/case/edit/' + id); |
| | | }, |
| | | /** å é¤æé®æä½ */ |
| | | handleDelete(row) { |
| | | const ids = row.id || this.ids; |
| | | this.$modal.confirm('æ¯å¦ç¡®è®¤å 餿¡ä¾ç¼å·ä¸º"' + ids + '"çæ°æ®é¡¹ï¼').then(() => { |
| | | // 模æå é¤æä½ |
| | | this.caseList = this.caseList.filter(item => !ids.includes(item.id)); |
| | | this.total = this.caseList.length; |
| | | this.$modal.msgSuccess("å 餿å"); |
| | | }).catch(() => {}); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .filter-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | .mb8 { |
| | | margin-bottom: 8px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- æç´¢çéåºå --> |
| | | <el-card class="filter-card"> |
| | | <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="80px"> |
| | | <el-form-item label="转è¿åå·" prop="transportNo"> |
| | | <el-input |
| | | v-model="queryParams.transportNo" |
| | | placeholder="请è¾å
¥è½¬è¿åå·" |
| | | clearable |
| | | style="width: 200px" |
| | | @keyup.enter.native="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="æ¡ä¾ç¼å·" prop="caseNo"> |
| | | <el-input |
| | | v-model="queryParams.caseNo" |
| | | placeholder="请è¾å
¥æ¡ä¾ç¼å·" |
| | | clearable |
| | | style="width: 200px" |
| | | @keyup.enter.native="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="æç®è
å§å" prop="donorName"> |
| | | <el-input |
| | | v-model="queryParams.donorName" |
| | | placeholder="请è¾å
¥æç®è
å§å" |
| | | clearable |
| | | style="width: 200px" |
| | | @keyup.enter.native="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="转è¿ç¶æ" prop="status"> |
| | | <el-select v-model="queryParams.status" placeholder="转è¿ç¶æ" clearable style="width: 200px"> |
| | | <el-option label="å
¨é¨" value=""/> |
| | | <el-option label="å¾
åºå" value="pending"/> |
| | | <el-option label="转è¿ä¸" value="transporting"/> |
| | | <el-option label="已宿" value="completed"/> |
| | | <el-option label="已忶" value="cancelled"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å建æ¶é´"> |
| | | <el-date-picker |
| | | v-model="dateRange" |
| | | style="width: 240px" |
| | | value-format="yyyy-MM-dd" |
| | | type="daterange" |
| | | range-separator="-" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | ></el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" icon="el-icon-search" @click="handleQuery">æç´¢</el-button> |
| | | <el-button icon="el-icon-refresh" @click="resetQuery">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- ç»è®¡å¡ç --> |
| | | <el-row :gutter="20" class="stats-row"> |
| | | <el-col :span="8"> |
| | | <el-card class="stats-card total"> |
| | | <div class="stat-content"> |
| | | <div class="stat-icon">ð¦</div> |
| | | <div class="stat-info"> |
| | | <div class="stat-count">{{ stats.totalTransports }}</div> |
| | | <div class="stat-label">æ»è½¬è¿å</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-card class="stats-card pending"> |
| | | <div class="stat-content"> |
| | | <div class="stat-icon">â³</div> |
| | | <div class="stat-info"> |
| | | <div class="stat-count">{{ stats.pendingTransports }}</div> |
| | | <div class="stat-label">å¾
åºå</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-card class="stats-card completed"> |
| | | <div class="stat-content"> |
| | | <div class="stat-icon">â
</div> |
| | | <div class="stat-info"> |
| | | <div class="stat-count">{{ stats.completedTransports }}</div> |
| | | <div class="stat-label">已宿</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- æä½æé®åºå --> |
| | | <el-row :gutter="10" class="mb8"> |
| | | <el-col :span="1.5"> |
| | | <el-button type="primary" plain icon="el-icon-plus" @click="handleAdd">æ°å»ºè½¬è¿å</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button type="success" plain icon="el-icon-edit" :disabled="single" @click="handleUpdate">ä¿®æ¹</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button type="danger" plain icon="el-icon-delete" :disabled="multiple" @click="handleDelete">å é¤</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button type="warning" plain icon="el-icon-download" @click="handleExport">导åº</el-button> |
| | | </el-col> |
| | | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| | | </el-row> |
| | | |
| | | <!-- æ°æ®è¡¨æ ¼ --> |
| | | <el-table v-loading="loading" :data="transportList" @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" width="55" align="center"/> |
| | | <el-table-column label="åºå·" type="index" width="60" align="center"/> |
| | | <el-table-column label="转è¿åå·" align="center" prop="id" width="140"/> |
| | | <el-table-column label="æ¡ä¾ç¼å·" align="center" prop="caseNo" width="140"/> |
| | | <el-table-column label="æç®è
ä¿¡æ¯" align="center" width="180"> |
| | | <template slot-scope="scope"> |
| | | <div class="donor-info"> |
| | | <div class="donor-name">{{ scope.row.donorName }}</div> |
| | | <div class="donor-details">{{ scope.row.gender }} | {{ scope.row.age }}å²</div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç¾ç
è¯æ" align="center" prop="diagnosis" min-width="200" show-overflow-tooltip/> |
| | | <el-table-column label="å»çæºæ" align="center" prop="hospitalName" width="150"/> |
| | | <el-table-column label="计åè½¬è¿æ¶é´" align="center" prop="transportTime" width="160"/> |
| | | <el-table-column label="è´è´£åè°å" align="center" prop="coordinator" width="100"/> |
| | | <el-table-column label="转è¿ç¶æ" align="center" prop="status" width="100"> |
| | | <template slot-scope="scope"> |
| | | <el-tag :type="scope.row.status | statusFilter"> |
| | | {{ scope.row.statusText }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å建æ¶é´" align="center" prop="createTime" width="160"/> |
| | | <el-table-column label="æä½" align="center" class-name="small-padding fixed-width" width="220"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="mini" type="text" icon="el-icon-view" @click="handleDetail(scope.row)">详æ
</el-button> |
| | | <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">ç¼è¾</el-button> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-video-play" |
| | | @click="handleStartTransport(scope.row)" |
| | | v-if="scope.row.status === 'pending'" |
| | | >å¼å§è½¬è¿</el-button> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-check" |
| | | @click="handleCompleteTransport(scope.row)" |
| | | v-if="scope.row.status === 'transporting'" |
| | | >å®æè½¬è¿</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <pagination |
| | | v-show="total>0" |
| | | :total="total" |
| | | :page.sync="queryParams.pageNum" |
| | | :limit.sync="queryParams.pageSize" |
| | | @pagination="getList" |
| | | /> |
| | | |
| | | <!-- 转è¿å详æ
å¼¹æ¡ --> |
| | | <el-dialog |
| | | :title="detailTitle" |
| | | :visible.sync="detailOpen" |
| | | width="1000px" |
| | | append-to-body |
| | | :close-on-click-modal="false" |
| | | > |
| | | <transport-detail :transportData="currentTransport" @close="detailOpen = false"/> |
| | | </el-dialog> |
| | | |
| | | <!-- æä½ç¡®è®¤å¼¹æ¡ --> |
| | | <el-dialog |
| | | :title="actionTitle" |
| | | :visible.sync="actionOpen" |
| | | width="500px" |
| | | append-to-body |
| | | > |
| | | <div class="action-confirm"> |
| | | <p>ç¡®å®è¦{{ actionText }}转è¿å "{{ currentTransport.id }}" åï¼</p> |
| | | </div> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button @click="actionOpen = false">å æ¶</el-button> |
| | | <el-button type="primary" @click="confirmAction">ç¡® å®</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { listTransport, getTransport, delTransport, updateTransportStatus } from "@/api/system/business"; |
| | | import TransportDetail from './transportDetail'; |
| | | |
| | | export default { |
| | | name: "TransportList", |
| | | components: { TransportDetail }, |
| | | data() { |
| | | return { |
| | | // é®ç½©å± |
| | | loading: false, |
| | | // é䏿°ç» |
| | | ids: [], |
| | | // éå个ç¦ç¨ |
| | | single: true, |
| | | // éå¤ä¸ªç¦ç¨ |
| | | multiple: true, |
| | | // æ¾ç¤ºæç´¢æ¡ä»¶ |
| | | showSearch: true, |
| | | // æ»æ¡æ° |
| | | total: 0, |
| | | // 转è¿åè¡¨æ ¼æ°æ® |
| | | transportList: [], |
| | | // 详æ
å¼¹æ¡æ¯å¦æ¾ç¤º |
| | | detailOpen: false, |
| | | // æä½ç¡®è®¤å¼¹æ¡æ¯å¦æ¾ç¤º |
| | | actionOpen: false, |
| | | // 详æ
å¼¹æ¡æ é¢ |
| | | detailTitle: "", |
| | | // æä½ç¡®è®¤æ é¢ |
| | | actionTitle: "", |
| | | // æä½ææ¬ |
| | | actionText: "", |
| | | // å½åæä½ç转è¿å |
| | | currentTransport: {}, |
| | | // æ¥æèå´ |
| | | dateRange: [], |
| | | // ç»è®¡æ°æ® |
| | | stats: { |
| | | totalTransports: 0, |
| | | pendingTransports: 0, |
| | | completedTransports: 0 |
| | | }, |
| | | // æ¥è¯¢åæ° |
| | | queryParams: { |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | transportNo: undefined, |
| | | caseNo: undefined, |
| | | donorName: undefined, |
| | | status: undefined |
| | | } |
| | | }; |
| | | }, |
| | | filters: { |
| | | statusFilter(status) { |
| | | const statusMap = { |
| | | 'pending': 'warning', |
| | | 'transporting': 'primary', |
| | | 'completed': 'success', |
| | | 'cancelled': 'danger' |
| | | }; |
| | | return statusMap[status]; |
| | | } |
| | | }, |
| | | created() { |
| | | this.getList(); |
| | | }, |
| | | methods: { |
| | | /** æ¥è¯¢è½¬è¿åå表 */ |
| | | getList() { |
| | | this.loading = true; |
| | | // 模æAPIè°ç¨å»¶è¿ |
| | | setTimeout(() => { |
| | | // æµè¯æ°æ® |
| | | this.transportList = [ |
| | | { |
| | | id: 'T20241217001', |
| | | caseNo: 'DON20241216001', |
| | | donorName: 'å¼ ä¸', |
| | | gender: 'ç·', |
| | | age: 38, |
| | | diagnosis: 'èå¤ä¼¤å¯¼è´èæ»äº¡ï¼ç»æ¢ææ æå®£å¸èæ»äº¡ãå®¶å±åæå¨å®æç®ã', |
| | | hospitalName: 'éå²éæ¹å»é¢', |
| | | transportTime: '2024-12-17 14:30:00', |
| | | coordinator: 'å¼ å»ç', |
| | | createTime: '2024-12-16 09:30:00', |
| | | status: 'pending', |
| | | statusText: 'å¾
åºå', |
| | | departureLocation: 'éå²å¸ç«å»é¢æ¥è¯ç§', |
| | | destinationHospital: 'éå²éæ¹å»é¢', |
| | | emergencyDoctor: 'çå»ç', |
| | | nurse: 'ææ¤å£«', |
| | | driver: 'åå¸å
', |
| | | icuDoctor: 'èµµå»ç', |
| | | contacts: [ |
| | | { role: 'åè°åçµè¯', phone: '13800138000' }, |
| | | { role: 'æ¥è¯å»ççµè¯', phone: '13800138001' }, |
| | | { role: 'æ¤å£«çµè¯', phone: '13800138002' }, |
| | | { role: '叿ºçµè¯', phone: '13800138003' }, |
| | | { role: 'ICUå»ççµè¯', phone: '13800138004' } |
| | | ], |
| | | remarks: 'éè¦åå¤å¼å¸æºçæ¥æè®¾å¤' |
| | | }, |
| | | { |
| | | id: 'T20241217002', |
| | | caseNo: 'DON20241216002', |
| | | donorName: 'æå', |
| | | gender: '女', |
| | | age: 45, |
| | | diagnosis: 'èæ¢æ»ï¼èå¹²åè½ä¸§å¤±', |
| | | hospitalName: 'éå²å¤§å¦éå±å»é¢', |
| | | transportTime: '2024-12-17 16:00:00', |
| | | coordinator: 'æå»ç', |
| | | createTime: '2024-12-16 11:20:00', |
| | | status: 'transporting', |
| | | statusText: '转è¿ä¸', |
| | | departureLocation: 'éå²å¤§å¦éå±å»é¢ICU', |
| | | destinationHospital: 'éå²å¨å®ç§»æ¤ä¸å¿', |
| | | currentLocation: 'éå²å¸ååºé¦æ¸¯ä¸è·¯', |
| | | estimatedTime: '30åé' |
| | | }, |
| | | { |
| | | id: 'T20241216003', |
| | | caseNo: 'DON20241215001', |
| | | donorName: 'çäº', |
| | | gender: 'ç·', |
| | | age: 52, |
| | | diagnosis: 'å¿è骤åï¼å¤å¨å®åè½è¡°ç«', |
| | | hospitalName: 'éå²å¸ç«å»é¢', |
| | | transportTime: '2024-12-16 10:15:00', |
| | | coordinator: 'çå»ç', |
| | | createTime: '2024-12-15 14:45:00', |
| | | status: 'completed', |
| | | statusText: '已宿', |
| | | departureLocation: 'éå²å¸ç«å»é¢å¿å
ç§', |
| | | destinationHospital: 'éå²å¨å®ç§»æ¤ä¸å¿', |
| | | completedTime: '2024-12-16 12:30:00', |
| | | distance: '15å
Ž', |
| | | duration: '2å°æ¶15åé' |
| | | }, |
| | | { |
| | | id: 'T20241216004', |
| | | caseNo: 'DON20241214001', |
| | | donorName: 'èµµå
', |
| | | gender: '女', |
| | | age: 29, |
| | | diagnosis: 'æ¥æ§èè¡°ç«', |
| | | hospitalName: 'éå²ç§å¤§å»é¢', |
| | | transportTime: '2024-12-16 08:30:00', |
| | | coordinator: 'èµµå»ç', |
| | | createTime: '2024-12-14 16:20:00', |
| | | status: 'cancelled', |
| | | statusText: '已忶', |
| | | cancelReason: 'å®¶å±ä¸´æ¶æ¹åå³å®' |
| | | } |
| | | ]; |
| | | |
| | | // æ´æ°ç»è®¡æ°æ® |
| | | this.updateStats(); |
| | | this.total = this.transportList.length; |
| | | this.loading = false; |
| | | }, 500); |
| | | }, |
| | | |
| | | // æ´æ°ç»è®¡æ°æ® |
| | | updateStats() { |
| | | this.stats.totalTransports = this.transportList.length; |
| | | this.stats.pendingTransports = this.transportList.filter(item => item.status === 'pending').length; |
| | | this.stats.completedTransports = this.transportList.filter(item => item.status === 'completed').length; |
| | | }, |
| | | |
| | | // å¤éæ¡é䏿°æ® |
| | | handleSelectionChange(selection) { |
| | | this.ids = selection.map(item => item.id); |
| | | this.single = selection.length !== 1; |
| | | this.multiple = !selection.length; |
| | | }, |
| | | |
| | | /** æç´¢æé®æä½ */ |
| | | handleQuery() { |
| | | this.queryParams.pageNum = 1; |
| | | this.getList(); |
| | | }, |
| | | |
| | | /** éç½®æé®æä½ */ |
| | | resetQuery() { |
| | | this.dateRange = []; |
| | | this.resetForm("queryForm"); |
| | | this.handleQuery(); |
| | | }, |
| | | |
| | | /** 详æ
æé®æä½ */ |
| | | handleDetail(row) { |
| | | this.currentTransport = row; |
| | | this.detailTitle = `转è¿å详æ
- ${row.id}`; |
| | | this.detailOpen = true; |
| | | }, |
| | | |
| | | /** å¼å§è½¬è¿æä½ */ |
| | | handleStartTransport(row) { |
| | | this.currentTransport = row; |
| | | this.actionTitle = 'å¼å§è½¬è¿'; |
| | | this.actionText = 'å¼å§'; |
| | | this.actionOpen = true; |
| | | }, |
| | | |
| | | /** å®æè½¬è¿æä½ */ |
| | | handleCompleteTransport(row) { |
| | | this.currentTransport = row; |
| | | this.actionTitle = 'å®æè½¬è¿'; |
| | | this.actionText = '宿'; |
| | | this.actionOpen = true; |
| | | }, |
| | | |
| | | /** 确认æä½ */ |
| | | confirmAction() { |
| | | const index = this.transportList.findIndex(item => item.id === this.currentTransport.id); |
| | | if (index !== -1) { |
| | | if (this.actionText === 'å¼å§') { |
| | | this.transportList[index].status = 'transporting'; |
| | | this.transportList[index].statusText = '转è¿ä¸'; |
| | | } else if (this.actionText === '宿') { |
| | | this.transportList[index].status = 'completed'; |
| | | this.transportList[index].statusText = '已宿'; |
| | | this.transportList[index].completedTime = new Date().toLocaleString(); |
| | | } |
| | | |
| | | // æ´æ°ç»è®¡æ°æ® |
| | | this.updateStats(); |
| | | |
| | | this.$modal.msgSuccess(`${this.actionText}æå`); |
| | | } |
| | | this.actionOpen = false; |
| | | }, |
| | | |
| | | /** æ°å¢æé®æä½ */ |
| | | handleAdd() { |
| | | this.$router.push('/transport/add'); |
| | | }, |
| | | |
| | | /** ä¿®æ¹æé®æä½ */ |
| | | handleUpdate(row) { |
| | | const id = row.id || this.ids[0]; |
| | | this.$router.push('/transport/edit/' + id); |
| | | }, |
| | | |
| | | /** å é¤æé®æä½ */ |
| | | handleDelete(row) { |
| | | const ids = row.id || this.ids; |
| | | this.$modal.confirm('æ¯å¦ç¡®è®¤å é¤è½¬è¿åç¼å·ä¸º"' + ids + '"çæ°æ®é¡¹ï¼').then(() => { |
| | | // 模æå é¤æä½ |
| | | this.transportList = this.transportList.filter(item => !ids.includes(item.id)); |
| | | this.total = this.transportList.length; |
| | | this.updateStats(); |
| | | this.$modal.msgSuccess("å 餿å"); |
| | | }).catch(() => {}); |
| | | }, |
| | | |
| | | /** å¯¼åºæé®æä½ */ |
| | | handleExport() { |
| | | this.download('system/transport/export', { |
| | | ...this.queryParams |
| | | }, `transport_${new Date().getTime()}.xlsx`) |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .filter-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .stats-row { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .stats-card { |
| | | border-radius: 8px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .stats-card:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .stats-card.total { |
| | | border-left: 4px solid #409EFF; |
| | | } |
| | | |
| | | .stats-card.pending { |
| | | border-left: 4px solid #E6A23C; |
| | | } |
| | | |
| | | .stats-card.completed { |
| | | border-left: 4px solid #67C23A; |
| | | } |
| | | |
| | | .stat-content { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 10px; |
| | | } |
| | | |
| | | .stat-icon { |
| | | font-size: 40px; |
| | | margin-right: 15px; |
| | | } |
| | | |
| | | .stat-info { |
| | | flex: 1; |
| | | } |
| | | |
| | | .stat-count { |
| | | font-size: 28px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .donor-info { |
| | | text-align: left; |
| | | } |
| | | |
| | | .donor-name { |
| | | font-weight: 500; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .donor-details { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .mb8 { |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .action-confirm { |
| | | text-align: center; |
| | | font-size: 16px; |
| | | padding: 20px 0; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="transport-detail"> |
| | | <el-tabs v-model="activeTab"> |
| | | <!-- åºç¡ä¿¡æ¯ --> |
| | | <el-tab-pane label="åºç¡ä¿¡æ¯" name="basic"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="转è¿åå·">{{ |
| | | transportData.id |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ¡ä¾ç¼å·">{{ |
| | | transportData.caseNo |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æç®è
å§å">{{ |
| | | transportData.donorName |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ§å«">{{ |
| | | transportData.gender |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¹´é¾" |
| | | >{{ transportData.age }}å²</el-descriptions-item |
| | | > |
| | | <el-descriptions-item label="ç¾ç
è¯æ">{{ |
| | | transportData.diagnosis |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="åºåå»é¢">{{ |
| | | transportData.hospitalName |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç®çå»é¢">{{ |
| | | transportData.destinationHospital |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="计åè½¬è¿æ¶é´">{{ |
| | | transportData.transportTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="è´è´£åè°å">{{ |
| | | transportData.coordinator |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="转è¿ç¶æ"> |
| | | <el-tag :type="transportData.status | statusFilter"> |
| | | {{ transportData.statusText }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å建æ¶é´">{{ |
| | | transportData.createTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item |
| | | label="宿æ¶é´" |
| | | v-if="transportData.completedTime" |
| | | > |
| | | {{ transportData.completedTime }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-tab-pane> |
| | | |
| | | <!-- 转è¿è¯¦æ
--> |
| | | <el-tab-pane label="转è¿è¯¦æ
" name="transport"> |
| | | <el-descriptions :column="1" border> |
| | | <el-descriptions-item label="åºåå°ç¹">{{ |
| | | transportData.departureLocation |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç®çå°">{{ |
| | | transportData.destinationHospital |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item |
| | | label="å½åä½ç½®" |
| | | v-if="transportData.currentLocation" |
| | | > |
| | | {{ transportData.currentLocation }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item |
| | | label="é¢è®¡å°è¾¾æ¶é´" |
| | | v-if="transportData.estimatedTime" |
| | | > |
| | | {{ transportData.estimatedTime }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="转è¿è·ç¦»" v-if="transportData.distance"> |
| | | {{ transportData.distance }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="è½¬è¿æ¶é¿" v-if="transportData.duration"> |
| | | {{ transportData.duration }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-tab-pane> |
| | | |
| | | <!-- å¢éæå --> |
| | | <el-tab-pane label="å¢éæå" name="team"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="åè°å">{{ |
| | | transportData.coordinator |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè°åçµè¯"> |
| | | {{ getContactPhone("åè°åçµè¯") }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item |
| | | label="æ¥è¯ç§å»ç" |
| | | v-if="transportData.emergencyDoctor" |
| | | > |
| | | {{ transportData.emergencyDoctor }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æ¥è¯å»ççµè¯"> |
| | | {{ getContactPhone("æ¥è¯å»ççµè¯") }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æ¤å£«" v-if="transportData.nurse"> |
| | | {{ transportData.nurse }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æ¤å£«çµè¯"> |
| | | {{ getContactPhone("æ¤å£«çµè¯") }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="叿º" v-if="transportData.driver"> |
| | | {{ transportData.driver }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="叿ºçµè¯"> |
| | | {{ getContactPhone("叿ºçµè¯") }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item |
| | | label="ICUè¯ä¼°å»ç" |
| | | v-if="transportData.icuDoctor" |
| | | > |
| | | {{ transportData.icuDoctor }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ICUå»ççµè¯"> |
| | | {{ getContactPhone("ICUå»ççµè¯") }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-tab-pane> |
| | | <el-tab-pane label="éä»¶ä¿¡æ¯" name="attachments"> |
| | | <el-card class="attachment-card"> |
| | | <div slot="header" class="clearfix"> |
| | | <span>éä»¶å表</span> |
| | | <!-- <el-button |
| | | style="float: right; padding: 3px 0" |
| | | type="text" |
| | | @click="handleUpload" |
| | | > |
| | | ä¸ä¼ éä»¶ |
| | | </el-button> --> |
| | | </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 |
| | | > |
| | | <el-button |
| | | size="mini" |
| | | type="danger" |
| | | @click="handleDelete(scope.row)" |
| | | >å é¤</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | </el-tab-pane> |
| | | <!-- 夿³¨ä¿¡æ¯ --> |
| | | <el-tab-pane label="夿³¨ä¿¡æ¯" name="remarks" v-if="transportData.remarks"> |
| | | <el-card> |
| | | <div class="remarks-content"> |
| | | {{ transportData.remarks }} |
| | | </div> |
| | | </el-card> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | <!-- 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" |
| | | > |
| | | <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> |
| | | </el-dialog> |
| | | |
| | | <!-- å¾çé¢è§å¼¹çªï¼ä½¿ç¨Element UIèªå¸¦é¢è§ï¼ --> |
| | | <el-dialog |
| | | :append-to-body="true" |
| | | :title="previewTitle" |
| | | :visible.sync="imagePreviewVisible" |
| | | width="60%" |
| | | top="10vh" |
| | | :close-on-click-modal="true" |
| | | > |
| | | <div class="image-preview-container"> |
| | | <img :src="previewUrl" alt="é¢è§å¾ç" class="preview-image" /> |
| | | </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> |
| | | <div class="detail-footer"> |
| | | <el-button @click="handleClose">å
³é</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import pdf from "vue-pdf"; |
| | | |
| | | export default { |
| | | name: "TransportDetail", |
| | | components: { |
| | | pdf |
| | | }, |
| | | props: { |
| | | transportData: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | } |
| | | }, |
| | | filters: { |
| | | statusFilter(status) { |
| | | const statusMap = { |
| | | pending: "warning", |
| | | transporting: "primary", |
| | | completed: "success", |
| | | cancelled: "danger" |
| | | }; |
| | | return statusMap[status]; |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | activeTab: "basic", |
| | | // éä»¶ç¸å
³æ°æ® |
| | | attachmentList: [ |
| | | { |
| | | id: 1, |
| | | fileName: "转è¿äº¤æ¥å.jpg", |
| | | fileType: "docx", |
| | | fileSize: 102400, |
| | | uploadTime: "2024-12-19 10:30:00", |
| | | fileUrl: "https://img95.699pic.com/photo/40142/8262.jpg_wh860.jpg" |
| | | }, |
| | | { |
| | | id: 2, |
| | | fileName: "å»çè®°å½.pdf", |
| | | fileType: "pdf", |
| | | fileSize: 2048000, |
| | | uploadTime: "2024-12-19 11:20:00", |
| | | fileUrl: |
| | | "http://192.168.100.10:8080/profile/upload/2025/12/19/(å´é¾8.7)æ¯æ¥å·¥ä½æ»ç»1766131266142.pdf" |
| | | }, |
| | | { |
| | | id: 3, |
| | | fileName: "æ£è
ç
§ç.jpg", |
| | | fileType: "jpg", |
| | | fileSize: 512000, |
| | | uploadTime: "2024-12-19 14:15:00", |
| | | fileUrl: "https://img95.699pic.com/photo/40019/3490.jpg_wh860.jpg" |
| | | } |
| | | ], |
| | | // PDFé¢è§ç¸å
³æ°æ® |
| | | pdfPreviewVisible: false, |
| | | pdfLoading: false, |
| | | pdfUrl: "", |
| | | currentPage: 1, |
| | | pageCount: 0, |
| | | scale: 100, |
| | | pageRotate: 0, |
| | | |
| | | // å¾çé¢è§ç¸å
³ |
| | | imagePreviewVisible: false, |
| | | |
| | | // 䏿¯æé¢è§ç¸å
³ |
| | | unsupportedPreviewVisible: false, |
| | | |
| | | // éç¨é¢è§æ°æ® |
| | | previewTitle: "", |
| | | previewUrl: "", |
| | | currentFile: null |
| | | }; |
| | | }, |
| | | methods: { |
| | | // è·åèç³»æ¹å¼ |
| | | getContactPhone(role) { |
| | | if (this.transportData.contacts) { |
| | | const contact = this.transportData.contacts.find( |
| | | item => item.role === role |
| | | ); |
| | | return contact ? contact.phone : "æªå¡«å"; |
| | | } |
| | | return "æªå¡«å"; |
| | | }, |
| | | // å
³éå¼¹æ¡ |
| | | handleClose() { |
| | | this.$emit("close"); |
| | | }, |
| | | // è·åæä»¶ç±»å |
| | | getFileType(fileName) { |
| | | const extension = fileName |
| | | .split(".") |
| | | .pop() |
| | | .toLowerCase(); |
| | | const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"]; |
| | | const pdfTypes = ["pdf"]; |
| | | |
| | | if (imageTypes.includes(extension)) return "image"; |
| | | if (pdfTypes.includes(extension)) return "pdf"; |
| | | 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; |
| | | } |
| | | }, |
| | | |
| | | // PDFé¢è§æ¹æ³ [1,2](@ref) |
| | | previewPdf(file) { |
| | | this.pdfPreviewVisible = true; |
| | | this.pdfLoading = true; |
| | | this.currentPage = 1; |
| | | this.scale = 100; |
| | | this.pageRotate = 0; |
| | | // 设置PDFæº [4](@ref) |
| | | this.pdfUrl = file.fileUrl; |
| | | }, |
| | | |
| | | // PDFå è½½å®æåè° [1,2](@ref) |
| | | loadPdfHandler() { |
| | | this.pdfLoading = false; |
| | | this.currentPage = 1; |
| | | }, |
| | | |
| | | // PDFå è½½é误å¤ç [4](@ref) |
| | | pdfErrorHandler(error) { |
| | | console.error("PDFå 载失败:", error); |
| | | this.pdfLoading = false; |
| | | this.$message.error("PDFæä»¶å 载失败ï¼è¯·å°è¯ä¸è½½åæ¥ç"); |
| | | this.pdfPreviewVisible = false; |
| | | }, |
| | | |
| | | // 翻页åè½ [2](@ref) |
| | | changePage(newPage) { |
| | | if (newPage < 1 || newPage > this.pageCount) return; |
| | | |
| | | this.currentPage = newPage; |
| | | }, |
| | | |
| | | // 缩æ¾åè½ [2,3](@ref) |
| | | zoomIn() { |
| | | if (this.scale >= 200) { |
| | | this.$message.info("å·²æ¾å¤§å°æå¤§æ¯ä¾"); |
| | | return; |
| | | } |
| | | this.scale += 10; |
| | | }, |
| | | |
| | | zoomOut() { |
| | | if (this.scale <= 50) { |
| | | this.$message.info("已缩å°å°æå°æ¯ä¾"); |
| | | return; |
| | | } |
| | | this.scale -= 10; |
| | | }, |
| | | |
| | | resetZoom() { |
| | | this.scale = 100; |
| | | }, |
| | | |
| | | // å¾çé¢è§æ¹æ³ |
| | | previewImage(file) { |
| | | this.imagePreviewVisible = true; |
| | | }, |
| | | |
| | | // 䏿¯æé¢è§çæä»¶ç±»å |
| | | previewUnsupported(file) { |
| | | this.unsupportedPreviewVisible = true; |
| | | }, |
| | | |
| | | // PDFå¯¹è¯æ¡å
³éå¤ç |
| | | 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("å¼å§ä¸è½½æä»¶"); |
| | | }, |
| | | |
| | | // ä¸ç¨PDFä¸è½½æ¹æ³ |
| | | downloadPdf(file) { |
| | | this.handleDownload(file); |
| | | }, |
| | | |
| | | // æä»¶å é¤ |
| | | handleDelete(file) { |
| | | this.$confirm("ç¡®å®è¦å é¤è¿ä¸ªéä»¶åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning" |
| | | }).then(() => { |
| | | this.attachmentList = this.attachmentList.filter( |
| | | item => item.id !== file.id |
| | | ); |
| | | this.$message.success("å 餿å"); |
| | | }); |
| | | }, |
| | | // ä¸ä¼ éä»¶ |
| | | handleUpload() { |
| | | this.$message.info("ä¸ä¼ åè½å¾
å®ç°"); |
| | | }, |
| | | |
| | | // åææ¹æ³ä¿æä¸å |
| | | getContactPhone(role) { |
| | | if (this.transportData.contacts) { |
| | | const contact = this.transportData.contacts.find( |
| | | item => item.role === role |
| | | ); |
| | | return contact ? contact.phone : "æªå¡«å"; |
| | | } |
| | | return "æªå¡«å"; |
| | | }, |
| | | |
| | | handleClose() { |
| | | this.$emit("close"); |
| | | }, |
| | | |
| | | 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]; |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .transport-detail { |
| | | padding: 0 20px; |
| | | } |
| | | |
| | | /* PDFé¢è§å¯¹è¯æ¡æ ·å¼ */ |
| | | .pdf-preview-dialog { |
| | | margin-top: 5vh !important; |
| | | } |
| | | |
| | | .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; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .zoom-controls { |
| | | margin: 0 15px; |
| | | } |
| | | |
| | | /* PDFè§å¾åºåæ ·å¼ */ |
| | | .pdf-viewport { |
| | | flex: 1; |
| | | overflow: auto; |
| | | padding: 20px; |
| | | background: #f8f9fa; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | /* å¾çé¢è§æ ·å¼ */ |
| | | .image-preview-container { |
| | | text-align: center; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | } |
| | | |
| | | .detail-footer { |
| | | text-align: center; |
| | | margin-top: 20px; |
| | | padding-top: 20px; |
| | | border-top: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .remarks-content { |
| | | padding: 15px; |
| | | line-height: 1.6; |
| | | color: #606266; |
| | | } |
| | | |
| | | ::v-deep .el-descriptions__label { |
| | | width: 120px; |
| | | background-color: #f5f7fa; |
| | | font-weight: bold; |
| | | } |
| | | </style> |