车间能级提升-智能设备管理系统
baoshiwei
2025-06-09 df64c34d92cbe8501bbbfe837bc491a47452c0b6
feat(eims): 新增保养工单批量修改功能并优化相关领域对象

- 新增保养工单批量修改功能,支持一次性修改多个工单
- 在 EimsMaintOrder 模型中添加 maintFun 和 repairDesc 字段
- 在 EimsMaintOrderBo 和 EimsMaintOrderVo 中添加相关字段映射
- 优化 EimsSpareBo 和 EimsSpareInoutdtBo 的字段定义
- 新增 MaintOrderBo 类用于批量操作
- 更新 EimsMaintOrderService 接口和实现类,添加批量修改方法
-调整 EimsSpareInoutService 以支持备件出库操作
- 优化前端界面样式,调整字体大小和布局
已添加1个文件
已修改28个文件
917 ■■■■ 文件已修改
eims-ui-mobile/env/.env 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/env/.env.development 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/env/.env.production 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/package.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/equ/equ-list.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/fixture/fixture-list.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/home/index.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/inspect/insp-record.vue 147 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/inspect/insp-st.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/maint/maint-order.vue 453 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/maint/maint-st.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/repair/repair-fb.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/repair/req-list.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/repair/res-list.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/scan/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/spare/spare-list.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/service/maint.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsMaintOrderController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsMaintOrder.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsMaintOrderBo.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareBo.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareInoutdtBo.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/MaintOrderBo.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsMaintOrderVo.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareInoutdtVo.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsMaintOrderService.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsMaintOrderServiceImpl.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareInoutServiceImpl.java 63 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/env/.env
@@ -7,8 +7,7 @@
# h5部署网站的base,配置到 manifest.config.ts é‡Œçš„ h5.router.base
VITE_APP_PUBLIC_BASE=/
VITE_SERVER_BASEURL = 'http://lanpucloud.cn:8091'
VITE_UPLOAD_BASEURL = 'http://lanpucloud.cn:8091/resource/oss/upload'
# æœ‰äº›åŒå­¦å¯èƒ½éœ€è¦åœ¨å¾®ä¿¡å°ç¨‹åºé‡Œé¢æ ¹æ® develop、trial、release åˆ†åˆ«è®¾ç½®ä¸Šä¼ åœ°å€ï¼Œå‚考代码如下。
# ä¸‹é¢çš„变量如果没有设置,会默认使用 VITE_SERVER_BASEURL or VITE_UPLOAD_BASEURL
eims-ui-mobile/env/.env.development
@@ -4,3 +4,5 @@
VITE_DELETE_CONSOLE = false
# æ˜¯å¦å¼€å¯sourcemap
VITE_SHOW_SOURCEMAP = true
VITE_SERVER_BASEURL = 'http://192.168.21.236:8080'
VITE_UPLOAD_BASEURL = 'http://192.168.21.236:8080/resource/oss/upload'
eims-ui-mobile/env/.env.production
@@ -4,3 +4,5 @@
VITE_DELETE_CONSOLE = true
# æ˜¯å¦å¼€å¯sourcemap
VITE_SHOW_SOURCEMAP = false
VITE_SERVER_BASEURL = 'http://lanpucloud.cn:8091'
VITE_UPLOAD_BASEURL = 'http://lanpucloud.cn:8091/resource/oss/upload'
eims-ui-mobile/package.json
@@ -110,7 +110,7 @@
    "qs": "6.5.3",
    "vue": "3.4.21",
    "vue-i18n": "^9.1.9",
    "wot-design-uni": "^1.4.0",
    "wot-design-uni": "^1.9.1",
    "z-paging": "^2.8.4"
  },
  "devDependencies": {
eims-ui-mobile/src/pages/equ/equ-list.vue
@@ -39,7 +39,7 @@
          <view class="flex justify-between items-baseline">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <text class="ml-1 text-xs">{{ item.assetNo }}</text>
              <text class="ml-1 text-sm">{{ item.assetNo }}</text>
              <wd-tag v-if="item.status === '0'" class="ml-2" bg-color="cyan">试用</wd-tag>
              <wd-tag v-else-if="item.status === '1'" class="ml-2" type="success">使用</wd-tag>
              <wd-tag v-else-if="item.status === '2'" class="ml-2" type="danger">停用</wd-tag>
@@ -61,7 +61,7 @@
              {{ item.equName }}
              <text class="text-color-gray ml-2 text-mini">{{ item.modelNo }}</text>
            </view>
            <view class="text-color-gray text-xs mt-1">
            <view class="text-color-gray text-sm mt-1">
              {{ item.location }} | {{ item.madeIn }}
            </view>
          </view>
eims-ui-mobile/src/pages/fixture/fixture-list.vue
@@ -22,7 +22,7 @@
          <view class="flex justify-between items-center">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <text class="ml-1 text-xs">{{ item.fixtureName }}</text>
              <text class="ml-1 text-sm">{{ item.fixtureName }}</text>
              <text class="ml-1 text-mini text-color-gray">{{ item.assetNo }}</text>
            </view>
@@ -40,7 +40,7 @@
              <text class="text-color-gray ml-2 text-mini">规格: {{ item.specNo }}</text>
              <text class="text-color-gray ml-2 text-mini">型号: {{ item.modelNo }}</text>
            </view>
            <view class="text-color-gray text-xs mt-1 flex">
            <view class="text-color-gray text-sm mt-1 flex">
              <template
                v-if="!item.borrowStatus || item.borrowStatus === '0'"
              >
eims-ui-mobile/src/pages/home/index.vue
@@ -61,7 +61,7 @@
          <template #title>
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <view class="ml-1 text-xs">数据总览</view>
              <view class="ml-1 text-sm">数据总览</view>
            </view>
          </template>
          <view class="flex flex-row justify-around">
@@ -85,7 +85,7 @@
          <template #title>
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <view class="ml-1 text-xs">快捷操作</view>
              <view class="ml-1 text-sm">快捷操作</view>
            </view>
          </template>
          <wd-grid :column="4">
@@ -108,7 +108,7 @@
          <template #title>
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <view class="ml-1 text-xs">设备管理</view>
              <view class="ml-1 text-sm">设备管理</view>
            </view>
          </template>
          <wd-grid :column="4">
@@ -130,7 +130,7 @@
          <template #title>
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <view class="ml-1 text-xs">台账相关</view>
              <view class="ml-1 text-sm">台账相关</view>
            </view>
          </template>
          <wd-grid :column="4">
eims-ui-mobile/src/pages/inspect/insp-record.vue
@@ -6,7 +6,13 @@
}
</route>
<template>
  <z-paging ref="paging" v-model="dataList" :auto="false" @query="queryList" show-refresher-update-time>
  <z-paging
    ref="paging"
    v-model="dataList"
    :auto="false"
    @query="queryList"
    show-refresher-update-time
  >
    <template #top>
      <wd-navbar
        title="点检记录"
@@ -24,10 +30,13 @@
      <wd-card type="rectangle">
        <template #title>
          <view class="flex justify-between">
            <view class="flex items-center menu-title-box">
            <view
              class="flex items-center menu-title-box center"
              style="align-content: center; flex-wrap: wrap"
            >
              <view class="menu-indicator"></view>
              <view class="ml-1 text-sm align-center">{{ inspSt.equName }}</view>
              <view class="text-color-gray ml-2 text-mini">{{ inspSt.assetNo }}</view>
              <view class="ml-1 text-lg align-center">{{ inspSt.equName }}</view>
              <view class="text-color-gray ml-2 text-sm">{{ inspSt.assetNo }}</view>
            </view>
            <view class="flex items-center">
