From 68473cefa689a71b22ee4b13e7a2298f2826d46d Mon Sep 17 00:00:00 2001
From: 董国庆 <364620639@qq.com>
Date: 星期四, 11 十二月 2025 10:11:25 +0800
Subject: [PATCH] 12.07 菌种库 除了谱系图内容

---
 culture/src/views/strain-library/production-cell-library/index.vue                          |    4 
 culture/vue.config.js                                                                       |   98 +-
 culture/src/router/index.js                                                                 |   52 +
 culture/src/views/strain-library/production-cell-grandson/index.vue                         |  521 ++++++++++++++++++
 culture/src/views/strain-library/production-cell-grandson/service.js                        |   56 ++
 culture/src/views/strain-library/validation/primitive-cell/primitive-cell-detail-dialog.vue |    2 
 culture/src/views/strain-library/validation/chief-cell/primitive-cell-detail-dialog.vue     |    2 
 culture/src/utils/baseurl.js                                                                |    9 
 culture/src/views/strain-library/validation/primitive-cell/index.vue                        |    4 
 culture/src/views/pedigree-chart/index.vue                                                  |    2 
 culture/src/views/strain-library/production-cell-grandson/add.vue                           |  439 +++++++++++++++
 culture/src/views/strain-library/production-cell-grandson/record.vue                        |  437 +++++++++++++++
 culture/src/views/strain-library/validation/chief-cell/index.vue                            |    6 
 culture/src/views/strain-library/breeding-record/index.vue                                  |    9 
 culture/src/views/strain-library/validation/chief-cell/add.vue                              |    2 
 15 files changed, 1,567 insertions(+), 76 deletions(-)

diff --git a/culture/src/router/index.js b/culture/src/router/index.js
index 4e858a4..2114ac0 100644
--- a/culture/src/router/index.js
+++ b/culture/src/router/index.js
@@ -177,7 +177,7 @@
             path: "strain-library-manage",
             name: "StrainLibraryManage",
             meta: {
-              title: "原始细胞库",
+              title: "原始细胞库(原种登记)",
               privilege:'strain_library_strain_library_manage'
             },
             component: () =>
@@ -219,7 +219,7 @@
             path: "main-cell-library",
             name: "MainCellLibrary",
             meta: {
-              title: "主细胞库",
+              title: "主细胞库(祖传母)",
               privilege:'strain_library_main_cell_library'
             },
             component: () =>
@@ -249,7 +249,7 @@
             path: "production-cell-library",
             name: "ProductionCellLibrary",
             meta: {
-              title: "生产细胞库",
+              title: "子细胞库(母传子)",
               privilege:'strain_library_production_cell_library'
             },
             component: () =>
@@ -264,6 +264,36 @@
             },
             component: () =>
               import("../views/strain-library/production-cell-library/add.vue"),
+          },
+          {
+            path: "production-cell-grandson",
+            name: "ProductionCellGrandson",
+            meta: {
+              title: "孙细胞库(子传孙)",
+              privilege:'strain_library_production_cell_library'
+            },
+            component: () =>
+              import("../views/strain-library/production-cell-grandson"),
+          },
+          {
+            path: "production-cell-grandson/add",
+            name: "ProductionCellGrandsonAdd",
+            meta: {
+              title: "新增生产细胞",
+              hide: true,
+            },
+            component: () =>
+              import("../views/strain-library/production-cell-grandson/add.vue"),
+          },
+          {
+            path: "production-cell-grandson/edit",
+            name: "ProductionCellGrandsonAdd",
+            meta: {
+              title: "编辑生产细胞",
+              hide: true,
+            },
+            component: () =>
+              import("../views/strain-library/production-cell-grandson/add.vue"),
           },
           {
             path: "production-cell-library/edit",
@@ -379,7 +409,7 @@
             path: "primitive-cell",
             name: "PrimitiveCell",
             meta: {
-              title: "原始细胞库资料",
+              title: "祖代菌种库验证资料",
               privilege:'strain_validation_primitive_cell'
             },
             component: () =>
@@ -391,7 +421,7 @@
             path: "add-primitive-cell",
             name: "AddPrimitiveCell",
             meta: {
-              title: "新增原始细胞库资料",
+              title: "新增祖代菌种库验证资料",
               hide: true,
             },
             component: () =>
@@ -403,7 +433,7 @@
             path: "edit-primitive-cell",
             name: "AddPrimitiveCell",
             meta: {
-              title: "编辑原始细胞库资料",
+              title: "编辑祖代菌种库验证资料",
               hide: true,
             },
             component: () =>
@@ -415,7 +445,7 @@
             path: "confirm-detail",
             name: "ConfirmDetail",
             meta: {
-              title: "确认原始细胞库资料",
+              title: "确认祖代菌种库验证资料",
               hide: true,
             },
             component: () =>
@@ -427,7 +457,7 @@
             path: "chief-cell",
             name: "ChiefCell",
             meta: {
-              title: "主细胞库资料",
+              title: "母代菌种库验证资料",
               privilege:'strain_validation_chief_cell'
             },
             component: () =>
@@ -437,7 +467,7 @@
             path: "add-chief-cell",
             name: "AddChiefCell",
             meta: {
-              title: "新增主细胞库资料",
+              title: "新增母代菌种库验证资料",
               hide: true,
             },
             component: () =>
@@ -447,7 +477,7 @@
             path: "edit-chief-cell",
             name: "EditChiefCell",
             meta: {
-              title: "编辑主细胞库资料",
+              title: "编辑母代菌种库验证资料",
               hide: true,
             },
             component: () =>
@@ -457,7 +487,7 @@
             path: "confirm-detail-chief",
             name: "ConfirmDetailChief",
             meta: {
-              title: "确认主细胞库资料",
+              title: "确认母代菌种库验证资料",
               hide: true,
             },
             component: () =>
diff --git a/culture/src/utils/baseurl.js b/culture/src/utils/baseurl.js
index 7b64940..fa72e83 100644
--- a/culture/src/utils/baseurl.js
+++ b/culture/src/utils/baseurl.js
@@ -2,13 +2,14 @@
     // 开发环境
     development: {
         baseURL: "http://221.182.45.100:8081",
-        imgUrl: "http://221.182.45.100:8081/open/file/upload",
-        showImgUrl:'http://221.182.45.100:9998/'
+        imgUrl: "http://192.168.110.64:8081/open/file/upload",
+        showImgUrl:'http://192.168.110.64:9998/'
     },
     // 生产环境
     production: {
-        baseURL: "",
-        imgUrl: "",
+        baseURL: "http://221.182.45.100:8081",
+        imgUrl: "http://221.182.45.100:8081/open/file/upload",
+        showImgUrl:'http://221.182.45.100:9998/'
     },
 };
 
