13404089107
2025-06-02 b1cb0925184e4426b93306cb4c290586126383c7
Merge branch 'main' of http://120.76.84.145:10101/gitblit/r/H5/leshan-laboratory
18个文件已修改
12个文件已添加
3963 ■■■■■ 已修改文件
culture/package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/assets/login/iconBg.png 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/assets/login/img5.png 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/assets/login/img6.png 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/assets/login/img7.png 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/assets/login/img8.png 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/main.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/middleground/index.vue 1130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/add.vue 424 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/addProgenitor.vue 198 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/components/AddSublevelForm.vue 422 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/components/AddSublevelPlan.vue 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/components/ParentForm.vue 354 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/components/PlanForm.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/index.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/progenitorComponents/AddAncestor.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/progenitorComponents/AddSublevelForm.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/service.js 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/system/operation-log/index.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/system/operation-log/service.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/assets/login/iconBg.png 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/assets/login/img5.png 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/assets/login/img6.png 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/assets/login/img7.png 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/assets/login/img8.png 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/main.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/middleground/index.vue 1203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/system/operation-log/index.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/system/operation-log/service.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/package.json
@@ -13,6 +13,7 @@
    "@antv/g6": "^4.8.24",
    "@tinymce/tinymce-vue": "^3.2.8",
    "aieditor": "^1.3.6",
    "ant-design-vue": "^1.7.8",
    "axios": "^0.24.0",
    "core-js": "^3.41.0",
    "element-ui": "^2.15.6",
culture/src/assets/login/iconBg.png
culture/src/assets/login/img5.png
culture/src/assets/login/img6.png
culture/src/assets/login/img7.png
culture/src/assets/login/img8.png
culture/src/main.js
@@ -1,6 +1,8 @@
import Vue from "vue";
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
import '@/assets/font/font.css'
import App from "./App.vue";
import router from "./router";
@@ -18,6 +20,7 @@
Vue.config.productionTip = false;
Vue.use(ElementUI, { size: 'small' })
Vue.use(Antd)
Vue.component('Table', Table)
Vue.component('TableCustom', TableCustom)
Vue.component('Card', Card)
culture/src/views/middleground/index.vue
@@ -1,29 +1,26 @@
<template>
  <div class="login-page">
    <div class="top-nav">
      <HeaderNav class="header-main" :logo="true" />
      <HeaderNav class="header-main" :logo="'true'" />
    </div>
    <div class="middleground" :class="windowWidth<1240 ? 'column' : ''">
    <div class="middleground" :class="{
      'column': windowWidth < 1240,
      'mobile': windowWidth < 800
    }">
      <!-- 左侧模块区域 -->
      <div class="left-modules">
        <!-- 这里将放置四个模块 -->
        <div class="module-item">
          <!-- 模块内容,例如图标和文字 -->
          <div class="module-icon"></div>
          <div class="module-text">实验室运行模块</div>
        </div>
        <div class="module-item">
          <div class="module-icon"></div>
          <div class="module-text">专业报告库</div>
        </div>
        <div class="module-item">
          <div class="module-icon"></div>
          <div class="module-text">化验师QA专题报告库</div>
        </div>
        <div class="module-item">
          <div class="module-icon"></div>
          <div class="module-text">评定模块</div>
      <div class="left-modules" :class="[currentModuleLayout, {
        'mobile-layout': windowWidth < 800
      }]">
        <div class="module-item" v-for="(item, index) in filteredModuleList" :key="index"
          @click="handleModuleClick(item)">
          <div class="module-bg"></div>
          <div class="module-content">
            <div class="icon-wrapper">
              <div class="module-icon" :style="{ backgroundImage: `url(${item.icon})` }"></div>
            </div>
            <div class="module-text">{{ item.text }}</div>
          </div>
        </div>
      </div>
@@ -31,8 +28,8 @@
      <div class="right-content">
        <!-- 日历 -->
        <div class="calendar-section">
          <!-- <h3>日历</h3> -->
          <el-calendar v-model="date" />
          <div class="title-canlender">日历</div>
          <a-calendar @panelChange="onPanelChange" />
        </div>
        <!-- 待办事项 -->
