From 06b2be3bbb48e0275fbd25624c1cce54a7cac2b1 Mon Sep 17 00:00:00 2001 From: 董国庆 <364620639@qq.com> Date: 星期二, 20 五月 2025 16:44:34 +0800 Subject: [PATCH] Merge branch 'main' of http://120.76.84.145:10101/gitblit/r/H5/leshan-laboratory --- culture/src/views/strain-library/production-cell-library/record.vue | 864 +++++++++++++++++++++++++-------------------------------- 1 files changed, 382 insertions(+), 482 deletions(-) diff --git a/culture/src/views/strain-library/production-cell-library/record.vue b/culture/src/views/strain-library/production-cell-library/record.vue index b23bd7a..aef2643 100644 --- a/culture/src/views/strain-library/production-cell-library/record.vue +++ b/culture/src/views/strain-library/production-cell-library/record.vue @@ -1,538 +1,438 @@ <template> - <div class="production-cell-record"> - <!-- 页面头部 --> - <div class="page-header"> - <div class="header-left"> - <el-button icon="el-icon-arrow-left" @click="$router.go(-1)">返回</el-button> - <h2>生产细胞库菌种详情</h2> - </div> - <div class="header-actions"> - <el-button type="primary" plain icon="el-icon-edit" @click="handleEdit">编辑</el-button> - <el-button type="primary" plain icon="el-icon-printer" @click="handlePrint">打印</el-button> - </div> - </div> + <div class="record-page"> + <!-- 基本信息展示区域 --> + <el-card class="header-box"> + <div class="header-content"> + <!-- 第一行 --> + <div class="info-row"> + <div class="info-item left-column"> + <span class="label">菌种编号:</span> + <span class="value">{{ detail.strainCode }}</span> + </div> + <div class="info-item flex-column"> + <span class="label">鉴定方法:</span> + <span class="value">{{ detail.appraisalMethod }}</span> + </div> + <div class="info-item flex-column"> + <span class="label">保藏位置:</span> + <span class="value">{{ detail.saveLocation }}</span> + </div> + </div> - <!-- 菌种信息卡片 --> - <el-card class="strain-card" v-loading="loading"> - <div slot="header" class="card-header"> - <span class="card-title">菌种信息</span> - <div class="status-tag"> - <el-tag :type="getStatusType(strainData.status)">{{ strainData.status }}</el-tag> + <!-- 第二行 --> + <div class="info-row"> + <div class="info-item left-column"> + <span class="label">菌种名称:</span> + <span class="value">{{ detail.strainName }}</span> + </div> + <div class="info-item flex-column full-width"> + <span class="label">特性描述:</span> + <span class="value">{{ detail.features }}</span> + </div> </div> - </div> - - <div class="strain-info"> - <div class="info-item"> - <span class="label">菌种编号:</span> - <span class="value">{{ strainData.strainNo }}</span> - </div> - <div class="info-item"> - <span class="label">菌种名称:</span> - <span class="value">{{ strainData.strainName }}</span> - </div> - <div class="info-item"> - <span class="label">菌种来源:</span> - <span class="value">{{ strainData.source }}</span> - </div> - <div class="info-item"> - <span class="label">生产批次:</span> - <span class="value">{{ strainData.batchNo }}</span> - </div> - <div class="info-item"> - <span class="label">保存方法:</span> - <span class="value">{{ strainData.preservationMethod }}</span> - </div> - <div class="info-item"> - <span class="label">保存位置:</span> - <span class="value">{{ strainData.storageLocation }}</span> - </div> - <div class="info-item"> - <span class="label">当前库存:</span> - <span class="value">{{ strainData.inventory }} 份</span> - </div> - <div class="info-item"> - <span class="label">状态:</span> - <span class="value">{{ strainData.status }}</span> - </div> - <div class="info-item"> - <span class="label">更新时间:</span> - <span class="value">{{ strainData.updateTime }}</span> - </div> - <div class="info-item"> - <span class="label">制备日期:</span> - <span class="value">{{ strainData.preparationDate }}</span> - </div> - <div class="info-item"> - <span class="label">有效期至:</span> - <span class="value">{{ strainData.expiryDate }}</span> - </div> - <div class="info-item full-width"> - <span class="label">特征描述:</span> - <div class="value description">{{ strainData.description }}</div> - </div> - - <div class="info-item full-width" v-if="strainData.certificateUrl"> - <span class="label">质量证书:</span> - <div class="value"> - <el-button type="text" @click="handleViewCertificate">查看证书</el-button> - <el-button type="text" @click="handleDownloadCertificate">下载证书</el-button> + + <!-- 第三行 --> + <div class="info-row"> + <div class="info-item left-column"> + <span class="label">菌种来源:</span> + <span class="value">{{ detail.strainSource }}</span> + </div> + <div class="info-item flex-column"> + <span class="label">菌种保存方法:</span> + <span class="value">{{ detail.saveMethod }}</span> </div> </div> </div> </el-card> - <!-- 使用记录 --> - <el-card class="record-card" v-loading="loadingRecords"> - <div slot="header" class="card-header"> - <span class="card-title">使用记录</span> - <el-button type="text" @click="handleAddUsage">新增使用记录</el-button> - </div> - - <el-table - :data="usageRecords" - style="width: 100%" - :empty-text="usageRecords.length ? '' : '暂无使用记录'" - > - <el-table-column prop="date" label="使用日期" width="120"></el-table-column> - <el-table-column prop="amount" label="使用数量" width="100"></el-table-column> - <el-table-column prop="operator" label="操作人" width="120"></el-table-column> - <el-table-column prop="project" label="项目名称"></el-table-column> - <el-table-column prop="batchNo" label="生产批次" width="150"></el-table-column> - <el-table-column prop="purpose" label="使用目的"></el-table-column> - <el-table-column label="操作" width="120"> - <template slot-scope="scope"> - <el-button type="text" size="small" @click="handleViewUsageDetail(scope.row)">查看</el-button> - <el-button type="text" size="small" @click="handleEditUsage(scope.row)">编辑</el-button> + <!-- 出入库记录表格 --> + <TableCustom + :queryForm="queryForm" + :tableData="recordList" + :total="total" + @currentChange="handlePageChange" + > + <template #setting> + <div class="tableTitle"> + <div class="flex a-center"> + <div + class="title" + :class="{ active: currentType === 'table' }" + @click="handleTypeChange('table')" + > + 原始细胞保藏出/入库登记表 + </div> + <div + class="drafts" + :class="{ active: currentType === 'timeline' }" + @click="handleTypeChange('timeline')" + > + 原始细胞保藏出/入库时间轴 + </div> + </div> + <div class="flex a-center"> + <el-button + v-if="roleType == 4" + @click="handleAddRecord" + class="el-icon-plus" + type="primary" + >新增出入库记录</el-button + > + </div> + </div> + </template> + + <template #table v-if="currentType === 'table'"> + <el-table-column prop="type" label="出库/入库"> + <template #default="{ row }"> + <span> + {{ row.type === 1 ? "出库" : "入库" }} + </span> </template> </el-table-column> - </el-table> - - <div class="pagination-container" v-if="usageRecords.length"> - <el-pagination - background - layout="prev, pager, next" - :total="usageTotalCount" - :current-page.sync="usageCurrentPage" - :page-size="usagePageSize" - @current-change="handleUsagePageChange"> - </el-pagination> - </div> - </el-card> - - <!-- 测试记录 --> - <el-card class="record-card" v-loading="loadingTests"> - <div slot="header" class="card-header"> - <span class="card-title">测试记录</span> - <el-button type="text" @click="handleAddTest">新增测试记录</el-button> - </div> - - <el-table - :data="testRecords" - style="width: 100%" - :empty-text="testRecords.length ? '' : '暂无测试记录'" - > - <el-table-column prop="date" label="测试日期" width="120"></el-table-column> - <el-table-column prop="type" label="测试类型" width="150"></el-table-column> - <el-table-column prop="operator" label="操作人" width="120"></el-table-column> - <el-table-column prop="result" label="测试结果"> - <template slot-scope="scope"> - <el-tag :type="scope.row.result === '合格' ? 'success' : scope.row.result === '不合格' ? 'danger' : 'info'"> - {{ scope.row.result }} + <el-table-column prop="boundTime" label="操作时间" /> + <el-table-column prop="handleSignature" label="操作人签字"> + <template #default="{ row }"> + <el-image + v-if="row.handleSignature" + style="width: 100px; height: 100px" + :src="row.handleSignature" + :preview-src-list="[row.handleSignature]" + > + </el-image> + </template> + </el-table-column> + <el-table-column prop="preserveSignature" label="菌种保藏人签字"> + <template #default="{ row }"> + <el-image + v-if="row.preserveSignature" + style="width: 100px; height: 100px" + :src="row.preserveSignature" + :preview-src-list="[row.preserveSignature]" + > + </el-image> + </template> + </el-table-column> + <el-table-column prop="status" label="状态"> + <template #default="{ row }"> + <el-tag :type="row.preserveSignature ? 'success' : 'warning'"> + {{ row.preserveSignature ? "已确认" : "待确认" }} </el-tag> </template> </el-table-column> - <el-table-column prop="remark" label="备注"></el-table-column> - <el-table-column label="操作" width="120"> - <template slot-scope="scope"> - <el-button type="text" size="small" @click="handleViewTestDetail(scope.row)">查看</el-button> - <el-button type="text" size="small" @click="handleEditTest(scope.row)">编辑</el-button> + <el-table-column label="操作" width="180"> + <template #default="{ row }"> + <el-button + v-if="!row.preserveSignature && roleType == 3" + type="text" + class="operation-btn" + @click="handleConfirm(row)" + >确认</el-button + > + <el-button + type="text" + class="operation-btn" + @click="handleView(row)" + >详情</el-button + > + <el-button v-if="roleType == 1" type="text" @click="handleDelete(row)">删除</el-button> </template> </el-table-column> - </el-table> - - <div class="pagination-container" v-if="testRecords.length"> - <el-pagination - background - layout="prev, pager, next" - :total="testTotalCount" - :current-page.sync="testCurrentPage" - :page-size="testPageSize" - @current-change="handleTestPageChange"> - </el-pagination> - </div> - </el-card> + </template> + <template #tableCustom v-if="currentType === 'timeline'"> + <record-timeline :list="timelineList" /> + </template> + </TableCustom> - <!-- 证书预览对话框 --> - <el-dialog - title="证书预览" - :visible.sync="certificateDialogVisible" - width="70%"> - <div v-if="strainData.certificateUrl" class="certificate-preview"> - <iframe v-if="strainData.certificateUrl.endsWith('.pdf')" :src="strainData.certificateUrl" width="100%" height="500"></iframe> - <img v-else :src="strainData.certificateUrl" style="max-width: 100%; max-height: 500px;" /> - </div> - </el-dialog> + <!-- 详情弹窗 --> + <record-detail-dialog + :visible.sync="dialogVisible" + :record-data="currentRecord" + @close="handleDialogClose" + @confirm="handleOutbound" + :type="dialogType" + /> + <!-- 新增出入库记录弹窗 --> + <add-record-dialog + :visible.sync="addDialogVisible" + @confirm="handleAddRecordConfirm" + /> </div> </template> <script> +import RecordDetailDialog from "../strain-library-manage/components/RecordDetailDialog.vue"; +import AddRecordDialog from "../strain-library-manage/components/AddRecordDialog.vue"; +import RecordTimeline from "../strain-library-manage/components/RecordTimeline.vue"; +import { + timeList, + getDetail, + addWarehousing, + getDetailById, + confirmWarehousing, +} from "./service"; + export default { - name: 'ProductionCellLibraryRecord', + name: "StrainRecord", + components: { + RecordDetailDialog, + AddRecordDialog, + RecordTimeline, + }, data() { return { - loading: false, - loadingRecords: false, - loadingTests: false, - strainId: '', - strainData: { - id: '', - strainNo: '', - strainName: '', - source: '', - batchNo: '', - preservationMethod: '', - storageLocation: '', - inventory: 0, - status: '', - description: '', - preparationDate: '', - expiryDate: '', - updateTime: '', - certificateUrl: '' + currentType: "table", + detail: {}, + currentPage: 1, + pageSize: 10, + total: 0, + queryForm: { + pageSize: 10, + pageNum: 1, }, - certificateDialogVisible: false, - - // 使用记录分页数据 - usageRecords: [], - usageCurrentPage: 1, - usagePageSize: 10, - usageTotalCount: 0, - - // 测试记录分页数据 - testRecords: [], - testCurrentPage: 1, - testPageSize: 10, - testTotalCount: 0 - } + recordList: [], + timelineList: [], + dialogVisible: false, + currentRecord: {}, + addDialogVisible: false, + dialogType: "detail", + roleType: "", + }; }, - created() { - this.strainId = this.$route.params.id; - this.fetchStrainData(); - this.fetchUsageRecords(); - this.fetchTestRecords(); + activated() { + this.roleType = JSON.parse(sessionStorage.getItem("userInfo")).roleType; + + // 获取路由参数中的菌种信息 + const strainId = this.$route.query.id; + this.queryForm.id = strainId; + if (strainId) { + this.getStrainDetail(strainId); + this.getRecordList(); + } }, methods: { - // 获取菌种详情 - fetchStrainData() { - this.loading = true; - - // 模拟API请求 - setTimeout(() => { - // 模拟数据,实际项目中应替换为真实API调用 - this.strainData = { - id: this.strainId, - strainNo: 'PCLS-2023-001', - strainName: '枯草芽孢杆菌生产株', - source: '主细胞库', - batchNo: 'P20230515-001', - preservationMethod: '冻干保存', - storageLocation: 'A区-A-102-冷藏柜', - inventory: 12, - status: '正常', - description: '本菌种为工业生产级别枯草芽孢杆菌生产株,由主细胞库转入,经过严格筛选和稳定性测试。该菌株具有高产蛋白酶能力,发酵条件适应性强,适合大规模工业化生产。产品稳定性好,批次间差异小,可用于洗涤用酶制剂、食品加工酶制剂等多种产品的生产。', - preparationDate: '2023-05-10', - expiryDate: '2024-05-10', - updateTime: '2023-05-15 14:30:22', - certificateUrl: '/api/strain-library/certificates/sample.pdf' - }; - - this.loading = false; - }, 500); + handleDelete(row) { + this.$confirm("确定删除该数据吗?", "提示", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning", + }).then(() => { + deleteWarehousing({ id: row.id }).then((res) => { + this.$message.success("删除成功"); + this.getRecordList(); + }); + }); }, - - // 获取使用记录 - fetchUsageRecords() { - this.loadingRecords = true; - - // 模拟API请求 - setTimeout(() => { - // 模拟数据,实际项目中应替换为真实API调用 - this.usageRecords = [ - { - id: '1', - date: '2023-06-05', - amount: 2, - operator: '张工', - project: '酶制剂研发项目', - batchNo: 'E20230605-001', - purpose: '小试生产' - }, - { - id: '2', - date: '2023-07-12', - amount: 3, - operator: '李工', - project: '蛋白酶产品项目', - batchNo: 'E20230712-002', - purpose: '中试生产' - }, - { - id: '3', - date: '2023-08-18', - amount: 5, - operator: '王工', - project: '工业酶制剂生产', - batchNo: 'E20230818-003', - purpose: '规模化生产' - } - ]; - - this.usageTotalCount = 3; - this.loadingRecords = false; - }, 600); + getStrainDetail(id) { + // 这里应该调用接口获取菌种详情 + getDetail({ id }).then((res) => { + this.detail = res; + }); }, - - // 获取测试记录 - fetchTestRecords() { - this.loadingTests = true; - - // 模拟API请求 - setTimeout(() => { - // 模拟数据,实际项目中应替换为真实API调用 - this.testRecords = [ - { - id: '1', - date: '2023-05-15', - type: '活力测定', - operator: '刘工', - result: '合格', - remark: '酶活性达标,符合生产要求' - }, - { - id: '2', - date: '2023-05-15', - type: '纯度检测', - operator: '张工', - result: '合格', - remark: '纯度>98%,无杂菌污染' - }, - { - id: '3', - date: '2023-05-16', - type: '稳定性测试', - operator: '李工', - result: '合格', - remark: '常温保存7天活力下降小于5%' - } - ]; - - this.testTotalCount = 3; - this.loadingTests = false; - }, 700); + getRecordList() { + // 这里应该调用接口获取出入库记录 + timeList(this.queryForm).then((res) => { + this.timelineList = res.data; + }); + getDetailById({ id: this.$route.query.id }).then((res) => { + this.recordList = res.warehousingList.records; + this.total = res.warehousingList.total; + }); }, - - // 状态标签类型 - getStatusType(status) { - switch(status) { - case '正常': - return 'success'; - case '缺货': - return 'warning'; - case '异常': - return 'danger'; - case '已停用': - return 'info'; - default: - return 'info'; - } + handleView(row) { + this.dialogType = "detail"; + this.currentRecord = row; + this.dialogVisible = true; }, - - // 编辑菌种 - handleEdit() { - this.$router.push(`/strain-library/production-cell-library/edit/${this.strainId}`); + handleConfirm(row) { + this.dialogType = "confirm"; + this.currentRecord = row; + this.dialogVisible = true; }, - - // 打印菌种信息 - handlePrint() { - window.print(); + handlePageChange(page) { + this.queryForm.pageNum = page; + // 这里应该调用接口获取对应页码的数据 }, - - // 查看证书 - handleViewCertificate() { - this.certificateDialogVisible = true; + handleTypeChange(type) { + this.currentType = type; }, - - // 下载证书 - handleDownloadCertificate() { - // 实际项目中应处理文件下载逻辑 - window.open(this.strainData.certificateUrl, '_blank'); + handleAddRecord() { + this.addDialogVisible = true; }, - - // 新增使用记录 - handleAddUsage() { - this.$message.info('功能开发中:新增使用记录'); - // 实际项目中应跳转到新增使用记录页面或打开对话框 + handleDialogClose() { + this.currentRecord = {}; + this.dialogVisible = false; }, - - // 查看使用记录详情 - handleViewUsageDetail(row) { - this.$message.info(`查看使用记录: ${row.id}`); - // 实际项目中应跳转到使用记录详情页面或打开对话框 + handleOutbound(data) { + // 这里调用出库API + confirmWarehousing({ + id: this.currentRecord.id, + preserveSignature: data.preserveSignature, + }).then((res) => { + console.log(res); + if (res.code == 200) { + this.$message.success("操作成功"); + this.dialogVisible = false; + // 刷新列表 + this.getRecordList(); + } else { + this.$message.error(res.msg); + } + }); }, - - // 编辑使用记录 - handleEditUsage(row) { - this.$message.info(`编辑使用记录: ${row.id}`); - // 实际项目中应跳转到编辑使用记录页面或打开对话框 + handleAddRecordConfirm(record) { + addWarehousing({ ...record, trainLibraryId: this.$route.query.id }).then( + (res) => { + this.$message.success("操作成功"); + this.getRecordList(); + } + ); }, - - // 使用记录分页切换 - handleUsagePageChange(page) { - this.usageCurrentPage = page; - this.fetchUsageRecords(); + goBack() { + this.$router.go(-1); }, - - // 新增测试记录 - handleAddTest() { - this.$message.info('功能开发中:新增测试记录'); - // 实际项目中应跳转到新增测试记录页面或打开对话框 - }, - - // 查看测试记录详情 - handleViewTestDetail(row) { - this.$message.info(`查看测试记录: ${row.id}`); - // 实际项目中应跳转到测试记录详情页面或打开对话框 - }, - - // 编辑测试记录 - handleEditTest(row) { - this.$message.info(`编辑测试记录: ${row.id}`); - // 实际项目中应跳转到编辑测试记录页面或打开对话框 - }, - - // 测试记录分页切换 - handleTestPageChange(page) { - this.testCurrentPage = page; - this.fetchTestRecords(); - } - } -} + }, +}; </script> -<style scoped lang="less"> -.production-cell-record { - padding: 20px; +<style lang="less" scoped> +.record-page { + min-height: 100vh; - .page-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 24px; - - .header-left { - display: flex; - align-items: center; - - h2 { - margin: 0 0 0 12px; - font-size: 22px; - font-weight: 500; - } - } - - .header-actions { - display: flex; - gap: 12px; - } - } - - .strain-card { - margin-bottom: 24px; - - .card-header { - display: flex; - justify-content: space-between; - align-items: center; - - .card-title { - font-size: 16px; - font-weight: 500; - } - } - - .strain-info { - display: flex; - flex-wrap: wrap; - - .info-item { - width: 33.33%; - margin-bottom: 16px; - - &.full-width { - width: 100%; + .header-box { + margin-bottom: 20px; + border-radius: 16px; + background: rgba(255, 255, 255, 0.8); + height: 130px; + overflow: hidden; + + .header-content { + color: rgba(0, 0, 0, 0.88); + font-size: 14px; + line-height: 1.5; + + .info-row { + display: flex; + flex-wrap: wrap; + margin-bottom: 8px; + + &:last-child { + margin-bottom: 0; } - - .label { - font-weight: 500; - color: #606266; - } - - .value { - color: #303133; - - &.description { - white-space: pre-line; - line-height: 1.6; - padding: 8px 0; + + .info-item { + display: flex; + align-items: flex-start; + margin-right: 24px; + margin-bottom: 6px; + + &.left-column { + width: 33%; + min-width: 200px; + } + + &.flex-column { + flex: 1; + min-width: 150px; + } + + &.full-width { + flex: 1; + min-width: 300px; + } + + .label { + color: #606266; + margin-right: 8px; + white-space: nowrap; + } + + .value { + flex: 1; + color: #303133; + word-break: break-all; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; } } } } } - - .record-card { - margin-bottom: 24px; - - .card-header { - display: flex; - justify-content: space-between; - align-items: center; - - .card-title { - font-size: 16px; - font-weight: 500; - } - } - - .pagination-container { - display: flex; - justify-content: flex-end; - margin-top: 20px; - } - } - - .certificate-preview { + + .flex { display: flex; - justify-content: center; align-items: center; - min-height: 500px; - background-color: #f5f7fa; } - - @media print { - .page-header, .header-actions, .record-card { - display: none; + + .tableTitle { + display: flex; + padding-bottom: 20px; + justify-content: space-between; + align-items: center; + + .title { + background: #fafafc; + border-radius: 8px 8px 0px 0px; + border: 1px solid #dcdfe6; + font-weight: bold; + font-size: 18px; + color: #606266; + cursor: pointer; + height: 50px; + line-height: 50px; + width: 280px; + text-align: center; } - - .strain-card { - border: none; - box-shadow: none; - - .card-header { - background-color: #fff !important; - border-bottom: 1px solid #ddd; + + .drafts { + background: #fafafc; + border-radius: 8px 8px 0px 0px; + border: 1px solid #dcdfe6; + font-weight: 400; + font-size: 18px; + color: #606266; + margin-left: 16px; + cursor: pointer; + height: 50px; + line-height: 50px; + width: 280px; + text-align: center; + } + + .active { + color: #049c9a; + background: #ffffff; + border-radius: 8px 8px 0px 0px; + border: 1px solid #049c9a; + } + } + + .timeline-container { + padding: 20px; + background: rgba(255, 255, 255, 0.8); + + .timeline-card { + margin-bottom: 10px; + background: rgba(255, 255, 255, 0.8); + + h4 { + margin: 0 0 10px; + font-size: 16px; + font-weight: bold; + } + + p { + margin: 5px 0; + font-size: 14px; } } + } + + .operation-btn { + margin-right: 12px; } } -</style> \ No newline at end of file +</style> -- Gitblit v1.7.1