diff --git a/culture/src/views/pedigree-chart/index.vue b/culture/src/views/pedigree-chart/index.vue
index 15d3c0e..8ed7a74 100644
--- a/culture/src/views/pedigree-chart/index.vue
+++ b/culture/src/views/pedigree-chart/index.vue
@@ -32,7 +32,7 @@
               草稿箱
             </div> -->
           </div>
-          <div class="flex a-center" v-if="roleType == 4">
+          <div class="flex a-center" v-if="roleType == 3">
             <el-button @click="handleNewStrain" class="el-icon-plus" type="primary"
               style="margin-right: 12px">新增祖代起传</el-button>
             <el-button @click="handleBatchAdd" class="el-icon-plus" type="primary">新增母代起传</el-button>
diff --git a/culture/src/views/strain-library/breeding-record/index.vue b/culture/src/views/strain-library/breeding-record/index.vue
index 75e5ee4..56cb853 100644
--- a/culture/src/views/strain-library/breeding-record/index.vue
+++ b/culture/src/views/strain-library/breeding-record/index.vue
@@ -49,10 +49,13 @@
             <span>{{ scope.row.preserveSource === 1 ? '来源菌株' : '来源物资' }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="strainName" label="菌株名称">
+        <el-table-column prop="strainName" label="来源信息">
+          <template slot-scope="scope">
+            <span>{{ scope.row.preserveSource === 1 ? scope.row.strainName : scope.row.sourceMaterialTimeBatchNumber }}</span>
+          </template>
         </el-table-column>
-        <el-table-column prop="sourceMaterialTimeBatchNumber" label="来源物资、时间及批号">
-        </el-table-column>
+        <!-- <el-table-column prop="sourceMaterialTimeBatchNumber" label="来源物资、时间及批号">
+        </el-table-column> -->
         <el-table-column prop="cultureMedium" label="培养基"></el-table-column>
         <el-table-column prop="temperature" label="培养温度"></el-table-column>
         <el-table-column prop="aerobicType" label="需氧类型">
diff --git a/culture/src/views/strain-library/production-cell-grandson/add.vue b/culture/src/views/strain-library/production-cell-grandson/add.vue
new file mode 100644
index 0000000..bf1349f
--- /dev/null
+++ b/culture/src/views/strain-library/production-cell-grandson/add.vue
@@ -0,0 +1,439 @@
+<template>
+    <Card>
+        <el-form :model="form" :rules="rules" ref="strainForm" label-position="top" class="strain-form">
+            <div class="form-row three-columns">
+                <el-form-item label="菌种编号" prop="strainCode">
+                    <el-input v-model="form.strainCode" placeholder="请输入"></el-input>
+                </el-form-item>
+                <el-form-item label="菌种名称" prop="strainName">
+                    <el-input v-model="form.strainName" placeholder="请输入"></el-input>
+                </el-form-item>
+                <el-form-item label="菌种来源" prop="strainSource">
+                    <el-input v-model="form.strainSource" placeholder="请输入"></el-input>
+                </el-form-item>
+            </div>
+
+            <div class="form-row">
+                <el-form-item label="鉴定方法" prop="appraisalMethod" class="full-width">
+                    <el-input type="textarea" :rows="4" v-model="form.appraisalMethod" placeholder="请输入"></el-input>
+                </el-form-item>
+            </div>
+
+            <div class="form-row">
+                <el-form-item label="特征描述" prop="features" class="full-width">
+                    <el-input type="textarea" v-model="form.features" :rows="4" placeholder="请输入"></el-input>
+                </el-form-item>
+            </div>
+
+            <div class="form-row three-columns">
+                <el-form-item label="保藏位置" prop="saveLocation">
+                    <el-input v-model="form.saveLocation" placeholder="请输入"></el-input>
+                </el-form-item>
+                <el-form-item label="菌种保存方法" prop="saveMethod">
+                    <el-input v-model="form.saveMethod" placeholder="请输入"></el-input>
+                </el-form-item>
+                <div class="form-item-placeholder"></div>
+            </div>
+
+            <div class="form-row">
+                <el-form-item label="备注" prop="remarks" class="full-width">
+                    <el-input type="textarea" v-model="form.remark" :rows="4" placeholder="请输入"></el-input>
+                </el-form-item>
+            </div>
+
+            <div class="end-btn" style="margin-top: 38px">
+                <el-button type="primary" @click="handleSubmit(0)">提交</el-button>
+                <el-button v-if="!$route.query.id" type="primary" @click="handleBatchAdd">批量新增</el-button>
+                <el-button @click="handleSubmit(1)">存草稿</el-button>
+            </div>
+        </el-form>
+
+        <!-- 批量新增弹窗 -->
+        <el-dialog title="批量新增" :visible.sync="batchAddDialogVisible" width="520px" :close-on-click-modal="false"
+            :close-on-press-escape="false" custom-class="batch-add-dialog">
+            <div class="dialog-content">
+                <el-form :model="batchForm" ref="batchFormRef" label-position="top" class="batch-form">
+                    <el-form-item label="批量新增数量" prop="count"
+                        :rules="[{ required: true, message: '请输入批量新增数量', trigger: 'blur' }]">
+                        <el-input v-model.number="batchForm.count" placeholder="请输入" />
+                    </el-form-item>
+                </el-form>
+                <div class="dialog-notice">
+                    <p>注意:操作批量新增后,系统会自动生成对应数量的菌种数据,</p>
+                    <p>这些菌种的基础信息内容都是一致的,唯独菌种编号内容系统</p>
+                    <p>不会自动生成,需要操作员自行编辑</p>
+                </div>
+            </div>
+            <template #footer>
+                <div class="end-btn">
+                    <el-button type="primary" @click="handleConfirmBatchAdd">确认新增</el-button>
+                </div>
+            </template>
+        </el-dialog>
+
+        <!-- 签字确认组件 -->
+        <SignatureCanvas :visible.sync="signatureVisible" @confirm="handleSignatureConfirm" />
+    </Card>
+</template>
+
+<script>
+import SignatureCanvas from '@/components/SignatureCanvas.vue'
+import { add, edit, getDetail, addBatch } from './service'
+
+export default {
+    name: 'StrainLibraryManageAdd',
+    components: {
+        SignatureCanvas
+    },
+    data() {
+        return {
+            batchAddDialogVisible: false,
+            signatureVisible: false,
+            currentAction: '', // 'submit' or 'batchAdd'
+            batchForm: {
+                count: ''
+            },
+            form: {
+                strainCode: '',
+                strainName: '',
+                source: '',
+                appraisalMethod: '',
+                characteristics: '',
+                storageLocation: '',
+                preservationMethod: '',
+                remark: ''
+            },
+            rules: {
+                strainCode: [{
+                    validator: (rule, value, callback) => {
+                        if (this.currentAction === 'submit' && !value) {
+                            callback(new Error('请输入菌种编号'));
+                        } else {
+                            callback();
+                        }
+                    },
+                    trigger: 'change'
+                }],
+                strainName: [{ required: true, message: '请输入菌种名称', trigger: 'blur' }],
+                strainSource: [{ required: true, message: '请输入菌种来源', trigger: 'blur' }],
+                appraisalMethod: [{ required: true, message: '请输入鉴定方法', trigger: 'blur' }],
+                features: [{ required: true, message: '请输入特征描述', trigger: 'blur' }],
+                saveLocation: [{ required: true, message: '请输入保存位置', trigger: 'blur' }],
+                saveMethod: [{ required: true, message: '请输入菌种保存方法', trigger: 'blur' }]
+            }
+        }
+    },
+    mounted() {
+        this.form = {
+            strainCode: '',
+            strainName: '',
+            source: '',
+            appraisalMethod: '',
+            characteristics: '',
+            storageLocation: '',
+            preservationMethod: '',
+            remark: ''
+        }
+        if (this.$route.query.id) {
+            getDetail({ id: this.$route.query.id }).then(res => {
+                this.form = res
+            })
+        }
+    },
+    watch: {
+        '$route.query.id'() {
+            this.form = {
+                strainCode: '',
+                strainName: '',
+                source: '',
+                appraisalMethod: '',
+                characteristics: '',
+                storageLocation: '',
+                preservationMethod: '',
+                remark: ''
+            }
+        }
+    },
+    methods: {
+        handleSubmit(isDraft) {
+            this.currentAction = 'submit'
+            this.$refs.strainForm.validate((valid) => {
+                if (valid) {
+                    this.form.isDraft = isDraft
+                    if (isDraft == 1) {
+                        //存草稿
+                        this.handleSignatureConfirm('')
+                    } else {
+                        this.signatureVisible = true
+                    }
+                }
+            })
+        },
+        handleBatchAdd() {
+            this.currentAction = 'batchAdd'
+            this.$refs.strainForm.validate((valid) => {
+                if (valid) {
+                    this.batchAddDialogVisible = true
+                }
+            })
+        },
+        handleConfirmBatchAdd() {
+            this.$refs.batchFormRef.validate((valid) => {
+                if (valid) {
+                    this.batchAddDialogVisible = false
+                    this.signatureVisible = true
+                }
+            })
+        },
+        async handleSignatureConfirm(signatureImage) {
+            let requestData = {
+                strainCode: this.form.strainCode,
+                strainName: this.form.strainName,
+                strainSource: this.form.strainSource,
+                appraisalMethod: this.form.appraisalMethod,
+                features: this.form.features,
+                saveLocation: this.form.saveLocation,
+                saveMethod: this.form.saveMethod,
+                remark: this.form.remark,
+                signature: signatureImage,
+                type: 4,
+            };
+            if (this.currentAction === 'batchAdd') {
+                requestData.batchCount = this.batchForm.count;
+            } else {
+                requestData.isDraft = this.form.isDraft
+            }
+            try {
+                if (this.$route.query.id) {
+                    requestData.id = this.$route.query.id;
+                    await edit(requestData);
+                } else if (this.currentAction === 'batchAdd') {
+                    await addBatch(requestData);
+                } else {
+                    await add(requestData);
+                }
+                this.signatureVisible = false;
+                this.$router.push({ path: '/strain-library/production-cell-grandson' });
+                this.$message.success('操作成功');
+            } catch (error) {
+                this.$message.error('操作失败');
+            }
+        }
+    }
+}
+</script>
+
+<style scoped lang="less">
+.add-strain {
+    height: 100%;
+    background: #F5F7FA;
+
+    .form-card {
+        background: #fff;
+        border-radius: 8px;
+    }
+}
+
+.header-title {
+    margin-bottom: 24px;
+
+    &-left {
+        display: flex;
+        align-items: center;
+
+        img {
+            width: 20px;
+            height: 20px;
+            margin-right: 8px;
+        }
+
+        div {
+            font-size: 18px;
+            font-weight: bold;
+            color: #303133;
+        }
+    }
+}
+
+.end-btn {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 10px;
+
+    button {
+        width: 180px;
+        height: 36px;
+        // background: #409EFF; 
+    }
+}
+
+.strain-form {
+    padding: 0 40px;
+
+    .form-row {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 24px;
+        margin-bottom: 24px;
+
+        &.three-columns {
+
+            .el-form-item,
+            .form-item-placeholder {
+                flex: 1;
+                min-width: 280px;
+
+                @media screen and (max-width: 1200px) {
+                    min-width: calc(50% - 12px);
+                }
+
+                @media screen and (max-width: 768px) {
+                    min-width: 100%;
+                }
+            }
+
+            .form-item-placeholder {
+                @media screen and (max-width: 1200px) {
+                    display: none;
+                }
+            }
+        }
+
+        .el-form-item {
+            margin-bottom: 0;
+
+            &.full-width {
+                width: 100%;
+            }
+        }
+    }
+
+    :deep(.el-form-item__label) {
+        font-weight: normal;
+        color: #606266;
+        padding-bottom: 8px;
+        line-height: 20px;
+    }
+
+    :deep(.el-form-item__content) {
+        line-height: unset;
+    }
+
+    :deep(.el-input__inner) {
+        border-radius: 4px;
+        height: 36px;
+        line-height: 36px;
+    }
+
+    :deep(.el-textarea__inner) {
+        border-radius: 4px;
+        padding: 8px 12px;
+        min-height: 120px;
+    }
+}
+
+.batch-add-dialog {
+    :deep(.el-dialog__header) {
+        margin: 0;
+        padding: 20px;
+        text-align: center;
+        border-bottom: 1px solid #EBEEF5;
+
+        .el-dialog__title {
+            font-size: 16px;
+            font-weight: 600;
+            color: #303133;
+        }
+    }
+
+    :deep(.el-dialog__body) {
+        padding: 20px;
+    }
+
+    .dialog-content {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+    }
+
+    .batch-form {
+        width: 100%;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+
+        :deep(.el-form-item) {
+            width: 320px;
+            margin-bottom: 0;
+        }
+
+        :deep(.el-form-item__label) {
+            width: 100%;
+            color: #606266;
+            font-weight: normal;
+            padding-bottom: 8px;
+
+            &::before {
+                color: #F56C6C;
+            }
+        }
+
+        :deep(.el-input) {
+            width: 100%;
+
+            input {
+                width: 100%;
+            }
+        }
+    }
+
+    .dialog-notice {
+        margin-top: 16px;
+        text-align: center;
+
+        p {
+            margin: 0;
+            line-height: 22px;
+            color: #606266;
+            font-size: 14px;
+        }
+    }
+
+    :deep(.el-dialog__footer) {
+        padding: 0 20px 20px;
+        text-align: center;
+
+        .el-button {
+            width: 180px;
+            height: 36px;
+            padding: 0;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            font-size: 14px;
+            border-radius: 4px;
+            margin: 0;
+        }
+    }
+}
+
+.end-btn {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 10px;
+
+    :deep(.el-button) {
+        width: 180px;
+        height: 36px;
+        padding: 0;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 14px;
+        border-radius: 4px;
+        margin: 0;
+    }
+}
+</style>
\ No newline at end of file
diff --git a/culture/src/views/strain-library/production-cell-grandson/index.vue b/culture/src/views/strain-library/production-cell-grandson/index.vue
new file mode 100644
index 0000000..191b75b
--- /dev/null
+++ b/culture/src/views/strain-library/production-cell-grandson/index.vue
@@ -0,0 +1,521 @@
+<template>
+  <div class="list">
+    <el-card class="header-box">
+      <div class="box-title">
+        <img src="@/assets/public/notice.png" class="header-icon" />
+        <span>菌种源保藏出/入细胞库登记表说明</span>
+        <el-button type="text" class="view-more" @click="handleViewMore"
+          >查看全部 >></el-button
+        >
+      </div>
+      <div class="header-content" :class="{ collapsed: true }">
+        <p> 1. 菌种全部集中登记在【菌种源保藏出/入细胞库登记表】,菌种来源有 3条路径。</p>
+        <p> 1.1 是沙土管或甘油管的源头菌种;入原始细胞库(祖代-O)。</p>
+        <p> 1.2 是斜面的源头菌种;接种入主细胞库(祖代-O)。经过育种、验证后,菌种保藏为甘油管或沙土管的,入原始细胞库(祖代-0)</p>
+        <p> 1.3 是含菌物质自己分离后获得的斜面源头菌种,接种入主细胞库;经生产验证后,保藏为沙土管或甘油管,入原始细胞库(祖代-O)。</p>
+        <p> 2.菌种细胞库,分类入三库,进行传代运行管理。三类库存空间进行区分,保藏菌种。</p>
+        <p> 2.1 原始细胞库(祖代-O)、2.2 主细胞库(母代-M)、 </p>
+        <p> 2.3 生产细胞库(子代-S)、(孙代-G)</p>
+        <p> 3. 细胞库编码规则 </p>
+        <p>3.1
+          细胞库编码规则:DD-M-240919-01-(O-0109-01)DD:代表项目组。M:“O”代表祖代原始细胞库,”M“代表母代主细胞库,”S“代表子代生产细胞库,“G”代表孙代生产细胞库。240919:代表在24 年
+          9 月 19
+          接种批次的菌种;或收到外来菌种时间的入库批次。01:代表两位序列号。(O-0109-01):代表传代菌种的编号</p>
+        <p>3.1.1
+        传代编码方式演例:祖代:DD-O-240919-01传母代:DD-M-241017-01-(O-091901)DD-M-241017-02-(O-091901)DD-M-241017-03-(O-091901)子代:DD-S-241019-01-(M-1017-02)版权归奥利元生物所有,禁止外传。DD-S-241019-02-(M-1017-03)孙代:DD-G-241109-01-(S-1019-02)3.1.2编码规则实现了编码唯一,编码可溯源,编码直观、方便。</p>
+       <p> 3.2 细胞库说明:</p>
+       <p> 3.2.1 直接购买、自行从(土壤、相关物料、商品)等分离出来菌株进入原始细胞库。</p>
+       <p> 3.2.2 从原始细胞库中选取出来再次纯化、改造、提高性能的菌株进入主细胞库。</p>
+       <p> 3.2.3 主细胞库中选取出稳定,生产性能良好的菌株扩培后保种进入生产细胞库。</p>
+       <p> 4.菌种选育-保藏过程编号说明4.1 菌种选育时,培养皿的编号可使用 a-01、a-02等用于清晰形态观察记录;菌落编号使用序号 1/2/3等。</p>
+       <p> 4.2 接种斜面菌种编码(-O)使用原始细胞库编码;斜面转菌种保藏使用与斜面一致的编码(-O);斜面传代入主细胞库的传代菌种,按编码器规则编码(-M)。</p>
+      </div>
+
+      <!-- 查看全部弹窗 -->
+      <el-dialog
+        title="菌种源保藏出/入细胞库登记表说明"
+        :visible.sync="dialogVisible"
+        width="50%"
+        class="view-all-dialog"
+      >
+        <div class="dialog-content">
+          <p> 1. 菌种全部集中登记在【菌种源保藏出/入细胞库登记表】,菌种来源有 3条路径。</p>
+        <p> 1.1 是沙土管或甘油管的源头菌种;入原始细胞库(祖代-O)。</p>
+        <p> 1.2 是斜面的源头菌种;接种入主细胞库(祖代-O)。经过育种、验证后,菌种保藏为甘油管或沙土管的,入原始细胞库(祖代-0)</p>
+        <p> 1.3 是含菌物质自己分离后获得的斜面源头菌种,接种入主细胞库;经生产验证后,保藏为沙土管或甘油管,入原始细胞库(祖代-O)。</p>
+        <p> 2.菌种细胞库,分类入三库,进行传代运行管理。三类库存空间进行区分,保藏菌种。</p>
+        <p> 2.1 原始细胞库(祖代-O)、2.2 主细胞库(母代-M)、 </p>
+        <p> 2.3 生产细胞库(子代-S)、(孙代-G)</p>
+        <p> 3. 细胞库编码规则 </p>
+        <p>3.1
+          细胞库编码规则:DD-M-240919-01-(O-0109-01)DD:代表项目组。M:“O”代表祖代原始细胞库,”M“代表母代主细胞库,”S“代表子代生产细胞库,“G”代表孙代生产细胞库。240919:代表在24 年
+          9 月 19
+          接种批次的菌种;或收到外来菌种时间的入库批次。01:代表两位序列号。(O-0109-01):代表传代菌种的编号</p>
+        <p>3.1.1
+        传代编码方式演例:祖代:DD-O-240919-01传母代:DD-M-241017-01-(O-091901)DD-M-241017-02-(O-091901)DD-M-241017-03-(O-091901)子代:DD-S-241019-01-(M-1017-02)版权归奥利元生物所有,禁止外传。DD-S-241019-02-(M-1017-03)孙代:DD-G-241109-01-(S-1019-02)3.1.2编码规则实现了编码唯一,编码可溯源,编码直观、方便。</p>
+       <p> 3.2 细胞库说明:</p>
+       <p> 3.2.1 直接购买、自行从(土壤、相关物料、商品)等分离出来菌株进入原始细胞库。</p>
+       <p> 3.2.2 从原始细胞库中选取出来再次纯化、改造、提高性能的菌株进入主细胞库。</p>
+       <p> 3.2.3 主细胞库中选取出稳定,生产性能良好的菌株扩培后保种进入生产细胞库。</p>
+       <p> 4.菌种选育-保藏过程编号说明4.1 菌种选育时,培养皿的编号可使用 a-01、a-02等用于清晰形态观察记录;菌落编号使用序号 1/2/3等。</p>
+       <p> 4.2 接种斜面菌种编码(-O)使用原始细胞库编码;斜面转菌种保藏使用与斜面一致的编码(-O);斜面传代入主细胞库的传代菌种,按编码器规则编码(-M)。</p>
+        </div>
+      </el-dialog>
+    </el-card>
+
+    <!-- Table -->
+    <TableCustom
+      :queryForm="queryForm"
+      :tableData="tableData"
+      :total="total"
+      @handleCurrentChange="handleCurrentChange"
+      @handleSizeChange="handleSizeChange"
+    >
+      <template #search>
+        <el-form :model="form" label-width="auto" inline>
+          <el-form-item label="菌种编号:">
+            <el-input v-model="form.strainNo" placeholder="请输入"></el-input>
+          </el-form-item>
+          <el-form-item label="菌种名称:">
+            <el-input v-model="form.strainName" placeholder="请输入"></el-input>
+          </el-form-item>
+          <el-form-item v-if="roleType == 4" label="状态:">
+            <el-select v-model="form.status" placeholder="请选择">
+              <el-option label="全部" value=""></el-option>
+              <el-option label="已出库" value="1"></el-option>
+              <el-option label="出库待确认" value="2"></el-option>
+              <el-option label="已入库" value="3"></el-option>
+              <el-option label="入库待确认" value="4"></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item class="search-btn-box">
+            <el-button type="default" @click="resetForm">重置</el-button>
+            <el-button type="primary" @click="searchData">查询</el-button>
+          </el-form-item>
+        </el-form>
+      </template>
+
+      <template #setting>
+        <div class="tableTitle">
+          <div class="flex a-center">
+            <div
+              class="title"
+              :class="{ active: currentType === 'list' }"
+              @click="handleTypeChange('list')"
+            >
+              孙细胞列表
+            </div>
+            <div
+              class="drafts"
+              :class="{ active: currentType === 'draft' }"
+              @click="handleTypeChange('draft')"
+            >
+              草稿箱
+            </div>
+          </div>
+          <div v-if="roleType == 4" class="flex a-center">
+            <el-button
+              @click="handleNewStrain"
+              class="el-icon-plus"
+              type="primary"
+              style="margin-right: 12px"
+              >新增孙细胞</el-button
+            >
+            <el-button
+              @click="handleNewStrain"
+              class="el-icon-plus"
+              type="primary"
+              >批量新增</el-button
+            >
+          </div>
+        </div>
+      </template>
+
+      <template #table>
+        <el-table-column prop="strainCode" label="菌种编号" />
+        <el-table-column prop="strainName" label="菌种名称" />
+        <el-table-column prop="strainSource" label="菌种来源" />
+        <el-table-column prop="appraisalMethod" label="鉴定方法" />
+          <el-table-column prop="features" label="特征描述" >
+          <template #default="{ row }">
+            <el-tooltip :content="row.features" placement="top" effect="dark">
+              <div class="ellipsis-text">{{ row.features }}</div>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column prop="saveMethod" label="菌种保存方法" />
+        <el-table-column prop="saveLocation" label="保藏位置" />
+        <el-table-column prop="stock" label="库存余量" />
+        <el-table-column prop="remark" label="备注" >
+          <template #default="{ row }">
+            <el-tooltip popper-class="custom-tooltip" :content="row.remark" style="width: 200px;" placement="top" effect="dark">
+              <div class="ellipsis-text">{{ row.remark }}</div>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column
+          v-if="currentType === 'list'"
+          prop="status"
+          label="当前状态"
+        >
+          <template #default="{ row }">
+            <el-tag :type="getStatusType(row.status)">{{
+              getStatusText(row.status)
+            }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="200">
+          <template #default="{ row }">
+            <el-button style="margin-right: 10px;" type="text" @click="handleDetail(row)">详情</el-button>
+            <el-button style="margin-right: 10px;" v-if="row.status == 2 || row.status == 4" type="text" @click="handleEdit(row)">编辑</el-button>
+            <el-button
+              style="margin-right: 10px;"
+              v-if="currentType === 'list'"
+              type="text"
+              @click="handleRecord(row)"
+              >出入库记录</el-button
+            >
+            <el-button v-if="roleType == 1" type="text" @click="handleDelete(row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </template>
+    </TableCustom>
+    <StrainDetail title="生产细胞库" :visible.sync="detailVisible" :detail="currentDetail" />
+  </div>
+</template>
+
+<script>
+import StrainDetail from "../strain-library-manage/components/StrainDetail.vue";
+import { getList, deleteStrainLibrary } from "../strain-library-manage/service";
+
+export default {
+  name: "StrainLibraryManage",
+  components: {
+    StrainDetail,
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      currentType: "list",
+      detailVisible: false,
+      currentDetail: {},
+      form: {
+        strainNo: "",
+        strainName: "",
+        status: "",
+      },
+      queryForm: {
+        pageSize: 10,
+        pageNum: 1,
+      },
+      total: 800,
+      tableData: [],
+      roleType: "",
+    };
+  },
+  created() {
+    this.searchData();
+    // 角色类型 1=超级管理员 2=审批人 3=工程师 4=实验员
+    this.roleType = JSON.parse(sessionStorage.getItem("userInfo")).roleType;
+  },
+  activated() {
+    this.searchData();
+    // 角色类型 1=超级管理员 2=审批人 3=工程师 4=实验员
+    this.roleType = JSON.parse(sessionStorage.getItem("userInfo")).roleType;
+  },
+  methods: {
+    handleDelete(row) {
+      this.$confirm("确定删除该数据吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }).then(() => {
+        deleteStrainLibrary({ id: row.id }).then((res) => {
+            this.$message.success("删除成功");
+            this.searchData();
+        });
+      });
+    },
+    handleRecord(row) {
+      this.$router.push({
+        path: `/strain-library/strain-library-manage/record?id=${row.id}&type=4`,
+      });
+    },
+    handleNewStrain() {
+      this.$router.push({ path: "/strain-library/production-cell-grandson/add" });
+    },
+    handleEdit(row) {
+      this.$router.push({
+        path: `/strain-library/production-cell-grandson/edit?id=${row.id}`,
+      });
+    },
+    handleDetail(row) {
+      this.currentDetail = row;
+      this.detailVisible = true;
+    },
+    handleViewMore() {
+      this.dialogVisible = true;
+    },
+    resetForm() {
+      this.form = {
+        strainNo: "",
+        strainName: "",
+        status: "",
+      };
+      this.searchData();
+    },
+    searchData() {
+      const params = {
+        pageNum: this.queryForm.pageNum,
+        pageSize: this.queryForm.pageSize,
+        strainCode: this.form.strainNo,
+        strainName: this.form.strainName,
+        isDraft: this.currentType === "draft" ? 1 : 0,
+        status: this.form.status,
+        type: 4,
+      };
+      getList(params)
+        .then((res) => {
+          if (res.code === 200) {
+            this.tableData = res.data.records;
+            this.total = res.data.total;
+          }
+        })
+        .catch((err) => {
+          this.$message.error("数据加载失败");
+        });
+    },
+    handleCurrentChange(page) {
+      this.queryForm.pageNum = page;
+      this.searchData();
+    },
+    handleSizeChange(size) {
+      this.queryForm.pageSize = size;
+      this.searchData();
+    },
+    handleTypeChange(type) {
+      this.currentType = type;
+      this.searchData();
+    },
+    getStatusType(status) {
+      const types = {
+        1: "info",
+        2: "success",
+        3: "info",
+        4: "warning",
+      };
+      return types[status] || "info";
+    },
+    getStatusText(status) {
+      const texts = {
+        1: "已出库",
+        2: "出库待确认",
+        3: "已入库",
+        4: "入库待确认",
+      };
+      return texts[status] || "未知状态";
+    },
+  },
+};
+</script>
+
+<style scoped lang="less">
+.list {
+  padding: 20px;
+}
+
+.header-box {
+  margin-bottom: 20px;
+  border-radius: 16px;
+  background: rgba(255, 255, 255, 0.8);
+
+  .box-title {
+    display: flex;
+    align-items: center;
+    font-size: 18px;
+    font-weight: bold;
+    margin-bottom: 15px;
+    position: relative;
+
+    .header-icon {
+      width: 20px;
+      height: 20px;
+      margin-right: 10px;
+    }
+
+    .view-more {
+      position: absolute;
+      right: 0;
+      color: #049c9a;
+    }
+  }
+
+  .header-content {
+    color: rgba(0, 0, 0, 0.88);
+    font-size: 14px;
+    line-height: 1.8;
+    margin-left: 30px;
+    transition: max-height 0.3s ease-in-out;
+    overflow: hidden;
+
+    &.collapsed {
+      max-height: 130px;
+      overflow: hidden;
+    }
+
+    p {
+      margin: 5px 0;
+    }
+  }
+}
+
+.search-form {
+  margin-bottom: 20px;
+  background: #f5f7fa;
+  padding: 24px;
+  border-radius: 8px;
+
+  .el-form-item {
+    margin-right: 20px;
+    margin-bottom: 0;
+  }
+
+  .el-button {
+    margin-left: 10px;
+  }
+}
+
+.action-buttons {
+  margin-bottom: 20px;
+
+  .el-button {
+    margin-right: 10px;
+  }
+}
+
+.tab-container {
+  display: flex;
+  margin-bottom: 20px;
+
+  .tab {
+    padding: 10px 30px;
+    border: 1px solid #dcdfe6;
+    border-bottom: none;
+    border-radius: 8px 8px 0 0;
+    cursor: pointer;
+    margin-right: 10px;
+    background: #f5f7fa;
+
+    &.active {
+      background: #fff;
+      border-color: #049c9a;
+      color: #049c9a;
+      font-weight: bold;
+    }
+  }
+}
+
+.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;
+    width: unset;
+    cursor: pointer;
+    height: 50px;
+    line-height: 50px;
+    width: 166px;
+    text-align: center;
+  }
+
+  .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: 166px;
+    text-align: center;
+  }
+
+  .active {
+    color: #049c9a;
+    background: #ffffff;
+    border-radius: 8px 8px 0px 0px;
+    border: 1px solid #049c9a;
+  }
+}
+
+.view-all-dialog {
+  :deep(.el-dialog__header) {
+    padding: 20px;
+    border-bottom: 1px solid #ebeef5;
+    margin-right: 0;
+
+    .el-dialog__title {
+      font-size: 18px;
+      font-weight: bold;
+      color: #303133;
+    }
+  }
+
+  :deep(.el-dialog__body) {
+    padding: 20px;
+
+    .dialog-content {
+      font-size: 14px;
+      line-height: 1.8;
+      color: #606266;
+
+      p {
+        margin: 12px 0;
+
+        &:first-child {
+          margin-top: 0;
+        }
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+      }
+    }
+  }
+}
+
+
+.ellipsis-text {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  word-break: break-all;
+}
+</style>
+
+<style lang="less">
+.custom-tooltip {
+  width: 200px !important;
+  max-width: 200px !important;
+  white-space: normal;
+  word-break: break-all;
+}
+</style>
diff --git a/culture/src/views/strain-library/production-cell-grandson/record.vue b/culture/src/views/strain-library/production-cell-grandson/record.vue
new file mode 100644
index 0000000..4405c4e
--- /dev/null
+++ b/culture/src/views/strain-library/production-cell-grandson/record.vue
@@ -0,0 +1,437 @@
+<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.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>
+            <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-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>
+
+    <!-- 详情弹窗 -->
+    <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: "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: "",
+    };
+  },
+  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: {
+    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) => {
+        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;
+
+  .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;
+        }
+
+        .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 {
+    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;
+    }
+
+    .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>
diff --git a/culture/src/views/strain-library/production-cell-grandson/service.js b/culture/src/views/strain-library/production-cell-grandson/service.js
new file mode 100644
index 0000000..08f789d
--- /dev/null
+++ b/culture/src/views/strain-library/production-cell-grandson/service.js
@@ -0,0 +1,56 @@
+import axios from '@/utils/request';
+
+// 列表
+export const getList = (data) => {
+  return axios.post('/api/t-train-library/pageList', { ...data })
+}
+
+// 新增
+export const add = (data) => {
+  return axios.post('/api/t-train-library/add', { ...data })
+}
+
+// 编辑
+export const edit = (data) => {
+  return axios.post('/api/t-train-library/update', { ...data })
+}
+
+// 查看详情
+export const getDetail = (params) => {
+  return axios.get('/open/t-train-library/getDetailEditById', { params })
+}
+
+// 批量新增
+export const addBatch = (data) => {
+  return axios.post('/api/t-train-library/addBatch', data)
+}
+
+// 查看菌种库详情
+export const getDetailById = (data) => {
+  return axios.post('/open/t-train-library/getDetailById', { ...data })
+}
+
+// 获取菌种库出入库时间轴列表
+export const timeList = (data) => {
+  return axios.post('/api/t-train-library/timeList?id='+data.id, { ...data })
+}
+
+// 新增菌种库出入记录
+export const addWarehousing = (data) => {
+  return axios.post('/open/t-train-library/addWarehousing', { ...data })
+}
+
+// 确认出入库
+export const confirmWarehousing = (data) => {
+  return axios.post('/api/t-train-library/confirm', { ...data })
+}
+
+// 删除菌种库
+export const deleteStrainLibrary = (params) => {
+  return axios.delete('/open/t-train-library/deleteById', { params })
+}
+
+// 删除菌种库出入库记录
+export const deleteWarehousing = (params) => {
+  return axios.delete('/open/t-train-library/deleteWarehousingById', { params })
+}
diff --git a/culture/src/views/strain-library/production-cell-library/index.vue b/culture/src/views/strain-library/production-cell-library/index.vue
index 7fcc328..34cc9f3 100644
--- a/culture/src/views/strain-library/production-cell-library/index.vue
+++ b/culture/src/views/strain-library/production-cell-library/index.vue
@@ -103,7 +103,7 @@
               :class="{ active: currentType === 'list' }"
               @click="handleTypeChange('list')"
             >