@@ -69,7 +66,6 @@
<script>
import { loginReq } from './service'
import HeaderNav from '../../layouts/components/HeaderNav.vue'
import { mapState } from 'vuex'
// 引入 Element UI 的日历组件
// import { ElCalendar } from 'element-ui';
export default {
@@ -89,12 +85,66 @@
      },
      date: new Date(),
      viewWidth: '',
      scale: 1
      scale: 1,
      // 审批人
      moduleList2: [
      {
          text: '菌种库',
          icon: require('../../assets/login/img1.png'),
          path: '/strain'
        },
        {
          text: '菌种报告库',
          icon: require('../../assets/login/img4.png'),
          path: '/deliveryAssessment'
        },
      ],
      // 工艺工程师
      moduleList3: [
      {
          text: '菌种库',
          icon: require('../../assets/login/img1.png'),
          path: '/strain'
        },
        {
          text: '菌种报告库',
          icon: require('../../assets/login/img4.png'),
          path: '/deliveryAssessment'
        },
      ],
      // 实验员
      moduleList5: [
      {
          text: '菌种库',
          icon: require('../../assets/login/img1.png'),
          path: '/strain'
        },
      ],
      // 超级管理员
      moduleList6: [
        {
          text: '菌种库',
          icon: require('../../assets/login/img1.png'),
          path: '/strain'
        },
        {
          text: '菌种报告库',
          icon: require('../../assets/login/img4.png'),
          path: '/deliveryAssessment'
        },
        {
          text: '项目组管理',
          icon: require('../../assets/login/img5.png'),
          path: '/projectList'
        },
        {
          text: '系统管理',
          icon: require('../../assets/login/img6.png'),
          path: '/system'
        }
      ]
    }
  },
  // computed: {},
  computed: {
    ...mapState(['tagList', 'isFold'])
  },
  created() {
@@ -102,43 +152,71 @@
    this.handleResize();
  },
  mounted() {
     // 监听窗口大小变化
     window.addEventListener("resize", this.handleResize);
   },
  methods: {
    // 监听窗口大小变化
    window.addEventListener("resize", this.handleResize);
    handleResize() {
      // if (window.innerWidth < 1000) {
      //   this.$store.commit("SET_ISFOLD", true);
      // } else if (window.innerWidth >= 1000 && this.isFold) {
      //   this.$store.commit("SET_ISFOLD", false);
      // }
      this.windowWidth = window.innerWidth;
    },
    // ...mapActions(['setIsFold']),
    handleKeyDown(event) {
      if (event.key === 'Enter') {
        this.login()
      }
  },
  beforeDestroy() {
    // 组件销毁前移除事件监听
    window.removeEventListener("resize", this.handleResize);
  },
  methods: {
    onPanelChange(e) {
      console.log('eeeeeee', e)
    },
    // 添加处理窗口大小变化的方法
    handleResize() {
      this.viewWidth = window.innerWidth
      this.windowWidth = window.innerWidth;
    },
    login() {
      if (this.loginForm.username == '') {
        this.$message.warning('请输入账号')
        return
      }
      if (this.loginForm.password == '') {
        this.$message.warning('请输入密码')
        return
      }
      loginReq(this.loginForm).then(res => {
        sessionStorage.setItem('token', res.token)
        sessionStorage.setItem('userInfo', JSON.stringify(res.userInfo.user))
        this.$router.push('/system')
    handleModuleClick(item) {
     if(item.path){
      this.$router.push({
        path: item.path,
      })
     }
    },
  },
  computed: {
    currentModuleList() {
      const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
      const userType = userInfo.userType;
      switch (userType) {
        case 2: // 审批人
          return this.moduleList2;
        case 3: // 工艺工程师
          return this.moduleList3;
        // case 4: // 化验师
        //   return this.moduleList4;
        case 5: // 实验员
          return this.moduleList5;
        case 6: // 超级管理员
          return this.moduleList6;
        default:
          return this.moduleList6; // 默认返回化验师的模块列表
      }
    },
    filteredModuleList() {
      return this.currentModuleList.filter(item => item);
    },
    currentModuleLayout() {
      const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
      const userType = userInfo.userType;
      switch (userType) {
        case 2: // 审批人
          return 'layout-4';
        case 3: // 工艺工程师
          return 'layout-4';
        // case 4: // 化验师
        //   return 'layout-4';
        case 5: // 实验员
          return 'layout-5';
        case 6: // 超级管理员
          return 'layout-6';
        default:
          return 'layout-6'; // 默认返回化验师的布局
      }
    }
  }
}
@@ -186,329 +264,268 @@
  .middleground {
    flex: 1;
    height: calc(100% - 70px);
    display: flex;
    // flex-wrap: wrap;
    justify-content: flex-start;
    /* 左侧内容靠左对齐 */
    padding: 20px 20px 20px 108px;
    /* 调整左右内边距 */
    justify-content: center;
    margin: 0px 20px 0px 108px;
    gap: 40px;
    /* 添加左右间距 */
    box-sizing: border-box;
    .left-modules {
      align-items: center;
      padding: 40px 0;
      display: flex;
      flex-wrap: wrap;
      box-sizing: content-box;
      justify-content: center;
      /* 模块之间水平分散对齐 */
      /* 水平居中 */
      align-items: flex-start;
      /* 模块垂直靠上对齐 */
      /* 垂直居中 */
      flex-grow: 1;
      /* 左侧宽度自适应 */
      align-content: flex-start;
      /* 多行模块垂直靠上对齐 */
      gap: 20px;
      /* 添加模块之间的垂直间距 */
      display: grid;
      gap: 40px;
      padding-right: 20px;
      /* 给左侧模块区域右侧添加一些间距,避免紧贴右侧内容 */
      width: 100%;
      max-width: 1200px;
      margin: 0 auto;
      align-self: center;
      justify-self: center;
      &.mobile-layout {
        grid-template-columns: repeat(2, 1fr);
        gap: 15px;
        padding-right: 0;
        max-width: 100%;
        .module-item {
          width: 100%;
          height: 307px;
        }
      }
      // 4个模块布局(2x2)
      &.layout-4 {
        grid-template-columns: repeat(2, 260px);
        grid-template-rows: repeat(2, 260px);
        justify-content: center;
      }
      // 5个模块布局(2+3)
      &.layout-5 {
        grid-template-columns: repeat(3, 240px);
        grid-template-rows: repeat(2, 240px);
        justify-content: center;
        position: relative;
        // 上面两个模块整体居中
        .module-item:nth-child(1),
        .module-item:nth-child(2) {
          transform: translateX(120px);
        }
        // 下面三个模块
        .module-item:nth-child(3) {
          grid-column: 1;
        }
        .module-item:nth-child(4) {
          grid-column: 2;
        }
        .module-item:nth-child(5) {
          grid-column: 3;
        }
        &.mobile-layout {
          grid-template-columns: repeat(2, 1fr);
          grid-template-rows: auto;
          .module-item:nth-child(1),
          .module-item:nth-child(2) {
            transform: none;
          }
        }
      }
      // 6个模块布局(3x2)
      &.layout-6 {
        grid-template-columns: repeat(3, 220px);
        grid-template-rows: repeat(2, 220px);
        justify-content: center;
        &.mobile-layout {
          grid-template-columns: repeat(2, 1fr);
          grid-template-rows: auto;
        }
      }
    }
    .module-item {
      // width: 37%;
      // height: 30%;
      width: 307px;
      height: 307px;
      background-color: red;
      position: relative;
      width: 100%;
      height: 100%;
      cursor: pointer;
      transition: all 0.3s ease;
      overflow: hidden;
    }
    .module-bg {
      position: absolute;
      top: 75px;
      left: 0;
      width: 100%;
      height: calc(100% - 75px);
      background: url('../../assets/login/cardBg.png') no-repeat center;
      background-size: cover;
      transition: transform 0.3s ease;
      z-index: 1;
    }
    .module-content {
      position: relative;
      height: 100%;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: space-between;
      background: url('../../assets/login/cardBg.png');
      /* 设置模块背景图 */
      background-size: cover;
      /* 背景图覆盖整个元素 */
      background-position: center top;
      /* 调整背景图位置:顶部居中 */
      z-index: 2;
    }
      .module-icon {
        width: 80px;
        height: 80px;
        // background-color: #eee; /* Placeholder for icon */
        z-index: 1;
        /* 确保图标在背景之上 */
        background-size: contain;
        /* 图标背景图包含在元素内 */
        background-repeat: no-repeat;
        /* 不重复 */
        background-position: 100% 100%;
        /* 图标背景图居中 */
    .icon-wrapper {
      width: 100%;
      height: 150px;
      display: flex;
      justify-content: center;
      align-items: center;
      background: url('../../assets/login/iconBg.png') no-repeat center;
      background-size: 100% 100%;
      z-index: 2;
      &::before {
        content: '';
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 40px;
        background: linear-gradient(to bottom, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0) 100%);
        backdrop-filter: blur(4px);
        -webkit-backdrop-filter: blur(4px);
        pointer-events: none;
        z-index: 2;
      }
      /* 为每个模块图标设置特定的背景图片 */
      &:nth-child(1) .module-icon {
        background-image: url('../../assets/login/img1.png');
      }
      &:nth-child(2) .module-icon {
        background-image: url('../../assets/login/img2.png');
      }
      &:nth-child(3) .module-icon {
        background-image: url('../../assets/login/img3.png');
      }
      &:nth-child(4) .module-icon {
        background-image: url('../../assets/login/img4.png');
      }
      .module-text {
        font-size: 16px;
        font-weight: bold;
        position: relative;
        /* 为下划线定位 */
        z-index: 1;
        /* 确保文字在背景之上 */
        margin-bottom: 50px;
        &::after {
          content: '';
          position: absolute;
          left: 50%;
          /* 从文字中间开始 */
          bottom: -5px;
          /* 调整下划线位置 */
          transform: translateX(-50%);
          /* 使下划线居中 */
          width: 50%;
          /* 调整下划线宽度 */
          height: 2px;
          /* 调整下划线粗细 */
          background-color: #007bff;
          /* 示例下划线颜色 */
        }
      }
      &:nth-of-type(2n+1) {
        margin-right: 0;
      }
      &:nth-child(odd) {
        margin-right: 0px;
      &::after {
        content: '';
        position: absolute;
        left: 0;
        bottom: 0;
        width: 100%;
        height: 40px;
        background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 100%);
        backdrop-filter: blur(4px);
        -webkit-backdrop-filter: blur(4px);
        pointer-events: none;
        z-index: 2;
      }
    }
    .right-content {
      width: 870px;
      /* 固定右侧宽度 */
      flex-shrink: 0;
      /* 防止右侧被压缩 */
      padding: 20px;
      border-radius: 8px;
      // background-color: #fff;
      // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    .module-icon {
      width: 100px;
      height: 100px;
      background-size: contain;
      background-repeat: no-repeat;
      background-position: center;
      transition: all 0.3s ease;
      position: relative;
      z-index: 3;
    }
      display: flex;
      /* 使日历和待办事项垂直排列 */
      flex-direction: column;
    .module-text {
      position: relative;
      font-size: 16px;
      font-weight: bold;
      color: #333;
      margin-top: 35px;
      text-align: center;
      z-index: 3;
      .calendar-section {
        margin-bottom: 20px;
        box-sizing: border-box;
        /* 日历下方间距 */
        height: 595px;
        padding: 20px;
        border-radius: 14px;
        background: url(../../assets/login/rili.png) no-repeat center;
      &::after {
        content: '';
        position: absolute;
        left: 50%;
        bottom: -5px;
        transform: translateX(-50%);
        width: 50%;
        height: 2px;
        background-color: #007bff;
      }
    }
        h3 {
          margin-top: 0;
          margin-bottom: 10px;
          font-size: 18px;
          color: #333;
        }
        .calendar {
          height: 100% !important;
          width: 100% !important;
          box-sizing: border-box;
        }
        /* 尝试调整 Element UI 日历的样式 */
        ::v-deep .el-calendar {
          height: 100%;
          width: 100%;
          background: none !important;
          box-sizing: border-box;
          .el-calendar__header {
            padding: 12px 0;
            box-sizing: border-box;
          }
          .el-calendar__body {
            padding: 0;
          }
          .el-calendar-table {
            thead th {
              padding: 5px 0;
              box-sizing: border-box;
              font-weight: normal;
            }
            tbody td {
              border: none;
              .el-calendar-day {
                /* 调整日期单元格高度 */
                display: flex;
                justify-content: center;
                align-items: center;
                border-radius: 4px;
                box-sizing: border-box;
                /* 圆角 */
                margin: 8px;
                /* 单元格间距 */
                cursor: pointer;
                // max-width: 90px;
                max-height: 61px;
                background: #FFFFFF;
                border-radius: 8px;
                border: 1px solid #EDEDED;
                &:hover {
                  background-color: #f0f0f0;
                }
              }
              /* 当前日期样式 */
              &.is-selected .el-calendar-day {
                background-color: #007bff;
                /* 示例选中颜色 */
                color: #fff;
              }
            }
          }
        }
    // 根据布局调整图标和文字大小
    .layout-5 & {
      .module-icon {
        width: 80px;
        height: 80px;
      }
      .todo-list-section {
        flex: 1;
        border-radius: 14px;
        background-color: rgba(255, 255, 255, 1);
        // overflow: auto;
        padding: 24px 34px;
        display: flex;
        flex-direction: column;
      .module-text {
        font-size: 15px;
        margin-top: 30px;
      }
    }
        .title {
          height: 22px;
          font-family: SourceHanSansCN, SourceHanSansCN;
          font-weight: bold;
          font-size: 18px;
          color: #222222;
          line-height: 22px;
          margin-bottom: 20px;
    .layout-6 & {
      .module-icon {
        width: 70px;
        height: 70px;
      }
      .module-text {
        font-size: 14px;
        margin-top: 25px;
      }
    }
    // 响应式调整
    @media screen and (max-width: 1240px) {
      .module-item {
        .module-bg {
          top: 60px;
          height: calc(100% - 60px);
        }
        .todo-list {
          flex: 1;
          height: 100%;
          overflow: auto;
        .icon-wrapper {
          height: 120px;
          &::before,
          &::after {
            height: 35px;
          }
        }
        /* 待办事项列表样式 */
        .todo-item {
          display: flex;
          align-items: center;
          justify-content: space-between;
          margin-bottom: 20PX;
        .module-icon {
          width: 90px;
          height: 90px;
        }
          /* 分隔线 */
          &:first-child {
            margin-top: 3px;
        .module-text {
          margin-top: 30px;
        }
      }
    }
    @media screen and (max-width: 900px) {
      .module-item {
        .module-bg {
          top: 50px;
          height: calc(100% - 50px);
        }
        .icon-wrapper {
          height: 100px;
          &::before,
          &::after {
            height: 30px;
          }
        }
          .todo-details {
            display: flex;
            align-content: center;
          }
        .module-icon {
          width: 80px;
          height: 80px;
        }
          .notice-card {
            position: relative;
            margin-right: 12px;
          }
          .todo-icon {
            position: relative;
            width: 24px;
            height: 24px;
            flex-shrink: 0;
            background: url('../../assets//login/notice.png') no-repeat center;
            background-position: center;
            background-size: 100% 100%;
          }
          .red-notice {
            position: absolute;
            top: -3px;
            right: -3px;
            width: 6px;
            height: 6px;
            border-radius: 50%;
            background: rgba(235, 65, 65, 1);
          }
          .todo-title {
            font-family: SourceHanSansCN, SourceHanSansCN;
            font-weight: 500;
            font-size: 14px;
            color: #303133;
            line-height: 24px;
          }
          .todo-meta {
            display: flex;
            align-items: center;
          }
          .time {
            width: 14px;
            height: 14px;
            margin-right: 6px;
            margin-left: 20px;
            flex-shrink: 0;
            background: url('../../assets//login/time.png') no-repeat center;
            background-position: center;
            background-size: 100% 100%;
          }
          .me {
            width: 14px;
            height: 14px;
            margin-right: 6px;
            flex-shrink: 0;
            background: url('../../assets//login/mi.png') no-repeat center;
            background-position: center;
            background-size: 100% 100%;
          }
          .todo-submitter {
            font-family: SourceHanSansCN, SourceHanSansCN;
            font-weight: 400;
            font-size: 12px;
            color: #909399;
            // line-height: 18px;
          }
        .module-text {
          margin-top: 25px;
        }
      }
    }
@@ -516,14 +533,21 @@
  .column {
    flex-direction: column;
    /* 改为上下布局 */
    padding: 20px;
    /* 调整内边距 */
    gap: 20px;
    /* 调整上下间距 */
    height: auto;
    /* 高度自适应内容 */
    padding: 20px 20px 20px 18px !important;
    align-items: center;
    margin: 0 !important;
    .left-modules {
      // width: 100%;
      // display: flex;
      // flex-wrap: wrap;
      // justify-content: center;
      // gap: 20px;
      // margin-bottom: 20px;
    }
  }
  // Tablet layout
@@ -531,44 +555,410 @@
    .login-page {
      .middleground {
        flex-direction: column;
        /* 改为上下布局 */
        padding: 20px;
        /* 调整内边距 */
        gap: 20px;
        /* 调整上下间距 */
        height: auto;
        /* 高度自适应内容 */
        padding: 20px 20px 20px 18px !important;
        .left-modules {
          width: 100%;
          /* 左侧宽度占满 */
          justify-content: center;
          /* 在上下布局中水平居中模块 */
          padding: 0;
          /* 移除 padding */
          gap: 20px;
          /* 上下布局时模块之间的间距 */
          padding-right: 0;
          /* 移除右侧间距 */
        }
        .module-item {
          width: 307px;
          /* 模块宽度固定 */
          height: 307px;
          /* 模块高度固定 */
          margin-right: 0 !important;
          /* 移除桌面布局的右侧间距 */
        }
        .right-content {
          width: 100%;
          /* 右侧宽度占满 */
          height: auto;
          /* 高度自适应 */
          padding: 15px;
          /* 调整内边距 */
          .calendar-section {
            height: auto;
            min-height: 400px;
            padding: 15px;
            ::v-deep .ant-fullcalendar-fullscreen {
              .ant-fullcalendar-table {
                tbody td {
                  padding: 4px;
                  .ant-fullcalendar-date {
                    height: 40px;
                  }
                }
              }
            }
          }
          .todo-list-section {
            padding: 15px;
          }
        }
      }
    }
  }
  .mobile {
    margin: 0;
    padding: 10px;
    margin: 0 !important;
    .left-modules {
      width: 100%;
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: 15px;
      margin-bottom: 15px;
      padding: 0;
      .module-item {
        width: calc(50% - 15px);
        max-width: 307px;
        height: 307px;
        margin: 0;
        box-sizing: border-box;
      }
    }
    .right-content {
      width: 100%;
      padding: 10px;
      box-sizing: border-box;
      .calendar-section {
        height: auto;
        min-height: 400px;
        padding: 10px;
        margin-bottom: 15px;
        ::v-deep .ant-fullcalendar-fullscreen {
          .ant-fullcalendar-table {
            tbody td {
              padding: 4px;
              .ant-fullcalendar-date {
                height: 40px;
              }
            }
          }
        }
      }
      .todo-list-section {
        padding: 10px;
      }
    }
  }
  .right-content {
    width: 870px;
    /* 固定右侧宽度 */
    flex-shrink: 0;
    /* 防止右侧被压缩 */
    padding: 20px;
    border-radius: 8px;
    // background-color: #fff;
    // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    display: flex;
    /* 使日历和待办事项垂直排列 */
    flex-direction: column;
    .calendar-section {
      margin-bottom: 20px;
      box-sizing: border-box;
      height: 595px;
      padding: 20px;
      border-radius: 14px;
      background: url(../../assets/login/rili.png) no-repeat center;
      .title-canlender {
        height: 22px;
        font-family: SourceHanSansCN, SourceHanSansCN;
        font-weight: bold;
        font-size: 18px;
        color: #222222;
        line-height: 22px;
        margin-bottom: 20px;
      }
      /* 调整 ant-design-vue 日历的样式 */
      ::v-deep .ant-fullcalendar-fullscreen {
        background: none !important;
        border: none !important;
        padding: 0 !important;
        .ant-fullcalendar-header {
          .ant-radio-group {
            .ant-radio-button-wrapper {
              background: #FFFFFF;
              margin: 0 4px;
              color: #666;
              &:hover {
                color: #009688;
              }
              &.ant-radio-button-wrapper-checked {
                background-color: #009688 !important;
                border-color: #009688 !important;
                color: #FFFFFF !important;
                box-shadow: none !important;
              }
            }
          }
          .ant-select {
            .ant-select-selection {
              background: #FFFFFF;
              border: 1px solid #EDEDED;
              border-radius: 8px;
              color: #666;
              &:hover {
                border-color: #009688;
              }
              .ant-select-selection__rendered {
                line-height: 32px;
              }
            }
            &.ant-select-open .ant-select-selection {
              border-color: #009688;
              box-shadow: none;
            }
          }
          .ant-select-dropdown {
            .ant-select-dropdown-menu {
              background: #FFFFFF;
              border: 1px solid #EDEDED;
              border-radius: 8px;
              box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
              .ant-select-dropdown-menu-item {
                color: #666;
                &:hover {
                  background-color: #f0f0f0;
                }
                &.ant-select-dropdown-menu-item-selected {
                  background-color: #009688 !important;
                  color: #FFFFFF !important;
                }
              }
            }
          }
        }
        .ant-fullcalendar-table {
          thead th {
            padding: 5px 0;
            font-weight: normal;
            color: #666;
            text-align: center;
            .ant-fullcalendar-column-header {
              text-align: center;
            }
          }
          tbody td {
            border: none;
            padding: 8px;
            .ant-fullcalendar-date {
              margin: 0;
              padding: 0;
              height: 55px;
              background: #FFFFFF;
              border-radius: 8px;
              border: 1px solid #EDEDED;
              display: flex;
              justify-content: center;
              align-items: center;
              &:hover {
                background-color: rgba(4, 156, 154, .1);
              }
              .ant-fullcalendar-value {
                color: #666;
              }
            }
            &.ant-fullcalendar-selected-day .ant-fullcalendar-date {
              // background-color: #009688 !important;
              border-color: #009688 !important;
              color: rgba(4, 156, 154, 1);
              .ant-fullcalendar-value {
                color: rgba(4, 156, 154, 1) !important;
              }
            }
            &.ant-fullcalendar-today .ant-fullcalendar-date {
              // border-color: #009688;
            }
          }
        }
        .ant-fullcalendar-month-panel-selected-cell {
          .ant-fullcalendar-month {
            border-top-color: #009688 !important;
            background: rgba(4, 156, 154, .2);
            .ant-fullcalendar-value {
              color: #FFFFFF;
            }
            :hover {
              .ant-fullcalendar-month {
                background: rgba(4, 156, 154, .2);
              }
            }
          }
        }
        .ant-fullcalendar-month-panel-current-cell {
          .ant-fullcalendar-month {
            border-top-color: #009688 !important;
            :hover {
              .ant-fullcalendar-month {
                background: rgba(4, 156, 154, .2);
              }
            }
          }
        }
        .ant-fullcalendar-month-panel-cell {
          .ant-fullcalendar-month:hover {
            background: rgba(4, 156, 154, .2);
          }
        }
      }
    }
    .todo-list-section {
      flex: 1;
      border-radius: 14px;
      background-color: rgba(255, 255, 255, 1);
      // overflow: auto;
      padding: 24px 34px;
      display: flex;
      flex-direction: column;
      .title {
        height: 22px;
        font-family: SourceHanSansCN, SourceHanSansCN;
        font-weight: bold;
        font-size: 18px;
        color: #222222;
        line-height: 22px;
        margin-bottom: 20px;
      }
      .todo-list {
        flex: 1;
        height: 100%;
        overflow: auto;
      }
      /* 待办事项列表样式 */
      .todo-item {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 20PX;
        /* 分隔线 */
        &:first-child {
          margin-top: 3px;
        }
        .todo-details {
          display: flex;
          align-content: center;
        }
        .notice-card {
          position: relative;
          margin-right: 12px;
        }
        .todo-icon {
          position: relative;
          width: 24px;
          height: 24px;
          flex-shrink: 0;
          background: url('../../assets//login/notice.png') no-repeat center;
          background-position: center;
          background-size: 100% 100%;
        }
        .red-notice {
          position: absolute;
          top: -3px;
          right: -3px;
          width: 6px;
          height: 6px;
          border-radius: 50%;
          background: rgba(235, 65, 65, 1);
        }
        .todo-title {
          font-family: SourceHanSansCN, SourceHanSansCN;
          font-weight: 500;
          font-size: 14px;
          color: #303133;
          line-height: 24px;
        }
        .todo-meta {
          display: flex;
          align-items: center;
        }
        .time {
          width: 14px;
          height: 14px;
          margin-right: 6px;
          margin-left: 20px;
          flex-shrink: 0;
          background: url('../../assets//login/time.png') no-repeat center;
          background-position: center;
          background-size: 100% 100%;
        }
        .me {
          width: 14px;
          height: 14px;
          margin-right: 6px;
          flex-shrink: 0;
          background: url('../../assets//login/mi.png') no-repeat center;
          background-position: center;
          background-size: 100% 100%;
        }
        .todo-submitter {
          font-family: SourceHanSansCN, SourceHanSansCN;
          font-weight: 400;
          font-size: 12px;
          color: #909399;
          // line-height: 18px;
        }
      }
    }
culture/src/views/pedigree-chart/add.vue
@@ -49,20 +49,29 @@
          </el-form-item>
        </div>
      </div>
      <div class="card" style="margin-top: 30px">
        <Table :height="null" :total="0" :tableData="tableData">
          <el-table-column label="接种操作人" prop="strainSource" />
          <el-table-column label="接种操作时间" prop="strainCode" />
          <el-table-column label="传代菌种编号" prop="strainName" />
          <el-table-column label="传代菌种名称" prop="strainName" />
          <el-table-column label="接种菌种编号" prop="strainName" />
          <el-table-column label="接种菌种名称" prop="strainName" />
          <el-table-column label="入库总数" prop="strainName" />
          <el-table-column label="保存/废弃" prop="strainName" />
          <el-table-column label="入库时间" prop="strainName" />
      <div class="card" style="margin-top: 30px; display: block">
        <Table
          :height="null"
          :total="total"
          :data="tableData"
          :queryForm="queryForm"
          @handleCurrentChange="handleCurrentChange"
          @handleSizeChange="handleSizeChange"
        >
          <el-table-column label="菌种实验员" prop="experimenter" />
          <el-table-column label="菌种编号" prop="strainCode" />
          <el-table-column label="菌种名称" prop="strainName" />
          <el-table-column label="保存/废弃">
            <template #default="{ row }">
              {{ row.status === 1 ? "保存" : "废弃" }}
            </template>
          </el-table-column>
          <el-table-column label="菌种保存/废弃时间" prop="storageTime" />
          <el-table-column label="操作">
            <template #default="{ row }">
              <el-button type="text">确认入库</el-button>
              <el-button type="text" @click="handleConfirm(row)"
                >确认入库</el-button
              >
            </template>
          </el-table-column>
        </Table>
@@ -74,17 +83,21 @@
            <el-button type="primary" class="el-icon-plus" @click="addNode">
              新增</el-button
            >
            <el-button type="primary" @click="setGenerationPlan"
              >设置传代计划数</el-button
            >
            <el-button type="primary" @click="showDetail">详情</el-button>
            <el-button type="primary" @click="handleEdit">编辑</el-button>
            <el-button type="primary" @click="handleDelete">删除</el-button>
          </div>
        </div>
        <div class="strain-flow-chart">
          <div id="mountNode"></div>
        </div>
        <el-button type="primary" @click="handleSubmit" style="width: 150px"
          <el-button type="primary" v-if="!$route.query.id" @click="handleSubmit" style="width: 150px"
          >保存</el-button
        >
      </div>
@@ -108,7 +121,7 @@
      name="菌种保藏人签字"
      text="是否确认该项菌种信息入库"
      :visible.sync="storageVisible"
      @confirm="handleSignatureConfirm"
      @confirm="handleSignatureConfirm1"
    />
  </div>
</template>
@@ -120,7 +133,16 @@
import AddSublevelForm from "./components/AddSublevelForm.vue";
import AddSublevelPlan from "./components/AddSublevelPlan.vue";
import ConfirmStorageDialog from "@/components/confirm-storage-dialog";
import { add } from "./service";
import {
  add,
  getDetail,
  addProgenitorChild,
  addProgenitor,
  pageListDetail,
  confirmStorage,
  deleteNode,
  updateChild,
} from "./service";
export default {
  name: "AddPedigree",
  components: {
@@ -132,6 +154,10 @@
  },
  data() {
    return {
      queryForm: {
        pageSize: 10,
        pageNum: 1,
      },
      signatureVisible: false,
      form: {
        strainSourceStart: "",
@@ -175,6 +201,7 @@
      confirmStorageDialogVisible: false,
      storageVisible: false,
      treeData: [],
      total: 0,
    };
  },
  computed: {
@@ -194,11 +221,236 @@
    this.initGraph();
    this.initEvents();
  },
  created() {
    if (this.$route.query.id) {
      this.reloadData();
    }
  },
  beforeDestroy() {
    this.graph?.destroy();
    window.removeEventListener("resize", this.handleResize);
  },
  methods: {
    handleEdit() {
      if (!this.selectedNode) {
        this.$message.warning("请先选择节点");
        return;
      }
      const nodeModel = this.selectedNode;
      console.log(nodeModel);
      if (nodeModel.label === "母代") {
        this.$refs.parentForm.openInitData({
          strainSourceStart: this.form.strainSourceStart,
          strainSourceEnd: this.form.strainSourceEnd,
          ...nodeModel.data,
          status: "edit",
        });
      } else if (nodeModel.label === "子代" || nodeModel.label === "孙代") {
        this.dialogTitle = `${nodeModel.label}详情`;
        this.$refs.addSublevelForm.openInitData({
          strainCode1: this.form.strainCode,
            strainName1: this.form.strainName,
          title: `编辑${nodeModel.label}`,
          form: { ...nodeModel.data },
          formStatus: "edit",
        });
      } else if (nodeModel.label === "传代计划数") {
        const nodeIndex = this.graphData.nodes.findIndex(
          (n) => n.nodeId === nodeModel.nodeId
        );
        if (this.graphData.nodes[nodeIndex - 1].label === "子代") {
          this.$refs.addSublevelPlan.openInitData({
            strainSourceStart: this.form.strainSourceStart,
            strainCode1: this.form.strainCode,
            strainName1: this.form.strainName,
            strainSourceEnd: this.form.strainSourceEnd,
            confirmTime: this.form.confirmTime,
            parentId: nodeModel.id,
            formStatus: "edit",
          });
        } else {
          this.$refs.planForm.openInitData({
            strainSourceStart: this.form.strainSourceStart,
            strainSourceEnd: this.form.strainSourceEnd,
            strainCode1: this.form.strainCode,
            strainName1: this.form.strainName,
            ...nodeModel.data,
            status: "edit",
          });
        }
      }
    },
    handleDelete() {
      if (!this.selectedNode) {
        this.$message.warning("请先选择节点");
        return;
      }
      this.$confirm("确定删除该节点吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
        deleteNode({ id: this.selectedNode.id }).then((res) => {
          if (res.code == 200) {
            this.$message.success("删除成功");
            this.reloadData();
          }
        });
      });
    },
    handleConfirm(row) {
      this.comfirmId = row.id;
      this.storageVisible = true;
    },
    handleSignatureConfirm1(signatureImage) {
      confirmStorage({
        id: this.comfirmId,
        preserveSignature: signatureImage.signature,
      }).then((res) => {
        if (res.code == 200) {
          this.$message.success("确认入库成功");
          this.reloadData();
        }
      });
    },
    // 处理每页显示数量变化
    handleSizeChange(val) {
      this.size = val;
      this.queryForm.pageSize = val;
      this.getList();
    },
    // 处理页码变化
    handleCurrentChange(val) {
      this.current = val;
      this.queryForm.pageNum = val;
      this.getList();
    },
    async getList() {
      try {
        // 获取列表数据
        const listRes = await pageListDetail({
          id: this.$route.query.id,
          current: this.current,
          size: this.size,
        });
        if (listRes.code === 200) {
          this.tableData = listRes.data.records;
          this.total = listRes.data.total;
          this.current = listRes.data.current;
          this.size = listRes.data.size;
        }
        // ... 其他现有的 reloadData 代码 ...
      } catch (error) {
        console.error("Failed to reload data:", error);
        this.$message.error("数据加载失败");
      }
    },
    reloadData() {
      this.getList();
      getDetail({ id: this.$route.query.id }).then((res) => {
        console.log(JSON.stringify(res.pedigreeChartParentAllDetailDTO));
        const nodeMap = new Map();
        const calculateLevel = (node) => {
          if (node.parentId === "0") return 0;
          const parent = nodeMap.get(node.parentId);
          if (!parent) return 0;
          return calculateLevel(parent) + 1;
        };
        // 首先创建所有节点的映射
        const createNodeMap = (nodes) => {
          nodes.forEach((node) => {
            nodeMap.set(node.id, node);
            if (node.children) {
              createNodeMap(node.children);
            }
          });
        };
        const processNode = (node) => {
          const nodeId = node.id; // 直接使用后端返回的id
          const level = calculateLevel(node);
          let label = "";
          if (level === 0) {
            label = "母代";
          } else if (level === 1) {
            label = node.generationCount !== null ? "传代计划数" : "子代";
          } else if (level === 2) {
            label = "子代";
          } else if (level === 3) {
            label = node.generationCount !== null ? "传代计划数" : "孙代";
          } else if (level === 4) {
            label = "孙代";
          }
          return {
            nodeId,
            label,
            number: node.strainCode,
            data: node,
            status: node.status,
            planCount: node.generationCount,
            currentCount: 0,
            style: {
              fill: node.status === 1 ? "#00B5AA" : "#999",
              opacity: node.status === 1 ? (level === 0 ? 1 : level === 1 ? 0.6 : 0.4) : 0.3,
            },
          };
        };
        // 重置数据
        this.graphData.nodes = [];
        this.graphData.edges = [];
        if (res.pedigreeChartParentAllDetailDTO && res.pedigreeChartParentAllDetailDTO.one) {
          // 创建节点映射
          createNodeMap(res.pedigreeChartParentAllDetailDTO.one);
          // 处理所有节点
          const processAllNodes = (nodes) => {
            nodes.forEach(node => {
              const processedNode = processNode(node);
              this.graphData.nodes.push(processedNode);
              // 根据parentId创建边,直接使用原始id
              if (node.parentId !== "0") {
                this.graphData.edges.push({
                  source: node.parentId,
                  target: node.id,
                  style: {
                    stroke: "rgba(4, 156, 154, 1)",
                    lineWidth: 1,
                  },
                });
              }
              if (node.children) {
                processAllNodes(node.children);
              }
            });
          };
          // 处理所有节点
          processAllNodes(res.pedigreeChartParentAllDetailDTO.one);
        }
        this.form = {
          strainSourceStart: res.strainSourceStart,
          strainSourceEnd: res.strainSourceEnd,
          strainCode: res.strainCode,
          strainName: res.strainName,
        };
        // 更新图表
        this.updateGraphData();
      });
    },
    addNodeSign(value, type) {
      this.nodeData = value;
      this.nodeType = type;
@@ -213,13 +465,11 @@
          }
          // 处理节点数据,将data字段中的数据提升到根级别
          const processNodeData = (node, level = 0) => {
          const processNodeData = (node) => {
            const processedNode = {
              ...node,
              ...node.data,
              children: [],
              level,
              type: [1, 3, 5].includes(level + 1) ? 1 : 2,
            };
            delete processedNode.data;
            return processedNode;
@@ -241,11 +491,9 @@
              const child = nodeMap.get(edge.target);
              if (parent && child) {
                child.level = parent.level + 1;
                child.type = [1, 3, 5].includes(child.level + 1) ? 1 : 2;
                parent.children.push(child);
              }
            });
            // 返回根节点数组
            return nodes
              .filter((node) => !childNodeIds.has(node.nodeId))
@@ -263,7 +511,6 @@
            },
            one: buildTree(this.graphData.nodes, this.graphData.edges),
          };
          console.log("提交的数据:", baseData, this.graphData);
          add(baseData).then((res) => {
            if (res.code == 200) {
@@ -283,22 +530,23 @@
    },
    handleSignatureConfirm(signatureImage) {
      this.confirmStorageDialogVisible = false;
      console.log(this.nodeType);
      if (this.nodeType === 1) {
        this.handleAddParent({
          ...this.nodeData,
          preserveSignature: signatureImage.signature,
          type: this.nodeType == 2 ? 2 : 1,
          vaccinateSignature: signatureImage.signature,
        });
      } else if (this.nodeType === 2) {
        this.handleAddPlan({
          ...this.nodeData,
          preserveSignature: signatureImage.signature,
          type: this.nodeType == 2 ? 2 : 1,
          vaccinateSignature: signatureImage.signature,
        });
      } else if (this.nodeType === 3) {
        this.handleAddSublevel({
          ...this.nodeData,
          preserveSignature: signatureImage.signature,
          type: this.nodeType == 2 ? 2 : 1,
          vaccinateSignature: signatureImage.signature,
        });
        this.$refs.addSublevelPlan.closeDialog();
      }
@@ -595,6 +843,7 @@
            ...node,
            id: node.nodeId,
            level: 0, // 添加层级标记
            children: [] // 添加children数组
          });
        });
@@ -605,6 +854,8 @@
          if (parent && child) {
            // 设置子节点的层级
            child.level = parent.level + 1;
            // 将子节点添加到父节点的children数组中
            parent.children.push(child);
          }
        });
@@ -618,20 +869,27 @@
        // 将所有节点按层级排序并设置type字段
        const allNodes = Array.from(nodeMap.values());
        allNodes.sort((a, b) => a.level - b.level);
        allNodes.sort((a, b) => {
          // 首先按层级排序
          if (a.level !== b.level) {
            return a.level - b.level;
          }
          // 如果层级相同,按节点ID排序以保持稳定
          return a.nodeId.localeCompare(b.nodeId);
        });
        // 设置type字段:第1,3,5层为1,其他层级为2
        allNodes.forEach((node) => {
          node.type = [1, 3, 5].includes(node.level + 1) ? 1 : 2;
        });
        return allNodes; // 返回所有节点,包含type字段
        return allNodes;
      };
      const treeData = buildTree(this.graphData.nodes, this.graphData.edges);
      // 更新图表数据
      this.graph.changeData({
      // 更新图表数据,确保边的连接关系正确
      const processedData = {
        nodes: this.graphData.nodes.map((node) => ({
          ...node,
          id: node.nodeId,
@@ -639,8 +897,22 @@
        edges: this.graphData.edges.map((edge) => ({
          ...edge,
          id: `${edge.source}-${edge.target}`,
          // 确保边的样式一致
          style: {
            stroke: "rgba(4, 156, 154, 1)",
            lineWidth: 1,
            opacity: 0.5,
            endArrow: {
              path: G6.Arrow.triangle(6, 6),
              fill: "rgba(4, 156, 154, 1)",
              stroke: "rgba(4, 156, 154, 1)",
            },
          },
        })),
      });
      };
      // 更新图表数据
      this.graph.changeData(processedData);
      // 保存树形结构数据
      this.treeData = treeData;
@@ -735,10 +1007,11 @@
        this.showDiscarded = true;
        this.isAddingNode = true;
        this.$refs.addSublevelForm.openInitData({
          parentId: nodeModel.id,
          title: `新增${nextLevel}`,
          strainName1: this.form.strainName,
            strainCode1: this.form.strainCode,
          form: {
            strainName: this.form.strainName,
            strainCode: this.form.strainCode,
            isDiscarded: true,
          },
        });
@@ -747,6 +1020,18 @@
      }
    },
    handleAddParent(value) {
      if (this.$route.query.id) {
        value.pedigreeChartId = this.$route.query.id;
        value.parentId = "0";
        addProgenitor(value).then((res) => {
          if (res.code == 200) {
            this.$message.success("操作成功");
            this.$refs.parentForm.closeDialog();
            this.reloadData();
          }
        });
        return;
      }
      const parentId = `parent-${++this.nodeCount}`;
      this.graphData.nodes.push({
        nodeId: parentId,
@@ -768,6 +1053,26 @@
      this.$refs.form.resetFields();
    },
    handleAddSublevel(value) {
      if (value.formStatus == 'edit') {
        updateChild(value).then((res) => {
          if (res.code === 200) {
            this.$message.success("操作成功");
            this.$refs.addSublevelForm.closeDialog();
            this.reloadData();
          }
        });
        return
      }
      if (this.$route.query.id) {
        addProgenitor(value).then((res) => {
          if (res.code === 200) {
            this.$message.success("操作成功");
            this.$refs.addSublevelForm.closeDialog();
            this.reloadData();
          }
        });
        return;
      }
      if (this.isAddingNode) {
        // 新增节点的处理逻辑
        const nodeModel = this.selectedNode;
@@ -784,7 +1089,7 @@
        this.graphData.nodes.push({
          nodeId: childId,
          label: nextLevel,
          number: value.inoculateNo.trim(),
          number: value.strainCode.trim(),
          status: value.status,
          data: value,
          style: {
@@ -894,8 +1199,8 @@
        this.$refs.addSublevelPlan.openInitData({
          ...nodeModel.data,
          label: nodeModel.label,
          strainName: this.form.strainName,
          strainCode: this.form.strainCode,
          strainName1: this.form.strainName,
          strainCode1: this.form.strainCode,
          strainSourceStart: this.form.strainSourceStart,
          strainSourceEnd: this.form.strainSourceEnd,
        });
@@ -911,6 +1216,35 @@
      }
    },
    handleAddPlan(value) {
      if (value.formStatus == 'edit') {
        console.log(value,'params');
        updateChild(value).then((res) => {
          if (res.code === 200) {
            this.$message.success("操作成功");
            this.$refs.planForm.closeDialog();
            this.$refs.addSublevelPlan.closeDialog();
            this.reloadData();
          }
        });
        return
      }
      if (this.$route.query.id) {
        let params = {
          parentId: value.id,
          generationCount: value.generationCount,
          vaccinateSignature: value.vaccinateSignature,
        };
        addProgenitorChild(params).then((res) => {
          if (res.code === 200) {
            this.$message.success("操作成功");
            this.$refs.planForm.closeDialog();
            this.$refs.addSublevelPlan.closeDialog();
            this.reloadData();
          }
        });
        return;
      }
      const nodeModel = this.selectedNode;
      const generationId = `generation-${++this.nodeCount}`;
@@ -947,29 +1281,45 @@
      }
      const nodeModel = this.selectedNode;
      if (nodeModel.label === "母代") {
        this.$refs.parentForm.openInitData({
          strainSourceStart: this.form.strainSourceStart,
          strainSourceEnd: this.form.strainSourceEnd,
          ...nodeModel.data,
          status: "detail",
        });
      } else if (nodeModel.label === "子代" || nodeModel.label === "孙代") {
        this.dialogTitle = `${nodeModel.label}详情`;
        this.$refs.addSublevelForm.openInitData({
          strainCode1: this.form.strainCode,
            strainName1: this.form.strainName,
          title: `${nodeModel.label}详情`,
          form: { ...nodeModel.data },
        });
      } else if (nodeModel.label === "传代计划数") {
        return
        const nodeIndex = this.graphData.nodes.findIndex(
          (n) => n.nodeId === nodeModel.nodeId
        );
        if (this.graphData.nodes[nodeIndex - 1].label === "子代") {
          console.log(123);
          this.$refs.addSublevelPlan.openInitData({
            strainSourceStart: this.form.strainSourceStart,
            strainCode1: this.form.strainCode,
            strainName1: this.form.strainName,
            strainSourceEnd: this.form.strainSourceEnd,
            ...nodeModel.data,
            status: "detail",
          });
        } else {
          console.log(123);
          this.$refs.planForm.openInitData({
            strainSourceStart: this.form.strainSourceStart,
            strainSourceEnd: this.form.strainSourceEnd,
            strainCode1: this.form.strainCode,
            strainName1: this.form.strainName,
            ...nodeModel.data,
            status: "detail",
          });
culture/src/views/pedigree-chart/addProgenitor.vue
@@ -25,23 +25,34 @@
          </el-form-item>
        </div>
      </div>
      <div class="card" style="margin-top: 30px">
        <Table :height="null" :total="0" :tableData="tableData">
          <el-table-column label="菌株类型" prop="strainSource" />
          <el-table-column label="来源获得/菌落编号" prop="strainCode" />
          <el-table-column label="菌种编号" prop="strainName" />
      <div class="card" style="margin-top: 30px; display: block">
        <Table
          :height="null"
          :total="total"
          :data="tableData"
          :queryForm="queryForm"
          @handleCurrentChange="handleCurrentChange"
          @handleSizeChange="handleSizeChange"
        >
          <el-table-column label="菌种实验员" prop="experimenter" />
          <el-table-column label="菌种编号" prop="strainCode" />
          <el-table-column label="菌种名称" prop="strainName" />
          <el-table-column label="入库总数" prop="strainName" />
          <el-table-column label="保存/废弃" prop="strainName" />
          <el-table-column label="菌种入库时间" prop="strainName" />
          <el-table-column label="接种操作人" prop="strainName" />
          <el-table-column label="保存/废弃">
            <template #default="{ row }">
              {{ row.status === 1 ? "保存" : "废弃" }}
            </template>
          </el-table-column>
          <el-table-column label="菌种保存/废弃时间" prop="storageTime" />
          <el-table-column label="操作">
            <template #default="{ row }">
              <el-button type="text">确认入库</el-button>
              <el-button type="text" @click="handleConfirm(row)"
                >确认入库</el-button
              >
            </template>
          </el-table-column>
        </Table>
      </div>
      <div class="chart">
        <div class="header">
          <div class="title">菌种传代生产谱系图</div>
@@ -53,6 +64,8 @@
              >设置传代计划数</el-button
            >
            <el-button type="primary" @click="showDetail">详情</el-button>
            <el-button type="primary" @click="handleEdit">编辑</el-button>
            <el-button type="primary" @click="handleDelete">删除</el-button>
          </div>
        </div>
@@ -66,11 +79,6 @@
          style="width: 150px"
          >保存</el-button
        >
      </div>
      <div class="end-btn">
        <!-- <el-button type="primary" @click="handleSubmit">提交</el-button>
        <el-button @click="handleDraft">存草稿</el-button>
        <el-button @click="handleCancel">取消</el-button> -->
      </div>
    </el-form>
@@ -87,7 +95,7 @@
      name="菌种保藏人签字"
      text="是否确认该项菌种信息入库"
      :visible.sync="storageVisible"
      @confirm="handleSignatureConfirm"
      @confirm="handleSignatureConfirm1"
    />
  </div>
</template>
@@ -98,7 +106,16 @@
import AddAncestor from "./progenitorComponents/AddAncestor.vue";
import AddSublevelForm from "./progenitorComponents/AddSublevelForm.vue";
import ConfirmStorageDialog from "@/components/confirm-storage-dialog";
import { add, getDetail, addProgenitorChild, addProgenitor } from "./service";
import {
  add,
  getDetail,
  addProgenitorChild,
  addProgenitor,
  pageListDetail,
  confirmStorage,
  deleteNode,
  updateChild,
} from "./service";
export default {
  name: "AddPedigree",
@@ -110,6 +127,10 @@
  },
  data() {
    return {
      queryForm: {
        pageSize: 10,
        pageNum: 1,
      },
      signatureVisible: false,
      form: {
        strainCode: "",
@@ -143,6 +164,10 @@
      tableData: [],
      confirmStorageDialogVisible: false,
      storageVisible: false,
      total: 0,
      current: 1,
      size: 10,
      comfirmId: "",
    };
  },
  computed: {
@@ -172,7 +197,93 @@
    window.removeEventListener("resize", this.handleResize);
  },
  methods: {
    handleDelete() {
      if (!this.selectedNode) {
        this.$message.warning("请先选择节点");
        return;
      }
      this.$confirm("确定删除该节点吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
        deleteNode({ id: this.selectedNode.id }).then((res) => {
          if (res.code == 200) {
            this.$message.success("删除成功");
            this.reloadData();
          }
        });
      });
    },
    handleEdit() {
      if (!this.selectedNode) {
        this.$message.warning("请先选择节点");
        return;
      }
      const nodeModel = this.selectedNode;
      if (nodeModel.label === "祖代") {
        this.$refs.addAncestor.openInitData({
          ...nodeModel.data,
          formStatus: "edit",
        });
      } else if (nodeModel.label === "母代") {
        nodeModel.data.strainCode1 = this.form.strainCode;
        nodeModel.data.strainName1 = this.form.strainName;
        this.$refs.addSublevelForm.openInitData({
          title: "编辑母代",
          form: { ...nodeModel.data },
          formStatus: "edit",
        });
      } else if (nodeModel.label === "传代计划数") {
        this.$refs.planForm.openInitData({
          ...nodeModel.data,
          formStatus: "edit",
        });
      }
    },
    handleConfirm(row) {
      this.comfirmId = row.id;
      this.storageVisible = true;
    },
    // 处理每页显示数量变化
    handleSizeChange(val) {
      this.size = val;
      this.queryForm.pageSize = val;
      this.getList();
    },
    // 处理页码变化
    handleCurrentChange(val) {
      this.current = val;
      this.queryForm.pageNum = val;
      this.getList();
    },
    async getList() {
      try {
        // 获取列表数据
        const listRes = await pageListDetail({
          id: this.$route.query.id,
          current: this.current,
          size: this.size,
        });
        if (listRes.code === 200) {
          this.tableData = listRes.data.records;
          this.total = listRes.data.total;
          this.current = listRes.data.current;
          this.size = listRes.data.size;
        }
        // ... 其他现有的 reloadData 代码 ...
      } catch (error) {
        console.error("Failed to reload data:", error);
        this.$message.error("数据加载失败");
      }
    },
    async reloadData() {
      this.getList();
      getDetail({ id: this.$route.query.id }).then((res) => {
        const detailData = res.pedigreeChartParentAllDetailDTO;
        // 设置基础表单数据
@@ -221,8 +332,10 @@
          return processedNode;
        }
        // 构建完整的树形结构
        this.graphData.nodes = detailData.one.map((node) => processNode(node));
        // 构建完整的树形结构,添加空值校验
        this.graphData.nodes = detailData?.one
          ? detailData.one.map((node) => processNode(node))
          : [];
        // 在数据加载完成后重新渲染图表
        this.$nextTick(() => {
@@ -298,8 +411,6 @@
            parent: { ...this.form },
            one: processNodeData(this.graphData.nodes),
          };
          console.log("提交数据:", baseData);
          add(baseData).then((res) => {
            if (res.code == 200) {
              this.$message.success("保存成功");
@@ -327,6 +438,17 @@
        this.handleAddSublevel(this.nodeData);
      }
      // 处理提交逻辑
    },
    handleSignatureConfirm1(signatureImage) {
      confirmStorage({
        id: this.comfirmId,
        preserveSignature: signatureImage.signature,
      }).then((res) => {
        if (res.code == 200) {
          this.$message.success("确认入库成功");
          this.reloadData();
        }
      });
    },
    addNode() {
      // 如果没有节点,新增祖代
@@ -388,8 +510,6 @@
        this.showDiscarded = true;
        this.isAddingNode = true;
        console.log(parentNode);
        this.$refs.addSublevelForm.openInitData({
          title: "新增菌种传代项",
          form: {
@@ -404,6 +524,28 @@
      }
    },
    handleAddParent(value) {
      if (value.formStatus == "edit") {
        updateChild(value).then((res) => {
          if (res.code == 200) {
            this.$message.success("操作成功");
            this.$refs.addAncestor.closeDialog();
            this.reloadData();
          }
        });
        return;
      }
      if (this.$route.query.id) {
        value.pedigreeChartId = this.$route.query.id;
        value.parentId = "0";
        addProgenitor(value).then((res) => {
          if (res.code == 200) {
            this.$message.success("操作成功");
            this.$refs.addAncestor.closeDialog();
            this.reloadData();
          }
        });
        return;
      }
      const parentId = `parent-${++this.nodeCount}`;
      const parentNode = {
        id: parentId,
@@ -427,6 +569,16 @@
      this.$refs.form.resetFields();
    },
    handleAddSublevel(value) {
      if (value.formStatus == "edit") {
        updateChild(value).then((res) => {
          if (res.code == 200) {
            this.$message.success("操作成功");
            this.$refs.addSublevelForm.closeDialog();
            this.reloadData();
          }
        });
        return;
      }
      if (this.$route.query.id) {
        addProgenitor(value).then((res) => {
          if (res.code === 200) {
culture/src/views/pedigree-chart/components/AddSublevelForm.vue
@@ -1,215 +1,245 @@
<template>
    <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="40%" @close="closeDialog"
        :close-on-click-modal="false">
        <el-form :model="form" :rules="rules" ref="form" label-position="top">
            <el-row :gutter="20">
                <el-col :span="10">
                    <el-form-item label="接种操作人" prop="thisName">
                        <el-input disabled v-model="form.thisName"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="10">
                    <el-form-item label="接种操作时间" prop="thisTime">
                        <el-input disabled v-model="form.thisTime"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="10">
                    <el-form-item label="传代菌种编号" prop="strainCode">
                        <el-input disabled v-model="form.strainCode"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="10">
                    <el-form-item label="传代菌种名称" prop="strainName">
                        <el-input disabled v-model="form.strainName"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="10">
                    <el-form-item label="接种菌种编号" prop="inoculateNo">
                        <el-input :disabled="!dialogTitle.includes('新增')" v-model="form.inoculateNo"
                            placeholder="请输入"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="10">
                    <el-form-item label="接种菌种名称" prop="inoculateName">
                        <el-input :disabled="!dialogTitle.includes('新增')" v-model="form.inoculateName"
                            placeholder="请输入"></el-input>
                    </el-form-item>
                </el-col>
            </el-row>
            <el-form-item label="保存/废弃">
                <div class="flex-row">
                    <div :class="form.status === 1 && 'active'" @click="handleStatus(1)">保存</div>
                    <div :class="form.status === 2 && 'active'" @click="handleStatus(2)">废弃</div>
                </div>
            </el-form-item>
            <el-row :gutter="20">
                <el-col :span="10">
                    <el-form-item v-if="form.status === 2" label="废弃原因说明" required>
                        <el-input :disabled="!dialogTitle.includes('新增')" v-model="form.discardReason"
                            placeholder="请输入"></el-input>
                    </el-form-item>
                </el-col>
            </el-row>
            <el-row :gutter="20">
                <el-col :span="10">
                    <el-form-item label="菌种入库时间" prop="inTime" required>
                        <el-input disabled v-model="form.inTime"></el-input>
                    </el-form-item>
                </el-col>
            </el-row>
            <!-- <el-row v-if="!dialogTitle.includes('新增')" :gutter="20">
                <el-col :span="10">
                    <el-form-item label="接种操作人签字">
                        <el-image />
                    </el-form-item>
                </el-col>
                <el-col :span="10">
                    <el-form-item label="菌种保藏人签字">
                        <el-image />
                    </el-form-item>
                </el-col>
            </el-row> -->
        </el-form>
        <div v-if="dialogTitle.includes('新增')" class="dialog-footer">
            <el-button type="primary" @click="handleSubmit">提交签字</el-button>
  <el-dialog
    :title="dialogTitle"
    :visible.sync="dialogVisible"
    width="40%"
    @close="closeDialog"
    :close-on-click-modal="false"
  >
    <el-form :model="form" :rules="rules" ref="form" label-position="top">
      <el-row :gutter="20">
        <el-col :span="10">
          <el-form-item label="接种操作人" prop="thisName">
            <el-input disabled v-model="form.thisName"></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="10">
          <el-form-item label="接种操作时间" prop="thisTime">
            <el-input disabled v-model="form.thisTime"></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="10">
          <el-form-item label="传代菌种编号" prop="strainCode1">
            <el-input disabled v-model="form.strainCode1"></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="10">
          <el-form-item label="传代菌种名称" prop="strainName1">
            <el-input disabled v-model="form.strainName1"></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="10">
          <el-form-item label="接种菌种编号" prop="strainCode">
            <el-input
              :disabled="dialogTitle.includes('详情')"
              v-model="form.strainCode"
              placeholder="请输入"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="10">
          <el-form-item label="接种菌种名称" prop="strainName">
            <el-input
              :disabled="dialogTitle.includes('详情')"
              v-model="form.strainName"
              placeholder="请输入"
            ></el-input>
          </el-form-item>
        </el-col>
      </el-row>
      <el-form-item label="保存/废弃">
        <div class="flex-row">
          <div :class="form.status === 1 && 'active'" @click="handleStatus(1)">
            保存
          </div>
          <div :class="form.status === 2 && 'active'" @click="handleStatus(2)">
            废弃
          </div>
        </div>
    </el-dialog>
      </el-form-item>
      <el-row :gutter="20">
        <el-col :span="10">
          <el-form-item v-if="form.status === 2" label="废弃原因说明" required>
            <el-input
              :disabled="dialogTitle.includes('详情')"
              v-model="form.discardReason"
              placeholder="请输入"
            ></el-input>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="20">
        <el-col :span="10">
          <el-form-item label="菌种入库时间" prop="confirmTime" >
            <el-input disabled v-model="form.confirmTime"></el-input>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row v-if="!dialogTitle.includes('新增')" :gutter="20">
        <el-col v-if="form.vaccinateSignature" :span="10">
          <el-form-item label="接种操作人签字">
            <el-image :src="form.vaccinateSignature" />
          </el-form-item>
        </el-col>
        <el-col v-if="form.preserveSignature" :span="10">
          <el-form-item label="菌种保藏人签字">
            <el-image :src="form.preserveSignature" />
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <div v-if="!dialogTitle.includes('详情')" class="dialog-footer">
      <el-button type="primary" @click="handleSubmit">提交签字</el-button>
    </div>
  </el-dialog>
</template>
<script>
export default {
    data() {
        return {
            dialogVisible: false,
            dialogTitle: '',
            form: {
                thisName: '',
                thisTime: '',
                strainCode: '',
                strainName: '',
                inoculateNo: '',
                inoculateName: '',
                status: 1,
                inTime: '',
                discardReason: ''
            },
            rules: {
                thisName: [
                    { required: true, message: '请输入接种操作人', trigger: 'blur' }
                ],
                thisTime: [
                    { required: true, message: '请输入接种操作时间', trigger: 'blur' }
                ],
                strainCode: [
                    { required: true, message: '请输入传代菌种编号', trigger: 'blur' }
                ],
                strainName: [
                    { required: true, message: '请输入传代菌种名称', trigger: 'blur' }
                ],
                inoculateNo: [
                    { required: true, message: '请输入接种菌种编号', trigger: 'blur' }
                ],
                inoculateName: [
                    { required: true, message: '请输入接种菌种名称', trigger: 'blur' }
                ]
            }
        }
  data() {
    return {
      dialogVisible: false,
      dialogTitle: "",
      form: {
        thisName: "",
        thisTime: "",
        strainCode: "",
        strainName: "",
        strainCode1: "",
        strainName1: "",
        status: 1,
        confirmTime: "",
        discardReason: "",
      },
      rules: {
        thisName: [
          { required: true, message: "请输入接种操作人", trigger: "blur" },
        ],
        thisTime: [
          { required: true, message: "请输入接种操作时间", trigger: "blur" },
        ],
        strainCode: [
          { required: true, message: "请输入传代菌种编号", trigger: "blur" },
        ],
        strainName: [
          { required: true, message: "请输入传代菌种名称", trigger: "blur" },
        ],
        strainCode1: [
          { required: true, message: "请输入接种菌种编号", trigger: "blur" },
        ],
        strainName1: [
          { required: true, message: "请输入接种菌种名称", trigger: "blur" },
        ],
      },
    };
  },
  methods: {
    openInitData(value) {
      this.dialogTitle = value.title;
      // 获取用户信息
      const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
      // 获取当前时间并格式化
      const now = new Date();
      const formatTime = `${now.getFullYear()}-${String(
        now.getMonth() + 1
      ).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")} ${String(
        now.getHours()
      ).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}:${String(
        now.getSeconds()
      ).padStart(2, "0")}`;
      console.log(value);
      this.form = {
        parentId: value.parentId,
        ...this.form,
        ...value.form,
        formStatus: value.formStatus,
        strainCode1: value.strainCode1,
        strainName1: value.strainName1,
        thisName: userInfo.nickName || "",
        thisTime: value.form.vaccinateTime
          ? value.form.vaccinateTime
          : formatTime,
      };
      this.dialogVisible = true;
    },
    methods: {
        openInitData(value) {
            this.dialogTitle = value.title
            // 获取用户信息
            const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}')
            // 获取当前时间并格式化
            const now = new Date()
            const formatTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`
            this.form = {
                ...this.form,
                ...value.form,
                thisName: userInfo.nickName || '',
                thisTime: formatTime,
                inTime: formatTime
            }
            this.dialogVisible = true
        },
        closeDialog() {
            this.dialogVisible = false
            // 重置表单数据
            this.form = {
                thisName: '',
                thisTime: '',
                strainCode: '',
                strainName: '',
                inoculateNo: '',
                inoculateName: '',
                status: 1,
                inTime: '',
                discardReason: ''
            }
            // 重置表单验证
            this.$refs.form && this.$refs.form.resetFields()
        },
        handleSubmit() {
            this.$refs.form.validate((valid) => {
                if (valid) {
                    this.$emit('addNodeSign', this.form, 3)
                }
            })
        },
        handleStatus(status) {
            if (!this.dialogTitle.includes('新增')) return
            this.form.status = status
            this.$forceUpdate()
    closeDialog() {
      this.dialogVisible = false;
      // 重置表单数据
      this.form = {
        thisName: "",
        thisTime: "",
        strainCode: "",
        strainName: "",
        strainCode1: "",
        strainName1: "",
        status: 1,
        confirmTime: "",
        discardReason: "",
      };
      // 重置表单验证
      this.$refs.form && this.$refs.form.resetFields();
    },
    handleSubmit() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          this.$emit("addNodeSign", this.form, 3);
        }
    }
}
      });
    },
    handleStatus(status) {
      if (this.dialogTitle.includes("详情")) return;
      this.form.status = status;
      this.$forceUpdate();
    },
  },
};
</script>
<style scoped lang="less">
.dialog-footer {
    margin-top: 39px;
    display: flex;
    justify-content: center;
  margin-top: 39px;
  display: flex;
  justify-content: center;
    .el-button--primary {
        width: 150px;
        height: 40px;
        background: #049C9A;
        border-radius: 4px;
    }
  .el-button--primary {
    width: 150px;
    height: 40px;
    background: #049c9a;
    border-radius: 4px;
  }
}
.flex-row {
    width: 370px;
    display: flex;
    align-items: center;
    font-size: 16px;
    color: #333333;
    padding: 4px;
  width: 370px;
  display: flex;
  align-items: center;
  font-size: 16px;
  color: #333333;
  padding: 4px;
  border-radius: 10px;
  border: 2px solid rgba(4, 156, 154, 0.5);
  font-family: "PingFangSCRegular";
  .flex-row-save {
    background: #049c9a;
    color: #fff;
  }
  div {
    width: 183px;
    height: 32px;
    text-align: center;
    flex-shrink: 0;
    cursor: pointer;
  }
  .active {
    font-family: "SourceHanSansCN-Medium";
    color: #049c9a;
    background: #ebfefd;
    box-shadow: 0px 0px 6px 0px rgba(10, 109, 108, 0.25);
    border-radius: 10px;
    border: 2px solid rgba(4, 156, 154, 0.5);
    font-family: 'PingFangSCRegular';
    .flex-row-save {
        background: #049C9A;
        color: #fff;
    }
    div {
        width: 183px;
        height: 32px;
        text-align: center;
        flex-shrink: 0;
        cursor: pointer;
    }
    .active {
        font-family: 'SourceHanSansCN-Medium';
        color: #049C9A;
        background: #EBFEFD;
        box-shadow: 0px 0px 6px 0px rgba(10, 109, 108, 0.25);
        border-radius: 10px;
    }
  }
}
</style>
culture/src/views/pedigree-chart/components/AddSublevelPlan.vue
@@ -5,23 +5,23 @@
        <el-form :model="planForm" :rules="planRules" ref="planForm" label-position="top">
            <el-row :gutter="20">
                <el-col :span="10">
                    <el-form-item label="传代菌种编号" prop="strainCode">
                        <el-input disabled v-model="planForm.strainCode"></el-input>
                    <el-form-item label="传代菌种编号" prop="strainCode1">
                        <el-input disabled v-model="planForm.strainCode1"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="10">
                    <el-form-item label="传代菌种名称" prop="strainName">
                        <el-input disabled v-model="planForm.strainName"></el-input>
                    <el-form-item label="传代菌种名称" prop="strainName1">
                        <el-input disabled v-model="planForm.strainName1"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="10">
                    <el-form-item label="接种菌种编号" prop="inoculateNo">
                        <el-input disabled v-model="planForm.inoculateNo" placeholder="请输入"></el-input>
                    <el-form-item label="接种菌种编号">
                        <el-input disabled v-model="planForm.strainCode" placeholder="请输入"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="10">
                    <el-form-item label="接种菌种名称" prop="inoculateName">
                        <el-input disabled v-model="planForm.inoculateName" placeholder="请输入"></el-input>
                    <el-form-item label="接种菌种名称">
                        <el-input disabled v-model="planForm.strainName" placeholder="请输入"></el-input>
                    </el-form-item>
                </el-col>
            </el-row>
@@ -38,7 +38,7 @@
            <el-row :gutter="20">
                <el-col :span="10">
                    <el-form-item label="菌种入库时间">
                        <el-input disabled v-model="planForm.inTime"></el-input>
                        <el-input disabled v-model="planForm.confirmTime"></el-input>
                    </el-form-item>
                </el-col>
            </el-row>
@@ -66,11 +66,10 @@
                status: '1',
                strainCode: '',
                strainName: '',
                inoculateNo: '',
                inoculateName: '',
                strainCode1: '',
                strainName1: '',
                generationCount: 1,
                isDiscarded: true,
                inTime: ''
            },
            planRules: {
                strainCode: [
@@ -79,10 +78,10 @@
                strainName: [
                    { required: true, message: '请输入传代菌种名称', trigger: 'blur' }
                ],
                inoculateNo: [
                strainCode1: [
                    { required: true, message: '请输入接种菌种编号', trigger: 'blur' }
                ],
                inoculateName: [
                strainName1: [
                    { required: true, message: '请输入接种菌种名称', trigger: 'blur' }
                ],
                generationCount: [
@@ -109,11 +108,10 @@
                status: '1',
                strainCode: '',
                strainName: '',
                inoculateNo: '',
                inoculateName: '',
                strainCode1: '',
                strainName1: '',
                generationCount: 1,
                isDiscarded: true,
                inTime: ''
            }
            // 重置表单验证
            this.$refs.planForm && this.$refs.planForm.resetFields()
@@ -126,7 +124,7 @@
            })
        },
        handleStatus(status) {
            if (this.planForm.status === 'detail') return
            if (this.planForm.formStatus === 'detail') return
            this.planForm.isDiscarded = status === 'save'
            this.$forceUpdate()
        }
culture/src/views/pedigree-chart/components/ParentForm.vue
@@ -1,195 +1,221 @@
<template>
    <!-- 新增母代弹窗 -->
    <el-dialog :title="planForm.status === 'detail' ? '母代详情' : '新增母代'" :visible.sync="planDialogVisible"
        width="40%" :close-on-click-modal="false">
        <el-form :model="planForm" :rules="planRules" ref="planForm" label-position="top">
            <el-row :gutter="20">
                <el-col :span="24">
                    <el-form-item label="菌种源" prop="strainSourceStart">
                        <div class="input-group">
                            <div class="input-wrapper">
                                <el-input disabled v-model="planForm.strainSourceStart" class="fixed-width-input"></el-input>
                            </div>
                            <span class="form-text">代—</span>
                            <div class="input-wrapper">
                                <el-input disabled v-model="planForm.strainSourceEnd" class="fixed-width-input"></el-input>
                            </div>
                            <span class="form-text">细胞库</span>
                        </div>
                    </el-form-item>
                </el-col>
                <el-col :span="10">
                    <el-form-item label="传代菌种编号" prop="strainCode">
                        <el-input disabled v-model="planForm.strainCode"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="10">
                    <el-form-item label="传代菌种名称" prop="strainName">
                        <el-input disabled v-model="planForm.strainName"></el-input>
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <div v-if="planForm.status !== 'detail'" class="dialog-footer">
            <el-button type="primary" @click="handleSubmit">提交</el-button>
        </div>
    </el-dialog>
  <!-- 新增母代弹窗 -->
  <el-dialog
    :title="planForm.status == 'add' ? '新增母代' : planForm.status == 'edit' ? '编辑母代' : '母代详情'"
    :visible.sync="planDialogVisible"
    width="40%"
    :close-on-click-modal="false"
  >
    <el-form
      :model="planForm"
      :rules="planRules"
      ref="planForm"
      label-position="top"
    >
      <el-row :gutter="20">
        <el-col :span="24">
          <el-form-item label="菌种源" prop="strainSourceStart">
            <div class="input-group">
              <div class="input-wrapper">
                <el-input
                  disabled
                  v-model="planForm.strainSourceStart"
                  class="fixed-width-input"
                ></el-input>
              </div>
              <span class="form-text">代—</span>
              <div class="input-wrapper">
                <el-input
                  disabled
                  v-model="planForm.strainSourceEnd"
                  class="fixed-width-input"
                ></el-input>
              </div>
              <span class="form-text">细胞库</span>
            </div>
          </el-form-item>
        </el-col>
        <el-col :span="10">
          <el-form-item label="传代菌种编号" prop="strainCode">
            <el-input disabled v-model="planForm.strainCode"></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="10">
          <el-form-item label="传代菌种名称" prop="strainName">
            <el-input disabled v-model="planForm.strainName"></el-input>
          </el-form-item>
        </el-col>
        <el-col v-if="planForm.vaccinateSignature && planForm.status == 'detail'" :span="10">
            <el-form-item label="接种操作人签字">
              <el-image :src="planForm.vaccinateSignature" />
            </el-form-item>
          </el-col>
          <el-col v-if="planForm.preserveSignature && planForm.status == 'detail'" :span="10">
            <el-form-item label="菌种保藏人签字">
              <el-image :src="planForm.preserveSignature" />
            </el-form-item>
          </el-col>
      </el-row>
    </el-form>
    <div v-if="planForm.status == 'add'" class="dialog-footer">
      <el-button type="primary" @click="handleSubmit">提交</el-button>
    </div>
  </el-dialog>
</template>
<script>
export default {
    data() {
        return {
            planDialogVisible: false,
            planForm: {
                strainCode: '',
                strainName: '',
                strainSourceStart: '',
                strainSourceEnd: '',
                isDiscarded: true
            },
            planRules: {
                strainCode: [
                    { required: true, message: '请输入传代菌种编号', trigger: 'blur' }
                ],
                strainName: [
                    { required: true, message: '请输入传代菌种名称', trigger: 'blur' }
                ],
                strainSourceStart: [
                    { required: true, message: '请输入菌种源起始', trigger: 'blur' }
                ],
                strainSourceEnd: [
                    { required: true, message: '请输入菌种源结束', trigger: 'blur' }
                ]
            }
        }
  data() {
    return {
      planDialogVisible: false,
      planForm: {
        strainCode: "",
        strainName: "",
        strainSourceStart: "",
        strainSourceEnd: "",
        isDiscarded: true,
      },
      planRules: {
        strainCode: [
          { required: true, message: "请输入传代菌种编号", trigger: "blur" },
        ],
        strainName: [
          { required: true, message: "请输入传代菌种名称", trigger: "blur" },
        ],
        strainSourceStart: [
          { required: true, message: "请输入菌种源起始", trigger: "blur" },
        ],
        strainSourceEnd: [
          { required: true, message: "请输入菌种源结束", trigger: "blur" },
        ],
      },
    };
  },
  methods: {
    openInitData(value) {
      this.planForm = {
        ...this.planForm,
        ...value,
      };
      this.openDialog();
    },
    methods: {
        openInitData(value) {
            this.planForm = {
                ...this.planForm,
                ...value
            }
            this.openDialog()
        },
        openDialog() {
            this.planDialogVisible = true
        },
        closeDialog() {
            this.planDialogVisible = false
            // 重置表单数据
            this.planForm = {
                strainCode: '',
                strainName: '',
                strainSourceStart: '',
                strainSourceEnd: '',
                isDiscarded: true
            }
            // 重置表单验证
            this.$refs.planForm && this.$refs.planForm.resetFields()
        },
        handleSubmit() {
            this.$refs.planForm.validate((valid) => {
                if (valid) {
                    console.log(this.planForm,'22');
                    this.$emit('addNodeSign', this.planForm, 1)
                }
            })
        },
        handleStatus(status) {
            if (this.planForm.status === 'detail') return
            this.planForm.isDiscarded = status === 'save'
            this.$forceUpdate()
    openDialog() {
      this.planDialogVisible = true;
    },
    closeDialog() {
      this.planDialogVisible = false;
      // 重置表单数据
      this.planForm = {
        strainCode: "",
        strainName: "",
        strainSourceStart: "",
        strainSourceEnd: "",
        isDiscarded: true,
      };
      // 重置表单验证
      this.$refs.planForm && this.$refs.planForm.resetFields();
    },
    handleSubmit() {
      this.$refs.planForm.validate((valid) => {
        if (valid) {
          console.log(this.planForm, "22");
          this.$emit("addNodeSign", this.planForm, 1);
        }
    }
}
      });
    },
    handleStatus(status) {
      if (this.planForm.status === "detail") return;
      this.planForm.isDiscarded = status === "save";
      this.$forceUpdate();
    },
  },
};
</script>
<style scoped lang="less">
.input-group {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
    @media (max-width: 768px) {
        flex-direction: column;
        align-items: flex-start;
        width: 100%;
    }
  @media (max-width: 768px) {
    flex-direction: column;
    align-items: flex-start;
    width: 100%;
  }
}
.input-wrapper {
    @media (min-width: 769px) {
        width: 290px;
        min-width: 290px;
    }
  @media (min-width: 769px) {
    width: 290px;
    min-width: 290px;
  }
    @media (max-width: 768px) {
        width: 100%;
    }
  @media (max-width: 768px) {
    width: 100%;
  }
}
.fixed-width-input {
    width: 100%;
  width: 100%;
    @media (min-width: 769px) {
        width: 290px !important;
        min-width: 290px !important;
    }
  @media (min-width: 769px) {
    width: 290px !important;
    min-width: 290px !important;
  }
}
.form-text {
    margin: 0 8px;
    white-space: nowrap;
  margin: 0 8px;
  white-space: nowrap;
    @media (max-width: 768px) {
        margin: 8px 0;
    }
  @media (max-width: 768px) {
    margin: 8px 0;
  }
}
.dialog-footer {
    margin-top: 39px;
    display: flex;
    justify-content: center;
  margin-top: 39px;
  display: flex;
  justify-content: center;
    .el-button--primary {
        width: 150px;
        height: 40px;
        background: #049C9A;
        border-radius: 4px;
    }
  .el-button--primary {
    width: 150px;
    height: 40px;
    background: #049c9a;
    border-radius: 4px;
  }
}
.flex-row {
    width: 370px;
    display: flex;
    align-items: center;
    font-size: 16px;
    color: #333333;
    padding: 4px;
  width: 370px;
  display: flex;
  align-items: center;
  font-size: 16px;
  color: #333333;
  padding: 4px;
  border-radius: 10px;
  border: 2px solid rgba(4, 156, 154, 0.5);
  font-family: "PingFangSCRegular";
  .flex-row-save {
    background: #049c9a;
    color: #fff;
  }
  div {
    width: 183px;
    height: 32px;
    text-align: center;
    flex-shrink: 0;
    cursor: pointer;
  }
  .active {
    font-family: "SourceHanSansCN-Medium";
    color: #049c9a;
    background: #ebfefd;
    box-shadow: 0px 0px 6px 0px rgba(10, 109, 108, 0.25);
    border-radius: 10px;
    border: 2px solid rgba(4, 156, 154, 0.5);
    font-family: 'PingFangSCRegular';
    .flex-row-save {
        background: #049C9A;
        color: #fff;
    }
    div {
        width: 183px;
        height: 32px;
        text-align: center;
        flex-shrink: 0;
        cursor: pointer;
    }
    .active {
        font-family: 'SourceHanSansCN-Medium';
        color: #049C9A;
        background: #EBFEFD;
        box-shadow: 0px 0px 6px 0px rgba(10, 109, 108, 0.25);
        border-radius: 10px;
    }
  }
}
</style>
</style>
culture/src/views/pedigree-chart/components/PlanForm.vue
@@ -1,6 +1,6 @@
<template>
    <!-- 设置传代计划数弹窗 -->
    <el-dialog :title="planForm.status === 'detail' ? '传代计划数详情' : '设置传代计划数'" :visible.sync="planDialogVisible"
    <el-dialog :title="planForm.status == 'add' ? '新增传代计划数' : planForm.status == 'edit' ? '编辑传代计划数' : '传代计划数详情'" :visible.sync="planDialogVisible"
        width="40%" :close-on-click-modal="false">
        <el-form :model="planForm" :rules="planRules" ref="planForm" label-position="top">
            <el-form-item label="菌种源" prop="strainSourceStart">
@@ -69,10 +69,16 @@
    },
    methods: {
        openInitData(value) {
            console.log(value);
            this.planForm = {
                ...this.planForm,
                ...value
            }
            if (value.status == 'edit') {
                this.planForm.strainCode = value.strainCode1
                this.planForm.strainName = value.strainName1
            }
            this.openDialog()
        },
        openDialog() {
culture/src/views/pedigree-chart/index.vue
@@ -57,8 +57,6 @@
          <template slot-scope="scope">
            <el-button type="text" @click="handleDetail(scope.row)">详情</el-button>
            <!-- 菌种超级管理员 -->
            <el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
            <!-- 菌种超级管理员 -->
            <el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
          </template>
        </el-table-column>
@@ -68,7 +66,7 @@
</template>
<script>
import { getList } from "./service";
import { getList,deleteProgenitor } from "./service";
export default {
  name: "PedigreeChart",
  components: {},
@@ -235,6 +233,20 @@
    },
    handleDelete(row) {
      // 实现删除逻辑
      this.$confirm("确定删除该数据吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
        console.log(123);
        deleteProgenitor({ id: row.id }).then((res) => {
          if (res.code == 200) {
            this.$message.success("删除成功");
            this.resetForm();
          }
        });
      });
      console.log("删除数据:", row);
    },
    handleDetail(row) {
culture/src/views/pedigree-chart/progenitorComponents/AddAncestor.vue
@@ -1,7 +1,7 @@
<template>
  <!-- 设置传代计划数弹窗 -->
  <el-dialog
    :title="planForm.formStatus == 'add' ? '新增菌种传代项' : '菌种传代项详情'"
    :title="planForm.formStatus == 'add' ? '新增菌种传代项' : planForm.formStatus == 'edit' ? '编辑菌种传代项' : '菌种传代项详情'"
    :visible.sync="planDialogVisible"
    width="40%"
    :close-on-click-modal="false"
@@ -15,7 +15,7 @@
      <el-row :gutter="20">
        <el-col :span="16">
          <el-form-item label="菌株类型" required>
            <div class="type-box" v-if="planForm.formStatus == 'add'">
            <div class="type-box" v-if="planForm.formStatus != 'detail'">
              <div
                @click="handleType(index)"
                v-for="(item, index) in [
@@ -49,7 +49,7 @@
        <el-col :span="20" v-if="planForm.strainType == 1">
          <el-form-item label="来源获得" prop="source">
            <el-input
              :disabled="planForm.formStatus != 'add'"
              :disabled="planForm.formStatus == 'detail'"
              v-model="planForm.source"
              placeholder="请输入"
            ></el-input>
@@ -58,7 +58,7 @@
        <el-col :span="10" v-else>
          <el-form-item label="菌落编号" prop="colonyNumber">
            <el-input
              :disabled="planForm.formStatus != 'add'"
              :disabled="planForm.formStatus == 'detail'"
              v-model="planForm.colonyNumber"
              placeholder="请输入"
            ></el-input>
@@ -67,7 +67,7 @@
        <el-col :span="10">
          <el-form-item label="菌种名称" prop="strainName">
            <el-input
              :disabled="planForm.formStatus != 'add'"
              :disabled="planForm.formStatus == 'detail'"
              v-model="planForm.strainName"
              placeholder="请输入"
            ></el-input>
@@ -76,7 +76,7 @@
        <el-col :span="10">
          <el-form-item label="菌种编号" prop="strainCode">
            <el-input
              :disabled="planForm.formStatus != 'add'"
              :disabled="planForm.formStatus == 'detail'"
              v-model="planForm.strainCode"
              placeholder="请输入"
            ></el-input>
@@ -86,7 +86,7 @@
      <el-row :gutter="20">
        <el-col :span="10">
          <el-form-item label="保存/废弃" required>
            <div class="flex-row" v-if="planForm.formStatus == 'add'">
            <div class="flex-row" v-if="planForm.formStatus != 'detail'">
              <div
                @click="handleStatus('save')"
                :class="planForm.status === 1 && 'active'"
@@ -110,7 +110,7 @@
        <el-col :span="10">
          <el-form-item label="废弃原因说明" required>
            <el-input
              :disabled="planForm.formStatus != 'add'"
              :disabled="planForm.formStatus == 'detail'"
              v-model="planForm.remark"
              placeholder="请输入"
            ></el-input>
@@ -124,7 +124,7 @@
          </el-form-item>
        </el-col>
      </el-row>
      <el-row v-if="planForm.formStatus != 'add'" :gutter="20">
      <el-row v-if="planForm.formStatus == 'detail'" :gutter="20">
        <el-col v-if="planForm.vaccinateSignature" :span="10">
          <el-form-item label="接种操作人签字">
            <el-image :src="planForm.vaccinateSignature" />
@@ -137,8 +137,7 @@
        </el-col>
      </el-row>
    </el-form>
    <div v-if="planForm.formStatus == 'add'" class="dialog-footer">
      <el-button>保存草稿</el-button>
    <div v-if="planForm.formStatus != 'detail'" class="dialog-footer">
      <el-button type="primary" @click="handleAddPlan">提交</el-button>
    </div>
  </el-dialog>
@@ -208,12 +207,12 @@
      });
    },
    handleStatus(status) {
      if (this.planForm.formStatus !== "add") return;
      if (this.planForm.formStatus === "detail") return;
      this.planForm.status = status === "save" ? 1 : 2;
      this.$forceUpdate();
    },
    handleType(index) {
      if (this.planForm.formStatus !== "add") return;
      if (this.planForm.formStatus === "detail") return;
      this.planForm.strainType = index + 1;
      // 如果是分离菌落或祖代菌株,colonyNumber=source,否则清空
      this.planForm.colonyNumber = "";
culture/src/views/pedigree-chart/progenitorComponents/AddSublevelForm.vue
@@ -31,7 +31,7 @@
        <el-col :span="10">
          <el-form-item label="接种菌种编号" prop="strainCode">
            <el-input
              :disabled="!dialogTitle.includes('新增')"
              :disabled="dialogTitle.includes('详情')"
              v-model="form.strainCode"
              placeholder="请输入"
            ></el-input>
@@ -40,7 +40,7 @@
        <el-col :span="10">
          <el-form-item label="接种菌种名称" prop="strainName">
            <el-input
              :disabled="!dialogTitle.includes('新增')"
              :disabled="dialogTitle.includes('详情')"
              v-model="form.strainName"
              placeholder="请输入"
            ></el-input>
@@ -65,7 +65,7 @@
            prop="remark"
          >
            <el-input
              :disabled="!dialogTitle.includes('新增')"
              :disabled="dialogTitle.includes('详情')"
              v-model="form.remark"
              placeholder="请输入"
            ></el-input>
@@ -92,7 +92,7 @@
        </el-col>
      </el-row>
    </el-form>
    <div v-if="dialogTitle.includes('新增')" class="dialog-footer">
    <div v-if="!dialogTitle.includes('详情')" class="dialog-footer">
      <el-button type="primary" @click="handleSubmit">提交</el-button>
    </div>
  </el-dialog>
@@ -124,7 +124,6 @@
  },
  methods: {
    openInitData(value) {
      console.log(value);
      this.dialogTitle = value.title;
      // 获取用户信息
@@ -142,12 +141,16 @@
      this.form = {
        ...this.form,
        ...value.form,
        strainCode1: value.strainCode1,
        strainName1: value.strainName1,
        formStatus:value.formStatus,
        thisName: userInfo.nickName || "",
        thisTime: value.form.vaccinateTime
          ? value.form.vaccinateTime
          : formatTime,
        type: 1,
      };
      this.dialogVisible = true;
    },
    closeDialog() {
@@ -161,7 +164,7 @@
      });
    },
    handleStatus(status) {
      if (!this.dialogTitle.includes("新增")) return;
      if (this.dialogTitle.includes("详情")) return;
      this.form.status = status;
      this.$forceUpdate();
    },
culture/src/views/pedigree-chart/service.js
@@ -28,4 +28,28 @@
// 祖代-母代新增菌种传代项
export const addProgenitor = (params) => {
  return axios.post('/api/t-pedigree-chart/addProgenitorChild', { ...params })
}
}
// 谱系图-获取谱系图详情待入库菌种分页列表
export const pageListDetail = (params) => {
  return axios.post('/api/t-pedigree-chart/pageListDetail', { ...params })
}
// 谱系图-菌种工程师确认入库
export const confirmStorage = (params) => {
  return axios.post('/api/t-pedigree-chart/confirm', { ...params })
}
// 谱系图-删除节点
export const deleteNode = (params) => {
  return axios.delete('/open/t-pedigree-chart/deleteProgenitorChild', { params })
}
// 谱系图-编辑菌种传代-传代数项
export const updateChild = (data) => {
  return axios.post('/api/t-pedigree-chart/updateChild', { ...data })
}
// 谱系图-删除谱系图
export const deleteProgenitor = (params) => {
  return axios.delete('/open/t-pedigree-chart/deleteProgenitor', { params })
}
culture/src/views/system/operation-log/index.vue
@@ -1,7 +1,7 @@
<template>
  <div class="operationLog">
    <TableCustom :queryForm="queryForm" :tableData="list" :total="total"
      @currentChange="handleCurrentChange" @sizeChange="handleSizeChange">
    <TableCustom :tableData="list" :total="total" :heigth="null"
      @handleCurrentChange="handleCurrentChange" @handleSizeChange="handleSizeChange">
      <template #table>
        <el-table-column label="序号" type="index" show-overflow-tooltip />
        <el-table-column label="操作时间" prop="operTime" show-overflow-tooltip />
@@ -13,26 +13,22 @@
</template>
<script>
// import { getOperlogList } from '@/api/systemSettings'
import { getList } from './service'
export default {
  name: 'operationLog',
  data() {
    return {
      list: [],
      total: 0,
      listLoading: false,
      queryForm: {
        pageNum: 1,
        pageSize: 10,
      },
        pageSize: 10
      }
    }
  },
  computed: {
    height() {
      return this.$baseTableHeight()
    },
  },
  created() {
    // this.fetchData()
    this.fetchData()
  },
  methods: {
    handleSizeChange(val) {
@@ -44,12 +40,24 @@
      this.fetchData()
    },
    async fetchData() {
      this.listLoading = true
      const { data } = await getOperlogList(this.queryForm)
      this.list = data.data.records
      this.total = data.data.total
    },
  },
      try {
        this.listLoading = true
        getList(this.queryForm).then(res => {
          if (res) {
            this.list = res.records
            this.total = res.total
          } else {
            this.$message.error('获取数据失败')
          }
        })
      } catch (error) {
        console.error('获取操作日志列表失败:', error)
        this.$message.error('获取操作日志列表失败')
      } finally {
        this.listLoading = false
      }
    }
  }
}
</script>
<style scoped lang="less">
culture/src/views/system/operation-log/service.js
New file
@@ -0,0 +1,6 @@
import axios from '@/utils/request';
// 列表
export const getList = (data) => {
  return axios.post('/monitor/operlog/list', { ...data })
}
laboratory/package.json
@@ -12,6 +12,7 @@
  "dependencies": {
    "@tinymce/tinymce-vue": "^3.2.8",
    "aieditor": "^1.3.6",
    "ant-design-vue": "^1.7.8",
    "axios": "^0.24.0",
    "core-js": "^3.41.0",
    "element-ui": "^2.15.6",
laboratory/src/assets/login/iconBg.png
laboratory/src/assets/login/img5.png
laboratory/src/assets/login/img6.png
laboratory/src/assets/login/img7.png
laboratory/src/assets/login/img8.png
laboratory/src/main.js
@@ -1,6 +1,8 @@
import Vue from "vue";
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
import '@/assets/font/font.css'
import App from "./App.vue";
import router from "./router";
@@ -15,8 +17,11 @@
import './assets/tailwind.css'
import './styles/element-variables.less'
Vue.config.productionTip = false;
Vue.use(ElementUI, { size: 'small' })
Vue.use(Antd)
Vue.component('Table', Table)
Vue.component('TableCustom', TableCustom)
Vue.component('Card', Card)
laboratory/src/views/middleground/index.vue
@@ -1,29 +1,26 @@
<template>
  <div class="login-page">
    <div class="top-nav">
      <HeaderNav class="header-main" :logo="true" />
      <HeaderNav class="header-main" :logo="'true'" />
    </div>
    <div class="middleground" :class="windowWidth<1240 ? 'column' : ''">
    <div class="middleground" :class="{
      'column': windowWidth < 1240,
      'mobile': windowWidth < 800
    }">
      <!-- 左侧模块区域 -->
      <div class="left-modules">
        <!-- 这里将放置四个模块 -->
        <div class="module-item">
          <!-- 模块内容,例如图标和文字 -->
          <div class="module-icon"></div>
          <div class="module-text">实验室运行模块</div>
        </div>
        <div class="module-item">
          <div class="module-icon"></div>
          <div class="module-text">专业报告库</div>
        </div>
        <div class="module-item">
          <div class="module-icon"></div>
          <div class="module-text">化验师QA专题报告库</div>
        </div>
        <div class="module-item">
          <div class="module-icon"></div>
          <div class="module-text">评定模块</div>
      <div class="left-modules" :class="[currentModuleLayout, {
        'mobile-layout': windowWidth < 800
      }]">
        <div class="module-item" v-for="(item, index) in filteredModuleList" :key="index"
          @click="handleModuleClick(item)">
          <div class="module-bg"></div>
          <div class="module-content">
            <div class="icon-wrapper">
              <div class="module-icon" :style="{ backgroundImage: `url(${item.icon})` }"></div>
            </div>
            <div class="module-text">{{ item.text }}</div>
          </div>
        </div>
      </div>
@@ -31,8 +28,8 @@
      <div class="right-content">
        <!-- 日历 -->
        <div class="calendar-section">
          <!-- <h3>日历</h3> -->
          <el-calendar v-model="date" />
          <div class="title-canlender">日历</div>
          <a-calendar @panelChange="onPanelChange" />
        </div>
        <!-- 待办事项 -->
@@ -69,7 +66,6 @@
<script>
import { loginReq } from './service'
import HeaderNav from '../../layouts/components/HeaderNav.vue'
import { mapState } from 'vuex'
// 引入 Element UI 的日历组件
// import { ElCalendar } from 'element-ui';
export default {
@@ -89,12 +85,139 @@
      },
      date: new Date(),
      viewWidth: '',
      scale: 1
      scale: 1,
      // 审批人
      moduleList2: [
      {
          text: '实验室运行模块',
          icon: require('../../assets/login/img1.png'),
          path: '/dataManagement'
        },
        {
          text: '专业报告库',
          icon: require('../../assets/login/img2.png'),
          path: '/reportLibrary'
        },
        {
          text: '化验师提交',
          icon: require('../../assets/login/img7.png'),
          path: '/chemistQa'
        },
        {
          text: '评定模块',
          icon: require('../../assets/login/img4.png'),
          path: '/deliveryAssessment'
        }
      ],
      // 工艺工程师
      moduleList3: [
        {
          text: '实验室运行模块',
          icon: require('../../assets/login/img1.png'),
          path: '/dataManagement'
        },
        {
          text: '评定模块',
          icon: require('../../assets/login/img4.png'),
          path: '/deliveryAssessment'
        },
        {
          text: '专业报告库',
          icon: require('../../assets/login/img2.png'),
          path: '/reportLibrary'
        },
        {
          text: '化验师QA专题报告库',
          icon: require('../../assets/login/img7.png'),
          path: '/chemistQa'
        }
      ],
      // 化验师
      moduleList4: [
        {
          text: '实验室运行模块',
          icon: require('../../assets/login/img1.png'),
          path: '/dataManagement'
        },
        {
          text: '评定模块',
          icon: require('../../assets/login/img4.png'),
          path: '/deliveryAssessment'
        },
        {
          text: '专业报告库',
          icon: require('../../assets/login/img2.png'),
          path: '/reportLibrary'
        },
        {
          text: '化验师提交',
          icon: require('../../assets/login/img7.png'),
          path: '/chemistQa'
        }
      ],
      // 实验员
      moduleList5: [
        {
          text: '实验室运行模块',
          icon: require('../../assets/login/img1.png'),
          path: '/dataManagement'
        },
        {
          text: '菌种库',
          icon: require('../../assets/login/img8.png'),
          path: '/projectList'
        },
        {
          text: '专业报告库',
          icon: require('../../assets/login/img2.png'),
          path: '/reportLibrary'
        },
        {
          text: '化验师提交',
          icon: require('../../assets/login/img3.png'),
          path: '/chemistQa'
        },
        {
          text: '评定模块',
          icon: require('../../assets/login/img4.png'),
          path: '/deliveryAssessment'
        },
      ],
      // 超级管理员
      moduleList6: [
        {
          text: '实验室运行模块',
          icon: require('../../assets/login/img1.png'),
          path: '/dataManagement'
        },
        {
          text: '评定模块',
          icon: require('../../assets/login/img4.png'),
          path: '/deliveryAssessment'
        },
        {
          text: '项目组管理',
          icon: require('../../assets/login/img5.png'),
          path: '/projectList'
        },
        {
          text: '专业报告库',
          icon: require('../../assets/login/img2.png'),
          path: '/reportLibrary'
        },
        {
          text: '化验师提交',
          icon: require('../../assets/login/img7.png'),
          path: '/chemistQa'
        },
        {
          text: '系统管理',
          icon: require('../../assets/login/img6.png'),
          path: '/system'
        }
      ]
    }
  },
  // computed: {},
  computed: {
    ...mapState(['tagList', 'isFold'])
  },
  created() {
@@ -102,43 +225,71 @@
    this.handleResize();
  },
  mounted() {
     // 监听窗口大小变化
     window.addEventListener("resize", this.handleResize);
   },
  methods: {
    // 监听窗口大小变化
    window.addEventListener("resize", this.handleResize);
    handleResize() {
      // if (window.innerWidth < 1000) {
      //   this.$store.commit("SET_ISFOLD", true);
      // } else if (window.innerWidth >= 1000 && this.isFold) {
      //   this.$store.commit("SET_ISFOLD", false);
      // }
      this.windowWidth = window.innerWidth;
    },
    // ...mapActions(['setIsFold']),
    handleKeyDown(event) {
      if (event.key === 'Enter') {
        this.login()
      }
  },
  beforeDestroy() {
    // 组件销毁前移除事件监听
    window.removeEventListener("resize", this.handleResize);
  },
  methods: {
    onPanelChange(e) {
      console.log('eeeeeee', e)
    },
    // 添加处理窗口大小变化的方法
    handleResize() {
      this.viewWidth = window.innerWidth
      this.windowWidth = window.innerWidth;
    },
    login() {
      if (this.loginForm.username == '') {
        this.$message.warning('请输入账号')
        return
      }
      if (this.loginForm.password == '') {
        this.$message.warning('请输入密码')
        return
      }
      loginReq(this.loginForm).then(res => {
        sessionStorage.setItem('token', res.token)
        sessionStorage.setItem('userInfo', JSON.stringify(res.userInfo.user))
        this.$router.push('/system')
    handleModuleClick(item) {
     if(item.path){
      this.$router.push({
        path: item.path,
      })
     }
    },
  },
  computed: {
    currentModuleList() {
      const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
      const userType = userInfo.userType;
      switch (userType) {
        case 2: // 审批人
          return this.moduleList2;
        case 3: // 工艺工程师
          return this.moduleList3;
        case 4: // 化验师
          return this.moduleList4;
        case 5: // 实验员
          return this.moduleList5;
        case 6: // 超级管理员
          return this.moduleList6;
        default:
          return this.moduleList6; // 默认返回化验师的模块列表
      }
    },
    filteredModuleList() {
      return this.currentModuleList.filter(item => item);
    },
    currentModuleLayout() {
      const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
      const userType = userInfo.userType;
      switch (userType) {
        case 2: // 审批人
          return 'layout-4';
        case 3: // 工艺工程师
          return 'layout-4';
        case 4: // 化验师
          return 'layout-4';
        case 5: // 实验员
          return 'layout-5';
        case 6: // 超级管理员
          return 'layout-6';
        default:
          return 'layout-6'; // 默认返回化验师的布局
      }
    }
  }
}
@@ -186,329 +337,268 @@
  .middleground {
    flex: 1;
    height: calc(100% - 70px);
    display: flex;
    // flex-wrap: wrap;
    justify-content: flex-start;
    /* 左侧内容靠左对齐 */
    padding: 20px 20px 20px 108px;
    /* 调整左右内边距 */
    justify-content: center;
    margin: 0px 20px 0px 108px;
    gap: 40px;
    /* 添加左右间距 */
    box-sizing: border-box;
    .left-modules {
      align-items: center;
      padding: 40px 0;
      display: flex;
      flex-wrap: wrap;
      box-sizing: content-box;
      justify-content: center;
      /* 模块之间水平分散对齐 */
      /* 水平居中 */
      align-items: flex-start;
      /* 模块垂直靠上对齐 */
      /* 垂直居中 */
      flex-grow: 1;
      /* 左侧宽度自适应 */
      align-content: flex-start;
      /* 多行模块垂直靠上对齐 */
      gap: 20px;
      /* 添加模块之间的垂直间距 */
      display: grid;
      gap: 40px;
      padding-right: 20px;
      /* 给左侧模块区域右侧添加一些间距,避免紧贴右侧内容 */
      width: 100%;
      max-width: 1200px;
      margin: 0 auto;
      align-self: center;
      justify-self: center;
      &.mobile-layout {
        grid-template-columns: repeat(2, 1fr);
        gap: 15px;
        padding-right: 0;
        max-width: 100%;
        .module-item {
          width: 100%;
          height: 307px;
        }
      }
      // 4个模块布局(2x2)
      &.layout-4 {
        grid-template-columns: repeat(2, 260px);
        grid-template-rows: repeat(2, 260px);
        justify-content: center;
      }
      // 5个模块布局(2+3)
      &.layout-5 {
        grid-template-columns: repeat(3, 240px);
        grid-template-rows: repeat(2, 240px);
        justify-content: center;
        position: relative;
        // 上面两个模块整体居中
        .module-item:nth-child(1),
        .module-item:nth-child(2) {
          transform: translateX(120px);
        }
        // 下面三个模块
        .module-item:nth-child(3) {
          grid-column: 1;
        }
        .module-item:nth-child(4) {
          grid-column: 2;
        }
        .module-item:nth-child(5) {
          grid-column: 3;
        }
        &.mobile-layout {
          grid-template-columns: repeat(2, 1fr);
          grid-template-rows: auto;
          .module-item:nth-child(1),
          .module-item:nth-child(2) {
            transform: none;
          }
        }
      }
      // 6个模块布局(3x2)
      &.layout-6 {
        grid-template-columns: repeat(3, 220px);
        grid-template-rows: repeat(2, 220px);
        justify-content: center;
        &.mobile-layout {
          grid-template-columns: repeat(2, 1fr);
          grid-template-rows: auto;
        }
      }
    }
    .module-item {
      // width: 37%;
      // height: 30%;
      width: 307px;
      height: 307px;
      background-color: red;
      position: relative;
      width: 100%;
      height: 100%;
      cursor: pointer;
      transition: all 0.3s ease;
      overflow: hidden;
    }
    .module-bg {
      position: absolute;
      top: 75px;
      left: 0;
      width: 100%;
      height: calc(100% - 75px);
      background: url('../../assets/login/cardBg.png') no-repeat center;
      background-size: cover;
      transition: transform 0.3s ease;
      z-index: 1;
    }
    .module-content {
      position: relative;
      height: 100%;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: space-between;
      background: url('../../assets/login/cardBg.png');
      /* 设置模块背景图 */
      background-size: cover;
      /* 背景图覆盖整个元素 */
      background-position: center top;
      /* 调整背景图位置:顶部居中 */
      z-index: 2;
    }
      .module-icon {
        width: 80px;
        height: 80px;
        // background-color: #eee; /* Placeholder for icon */
        z-index: 1;
        /* 确保图标在背景之上 */
        background-size: contain;
        /* 图标背景图包含在元素内 */
        background-repeat: no-repeat;
        /* 不重复 */
        background-position: 100% 100%;
        /* 图标背景图居中 */
    .icon-wrapper {
      width: 100%;
      height: 150px;
      display: flex;
      justify-content: center;
      align-items: center;
      background: url('../../assets/login/iconBg.png') no-repeat center;
      background-size: 100% 100%;
      z-index: 2;
      &::before {
        content: '';
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 40px;
        background: linear-gradient(to bottom, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0) 100%);
        backdrop-filter: blur(4px);
        -webkit-backdrop-filter: blur(4px);
        pointer-events: none;
        z-index: 2;
      }
      /* 为每个模块图标设置特定的背景图片 */
      &:nth-child(1) .module-icon {
        background-image: url('../../assets/login/img1.png');
      }
      &:nth-child(2) .module-icon {
        background-image: url('../../assets/login/img2.png');
      }
      &:nth-child(3) .module-icon {
        background-image: url('../../assets/login/img3.png');
      }
      &:nth-child(4) .module-icon {
        background-image: url('../../assets/login/img4.png');
      }
      .module-text {
        font-size: 16px;
        font-weight: bold;
        position: relative;
        /* 为下划线定位 */
        z-index: 1;
        /* 确保文字在背景之上 */
        margin-bottom: 50px;
        &::after {
          content: '';
          position: absolute;
          left: 50%;
          /* 从文字中间开始 */
          bottom: -5px;
          /* 调整下划线位置 */
          transform: translateX(-50%);
          /* 使下划线居中 */
          width: 50%;
          /* 调整下划线宽度 */
          height: 2px;
          /* 调整下划线粗细 */
          background-color: #007bff;
          /* 示例下划线颜色 */
        }
      }
      &:nth-of-type(2n+1) {
        margin-right: 0;
      }
      &:nth-child(odd) {
        margin-right: 0px;
      &::after {
        content: '';
        position: absolute;
        left: 0;
        bottom: 0;
        width: 100%;
        height: 40px;
        background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 100%);
        backdrop-filter: blur(4px);
        -webkit-backdrop-filter: blur(4px);
        pointer-events: none;
        z-index: 2;
      }
    }
    .right-content {
      width: 870px;
      /* 固定右侧宽度 */
      flex-shrink: 0;
      /* 防止右侧被压缩 */
      padding: 20px;
      border-radius: 8px;
      // background-color: #fff;
      // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    .module-icon {
      width: 100px;
      height: 100px;
      background-size: contain;
      background-repeat: no-repeat;
      background-position: center;
      transition: all 0.3s ease;
      position: relative;
      z-index: 3;
    }
      display: flex;
      /* 使日历和待办事项垂直排列 */
      flex-direction: column;
    .module-text {
      position: relative;
      font-size: 16px;
      font-weight: bold;
      color: #333;
      margin-top: 35px;
      text-align: center;
      z-index: 3;
      .calendar-section {
        margin-bottom: 20px;
        box-sizing: border-box;
        /* 日历下方间距 */
        height: 595px;
        padding: 20px;
        border-radius: 14px;
        background: url(../../assets/login/rili.png) no-repeat center;
      &::after {
        content: '';
        position: absolute;
        left: 50%;
        bottom: -5px;
        transform: translateX(-50%);
        width: 50%;
        height: 2px;
        background-color: #007bff;
      }
    }
        h3 {
          margin-top: 0;
          margin-bottom: 10px;
          font-size: 18px;
          color: #333;
        }
        .calendar {
          height: 100% !important;
          width: 100% !important;
          box-sizing: border-box;
        }
        /* 尝试调整 Element UI 日历的样式 */
        ::v-deep .el-calendar {
          height: 100%;
          width: 100%;
          background: none !important;
          box-sizing: border-box;
          .el-calendar__header {
            padding: 12px 0;
            box-sizing: border-box;
          }
          .el-calendar__body {
            padding: 0;
          }
          .el-calendar-table {
            thead th {
              padding: 5px 0;
              box-sizing: border-box;
              font-weight: normal;
            }
            tbody td {
              border: none;
              .el-calendar-day {
                /* 调整日期单元格高度 */
                display: flex;
                justify-content: center;
                align-items: center;
                border-radius: 4px;
                box-sizing: border-box;
                /* 圆角 */
                margin: 8px;
                /* 单元格间距 */
                cursor: pointer;
                // max-width: 90px;
                max-height: 61px;
                background: #FFFFFF;
                border-radius: 8px;
                border: 1px solid #EDEDED;
                &:hover {
                  background-color: #f0f0f0;
                }
              }
              /* 当前日期样式 */
              &.is-selected .el-calendar-day {
                background-color: #007bff;
                /* 示例选中颜色 */
                color: #fff;
              }
            }
          }
        }
    // 根据布局调整图标和文字大小
    .layout-5 & {
      .module-icon {
        width: 80px;
        height: 80px;
      }
      .todo-list-section {
        flex: 1;
        border-radius: 14px;
        background-color: rgba(255, 255, 255, 1);
        // overflow: auto;
        padding: 24px 34px;
        display: flex;
        flex-direction: column;
      .module-text {
        font-size: 15px;
        margin-top: 30px;
      }
    }
        .title {
          height: 22px;
          font-family: SourceHanSansCN, SourceHanSansCN;
          font-weight: bold;
          font-size: 18px;
          color: #222222;
          line-height: 22px;
          margin-bottom: 20px;
    .layout-6 & {
      .module-icon {
        width: 70px;
        height: 70px;
      }
      .module-text {
        font-size: 14px;
        margin-top: 25px;
      }
    }
    // 响应式调整
    @media screen and (max-width: 1240px) {
      .module-item {
        .module-bg {
          top: 60px;
          height: calc(100% - 60px);
        }
        .todo-list {
          flex: 1;
          height: 100%;
          overflow: auto;
        .icon-wrapper {
          height: 120px;
          &::before,
          &::after {
            height: 35px;
          }
        }
        /* 待办事项列表样式 */
        .todo-item {
          display: flex;
          align-items: center;
          justify-content: space-between;
          margin-bottom: 20PX;
        .module-icon {
          width: 90px;
          height: 90px;
        }
          /* 分隔线 */
          &:first-child {
            margin-top: 3px;
        .module-text {
          margin-top: 30px;
        }
      }
    }
    @media screen and (max-width: 900px) {
      .module-item {
        .module-bg {
          top: 50px;
          height: calc(100% - 50px);
        }
        .icon-wrapper {
          height: 100px;
          &::before,
          &::after {
            height: 30px;
          }
        }
          .todo-details {
            display: flex;
            align-content: center;
          }
        .module-icon {
          width: 80px;
          height: 80px;
        }
          .notice-card {
            position: relative;
            margin-right: 12px;
          }
          .todo-icon {
            position: relative;
            width: 24px;
            height: 24px;
            flex-shrink: 0;
            background: url('../../assets//login/notice.png') no-repeat center;
            background-position: center;
            background-size: 100% 100%;
          }
          .red-notice {
            position: absolute;
            top: -3px;
            right: -3px;
            width: 6px;
            height: 6px;
            border-radius: 50%;
            background: rgba(235, 65, 65, 1);
          }
          .todo-title {
            font-family: SourceHanSansCN, SourceHanSansCN;
            font-weight: 500;
            font-size: 14px;
            color: #303133;
            line-height: 24px;
          }
          .todo-meta {
            display: flex;
            align-items: center;
          }
          .time {
            width: 14px;
            height: 14px;
            margin-right: 6px;
            margin-left: 20px;
            flex-shrink: 0;
            background: url('../../assets//login/time.png') no-repeat center;
            background-position: center;
            background-size: 100% 100%;
          }
          .me {
            width: 14px;
            height: 14px;
            margin-right: 6px;
            flex-shrink: 0;
            background: url('../../assets//login/mi.png') no-repeat center;
            background-position: center;
            background-size: 100% 100%;
          }
          .todo-submitter {
            font-family: SourceHanSansCN, SourceHanSansCN;
            font-weight: 400;
            font-size: 12px;
            color: #909399;
            // line-height: 18px;
          }
        .module-text {
          margin-top: 25px;
        }
      }
    }
@@ -516,14 +606,21 @@
  .column {
    flex-direction: column;
    /* 改为上下布局 */
    padding: 20px;
    /* 调整内边距 */
    gap: 20px;
    /* 调整上下间距 */
    height: auto;
    /* 高度自适应内容 */
    padding: 20px 20px 20px 18px !important;
    align-items: center;
    margin: 0 !important;
    .left-modules {
      // width: 100%;
      // display: flex;
      // flex-wrap: wrap;
      // justify-content: center;
      // gap: 20px;
      // margin-bottom: 20px;
    }
  }
  // Tablet layout
@@ -531,44 +628,410 @@
    .login-page {
      .middleground {
        flex-direction: column;
        /* 改为上下布局 */
        padding: 20px;
        /* 调整内边距 */
        gap: 20px;
        /* 调整上下间距 */
        height: auto;
        /* 高度自适应内容 */
        padding: 20px 20px 20px 18px !important;
        .left-modules {
          width: 100%;
          /* 左侧宽度占满 */
          justify-content: center;
          /* 在上下布局中水平居中模块 */
          padding: 0;
          /* 移除 padding */
          gap: 20px;
          /* 上下布局时模块之间的间距 */
          padding-right: 0;
          /* 移除右侧间距 */
        }
        .module-item {
          width: 307px;
          /* 模块宽度固定 */
          height: 307px;
          /* 模块高度固定 */
          margin-right: 0 !important;
          /* 移除桌面布局的右侧间距 */
        }
        .right-content {
          width: 100%;
          /* 右侧宽度占满 */
          height: auto;
          /* 高度自适应 */
          padding: 15px;
          /* 调整内边距 */
          .calendar-section {
            height: auto;
            min-height: 400px;
            padding: 15px;
            ::v-deep .ant-fullcalendar-fullscreen {
              .ant-fullcalendar-table {
                tbody td {
                  padding: 4px;
                  .ant-fullcalendar-date {
                    height: 40px;
                  }
                }
              }
            }
          }
          .todo-list-section {
            padding: 15px;
          }
        }
      }
    }
  }
  .mobile {
    margin: 0;
    padding: 10px;
    margin: 0 !important;
    .left-modules {
      width: 100%;
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: 15px;
      margin-bottom: 15px;
      padding: 0;
      .module-item {
        width: calc(50% - 15px);
        max-width: 307px;
        height: 307px;
        margin: 0;
        box-sizing: border-box;
      }
    }
    .right-content {
      width: 100%;
      padding: 10px;
      box-sizing: border-box;
      .calendar-section {
        height: auto;
        min-height: 400px;
        padding: 10px;
        margin-bottom: 15px;
        ::v-deep .ant-fullcalendar-fullscreen {
          .ant-fullcalendar-table {
            tbody td {
              padding: 4px;
              .ant-fullcalendar-date {
                height: 40px;
              }
            }
          }
        }
      }
      .todo-list-section {
        padding: 10px;
      }
    }
  }
  .right-content {
    width: 870px;
    /* 固定右侧宽度 */
    flex-shrink: 0;
    /* 防止右侧被压缩 */
    padding: 20px;
    border-radius: 8px;
    // background-color: #fff;
    // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    display: flex;
    /* 使日历和待办事项垂直排列 */
    flex-direction: column;
    .calendar-section {
      margin-bottom: 20px;
      box-sizing: border-box;
      height: 595px;
      padding: 20px;
      border-radius: 14px;
      background: url(../../assets/login/rili.png) no-repeat center;
      .title-canlender {
        height: 22px;
        font-family: SourceHanSansCN, SourceHanSansCN;
        font-weight: bold;
        font-size: 18px;
        color: #222222;
        line-height: 22px;
        margin-bottom: 20px;
      }
      /* 调整 ant-design-vue 日历的样式 */
      ::v-deep .ant-fullcalendar-fullscreen {
        background: none !important;
        border: none !important;
        padding: 0 !important;
        .ant-fullcalendar-header {
          .ant-radio-group {
            .ant-radio-button-wrapper {
              background: #FFFFFF;
              margin: 0 4px;
              color: #666;
              &:hover {
                color: #009688;
              }
              &.ant-radio-button-wrapper-checked {
                background-color: #009688 !important;
                border-color: #009688 !important;
                color: #FFFFFF !important;
                box-shadow: none !important;
              }
            }
          }
          .ant-select {
            .ant-select-selection {
              background: #FFFFFF;
              border: 1px solid #EDEDED;
              border-radius: 8px;
              color: #666;
              &:hover {
                border-color: #009688;
              }
              .ant-select-selection__rendered {
                line-height: 32px;
              }
            }
            &.ant-select-open .ant-select-selection {
              border-color: #009688;
              box-shadow: none;
            }
          }
          .ant-select-dropdown {
            .ant-select-dropdown-menu {
              background: #FFFFFF;
              border: 1px solid #EDEDED;
              border-radius: 8px;
              box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
              .ant-select-dropdown-menu-item {
                color: #666;
                &:hover {
                  background-color: #f0f0f0;
                }
                &.ant-select-dropdown-menu-item-selected {
                  background-color: #009688 !important;
                  color: #FFFFFF !important;
                }
              }
            }
          }
        }
        .ant-fullcalendar-table {
          thead th {
            padding: 5px 0;
            font-weight: normal;
            color: #666;
            text-align: center;
            .ant-fullcalendar-column-header {
              text-align: center;
            }
          }
          tbody td {
            border: none;
            padding: 8px;
            .ant-fullcalendar-date {
              margin: 0;
              padding: 0;
              height: 55px;
              background: #FFFFFF;
              border-radius: 8px;
              border: 1px solid #EDEDED;
              display: flex;
              justify-content: center;
              align-items: center;
              &:hover {
                background-color: rgba(4, 156, 154, .1);
              }
              .ant-fullcalendar-value {
                color: #666;
              }
            }
            &.ant-fullcalendar-selected-day .ant-fullcalendar-date {
              // background-color: #009688 !important;
              border-color: #009688 !important;
              color: rgba(4, 156, 154, 1);
              .ant-fullcalendar-value {
                color: rgba(4, 156, 154, 1) !important;
              }
            }
            &.ant-fullcalendar-today .ant-fullcalendar-date {
              // border-color: #009688;
            }
          }
        }
        .ant-fullcalendar-month-panel-selected-cell {
          .ant-fullcalendar-month {
            border-top-color: #009688 !important;
            background: rgba(4, 156, 154, .2);
            .ant-fullcalendar-value {
              color: #FFFFFF;
            }
            :hover {
              .ant-fullcalendar-month {
                background: rgba(4, 156, 154, .2);
              }
            }
          }
        }
        .ant-fullcalendar-month-panel-current-cell {
          .ant-fullcalendar-month {
            border-top-color: #009688 !important;
            :hover {
              .ant-fullcalendar-month {
                background: rgba(4, 156, 154, .2);
              }
            }
          }
        }
        .ant-fullcalendar-month-panel-cell {
          .ant-fullcalendar-month:hover {
            background: rgba(4, 156, 154, .2);
          }
        }
      }
    }
    .todo-list-section {
      flex: 1;
      border-radius: 14px;
      background-color: rgba(255, 255, 255, 1);
      // overflow: auto;
      padding: 24px 34px;
      display: flex;
      flex-direction: column;
      .title {
        height: 22px;
        font-family: SourceHanSansCN, SourceHanSansCN;
        font-weight: bold;
        font-size: 18px;
        color: #222222;
        line-height: 22px;
        margin-bottom: 20px;
      }
      .todo-list {
        flex: 1;
        height: 100%;
        overflow: auto;
      }
      /* 待办事项列表样式 */
      .todo-item {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 20PX;
        /* 分隔线 */
        &:first-child {
          margin-top: 3px;
        }
        .todo-details {
          display: flex;
          align-content: center;
        }
        .notice-card {
          position: relative;
          margin-right: 12px;
        }
        .todo-icon {
          position: relative;
          width: 24px;
          height: 24px;
          flex-shrink: 0;
          background: url('../../assets//login/notice.png') no-repeat center;
          background-position: center;
          background-size: 100% 100%;
        }
        .red-notice {
          position: absolute;
          top: -3px;
          right: -3px;
          width: 6px;
          height: 6px;
          border-radius: 50%;
          background: rgba(235, 65, 65, 1);
        }
        .todo-title {
          font-family: SourceHanSansCN, SourceHanSansCN;
          font-weight: 500;
          font-size: 14px;
          color: #303133;
          line-height: 24px;
        }
        .todo-meta {
          display: flex;
          align-items: center;
        }
        .time {
          width: 14px;
          height: 14px;
          margin-right: 6px;
          margin-left: 20px;
          flex-shrink: 0;
          background: url('../../assets//login/time.png') no-repeat center;
          background-position: center;
          background-size: 100% 100%;
        }
        .me {
          width: 14px;
          height: 14px;
          margin-right: 6px;
          flex-shrink: 0;
          background: url('../../assets//login/mi.png') no-repeat center;
          background-position: center;
          background-size: 100% 100%;
        }
        .todo-submitter {
          font-family: SourceHanSansCN, SourceHanSansCN;
          font-weight: 400;
          font-size: 12px;
          color: #909399;
          // line-height: 18px;
        }
      }
    }
laboratory/src/views/system/operation-log/index.vue
@@ -1,7 +1,7 @@
<template>
  <div class="operationLog">
    <TableCustom :queryForm="queryForm" :tableData="list" :total="total"
      @currentChange="handleCurrentChange" @sizeChange="handleSizeChange">
    <TableCustom :tableData="list" :total="total" :heigth="null"
      @handleCurrentChange="handleCurrentChange" @handleSizeChange="handleSizeChange">
      <template #table>
        <el-table-column label="序号" type="index" show-overflow-tooltip />
        <el-table-column label="操作时间" prop="operTime" show-overflow-tooltip />
@@ -13,26 +13,22 @@
</template>
<script>
// import { getOperlogList } from '@/api/systemSettings'
import { getList } from './service'
export default {
  name: 'operationLog',
  data() {
    return {
      list: [],
      total: 0,
      listLoading: false,
      queryForm: {
        pageNum: 1,
        pageSize: 10,
      },
        pageSize: 10
      }
    }
  },
  computed: {
    height() {
      return this.$baseTableHeight()
    },
  },
  created() {
    // this.fetchData()
    this.fetchData()
  },
  methods: {
    handleSizeChange(val) {
@@ -44,12 +40,24 @@
      this.fetchData()
    },
    async fetchData() {
      this.listLoading = true
      const { data } = await getOperlogList(this.queryForm)
      this.list = data.data.records
      this.total = data.data.total
    },
  },
      try {
        this.listLoading = true
        getList(this.queryForm).then(res => {
          if (res) {
            this.list = res.records
            this.total = res.total
          } else {
            this.$message.error('获取数据失败')
          }
        })
      } catch (error) {
        console.error('获取操作日志列表失败:', error)
        this.$message.error('获取操作日志列表失败')
      } finally {
        this.listLoading = false
      }
    }
  }
}
</script>
<style scoped lang="less">
laboratory/src/views/system/operation-log/service.js
New file
@@ -0,0 +1,6 @@
import axios from '@/utils/request';
// 列表
export const getList = (data) => {
  return axios.post('/monitor/operlog/list', { ...data })
}