Merge branch 'main' of http://120.76.84.145:10101/gitblit/r/H5/leshan-laboratory
| | |
| | | <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> |
| | |
| | | <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> |
| | |
| | | <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> |
| | |
| | | </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 = '' |
| | |
| | | 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 ''; |
| | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | &:hover, |
| | | &.active { |
| | | background: rgba(4, 156, 154, 0.1); |
| | | color: #049C9A; |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | 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 }) |
| | | } |
| | |
| | | <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"> |
| | |
| | | } |
| | | }, |
| | | 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) |
| | | }, |
| | |
| | | </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" /> |
| | |
| | | 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"), |
| | | } |
| | | ] |
| | | }, |
| | |
| | | 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: { |
| | |
| | | |
| | | // 登录验证 |
| | | // 排除登录页的校验 |
| | | // 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 |
| | |
| | | <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> |
| | |
| | | </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> |
| | |
| | | <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> |
| | |
| | | </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: [ |
| | |
| | | gainer: '1、实验操作评定', |
| | | situationOne: '考核数:', |
| | | situationTwo: '积分数:', |
| | | keys: ['handleCount', 'handleIntegral', 'handleStartTime', 'handleEndTime'] |
| | | }, |
| | | ],//菌种实验员 |
| | | detailData: {}, |
| | | selectedExperimenter: null |
| | | } |
| | | }, |
| | | computed: { |
| | |
| | | } |
| | | }, |
| | | 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) { |
| | |
| | | <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> |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { getListData } from './service' |
| | | export default { |
| | | name: 'ProjectTeamIntegral', |
| | | data() { |
| | | return { |
| | | form: { |
| | | }, |
| | | form: {}, |
| | | tableData: [], |
| | | queryForm: { |
| | | pageSize: 10, |
| | |
| | | total: 0 |
| | | } |
| | | }, |
| | | created() { |
| | | this.getList() |
| | | }, |
| | | methods: { |
| | | goDetail() { |
| | | goDetail(id) { |
| | | this.$router.push({ |
| | | path: '/projectList/addProject' |
| | | path: `/deliveryAssessment/projectTeamIntegral-detail?id=${id}`, |
| | | }) |
| | | }, |
| | | handleCurrentChange(page) { |
| | |
| | | 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() |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | 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}`) |
| | | } |
| | |
| | | <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"> |
| | |
| | | <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' }) |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | }, |
| | |
| | | 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); |
| | | }); |
| | | } |
| | | } |
| | | } |
| | |
| | | font-weight: 500; |
| | | font-size: 16px; |
| | | color: #FFFFFF; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .member-edit { |
New file |
| | |
| | | <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> |
New file |
| | |
| | | <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> |
| | |
| | | |
| | | // 列表 |
| | | 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}`) |
| | | } |
New file |
| | |
| | | <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> |
| | |
| | | <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> |
| | |
| | | <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> |
| | |
| | | <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> |
| | |
| | | <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> |
| | |
| | | </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> |
| | |
| | | </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 { |
| | |
| | | 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> |
| | | |
| | |
| | | <template> |
| | | <el-dialog |
| | | title="添加出入库记录" |
| | | title="新增培养皿分离记录" |
| | | :visible.sync="visible" |
| | | width="520px" |
| | | :close-on-click-modal="false" |
| | |
| | | <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> |
| | |
| | | <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> |
| | |
| | | <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> |
| | | |
New file |
| | |
| | | <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> |
New file |
| | |
| | | <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> |
| | |
| | | </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 { |
New file |
| | |
| | | <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> |
| | |
| | | <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="鉴别菌株编号:"> |
| | |
| | | <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> |
| | |
| | | <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) { |
| | |
| | | }, |
| | | 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"> |
| | |
| | | .view-more { |
| | | position: absolute; |
| | | right: 0; |
| | | color: #049C9A; |
| | | color: #049c9a; |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | .search-form { |
| | | margin-bottom: 20px; |
| | | background: #F5F7FA; |
| | | background: #f5f7fa; |
| | | padding: 24px; |
| | | border-radius: 8px; |
| | | |
| | |
| | | |
| | | .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; |
| | | } |
| | | } |
| | |
| | | line-height: 50px; |
| | | width: 166px; |
| | | text-align: center; |
| | | |
| | | } |
| | | |
| | | .drafts { |
| | |
| | | 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 { |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | <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> |
| | |
| | | :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"> |
| | |
| | | }, |
| | | }, |
| | | 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() { }, |
| | |
| | | <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"> |
| | |
| | | <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="人员搜索"> |
| | |
| | | </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> |
| | |
| | | <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> |
| | |
| | | <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="确认要删除该人员吗?" |
| | |
| | | <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' |
| | |
| | | name: 'User', |
| | | components: { |
| | | AddEdit, |
| | | ViewData, |
| | | Disb, |
| | | ResetPassword, |
| | | Inherit, |
| | |
| | | inheritDialogVisible: false,//账号继承 |
| | | data: [],//列表数据 |
| | | nickNameOrPhone: '',//人员搜索 |
| | | deptId: [],//部门 |
| | | roleId: [],//角色 |
| | | status: '',//状态 |
| | | roleList: [],//角色列表 |
| | |
| | | }, |
| | | watch: {}, |
| | | created() { |
| | | // this.getRoleList() |
| | | // this.getListData() |
| | | // this.getTypeList() |
| | | this.getRoleList() |
| | | this.getListData() |
| | | }, |
| | | mounted() { }, |
| | | methods: { |
| | |
| | | }, |
| | | getRoleList() { |
| | | roleList().then((res) => { |
| | | this.roleList = res.data.data |
| | | this.roleList = res |
| | | }) |
| | | }, |
| | | delConfirm() { |
| | |
| | | this.row = row |
| | | this.dialogVisible = true |
| | | }, |
| | | inherit(row) { |
| | | this.inheritRow = row |
| | | this.inheritDialogVisible = true |
| | | }, |
| | | updateStatus(row, type) { |
| | | if (type) { |
| | | changeStatus({ ...row, status: 0 }).then(() => { |
| | |
| | | }, |
| | | 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(() => { |
| | |
| | | reset() { |
| | | this.nickNameOrPhone = '' |
| | | this.roleId = [] |
| | | this.deptId = [] |
| | | this.status = '' |
| | | this.pagination.pageNum = 1 |
| | | this.getListData() |
| | |
| | | height: 100%; |
| | | } |
| | | |
| | | .action-button { |
| | | margin-right: 8px; |
| | | } |
| | | |
| | | .green { |
| | | background-color: green; |
| | | } |
| | |
| | | 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`,) |
| | | } |