-              生产细胞列表
+              子细胞列表
             </div>
             <div
               class="drafts"
@@ -119,7 +119,7 @@
               class="el-icon-plus"
               type="primary"
               style="margin-right: 12px"
-              >新增生产细胞</el-button
+              >新增子细胞</el-button
             >
             <el-button
               @click="handleNewStrain"
diff --git a/culture/src/views/strain-library/validation/chief-cell/add.vue b/culture/src/views/strain-library/validation/chief-cell/add.vue
index 2a20f16..c4401d7 100644
--- a/culture/src/views/strain-library/validation/chief-cell/add.vue
+++ b/culture/src/views/strain-library/validation/chief-cell/add.vue
@@ -3,7 +3,7 @@
         <!-- <div class="header-title">
             <div class="header-title-left">
                 <img src="@/assets/public/headercard.png" />
-                <div>新增原始细胞</div>
+                <div>新增母代菌种库验证资料</div>
             </div>
         </div> -->
         <el-form :model="form" :rules="rules" ref="strainForm" label-position="top" class="strain-form">
diff --git a/culture/src/views/strain-library/validation/chief-cell/index.vue b/culture/src/views/strain-library/validation/chief-cell/index.vue
index d0a83f2..c160070 100644
--- a/culture/src/views/strain-library/validation/chief-cell/index.vue
+++ b/culture/src/views/strain-library/validation/chief-cell/index.vue
@@ -26,7 +26,7 @@
         <div class="tableTitle">
           <div class="flex a-center">
             <div class="title" :class="{ active: currentType === 'list' }" @click="handleTypeChange('list')">
