Эх сурвалжийг харах

Merge branch 'iscs_dev_mars' of https://e.coding.net/g-pheu9517/grt-iscs/ISCS_Ui_Admin_Base into iscs_dev_mars

wangyani 8 сар өмнө
parent
commit
670394a525

+ 63 - 0
src/api/mes/material/doorException.js

@@ -0,0 +1,63 @@
+import request from '@/utils/request'
+
+// 查询物资信息--分页 /dev-api/iscs/materials/getIsMaterialsPage
+export function MaterialsLoanExceptionPage(query) {
+  return request({
+    url: '/iscs/exception/getIsMaterialsLoanExceptionPage',
+    method: 'get',
+    params: query
+  })
+}
+
+// 新增物资信息 /dev-api/iscs/materials/insertIsMaterials
+export function addMaterials(data) {
+  return request({
+    url: '/iscs/materials/insertIsMaterials',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改物资信息 /dev-api/iscs/materials/updateIsMaterials
+export function updateMaterials(data) {
+  return request({
+    url: '/iscs/materials/updateIsMaterials',
+    method: 'post',
+    data: data
+  })
+}
+
+// 删除物资信息 /dev-api/iscs/materials/deleteIsMaterialsByMaterialsIds
+export function deleteMaterials(data) {
+  return request({
+    url: '/iscs/materials/deleteIsMaterialsByMaterialsIds?materialsIds=' + data,
+    method: 'post',
+  })
+}
+
+// 获取物资详细信息 /dev-api/iscs/materials/selectIsMaterialsById
+export function selectMaterialsById(query) {
+  return request({
+    url: '/iscs/materials/selectIsMaterialsById?materialsId=' + query,
+    method: 'get',
+  })
+}
+
+
+// 物资所属柜 查询物资柜-分页
+export function getIsMaterialsCabinets() {
+  return request({
+    url: '/iscs/cabinet/getIsMaterialsCabinetPage',
+    method: 'get',
+  })
+}
+//绑定物资
+export function updateMaterialsBinding(data) {
+  return request({
+    url: '/iscs/materials/updateMaterialsBinding',
+    method: 'post',
+    data: data
+  })
+}
+
+

+ 82 - 32
src/api/mes/material/information.js

@@ -2,55 +2,56 @@ import request from '@/utils/request'
 
 // 查询物资信息--分页 /dev-api/iscs/materials/getIsMaterialsPage
 export function listMaterials(query) {
-    return request({
-        url: '/iscs/materials/getIsMaterialsPage',
-        method: 'get',
-        params: query
-    })
+  return request({
+    url: '/iscs/materials/getIsMaterialsPage',
+    method: 'get',
+    params: query
+  })
 }
 
 // 新增物资信息 /dev-api/iscs/materials/insertIsMaterials
 export function addMaterials(data) {
-    return request({
-        url: '/iscs/materials/insertIsMaterials',
-        method: 'post',
-        data: data
-    })
+  return request({
+    url: '/iscs/materials/insertIsMaterials',
+    method: 'post',
+    data: data
+  })
 }
 
 // 修改物资信息 /dev-api/iscs/materials/updateIsMaterials
 export function updateMaterials(data) {
-    return request({
-        url: '/iscs/materials/updateIsMaterials',
-        method: 'post',
-        data: data
-    })
+  return request({
+    url: '/iscs/materials/updateIsMaterials',
+    method: 'post',
+    data: data
+  })
 }
 
 // 删除物资信息 /dev-api/iscs/materials/deleteIsMaterialsByMaterialsIds
 export function deleteMaterials(data) {
-    return request({
-        url: '/iscs/materials/deleteIsMaterialsByMaterialsIds?materialsIds=' + data,
-        method: 'post',
-    })
+  return request({
+    url: '/iscs/materials/deleteIsMaterialsByMaterialsIds?materialsIds=' + data,
+    method: 'post'
+  })
 }
 
 // 获取物资详细信息 /dev-api/iscs/materials/selectIsMaterialsById
 export function selectMaterialsById(query) {
-    return request({
-        url: '/iscs/materials/selectIsMaterialsById?materialsId=' + query,
-        method: 'get',
-    })
+  return request({
+    url: '/iscs/materials/selectIsMaterialsById?materialsId=' + query,
+    method: 'get'
+  })
 }
 
-
 // 物资所属柜 查询物资柜-分页
-export function getIsMaterialsCabinets() {
-    return request({
-        url: '/iscs/cabinet/getIsMaterialsCabinetPage',
-        method: 'get',
-    })
+export function getIsMaterialsCabinets(query) {
+  return request({
+    url: '/iscs/cabinet/getIsMaterialsCabinetPage',
+    method: 'get',
+    params: query
+  })
 }
+
 //绑定物资
 export function updateMaterialsBinding(data) {
   return request({
@@ -59,6 +60,7 @@ export function updateMaterialsBinding(data) {
     data: data
   })
 }
+
 ///dev-api/iscs/materials/updateIsMaterialById 借出/归还物资(借出参数materialsId-loanState-loanUserId)(归还参数materialsId-loanState-restitutionToId-restitutionUserId)
 //物资借出与归还
 export function updateIsMaterialById(data) {
@@ -66,10 +68,11 @@ export function updateIsMaterialById(data) {
     url: '/iscs/materials/updateIsMaterialById',
     method: 'post',
     data: {
-      list:data
+      list: data
     }
   })
 }
+
 //物资绑定与解绑
 export function insertIsMaterialsChangeRecord(data) {
   return request({
@@ -78,16 +81,27 @@ export function insertIsMaterialsChangeRecord(data) {
     data: data
   })
 }
+
+//物资 绑定物资-物资柜绑定/解绑物资(物资更换,多对多)
+export function updateMaterialsBindingRemove(data) {
+  return request({
+    url: '/iscs/cabinet/updateMaterialsBindingRemove',
+    method: 'post',
+    data: data
+  })
+}
+
 // 物资检查记录模拟 新增
 export function insertIsMaterialsCheckRecord(data) {
   return request({
     url: '/iscs/check/insertIsMaterialsCheckRecord',
     method: 'post',
-    data:{
-      list:data
+    data: {
+      list: data
     }
   })
 }
+
 //查询异常还错柜子的物资
 export function getExMaterials(query) {
   return request({
@@ -96,3 +110,39 @@ export function getExMaterials(query) {
     params: query
   })
 }
+
+//手动更换1 表单物资分类 与数据更换信息·
+export function selectExMaterialTypeById(data) {
+  return request({
+    url: '/iscs/hardware/material-api/selectExMaterialTypeById',
+    method: 'get',
+    params: data
+  })
+}
+
+///dev-api/iscs/hardware/material-api/insertReplaceRecord  手动更换1的确认更换函数
+export function insertReplaceRecord(data) {
+  return request({
+    url: '/iscs/hardware/material-api/insertReplaceRecord',
+    method: 'post',
+    data: {
+      list: data
+    }
+  })
+}
+//新增物资柜开门超时异常 /dev-api/iscs/hardware/material-api/insertCabinetOpenTimeout
+export function insertCabinetOpenTimeout(data) {
+  return request({
+    url: '/iscs/hardware/material-api/insertCabinetOpenTimeout',
+    method: 'post',
+    data: data
+  })
+}
+//解除物资柜开门超时异常 /dev-api/iscs/hardware/material-api/updateCabinetOpenTimeout
+export function updateCabinetOpenTimeout(data) {
+  return request({
+    url: '/iscs/hardware/material-api/updateCabinetOpenTimeout',
+    method: 'post',
+    data: data
+  })
+}

+ 1 - 0
src/views/mes/material/Exception/index.vue

@@ -182,6 +182,7 @@ export default {
       queryParams: {
         current: 1,
         size: 10,
+        exceptionType:'0',
         loanFromId:'',
         materialsName:'',
         restitutionUserName:'',

+ 6 - 1
src/views/mes/material/Inspectionrecords/index.vue

@@ -214,7 +214,8 @@
       </el-table-column>
       <el-table-column label="异常原因" align="center" prop="reason">
         <template slot-scope="scope">
-          <dict-tag :options="dict.type.exceptions_status" :value="scope.row.reason"/>
+          <dict-tag :options="dict.type.exceptions_status" :value="scope.row.reason" v-if="scope.row.reason!=='0'"/>
+          <span v-else>-</span>
         </template>
       </el-table-column>
       <el-table-column
@@ -222,6 +223,10 @@
         align="center"
         prop="measure"
       >
+        <template slot-scope="scope">
+         <span v-if="scope.row.measure">{{scope.row.measure}}</span>
+          <span v-else>-</span>
+        </template>
       </el-table-column>
       <el-table-column label="更换记录" align="center" prop="status">
         <template slot-scope="scope">

+ 1 - 0
src/views/mes/material/Lending/demo3.vue

@@ -73,6 +73,7 @@ export default {
     }
   },
   mounted() {
+    // 我是手动更换 一换一 柜外未绑定 柜内绑定 进行交换
     this.$nextTick(() => {
       this.initKonva()
       this.inputStatus = '当前柜门已关闭'

+ 2 - 1
src/views/mes/material/Lending/demo4.vue

@@ -192,7 +192,8 @@ export default {
         materialsRfid: item.materialsRfid,
         status: item.status,
         reason: item.reason,
-        measure: item.measure
+        measure: item.measure,
+        cabinetId: item.materialsCabinetId
       }))
 
       // 输出最终的数据(此处可以进行实际的提交操作)

+ 343 - 0
src/views/mes/material/Lending/demo5.vue

@@ -0,0 +1,343 @@
+<template>
+  <div class="mapBox">
+    <div style="width:400px;height: 350px;border: 1px solid black;margin: 10px;position: absolute;z-index: 1;top:0">
+      <h3 style="padding-left: 10px">操作流程:</h3>
+      <div style="display: flex;padding-left: 10px">
+        <h4>1.选择物资柜</h4>
+        <el-select
+          style="width: 215px;margin: 10px 20px;"
+          v-model="queryParams.materialsCabinetId"
+          placeholder="请选择物资柜"
+          @change="handleCabinetChange"
+        >
+          <el-option
+            v-for="dict in cabinets"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </div>
+      <div style="display: flex;padding-left: 10px">
+        <h4>2.</h4>
+        <el-button @click="changeDoor(true)" style="width:60px;height:30px;padding:5px 8px;margin: 12px 10px">开柜门
+        </el-button>
+      </div>
+      <div style="display: flex;padding-left: 10px">
+        <h4>3.取物资/还物资</h4>
+        <!--        <el-input v-model="inputStatus" style="width:150px"></el-input>-->
+      </div>
+      <div style="display: flex;padding-left: 10px">
+        <h4>4.</h4>
+        <el-button :disabled="!doorOpen" @click="changeDoor(false)"
+                   style="width:60px;height:30px;padding:5px 8px;margin: 12px 10px"
+        >关柜门
+        </el-button>
+      </div>
+
+    </div>
+    <div id="container" ref="container" style="width: 260vh; height: 90vh; background-color: white;"></div>
+  </div>
+</template>
+
+<script>
+import Konva from 'konva'
+import {
+  getIsMaterialsCabinets,
+  listMaterials,
+  selectMaterialsById,
+  updateIsMaterialById, insertIsMaterialsChangeRecord, updateMaterialsBindingRemove
+} from '@/api/mes/material/information'
+
+export default {
+  name: 'Lending',
+  data() {
+    return {
+      stage: null,
+      layer: null,
+      bgLayer: null,
+      list: [], // 三号柜子的物资
+      listInCabinet: [], // 存储物资柜内的物资
+      listOutOfCabinet: [], // 存储物资柜外的物资
+      cabinets: [], // 物资所属柜
+      doorOpen: false,
+      queryParams: {
+        current: 1,
+        size: -1,
+        materialsCabinetId: null,
+        loanState: null
+      },
+      inputStatus: '当前柜门已关闭',//柜门开关状态
+      materialElements: [], // 存储物资图标对象
+      unbindMaterialIds: [], // 批量增加的绑定id
+      bindingMaterialIds: []//批量解除绑定的id
+    }
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.initKonva()
+      this.inputStatus = '当前柜门已关闭'
+      this.materialsCabinets()
+    })
+  },
+  methods: {
+    changeDoor(value) {
+      this.doorOpen = value
+      if (this.doorOpen) {
+        this.$message.success('柜门已打开')
+        this.updateDraggableStatus()
+        this.inputStatus = '当前柜门已打开'
+      } else {
+        this.$message.info('柜门已关闭')
+        this.inputStatus = '当前柜门已关闭'
+        this.updateMaterialsBatch() // 柜门关闭时调用批量更新接口
+      }
+    },
+    // 获取物资柜信息
+    materialsCabinets() {
+      getIsMaterialsCabinets(this.queryParams).then((response) => {
+        if (response?.data?.records) {
+          this.cabinets = response.data.records.map((item) => ({
+            value: item.cabinetId,
+            label: item.cabinetName
+          }))
+        }
+      })
+    },
+    handleCabinetChange() {
+      // 清空之前渲染的物资元素
+      this.clearMaterialsFromLayer()
+
+      // 获取柜内和柜外物资
+      Promise.all([this.getMaterialsInCabinet(), this.getMaterialsOutOfCabinet()])
+        .then(() => {
+          // 在所有数据获取完成后,统一渲染物资
+          this.addMaterialsToLayer()
+        })
+    },
+
+    getMaterialsInCabinet() {
+      this.queryParams.loanState = '1' // 设置查询条件为物资柜内
+      return listMaterials(this.queryParams).then((res) => {
+        this.listInCabinet = res.data.records
+      })
+    },
+
+    getMaterialsOutOfCabinet() {
+      this.queryParams.loanState = '0' // 设置查询条件为物资柜外
+      const data = {
+        current: 1,
+        size: -1,
+        loanState: '0'
+        // materialsCabinetId: '0',
+      }
+      return listMaterials(data).then((res) => {
+        this.listOutOfCabinet = res.data.records.filter((item) => {
+          return item.materialsCabinetId == null || item.materialsCabinetId == '0'
+        })
+        console.log(this.listOutOfCabinet, '柜子外的数据demo3')
+      })
+    },
+    // 初始化Konva舞台
+    initKonva() {
+      this.stage = new Konva.Stage({
+        container: this.$refs.container,
+        width: 1600,
+        height: 1000
+      })
+
+      this.bgLayer = new Konva.Layer()
+      const bgImage = new Image()
+      bgImage.src = require('@/assets/images/table.png')
+      bgImage.onload = () => {
+        const konvaImage = new Konva.Image({
+          x: 600,
+          y: 25,
+          image: bgImage,
+          width: 500,
+          height: 700,
+          draggable: false
+        })
+        this.bgLayer.add(konvaImage)
+        this.bgLayer.draw()
+      }
+      this.stage.add(this.bgLayer)
+    },
+
+    // 清除Konva图层中的所有物资元素
+    clearMaterialsFromLayer() {
+      if (!this.bgLayer) return
+      const materialNodes = this.bgLayer.find('.material')
+      console.log('清除物资:', materialNodes)
+      materialNodes.forEach(node => node.destroy()) // 清除已渲染的物资元素
+      this.materialElements = [] // 清空已存储的物资图标对象
+      this.bgLayer.draw() // 重新绘制图层
+    },
+
+    // 物资拖动结束时的处理
+    handleDragEnd(e) {
+      const materialId = e.target.getAttr('materialId') // 获取物资ID
+      const position = e.target.getClientRect() // 获取物资当前的位置信息
+      const material = e.target.getAttr('material') // 获取物资对象
+      console.log(material, '物资移动')
+      const isInCabinet = material.inCabinet // 物资是否在柜子内
+      if (position.x < 600 || position.x > 1100 || position.y < 25 || position.y > 725) {
+        // **物资移出柜子**
+        if (isInCabinet) {
+          console.log('1--------')
+          this.unbindMaterialIds.push(materialId)
+
+          material.inCabinet = false // 更新状态
+        }
+      } else {
+        // **物资归还到柜子**
+        console.log('2--------')
+        // **物资从外部归还**
+        this.bindingMaterialIds.push(materialId)
+        material.inCabinet = true
+      }
+    },
+
+    // 将物资添加到Konva图层
+    addMaterialsToLayer() {
+      if (!this.bgLayer) return
+
+      const itemWidth = 60
+      const itemHeight = 60
+      const spacing = 40
+
+      // 清除之前的物资元素
+      this.clearMaterialsFromLayer()
+
+      // 渲染柜内物资
+      this.listInCabinet.forEach((material, index) => {
+        const x = 720 + (index % 3) * (itemWidth + spacing)
+        const y = 120 + Math.floor(index / 3) * (itemHeight + spacing)
+
+        const materialImage = new Image()
+        materialImage.src = material.materialsTypeIcon
+        materialImage.onload = () => {
+          const group = new Konva.Group({
+            x: x,
+            y: y,
+            draggable: this.doorOpen, // 初始时根据 doorOpen 状态设置可拖拽
+            name: 'material',
+            materialId: material.materialsId,
+            material: material
+          })
+
+          const konvaMaterialImage = new Konva.Image({
+            image: materialImage,
+            width: itemWidth,
+            height: itemHeight
+          })
+
+          const text = new Konva.Text({
+            x: 0,
+            y: itemHeight + 5,
+            text: material.materialsName,
+            fontSize: 12,
+            fill: 'black',
+            align: 'center',
+            width: itemWidth
+          })
+
+          group.add(konvaMaterialImage)
+          group.add(text)
+
+          // 给物资设置初始的柜内状态
+          material.inCabinet = true  // 设置物资最初处于柜子内
+
+          group.on('dragend', this.handleDragEnd) // 添加dragend事件监听
+
+          this.bgLayer.add(group)
+          this.bgLayer.draw()
+          this.materialElements.push(group)
+        }
+      })
+
+      // 渲染柜外物资
+      this.listOutOfCabinet.forEach((material, index) => {
+        const x = 1200 + (index % 3) * (itemWidth + spacing) // 左侧显示柜外物资
+        const y = 120 + Math.floor(index / 3) * (itemHeight + spacing)
+
+        const materialImage = new Image()
+        materialImage.src = material.materialsTypeIcon
+        materialImage.onload = () => {
+          const group = new Konva.Group({
+            x: x,
+            y: y,
+            draggable: this.doorOpen, // 初始时设为可拖拽
+            name: 'material',
+            materialId: material.materialsId,
+            material: material
+          })
+
+          const konvaMaterialImage = new Konva.Image({
+            image: materialImage,
+            width: itemWidth,
+            height: itemHeight
+          })
+
+          const text = new Konva.Text({
+            x: 0,
+            y: itemHeight + 5,
+            text: material.materialsName,
+            fontSize: 12,
+            fill: 'black',
+            align: 'center',
+            width: itemWidth
+          })
+
+          group.add(konvaMaterialImage)
+          group.add(text)
+
+          // 给物资设置初始的柜外状态
+          material.inCabinet = false  // 设置物资最初处于柜子外
+
+          group.on('dragend', this.handleDragEnd) // 添加dragend事件监听
+
+          this.bgLayer.add(group)
+          this.bgLayer.draw()
+          this.materialElements.push(group)
+        }
+      })
+    },
+
+// 监听 doorOpen 状态变化时更新物资的 draggable 状态
+    updateDraggableStatus() {
+      this.materialElements.forEach(group => {
+        group.draggable(this.doorOpen)  // 动态更新 draggable 状态
+      })
+      this.bgLayer.draw()
+    },
+
+    // 批量更新物资状态
+    updateMaterialsBatch() {
+      if (!this.bindingMaterialIds && !this.unbindMaterialIds) return // 如果没有需要更新的物资,直接返回
+      const data = {
+        bindingMaterialIds: this.bindingMaterialIds,
+        cabinetId: this.queryParams.materialsCabinetId,
+        unbindMaterialIds: this.unbindMaterialIds
+      }
+      updateMaterialsBindingRemove(data).then(response => {
+        if (response.data) {
+          this.$message.success('物资更新成功')
+          this.unbindMaterialIds = [] // 清空需要更新的物资列表
+          this.bindingMaterialIds = []
+        } else {
+          this.$message.error('物资更新失败')
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.mapBox {
+  width: 100%;
+  height: 100%;
+  //background-color: pink;
+}
+</style>

+ 402 - 0
src/views/mes/material/Lending/demo6.vue

@@ -0,0 +1,402 @@
+<template>
+  <div class="box">
+    <el-select
+      style="width: 215px;margin: 10px 20px;"
+      v-model="queryParams.materialsCabinetId"
+      placeholder="请选择物资柜"
+      @change="handleCabinetChange"
+    >
+      <el-option
+        v-for="dict in cabinets"
+        :key="dict.value"
+        :label="dict.label"
+        :value="dict.value"
+      />
+    </el-select>
+    <el-tabs style="padding: 10px;" v-model="activeName" @tab-click="handleClick">
+      <!-- 动态渲染 Tab Pane -->
+      <el-tab-pane v-for="(item, index) in allData"
+                   :key="item.materialsTypeId"
+                   :label="item.materialsTypeName + ' (' + item.exNumber + ')'"
+                   :name="item.materialsTypeName"
+      >
+
+        <div class="allBox">
+          <!-- 循环渲染每组旧物资和新物资 -->
+          <div style="width: 600px;height: 380px;display: flex;flex-wrap: wrap;margin: 10px"
+               v-for="(materialPair, idx) in item.materials" :key="'pair' + idx"
+          >
+            <!-- 旧物资 -->
+            <div class="oldBox">
+              <div class="old1">旧物资</div>
+              <div class="old2"><img :src="materialPair.oldMaterial.materialsTypePicture" alt=""></div>
+              <div class="old3">名 称:
+                <el-input disabled style="width: 217px" type="text" v-model="materialPair.oldMaterial.materialsName"
+                ></el-input>
+              </div>
+              <div class="old4" style="display: flex;">类 型:
+                <Treeselect
+                  disabled
+                  style="width: 220px;margin-left: 4px"
+                  v-model="materialPair.oldMaterial.materialsTypeId"
+                  :options="materialstypeOptions"
+                  :normalizer="normalizer"
+                  placeholder="请选择物资类型"
+
+                />
+              </div>
+              <div class="old5">型 号:
+                <el-select disabled v-model="materialPair.oldMaterial.propertiesValueId" placeholder="请选择型号"
+                           @click.native="handleChangeValue(materialPair.oldMaterial)"
+                >
+                  <el-option
+                    v-for="option in PropertyValueList"
+                    :key="option.value"
+                    :label="option.label"
+                    :value="option.value"
+                  >
+                  </el-option>
+                </el-select>
+              </div>
+              <div class="old6">RFID:
+                <el-input disabled style="width: 217px" type="text" v-model="materialPair.oldMaterial.materialsRfid"
+                ></el-input>
+              </div>
+              <div class="old7" style="margin-left: 4px">有效期:
+                <el-date-picker disabled clearable
+                                style="width: 217px;"
+                                v-model="materialPair.oldMaterial.expirationDate"
+                                type="date"
+                                value-format="yyyy-MM-dd"
+                                placeholder="请选择库存有效期"
+                >
+                </el-date-picker>
+              </div>
+            </div>
+
+            <!-- 新物资 -->
+            <div class="newBox">
+              <div class="new1">新物资</div>
+              <div class="new2"><img :src="materialPair.newMaterial.materialsTypePicture" alt=""></div>
+              <div class="new3">名 称:
+                <el-input :disabled="materialPair.newMaterial.isLocked"  style="width: 217px" type="text" v-model="materialPair.newMaterial.materialsName"></el-input>
+              </div>
+              <div class="new4" style="display: flex">类 型:
+                <Treeselect
+                  @input="onMaterialTypeChange(materialPair.newMaterial.materialsTypeId,materialPair)"
+                  :disabled="materialPair.newMaterial.isLocked"
+                  style="width: 220px;margin-left: 4px;"
+                  v-model="materialPair.newMaterial.materialsTypeId"
+                  :options="materialstypeOptions"
+                  :normalizer="normalizer"
+                  placeholder="请选择物资类型"
+                />
+              </div>
+              <div class="new5">型 号:
+                <el-select :disabled="materialPair.newMaterial.isLocked"  v-model="materialPair.newMaterial.propertiesValueId" placeholder="请选择型号"
+                           @click.native="handleChangeValue(materialPair.newMaterial.materialsTypeId)"
+                >
+                  <el-option
+                    v-for="option in PropertyValueList"
+                    :key="option.value"
+                    :label="option.label"
+                    :value="option.value"
+                  >
+                  </el-option>
+                </el-select>
+              </div>
+              <div class="new6">RFID:
+                <el-input :disabled="materialPair.newMaterial.isLocked"  style="width: 217px" type="text" v-model="materialPair.newMaterial.materialsRfid"></el-input>
+              </div>
+              <div class="new7" style="margin-left: 4px">有效期:
+                <el-date-picker :disabled="materialPair.newMaterial.isLocked" clearable
+                                style="width: 217px"
+                                v-model="materialPair.newMaterial.expirationDate"
+                                type="date"
+                                value-format="yyyy-MM-dd"
+                                placeholder="请选择库存有效期"
+                >
+                </el-date-picker>
+              </div>
+            </div>
+            <div class="allBoxBtn">
+              <el-button type="danger" v-if="!materialPair.newMaterial.isLocked" class="Btn" @click="confirm(materialPair)" >确定更换</el-button>
+              <el-button type="info" v-else  class="Btn" @click="confirm(materialPair)">重新填写</el-button>
+            </div>
+          </div>
+
+        </div>
+        <el-button type="primary" @click="submit">提交</el-button>
+      </el-tab-pane>
+    </el-tabs>
+
+
+  </div>
+</template>
+<script>
+import { getIsMaterialsCabinets, insertReplaceRecord, selectExMaterialTypeById } from '@/api/mes/material/information'
+import { PropertyValuePage } from '@/api/mes/standard/propertyForm'
+import { listType, selectMaterialsTypeById } from '@/api/mes/material/typeindex'
+import Treeselect from '@riophae/vue-treeselect'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+
+
+export default {
+  name: 'demo6',
+  components: { Treeselect },
+  data() {
+    return {
+      cabinets: [],//物资柜数据
+      queryParams: {
+        current: 1,
+        size: -1,
+        materialsCabinetId: '',
+        propertyId: ''//型号的对应值
+      },
+      materialPair: {
+        newMaterial: {
+          materialsTypePicture: "",
+          materialsName: "",
+          materialsTypeId: null,
+          propertiesValueId: null,
+          materialsRfid: "",
+          expirationDate: null,
+          isLocked: false
+        }
+      },
+      PropertyValueList: [],
+      allData: null,//全部类型数据
+      activeName: null,
+      materialstypeOptions: null//物资类型
+    }
+  },
+
+  mounted() {
+    this.materialsCabinets()
+    this.getTreeselect()
+  },
+  methods: {
+    onMaterialTypeChange(val,materialPair){
+    console.log(val,'拿到了')
+      selectMaterialsTypeById(val).then((response) => {
+        console.log(response,'拿到对应物资类型详细信息')
+        const data = {
+          pageSize: 99999999,
+          pageNum: 1,
+          propertyId: response.data.propertyIds,
+        };
+        // 更新物资图片
+        materialPair.newMaterial.materialsTypePicture = response.data.materialsTypePicture;
+        materialPair.newMaterial.propertyId=response.data.propertyIds
+        materialPair.newMaterial.propertiesValueId=null
+      });
+    },
+    handleClick(tab, event) {
+      console.log(tab, event)
+    },
+    // 获取物资柜信息
+    materialsCabinets() {
+      getIsMaterialsCabinets(this.queryParams).then((response) => {
+        if (response?.data?.records) {
+          this.cabinets = response.data.records.map((item) => ({
+            value: item.cabinetId,
+            label: item.cabinetName
+          }))
+        }
+      })
+      PropertyValuePage(this.queryParams).then((response) => {
+        console.log(response, '获取物资说明 ')
+        this.PropertyValueList = response.data.records.map((item) => ({
+          value: item.recordId,
+          label: item.valueName
+        }))
+      })
+
+    },
+    handleChangeValue(val) {
+      console.log(val, '拿到的全部下拉之')
+
+      selectMaterialsTypeById(val).then((response) => {
+        console.log(response,'拿到的对应类型详细信息')
+        this.queryParams.propertyId = response.data.propertyIds
+        PropertyValuePage(this.queryParams).then((response) => {
+          console.log(response, '获取物资说明 ')
+          this.PropertyValueList = response.data.records.map((item) => ({
+            value: item.recordId,
+            label: item.valueName
+          }))
+        })
+      })
+
+    },
+    /** 转换数据结构 */
+    normalizer(node) {
+      if (node.children && !node.children.length) {
+        delete node.children
+      }
+      return {
+        id: node.materialsTypeId,
+        label: node.materialsTypeName,
+        children: node.children
+      }
+    },
+    /** 查询物资类型下拉树结构 */
+    getTreeselect() {
+      const data = {
+        current: 1,
+        size: -1
+      }
+
+      listType(data).then((response) => {
+        this.materialstypeOptions = this.handleTree(
+          response.data.records,
+          'materialsTypeId',
+          'parentId',
+          'children'
+        )
+      })
+    },
+    handleCabinetChange() {
+      const data = {
+        cabinetId: this.queryParams.materialsCabinetId
+      }
+      selectExMaterialTypeById(data).then((response) => {
+        console.log(response, '获取到的类型总数居')
+        this.allData = response.data
+        this.allData.forEach(item => {
+          item.materials = item.materials.map(material => {
+            return {
+              oldMaterial: { ...material }, // 复制一份原始的旧物资数据
+              newMaterial: { ...material,isLocked:false } // 复制一份新的物资数据,初始与旧物资相同
+            }
+          })
+        })
+      })
+    },
+
+    //   确认更换
+    confirm(material) {
+      material.newMaterial.isLocked = !material.newMaterial.isLocked; // 锁定新物资
+    // this.submit()
+    },
+    submit(){
+      const tabData = this.allData.find(item => item.materialsTypeName === this.activeName);
+      if (!tabData) {
+        console.error('Tab data not found');
+        return;
+      }
+
+      const data = tabData.materials.map(materialPair => ({
+        expirationDate: materialPair.newMaterial.expirationDate,
+        materialsName: materialPair.newMaterial.materialsName,
+        materialsRfid: materialPair.newMaterial.materialsRfid,
+        materialsTypeId: materialPair.newMaterial.materialsTypeId,
+        oldMaterialsId: materialPair.oldMaterial.materialsId,
+        propertiesProperty: materialPair.newMaterial.propertiesProperty,
+        propertiesPropertyId: materialPair.newMaterial.propertiesPropertyId,
+        propertiesValue: materialPair.newMaterial.propertiesValue,
+        propertiesValueId: materialPair.newMaterial.propertiesValueId
+      }));
+      console.log(data, '确认呢更换参数')
+      insertReplaceRecord(data).then(response => {
+        console.log(response, '确认更换接口返回值')
+        if (response.data) {
+          this.$message.success('更换成功')
+        }
+      })
+    }
+
+  }
+
+}
+</script>
+<style scoped lang="scss">
+.allBox {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  //background: yellow;
+  flex-wrap: wrap;
+}
+
+.oldBox {
+  width: 300px;
+  height: 320px;
+  //background: pink;
+  border: 1px solid black;
+}
+
+.old1 {
+  //background: yellow;
+  text-align: center;
+  border: 1px solid black;
+  border-top: none;
+  border-left: none;
+  border-right: none;
+}
+
+.old2 {
+  display: flex;
+  justify-content: center;
+
+  img {
+    width: 80px;
+    height: 60px;
+  }
+}
+
+.old3, .old4, .old5, .old6 {
+  padding-left: 12px;
+  margin-bottom: 10px;
+}
+
+.newBox {
+  width: 300px;
+  height: 320px;
+  //background: pink;
+  border: 1px solid black;
+  border-left: none;
+}
+
+.new1 {
+  //background: yellow;
+  text-align: center;
+  border: 1px solid black;
+  border-top: none;
+  border-left: none;
+  border-right: none;
+}
+
+.new2 {
+  display: flex;
+  justify-content: center;
+
+  img {
+    width: 80px;
+    height: 60px;
+  }
+}
+
+.new3, .new4, .new5, .new6 {
+  padding-left: 12px;
+  margin-bottom: 10px;
+}
+
+.allBoxBtn {
+  width: 600px;
+  height: 60px;
+  //background: pink;
+  display: flex;
+  justify-content: flex-end;
+  border: 1px solid black;
+  border-top: none;
+}
+
+.Btn {
+  width: 100px;
+  height: 40px;
+  margin-top: 10px;
+  margin-right: 10px;
+}
+
+</style>

+ 347 - 0
src/views/mes/material/Lending/demo7.vue

@@ -0,0 +1,347 @@
+<template>
+  <div class="mapBox">
+    <div style="width:400px;height: 350px;border: 1px solid black;margin: 10px;position: absolute;z-index: 1;top:0">
+      <h3 style="padding-left: 10px">操作流程:</h3>
+      <div style="display: flex;padding-left: 10px">
+        <h4>1.选择物资柜</h4>
+        <el-select
+          style="width: 215px;margin: 10px 20px;"
+          v-model="queryParams.materialsCabinetId"
+          placeholder="请选择物资柜"
+          @change="handleCabinetChange"
+        >
+          <el-option
+            v-for="dict in cabinets"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </div>
+      <div style="display: flex;padding-left: 10px">
+        <h4>2.</h4>
+        <el-button @click="changeDoor(true)" style="width:60px;height:30px;padding:5px 8px;margin: 12px 10px">开柜门
+        </el-button>
+      </div>
+      <div style="display: flex;padding-left: 10px">
+        <h4>3.取物资/还物资</h4>
+        <!--        <el-input v-model="inputStatus" style="width:150px"></el-input>-->
+      </div>
+      <div style="display: flex;padding-left: 10px">
+        <h4>4.</h4>
+        <el-button :disabled="!doorOpen" @click="changeDoor(false)"
+                   style="width:60px;height:30px;padding:5px 8px;margin: 12px 10px"
+        >关柜门
+        </el-button>
+      </div>
+
+    </div>
+    <div id="container" ref="container" style="width: 260vh; height: 90vh; background-color: white;"></div>
+  </div>
+</template>
+
+<script>
+import Konva from 'konva'
+import {
+  getIsMaterialsCabinets,
+  listMaterials,
+  selectMaterialsById,
+  updateIsMaterialById, insertIsMaterialsChangeRecord, updateMaterialsBindingRemove
+} from '@/api/mes/material/information'
+
+export default {
+  name: 'Lending',
+  data() {
+    return {
+      stage: null,
+      layer: null,
+      bgLayer: null,
+      list: [], // 三号柜子的物资
+      listInCabinet: [], // 存储物资柜内的物资
+      listOutOfCabinet: [], // 存储物资柜外的物资
+      cabinets: [], // 物资所属柜
+      doorOpen: false,
+      queryParams: {
+        current: 1,
+        size: -1,
+        materialsCabinetId: null,
+        loanState: null
+      },
+      inputStatus: '当前柜门已关闭',//柜门开关状态
+      materialElements: [], // 存储物资图标对象
+      unbindMaterialIds: [], // 批量增加的绑定id
+      bindingMaterialIds: []//批量解除绑定的id
+    }
+  },
+  mounted() {
+    // 我是手动更换 不开门表单提交之后 需要实体更换物资的操作
+    this.$nextTick(() => {
+      this.initKonva()
+      this.inputStatus = '当前柜门已关闭'
+      this.materialsCabinets()
+    })
+  },
+  methods: {
+    changeDoor(value) {
+      this.doorOpen = value
+      if (this.doorOpen) {
+        this.$message.success('柜门已打开')
+        this.updateDraggableStatus()
+        this.inputStatus = '当前柜门已打开'
+      } else {
+        this.$message.info('柜门已关闭')
+        this.inputStatus = '当前柜门已关闭'
+        this.updateMaterialsBatch() // 柜门关闭时调用批量更新接口
+      }
+    },
+    // 获取物资柜信息
+    materialsCabinets() {
+      getIsMaterialsCabinets(this.queryParams).then((response) => {
+        if (response?.data?.records) {
+          this.cabinets = response.data.records.map((item) => ({
+            value: item.cabinetId,
+            label: item.cabinetName
+          }))
+        }
+      })
+    },
+    handleCabinetChange() {
+      // 清空之前渲染的物资元素
+      this.clearMaterialsFromLayer()
+
+      // 获取柜内和柜外物资
+      Promise.all([this.getMaterialsInCabinet(), this.getMaterialsOutOfCabinet()])
+        .then(() => {
+          // 在所有数据获取完成后,统一渲染物资
+          this.addMaterialsToLayer()
+        })
+    },
+
+    getMaterialsInCabinet() {
+      this.queryParams.loanState = '1' // 设置查询条件为物资柜内
+      return listMaterials(this.queryParams).then((res) => {
+        this.listInCabinet = res.data.records
+      })
+    },
+
+    getMaterialsOutOfCabinet() {
+      this.queryParams.loanState = '0' // 设置查询条件为物资柜外
+      const data = {
+        current: 1,
+        size: -1,
+        loanState: '0'
+        // materialsCabinetId: '0',
+      }
+      return listMaterials(data).then((res) => {
+        // this.listOutOfCabinet = res.data.records.filter((item) => {
+        //   return item.materialsCabinetId == null || item.materialsCabinetId == '0'
+        // })
+        this.listOutOfCabinet = res.data.records.filter((item) => {
+          return item.materialsCabinetId !== null
+        })
+        console.log(this.listOutOfCabinet, '柜子外的数据demo3')
+      })
+    },
+    // 初始化Konva舞台
+    initKonva() {
+      this.stage = new Konva.Stage({
+        container: this.$refs.container,
+        width: 1600,
+        height: 1000
+      })
+
+      this.bgLayer = new Konva.Layer()
+      const bgImage = new Image()
+      bgImage.src = require('@/assets/images/table.png')
+      bgImage.onload = () => {
+        const konvaImage = new Konva.Image({
+          x: 600,
+          y: 25,
+          image: bgImage,
+          width: 500,
+          height: 700,
+          draggable: false
+        })
+        this.bgLayer.add(konvaImage)
+        this.bgLayer.draw()
+      }
+      this.stage.add(this.bgLayer)
+    },
+
+    // 清除Konva图层中的所有物资元素
+    clearMaterialsFromLayer() {
+      if (!this.bgLayer) return
+      const materialNodes = this.bgLayer.find('.material')
+      console.log('清除物资:', materialNodes)
+      materialNodes.forEach(node => node.destroy()) // 清除已渲染的物资元素
+      this.materialElements = [] // 清空已存储的物资图标对象
+      this.bgLayer.draw() // 重新绘制图层
+    },
+
+    // 物资拖动结束时的处理
+    handleDragEnd(e) {
+      const materialId = e.target.getAttr('materialId') // 获取物资ID
+      const position = e.target.getClientRect() // 获取物资当前的位置信息
+      const material = e.target.getAttr('material') // 获取物资对象
+      console.log(material, '物资移动')
+      const isInCabinet = material.inCabinet // 物资是否在柜子内
+      if (position.x < 600 || position.x > 1100 || position.y < 25 || position.y > 725) {
+        // **物资移出柜子**
+        if (isInCabinet) {
+          console.log('1--------')
+          this.unbindMaterialIds.push(materialId)
+
+          material.inCabinet = false // 更新状态
+        }
+      } else {
+        // **物资归还到柜子**
+        console.log('2--------')
+        // **物资从外部归还**
+        this.bindingMaterialIds.push(materialId)
+        material.inCabinet = true
+      }
+    },
+
+    // 将物资添加到Konva图层
+    addMaterialsToLayer() {
+      if (!this.bgLayer) return
+
+      const itemWidth = 60
+      const itemHeight = 60
+      const spacing = 40
+
+      // 清除之前的物资元素
+      this.clearMaterialsFromLayer()
+
+      // 渲染柜内物资
+      this.listInCabinet.forEach((material, index) => {
+        const x = 720 + (index % 3) * (itemWidth + spacing)
+        const y = 120 + Math.floor(index / 3) * (itemHeight + spacing)
+
+        const materialImage = new Image()
+        materialImage.src = material.materialsTypeIcon
+        materialImage.onload = () => {
+          const group = new Konva.Group({
+            x: x,
+            y: y,
+            draggable: this.doorOpen, // 初始时根据 doorOpen 状态设置可拖拽
+            name: 'material',
+            materialId: material.materialsId,
+            material: material
+          })
+
+          const konvaMaterialImage = new Konva.Image({
+            image: materialImage,
+            width: itemWidth,
+            height: itemHeight
+          })
+
+          const text = new Konva.Text({
+            x: 0,
+            y: itemHeight + 5,
+            text: material.materialsName,
+            fontSize: 12,
+            fill: 'black',
+            align: 'center',
+            width: itemWidth
+          })
+
+          group.add(konvaMaterialImage)
+          group.add(text)
+
+          // 给物资设置初始的柜内状态
+          material.inCabinet = true  // 设置物资最初处于柜子内
+
+          group.on('dragend', this.handleDragEnd) // 添加dragend事件监听
+
+          this.bgLayer.add(group)
+          this.bgLayer.draw()
+          this.materialElements.push(group)
+        }
+      })
+
+      // 渲染柜外物资
+      this.listOutOfCabinet.forEach((material, index) => {
+        const x = 1200 + (index % 3) * (itemWidth + spacing) // 左侧显示柜外物资
+        const y = 120 + Math.floor(index / 3) * (itemHeight + spacing)
+
+        const materialImage = new Image()
+        materialImage.src = material.materialsTypeIcon
+        materialImage.onload = () => {
+          const group = new Konva.Group({
+            x: x,
+            y: y,
+            draggable: this.doorOpen, // 初始时设为可拖拽
+            name: 'material',
+            materialId: material.materialsId,
+            material: material
+          })
+
+          const konvaMaterialImage = new Konva.Image({
+            image: materialImage,
+            width: itemWidth,
+            height: itemHeight
+          })
+
+          const text = new Konva.Text({
+            x: 0,
+            y: itemHeight + 5,
+            text: material.materialsName,
+            fontSize: 12,
+            fill: 'black',
+            align: 'center',
+            width: itemWidth
+          })
+
+          group.add(konvaMaterialImage)
+          group.add(text)
+
+          // 给物资设置初始的柜外状态
+          material.inCabinet = false  // 设置物资最初处于柜子外
+
+          group.on('dragend', this.handleDragEnd) // 添加dragend事件监听
+
+          this.bgLayer.add(group)
+          this.bgLayer.draw()
+          this.materialElements.push(group)
+        }
+      })
+    },
+
+// 监听 doorOpen 状态变化时更新物资的 draggable 状态
+    updateDraggableStatus() {
+      this.materialElements.forEach(group => {
+        group.draggable(this.doorOpen)  // 动态更新 draggable 状态
+      })
+      this.bgLayer.draw()
+    },
+
+    // 批量更新物资状态
+    updateMaterialsBatch() {
+      if (!this.bindingMaterialIds && !this.unbindMaterialIds) return // 如果没有需要更新的物资,直接返回
+      const data = {
+        bindingMaterialIds: this.bindingMaterialIds,
+        cabinetId: this.queryParams.materialsCabinetId,
+        unbindMaterialIds: this.unbindMaterialIds
+      }
+      updateMaterialsBindingRemove(data).then(response => {
+        if (response.data) {
+          this.$message.success('物资更新成功')
+          this.unbindMaterialIds = [] // 清空需要更新的物资列表
+          this.bindingMaterialIds = []
+        } else {
+          this.$message.error('物资更新失败')
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.mapBox {
+  width: 100%;
+  height: 100%;
+  //background-color: pink;
+}
+</style>

+ 435 - 0
src/views/mes/material/Lending/demo8.vue

@@ -0,0 +1,435 @@
+<template>
+  <div class="mapBox">
+    <div style="width:400px;height: 350px;border: 1px solid black;margin: 10px;position: absolute;z-index: 1;top:0">
+      <h3 style="padding-left: 10px">操作流程:</h3>
+      <div style="display: flex;padding-left: 10px">
+        <h4>1.选择物资柜</h4>
+        <el-select
+          style="width: 215px;margin: 10px 20px;"
+          v-model="queryParams.materialsCabinetId"
+          placeholder="请选择物资柜"
+          @change="handleCabinetChange"
+        >
+          <el-option
+            v-for="dict in cabinets"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </div>
+      <div style="display: flex;padding-left: 10px">
+        <h4>2.</h4>
+        <el-button @click="changeDoor(true)" style="width:60px;height:30px;padding:5px 8px;margin: 12px 10px">开柜门
+        </el-button>
+      </div>
+      <div style="display: flex;padding-left: 10px">
+        <h4>3.取物资/还物资</h4>
+        <!--        <el-input v-model="inputStatus" style="width:150px"></el-input>-->
+      </div>
+      <div style="display: flex;padding-left: 10px">
+        <h4>4.</h4>
+        <el-button :disabled="!doorOpen" @click="changeDoor(false)"
+                   style="width:60px;height:30px;padding:5px 8px;margin: 12px 10px"
+        >关柜门
+        </el-button>
+      </div>
+
+    </div>
+    <div id="container" ref="container" style="width: 260vh; height: 90vh; background-color: white;"></div>
+  </div>
+</template>
+
+<script>
+import Konva from 'konva'
+import {
+  getExMaterials,
+  getIsMaterialsCabinets, insertCabinetOpenTimeout,
+  listMaterials,
+  selectMaterialsById,
+  updateIsMaterialById,
+  updateCabinetOpenTimeout
+} from '@/api/mes/material/information'
+import { getDicts } from '@/api/system/dict/data'
+
+export default {
+  name: 'Lending',
+  dicts:['timer_params'],
+  data() {
+    return {
+      stage: null,
+      layer: null,
+      bgLayer: null,
+      list: [], // 三号柜子的物资
+      listInCabinet: [], // 存储物资柜内的物资
+      listOutOfCabinet: [], // 存储物资柜外的物资
+      cabinets: [], // 物资所属柜
+      doorOpen: false,
+      queryParams: {
+        current: 1,
+        size: -1,
+        materialsCabinetId: null,
+        loanState: null
+      },
+      inputStatus: '当前柜门已关闭',//柜门开关状态
+      materialElements: [], // 存储物资图标对象
+      materialsToUpdate: [], // 存储需要更新的物资
+      overTime:0,//
+      timer:null,//定时器
+    }
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.initKonva()
+      this.inputStatus = '当前柜门已关闭'
+      this.materialsCabinets()
+    })
+    getDicts('timer_params').then(res => {
+      console.log(res,'字典值')
+      this.overTime=res.data[0].dictValue
+    })
+  },
+  methods: {
+    changeDoor(value) {
+      this.doorOpen = value
+      if (this.doorOpen) {
+        this.$message.success('柜门已打开')
+        this.updateDraggableStatus()
+
+        this.inputStatus = '当前柜门已打开'
+        // 获取定时器参数
+        this.getDicts('timer_params').then(res => {
+          console.log(res, '字典值');
+          this.overTime = res.data[0].dictValue;
+
+          // 如果 overTime 大于 0,则启动定时器
+          if (this.overTime > 0) {
+            this.timer = setTimeout(() => {
+              const data = {
+                loanFromId: this.queryParams.materialsCabinetId
+              };
+              insertCabinetOpenTimeout(data).then((res) => {
+                console.log(res);
+                if(res.data){
+                  this.$message.error('柜门超过十秒未关闭')
+                }
+
+              });
+            }, this.overTime * 1000); // 将秒转换为毫秒
+          }
+        });
+      } else {
+        this.$message.info('柜门已关闭')
+        this.inputStatus = '当前柜门已关闭'
+        // 清除定时器
+        if (this.timer) {
+          clearTimeout(this.timer);
+          this.timer = null;
+        }
+        this.updateMaterialsBatch() // 柜门关闭时调用批量更新接口
+      }
+    },
+    // 获取物资柜信息
+    materialsCabinets() {
+      getIsMaterialsCabinets(this.queryParams).then((response) => {
+        if (response?.data?.records) {
+          this.cabinets = response.data.records.map((item) => ({
+            value: item.cabinetId,
+            label: item.cabinetName
+          }))
+        }
+      })
+    },
+    handleCabinetChange() {
+      // 清空之前渲染的物资元素
+      this.clearMaterialsFromLayer()
+
+      // 获取柜内和柜外物资
+      Promise.all([this.getMaterialsInCabinet(), this.getMaterialsOutOfCabinet()])
+        .then(() => {
+          // 在所有数据获取完成后,统一渲染物资
+          this.addMaterialsToLayer()
+        })
+    },
+
+    async getMaterialsInCabinet() {
+      this.queryParams.loanState = '1'; // 设置查询条件为物资柜内
+
+      try {
+        const res1 = await listMaterials(this.queryParams);
+        this.listInCabinet = res1.data.records;
+
+        const data1 = {
+          materialsCabinetId: this.queryParams.materialsCabinetId
+        };
+
+        const res2 = await getExMaterials(data1);
+        this.listInCabinet = [...this.listInCabinet, ...res2.data];
+        console.log(this.listInCabinet, '柜子内的数据demo2');
+
+      } catch (error) {
+        console.error('Error fetching data:', error);
+      }
+    },
+
+    getMaterialsOutOfCabinet() {
+      this.queryParams.loanState = '0' // 设置查询条件为物资柜外
+
+      const data = {
+        current: 1,
+        size: -1,
+        loanState: '0'
+      }
+      listMaterials(data).then((res) => {
+        // item.status!=='3'这个判断的意思是放错柜子的数据不显示的柜子外 因为异常数据以及放在柜子里了 item.materialsCabinetId不显示未绑定柜子的 item.materialsCabinetId不显示绑定柜子是0的
+        this.listOutOfCabinet = res.data.records.filter((item) => {
+          return item.materialsCabinetId != null && item.materialsCabinetId !== '0' && item.status !== '3'
+        })
+      })
+
+    },
+    // 初始化Konva舞台
+    initKonva() {
+      this.stage = new Konva.Stage({
+        container: this.$refs.container,
+        width: 1600,
+        height: 1000
+      })
+
+      this.bgLayer = new Konva.Layer()
+      const bgImage = new Image()
+      bgImage.src = require('@/assets/images/table.png')
+      bgImage.onload = () => {
+        const konvaImage = new Konva.Image({
+          x: 600,
+          y: 25,
+          image: bgImage,
+          width: 500,
+          height: 700,
+          draggable: false
+        })
+        this.bgLayer.add(konvaImage)
+        this.bgLayer.draw()
+      }
+      this.stage.add(this.bgLayer)
+    },
+
+    // 清除Konva图层中的所有物资元素
+    clearMaterialsFromLayer() {
+      if (!this.bgLayer) return
+      const materialNodes = this.bgLayer.find('.material')
+      console.log('清除物资:', materialNodes)
+      materialNodes.forEach(node => node.destroy()) // 清除已渲染的物资元素
+      this.materialElements = [] // 清空已存储的物资图标对象
+      this.bgLayer.draw() // 重新绘制图层
+    },
+
+    // 物资拖动结束时的处理
+    handleDragEnd(e) {
+      const materialId = e.target.getAttr('materialId') // 获取物资ID
+      const position = e.target.getClientRect() // 获取物资当前的位置信息
+      const material = e.target.getAttr('material') // 获取物资对象
+      console.log(material, '物资移动')
+      const isInCabinet = material.inCabinet // 物资是否在柜子内
+
+      selectMaterialsById(material.materialsId).then((res) => {
+        console.log(res, '一大啊啊啊是')
+        const originalCabinetId = res.data.materialsCabinetId//原来的柜子ID
+        const currentCabinetId = this.queryParams.materialsCabinetId // 归还目标柜子ID
+        console.log(originalCabinetId, currentCabinetId, 'originalCabinetId')
+
+        if (!currentCabinetId || position.x < 600 || position.x > 1100 || position.y < 25 || position.y > 725) {
+          // **物资移出柜子**
+          if (isInCabinet) {
+            console.log('1--------')
+            this.materialsToUpdate.push({
+              materialsId: materialId,
+              loanState: 0,  // "借出"
+              loanUserId: 106
+            })
+            material.inCabinet = false // 更新状态
+          }
+        } else {
+          // **物资归还到柜子**
+          if (!isInCabinet && originalCabinetId === currentCabinetId) {
+            console.log('2--------')
+            // **物资从外部归还**
+            this.materialsToUpdate.push({
+              materialsId: materialId,
+              loanState: 1,  // "归还"
+              restitutionToId: currentCabinetId,
+              restitutionUserId: 106
+            })
+            material.inCabinet = true
+            material.materialsCabinetId = currentCabinetId // 更新物资的柜子ID
+          } else if (originalCabinetId !== currentCabinetId) {
+            console.log('3--------')
+            // **用户确认变更柜子**
+            this.materialsToUpdate.push({
+              materialsId: materialId,
+              loanState: 1,
+              restitutionToId: currentCabinetId,
+              restitutionUserId: 106
+            })
+            material.materialsCabinetId = currentCabinetId // 更新柜子ID
+            this.bgLayer.draw()
+          } else {
+            // **物资仍在同一个柜子内**
+            console.log('物资仍在同一个柜子内')
+            // 如果需要,可以在这里添加一些逻辑
+          }
+        }
+      }).catch((error) => {
+        console.error('获取物资信息失败:', error)
+        // 处理错误情况
+      })
+    },
+
+    // 将物资添加到Konva图层
+    addMaterialsToLayer() {
+      if (!this.bgLayer) return
+
+      const itemWidth = 60
+      const itemHeight = 60
+      const spacing = 40
+
+      // 清除之前的物资元素
+      this.clearMaterialsFromLayer()
+
+      // 渲染柜内物资
+      console.log(this.listInCabinet,'物资柜内部数据')
+      this.listInCabinet.forEach((material, index) => {
+        const x = 720 + (index % 3) * (itemWidth + spacing)
+        const y = 120 + Math.floor(index / 3) * (itemHeight + spacing)
+
+        const materialImage = new Image()
+        materialImage.src = material.materialsTypeIcon
+        materialImage.onload = () => {
+          const group = new Konva.Group({
+            x: x,
+            y: y,
+            draggable: this.doorOpen, // 初始时根据 doorOpen 状态设置可拖拽
+            name: 'material',
+            materialId: material.materialsId,
+            material: material
+          })
+
+          const konvaMaterialImage = new Konva.Image({
+            image: materialImage,
+            width: itemWidth,
+            height: itemHeight
+          })
+
+          const text = new Konva.Text({
+            x: 0,
+            y: itemHeight + 5,
+            text: material.materialsName,
+            fontSize: 12,
+            fill: 'black',
+            align: 'center',
+            width: itemWidth
+          })
+
+          group.add(konvaMaterialImage)
+          group.add(text)
+
+          // 给物资设置初始的柜内状态
+          material.inCabinet = true  // 设置物资最初处于柜子内
+
+          group.on('dragend', this.handleDragEnd) // 添加dragend事件监听
+
+          this.bgLayer.add(group)
+          this.bgLayer.draw()
+          this.materialElements.push(group)
+        }
+      })
+
+      // 渲染柜外物资
+      this.listOutOfCabinet.forEach((material, index) => {
+        const x = 1200 + (index % 3) * (itemWidth + spacing) // 左侧显示柜外物资
+        const y = 120 + Math.floor(index / 3) * (itemHeight + spacing)
+
+        const materialImage = new Image()
+        materialImage.src = material.materialsTypeIcon
+        materialImage.onload = () => {
+          const group = new Konva.Group({
+            x: x,
+            y: y,
+            draggable: this.doorOpen, // 初始时设为可拖拽
+            name: 'material',
+            materialId: material.materialsId,
+            material: material
+          })
+
+          const konvaMaterialImage = new Konva.Image({
+            image: materialImage,
+            width: itemWidth,
+            height: itemHeight
+          })
+
+          const text = new Konva.Text({
+            x: 0,
+            y: itemHeight + 5,
+            text: material.materialsName,
+            fontSize: 12,
+            fill: 'black',
+            align: 'center',
+            width: itemWidth
+          })
+
+          group.add(konvaMaterialImage)
+          group.add(text)
+
+          // 给物资设置初始的柜外状态
+          material.inCabinet = false  // 设置物资最初处于柜子外
+
+          group.on('dragend', this.handleDragEnd) // 添加dragend事件监听
+
+          this.bgLayer.add(group)
+          this.bgLayer.draw()
+          this.materialElements.push(group)
+        }
+      })
+    },
+
+// 监听 doorOpen 状态变化时更新物资的 draggable 状态
+    updateDraggableStatus() {
+      this.materialElements.forEach(group => {
+        group.draggable(this.doorOpen)  // 动态更新 draggable 状态
+      })
+      this.bgLayer.draw()
+    },
+
+    // 批量更新物资状态
+    updateMaterialsBatch() {
+      const data={
+        loanFromId:this.queryParams.materialsCabinetId
+      }
+      updateCabinetOpenTimeout(data).then((res)=>{
+        console.log(res)
+        if(res.data){
+          this.$message.success('柜门异常解除')
+        }
+      })
+      if (this.materialsToUpdate.length === 0) return // 如果没有需要更新的物资,直接返回
+
+      updateIsMaterialById(this.materialsToUpdate).then(response => {
+        if (response.data) {
+          this.$message.success('物资更新成功')
+          this.materialsToUpdate = [] // 清空需要更新的物资列表
+        } else {
+          this.$message.error('物资更新失败')
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.mapBox {
+  width: 100%;
+  height: 100%;
+  //background-color: pink;
+}
+</style>

+ 335 - 0
src/views/mes/material/doorException/index.vue

@@ -0,0 +1,335 @@
+<template>
+  <div class="app-container">
+    <el-form
+      :model="queryParams"
+      ref="queryForm"
+      size="small"
+      :inline="true"
+      v-show="showSearch"
+      label-width="100px"
+    >
+      <el-form-item label="物资柜" prop="loanFromId">
+        <el-select
+          v-model="queryParams.loanFromId"
+          placeholder="请选择物资柜"
+          clearable
+        >
+          <el-option
+            v-for="dict in cabinets"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+<!--      <el-form-item label="物资名称" prop="materialsName">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.materialsName"-->
+<!--          placeholder="请输入物资名称"-->
+<!--          clearable-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
+<!--      <el-form-item label="归还人" prop="restitutionUserName">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.restitutionUserName"-->
+<!--          placeholder="请输入归还人"-->
+<!--          clearable-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
+          <el-option
+            v-for="dict in dict.type.material_exception_status"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="异常发生时间">
+        <el-date-picker
+          style="width: 215px"
+          :default-time="['00:00:00', '23:59:59']"
+          v-model="createTime"
+          type="datetimerange"
+          :picker-options="pickerOptions"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          align="right"
+          @change="handleClearTime"
+        >
+        </el-date-picker>
+      </el-form-item>
+
+      <el-form-item label="异常解除时间">
+        <el-date-picker
+          style="width: 215px"
+          :default-time="['00:00:00', '23:59:59']"
+          v-model="handleTime"
+          type="datetimerange"
+          :picker-options="pickerOptions"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          align="right"
+          @change="handleClearTime2"
+        >
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button
+          v-no-more-click
+          type="primary"
+          icon="el-icon-search"
+          size="mini"
+          @click="handleQuery"
+        >搜索
+        </el-button
+        >
+        <el-button
+          v-no-more-click
+          icon="el-icon-refresh"
+          size="mini"
+          @click="resetQuery"
+        >重置
+        </el-button
+        >
+      </el-form-item>
+    </el-form>
+
+    <el-table
+      v-loading="loading"
+      :data="ExceptionList"
+
+    >
+      <el-table-column label="编号" align="center" prop="exceptionId">
+      </el-table-column>
+      <el-table-column label="物资柜" align="center" prop="loanFromName" />
+
+<!--      <el-table-column label="物资名称" align="center" prop="materialsName">-->
+<!--      </el-table-column>-->
+<!--      <el-table-column label="归还柜" align="center" prop="restitutionToName">-->
+<!--      </el-table-column>-->
+<!--      <el-table-column label="归还人" align="center" prop="restitutionUserName">-->
+<!--      </el-table-column>-->
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.material_exception_status" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="异常发生时间"
+        align="center"
+        prop="occurTime"
+      >
+      </el-table-column>
+      <el-table-column label="异常解除时间" align="center" prop="handleTime">
+      </el-table-column>
+    </el-table>
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="queryParams.current"
+      :limit.sync="queryParams.size"
+      @pagination="getList"
+    />
+
+  </div>
+</template>
+
+<script>
+
+import Treeselect from '@riophae/vue-treeselect'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import Template from '@/views/print/printtemplate/list.vue'
+import { MaterialsLoanExceptionPage } from '@/api/mes/material/exception'
+import { getIsMaterialsCabinets } from '@/api/mes/material/information'
+import { getDicts } from '@/api/system/dict/data'
+export default {
+  name: 'Team',
+  components: { Template, Treeselect },
+  dicts: ['material_exception_status','timer_params'],
+  data() {
+    return {
+      //自动生成编码
+      autoGenFlag: false,
+      optType: undefined,
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      codes: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 班组表格数据
+      ExceptionList: [],
+      // mars树选项
+      marsOptions: [],
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      createTime: '',
+      handleTime:'',
+      queryParams: {
+        current: 1,
+        size: 10,
+        exceptionType:'1',
+        loanFromId:'',
+        materialsName:'',
+        restitutionUserName:'',
+        status:'',
+        startTime:'',
+        endTime:'',
+        handleStartTime:"",
+        handleEndTime:'',
+      },
+      title:'',
+      // 表单参数
+      form: {},
+      cabinets: [], //物资所属柜
+      pickerOptions: {
+        shortcuts: [
+          {
+            text: "最近一周",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+          {
+            text: "最近一个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+          {
+            text: "最近三个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+        ],
+      },
+
+    }
+  },
+
+  created() {
+    this.getList()
+    this.materialsCabinets()
+  },
+
+  methods: {
+    formatDate(date) {
+      const year = date.getFullYear().toString().padStart(2, "0");
+      const month = (date.getMonth() + 1).toString().padStart(2, "0");
+      const day = date.getDate().toString().padStart(2, "0");
+      const hours = date.getHours().toString().padStart(2, "0");
+      const minutes = date.getMinutes().toString().padStart(2, "0");
+      const seconds = date.getSeconds().toString().padStart(2, "0");
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
+    /** 查询物资信息列表 */
+    getList() {
+      this.loading = true
+      if (Array.isArray(this.createTime) && this.createTime.length === 2) {
+        this.queryParams.startTime = this.formatDate(this.createTime[0])
+        this.queryParams.endTime = this.formatDate(this.createTime[1])
+      }
+      if(Array.isArray(this.handleTime) && this.handleTime.length === 2) {
+        this.queryParams.handleStartTime=this.formatDate(this.handleTime[0])
+        this.queryParams.handleEndTime=this.formatDate(this.handleTime[1])
+      }
+      const data={
+        ...this.queryParams,
+      }
+      MaterialsLoanExceptionPage(data).then((response) => {
+        console.log(response, '获取物资归还异常allList ')
+        this.ExceptionList = response.data.records
+        this.total = response.data.total
+        this.loading = false
+      })
+    },
+    materialsCabinets() {
+      const data1 = {
+        pasge: 1,
+        size: -1
+      }
+      getIsMaterialsCabinets(data1).then((response) => {
+        if (response?.data?.records) {
+          this.cabinets = response.data.records.map((item) => ({
+            value: item.cabinetId,
+            label: item.cabinetName
+          }))
+        }
+      })
+    },
+// 取消按钮
+    cancel() {
+      this.open = false
+      this.reset()
+    },
+    // 表单重置
+    reset() {
+
+      this.resetForm('form')
+      this.autoGenFlag = false
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.size = 10
+      this.getList()
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.queryParams.startTime = ''
+      this.queryParams.endTime = ''
+      this.createTime = ''
+      this.handleTime = ''
+      this.queryParams.handleStartTime = ''
+      this.queryParams.handleEndTime = ''
+      this.resetForm('queryForm')
+      this.handleQuery()
+    },
+    handleClearTime(value){
+      if(value==null){
+        this.queryParams.startTime=null
+        this.queryParams.endTime=null
+      }
+    },
+    handleClearTime2(value){
+      if(value==null){
+        this.queryParams.handleStartTime=null
+        this.queryParams.handleEndTime=null
+      }
+    },
+
+
+
+  }
+}
+</script>
+<style lang="scss" src="@/assets/styles/dialog-title.scss" scoped>
+.el-input-width {
+  width: 380px !important;
+
+}
+</style>

+ 2 - 2
src/views/mes/material/inspectionplan/index.vue

@@ -221,7 +221,7 @@
           </el-date-picker>
         </el-form-item>
         <el-form-item label="所属区域" prop="workstationId">
-          <treeselect style="width: 348px" v-model="form.workstationId" :options="workstationOption"
+          <treeselect  style="width: 348px" v-model="form.workstationId" :options="workstationOption"
                       :normalizer="normalizer" placeholder="选择所属区域"
           />
         </el-form-item>
@@ -550,7 +550,7 @@ export default {
         }
         getIsMaterialsCabinets(data).then((response) => {
           console.log(response, 'wgahah')
-          if (response?.data?.records) {
+          if (response.data.records) {
             this.cabinets = response.data.records.map((item) => ({
               value: item.cabinetId,
               label: item.cabinetName

+ 0 - 1
src/views/mes/material/materialinformation/index.vue

@@ -767,7 +767,6 @@ export default {
             console.log(response, "获取物资属性名 ");
             this.PropertyList = response.data.records;
           });
-
           PropertyValuePage(data).then((response) => {
             console.log(response, "获取物资属性值 ");
             this.PropertyValueList = response.data.records;