13404089107
2 天以前 993e5fd593398926af72af660cb5ed6aba8e4e2b
culture/src/views/strain-library/strain-library-manage/record.vue
@@ -1,417 +1,438 @@
<template>
    <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.strainNo }}</span>
                    </div>
                    <div class="info-item flex-column">
                        <span class="label">鉴定方法:</span>
                        <span class="value">{{ detail.method }}</span>
                    </div>
                    <div class="info-item flex-column">
                        <span class="label">保藏位置:</span>
                        <span class="value">{{ detail.amount }}</span>
                    </div>
                </div>
                <!-- 第二行 -->
                <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.certificate }}</span>
                    </div>
                </div>
                <!-- 第三行 -->
                <div class="info-row">
                    <div class="info-item left-column">
                        <span class="label">菌种来源:</span>
                        <span class="value">{{ detail.source }}</span>
                    </div>
                    <div class="info-item flex-column">
                        <span class="label">菌种保存方法:</span>
                        <span class="value">{{ detail.storage }}</span>
                    </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>
        <!-- 第二行 -->
        <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 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>
    <!-- 出入库记录表格 -->
    <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>
        </el-card>
            <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>
        <!-- 出入库记录表格 -->
        <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 @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-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 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>
      </template>
      <template #tableCustom v-if="currentType === 'timeline'">
        <record-timeline :list="timelineList" />
      </template>
    </TableCustom>
            <template #table v-if="currentType === 'table'">
                <el-table-column prop="type" label="出库/入库" />
                <el-table-column prop="operateTime" label="操作时间" />
                <el-table-column prop="operator" label="操作人签字" />
                <el-table-column prop="reviewer" label="菌种保藏人签字" />
                    <el-table-column prop="status" label="状态">
                        <template #default="{ row }">
                            <el-tag :type="row.status === '已确认' ? 'success' : 'warning'">
                                {{ row.status }}
                            </el-tag>
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" width="180">
                        <template #default="{ row }">
                            <el-button type="text" class="operation-btn" @click="handleView(row)">详情</el-button>
                            <el-button type="text" class="operation-btn" @click="handleEdit(row)">编辑</el-button>
                            <el-button type="text" @click="handleDelete(row)">删除</el-button>
                    </template>
                </el-table-column>
            </template>
            <template #tableCustom v-if="currentType === 'timeline'">
                <record-timeline :list="timelineList" />
            </template>
        </TableCustom>
        <!-- 详情弹窗 -->
        <record-detail-dialog
            :visible.sync="dialogVisible"
            :record-data="currentRecord"
            @close="handleDialogClose"
            @confirm="handleOutbound"
        />
        <add-record-dialog
            :visible.sync="addDialogVisible"
            @confirm="handleAddRecordConfirm"
        />
    </div>
    <!-- 详情弹窗 -->
    <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 './components/RecordDetailDialog.vue'