-              主细胞库验证资料
+              母代菌种库验证资料列表
             </div>
             <div class="drafts" v-if="roleType == 4|| roleType==3" :class="{ active: currentType === 'draft' }"
               @click="handleTypeChange('draft')">
@@ -35,7 +35,7 @@
           </div>
           <div class="flex a-center" v-if="roleType == 4">
             <el-button @click="handleNewStrain" class="el-icon-plus" type="primary"
-              style="margin-right: 12px">新增主细胞</el-button>
+              style="margin-right: 12px">新增母代菌种库验证资料</el-button>
           </div>
         </div>
       </template>
@@ -335,7 +335,7 @@
     cursor: pointer;
     height: 50px;
     line-height: 50px;
-    width: 166px;
+    min-width: 166px;
     text-align: center;
   }
 
diff --git a/culture/src/views/strain-library/validation/chief-cell/primitive-cell-detail-dialog.vue b/culture/src/views/strain-library/validation/chief-cell/primitive-cell-detail-dialog.vue
index 8227ad2..244385b 100644
--- a/culture/src/views/strain-library/validation/chief-cell/primitive-cell-detail-dialog.vue
+++ b/culture/src/views/strain-library/validation/chief-cell/primitive-cell-detail-dialog.vue
@@ -1,7 +1,7 @@
 <template>
   <el-dialog
     :visible.sync="visible"