@@ -38,19 +47,19 @@
        <view class="flex h-[140rpx]" items-center>
          <image class="slot-img text-center" src="/static/images/camera.png" />
          <view class="flex-1">
            <view class="text-color-gray text-xs mt-1 flex">
            <view class="text-color-gray text-sm mt-1 flex">
              <text class="mr-3">点检总数: {{ dataCount }}</text>
              |
              <text class="mx-3">已点检: {{ checkCount }}</text>
              |
              <text class="ml-3">未点检: {{ dataCount - checkCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
            <view class="text-color-gray text-sm mt-2 flex">
              <text class="mr-3">正常: {{ normalNum }}</text>
              |
              <text class="mx-3">异常: {{ abNormalNum }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
            <view class="text-color-gray text-sm mt-2 flex">
              <text>状态:</text>
              <template v-if="dataCount > 0 && dataCount === checkCount">
                <wd-icon class="icon-color-success" name="check-outline" size="34rpx"></wd-icon>
@@ -61,7 +70,7 @@
                <text class="ml-1">进行中</text>
              </template>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">创建时间: {{ inspSt.createTime }}</view>
            <view class="text-color-gray text-sm mt-2 flex">创建时间: {{ inspSt.createTime }}</view>
          </view>
        </view>
      </wd-card>
@@ -71,17 +80,22 @@
      <view class="w-full h-[24rpx]"></view>
      <wd-cell>
        <template #title>
          <text class="text-color-gray">点检项</text>
          <text class="text-color-gray text-sm">点检项</text>
        </template>
        <wd-button size="small" type="text" @click.stop="toggleCollapse">
          {{ isAllExpanded ? '全部折叠' : '全部展开' }}
        </wd-button>
        <!--        <wd-button size="small" type="text" @click.stop="toggleCollapse">-->
        <!--          {{ isAllExpanded ? '全部折叠' : '全部展开' }}-->
        <!--        </wd-button>-->
      </wd-cell>
      <wd-collapse v-model="collSelects" title="点检项" ref="collapseRef">
        <wd-collapse-item :name="item.id" v-for="(item, index) in dataList" disabled>
      <wd-collapse v-model="collSelects" title="点检项" ref="collapseRef" accordion>
        <wd-collapse-item
          :name="item.id"
          v-for="(item, index) in dataList"
          :key="item.id"
          :class="getItemClass(item)"
        >
          <template #title="{ expanded, disabled, isFirst }">
            <view class="flex justify-between">
              <view class="flex justify-center items-center" style="max-width: 60%;">
              <view class="flex justify-center items-center" style="max-width: 60%">
                <text class="text-sm" >{{ item.inspName }}</text>
              </view>
@@ -105,18 +119,27 @@
              </view>
            </view>
          </template>
          <view class="text-color-gray text-xs flex justify-between">
          <view v-if="item.showDesc" class="mt-2">
            <wd-input
              v-model="item.inspDesc"
              placeholder="请输入异常描述"
              clearable
              :maxlength="200"
            />
          </view>
          <view class="text-color-gray text-sm flex justify-between">
            <text class="mr-3">点检人: {{ item.inspUserName }}</text>
            <text class="mx-3">点检时间: {{ item.inspTime }}</text>
          </view>
        </wd-collapse-item>
      </wd-collapse>
      <view class="w-full h-[24rpx]"></view>
      <wd-cell>
        <template #title>
          <text class="text-color-gray">其他</text>
        </template>
      </wd-cell>
      <!--      <view class="w-full h-[24rpx]"></view>-->
      <!--      <wd-cell>-->
      <!--&lt;!&ndash;        <template #title>&ndash;&gt;-->
      <!--&lt;!&ndash;          <text class="text-color-gray">其他</text>&ndash;&gt;-->
      <!--&lt;!&ndash;        </template>&ndash;&gt;-->
      <!--      </wd-cell>-->
      <view class="w-full h-[1px] bg-base"></view>
      <wd-input
        label="运行时间"
@@ -125,6 +148,7 @@
        v-model="inspSt.runTimes"
        placeholder="请输入运行时间(h)"
        inputmode="numeric"
        size="large"
      />
      <wd-input
        label="故障时间"
@@ -133,6 +157,7 @@
        v-model="inspSt.faultTimes"
        placeholder="请输入故障时间(h)"
        inputmode="numeric"
        size="large"
      />
      <wd-textarea
        label="特记事项"
@@ -144,13 +169,17 @@
        show-word-limit
        placeholder="请输入特记事项"
        clearable
        size="large"
      />
      <!-- æ–°å¢žæäº¤æŒ‰é’® -->
      <view class="flex justify-center mt-4">
        <wd-button type="primary" block size="large" @click="handleClickRight">提交</wd-button>
      </view>
    </view>
  </z-paging>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useUserStore, useAccessStore, useSystemConfigStore } from '@/store'
import { isLeader, isOperatorOrRepair } from '@/utils/RoleUtils'
import {
@@ -194,7 +223,8 @@
const userStore = useUserStore()
const collSelects = ref<string[]>([])
const collSelects = ref('')
const selectedItems = ref([])
// ç‚¹æ£€æ±‡æ€»æ•°æ®(上个页面传值)
const inspSt = reactive<InspSt>({
@@ -237,7 +267,19 @@
}
function inspResultClick(item: any) {
  // userStore?.userInfo?.userId
  console.log('inspResultClick', userStore.userInfo)
  // è‡ªåŠ¨å¡«å……ç‚¹æ£€äººå’Œæ—¶é—´
  item.inspUserName = userStore?.userInfo?.realName || ''
  // ä¿®æ”¹æ—¶é—´æ ¼å¼ä¸º YYYY-MM-DD HH:mm:ss
  const now = new Date()
  item.inspTime = `${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')}`
  if (item.inspResult === '2') {
    item.showDesc = true
  } else {
    item.showDesc = false
    item.inspDesc = ''
  }
}
const goBack = () => {
@@ -248,7 +290,6 @@
  if(isOperatorOrRepair()){
    handleConfirm()
  }
}
const toggleCollapse = () => {
@@ -257,10 +298,31 @@
}
function handleConfirm() {
  console.log('handleConfirm')
  if (!dataChange.value) {
    message.alert('请操作后提交!')
    return false
  }
  // æ£€æŸ¥æ˜¯å¦æœ‰å¼‚常项未填写描述
  const invalidItems = dataList.value.filter(
    (item) => item.inspResult === '2' && !item.inspDesc?.trim(),
  )
  if (invalidItems.length > 0) {
    message.alert('请填写所有异常项的异常描述!')
    return false
  }
  // è¿‡æ»¤å‡ºå·²é€‰æ‹©çš„项目
  selectedItems.value = dataList.value.filter(
    (item) => item.inspResult === '1' || item.inspResult === '2',
  )
  if (selectedItems.value.length === 0) {
    message.alert('请至少选择一个点检项!')
    return false
  }
  message
    .confirm({
      msg: '确定提交?',
@@ -278,8 +340,9 @@
}
function updateData(resolve: any) {
  console.log('updateData', selectedItems.value)
  const params = {
    inspRecordList: dataList.value,
    inspRecordList: selectedItems.value,
  }
  // æ›´æ–°ç‚¹æ£€è®°å½•
  updateInspRecordBatch(params)
@@ -313,8 +376,17 @@
 */
function itemClick(item: any) {}
function getItemClass(item: any) {
  if (item.inspResult === '1') {
    return 'status-normal'
  } else if (item.inspResult === '2') {
    return 'status-abnormal'
  }
  return ''
}
watch(
  () => [...dataList.value], // ä½¿ç”¨æ‰©å±•运算符创建新数组以触发监听
  () => [...dataList.value, inspSt], // ä½¿ç”¨æ‰©å±•运算符创建新数组以触发监听
  (newVal, oldVal) => {
    if (oldVal.length > 0) {
      dataChange.value = true
@@ -375,7 +447,7 @@
  margin-right: 24rpx;
}
.text-mini {
  font-size: 22rpx;
  font-size: 24rpx;
}
.menu-indicator {
@@ -394,13 +466,26 @@
  background: $uni-color-primary;
}
:deep(.wd-navbar__text) {
  font-size: 26rpx;
  font-size: 28rpx;
  color: white;
}
:deep(.wd-icon-arrow-left:before),
:deep(.wd-navbar__title) {
  color: white;
  font-weight: 0;
  font-size: 32rpx;
  font-size: 34rpx;
}
// æ–°å¢žæ ·å¼ï¼šç‚¹æ£€é¡¹èƒŒæ™¯è‰²
.status-normal {
  background-color: #e6f7ff; // æ­£å¸¸çŠ¶æ€èƒŒæ™¯è‰²ï¼ˆç»¿è‰²ï¼‰
}
.status-abnormal {
  background-color: #fffbe6; // å¼‚常状态背景色(黄色)
}
:deep(.wd-radio-group) {
  // æ”¹ä¸ºæ— èƒŒæ™¯è‰²
  background-color: transparent;
}
</style>
eims-ui-mobile/src/pages/inspect/insp-st.vue
@@ -43,19 +43,19 @@
        <view class="flex h-[140rpx]" items-center>
          <image class="slot-img text-center" src="/static/images/camera.png" />
          <view class="flex-1">
            <view class="text-color-gray text-xs mt-1 flex">
              <text class="mr-3">点检总数: {{ item.recordCount }}</text>
            <view class="text-color-gray text-sm mt-1 flex">
              <text class="mr-3">总数: {{ item.recordCount }}</text>
              |
              <text class="mx-3">已点检: {{ item.checkCount }}</text>
              |
              <text class="ml-3">未点检: {{ item.unCheckCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
            <view class="text-color-gray text-sm mt-2 flex">
              <text class="mr-3">正常: {{ item.normalNum }}</text>
              |
              <text class="mx-3">异常: {{ item.abNormalNum }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
            <view class="text-color-gray text-sm mt-2 flex">
              <text>状态:</text>
              <template v-if="item.recordCount === item.checkCount">
                <wd-icon class="icon-color-success" name="check-outline" size="34rpx"></wd-icon>
@@ -66,7 +66,7 @@
                <text class="ml-1">进行中</text>
              </template>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">创建时间: {{ item.createTime }}</view>
            <view class="text-color-gray text-sm mt-2 flex">创建时间: {{ item.createTime }}</view>
          </view>
          <wd-button size="small" icon="edit-outline" @click.stop="itemClick(item)">明细</wd-button>
        </view>
@@ -164,7 +164,7 @@
  height: 40rpx;
}
.text-mini {
  font-size: 22rpx;
  font-size: 24rpx;
}
.menu-indicator {
eims-ui-mobile/src/pages/maint/maint-order.vue
@@ -39,31 +39,31 @@
          <view class="flex justify-between">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <view class="ml-1 text-sm align-center">{{ maintSt.equName }}</view>
              <view class="text-color-gray ml-2 text-mini">{{ maintSt.assetNo }}</view>
              <view class="ml-1 text-lg align-center">{{ maintSt.equName }}</view>
              <view class="text-color-gray ml-2 text-sm">{{ maintSt.assetNo }}</view>
            </view>
            <view class="flex items-center">
              <text class="text-color-gray text-mini">{{ maintSt.planTime }}</text>
              <text class="text-color-gray text-sm">{{ maintSt.planTime }}</text>
            </view>
          </view>
        </template>
        <view class="flex h-[140rpx]" items-center>
          <image class="slot-img text-center" src="/static/images/camera.png" />
          <view class="flex-1">
            <view class="text-color-gray text-xs mt-1 flex">
            <view class="text-color-gray text-sm mt-1 flex">
              <text class="mr-3">工单总数: {{ maintSt.orderCount }}</text>
              |
              <text class="mx-3">已完成: {{ maintSt.wcCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
            <view class="text-color-gray text-sm mt-2 flex">
              <text class="mr-3">待保养: {{ maintSt.dbyCount }}</text>
              |
              <text class="mx-3">保养中: {{ maintSt.byCount }}</text>
              |
              <text class="ml-3">待验证: {{ maintSt.dyzCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
            <view class="text-color-gray text-sm mt-2 flex">
              <text>状态:</text>
              <template v-if="maintSt.status === '1'">
                <wd-icon class="icon-color-success" name="check-outline" size="34rpx"></wd-icon>
@@ -75,7 +75,7 @@
              </template>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
            <view class="text-color-gray text-sm mt-2 flex">
              åˆ›å»ºæ—¶é—´: {{ maintSt.createTime }}
            </view>
          </view>
@@ -87,145 +87,118 @@
      <view class="w-full h-[24rpx]"></view>
      <wd-cell class="mb-[2px]">
        <template #title>
          <text class="text-color-gray">保养项</text>
          <text class="text-color-gray text-sm">保养项</text>
        </template>
      </wd-cell>
      <wd-card type="rectangle" v-for="(item, index) in dataList" :key="item.id">
      <wd-card type="rectangle" v-for="(item, index) in dataList" :key="item.id" :class="['status-' + item.maintFun]">
        <template #title>
          <view class="flex justify-between">
            <view class="flex items-center menu-title-box">
          <view class="flex items-center">
              <view class="menu-indicator"></view>
              <view class="ml-1 text-sm align-center">
                <wd-text :text="item.maintName" :lines="1"></wd-text>
              <wd-text color="black" :text="item.maintName"></wd-text>
              </view>
            </view>
            <view class="flex items-center w-[20%] justify-end">
              <text class="text-color-gray text-mini">
                {{ item?.planTime }}
              </text>
            </view>
          </view>
        </template>
        <view class="flex h-[200rpx]" items-center>
          <image class="slot-img text-center" src="/static/ico/ico-platform.png" />
          <view class="flex-1 text-color-gray text-xs flex-row">
            <view class="mr-3 mt-2">保养单号: {{ item.maintCode }}</view>
            <view class="mr-3 mt-2">计划保养日期: {{ item.planTime }}</view>
            <view class="mr-3 mt-2">保养开始时间: {{ item.startTime }}</view>
            <view class="mr-3 mt-2">保养结束时间: {{ item.endTime }}</view>
            <view class="text-color-gray text-xs mt-2 flex">
              <text>状态:</text>
              <template v-if="item.status === '0'">
                <wd-icon class="icon-color-warning" name="books" size="34rpx"></wd-icon>
                <text class="ml-1">待保养</text>
              </template>
              <template v-else-if="item.status === '1'">
                <wd-icon class="icon-color-base" name="books" size="34rpx"></wd-icon>
                <text class="ml-1">保养中</text>
              </template>
              <template v-else-if="item.status === '2'">
                <wd-icon class="icon-color-purple" name="books" size="34rpx"></wd-icon>
                <text class="ml-1">待验证</text>
              </template>
              <template v-else-if="item.status === '3'">
                <wd-icon class="icon-color-success" name="check-outline" size="34rpx"></wd-icon>
                <text class="ml-1">已完成</text>
              </template>
              <text class="mx-3">|</text>
              <wd-icon class="icon-color-base" name="camera" size="30rpx"></wd-icon>
              <text class="ml-1">{{ item.maintUserName }}</text>
            </view>
          </view>
          <!--操作工或维修工角色-->
          <template v-if="isOperatorOrRepair()">
            <view class="flex flex-col justify-between"  v-if="item.status === '0'">
              <wd-button
                size="small"
                icon="edit-outline"
                @click.stop="handleStartMaint(item)"
              >
                å¼€å§‹ä¿å…»
              </wd-button>
              <wd-button
                class="mt-3"
                size="small"
                icon="edit-outline"
                @click.stop="handleMaintFinish(item)"
              >
                ä¸€é”®ä¿å…»
              </wd-button>
            </view>
            <wd-button
              v-if="item.status === '1'"
              size="small"
              icon="edit-outline"
              @click.stop="itemClick(item)"
            >
              ä¿å…»ä¸­
            </wd-button>
            <wd-button
            <!-- æ–°å¢žçŠ¶æ€æ˜¾ç¤ºï¼Œç»‘å®šç‚¹å‡»äº‹ä»¶ -->
            <view
              v-if="item.status === '2'"
              size="small"
              icon="edit-outline"
              @click.stop="itemClick(item)"
              class="ml-auto text-sm"
              style="width: 60px; text-align: end"
              :style="{ color: getStatusColor(item.maintFun) }"
              @click.stop="handleUndoAction(item)"
            >
              å¾…验证
            </wd-button>
            <wd-button
              v-if="item.status === '3'"
              size="small"
              icon="check-outline"
              @click.stop="itemClick(item)"
            >
              å·²å®Œæˆ
            </wd-button>
              {{ getStatusText(item.maintFun) }}
            </view>
          </view>
          </template>
          <!--管理员角色-->
          <template v-else-if="isLeader()">
            <wd-button v-if="item.status === '0'" size="small" icon="warn-bold" disabled>
              å¾…保养
            </wd-button>
            <wd-button v-if="item.status === '1'" size="small" icon="warn-bold" disabled>
              ä¿å…»ä¸­
            </wd-button>
        <!-- æŒ‰é’®åŒºåŸŸ -->
        <view v-if="item.maintFun == null" class="flex justify-around mt-2">
            <wd-button
              v-if="item.status === '2'"
            type="primary"
              size="small"
              icon="edit-outline"
              @click.stop="itemClick(item)"
            class="mr-2"
            @click.stop="handleAction(item, '0')"
            >
              å¾…验证
            æ£€æŸ¥
            </wd-button>
            <wd-button
              v-if="item.status === '3'"
            type="success"
              size="small"
              icon="check-outline"
              @click.stop="itemClick(item)"
            class="mr-2"
            @click.stop="handleAction(item, '1')"
            >
              å·²å®Œæˆ
            ä¿å…»
            </wd-button>
          </template>
          <wd-button type="warning" size="small" @click.stop="handleAction(item, '2')">
            ç»´ä¿®
          </wd-button>
        </view>
        <!-- æ­£æ–‡åŒºåŸŸ -->
        <view v-else class="mt-2">
          <!-- ä¿å…»è¯´æ˜ŽåŒºåŸŸ -->
          <view v-if="item.maintFun === '1'" class="mt-2">
            <wd-input
              v-model="item.maintDesc"
              placeholder="请输入保养说明"
              clearable
              :maxlength="200"
            />
          </view>
          <!-- ç»´ä¿®è¯´æ˜ŽåŒºåŸŸ -->
          <view v-if="item.maintFun === '2'" class="mt-2">
            <wd-input
              v-model="item.repairDesc"
              placeholder="请输入维修说明"
              clearable
              :maxlength="200"
            />
          </view>
          <!-- å¤‡ä»¶ä¿¡æ¯å½•入区域 -->
          <view v-if="item.spareParts && item.spareParts.length > 0" class="mt-2">
            <view
              v-for="(part, partIndex) in item.spareParts"
              :key="partIndex"
              class="flex justify-between mt-1"
            >
              <wd-input
                v-model="part.name"
                label="名称"
                label-width="100rpx"
                placeholder="备件名称"
              />
              <wd-input
                v-model="part.quantity"
                label="数量" label-width="100rpx"
                placeholder="数量" type="number" :maxlength="5" />
            </view>
          </view>
          <!-- ç»´ä¿®è¯´æ˜ŽåŒºåŸŸ -->
          <view v-if="item.maintFun === '2'" class="mt-2">
            <wd-button type="info" size="small" @click.stop="addSparePart(item, index)">
              æ·»åР备件
            </wd-button>
          </view>
          <!-- ä¿å…»äººå’Œä¿å…»æ—¶é—´åŒºåŸŸ -->
          <view class="flex justify-between mt-2">
            <text>保养人: {{ item.maintUserName }}</text>
            <text>保养时间: {{ item.endTime }}</text>
          </view>
        </view>
      </wd-card>
      <wd-cell>
        <template #title>
          <text class="text-color-gray">其他信息</text>
        </template>
      </wd-cell>
      <view class="h-[2px] w-full bg-base"></view>
      <wd-textarea
        label="特记事项"
        label-width="200rpx"
        type="textarea"
        v-model="maintSt.specialNote"
        v-model="specialNote"
        auto-height
        :maxlength="200"
        show-word-limit
@@ -233,22 +206,63 @@
        clearable
      />
    </view>
    <!-- æ–°å¢žæäº¤æŒ‰é’® -->
    <view class="flex justify-center mt-4">
      <wd-button type="primary" block size="large" @click="handleClickRight">提交</wd-button>
    </view>
  </z-paging>
  <!-- å¤‡ä»¶é€‰æ‹©å¼¹å‡ºå±‚ -->
  <wd-popup v-model="showSparePopup" position="bottom" height="33.33vh">
    <view class="flex justify-between p-2 bg-white">
      <wd-button type="text" @click="closeSparePopup">取消</wd-button>
      <wd-button type="text" @click="addOtherSparePart">其他</wd-button>
    </view>
    <wd-input
      v-model="searchKeyword"
      placeholder="请输入备件名称或型号"
      clearable
      @input="filterSpareParts"
    />
    <view class="p-2">
      <view
        v-for="(part, index) in sparePartsList"
        :key="index"
        class="flex justify-between items-center p-2 border-b"
        @click="selectFilteredSparePart(part)"
      >
        <text>{{ part.name }} ({{ part.code }})</text>
        <text>剩余: {{ part.actualStock }}</text>
      </view>
    </view>
  </wd-popup>
</template>
<script setup lang="ts">
import type { MaintStVO } from '@/service/maint.d'
import { getMaintSt, getMaintStOrderList, updateMaintOrder, updateMaintSt } from '@/service/maint'
import { getMaintSt, getMaintStOrderList, updateMaintOrder, updateMaintSt, updateMaintOrderBatch } from '@/service/maint'
import { ref, reactive } from 'vue'
import { useToast, useMessage } from 'wot-design-uni'
import { isLeader, isOperatorOrRepair } from '@/utils/RoleUtils'
import { formatDate } from '@/utils/DateUtils'
import { useUserStore } from "@/store";
import { getSpareList } from '@/service/spare'
const message = useMessage()
const toast = useToast()
const userStore = useUserStore()
const paging = ref(null)
const dataList = ref([])
const maintStId = ref('')
const showSparePopup = ref(false)
const selectedPartIndex = ref(-1)
const sparePartsList = ref([])
const searchKeyword = ref('')
const dataChange = ref(false)
const specialNote = ref('')
interface QueryParams {
  pageNum: number
  pageSize: number
@@ -272,7 +286,7 @@
const queryList = (pageNum?: number, pageSize?: number) => {
  const params: QueryParams = {
    pageNum,
    pageSize,
    pageSize: 30,
    maintCode: maintCode.value,
  }
  getMaintStOrderList(params)
@@ -307,6 +321,65 @@
  uni.navigateTo({
    url: `/pages/maint/order-detail?id=${item.id}`,
  })
}
// å¤‡ä»¶é€‰æ‹©ç›¸å…³é€»è¾‘
function addSparePart(item: any, index: number) {
  if (!item.spareParts) {
    item.spareParts = []
  }
  selectedPartIndex.value = index
  // item.spareParts.push({ name: '', quantity: '' })
  selectSparePart(item)
}
function selectSparePart(item: any) {
  showSparePopup.value = true
  loadSpareParts()
}
function loadSpareParts(value?: string) {
  getSpareList({ name: value }).then((res: any) => {
    sparePartsList.value = res.rows
  })
}
function filterSpareParts() {
  if (!searchKeyword.value) {
    loadSpareParts()
  } else {
    loadSpareParts(searchKeyword.value)
  }
}
function selectFilteredSparePart(part: any) {
  dataList.value[selectedPartIndex.value].spareParts.push({
    id: part.id,
    name: part.name,
    quantity: '',
  })
  closeSparePopup()
}
function closeSparePopup() {
  showSparePopup.value = false
  selectedPartIndex.value = -1
  searchKeyword.value = ''
}
function confirmSpareSelection() {
  closeSparePopup()
}
function addOtherSparePart() {
  dataList.value[selectedPartIndex.value].spareParts.push({
    id:  '',
    name: '',
    quantity: '',
  })
  closeSparePopup();
}
/**
@@ -380,19 +453,36 @@
}
function handleUpdateMaintSt() {
  if (maintSt.orderCount !== maintSt.wcCount) {
    toast.info('请先完成所有保养项!')
  // if (maintSt.orderCount !== maintSt.wcCount) {
  //   toast.info('请先完成所有保养项!')
  //   return false
  // }
  // å…ˆåˆ¤æ–­ä¿å…»é¡¹æ˜¯å¦æœ‰æ›´æ”¹
  console.log('handleUPdateMaintst', dataChange.value)
  if (!dataChange.value) {
    message.alert('请操作后提交!')
    return false
  }
  // è¿‡æ»¤æŽ‰ maintFun为空的保养项
  const submitList = dataList.value.filter((item) => item.maintFun != null)
  // å¦‚æžœmaintFun为1的则判断保养说明不能为空,如果为2则判断维修说明不能为空
  if (submitList.some((item) => item.maintFun === '1' && !item.maintDesc)) {
    toast.info('请填写保养说明!')
    return false
  } else if (submitList.some((item) => item.maintFun === '2' && !item.repairDesc)) {
    toast.info('请填写维修说明!')
    return false
  }
  const data: any = Object.assign(
    {},
    {
      id: maintSt.id,
      orderCount: maintSt.orderCount,
      wcCount: maintSt.wcCount,
      wcCount: submitList.length,
      status: maintSt.status,
      specialNote: maintSt.specialNote,
      specialNote: specialNote.value,
    },
  )
  if (data.orderCount === data.wcCount) {
@@ -403,6 +493,7 @@
      msg: '确定更新工单汇总数据?',
      title: '提示',
      beforeConfirm: ({ resolve }) => {
        updateMaintOrderBatch({maintOrderList:submitList})
        updateMaintSt(data)
          .then((res: any) => {
            resolve(true)
@@ -457,6 +548,90 @@
  maintStId.value = options.id
  reloadData()
})
watch(
  () => [...dataList.value, specialNote.value ], // ä½¿ç”¨æ‰©å±•运算符创建新数组以触发监听
  (newVal, oldVal) => {
    if (oldVal.length > 0) {
      console.log('dataChange',oldVal,  newVal)
      dataChange.value = true
    }
  },
  { deep: true },
)
/**
 * å¤„理按钮点击事件
 * @param item ä¿å…»é¡¹
 * @param action æ“ä½œç±»åž‹ï¼ˆcheck/check/maintain/repair)
 */
function handleAction(item: any, action: number) {
  // è®¾ç½®å½“前选中的操作类型
  item.maintFun = action
  item.status = '2'
  // è‡ªåŠ¨å¡«å……ä¿å…»äººå’Œæ—¶é—´
  item.maintUserName = userStore?.userInfo?.realName || ''
  const now = new Date()
  item.endTime = `${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('handleAction', item)
}
/**
 * æ’¤é”€ä¿å…»æ“ä½œ
 * @param item ä¿å…»é¡¹
 */
function handleUndoAction(item: any) {
  message
    .confirm({
      msg: '确定撤销当前操作?',
      title: '提示',
      beforeConfirm: ({ resolve }) => {
        // é‡ç½®çŠ¶æ€å’Œç›¸å…³å­—æ®µ
        item.maintFun = null;
        item.status = '0';
        item.maintDesc = '';
        item.repairDesc = '';
        item.spareParts = [];
        item.maintUserName = '';
        item.endTime = '';
        resolve(true);
      },
    })
    .then(() => {
      toast.success('操作已撤销');
      dataChange.value = true;
    })
    .catch((error) => {
      console.log(error);
    });
}
// æ–°å¢žæ–¹æ³•:获取状态文本
function getStatusText(maintFun: string): string {
  switch (maintFun) {
    case '0':
      return '已检查';
    case '1':
      return '已保养';
    case '2':
      return '已维修';
    default:
      return '';
  }
}
// æ–°å¢žæ–¹æ³•:获取状态颜色
function getStatusColor(maintFun: string): string {
  switch (maintFun) {
    case '0':
      return '#007bff'; // æ£€æŸ¥æŒ‰é’®é¢œè‰²
    case '1':
      return '#28a745'; // ä¿å…»æŒ‰é’®é¢œè‰²
    case '2':
      return '#ffc107'; // ç»´ä¿®æŒ‰é’®é¢œè‰²
    default:
      return '#000';
  }
}
</script>
<style scoped lang="scss">
@@ -472,7 +647,7 @@
}
.text-mini {
  font-size: 22rpx;
  font-size: 24rpx;
}
.menu-indicator {
@@ -494,7 +669,7 @@
}
:deep(.wd-navbar__text) {
  font-size: 26rpx;
  font-size: 28rpx;
  color: white;
}
@@ -502,6 +677,20 @@
:deep(.wd-navbar__title) {
  color: white;
  font-weight: 0;
  font-size: 32rpx;
  font-size: 34rpx;
}
// æ–°å¢žæ ·å¼ï¼šåŠ¨æ€è®¾ç½®å¡ç‰‡èƒŒæ™¯è‰²
:deep(.wd-card) {
  transition: background-color 0.3s ease;
  &.status-0 {
    background-color: #e6f7ff; // æ£€æŸ¥çŠ¶æ€èƒŒæ™¯è‰²
  }
  &.status-1 {
    background-color: #f6ffed; // ä¿å…»çŠ¶æ€èƒŒæ™¯è‰²
  }
  &.status-2 {
    background-color: #fffbe6; // ç»´ä¿®çŠ¶æ€èƒŒæ™¯è‰²
  }
}
</style>
eims-ui-mobile/src/pages/maint/maint-st.vue
@@ -43,19 +43,19 @@
        <view class="flex h-[140rpx]" items-center>
          <image class="slot-img text-center" src="/static/images/camera.png" />
          <view class="flex-1">
            <view class="text-color-gray text-xs mt-1 flex">
            <view class="text-color-gray text-sm mt-1 flex">
              <text class="mr-3">工单总数: {{ item.orderCount }}</text>
              |
              <text class="mx-3">已完成: {{ item.wcCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
            <view class="text-color-gray text-sm mt-2 flex">
              <text class="mr-3">待保养: {{ item.dbyCount }}</text>
              |
              <text class="mx-3">保养中: {{ item.byCount }}</text>
              |
              <text class="ml-3">待验证: {{ item.dyzCount }}</text>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">
            <view class="text-color-gray text-sm mt-2 flex">
              <text>状态:</text>
              <template v-if="item.status === '1'">
                <wd-icon class="icon-color-success" name="check-outline" size="34rpx"></wd-icon>
@@ -66,7 +66,7 @@
                <text class="ml-1">进行中</text>
              </template>
            </view>
            <view class="text-color-gray text-xs mt-2 flex">创建时间: {{ item.createTime }}</view>
            <view class="text-color-gray text-sm mt-2 flex">创建时间: {{ item.createTime }}</view>
          </view>
          <wd-button size="small" icon="edit-outline" @click.stop="itemClick(item)">明细</wd-button>
        </view>
@@ -208,7 +208,7 @@
  height: 40rpx;
}
.text-mini {
  font-size: 22rpx;
  font-size: 24rpx;
}
.menu-indicator {
eims-ui-mobile/src/pages/repair/repair-fb.vue
@@ -29,20 +29,20 @@
      <template #title>
        <view class="flex items-center menu-title-box">
          <view class="menu-indicator"></view>
          <view class="ml-1 text-xs">维修概览</view>
          <view class="ml-1 text-sm">维修概览</view>
        </view>
      </template>
      <wd-steps :active="repairRecordList.length" vertical class="px-4">
        <wd-step v-for="(item, index) in repairRecordList">
          <template #title>
            <view class="flex items-center menu-title-box">
              <view class="ml-1 text-xs">{{ item?.operaResult }}</view>
              <view class="ml-1 text-sm">{{ item?.operaResult }}</view>
            </view>
          </template>
          <template #description>
            <view class="flex items-center menu-title-box">
              <view class="ml-1 text-xs">{{ item?.operaUserName }}</view>
              <view class="ml-1 text-xs">{{ item?.handleTime }}</view>
              <view class="ml-1 text-sm">{{ item?.operaUserName }}</view>
              <view class="ml-1 text-sm">{{ item?.handleTime }}</view>
            </view>
          </template>
        </wd-step>
@@ -53,7 +53,7 @@
      <template #title>
        <view class="flex items-center menu-title-box">
          <view class="menu-indicator"></view>
          <view class="ml-1 text-xs">维修评价</view>
          <view class="ml-1 text-sm">维修评价</view>
        </view>
      </template>
      <wd-cell title="维修满意度">
eims-ui-mobile/src/pages/repair/req-list.vue
@@ -52,7 +52,7 @@
          <view class="flex justify-between items-center">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <text class="ml-1 text-xs">{{ item.code }}</text>
              <text class="ml-1 text-sm">{{ item.code }}</text>
            </view>
            <view>
@@ -114,7 +114,7 @@
                  <text>报修人: {{ item.reqUserName }}</text>
                </view>
              </view>
              <view class="text-color-gray text-xs mt-1">
              <view class="text-color-gray text-sm mt-1">
                {{ item.location }} {{ item.madeIn }}
              </view>
            </view>
eims-ui-mobile/src/pages/repair/res-list.vue
@@ -50,7 +50,7 @@
          <view class="flex justify-between items-center">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <text class="ml-1 text-xs">{{ item.resCode }}</text>
              <text class="ml-1 text-sm">{{ item.resCode }}</text>
              <text class="text-color-gray ml-2 text-mini">接单:{{ item.createTime }}</text>
            </view>
eims-ui-mobile/src/pages/scan/index.vue
@@ -19,7 +19,7 @@
          <view class="flex justify-between items-baseline">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <text class="ml-1 text-xs">{{ model.assetNo }}</text>
              <text class="ml-1 text-sm">{{ model.assetNo }}</text>
              <wd-tag v-if="model.status === '0'" class="ml-2" bg-color="cyan">试用</wd-tag>
              <wd-tag v-else-if="model.status === '1'" class="ml-2" type="success">使用</wd-tag>
              <wd-tag v-else-if="model.status === '2'" class="ml-2" type="danger">停用</wd-tag>
@@ -41,7 +41,7 @@
              {{ model.equName }}
              <text class="text-color-gray ml-2 text-mini">{{ model.modelNo }}</text>
            </view>
            <view class="text-color-gray text-xs mt-1">
            <view class="text-color-gray text-sm mt-1">
              {{ model.location }} | {{ model.madeIn }}
            </view>
          </view>
eims-ui-mobile/src/pages/spare/spare-list.vue
@@ -22,7 +22,7 @@
          <view class="flex justify-between items-baseline">
            <view class="flex items-center menu-title-box">
              <view class="menu-indicator"></view>
              <text class="ml-1 text-xs">{{ item.code }}</text>
              <text class="ml-1 text-sm">{{ item.code }}</text>
            </view>
            <view>
@@ -38,10 +38,10 @@
              {{ item.name }}
              <text class="text-color-gray ml-2 text-mini">{{ item.modelNo }}</text>
            </view>
            <view class="text-color-gray text-xs mt-1">
            <view class="text-color-gray text-sm mt-1">
              åº“å­˜: {{ item.actualStock }} | å•ä»·: {{ item.referPrice }}
            </view>
            <view class="text-color-gray text-xs mt-1">
            <view class="text-color-gray text-sm mt-1">
              ä¾›åº”商: {{ item.supplier }}
            </view>
          </view>
eims-ui-mobile/src/service/maint.ts
@@ -48,3 +48,10 @@
export const updateMaintOrder = (data: any) => {
  return http.put<void>('/eims/maintOrder', data)
}
/**
 * æ‰¹é‡æ›´æ–°ä¿å…»å·¥å•
 */
export const updateMaintOrderBatch = (data: any) => {
  return http.put<void>('/eims/maintOrder/editBatch', data)
}
eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java
@@ -219,6 +219,16 @@
        String CK = "2"; // é¢†ç”¨å‡ºåº“
    }
    /**
     * ä¿å…»æ–¹æ³•
     */
    String MAINT_METHOD = "maint_method";
    interface MAINT_METHOD_DETAIL {
        String CHECK = "0";
        String MAINT = "1";
        String REPAIR = "2";
    }
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsMaintOrderController.java
@@ -11,8 +11,10 @@
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.core.constant.DictConstants;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.eims.domain.bo.MaintOrderBo;
import org.dromara.eims.domain.vo.MaintOrdeGroupVo;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
@@ -79,7 +81,14 @@
        params.put("beginPlanTime",startOfMonth);
        params.put("endPlanTime",endOfMonth);
        return eimsMaintOrderService.queryPageListCustom(bo, pageQuery);
        TableDataInfo<EimsMaintOrderVo> tableDataInfo = eimsMaintOrderService.queryPageListCustom(bo, pageQuery);
        tableDataInfo.getRows().forEach(e -> {
            // å¦‚果保养方法是维修,查询关联的备件
            if (DictConstants.MAINT_METHOD_DETAIL.REPAIR.equals(e.getMaintFun())) {
                e.setSpareParts(eimsMaintOrderService.querySpareParts(e.getMaintCode()));
            }
        });
        return tableDataInfo;
    }
    /**
@@ -127,6 +136,14 @@
        return toAjax(eimsMaintOrderService.updateByBo(bo));
    }
    @SaCheckPermission("eims:maintOrder:edit")
    @Log(title = "保养工单-批量修改", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping("editBatch")
    public R<Void> editBatch(@Validated(EditGroup.class) @RequestBody MaintOrderBo bo) {
        return toAjax(eimsMaintOrderService.updateBatchByBo(bo));
    }
    /**
     * åˆ é™¤ä¿å…»å·¥å•
     *
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsMaintOrder.java
@@ -113,5 +113,14 @@
     */
    private String remark;
    /**
     * ä¿å…»æ–¹å¼ï¼ˆ0-检查,1-保养,2-维修)
     */
    private String maintFun;
    /**
     * ç»´ä¿®è¯´æ˜Ž
     */
    private String repairDesc;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsMaintOrderBo.java
@@ -9,7 +9,10 @@
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.eims.domain.EimsSpareInout;
/**
 * ä¿å…»å·¥å•业务对象 eims_maint_order
@@ -114,9 +117,21 @@
     */
    private String remark;
    /**
     * ä¿å…»æ–¹å¼ï¼ˆ0-检查,1-保养,2-维修)
     */
    private String maintFun;
    /**
     * ç»´ä¿®è¯´æ˜Ž
     */
    private String repairDesc;
    // å…³è”表字段
    private String equName;
    private String maintUserName;
    private List<EimsSpareBo> spareParts;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareBo.java
@@ -33,7 +33,6 @@
    /**
     * ç±»åž‹
     */
    @NotNull(message = "类型不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long type;
    /**
@@ -45,7 +44,6 @@
    /**
     * å¤‡ä»¶ç¼–码
     */
    @NotBlank(message = "备件编码不能为空", groups = { AddGroup.class, EditGroup.class })
    private String code;
    /**
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareInoutdtBo.java
@@ -29,7 +29,7 @@
    /**
     * å‡ºåº“单或入库单id
     */
    @NotNull(message = "出库单或入库单id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long inoutId;
    /**
@@ -41,13 +41,13 @@
    /**
     * ä¹‹å‰åº“å­˜
     */
    @NotNull(message = "之前库存不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long beforeStock;
    /**
     * å®žé™…库存
     */
    @NotNull(message = "实际库存不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long actualStock;
    /**
@@ -59,13 +59,13 @@
    /**
     * å•ä»·
     */
    @NotNull(message = "单价不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long unitPrice;
    /**
     * é‡‘额
     */
    @NotNull(message = "金额不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long amount;
    /**
@@ -74,4 +74,7 @@
    private String remark;
    private String spareName;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/MaintOrderBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
package org.dromara.eims.domain.bo;
import lombok.Data;
import java.util.List;
@Data
public class MaintOrderBo {
    private List<EimsMaintOrderBo> maintOrderList;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsMaintOrderVo.java
@@ -12,11 +12,12 @@
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.eims.domain.bo.EimsSpareBo;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
@@ -155,5 +156,17 @@
    @ExcelProperty(value = "备注")
    private String remark;
    /**
     * ä¿å…»æ–¹å¼ï¼ˆ0-检查,1-保养,2-维修)
     */
    private String maintFun;
    /**
     * ç»´ä¿®è¯´æ˜Ž
     */
    private String repairDesc;
    private List<EimsSpareVo> spareParts;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareInoutdtVo.java
@@ -94,4 +94,6 @@
    private String unit;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsMaintOrderService.java
@@ -1,9 +1,12 @@
package org.dromara.eims.service;
import org.dromara.eims.domain.bo.EimsSpareBo;
import org.dromara.eims.domain.bo.MaintOrderBo;
import org.dromara.eims.domain.vo.EimsMaintOrderVo;
import org.dromara.eims.domain.bo.EimsMaintOrderBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.eims.domain.vo.EimsSpareVo;
import org.dromara.eims.domain.vo.MaintOrdeGroupVo;
import java.util.Collection;
@@ -59,6 +62,14 @@
    Boolean updateByBo(EimsMaintOrderBo bo);
    /**
     * æ‰¹é‡ä¿®æ”¹ä¿å…»å·¥å•
     *
     * @param bo ä¿å…»å·¥å•业务对象
     * @return æ“ä½œç»“æžœ
     */
    boolean updateBatchByBo(MaintOrderBo bo);
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤ä¿å…»å·¥å•信息
     *
     * @param ids     å¾…删除的主键集合
@@ -83,4 +94,6 @@
     * @return
     */
    TableDataInfo<MaintOrdeGroupVo> queryPageGroupList(EimsMaintOrderBo bo, PageQuery pageQuery);
    List<EimsSpareVo> querySpareParts(String id);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsMaintOrderServiceImpl.java
@@ -20,17 +20,19 @@
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.eims.domain.EimsMaintPlan;
import org.dromara.eims.domain.bo.EimsMaintPlanBo;
import org.dromara.eims.domain.vo.EimsEquVo;
import org.dromara.eims.domain.vo.MaintOrdeGroupVo;
import org.dromara.eims.domain.EimsSpareInout;
import org.dromara.eims.domain.bo.EimsSpareBo;
import org.dromara.eims.domain.bo.EimsSpareInoutBo;
import org.dromara.eims.domain.bo.MaintOrderBo;
import org.dromara.eims.domain.vo.*;
import org.dromara.eims.mapper.EimsEquMapper;
import org.dromara.eims.mapper.EimsMaintPlanMapper;
import org.dromara.eims.service.IEimsSpareInoutService;
import org.dromara.system.domain.SysDept;
import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.mapper.SysDeptMapper;
import org.springframework.stereotype.Service;
import org.dromara.eims.domain.bo.EimsMaintOrderBo;
import org.dromara.eims.domain.vo.EimsMaintOrderVo;
import org.dromara.eims.domain.EimsMaintOrder;
import org.dromara.eims.mapper.EimsMaintOrderMapper;
import org.dromara.eims.service.IEimsMaintOrderService;
@@ -52,6 +54,7 @@
    private final EimsMaintPlanMapper planMapper;
    private final SysDeptMapper sysDeptMapper;
    private final EimsEquMapper equMapper;
    private final IEimsSpareInoutService spareInoutService;
    /**
     * æŸ¥è¯¢ä¿å…»å·¥å•
@@ -293,6 +296,45 @@
    }
    /**
     * æ‰¹é‡ä¿®æ”¹ä¿å…»å·¥å•
     *
     * @param bo ä¿å…»å·¥å•
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    @Override
    public boolean updateBatchByBo(MaintOrderBo bo) {
        LoginUser loginUser = LoginHelper.getLoginUser();
        List<EimsMaintOrder> list = MapstructUtils.convert(bo.getMaintOrderList(),  EimsMaintOrder.class);
        list.forEach(e -> {
            e.setStatus(DictConstants.MAINT_ORDER_STATUS_DETAIL.DAIYANZHENG);
            e.setEndTime(new Date());
            e.setMaintUser(loginUser.getUserId());
            e.setUpdateTime(new Date());
        });
        bo.getMaintOrderList().forEach(item -> {
            // åˆ¤æ–­å¤‡ä»¶åˆ—表是否为空,不为空则新增一个备件出库单,同时增加出库明细
            if (item.getSpareParts() != null && item.getSpareParts().size() > 0) {
                EimsSpareInoutBo spareInoutBo = new EimsSpareInoutBo();
                // æ ¹æ®æ—¥æœŸç”Ÿæˆå•号,格式为CK+日期+流水号
                spareInoutBo.setOrderCode("CK"+DateUtils.dateTimeNow("yyyyMMddHHmmss"));
                spareInoutBo.setOrderTime(new Date());
                spareInoutBo.setType(DictConstants.SPARE_INOUT_TYPE_DETAIL.CK);
                spareInoutBo.setPartnerName(loginUser.getNickname());
                spareInoutBo.setSpareList(item.getSpareParts());
                spareInoutBo.setChargeUser(loginUser.getUserId());
                spareInoutBo.setChargeDept(loginUser.getDeptId());
                spareInoutBo.setAssociatedOrder(item.getMaintCode());
                // ä¿å­˜è¿›æ•°æ®åº“
                Boolean b = spareInoutService.insertByBo(spareInoutBo);
            }
        });
        return baseMapper.updateBatchById(list);
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     */
    private void validEntityBeforeSave(EimsMaintOrder entity){
@@ -326,4 +368,20 @@
        }
        return String.join(StringUtils.SEPARATOR, list);
    }
    @Override
    public List<EimsSpareVo> querySpareParts(String code) {
        // æŸ¥è¯¢å…³è”的备件,关系如下:备件出入库单的associatedOrder å…³è”传入的code,备件出入库明细通过出入库单的id关联,备件名称通过出入库明细中的spareId关联
        EimsSpareInoutBo bo = new EimsSpareInoutBo();
        bo.setAssociatedOrder(code);
        List<EimsSpareInoutVo> list = spareInoutService.queryList(bo);
        if (!list.isEmpty()) {
            // æŸ¥è¯¢å¤‡ä»¶æ˜Žç»†å’Œåç§°
            EimsSpareInoutVo eimsSpareInoutVo = spareInoutService.queryById(list.get(0).getId());
            return eimsSpareInoutVo.getSpareList();
        }
        return List.of();
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareInoutServiceImpl.java
@@ -137,57 +137,60 @@
            bo.setId(add.getId());
        }
        //入库+库存  å‡ºåº“-库存  ï¼ˆé»˜è®¤å…¥åº“)
        int  OperationType;
         //出库
         if(bo.getType().equals(DictConstants.SPARE_INOUT_TYPE_DETAIL.CK)){
            OperationType =  -1;
        } else {
             OperationType = 1;
         }
        int OperationType = bo.getType().equals(DictConstants.SPARE_INOUT_TYPE_DETAIL.CK) ? -1 : 1;
        List<EimsSpareBo> spareList = Optional.ofNullable(bo.getSpareList()).orElse(new ArrayList<>());
        // åˆå¹¶ä¸¤æ¬¡å¾ªçŽ¯ä¸ºä¸€æ¬¡å¾ªçŽ¯
        List<EimsSpareInoutdt> dtList = new ArrayList<>();
        List<EimsSpare> updateSpareList = new ArrayList<>();
        for (EimsSpareBo eimsSpareBo : spareList) {
            EimsSpare spare = spareMapper.selectById(eimsSpareBo.getId());
            if (spare == null) {
                spare = new EimsSpare();
                spare.setName(eimsSpareBo.getName());
                spare.setCode(eimsSpareBo.getCode());
                spare.setActualStock(100L);
                spareMapper.insert(spare);
                eimsSpareBo.setId(spare.getId());
            }
        // æ’入出库明细
        List<EimsSpareInoutdt> dtList = spareList.stream()
            .map(eimsSpareBo -> {
                EimsSpareInoutdt dt = new EimsSpareInoutdt();
                dt.setInoutId(add.getId());
                dt.setSpareId(eimsSpareBo.getId());
                dt.setBeforeStock(eimsSpareBo.getActualStock());
                // æ³¨æ„å…¥åº“出库
                dt.setActualStock(eimsSpareBo.getActualStock() + (eimsSpareBo.getQuantity() * OperationType));
            dt.setSpareId(spare.getId());
            dt.setBeforeStock(spare.getActualStock());
            dt.setActualStock(spare.getActualStock() + (eimsSpareBo.getQuantity() * OperationType));
                dt.setQuantity(eimsSpareBo.getQuantity());
                // è®¾ç½®å•价和金额
                Optional.ofNullable(eimsSpareBo.getReferPrice()).ifPresent(referPrice -> {
            Optional.ofNullable(spare.getReferPrice()).ifPresent(referPrice -> {
                    dt.setUnitPrice(referPrice);
                    dt.setAmount(referPrice.multiply(BigDecimal.valueOf(eimsSpareBo.getQuantity())));
                });
            dtList.add(dt);
                return dt;
            })
            .toList();
            // æ›´æ–°å¤‡ä»¶åº“å­˜
            EimsSpare updatedSpare = new EimsSpare();
            updatedSpare.setId(spare.getId());
            updatedSpare.setActualStock(spare.getActualStock() + (eimsSpareBo.getQuantity() * OperationType));
            Optional.ofNullable(spare.getReferPrice()).ifPresent(referPrice -> {
                updatedSpare.setStockAmount(referPrice.multiply(BigDecimal.valueOf(updatedSpare.getActualStock())));
            });
            updateSpareList.add(updatedSpare);
        }
        // æ‰¹é‡æ’入数据
        if (!dtList.isEmpty()) {
            inoutdtMapper.insertBatch(dtList);
        }
        // æ›´æ–°å¤‡ä»¶çš„库存
        List<EimsSpare> updateSpareList = spareList.stream().map(spareBo -> {
            EimsSpare spare = new EimsSpare();
            spare.setId(spareBo.getId());
            spare.setActualStock(spareBo.getActualStock() + (spareBo.getQuantity() * OperationType));
            // è®¾ç½®å•价和金额
            Optional.ofNullable(spareBo.getReferPrice()).ifPresent(referPrice -> {
                spare.setStockAmount(referPrice.multiply(BigDecimal.valueOf(spare.getActualStock())));
            });
            return spare;
        }).toList();
        // æ‰¹é‡æ›´æ–°å¤‡ä»¶åº“å­˜
        if (!updateSpareList.isEmpty()) {
            spareMapper.updateBatchById(updateSpareList);
        }
        return flag;
    }