import AddRecordDialog from './components/AddRecordDialog.vue'
import RecordTimeline from './components/RecordTimeline.vue'
import RecordDetailDialog from "./components/RecordDetailDialog.vue";
import AddRecordDialog from "./components/AddRecordDialog.vue";
import RecordTimeline from "./components/RecordTimeline.vue";
import {
  timeList,
  getDetail,
  addWarehousing,
  getDetailById,
  confirmWarehousing,
} from "./service";
export default {
    name: 'StrainRecord',
    components: {
        RecordDetailDialog,
        AddRecordDialog,
        RecordTimeline
    },
    data() {
        return {
            currentType: 'table',
            detail: {},
            currentPage: 1,
            pageSize: 10,
            total: 0,
            queryForm: {
                pageSize: 10,
                pageNum: 1
            },
            recordList: [
                {
                    type: '入库',
                    operateTime: '2025-1-21 15:46:50',
                    operator: '张三',
                    reviewer: '李四',
                    status: '已确认'
                },
                {
                    type: '出库',
                    operateTime: '2025-1-21 15:46:50',
                    operator: '张三',
                    reviewer: '李四',
                    status: '已确认'
                },
                {
                    type: '入库',
                    operateTime: '2025-1-21 15:46:50',
                    operator: '张三',
                    reviewer: '李四',
                    status: '已确认'
                },
                {
                    type: '出库',
                    operateTime: '2025-1-21 15:46:50',
                    operator: '张三',
                    reviewer: '李四',
                    status: '已确认'
                },
                {
                    type: '入库',
                    operateTime: '2025-1-21 15:46:50',
                    operator: '李四',
                    reviewer: '李四',
                    status: '已确认'
                }
            ],
            dialogVisible: false,
            currentRecord: {},
            addDialogVisible: false
        }
    },
    computed: {
        timelineList() {
            // 可根据需要处理数据格式,这里直接用 recordList
            return this.recordList.map(item => ({
                ...item,
                confirmTime: item.confirmTime || item.operateTime // 若无确认时间则用操作时间
            }))
        }
    },
    created() {
        // 获取路由参数中的菌种信息
        const strainId = this.$route.query.id
        if (strainId) {
            this.getStrainDetail(strainId)
            this.getRecordList()
        }
    },
    methods: {
        getStrainDetail(id) {
            // 这里应该调用接口获取菌种详情
            // 暂时使用模拟数据
            this.detail = {
                strainNo: '3418732431',
                strainName: '名称名称名称',
                source: '来源11111111111',
                method: '1231231',
                certificate: '特性描述',
                storage: '方法方法',
                amount: '位置位置位置位置位置位置位置位置',
                operator: '入库'
            }
        },
        getRecordList() {
            // 这里应该调用接口获取出入库记录
            // 暂时使用已有模拟数据
            this.total = this.recordList.length
        },
        handleView(row) {
            this.currentRecord = {
                ...row,
                operatorSignature: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', // 模拟操作人签字图片
                reviewerSignature: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', // 模拟保藏人签字图片
                operateTime: '2025-1-22 13:49:51',
                confirmTime: '2025-1-22 14:30:00'
            }
            this.dialogVisible = true
        },
        handlePageChange(page) {
            this.queryForm.pageNum = page
            // 这里应该调用接口获取对应页码的数据
        },
        handleTypeChange(type) {
            this.currentType = type
        },
        handleAddRecord() {
            this.addDialogVisible = true
        },
        handleEdit(row) {
            console.log('编辑记录:', row)
            // 实现编辑记录逻辑
        },
        handleDelete(row) {
            this.$confirm('确认删除该记录吗?', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                console.log('删除记录:', row)
                // 实际项目中这里应该调用删除API
                this.$message({
                    type: 'success',
                    message: '删除成功!'
                })
            }).catch(() => {
                this.$message({
                    type: 'info',
                    message: '已取消删除'
                })
            })
        },
        handleDialogClose() {
            this.currentRecord = {}
            this.dialogVisible = false
        },
        handleOutbound(data) {
            // 这里调用出库API
            console.log('出库操作:', data)
            this.$message.success('出库成功')
            this.dialogVisible = false
            // 刷新列表
            this.getRecordList()
        },
        handleAddRecordConfirm(record) {
            // 这里可以将新记录添加到 recordList 或调用后端API
            this.$message.success('新增出入库记录成功')
            // 例如:this.recordList.push(record)
            this.getRecordList() // 或刷新列表
        },
        goBack() {
            this.$router.go(-1)
        }
  name: "StrainRecord",
  components: {
    RecordDetailDialog,
    AddRecordDialog,
    RecordTimeline,
  },
  data() {
    return {
      currentType: "table",
      detail: {},
      currentPage: 1,
      pageSize: 10,
      total: 0,
      queryForm: {
        pageSize: 10,
        pageNum: 1,
      },
      recordList: [],
      timelineList: [],
      dialogVisible: false,
      currentRecord: {},
      addDialogVisible: false,
      dialogType: "detail",
      roleType: "",
    };
  },
  created() {
    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: {
    handleDelete(row) {
      this.$confirm("确定删除该数据吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
        deleteWarehousing({ id: row.id }).then((res) => {
          this.$message.success("删除成功");
          this.getRecordList();
        });
      });
    },
    getStrainDetail(id) {
      // 这里应该调用接口获取菌种详情
      getDetail({ id }).then((res) => {
        this.detail = res;
      });
    },
    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;
      });
    },
    handleView(row) {
      this.dialogType = "detail";
      this.currentRecord = row;
      this.dialogVisible = true;
    },
    handleConfirm(row) {
      this.dialogType = "confirm";
      this.currentRecord = row;
      this.dialogVisible = true;
    },
    handlePageChange(page) {
      this.queryForm.pageNum = page;
      // 这里应该调用接口获取对应页码的数据
    },
    handleTypeChange(type) {
      this.currentType = type;
    },
    handleAddRecord() {
      this.addDialogVisible = true;
    },
    handleDialogClose() {
      this.currentRecord = {};
      this.dialogVisible = false;
    },
    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);
        }
      });
    },
    handleAddRecordConfirm(record) {
      addWarehousing({ ...record, trainLibraryId: this.$route.query.id }).then(
        (res) => {
          this.$message.success("操作成功");
          this.getRecordList();
        }
      );
    },
    goBack() {
      this.$router.go(-1);
    },
  },
};
</script>
<style lang="less" scoped>
.record-page {
    min-height: 100vh;
  min-height: 100vh;
    .header-box {
        margin-bottom: 20px;
        border-radius: 16px;
        background: rgba(255, 255, 255, 0.8);
        height: 130px;
        overflow: hidden;
  .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;
    .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;
                }
                .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;
                    }
                }
            }
        }
    }
    .flex {
      .info-row {
        display: flex;
        align-items: center;
    }
        flex-wrap: wrap;
        margin-bottom: 8px;
    .tableTitle {
        display: flex;
        padding-bottom: 20px;
        justify-content: space-between;
        align-items: center;
        &:last-child {
          margin-bottom: 0;
        }
        .title {
            background: #fafafc;
            border-radius: 8px 8px 0px 0px;
            border: 1px solid #dcdfe6;
            font-weight: bold;
            font-size: 18px;
        .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;
            cursor: pointer;
            height: 50px;
            line-height: 50px;
            width: 280px;
            text-align: center;
        }
            margin-right: 8px;
            white-space: nowrap;
          }
        .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;
          .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;
          }
        }
      }
    }
  }
        .active {
            color: #049c9a;
            background: #ffffff;
            border-radius: 8px 8px 0px 0px;
            border: 1px solid #049c9a;
        }
  .flex {
    display: flex;
    align-items: center;
  }
  .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;
    }
    .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;
            }
        }
    .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;
    }
    .operation-btn {
        margin-right: 12px;
    .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>
</style>