-    title="主细胞库资料详情"
+    title="母代菌种库验证资料详情"
     width="650px"
     :close-on-click-modal="false"
     @close="handleClose"
diff --git a/culture/src/views/strain-library/validation/primitive-cell/index.vue b/culture/src/views/strain-library/validation/primitive-cell/index.vue
index 7e26411..c210af4 100644
--- a/culture/src/views/strain-library/validation/primitive-cell/index.vue
+++ b/culture/src/views/strain-library/validation/primitive-cell/index.vue
@@ -26,7 +26,7 @@
         <div class="tableTitle">
           <div class="flex a-center">
             <div class="title" :class="{ active: currentType === 'list' }" @click="handleTypeChange('list')">
-              原始细胞库验证资料
+              祖代菌种库验证资料
             </div>
             <div class="drafts" v-if="roleType == 4 || roleType == 3" :class="{ active: currentType === 'draft' }"
               @click="handleTypeChange('draft')">
@@ -35,7 +35,7 @@
           </div>
           <div class="flex a-center" v-if="roleType == 4">
             <el-button @click="handleNewStrain" class="el-icon-plus" type="primary"
-              style="margin-right: 12px">新增原始细胞库资料</el-button>
+              style="margin-right: 12px">新增祖代菌种库验证资料</el-button>
           </div>
         </div>
       </template>
