董国庆
6 天以前 10ffa741546c5d36b14932e4a6c17faa81e479bd
Merge branch 'main' of http://120.76.84.145:10101/gitblit/r/H5/leshan-laboratory
17个文件已修改
9个文件已添加
2413 ■■■■ 已修改文件
culture/src/components/SelectMember/index.vue 109 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/components/SelectMember/service.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/components/Table/index.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/layouts/components/HeaderNav.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/router/index.js 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/deliveryAssessment/projectTeamIntegral/detail.vue 80 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/deliveryAssessment/projectTeamIntegral/index.vue 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/deliveryAssessment/projectTeamIntegral/service.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/projectList/addProject.vue 91 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/projectList/detailProject.vue 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/projectList/editProject.vue 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/projectList/service.js 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/SlantRecordDialog.vue 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/add.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/separation-record-dialog.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/chief-cell/index.vue 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/primitive-cell/DetailConditionDialog.vue 282 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/primitive-cell/EditConditionDialog.vue 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/primitive-cell/add.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/primitive-cell/confirm-detail.vue 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/primitive-cell/index.vue 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/primitive-cell/primitive-cell-detail-dialog.vue 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/system/user/components/add-edit.vue 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/system/user/components/reset-password.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/system/user/index.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/system/user/service.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/components/SelectMember/index.vue
@@ -1,6 +1,6 @@
<template>
    <el-dialog class="select-member" :visible.sync="visible" width="53.33%" :close-on-click-modal="false"
        :show-close="false">
    <el-dialog @open="openDialog" class="select-member" :visible.sync="visible" width="53.33%"
        :close-on-click-modal="false" :show-close="false">
        <template #title>
            <div>选择参与人员</div>
        </template>
@@ -22,9 +22,10 @@
                        <div class="select-member-content-left-list">
                            <div class="select-member-content-left-list-title">角色列表</div>
                            <div class="select-member-content-left-list-itemBox">
                                <div v-for="item in 10" :key="item.id"
                                    class="select-member-content-left-list-itemBox-item">
                                    实验员
                                <div @click="searchUserList(item.roleId)" v-for="item in roleList" :key="item.roleId"
                                    class="select-member-content-left-list-itemBox-item"
                                    :class="roleId == item.roleId && 'active'">
                                    {{ item.roleName }}
                                </div>
                            </div>
                        </div>
@@ -35,15 +36,15 @@
                        <div class="select-member-content-right-header">
                            <div class="select-member-content-right-header-text">人员列表</div>
                            <div class="select-member-content-right-header-search">
                                <el-input clearable v-model="searchName" placeholder="请输入姓名" />
                                <el-input clearable v-model="nickNameOrPhone" placeholder="请输入姓名/手机号" />
                                <el-button type="primary">搜索</el-button>
                            </div>
                        </div>
                        <Table :data="tableData" :total="0" @selection-change="handleSelectionChange"
                            :row-class-name="tableRowClassName">
                        <Table ref="memberTable" :row-key="row => row.userId" :data="tableData" :total="0"
                            @selection-change="handleSelectionChange" :row-class-name="tableRowClassName">
                            <el-table-column type="selection" width="55" />
                            <el-table-column label="角色" prop="role" />
                            <el-table-column label="姓名" prop="name" />
                            <el-table-column label="角色" prop="roleName" />
                            <el-table-column label="姓名" prop="nickName" />
                            <el-table-column label="创建时间" prop="createTime" />
                        </Table>
                    </div>
@@ -52,40 +53,79 @@
        </div>
        <div class="select-member-footer">
            <el-button @click="close" type="default">关闭</el-button>
            <el-button type="primary">确认选择</el-button>
            <el-button type="primary" @click="submit">确认选择</el-button>
        </div>
    </el-dialog>
</template>
<script>
import { mapState } from 'vuex'
import { getRoleList, getUserList } from './service'
export default {
    props: {
        projectId: {
            type: [String, Number],
            default: null
        }
    },
    data() {
        return {
            visible: false,
            search: '',
            searchName: '',
            tableData: [
                {
                    name: '张三',
                    role: '实验员',
                    createTime: '2025-1-2 15:13:58'
                },
                {
                    name: '李四',
                    role: '实验员',
                    createTime: '2025-1-2 15:13:58'
                },
            ],
            selectData: []
            nickNameOrPhone: '',
            tableData: [],
            selectData: [],
            roleList: [],
            roleId: null,
        }
    },
    computed: {
        ...mapState(['isFold'])
    },
    methods: {
        setSelection(selected) {
            this.selectData = selected
            this.$nextTick(() => {
                // 设置新选中
                this.tableData.forEach(row => {
                    if (selected.some(i => i.userId === row.userId)) {
                        this.$refs.memberTable.toggleRowSelection(row, true)
                    }
                })
            })
        },
        openDialog() {
            // 获取角色列表并根据项目组ID进行过滤
            getRoleList().then(res => {
                if (this.projectId) {
                    // 过滤出实验员和化验师角色
                    this.roleList = res.filter(item => item.roleId == 4 || item.roleId == 5);
                } else {
                    this.roleList = res;
                }
            });
            this.searchUserList(null);
        },
        handleSelectionChange(val) {
            this.selectData = val
        },
        async searchUserList(roleId) {
            this.roleId = roleId
            // 根据是否有项目组ID来决定调用不同的接口
            const params = {
                roleIds: roleId ? [roleId] : [],
                nickNameOrPhone: this.searchName,
                pageSize: 9999,
                pageNum: 1
            };
            if (this.projectId) {
                params.projectId = this.projectId;
                // TODO: 这里需要替换为新的接口调用
                // const res = await getProjectUserList(params);
            } else {
                const res = await getUserList(params);
                this.tableData = res.records;
            }
            // 数据加载完成后重新应用选中状态
            this.setSelection(this.selectData)
        },
        searchRole() {
            this.search = ''
@@ -96,8 +136,11 @@
        close() {
            this.visible = false
        },
        submit() {
            this.$emit('submit', this.selectData)
        },
        tableRowClassName({ row, rowIndex }) {
            if (this.selectData.findIndex(item => item.name === row.name) != -1) {
            if (this.selectData.findIndex(item => item.userId === row.userId) != -1) {
                return 'select-row';
            }
            return '';
@@ -191,6 +234,12 @@
                    &:last-child {
                        margin-bottom: 0;
                    }
                    &:hover,
                    &.active {
                        background: rgba(4, 156, 154, 0.1);
                        color: #049C9A;
                    }
                }
            }
        }
culture/src/components/SelectMember/service.js
New file
@@ -0,0 +1,16 @@
import axios from '@/utils/request';
// 列表
export const getProjectList = (data) => {
    return axios.post('/api/t-project-team/pageList', { ...data })
}
// 用户列表
export const getUserList = (data) => {
    return axios.post('/system/user/list', { ...data })
}
// 角色列表不分页
export const getRoleList = (data) => {
    return axios.post('/system/role/listNotPage', { ...data })
}
culture/src/components/Table/index.vue
@@ -1,6 +1,6 @@
<template>
    <div class="table-container" style="width: 100%;">
        <el-table border v-bind="$attrs" v-on="$listeners" :height="height">
        <el-table ref="elTable"  border v-bind="$attrs" v-on="$listeners" :height="height">
            <slot></slot>
        </el-table>
        <div v-if="total > 0">
@@ -42,6 +42,14 @@
        }
    },
    methods: {
        toggleRowSelection(row, selected) {
            this.$refs.elTable.toggleRowSelection(row, selected)
            this.$forceUpdate()
        },
        clearSelection() {
            this.$refs.elTable.clearSelection()
            this.$forceUpdate()
        },
        handleCurrentChange(page) {
            this.$emit('handleCurrentChange', page)
        },
culture/src/layouts/components/HeaderNav.vue
@@ -17,7 +17,7 @@
      </div>
      <div class="user-info">
        <img src="@/assets/public/photo.png" />
        <div class="user-info-text">欢迎您,admin</div>
        <div class="user-info-text">欢迎您,{{ userInfo.nickName }}</div>
        <div class="user-info-line"></div>
        <div @click="outLogin" class="user-info-out">
          <img src="@/assets/public/logOut.png" />
culture/src/router/index.js
@@ -61,6 +61,24 @@
                    keepAlive: true,
                },
                component: () => import("../views/projectList/addProject"),
            },
            {
                path: "editProject",
                name: "EditProject",
                meta: {
                    title: "编辑菌种库项目组",
                    hide: true,
                },
                component: () => import("../views/projectList/editProject"),
            },
            {
                path: "detailProject",
                name: "DetailProject",
                meta: {
                    title: "菌种库项目组详情",
                    hide: true,
                },
                component: () => import("../views/projectList/detailProject"),
            }
        ]
    },
