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