diff --git a/culture/src/views/strain-library/validation/primitive-cell/primitive-cell-detail-dialog.vue b/culture/src/views/strain-library/validation/primitive-cell/primitive-cell-detail-dialog.vue
index aeb7507..92b79bd 100644
--- a/culture/src/views/strain-library/validation/primitive-cell/primitive-cell-detail-dialog.vue
+++ b/culture/src/views/strain-library/validation/primitive-cell/primitive-cell-detail-dialog.vue
@@ -1,7 +1,7 @@
 <template>
   <el-dialog
     :visible.sync="visible"
-    title="原始细胞库资料详情"
+    title="祖代菌种库验证资料详情"
     width="650px"
     :close-on-click-modal="false"
     @close="handleClose"
diff --git a/culture/vue.config.js b/culture/vue.config.js
index d811999..1209754 100644
--- a/culture/vue.config.js
+++ b/culture/vue.config.js
@@ -1,53 +1,57 @@
-const path = require('path')
+const path = require("path");
 function resolve(dir) {
-    return path.join(__dirname, dir)
+  return path.join(__dirname, dir);
 }
 module.exports = {
-    outputDir: 'culture', // 配置打包后的文件夹名称
-    lintOnSave: false,
-    publicPath: '/',
-    devServer: {
-        disableHostCheck: true, //禁用主机检查 
-        proxy: {
-            "/api": { // 设置以什么前缀开头的请求用来代理
-                target: "http://221.182.45.100:8081", //要访问的跨域的域名
-                secure: false, // 使用的是http协议则设置为false,https协议则设置为true
-                changOrigin: true, //开启代理
-                pathRewrite: {
-                    "^/api": "/api",
-                },
-            },
-            "/": { // 设置以什么前缀开头的请求用来代理
-                target: "http://221.182.45.100:8081", //要访问的跨域的域名
-                secure: false, // 使用的是http协议则设置为false,https协议则设置为true
-                changOrigin: true, //开启代理
-                pathRewrite: {
-                    "^/": "/",
-                },
-            },
+  outputDir: "culture", // 配置打包后的文件夹名称
+  lintOnSave: false,
+  publicPath: "/",
+  devServer: {
+    disableHostCheck: true, //禁用主机检查
+    proxy: {
+      "/api": {
+        // 设置以什么前缀开头的请求用来代理
+        // target: "http://221.182.45.100:8081", //要访问的跨域的域名
+        target: "http://192.168.110.64:8081",
+        secure: false, // 使用的是http协议则设置为false,https协议则设置为true
+        changOrigin: true, //开启代理
+        pathRewrite: {
+          "^/api": "/api",
         },
+      },
+      "/": {
+        // 设置以什么前缀开头的请求用来代理
+        // target: "http://221.182.45.100:8081", //要访问的跨域的域名
+        target: "http://192.168.110.64:8081",
+        secure: false, // 使用的是http协议则设置为false,https协议则设置为true
+        changOrigin: true, //开启代理
+        pathRewrite: {
+          "^/": "/",
+        },
+      },
     },
-    configureWebpack: {
-        resolve: {
-            alias: {
-                '@': resolve('src'),
-            }
+  },
+  configureWebpack: {
+    resolve: {
+      alias: {
+        "@": resolve("src"),
+      },
+    },
+    module: {
+      rules: [
+        {
+          test: /\.m?js$/,
+          include: /node_modules/,
+          type: "javascript/auto",
+          use: {
+            loader: "babel-loader",
+            options: {
+              presets: ["@babel/preset-env"],
+              plugins: ["@babel/plugin-proposal-nullish-coalescing-operator"],
+            },
+          },
         },
-        module: {
-            rules: [
-                {
-                    test: /\.m?js$/,
-                    include: /node_modules/,
-                    type: "javascript/auto",
-                    use: {
-                        loader: 'babel-loader',
-                        options: {
-                            presets: ['@babel/preset-env'],
-                            plugins: ['@babel/plugin-proposal-nullish-coalescing-operator']
-                        }
-                    }
-                }
-            ]
-        }
-    }
-};
\ No newline at end of file
+      ],
+    },
+  },
+};

--
Gitblit v1.7.1