@@ -283,6 +301,15 @@
                        component: () => import("../views/strain-library/validation/primitive-cell/add.vue")
                    },
                    {
                        path: 'confirm-detail',
                        name: 'ConfirmDetail',
                        meta: {
                            title: '确认原始细胞库资料',
                            hide: true
                        },
                        component: () => import("../views/strain-library/validation/primitive-cell/confirm-detail.vue")
                    },
                    {
                        path: 'chief-cell',
                        name: 'ChiefCell',
                        meta: {
@@ -408,21 +435,21 @@
    // 登录验证
    // 排除登录页的校验
    // if (to.path === "/login") {
    //     if (sessionStorage.getItem('token')) {
    //         next('/projectList');  // 已登录状态访问登录页时重定向到系统首页
    //         return;
    //     }
    //     next();
    //     return;
    // }
    if (to.path === "/login") {
        if (sessionStorage.getItem('token')) {
            next('/projectList');  // 已登录状态访问登录页时重定向到系统首页
            return;
        }
        next();
        return;
    }
    // // 登录状态校验
    // const isAuthenticated = sessionStorage.getItem('token');
    // if (!isAuthenticated) {
    //     next('/login');  // 未登录用户重定向到登录页
    //     return;
    // }
    // 登录状态校验
    const isAuthenticated = sessionStorage.getItem('token');
    if (!isAuthenticated) {
        next('/login');  // 未登录用户重定向到登录页
        return;
    }
    // 判断是否拥有要跳转菜单权限
    let menus = store.state.menus
culture/src/views/deliveryAssessment/projectTeamIntegral/detail.vue
@@ -1,20 +1,23 @@
<template>
    <div>
    <div v-if="Object.keys(detailData).length">
        <div class="top-box-header">
            <div class="top-box-header-title">
                <div>项目组总积分表</div>
                <div class="top-box-header-time">
                    <div>评定开始时间:2024-02-09</div>
                    <div>评定结束始时间:2024-02-09</div>
                    <div>评定开始时间:{{ detailData.startTime }}</div>
                    <div>评定结束始时间:{{ detailData.endTime }}</div>
                </div>
            </div>
            <div class="top-box-integral">
                <div :style="{ backgroundColor: ['rgba(232, 250, 246, 1)', 'rgba(254, 237, 220, 1)', 'rgba(239, 248, 255, 1)', 'rgba(255, 237, 238, 1)'][item - 1] }"
                    v-for="item in 4" :key="item" class="top-box-integral-card">
                    <div class="top-box-integral-card-title">{{ ['项目组总积分', '化验师积分', '实验员积分', '实验终止次数'][item -
                    <div class="top-box-integral-card-title">{{ ['项目组总积分', '菌种工程师积分', '菌种实验员积分', '菌种实验失败次数'][item -
                        1] }}</div>
                    <div :style="{ color: ['rgba(4, 156, 154, 1)', 'rgba(255, 147, 0, 1)', 'rgba(23, 119, 213, 1)', 'rgba(255, 73, 85, 1)'][item - 1] }"
                        class="top-box-integral-card-num">99.9</div>
                        class="top-box-integral-card-num">{{
                            detailData[['teamIntegral', 'engineerIntegral', 'experimenterIntegral', 'failCount'][item - 1]]
                        }}
                    </div>
                </div>
            </div>
        </div>
@@ -27,8 +30,13 @@
                    </div>
                </div>
                <div class="integral-content-box-right">
                    <div v-show="actionsLeftTab != 1" @wheel.prevent="handleWheel" class="integral-content-box-right-nameTab">
                        <div @click="changeActiveName(item)" :class="activeNameTab == item && 'activeName'" class="integral-content-box-right-nameTab-name" v-for="item in 8" :key="item">张三</div>
                    <div v-show="actionsLeftTab != 1" @wheel.prevent="handleWheel"
                        class="integral-content-box-right-nameTab">
                        <div @click="changeActiveName(item.userName)"
                            :class="activeNameTab == item.userName && 'activeName'"
                            class="integral-content-box-right-nameTab-name"
                            v-for="{ item, index } in detailData.detailExperimentVOS" :key="index">{{ item.userName }}
                        </div>
                    </div>
                    <div class="integral-content-box-right-thead">
                        <div>评定项</div>
@@ -37,14 +45,31 @@
                        <div>结束时间</div>
                    </div>
                    <div class="integral-content-box-right-body">
                        <div v-for="item in itemList" :key="item" class="integral-content-box-right-body-item">
                        <div v-for="(item, index) in itemList" :key="index"
                            class="integral-content-box-right-body-item">
                            <div>{{ item.gainer }}</div>
                            <div>
                                <div v-if="item.situationOne">{{ item.situationOne }}</div>
                                <div v-if="item.situationTwo">{{ item.situationTwo }}</div>
                                <div>{{ item.situationOne }}{{
                                    actionsLeftTab === 2 ?
                                        (selectedExperimenter || {})[item.keys[0]] :
                                        detailData[item.keys[0]]
                                }}</div>
                                <div>{{ item.situationTwo }}{{
                                    actionsLeftTab === 2 ?
                                        (selectedExperimenter || {})[item.keys[1]] :
                                        detailData[item.keys[1]]
                                }}</div>
                            </div>
                            <div></div>
                            <div></div>
                            <div>{{
                                actionsLeftTab === 2 ?
                                    (selectedExperimenter || {})[item.keys[2]] :
                                    detailData[item.keys[2]]
                            }}</div>
                            <div>{{
                                actionsLeftTab === 2 ?
                                    (selectedExperimenter || {})[item.keys[3]] :
                                    detailData[item.keys[3]]
                            }}</div>
                        </div>
                    </div>
                </div>
@@ -54,27 +79,30 @@
</template>
<script>
import { getDetailData } from './service'
export default {
    data() {
        return {
            actionsLeftTab: 1,
            activeNameTab: 1,
            actionspPersonnel: null,
            activeNameTab: '',
            craftList: [
                {
                    gainer: '1、创新型课题',
                    situationOne: '课题数:',
                    situationTwo: '积分数:',
                    keys: ['innovateCount', 'innovateIntegral', 'innovateStartTime', 'innovateEndTime']
                },
                {
                    gainer: '2、规程型课题',
                    situationOne: '课题数:',
                    situationTwo: '积分数:',
                    keys: ['regulationCount', 'regulationIntegral', 'regulationStartTime', 'regulationEndTime']
                },
                {
                    gainer: '3、实验操作评定',
                    situationOne: '考核数:',
                    situationTwo: '积分数:',
                    keys: ['handleCount', 'handleIntegral', 'handleStartTime', 'handleEndTime']
                },
            ],//菌种工程师
            assayList: [
@@ -82,8 +110,11 @@
                    gainer: '1、实验操作评定',
                    situationOne: '考核数:',
                    situationTwo: '积分数:',
                    keys: ['handleCount', 'handleIntegral', 'handleStartTime', 'handleEndTime']
                },
            ],//菌种实验员
            detailData: {},
            selectedExperimenter: null
        }
    },
    computed: {
@@ -99,14 +130,29 @@
        }
    },
    created() {
        getDetailData(this.$route.query.id).then(res => {
            this.detailData = res;
            // 确保detailExperimentVOS存在且是数组
            this.detailData.detailExperimentVOS = this.detailData.detailExperimentVOS || [];
            if (this.detailData.detailExperimentVOS.length) {
                this.selectedExperimenter = this.detailData.detailExperimentVOS[0];
                this.activeNameTab = this.selectedExperimenter.userName;
            } else {
                // 设置空对象保护
                this.selectedExperimenter = {};
                this.activeNameTab = '';
            }
        })
    },
    methods: {
        changeActiveItem(item) {
            this.actionsLeftTab = item
        },
        changeActiveName(item) {
            this.activeNameTab = item
        changeActiveName(userName) {
            this.activeNameTab = userName;
            this.selectedExperimenter = this.detailData.detailExperimentVOS.find(
                item => item.userName === userName
            );
        },
        handleWheel(e) {
            if (this.scrollTimer) {
culture/src/views/deliveryAssessment/projectTeamIntegral/index.vue
@@ -5,34 +5,38 @@
            <template #search>
                <el-form :model="form" label-width="140px" inline>
                    <el-form-item label="项目组名称:">
                        <el-input v-model="form.name" placeholder="请输入"></el-input>
                        <el-input v-model="form.teamName" placeholder="请输入"></el-input>
                    </el-form-item>
                    <el-form-item label="创建日期:">
                        <el-date-picker v-model="value1" type="daterange" range-separator="至" start-placeholder="开始日期"
                            end-placeholder="结束日期">
                        <el-date-picker v-model="form.date" type="daterange" range-separator="至"
                            start-placeholder="开始日期" end-placeholder="结束日期">
                        </el-date-picker>
                    </el-form-item>
                    <el-form-item label="状态:">
                        <el-select placeholder="请选择"></el-select>
                        <el-select placeholder="请选择" v-model="form.status">
                            <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>重置</el-button>
                        <el-button type="primary">查询</el-button>
                        <el-button @click="reset">重置</el-button>
                        <el-button type="primary" @click="search">查询</el-button>
                    </el-form-item>
                </el-form>
            </template>
            <template #table>
                <el-table-column prop="name" label="项目组名称" />
                <el-table-column prop="age" label="项目组总积分" />
                <el-table-column prop="age" label="工艺工程师积分" />
                <el-table-column prop="age" label="化验师积分" />
                <el-table-column prop="age" label="实验员积分" />
                <el-table-column prop="age" label="评定开始时间" />
                <el-table-column prop="age" label="评定结束时间" />
                <el-table-column prop="age" label="状态" />
                <el-table-column prop="age" label="操作">
                <el-table-column prop="teamName" label="所属项目组" />
                <el-table-column prop="teamIntegral" label="菌种项目组总积分" />
                <el-table-column prop="engineerIntegral" label="菌种工程师积分" />
                <el-table-column prop="experimenterIntegral" label="菌种实验员积分" />
                <el-table-column prop="failCount" label="菌种实验员失败次数" />
                <el-table-column prop="startTime" label="评定开始时间" />
                <el-table-column prop="endTime" label="评定结束时间" />
                <el-table-column label="操作">
                    <template #default="{ row }">
                        <el-button @click="goDetail" type="text">详情</el-button>
                        <el-button @click="goDetail(row.projectId)" type="text">详情</el-button>
                    </template>
                </el-table-column>
            </template>
@@ -41,12 +45,12 @@
</template>
<script>
import { getListData } from './service'
export default {
    name: 'ProjectTeamIntegral',
    data() {
        return {
            form: {
            },
            form: {},
            tableData: [],
            queryForm: {
                pageSize: 10,
@@ -55,10 +59,13 @@
            total: 0
        }
    },
    created() {
        this.getList()
    },
    methods: {
        goDetail() {
        goDetail(id) {
            this.$router.push({
                path: '/projectList/addProject'
                path: `/deliveryAssessment/projectTeamIntegral-detail?id=${id}`,
            })
        },
        handleCurrentChange(page) {
@@ -70,7 +77,28 @@
            this.getList()
        },
        getList() {
            let obj = {
                ...this.queryForm
            }
            if (obj.date) {
                obj.startTime = moment(obj.date[0]).format('YYYY-MM-DD')
                obj.endTime = moment(obj.date[1]).format('YYYY-MM-DD')
                delete obj.date
            }
            getListData(obj).then(res => {
                this.tableData = res.data.records
                this.total = res.data.total
            })
        },
        reset() {
            this.queryForm = {
                pageSize: 10,
                pageNum: 1
            }
            this.getList()
        },
        search() {
            this.getList()
        }
    }
}
culture/src/views/deliveryAssessment/projectTeamIntegral/service.js
New file
@@ -0,0 +1,11 @@
import axios from '@/utils/request';
// 列表
export const getListData = (data) => {
    return axios.post('/api/t-strain-report/pageListProject', { ...data })
}
// 详情
export const getDetailData = (id) => {
    return axios.get(`/open/t-strain-report/getDetailByIdProject?id=${id}`)
}
culture/src/views/projectList/addProject.vue
@@ -2,11 +2,11 @@
    <Card>
        <template>
            <el-form ref="form" :model="form" :rules="rules" inline label-position="top">
                <el-form-item prop="name" label="项目组名称">
                    <el-input v-model="form.name" placeholder="请输入" />
                <el-form-item prop="teamName" label="项目组名称">
                    <el-input v-model="form.teamName" placeholder="请输入" />
                </el-form-item>
                <el-form-item prop="description" label="项目负责人">
                    <el-input v-model="form.description" placeholder="请输入" />
                <el-form-item prop="personCharge" label="项目负责人">
                    <el-input v-model="form.personCharge" placeholder="请输入" />
                </el-form-item>
            </el-form>
            <div class="header-title">
@@ -17,41 +17,73 @@
                <el-button class="el-icon-plus" type="primary" @click="addMember"> 添加项目组成员</el-button>
            </div>
            <div class="member-list">
                <div v-for="(item,index) in ['审批人', '菌种工程师', '菌种实验员']" :key="item" class="member-list-card">
                <div v-for="item in 3" :key="item" class="member-list-card">
                    <div class="member-item">
                        <div class="member-title">{{ item }}</div>
                        <div :class="index == 0 || index == 1 ? 'member-name-box' : 'member-name-box-2'">
                            <div v-for="i in memberList(index+1)" :key="i" class="member-name">张三</div>
                        <div class="member-title">{{ ['菌种审批人', '菌种工程师', '菌种实验员'][item - 1] }}</div>
                        <div :class="item == 1 || item == 2 ? 'member-name-box' : 'member-name-box-2'">
                            <el-tooltip v-for="i in memberList(item)" :key="i.userId" class="member-name" effect="dark"
                                :content="i.nickName" placement="top">
                                <span>{{ i.nickName }}</span>
                            </el-tooltip>
                        </div>
                        <div class="member-edit">修改</div>
                        <div class="member-edit" v-if="memberList(item).length != 0" @click="editUserList">修改</div>
                    </div>
                </div>
            </div>
            <div class="add-project-footer">
                <el-button type="primary">保存</el-button>
                <el-button @click="submitForm" type="primary">保存</el-button>
            </div>
        </template>
        <SelectMember ref="selectMember" />
        <SelectMember ref="selectMember" @submit="selectUser" />
    </Card>
</template>
<script>
import { addProject } from './service'
export default {
    name: 'AddProject',
    data() {
        return {
            form: {},
            rules: {
                name: [{ required: true, message: '请输入项目组名称', trigger: 'blur' }],
                description: [{ required: true, message: '请输入项目组描述', trigger: 'blur' }]
            }
                teamName: [{ required: true, message: '请输入项目组名称', trigger: 'blur' }],
                personCharge: [{ required: true, message: '请输入项目组描述', trigger: 'blur' }]
            },
            selectMemberData: [],
            // 角色配置常量
            ROLE_CONFIG: {
                1: { key: 'approver', limit: 1, label: '菌种审批人' },
                2: { key: 'engineer', limit: 1, label: '菌种工程师' },
            },
        }
    },
    methods: {
        submitForm() {
            this.$refs.form.validate((valid) => {
                if (valid) {
                    console.log('submit!')
                    if (this.selectMemberData.length == 0) {
                        this.$message.error('请选择项目组成员')
                        return
                    }
                    const ROLE_NAME_TO_TYPE = {
                        '菌种审批人': 1,
                        '菌种工程师': 2,
                        '菌种实验员': 3,
                    };
                    const data = {
                        teamName: this.form.teamName,
                        personCharge: this.form.personCharge,
                        staffs: this.selectMemberData.map(member => ({
                            userId: member.userId,
                            roleType: ROLE_NAME_TO_TYPE[member.roleName]
                        }))
                    }
                    addProject(data).then(res => {
                        if (res.code == 200) {
                            this.$message.success('添加成功')
                            this.$router.push({ name: 'ProjectList' })
                        }
                    })
                }
            })
        },
@@ -61,16 +93,31 @@
        memberList(i) {
            switch (i) {
                case 1:
                    return [1]
                    return this.selectMemberData.filter(item => item.roleName == '菌种审批人')
                case 2:
                    return [1]
                    return this.selectMemberData.filter(item => item.roleName == '菌种工程师')
                case 3:
                    return [1, 2, 3, 4, 5, 6, 7, 8]
                case 4:
                    return [1, 2, 3, 4, 5, 6, 7, 8]
                    return this.selectMemberData.filter(item => item.roleName == '菌种实验员')
                default:
                    break;
            }
        },
        selectUser(data) {
            for (const [roleId, config] of Object.entries(this.ROLE_CONFIG)) {
                const members = data.filter(item => item.roleName === config.label);
                if (members.length > config.limit) {
                    this.$message.error(`${config.label}最多只能选择${config.limit}个`);
                    return
                }
            }
            this.selectMemberData = data;
            this.$refs.selectMember.close();
        },
        editUserList() {
            this.$refs.selectMember.open();
            this.$nextTick(() => {
                this.$refs.selectMember.setSelection(this.selectMemberData);
            });
        }
    }
}
@@ -188,6 +235,10 @@
                font-weight: 500;
                font-size: 16px;
                color: #FFFFFF;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                box-sizing: border-box;
            }
            .member-edit {
culture/src/views/projectList/detailProject.vue
New file
@@ -0,0 +1,233 @@
<template>
    <Card>
        <template>
            <el-form disabled ref="form" :model="form" :rules="rules" inline label-position="top">
                <el-form-item prop="teamName" label="项目组名称">
                    <el-input v-model="form.teamName" placeholder="请输入" />
                </el-form-item>
                <el-form-item prop="personCharge" label="项目组负责人">
                    <el-input v-model="form.personCharge" placeholder="请输入" />
                </el-form-item>
            </el-form>
            <div class="header-title">
                <div class="header-title-left">
                    <img src="@/assets/public/headercard.png" />
                    <div>项目组成员</div>
                </div>
            </div>
            <div class="member-list">
                <div v-for="item in 3" :key="item" class="member-list-card">
                    <div class="member-item">
                        <div class="member-title">{{ ['菌种审批人', '菌种工程师', '菌种实验员'][item - 1] }}</div>
                        <div :class="item == 1 || item == 2 ? 'member-name-box' : 'member-name-box-2'">
                            <el-tooltip v-for="i in memberList(item)" :key="i.userId" class="member-name" effect="dark"
                                :content="i.nickName" placement="top">
                                <span>{{ i.nickName }}</span>
                            </el-tooltip>
                        </div>
                    </div>
                </div>
            </div>
        </template>
        <SelectMember ref="selectMember" @submit="selectUser" />
    </Card>
</template>
<script>
import { getProjectDetail } from './service'
export default {
    name: 'EddProject',
    data() {
        return {
            form: {},
            rules: {
                teamName: [{ required: true, message: '请输入项目组名称', trigger: 'blur' }],
                personCharge: [{ required: true, message: '请输入项目组描述', trigger: 'blur' }]
            },
            selectMemberData: [],
            ROLE_NAME_TO_TYPE: {
                '菌种审批人': 1,
                '菌种工程师': 2,
                '菌种实验员': 3,
            }
        }
    },
    created() {
        getProjectDetail({ id: this.$route.query.id }).then(res => {
            this.form = {
                teamName: res.teamName,
                personCharge: res.personCharge
            }
            this.selectMemberData = res.staffs.map(item => ({
                userId: Number(item.userId),
                roleName: ['菌种审批人', '菌种工程师', '菌种实验员'][item.roleType - 1],
                nickName: item.nickName
            }))
        })
    },
    methods: {
        addMember() {
            this.$refs.selectMember.open()
        },
        memberList(i) {
            switch (i) {
                case 1:
                    return this.selectMemberData.filter(item => item.roleName == '菌种审批人')
                case 2:
                    return this.selectMemberData.filter(item => item.roleName == '菌种工程师')
                case 3:
                    return this.selectMemberData.filter(item => item.roleName == '菌种实验员')
                default:
                    break;
            }
        },
    }
}
</script>
<style scoped lang="less">
.el-form--inline .el-form-item {
    margin-right: 83px;
}
.header-title {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 13px;
    .header-title-left {
        display: flex;
        align-items: center;
        gap: 13px;
        img {
            width: 12px;
            height: 19px;
        }
        div {
            flex-shrink: 0;
            font-weight: bold;
            font-size: 18px;
            color: #222222;
            line-height: 27px;
            font-family: 'Source Han Sans CN Bold Bold';
            &:before {
                content: '*';
                color: #F56C6C;
                margin-right: 4px;
            }
        }
    }
}
.member-list {
    margin-top: 18px;
    display: flex;
    flex-wrap: wrap;
    gap: 28px;
    .member-list-card {
        position: relative;
        width: 340px;
        height: 400px;
        border-radius: 8px;
        border: 1px solid #DCDFE6;
        &:nth-child(1) {
            background: linear-gradient(to bottom, rgba(4, 156, 154, 0.2) 0%, rgba(5, 242, 194, 0) 70%);
        }
        &:nth-child(2) {
            background: linear-gradient(to bottom, rgba(5, 160, 193, 0.2) 0%, rgba(5, 242, 194, 0) 70%);
        }
        &:nth-child(3) {
            background: linear-gradient(to bottom, rgba(255, 77, 79, 0.20) 0%, rgba(255, 242, 194, 0) 70%);
        }
        &:nth-child(4) {
            background: linear-gradient(to bottom, rgba(250, 199, 20, 0.21) 0%, rgba(255, 242, 194, 0) 70%);
        }
        .member-item {
            height: 100%;
            display: flex;
            flex-direction: column;
            .member-title {
                margin-top: 20px;
                width: 100%;
                font-family: 'Source Han Sans CN Bold Bold';
                font-weight: bold;
                font-size: 16px;
                color: rgba(0, 0, 0, 0.8);
                line-height: 16px;
                text-align: center;
            }
            .member-name-box {
                flex: 1;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            .member-name-box-2 {
                padding: 0 20px;
                padding-top: 40px;
                display: grid;
                grid-template-columns: repeat(4, 1fr);
                align-items: flex-start;
                flex-wrap: wrap;
                gap: 20px;
                justify-content: center;
            }
            .member-name {
                width: 60px;
                height: 60px;
                background: #7D8B79;
                border-radius: 50%;
                text-align: center;
                line-height: 60px;
                font-weight: 500;
                font-size: 16px;
                color: #FFFFFF;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                box-sizing: border-box;
            }
            .member-edit {
                cursor: pointer;
                position: absolute;
                bottom: 10px;
                left: 50%;
                transform: translateX(-50%);
                font-weight: 400;
                font-size: 12px;
                color: #FF4D4F;
                line-height: 22px;
                width: 40px;
                background: #FFF1F0;
                border-radius: 4px;
                border: 1px solid #FFCCC7;
                text-align: center;
            }
        }
    }
}
.add-project-footer {
    margin-top: 43px;
    button {
        width: 220px;
    }
}
</style>
culture/src/views/projectList/editProject.vue
New file
@@ -0,0 +1,285 @@
<template>
    <Card>
        <template>
            <el-form ref="form" :model="form" :rules="rules" inline label-position="top">
                <el-form-item prop="teamName" label="项目组名称">
                    <el-input v-model="form.teamName" placeholder="请输入" />
                </el-form-item>
                <el-form-item prop="personCharge" label="项目组负责人">
                    <el-input v-model="form.personCharge" placeholder="请输入" />
                </el-form-item>
            </el-form>
            <div class="header-title">
                <div class="header-title-left">
                    <img src="@/assets/public/headercard.png" />
                    <div>项目组成员</div>
                </div>
                <el-button class="el-icon-plus" type="primary" @click="addMember"> 添加项目组成员</el-button>
            </div>
            <div class="member-list">
                <div v-for="item in 3" :key="item" class="member-list-card">
                    <div class="member-item">
                        <div class="member-title">{{ ['菌种审批人', '菌种工程师', '菌种实验员'][item - 1] }}</div>
                        <div :class="item == 1 || item == 2 ? 'member-name-box' : 'member-name-box-2'">
                            <el-tooltip v-for="i in memberList(item)" :key="i.userId" class="member-name" effect="dark"
                                :content="i.nickName" placement="top">
                                <span>{{ i.nickName }}</span>
                            </el-tooltip>
                        </div>
                        <div class="member-edit" v-if="memberList(item).length != 0" @click="editUserList">修改</div>
                    </div>
                </div>
            </div>
            <div class="add-project-footer">
                <el-button @click="submitForm" type="primary">保存</el-button>
            </div>
        </template>
        <SelectMember ref="selectMember" @submit="selectUser" />
    </Card>
</template>
<script>
import { getProjectDetail, editProject } from './service'
export default {
    name: 'EddProject',
    data() {
        return {
            form: {},
            rules: {
                teamName: [{ required: true, message: '请输入项目组名称', trigger: 'blur' }],
                personCharge: [{ required: true, message: '请输入项目组描述', trigger: 'blur' }]
            },
            selectMemberData: [],
            // 角色配置常量
            ROLE_CONFIG: {
                1: { key: 'approver', limit: 1, label: '菌种审批人' },
                2: { key: 'engineer', limit: 1, label: '菌种工程师' },
            },
            ROLE_NAME_TO_TYPE: {
                '菌种审批人': 1,
                '菌种工程师': 2,
                '菌种实验员': 3,
            }
        }
    },
    created() {
        getProjectDetail({ id: this.$route.query.id }).then(res => {
            this.form = {
                teamName: res.teamName,
                personCharge: res.personCharge
            }
            this.selectMemberData = res.staffs.map(item => ({
                userId: Number(item.userId),
                roleName: ['菌种审批人', '菌种工程师', '菌种实验员'][item.roleType - 1],
                nickName: item.nickName
            }))
        })
    },
    methods: {
        submitForm() {
            this.$refs.form.validate((valid) => {
                if (valid) {
                    if (this.selectMemberData.length == 0) {
                        this.$message.error('请选择项目组成员')
                        return
                    }
                    const data = {
                        id: this.$route.query.id,
                        teamName: this.form.teamName,
                        personCharge: this.form.personCharge,
                        staffs: this.selectMemberData.map(member => ({
                            userId: member.userId,
                            roleType: this.ROLE_NAME_TO_TYPE[member.roleName]
                        }))
                    }
                    editProject(data).then(res => {
                        if (res.code == 200) {
                            this.$message.success('添加成功')
                            this.$router.push({ name: 'ProjectList' })
                        }
                    })
                }
            })
        },
        addMember() {
            this.$refs.selectMember.open()
        },
        memberList(i) {
            switch (i) {
                case 1:
                    return this.selectMemberData.filter(item => item.roleName == '菌种审批人')
                case 2:
                    return this.selectMemberData.filter(item => item.roleName == '菌种工程师')
                case 3:
                    return this.selectMemberData.filter(item => item.roleName == '菌种实验员')
                default:
                    break;
            }
        },
        selectUser(data) {
            for (const [roleId, config] of Object.entries(this.ROLE_CONFIG)) {
                const members = data.filter(item => item.roleName === config.label);
                if (members.length > config.limit) {
                    this.$message.error(`${config.label}最多只能选择${config.limit}个`);
                    return
                }
            }
            this.selectMemberData = data;
            this.$refs.selectMember.close();
        },
        editUserList() {
            this.$refs.selectMember.open();
            this.$nextTick(() => {
                this.$refs.selectMember.setSelection(this.selectMemberData);
            });
        }
    }
}
</script>
<style scoped lang="less">
.el-form--inline .el-form-item {
    margin-right: 83px;
}
.header-title {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 13px;
    .header-title-left {
        display: flex;
        align-items: center;
        gap: 13px;
        img {
            width: 12px;
            height: 19px;
        }
        div {
            flex-shrink: 0;
            font-weight: bold;
            font-size: 18px;
            color: #222222;
            line-height: 27px;
            font-family: 'Source Han Sans CN Bold Bold';
            &:before {
                content: '*';
                color: #F56C6C;
                margin-right: 4px;
            }
        }
    }
}
.member-list {
    margin-top: 18px;
    display: flex;
    flex-wrap: wrap;
    gap: 28px;
    .member-list-card {
        position: relative;
        width: 340px;
        height: 400px;
        border-radius: 8px;
        border: 1px solid #DCDFE6;
        &:nth-child(1) {
            background: linear-gradient(to bottom, rgba(4, 156, 154, 0.2) 0%, rgba(5, 242, 194, 0) 70%);
        }
        &:nth-child(2) {
            background: linear-gradient(to bottom, rgba(5, 160, 193, 0.2) 0%, rgba(5, 242, 194, 0) 70%);
        }
        &:nth-child(3) {
            background: linear-gradient(to bottom, rgba(255, 77, 79, 0.20) 0%, rgba(255, 242, 194, 0) 70%);
        }
        &:nth-child(4) {
            background: linear-gradient(to bottom, rgba(250, 199, 20, 0.21) 0%, rgba(255, 242, 194, 0) 70%);
        }
        .member-item {
            height: 100%;
            display: flex;
            flex-direction: column;
            .member-title {
                margin-top: 20px;
                width: 100%;
                font-family: 'Source Han Sans CN Bold Bold';
                font-weight: bold;
                font-size: 16px;
                color: rgba(0, 0, 0, 0.8);
                line-height: 16px;
                text-align: center;
            }
            .member-name-box {
                flex: 1;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            .member-name-box-2 {
                padding: 0 20px;
                padding-top: 40px;
                display: grid;
                grid-template-columns: repeat(4, 1fr);
                align-items: flex-start;
                flex-wrap: wrap;
                gap: 20px;
                justify-content: center;
            }
            .member-name {
                width: 60px;
                height: 60px;
                background: #7D8B79;
                border-radius: 50%;
                text-align: center;
                line-height: 60px;
                font-weight: 500;
                font-size: 16px;
                color: #FFFFFF;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                box-sizing: border-box;
            }
            .member-edit {
                cursor: pointer;
                position: absolute;
                bottom: 10px;
                left: 50%;
                transform: translateX(-50%);
                font-weight: 400;
                font-size: 12px;
                color: #FF4D4F;
                line-height: 22px;
                width: 40px;
                background: #FFF1F0;
                border-radius: 4px;
                border: 1px solid #FFCCC7;
                text-align: center;
            }
        }
    }
}
.add-project-footer {
    margin-top: 43px;
    button {
        width: 220px;
    }
}
</style>
culture/src/views/projectList/service.js
@@ -2,30 +2,30 @@
// 列表
export const getProjectList = (data) => {
    return axios.post('/t_project_team/api/pageList', { ...data })
    return axios.post('/api/t_project_team/pageList', { ...data })
}
// 新增
export const addProject = (data) => {
    return axios.post('/t_project_team/api/add', { ...data })
    return axios.post('/api/t_project_team/add', { ...data })
}
// 编辑
export const editProject = (data) => {
    return axios.post('/t_project_team/api/update', { ...data })
    return axios.post('/api/t_project_team/update', { ...data })
}
// 详情
export const getProjectDetail = (data) => {
    return axios.get(`/t_project_team/open/getDetailById?id=${data.id}`)
    return axios.get(`/open/t_project_team/getDetailById?id=${data.id}`)
}
// 修改项目组状态
export const changeStatus = (data) => {
    return axios.post('/t_project_team/api/upAndDown', { ...data })
    return axios.post('/api/t_project_team/upAndDown', { ...data })
}
// 删除项目组
export const deleteProject = (data) => {
    return axios.delete(`/t_project_team/open/deleteById?id=${data.id}`)
    return axios.delete(`/open/t_project_team/deleteById?id=${data.id}`)
}
culture/src/views/strain-library/breeding-record/SlantRecordDialog.vue
New file
@@ -0,0 +1,196 @@
<template>
 <el-dialog :visible.sync="visible" title="新增培养皿观察记录" width="80%" @close="handleClose">
    <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" label-position="top">
      <el-row :gutter="24">
        <el-col :span="12">
          <el-form-item label="分离菌落编号" prop="colonyNo" required>
            <el-input v-model="form.colonyNo" placeholder="请输入分离菌落编号" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="形状强壮度排名" prop="rank" required>
            <el-input v-model="form.rank" placeholder="请输入形状强壮度排名" />
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <el-table :data="tableData" border style="width: 100%; margin-bottom: 16px;">
      <el-table-column prop="index" label="记录次数" width="90">
        <template #default="{ row }">
          第{{ row.index }}次
        </template>
      </el-table-column>
      <el-table-column prop="desc" label="形态记录">
        <template #default="{ row }">
          <el-input v-model="row.desc" placeholder="请输入形态记录" style="width: 100%;" />
        </template>
      </el-table-column>
      <el-table-column prop="images" label="拍照上传" width="120">
        <template #default="{ row }">
          <el-upload
            :file-list="row.images"
            list-type="picture-card"
            :on-preview="file => handlePreview(row, file)"
            :on-remove="(file, fileList) => handleRemove(row, file, fileList)"
            :on-success="(res, file, fileList) => handleUpload(row, file, fileList)"
            :before-upload="beforeUpload"
            action="#"
            :limit="5"
            class="mini-upload"
          >
            <i class="el-icon-plus"></i>
          </el-upload>
        </template>
      </el-table-column>
      <el-table-column prop="time" label="记录时间" width="160">
        <template #default="{ row }">
          {{ row.time }}
        </template>
      </el-table-column>
    </el-table>
    <div style="text-align: center;">
      <el-button type="primary" @click="handleOk">保存</el-button>
    </div>
    <el-dialog :visible.sync="previewVisible" width="400px">
      <img :src="previewImg" alt="图片预览" style="width: 100%;" />
    </el-dialog>
  </el-dialog>
</template>
<script>
export default {
  name: 'SlantRecordDialog',
  props: {
    visible: Boolean,
    value: {
      type: Object,
      default: () => ({ colonyNo: '', rank: '', records: [] })
    }
  },
  data() {
    return {
      form: {
        colonyNo: '',
        rank: ''
      },
      rules: {
        colonyNo: [{ required: true, message: '请输入分离菌落编号', trigger: 'blur' }],
        rank: [{ required: true, message: '请输入形状强壮度排名', trigger: 'blur' }]
      },
      tableData: [],
      previewVisible: false,
      previewImg: ''
    }
  },
  watch: {
    value: {
      immediate: true,
      handler(val) {
        this.form.colonyNo = val.colonyNo || ''
        this.form.rank = val.rank || ''
        this.tableData = (val.records && val.records.length === 10)
          ? val.records.map((item, i) => ({ ...item, index: i + 1 }))
          : Array.from({ length: 10 }, (_, i) => ({ index: i + 1, desc: '', images: [], time: this.getNowTime() }))
      }
    },
    visible(val) {
      if (!val) {
        this.reset()
      }
    }
  },
  methods: {
    getNowTime() {
      const d = new Date()
      return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()} ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`
    },
    reset() {
      this.form.colonyNo = ''
      this.form.rank = ''
      this.tableData = Array.from({ length: 10 }, (_, i) => ({ index: i + 1, desc: '', images: [], time: this.getNowTime() }))
    },
    handleOk() {
      this.$refs.formRef.validate(valid => {
        if (!valid) return
        // 校验每行形态记录必填
        for (let i = 0; i < this.tableData.length; i++) {
          if (!this.tableData[i].desc) {
            this.$message.error(`第${i + 1}次形态记录不能为空`)
            return
          }
        }
        this.$emit('ok', {
          colonyNo: this.form.colonyNo,
          rank: this.form.rank,
          records: this.tableData
        })
        this.handleClose()
      })
    },
    handleClose() {
      this.$emit('update:visible', false)
    },
    beforeUpload(file) {
      // 这里只做本地预览
      return new Promise(resolve => {
        const reader = new FileReader()
        reader.onload = e => {
          resolve()
        }
        reader.readAsDataURL(file)
      })
    },
    handleUpload(row, file, fileList) {
      // 这里只做本地预览
      row.images = fileList.map(f => ({ ...f, url: f.url || URL.createObjectURL(f.raw) }))
    },
    handleRemove(row, file, fileList) {
      row.images = fileList
    },
    handlePreview(row, file) {
      this.previewImg = file.url
      this.previewVisible = true
    }
  }
}
</script>
<style scoped lang="less">
::v-deep(.el-upload--picture-card) {
  width: 40px !important;
  height: 40px !important;
  line-height: 40px !important;
}
::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item) {
  width: 40px !important;
  height: 40px !important;
}
::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item-thumbnail) {
  width: 40px !important;
  height: 40px !important;
  object-fit: cover;
}
::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item-preview),
::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item-delete) {
  width: 18px;
  height: 18px;
  font-size: 14px;
}
::v-deep(.el-upload--picture-card) {
  width: 40px !important;
  height: 40px !important;
  line-height: 40px !important;
  display: flex;
  align-items: center;
  justify-content: center;
}
::v-deep(.mini-upload .el-upload--picture-card i.el-icon-plus) {
  font-size: 18px;      /* 缩小icon */
  color: #999;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}
</style>
culture/src/views/strain-library/breeding-record/add.vue
@@ -88,7 +88,7 @@
            <div>一、培养基分离记录</div>
          </div>
          <div class="header-title-right">
            <el-button @click="showChoose = true" class="el-icon-circle-plus-outline" type="primary">
            <el-button @click="showSeparationDialog = true" class="el-icon-circle-plus-outline" type="primary">
              新增培养皿分离记录</el-button>
          </div>
        </div>
@@ -112,7 +112,7 @@
            <div>二、培养皿生物学形态观察记录</div>
          </div>
          <div class="header-title-right">
            <el-button @click="showChoose = true" class="el-icon-circle-plus-outline" type="primary">
            <el-button @click="showObservationDialog = true" class="el-icon-circle-plus-outline" type="primary">
              新增观察记录</el-button>
          </div>
        </div>
@@ -134,7 +134,7 @@
            <div>三、接种斜面记录</div>
          </div>
          <div class="header-title-right">
            <el-button @click="showChoose = true" class="el-icon-circle-plus-outline" type="primary">
            <el-button @click="showInoculationDialog = true" class="el-icon-circle-plus-outline" type="primary">
              新增斜面记录</el-button>
          </div>
        </div>
@@ -163,7 +163,7 @@
            <div>四、菌种保藏记录</div>
          </div>
          <div class="header-title-right">
            <el-button @click="showChoose = true" class="el-icon-circle-plus-outline" type="primary">
            <el-button @click="showPreserveDialog = true" class="el-icon-circle-plus-outline" type="primary">
              新增菌种保藏记录</el-button>
          </div>
        </div>
@@ -182,6 +182,23 @@
            </el-table-column>
          </template>
        </Table>
        <!-- 弹窗组件 -->
        <SeparationRecordDialog
          :visible.sync="showSeparationDialog"
          @confirm="handleSeparationConfirm"
        />
        <SlantRecordDialog
          :visible.sync="showObservationDialog"
          @ok="handleObservationConfirm"
        />
        <InoculationSlopeRecordDialog
          :visible.sync="showInoculationDialog"
          @save="handleInoculationConfirm"
        />
        <PreserveStrainRecordDialog
          :visible.sync="showPreserveDialog"
          @save="handlePreserveConfirm"
        />
        <div class="end-btn">
          <el-button type="primary">发送</el-button>
          <el-button type="default">存草稿</el-button>
@@ -192,8 +209,19 @@
</template>
<script>
import AiEditor from "@/components/AiEditor";
import SeparationRecordDialog from "./separation-record-dialog.vue";
import SlantRecordDialog from "./SlantRecordDialog.vue";
import InoculationSlopeRecordDialog from "./inoculation-slope-record-dialog.vue";
import PreserveStrainRecordDialog from "./preserve-strain-record-dialog.vue";
export default {
  components: { AiEditor },
  components: {
    AiEditor,
    SeparationRecordDialog,
    SlantRecordDialog,
    InoculationSlopeRecordDialog,
    PreserveStrainRecordDialog,
  },
  name: "AddBreedingRecord",
  data() {
    return {
@@ -229,8 +257,26 @@
      status: "1",
      remark: "",
      queryForm: {},
      showSeparationDialog: false,
      showObservationDialog: false,
      showInoculationDialog: false,
      showPreserveDialog: false,
    };
  },
  methods: {
    handleSeparationConfirm(data) {
      console.log("培养皿分离记录确认", data);
    },
    handleObservationConfirm(data) {
      console.log("培养皿观察记录确认", data);
    },
    handleInoculationConfirm(data) {
      console.log("接种斜面记录确认", data);
    },
    handlePreserveConfirm(data) {
      console.log("菌种保藏记录确认", data);
    },
  },
};
</script>
culture/src/views/strain-library/breeding-record/separation-record-dialog.vue
@@ -1,6 +1,6 @@
<template>
    <el-dialog
      title="添加出入库记录"
      title="新增培养皿分离记录"
      :visible.sync="visible"
      width="520px"
      :close-on-click-modal="false"
culture/src/views/strain-library/validation/chief-cell/index.vue
@@ -1,54 +1,27 @@
<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),经过百种、验证后,菌种被保存日油管理沙土菌种,入细胞细胞库(现代-O)。</p>
                <p>1.3 是否菌种能自己分离后获得的源头菌种,接种入主细胞库:经过产验证后,保藏为少土管理日油管,入细胞细胞库(现代-O)。</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),经过百种、验证后,菌种被保存日油管理沙土菌种,入细胞细胞库(现代-O)。</p>
                    <p>1.3 是否菌种能自己分离后获得的源头菌种,接种入主细胞库:经过产验证后,保藏为少土管理日油管,入细胞细胞库(现代-O)。</p>
                </div>
            </el-dialog>
        </el-card>
        <!-- Table -->
        <TableCustom :queryForm="queryForm" :tableData="tableData" :total="total" @currentChange="handleCurrentChange"
            @sizeChange="handleSizeChange">
            <template #search>
                <el-form :model="form" label-width="auto" inline>
                    <el-form-item label="菌种编号:">
                    <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 label="验证实验编号:">
                        <el-input v-model="form.strainName" 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 label="菌种名称:">
                        <el-input v-model="form.strainName" placeholder="请输入"></el-input>
                    </el-form-item>
                    <el-form-item 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-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>
@@ -61,29 +34,25 @@
                    <div class="flex a-center">
                        <div class="title" :class="{ active: currentType === 'list' }"
                            @click="handleTypeChange('list')">
                            原始细胞列表</div>
                            主细胞库资料列表</div>
                        <div class="drafts" :class="{ active: currentType === 'draft' }"
                            @click="handleTypeChange('draft')">
                            草稿箱</div>
                    </div>
                    <div class="flex a-center">
                        <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>
                        <el-button @click="handleNewStrain" class="el-icon-plus" type="primary" style="margin-right: 12px;">新增主细胞</el-button>
                    </div>
                </div>
            </template>
            <template #table>
                <el-table-column prop="strainNo" label="菌种编号" />
                <el-table-column prop="strainName" label="菌种名称" />
                <el-table-column prop="source" label="菌种来源" />
                <el-table-column prop="method" label="鉴定方法" />
                <el-table-column prop="certificate" label="特征描述" />
                <el-table-column prop="storage" label="菌种保存方法" />
                <el-table-column prop="amount" label="保存位置" />
                <el-table-column prop="inventory" label="库存余量" />
                <el-table-column prop="notes" label="备注" />
                <el-table-column prop="status" label="当前状态">
                <el-table-column prop="strainNo" label="菌种来源" />
                <el-table-column prop="strainNo" label="鉴别菌株编号" />
                <el-table-column prop="strainName" label="鉴别菌株名称" />
                <el-table-column prop="source" label="验证实验编号" />
                <el-table-column prop="method" label="创建人" />
                <el-table-column prop="certificate" label="创建时间" />
                <el-table-column prop="status" label="状态">
                    <template #default="{ row }">
                        <el-tag :type="getStatusType(row.status)">{{ getStatusText(row.status) }}</el-tag>
                    </template>
@@ -92,12 +61,13 @@
                    <template #default="{ row }">
                        <el-button type="text" @click="handleDetail(row)">详情</el-button>
                        <el-button type="text" @click="handleEdit(row)">编辑</el-button>
                        <el-button type="text" @click="handleRecord(row)">出入库记录</el-button>
                        <el-button type="text" @click="handleRecord(row)">删除</el-button>
                        <el-button type="text" @click="handleRecord(row)">确认</el-button>
                    </template>
                </el-table-column>
            </template>
        </TableCustom>
    </div>
</template>
culture/src/views/strain-library/validation/primitive-cell/DetailConditionDialog.vue
New file
@@ -0,0 +1,282 @@
<template>
  <el-dialog :visible.sync="visible" width="80%" @close="handleClose">
    <el-card class="top-card">
      <el-row :gutter="24" class="top-info-row">
        <el-col :span="8" class="info-col">
          <div class="info-item">
            <span class="label">菌种来源:</span>{{ detail.source }}
          </div>
          <div class="info-item">
            <span class="label">鉴别菌株编号:</span>{{ detail.strainNo }}
          </div>
          <div class="info-item">
            <span class="label">鉴别菌株名称:</span>{{ detail.strainName }}
          </div>
        </el-col>
        <el-col :span="8" class="info-col">
          <div class="info-item">
            <span class="label">验证实验编号:</span>{{ detail.verifyNo }}
          </div>
          <div class="info-item">
            <span class="label">实验时间:</span>{{ detail.experimentTime }}
          </div>
          <div class="info-item"></div>
        </el-col>
        <el-col :span="8" class="info-col">
          <div class="info-item sign-label">
            <span class="label">菌种实验员签字</span>
          </div>
          <div class="info-item signature-item">
            <div class="signature-area">
              <img v-if="detail.signature" :src="detail.signature" alt="签字" />
              <span v-else class="waiting-text">暂无签名</span>
            </div>
          </div>
        </el-col>
      </el-row>
    </el-card>
    <div class="section-card" style="margin-top: 24px">
      <el-form label-width="100px" label-position="top">
        <el-form-item label="实验结论">
          <el-input
            type="textarea"
            v-model="detail.conclusion"
            :rows="3"
            placeholder="请输入"
          />
        </el-form-item>
        <el-form-item label="批准菌株用途">
          <el-checkbox-group v-model="detail.usage">
            <el-checkbox label="传代" />
            <el-checkbox label="菌种保藏" />
            <el-checkbox label="废弃" />
          </el-checkbox-group>
        </el-form-item>
      </el-form>
    </div>
    <EditConditionDialog
      :visible.sync="dialogVisible"
      :isEdit="dialogIsEdit"
      :isFixed="dialogIsFixed"
      :value="dialogValue"
      @ok="handleDialogOk"
    />
    <div class="section-card" style="margin-top: 12px">
      <el-table
        :data="tableData"
        border
        style="width: 100%"
        :row-class-name="getRowClassName"
      >
        <el-table-column prop="condition" label="菌种培养工艺条件" />
        <el-table-column prop="record" label="菌种培养工艺实况记录" />
        <el-table-column prop="process" label="菌种培养标准工艺" />
        <el-table-column label="操作" width="120">
          <template #default="{ row }">
            <el-button type="text" @click="handleEdit(row)">详情</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </el-dialog>
</template>
<script>
import EditConditionDialog from "./EditConditionDialog.vue";
import DetailConditionDialog from "./DetailConditionDialog.vue";
export default {
  name: "DetailConditionDialog",
  components: { EditConditionDialog, DetailConditionDialog },
  props: {
    visible: Boolean,
    value: {
      type: Object,
      default: () => ({ condition: "", record: "", process: "" }),
    },
  },
  data() {
    return {
      detail: {
        source: "内容的内容内容内容",
        strainNo: "3411234",
        strainName: "名称名称名称",
        verifyNo: "34133214",
        experimentTime: "2025-1-23 11:10:28",
        signature: "", // 签名图片url
        conclusion: "",
        usage: [],
      },
      activeTab: "condition",
      initialTableData: [
        {
          condition: "平板培养基",
          record: "文字内容文字内容文字内容文字内容文字内容文字内容",
          process: "文字内容文字内容文字内容文字内容文字内容文字内容",
        },
        { condition: "培养温度", record: "", process: "" },
        { condition: "培养时间", record: "", process: "" },
        { condition: "摇瓶培养基", record: "", process: "" },
        { condition: "接种量", record: "", process: "" },
        { condition: "培养时间", record: "", process: "" },
        { condition: "发酵时间", record: "", process: "" },
        { condition: "检测数据及结果", record: "", process: "" },
      ],
      tableData: [
        {
          condition: "平板培养基",
          record: "文字内容文字内容文字内容文字内容文字内容文字内容",
          process: "文字内容文字内容文字内容文字内容文字内容文字内容",
        },
        { condition: "培养温度", record: "", process: "" },
        { condition: "培养时间", record: "", process: "" },
        { condition: "摇瓶培养基", record: "", process: "" },
        { condition: "接种量", record: "", process: "" },
        { condition: "培养时间", record: "", process: "" },
        { condition: "发酵时间", record: "", process: "" },
        { condition: "检测数据及结果", record: "", process: "" },
      ],
      dialogVisible: false,
      dialogIsEdit: false,
      dialogIsFixed: false,
      dialogValue: {},
      dialogIndex: null,
    };
  },
  methods: {
    handleClose() {
      this.$emit("update:visible", false);
    },
    handleEdit(row) {
      const idx = this.tableData.indexOf(row);
      this.dialogVisible = true;
      this.dialogIsEdit = true;
      this.dialogIsFixed = idx < this.initialTableData.length;
      this.dialogValue = { ...row };
      this.dialogIndex = idx;
    },
    handleSave() {
      this.dialogVisible = true;
      this.dialogIsEdit = false;
      this.dialogIsFixed = false;
      this.dialogValue = { condition: "", record: "", process: "" };
      this.dialogIndex = null;
    },
    handleDraft() {
      // 存稿逻辑
    },
    handleDialogOk(val) {
      if (this.dialogIsEdit && this.dialogIndex !== null) {
        this.$set(this.tableData, this.dialogIndex, val);
      } else {
        this.tableData.push(val);
      }
    },
    getRowClassName({ rowIndex }) {
      return rowIndex < this.initialTableData.length ? "fixed-row" : "";
    },
  },
};
</script>
<style scoped lang="less">
.el-dialog__body {
  padding-bottom: 0;
}
.top-card {
  margin-bottom: 0;
  background: rgba(239, 239, 239, 1);
  border-radius: 16px;
}
.top-info-row {
  align-items: stretch;
}
.info-col {
  display: flex;
  flex-direction: column;
}
.info-item {
  display: flex;
  font-size: 15px;
  height: 45px;
  line-height: 45px;
}
.label {
  color: #666;
  font-weight: 500;
}
.sign-col {
  align-items: center;
  text-align: center;
}
.sign-label {
  justify-content: center;
}
.signature-item {
  justify-content: center;
}
.signature-area {
  min-height: 80px;
  min-width: 120px;
  background: #f5f7fa;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 8px;
}
.signature-area img {
  max-width: 100%;
  max-height: 100%;
  display: block;
}
.waiting-text {
  color: #909399;
  font-size: 14px;
}
.sign-time {
  justify-content: center;
  text-align: center;
  color: #666;
  font-size: 14px;
}
.section-card {
  margin-bottom: 0;
}
.footer-btns {
  display: flex;
  justify-content: center;
  padding: 24px;
  padding-bottom: 0;
  gap: 24px;
  .el-button {
    width: 150px;
  }
}
::v-deep(.fixed-row) {
  background-color: rgb(228, 248, 250) !important;
}
@media (max-width: 900px) {
  .info-col {
    height: auto;
  }
}
</style>
culture/src/views/strain-library/validation/primitive-cell/EditConditionDialog.vue
New file
@@ -0,0 +1,66 @@
<template>
  <el-dialog :visible.sync="visible"  width="800px" @close="handleClose">
    <el-form label-width="120px" label-position="top">
      <el-form-item label="菌种培养工艺条件">
        <el-input v-model="form.condition" :disabled="isFixed" placeholder="请输入" />
      </el-form-item>
      <el-form-item label="菌种培养工艺实况记录">
        <el-input type="textarea" v-model="form.record" :rows="7" placeholder="请输入文本内容" />
      </el-form-item>
      <el-form-item label="菌种培养标准工艺">
        <el-input type="textarea" v-model="form.process" :rows="7" placeholder="请输入文本内容" />
      </el-form-item>
    </el-form>
    <div style="text-align: center; margin-top: 24px;">
      <el-button type="primary" @click="handleOk">保存</el-button>
    </div>
  </el-dialog>
</template>
<script>
export default {
  name: 'EditConditionDialog',
  props: {
    visible: Boolean,
    isEdit: Boolean,
    isFixed: Boolean, // true: 固定的8个条件,false: 新增条件
    value: {
      type: Object,
      default: () => ({ condition: '', record: '', process: '' })
    }
  },
  data() {
    return {
      form: { condition: '', record: '', process: '' }
    }
  },
  watch: {
    value: {
      immediate: true,
      handler(val) {
        this.form = { ...val }
      }
    },
    visible(val) {
      if (!val) {
        this.form = { condition: '', record: '', process: '' }
      }
    }
  },
  methods: {
    handleOk() {
      this.$emit('ok', { ...this.form })
      this.handleClose()
    },
    handleClose() {
      this.$emit('update:visible', false)
    }
  }
}
</script>
<style scoped>
.el-dialog__body {
  padding-bottom: 0;
}
</style>
culture/src/views/strain-library/validation/primitive-cell/add.vue
@@ -77,20 +77,22 @@
        </el-dialog>
        <!-- 签字确认组件 -->
        <SignatureCanvas
        <ConfirmStorageDialog
            :visible.sync="signatureVisible"
            @confirm="handleSignatureConfirm"
            name="操作人签字"
            text="是否确认提交该项原始细胞库资料信息?"
        />
    </Card>
</template>
<script>
import SignatureCanvas from '@/components/SignatureCanvas.vue'
import ConfirmStorageDialog from '@/components/confirm-storage-dialog/index.vue'
export default {
    name: 'AddprimitiveCell',
    components: {
        SignatureCanvas
        ConfirmStorageDialog
    },
    data() {
        return {
culture/src/views/strain-library/validation/primitive-cell/confirm-detail.vue
New file
@@ -0,0 +1,254 @@
<template>
  <Card class="confirm-detail-page">
    <el-card class="top-card">
      <el-row :gutter="24" class="top-info-row">
        <el-col :span="8" class="info-col">
          <div class="info-item"><span class="label">菌种来源:</span>{{ detail.source }}</div>
          <div class="info-item"><span class="label">鉴别菌株编号:</span>{{ detail.strainNo }}</div>
          <div class="info-item"><span class="label">鉴别菌株名称:</span>{{ detail.strainName }}</div>
        </el-col>
        <el-col :span="8" class="info-col">
          <div class="info-item"><span class="label">验证实验编号:</span>{{ detail.verifyNo }}</div>
          <div class="info-item"><span class="label">实验时间:</span>{{ detail.experimentTime }}</div>
          <div class="info-item"></div>
        </el-col>
        <el-col :span="8" class="info-col ">
          <div class="info-item sign-label"><span class="label">菌种实验员签字</span></div>
          <div class="info-item signature-item">
            <div class="signature-area">
              <img v-if="detail.signature" :src="detail.signature" alt="签字" />
              <span v-else class="waiting-text">暂无签名</span>
            </div>
          </div>
        </el-col>
      </el-row>
    </el-card>
    <div class="section-card" style="margin-top: 24px;">
      <el-form label-width="100px" label-position="top">
        <el-form-item label="实验结论">
          <el-input type="textarea" v-model="detail.conclusion" :rows="3" placeholder="请输入" />
        </el-form-item>
        <el-form-item label="批准菌株用途">
          <el-checkbox-group v-model="detail.usage">
            <el-checkbox label="传代" />
            <el-checkbox label="菌种保藏" />
            <el-checkbox label="废弃" />
          </el-checkbox-group>
        </el-form-item>
      </el-form>
    </div>
    <el-button type="primary" @click="handleSave" class="el-icon-plus"> 新增</el-button>
    <EditConditionDialog
      :visible.sync="dialogVisible"
      :isEdit="dialogIsEdit"
      :isFixed="dialogIsFixed"
      :value="dialogValue"
      @ok="handleDialogOk"
    />
    <div class="section-card" style="margin-top: 12px;">
      <el-table :data="tableData" border style="width: 100%;" :row-class-name="getRowClassName">
        <el-table-column prop="condition" label="菌种培养工艺条件" />
        <el-table-column prop="record" label="菌种培养工艺实况记录" />
        <el-table-column prop="process" label="菌种培养标准工艺" />
        <el-table-column label="操作" width="120">
          <template #default="{ row }">
            <el-button type="text" @click="handleEdit(row)">编辑</el-button>
            <el-button type="text" @click="handleDetail(row)">详情2</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <div class="footer-btns">
      <el-button type="primary" @click="handleSave">保存</el-button>
      <el-button @click="handleDraft">存稿</el-button>
    </div>
    <DetailConditionDialog
      :visible.sync="detailDialogVisible"
      :value="detailDialogValue"
    />
  </Card>
</template>
<script>
import EditConditionDialog from './EditConditionDialog.vue'
import DetailConditionDialog from './DetailConditionDialog.vue'
export default {
  name: 'ConfirmDetail',
  components: { EditConditionDialog, DetailConditionDialog },
  data() {
    return {
      detail: {
        source: '内容的内容内容内容',
        strainNo: '3411234',
        strainName: '名称名称名称',
        verifyNo: '34133214',
        experimentTime: '2025-1-23 11:10:28',
        signature: '', // 签名图片url
        conclusion: '',
        usage: []
      },
      activeTab: 'condition',
      initialTableData: [
        { condition: '平板培养基', record: '文字内容文字内容文字内容文字内容文字内容文字内容', process: '文字内容文字内容文字内容文字内容文字内容文字内容' },
        { condition: '培养温度', record: '', process: '' },
        { condition: '培养时间', record: '', process: '' },
        { condition: '摇瓶培养基', record: '', process: '' },
        { condition: '接种量', record: '', process: '' },
        { condition: '培养时间', record: '', process: '' },
        { condition: '发酵时间', record: '', process: '' },
        { condition: '检测数据及结果', record: '', process: '' }
      ],
      tableData: [
        { condition: '平板培养基', record: '文字内容文字内容文字内容文字内容文字内容文字内容', process: '文字内容文字内容文字内容文字内容文字内容文字内容' },
        { condition: '培养温度', record: '', process: '' },
        { condition: '培养时间', record: '', process: '' },
        { condition: '摇瓶培养基', record: '', process: '' },
        { condition: '接种量', record: '', process: '' },
        { condition: '培养时间', record: '', process: '' },
        { condition: '发酵时间', record: '', process: '' },
        { condition: '检测数据及结果', record: '', process: '' }
      ],
      dialogVisible: false,
      dialogIsEdit: false,
      dialogIsFixed: false,
      dialogValue: {},
      dialogIndex: null,
      detailDialogVisible: false,
      detailDialogValue: {}
    }
  },
  methods: {
    handleEdit(row) {
      const idx = this.tableData.indexOf(row)
      this.dialogVisible = true
      this.dialogIsEdit = true
      this.dialogIsFixed = idx < this.initialTableData.length
      this.dialogValue = { ...row }
      this.dialogIndex = idx
    },
    handleDetail(row) {
      this.detailDialogVisible = true
      this.detailDialogValue = { ...row }
    },
    handleSave() {
      this.dialogVisible = true
      this.dialogIsEdit = false
      this.dialogIsFixed = false
      this.dialogValue = { condition: '', record: '', process: '' }
      this.dialogIndex = null
    },
    handleDraft() {
      // 存稿逻辑
    },
    handleDialogOk(val) {
      if (this.dialogIsEdit && this.dialogIndex !== null) {
        this.$set(this.tableData, this.dialogIndex, val)
      } else {
        this.tableData.push(val)
      }
    },
    getRowClassName({ rowIndex }) {
      return rowIndex < this.initialTableData.length ? 'fixed-row' : '';
    }
  }
}
</script>
<style scoped lang="less">
.top-card {
  margin-bottom: 0;
  background: rgba(239, 239, 239, 1);
  border-radius: 16px;
}
.top-info-row {
  align-items: stretch;
}
.info-col {
  display: flex;
  flex-direction: column;
}
.info-item {
  display: flex;
  font-size: 15px;
  height: 45px;
  line-height: 45px;
}
.label {
  color: #666;
  font-weight: 500;
}
.sign-col {
  align-items: center;
  text-align: center;
}
.sign-label {
  justify-content: center;
}
.signature-item {
  justify-content: center;
}
.signature-area {
  min-height: 80px;
  min-width: 120px;
  background: #f5f7fa;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 8px;
}
.signature-area img {
  max-width: 100%;
  max-height: 100%;
  display: block;
}
.waiting-text {
  color: #909399;
  font-size: 14px;
}
.sign-time {
  justify-content: center;
  text-align: center;
  color: #666;
  font-size: 14px;
}
.section-card {
  margin-bottom: 0;
}
.footer-btns {
  display: flex;
  justify-content: center;
  padding: 24px;
  padding-bottom: 0;
  gap: 24px;
  .el-button {
    width: 150px;
  }
}
::v-deep(.fixed-row) {
  background-color: rgb(228, 248, 250) !important;
}
@media (max-width: 900px) {
  .info-col {
    height: auto;
  }
}
</style>
culture/src/views/strain-library/validation/primitive-cell/index.vue
@@ -1,8 +1,13 @@
<template>
    <div class="list">
        <!-- Table -->
        <TableCustom :queryForm="queryForm" :tableData="tableData" :total="total" @currentChange="handleCurrentChange"
            @sizeChange="handleSizeChange">
    <TableCustom
      :queryForm="queryForm"
      :tableData="tableData"
      :total="total"
      @currentChange="handleCurrentChange"
      @sizeChange="handleSizeChange"
    >
            <template #search>
                <el-form :model="form" label-width="auto" inline>
                    <el-form-item label="鉴别菌株编号:">
@@ -24,15 +29,29 @@
            <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
              class="title"
              :class="{ active: currentType === 'list' }"
              @click="handleTypeChange('list')"
            >
              原始细胞库验证资料
            </div>
            <div
              class="drafts"
              :class="{ active: currentType === 'draft' }"
              @click="handleTypeChange('draft')"
            >
              草稿箱
            </div>
                    </div>
                    <div 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"
              style="margin-right: 12px"
              >新增原始细胞库资料</el-button
            >
                    </div>
                </div>
            </template>
@@ -46,175 +65,196 @@
                <el-table-column prop="inventory" label="创建时间" />
                <el-table-column prop="status" label="状态">
                    <template #default="{ row }">
                        <el-tag :type="getStatusType(row.status)">{{ getStatusText(row.status) }}</el-tag>
            <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 type="text" @click="handleDetail(row)">确认</el-button>
                        <el-button type="text" @click="handleDetail(row)">详情</el-button>
            <el-button
              type="text"
              @click="$router.push('/strain/validation/confirm-detail')"
              >确认</el-button
            >
            <el-button type="text" @click="handleDetail(row)">详情1</el-button>
            <el-button type="text" @click="handleDetail2(row)">详情2</el-button>
                        <el-button type="text" @click="handleEdit(row)">编辑</el-button>
                        <el-button type="text" @click="handleRecord(row)">删除</el-button>
                    </template>
                </el-table-column>
            </template>
        </TableCustom>
    <PrimitiveCellDetailDialog
      :visible.sync="detailVisible"
      :detail="currentDetail"
    />
    <DetailConditionDialog
    :visible.sync="detailDialogVisible"
    :value="detailDialogValue"
/>
    </div>
</template>
<script>
import PrimitiveCellDetailDialog from "./primitive-cell-detail-dialog.vue";
import DetailConditionDialog from "./DetailConditionDialog.vue";
export default {
    name: 'PrimitiveCell',
  name: "PrimitiveCell",
    components: {
    PrimitiveCellDetailDialog,
    DetailConditionDialog,
    },
    data() {
        return {
        detailDialogVisible: false,
        detailDialogValue: {},
            dialogVisible: false,
            currentType: 'list',
      currentType: "list",
            detailVisible: false,
            currentDetail: {},
            form: {
                strainNo: '',
                strainName: '',
                status: ''
        strainNo: "",
        strainName: "",
        status: "",
            },
            queryForm: {
                pageSize: 10,
                pageNum: 1
        pageNum: 1,
            },
            total: 800,
            tableData: [
                {
                    strainNo: 'YX-2024001',
                    strainName: '大肠杆菌',
                    source: '实验室分离',
                    method: '形态学鉴定、生理生化试验',
                    certificate: '革兰氏阴性杆菌,可发酵葡萄糖产酸产气,IMViC试验++--',
                    storage: '斜面培养',
                    amount: 'A区-01-001',
                    inventory: '50',
                    notes: '用于质粒转化',
                    status: '1'
          strainNo: "YX-2024001",
          strainName: "大肠杆菌",
          source: "实验室分离",
          method: "形态学鉴定、生理生化试验",
          certificate: "革兰氏阴性杆菌,可发酵葡萄糖产酸产气,IMViC试验++--",
          storage: "斜面培养",
          amount: "A区-01-001",
          inventory: "50",
          notes: "用于质粒转化",
          status: "1",
                },
                {
                    strainNo: 'YX-2024002',
                    strainName: '枯草芽孢杆菌',
                    source: '菌种保藏中心',
                    method: '16S rDNA测序',
                    certificate: '革兰氏阳性芽孢杆菌,可水解淀粉,产生溶菌素',
                    storage: '冷冻保存',
                    amount: 'B区-02-005',
                    inventory: '30',
                    notes: '工业发酵菌种',
                    status: '1'
          strainNo: "YX-2024002",
          strainName: "枯草芽孢杆菌",
          source: "菌种保藏中心",
          method: "16S rDNA测序",
          certificate: "革兰氏阳性芽孢杆菌,可水解淀粉,产生溶菌素",
          storage: "冷冻保存",
          amount: "B区-02-005",
          inventory: "30",
          notes: "工业发酵菌种",
          status: "1",
                },
                {
                    strainNo: 'YX-2024003',
                    strainName: '酿酒酵母',
                    source: '发酵工厂',
                    method: '显微镜观察、生理特性',
                    certificate: '椭圆形单细胞真菌,可发酵葡萄糖产生乙醇',
                    storage: '甘油管保存',
                    amount: 'A区-03-002',
                    inventory: '40',
                    notes: '发酵工艺优化',
                    status: '2'
          strainNo: "YX-2024003",
          strainName: "酿酒酵母",
          source: "发酵工厂",
          method: "显微镜观察、生理特性",
          certificate: "椭圆形单细胞真菌,可发酵葡萄糖产生乙醇",
          storage: "甘油管保存",
          amount: "A区-03-002",
          inventory: "40",
          notes: "发酵工艺优化",
          status: "2",
                },
                {
                    strainNo: 'YX-2024004',
                    strainName: '乳酸菌',
                    source: '乳制品分离',
                    method: '生化鉴定、API条',
                    certificate: '革兰氏阳性球菌,产生乳酸,耐酸性强',
                    storage: '冷冻干燥',
                    amount: 'C区-01-003',
                    inventory: '25',
                    notes: '益生菌研究',
                    status: '3'
          strainNo: "YX-2024004",
          strainName: "乳酸菌",
          source: "乳制品分离",
          method: "生化鉴定、API条",
          certificate: "革兰氏阳性球菌,产生乳酸,耐酸性强",
          storage: "冷冻干燥",
          amount: "C区-01-003",
          inventory: "25",
          notes: "益生菌研究",
          status: "3",
                },
                {
                    strainNo: 'YX-2024005',
                    strainName: '青霉菌',
                    source: '环境样本',
                    method: '形态学特征、ITS测序',
                    certificate: '丝状真菌,产生蓝绿色分生孢子,可产青霉素',
                    storage: '斜面培养',
                    amount: 'B区-04-001',
                    inventory: '35',
                    notes: '次级代谢产物研究',
                    status: '1'
                }
            ]
        }
          strainNo: "YX-2024005",
          strainName: "青霉菌",
          source: "环境样本",
          method: "形态学特征、ITS测序",
          certificate: "丝状真菌,产生蓝绿色分生孢子,可产青霉素",
          storage: "斜面培养",
          amount: "B区-04-001",
          inventory: "35",
          notes: "次级代谢产物研究",
          status: "1",
        },
      ],
    };
    },
    methods: {
    handleDetail(row) {
      this.currentDetail = row;
      this.detailVisible = true;
    },
    handleDetail2(row) {
        this.detailDialogValue = row;
        this.detailDialogVisible = true;
    },
        handleViewMore() {
            this.dialogVisible = true;
        },
        resetForm() {
            this.form = {
                strainNo: '',
                strainName: '',
                status: ''
            }
            this.searchData()
        strainNo: "",
        strainName: "",
        status: "",
      };
      this.searchData();
        },
        searchData() {
            // 模拟搜索逻辑
            const { strainNo, strainName, status } = this.form
            let filteredData = [...this.tableData]
      const { strainNo, strainName, status } = this.form;
      let filteredData = [...this.tableData];
            
            if (strainNo) {
                filteredData = filteredData.filter(item =>
        filteredData = filteredData.filter((item) =>
                    item.strainNo.toLowerCase().includes(strainNo.toLowerCase())
                )
        );
            }
            if (strainName) {
                filteredData = filteredData.filter(item =>
        filteredData = filteredData.filter((item) =>
                    item.strainName.toLowerCase().includes(strainName.toLowerCase())
                )
        );
            }
            if (status) {
                filteredData = filteredData.filter(item =>
                    item.status === status
                )
        filteredData = filteredData.filter((item) => item.status === status);
            }
            
            this.total = filteredData.length
      this.total = filteredData.length;
            // 实际项目中这里应该调用API
            console.log('搜索条件:', this.form)
            console.log('分页信息:', this.queryForm)
      console.log("搜索条件:", this.form);
      console.log("分页信息:", this.queryForm);
        },
        handleNewStrain() {
            this.$router.push('/strain/validation/add-primitive-cell')
      this.$router.push("/strain/validation/add-primitive-cell");
            // Implement new strain logic
        },
        handleBatchAdd() {
            // Implement batch add logic
        },
        handleDetail(row) {
            this.currentDetail = row;
            this.detailVisible = true;
        },
        handleEdit(row) {
            // Implement edit logic
        },
        handleRecord(row) {
            this.$router.push({
                path: '/strain-library/strain-library-manage/record',
        path: "/strain-library/strain-library-manage/record",
                query: {
                    id: row.strainNo
                }
            })
          id: row.strainNo,
        },
      });
        },
        handleCurrentChange(page) {
            this.queryForm.pageNum = page
      this.queryForm.pageNum = page;
            // Implement page change logic
        },
        handleSizeChange(size) {
            this.queryForm.pageSize = size
      this.queryForm.pageSize = size;
            // Implement size change logic
        },
        handleTypeChange(type) {
@@ -223,22 +263,22 @@
        },
        getStatusType(status) {
            const types = {
                1: 'success',
                2: 'info',
                3: 'warning'
            }
            return types[status] || 'info'
        1: "success",
        2: "info",
        3: "warning",
      };
      return types[status] || "info";
        },
        getStatusText(status) {
            const texts = {
                1: '已入库',
                2: '已出库',
                3: '入库待确认'
            }
            return texts[status] || '未知状态'
        }
    }
}
        1: "已入库",
        2: "已出库",
        3: "入库待确认",
      };
      return texts[status] || "未知状态";
    },
  },
};
</script>
<style scoped lang="less">
@@ -268,7 +308,7 @@
        .view-more {
            position: absolute;
            right: 0;
            color: #049C9A;
      color: #049c9a;
        }
    }
@@ -293,7 +333,7 @@
.search-form {
    margin-bottom: 20px;
    background: #F5F7FA;
  background: #f5f7fa;
    padding: 24px;
    border-radius: 8px;
@@ -321,17 +361,17 @@
    .tab {
        padding: 10px 30px;
        border: 1px solid #DCDFE6;
    border: 1px solid #dcdfe6;
        border-bottom: none;
        border-radius: 8px 8px 0 0;
        cursor: pointer;
        margin-right: 10px;
        background: #F5F7FA;
    background: #f5f7fa;
        &.active {
            background: #fff;
            border-color: #049C9A;
            color: #049C9A;
      border-color: #049c9a;
      color: #049c9a;
            font-weight: bold;
        }
    }
@@ -361,7 +401,6 @@
        line-height: 50px;
        width: 166px;
        text-align: center;
    }
    .drafts {
@@ -384,14 +423,13 @@
        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;
    border-bottom: 1px solid #ebeef5;
        margin-right: 0;
        
        .el-dialog__title {
@@ -423,5 +461,4 @@
        }
    }
}
</style>
culture/src/views/strain-library/validation/primitive-cell/primitive-cell-detail-dialog.vue
New file
@@ -0,0 +1,107 @@
<template>
  <el-dialog
    :visible.sync="visible"
    title="原始细胞库资料详情"
    width="650px"
    @close="handleClose"
  >
    <el-form label-width="120px" label-position="top" class="detail-form">
        <el-row :gutter="24">
            <el-col :span="12">
            <el-form-item label="菌种源">
                <el-input v-model="detail.source" disabled placeholder="请输入" />
            </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="24">
        <el-col :span="12">
          <el-form-item label="菌种编号">
            <el-input v-model="detail.strainNo" disabled placeholder="请输入" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="菌种名称">
            <el-input v-model="detail.strainName" disabled placeholder="请输入" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="验证实验编号">
            <el-input v-model="detail.verifyNo" disabled placeholder="请输入" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="实验时间">
            <el-input v-model="detail.experimentTime" disabled placeholder="请输入" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="24">
        <el-col :span="12">
          <el-form-item label="菌种实验员签字">
            <div class="signature-area">
              <img v-if="detail.signature" :src="detail.signature" alt="签字" />
              <span v-else class="waiting-text">暂无签名</span>
            </div>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="签字时间">
            <el-input v-model="detail.signTime" disabled />
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </el-dialog>
</template>
<script>
export default {
  name: 'PrimitiveCellDetailDialog',
  props: {
    visible: Boolean,
    detail: {
      type: Object,
      default: () => ({
        source: '',
        strainNo: '',
        strainName: '',
        verifyNo: '',
        experimentTime: '',
        signature: '',
        signTime: ''
      })
    }
  },
  methods: {
    handleClose() {
      this.$emit('update:visible', false)
    }
  }
}
</script>
<style scoped>
.detail-form {
  margin-top: 10px;
}
.signature-area {
  min-height: 120px;
  min-width: 240px;
  background: #f5f7fa;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.signature-area img {
  max-width: 100%;
  max-height: 100%;
  display: block;
}
.waiting-text {
  color: #909399;
  font-size: 14px;
}
</style>
culture/src/views/system/user/components/add-edit.vue
@@ -20,7 +20,7 @@
              :value="item.roleId"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="登录状态" prop="status">
        <el-form-item label="启动状态" prop="status">
          <el-switch v-model="form.status"></el-switch>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
@@ -56,33 +56,51 @@
    },
  },
  data() {
    var validatePhone = (rule, value, callback) => {
      if (!value) {
        // The 'required' rule will handle empty value, so we can make this optional here
        // or keep it for a more specific message if needed.
        // For now, let's assume 'required' handles the empty case.
        callback();
        return;
      }
      const phoneRegex = new RegExp(/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,'g'); // Regex for 11-digit Chinese mobile numbers
      if (!phoneRegex.test(value)) {
        callback(new Error('请输入有效的11位手机号码'));
      } else {
        callback();
      }
    };
    return {
      form: { status: true },
      userDeptId: '',
      rules: {
        nickName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
        phonenumber: [{ required: true, message: '请输入联系电话', trigger: 'blur' }],
        roleId: [{ required: true, message: '请选择角色', trigger: 'blur' }],
        phonenumber: [
          { required: true, message: '请输入联系电话', trigger: 'blur' },
          { validator: validatePhone, trigger: 'blur' }
        ],
        roleId: [{ required: true, message: '请选择角色', trigger: 'change' }],
        userName: [{ required: true, message: '请输入登陆账号', trigger: 'blur' }],
        status: [{ required: true, message: '请选择启动状态', trigger: 'blur' }],
      },
    }
  },
  created() {
    this.form = { status: true }
    if (Object.keys(this.row).length) {
    if (this.row && this.row.userId) {
      this.form = {
        userId: this.row.userId,
        nickName: this.row.nickName,
        phonenumber: this.row.phonenumber,
        roleId: this.row.roleId,
        userName: this.row.userName,
        remark: this.row.remark,
        ...this.row,
        status: this.row.status == 0 ? true : false,
      }
    } else {
      this.form = {}
      this.form = {
        nickName: '',
        phonenumber: '',
        userName: '',
        roleId: '',
        status: true,
        remark: '',
      }
    }
  },
  mounted() { },
culture/src/views/system/user/components/reset-password.vue
@@ -3,16 +3,16 @@
    <el-dialog :visible.sync="dialogVisible" @close="$emit('close')" title="重置密码" width="30%">
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="姓名" prop="nickName">
          <el-input :disabled="true" v-model="form.nickName" placeholder="请输入" style="width: 50%;"></el-input>
          <el-input :disabled="true" v-model="form.nickName" placeholder="请输入" style="width: 95%;"></el-input>
        </el-form-item>
        <el-form-item label="登陆账号" prop="account">
          <el-input :disabled="true" v-model="form.account" placeholder="请输入" style="width: 50%;"></el-input>
          <el-input :disabled="true" v-model="form.account" placeholder="请输入" style="width: 95%;"></el-input>
        </el-form-item>
        <el-form-item label="新密码" prop="password">
          <el-input v-model="form.password" placeholder="请输入" style="width: 50%;"></el-input>
          <el-input v-model="form.password" type="password" placeholder="请输入" style="width: 95%;"></el-input>
        </el-form-item>
        <el-form-item label="确认密码" prop="confirmPassword">
          <el-input v-model="form.confirmPassword" placeholder="请输入" style="width: 50%;"></el-input>
          <el-input v-model="form.confirmPassword" type="password" placeholder="请输入" style="width: 95%;"></el-input>
        </el-form-item>
      </el-form>
      <div class="select-member-footer">
culture/src/views/system/user/index.vue
@@ -1,7 +1,7 @@
<template>
  <div class="list">
    <TableCustom :queryForm="pagination" :tableData="data" :total="pagination.total"
      @currentChange="handleCurrentChange" @sizeChange="handleSizeChange">
      @handleCurrentChange="handleCurrentChange" @handleSizeChange="handleSizeChange">
      <template #search>
        <el-form label-width="100px" inline>
          <el-form-item label="人员搜索">
@@ -24,7 +24,7 @@
          </el-form-item>
          <el-form-item style="margin-left: 63px;">
            <el-button @click="reset">重置</el-button>
            <el-button type="primary" @click="onSubmit">查询</el-button>
            <el-button type="primary" @click="onSubmit" style="margin-left: 10px;">查询</el-button>
          </el-form-item>
        </el-form>
      </template>
@@ -45,10 +45,6 @@
            <div class="status_class">
              <div :class="row.status == 0 ? 'green' : 'red'"></div>
              <div>{{ row.status == 0 ? '正常' : '禁用' }}</div>
              <div v-if="row.status == 1" style="cursor: pointer"
                @click="; (dialogVisibleView = true), (rowView = row), $forceUpdate()">
                <i class="el-icon-warning"></i>
              </div>
            </div>
          </template>
        </el-table-column>
@@ -56,28 +52,26 @@
        <el-table-column label="操作" width="300">
          <template slot-scope="{ row }">
            <div>
              <el-button type="text" @click="edit(row)">编辑</el-button>
              <el-button type="text" @click="edit(row)">账号继承</el-button>
              <el-button v-if="row.status != 0" type="text" @click="updateStatus(row, true)">
              <el-button type="text" @click="edit(row)" class="action-button">编辑</el-button>
              <el-button type="text" @click="inherit(row)" class="action-button">账号继承</el-button>
              <el-button v-if="row.status != 0" type="text" @click="updateStatus(row, true)" class="action-button">
                启用
              </el-button>
              <el-button v-if="row.status == 0" type="text" @click="updateStatus(row, false)">
              <el-button v-if="row.status == 0" type="text" @click="updateStatus(row, false)" class="action-button">
                禁用
              </el-button>
              <el-button type="text" @click="detail(row)">重置密码</el-button>
              <el-button type="text" @click="del(row)">删除</el-button>
              <el-button type="text" @click="detail(row)" class="action-button">重置密码</el-button>
              <el-button type="text" @click="del(row)" class="action-button">删除</el-button>
            </div>
          </template>
        </el-table-column>
      </template>
    </TableCustom>
    <AddEdit v-if="dialogVisible" :row="row" :deptList="deptList" :deptType="deptTypeList" :roleList="roleList"
    <AddEdit v-if="dialogVisible" :row="row" :deptType="deptTypeList" :roleList="roleList"
      :dialogVisible="dialogVisible" @close="dialogVisible = false, row = {}" @confirm="confirm" />
    <ResetPassword v-if="passwordVisible" :row="row" :dialogVisible="passwordVisible"
      @close="passwordVisible = false, row = {}" @confirm="passwordConfirm" />
    <ViewData v-if="dialogVisibleView" :row="rowView" :dialogVisible="dialogVisibleView"
      @close="dialogVisibleView = false, rowView = {}" />
    <Disb v-if="disbDialogVisible" :row="disbRow" :dialogVisible="disbDialogVisible"
      @close="disbDialogVisible = false, disbRow = {}" @confirm="disbConfirm" />
    <ShowDelConfirm :show="delShow" @close="delShow = false" @confirm="delConfirm" title="确认要删除该人员吗?"
@@ -90,7 +84,6 @@
<script>
import { getList, add, edit, delDept, roleList, updatePwd, changeStatus, typeList } from './service'
import AddEdit from './components/add-edit.vue'
import ViewData from './components/view-data.vue'
import Disb from './components/disb.vue'
import ResetPassword from './components/reset-password.vue'
import Inherit from './components/inherit.vue'
@@ -98,7 +91,6 @@
  name: 'User',
  components: {
    AddEdit,
    ViewData,
    Disb,
    ResetPassword,
    Inherit,
@@ -113,7 +105,6 @@
      inheritDialogVisible: false,//账号继承
      data: [],//列表数据
      nickNameOrPhone: '',//人员搜索
      deptId: [],//部门
      roleId: [],//角色
      status: '',//状态
      roleList: [],//角色列表
@@ -137,9 +128,8 @@
  },
  watch: {},
  created() {
    // this.getRoleList()
    // this.getListData()
    // this.getTypeList()
    this.getRoleList()
    this.getListData()
  },
  mounted() { },
  methods: {
@@ -150,7 +140,7 @@
    },
    getRoleList() {
      roleList().then((res) => {
        this.roleList = res.data.data
        this.roleList = res
      })
    },
    delConfirm() {
@@ -201,6 +191,10 @@
      this.row = row
      this.dialogVisible = true
    },
    inherit(row) {
      this.inheritRow = row
      this.inheritDialogVisible = true
    },
    updateStatus(row, type) {
      if (type) {
        changeStatus({ ...row, status: 0 }).then(() => {
@@ -220,18 +214,14 @@
    },
    async getListData() {
      let obj = {
        ...this.pagination,
        pageNum: this.pagination.pageNum,
        pageSize: this.pagination.pageSize,
        nickNameOrPhone: this.nickNameOrPhone,
        deptIds: this.deptId,
        roleIds: this.roleId,
        status: this.status,
      }
      this.listLoading = true
      const {
        data: {
          data: { records, total },
        },
      } = await getList(obj)
      const { records,total } = await getList(obj)
      this.data = records
      this.pagination.total = total
      this.timeOutID = setTimeout(() => {
@@ -241,7 +231,6 @@
    reset() {
      this.nickNameOrPhone = ''
      this.roleId = []
      this.deptId = []
      this.status = ''
      this.pagination.pageNum = 1
      this.getListData()
@@ -267,6 +256,10 @@
  height: 100%;
}
.action-button {
  margin-right: 8px;
}
.green {
  background-color: green;
}
culture/src/views/system/user/service.js
@@ -36,6 +36,6 @@
export const updatePwd = (data) => {
  return axios.post(`/system/user/resetPwd`, { ...data })
}
export const typeList = (data) => {
export const typeList = () => {
  return axios.get(`/t-business-dept/list/type?type=1`,)
}