车间能级提升-智能设备管理系统
zhuguifei
2025-05-26 bbfd68648872621be182fd51f3a56a09cd21e09e
添加首页数据接口
已添加17个文件
已修改15个文件
1825 ■■■■ 文件已修改
eims-ui/apps/web-antd/public/static/menu/ico1.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico10.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico11.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico12.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico13.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico2.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico3.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico4.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico5.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico6.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico7.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico8.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico9.png 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/dashboard/index.ts 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/repair-res/index.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/equ-detail/index.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/fixture/data.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/fixture/fixture-import-modal.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/fixture/index.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/index.vue 593 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/packages/@core/base/shared/src/constants/dict-enum.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/BoardController.java 376 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsFixtureController.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsFixture.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsInspectSt.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsFixtureBo.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsInspectStBo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsFixtureImportVo.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsFixtureVo.java 162 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsInspectStVo.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/listener/EimsFixtureImportListener.java 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/public/static/menu/ico1.png
eims-ui/apps/web-antd/public/static/menu/ico10.png
eims-ui/apps/web-antd/public/static/menu/ico11.png
eims-ui/apps/web-antd/public/static/menu/ico12.png
eims-ui/apps/web-antd/public/static/menu/ico13.png
eims-ui/apps/web-antd/public/static/menu/ico2.png
eims-ui/apps/web-antd/public/static/menu/ico3.png
eims-ui/apps/web-antd/public/static/menu/ico4.png
eims-ui/apps/web-antd/public/static/menu/ico5.png
eims-ui/apps/web-antd/public/static/menu/ico6.png
eims-ui/apps/web-antd/public/static/menu/ico7.png
eims-ui/apps/web-antd/public/static/menu/ico8.png
eims-ui/apps/web-antd/public/static/menu/ico9.png
eims-ui/apps/web-antd/src/api/eims/dashboard/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
import { requestClient } from '#/api/request';
enum Api {
  equStatu = '/eims/board/equStatu',
  monthMaint = '/eims/board/monthMainit',
  monthReq = '/eims/board/monthReq',
  spareWarn = '/eims/board/spareWarn',
  todayInsp = '/eims/board/todayInsp',
  todoList = '/eims/board/todoList'
}
/**
 * æŸ¥è¯¢ã€todolist】板块数据
 * @param query
 * @returns {*}
 */
export function queryTodoList(params?: any) {
  return requestClient.get<any[]>(Api.todoList, { params });
}
/**
 * æŸ¥è¯¢ã€å¤‡ä»¶åº“存预警】板块数据
 * @param query
 * @returns {*}
 */
export function querySpareWarn(params?: any) {
  return requestClient.get<any>(Api.spareWarn, { params });
}
/**
 * æŸ¥è¯¢ã€è®¾å¤‡çŠ¶æ€ã€‘æ¿å—æ•°æ®
 * @param query
 * @returns {*}
 */
export function queryEquStatu(params?: any) {
  return requestClient.get<any>(Api.equStatu, { params });
}
/**
 * æŸ¥è¯¢ã€ç‚¹æ£€ç»Ÿè®¡ã€‘板块数据
 * @param query
 * @returns {*}
 */
export function queryTodayInsp(params?: any) {
  return requestClient.get<any>(Api.todayInsp, { params });
}
/**
 * æŸ¥è¯¢ã€æœ¬æœˆç»´ä¿®ã€‘板块数据
 * @param query
 * @returns {*}
 */
export function queryMonthReq(params?: any) {
  return requestClient.get<any>(Api.monthReq, { params });
}
/**
 * æŸ¥è¯¢ã€æœ¬æœˆä¿å…»ã€‘板块数据
 * @param query
 * @returns {*}
 */
export function queryMonthMainit(params?: any) {
  return requestClient.get<any>(Api.monthMaint, { params });
}
eims-ui/apps/web-antd/src/api/eims/repair-res/index.ts
@@ -1,6 +1,6 @@
import type { RepairResVO } from './model';
import type { ID, IDS } from '#/api/common';
import type { ID, IDS, PageResult } from '#/api/common';
import { commonExport } from '#/api/helper';
import { requestClient } from '#/api/request';
@@ -18,7 +18,8 @@
 */
export function listRepairRes(params?: any) {
  return requestClient.get<RepairResVO[]>(Api.repairResList, { params });
  // return requestClient.get<RepairResVO[]>(Api.repairResList, { params });
  return requestClient.get<PageResult<RepairResVO>>(Api.repairResList, { params });
}
/**
eims-ui/apps/web-antd/src/views/eims/equ-detail/index.vue
@@ -60,21 +60,21 @@
          <div class="w-3/4 min-w-[640px] flex align-center justify-around">
            <Divider class="h-full" type="vertical" />
            <div class="flex w-1/4 h-full cursor-pointer items-center justify-center rounded-sm hover:bg-gray-100" style="background: #f2f6fe">
              <Image :preview="false" :width="40" src="/src/assets/logo.png" />
              <Image :preview="false" :width="40" src="/static/menu/ico7.png" />
              <div class="flex flex-col ml-6 mr-2">
                <span class="text-xl font-bold">100%</span>
                <span class="mt-5 text-gray-500">设备健康度</span>
              </div>
            </div>
            <div class="flex w-1/4 h-full cursor-pointer items-center justify-center rounded-sm hover:bg-gray-100" style="background: #fcf8ee">
              <Image :preview="false" :width="40" src="/src/assets/logo.png" />
              <Image :preview="false" :width="40" src="/static/menu/ico1.png" />
              <div class="flex flex-col ml-6 mr-2">
                <span class="text-xl font-bold">86.6h</span>
                <span class="text-xl font-bold">0.0h</span>
                <span class="mt-5 text-gray-500">平均故障时间</span>
              </div>
            </div>
            <div class="flex w-1/4 h-full cursor-pointer items-center justify-center rounded-sm hover:bg-gray-100" style="background: #ecf5de">
              <Image :preview="false" :width="40" src="/src/assets/logo.png" />
              <Image :preview="false" :width="40" src="/static/menu/ico5.png" />
              <div class="flex flex-col ml-6 mr-2">
                <span class="text-xl font-bold">0元</span>
                <span class="mt-5 text-gray-500">维保总费用</span>
@@ -120,12 +120,12 @@
              </DescriptionsItem>
            </Descriptions>
          </TabPane>
          <TabPane key="2" tab="ç»´ä¿®">Content of Tab Pane 2</TabPane>
<!--          <TabPane key="2" tab="ç»´ä¿®">Content of Tab Pane 2</TabPane>
          <TabPane key="3" tab="保养">Content of Tab Pane 3</TabPane>
          <TabPane key="4" tab="点检">Content of Tab Pane 3</TabPane>
          <TabPane key="5" tab="备件">Content of Tab Pane 3</TabPane>
          <TabPane key="6" tab="附件">Content of Tab Pane 3</TabPane>
          <TabPane key="7" tab="设备履历">Content of Tab Pane 3</TabPane>
          <TabPane key="7" tab="设备履历">Content of Tab Pane 3</TabPane>-->
          <TabPane key="8" tab="试产记录">
            <EquTrial :equ-detail-flag="true" :equ-id="equId" />
          </TabPane>
eims-ui/apps/web-antd/src/views/eims/fixture/data.tsx
@@ -90,7 +90,8 @@
    title: '工具(治具)名称',
    field: 'fixtureName',
    minWidth: 140,
    fixed: 'left'
    fixed: 'left',
    slots: { default: 'fixtureName' }
  },
  {
eims-ui/apps/web-antd/src/views/eims/fixture/fixture-import-modal.vue
@@ -8,7 +8,7 @@
import { Modal, Switch, Upload } from 'ant-design-vue';
import { downloadImportTemplate, equImportData } from '#/api/eims/equ';
import { downloadImportTemplate, fixtureImportData } from '#/api/eims/fixture';
import { commonDownloadExcel } from '#/utils/file/download';
const emit = defineEmits<{ reload: [] }>();
@@ -34,7 +34,7 @@
      file: fileList.value[0]!.originFileObj as Blob,
      updateSupport: unref(checked),
    };
    const { code, msg } = await equImportData(data);
    const { code, msg } = await fixtureImportData(data);
    let modal = Modal.success;
    if (code === 200) {
      emit('reload');
@@ -69,7 +69,7 @@
  <BasicModal
    :close-on-click-modal="false"
    :fullscreen-button="false"
    title="设备导入"
    title="工具导入"
  >
    <!-- z-index不设置会遮挡模板下载loading -->
    <!-- æ‰‹åŠ¨å¤„ç† è€Œä¸æ˜¯æ”¾å…¥æ–‡ä»¶å°±ä¸Šä¼  -->
eims-ui/apps/web-antd/src/views/eims/fixture/index.vue
@@ -108,7 +108,7 @@
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams),
    cellClick: (e: any) => {
      const { row } = e;
      handleBorroeList(row);
      // handleBorroeList(row);
    }
  }
});
@@ -300,9 +300,9 @@
            <a-button v-access:code="['eims:fixture:export']" @click="handleDownloadExcel">
              {{ $t('pages.common.export') }}
            </a-button>
            <!-- <a-button v-access:code="['eims:fixture:import']" @click="handleImport">
             <a-button v-access:code="['eims:fixture:import']" @click="handleImport">
              {{ $t('pages.common.import') }}
            </a-button>-->
            </a-button>
            <a-button
              :disabled="!vxeCheckboxChecked(tableApi)"
              danger
@@ -318,6 +318,12 @@
          </Space>
        </template>
        <template #fixtureName="{ row }">
          <Space>
            <a-button type="link" @click="handleBorroeList(row)"> {{ row.fixtureName }}</a-button>
          </Space>
        </template>
        <template #action="{ row }">
          <Space>
            <ghost-button v-access:code="['eims:fixture:edit']" @click.stop="handleEdit(row)">
eims-ui/apps/web-antd/src/views/index.vue
@@ -1,51 +1,166 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { computed, onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/plugins/echarts';
import { AlertTwoTone, NotificationTwoTone, TagTwoTone } from '@ant-design/icons-vue';
import { Avatar, Card, Col, Image, Row } from 'ant-design-vue';
import { Card, Col, Image, Row, Skeleton } from 'ant-design-vue';
import { queryEquStatu, queryMonthMainit, queryMonthReq, querySpareWarn, queryTodayInsp, queryTodoList } from '#/api/eims/dashboard';
import { listEqu } from '#/api/eims/equ';
import { listRepairRes } from '#/api/eims/repair-res';
const router = useRouter();
const equTotal = ref<number>(0);
const equFaultTotal = ref<number>(0);
const showInstSkeleton = ref<boolean>(true);
const showEquStatuSkeleton = ref<boolean>(true);
const showInspSkeleton = ref<boolean>(true);
const showRepairSkeleton = ref<boolean>(true);
const showMaintSkeleton = ref<boolean>(true);
const instChart = ref<EchartsUIType>();
const { renderEcharts: renderInstChart } = useEcharts(instChart);
const useStatuChart = ref<EchartsUIType>();
const { renderEcharts: renderUseChart } = useEcharts(useStatuChart);
const inspChart = ref<EchartsUIType>();
const { renderEcharts: renderInspChart } = useEcharts(inspChart);
const repairChart = ref<EchartsUIType>();
const { renderEcharts: renderRepairChart } = useEcharts(repairChart);
const faultChart = ref<EchartsUIType>();
const { renderEcharts: renderFaultChart } = useEcharts(faultChart);
const maintenChart = ref<EchartsUIType>();
const { renderEcharts: renderMaintenChart } = useEcharts(maintenChart);
const maintChart = ref<EchartsUIType>();
const { renderEcharts: renderMaintChart } = useEcharts(maintChart);
const todoItems = ref<any>([
  {
    completed: false,
    content: `审查最近提交到Git仓库的前端代码,确保代码质量和规范。`,
    date: '2024-07-30 11:00:00',
    title: '审查前端代码提交'
  },
  {
    completed: true,
    content: `检查并优化系统性能,降低CPU使用率。`,
    date: '2024-07-30 11:00:00',
    title: '系统性能优化'
    content: `为提升系统稳定性及用户体验,设备管理系统将于 2025å¹´5月25日(星期日)凌晨0:00至4:00 è¿›è¡Œä¾‹è¡Œç³»ç»Ÿç»´æŠ¤å‡çº§ã€‚期间系统可能暂时无法访问,请您提前做好相关安排。`,
    date: '2025-05-18 10:12:00',
    title: '系统维护通知'
  },
  {
    completed: false,
    content: `进行系统安全检查,确保没有安全漏洞或未授权的访问。 `,
    date: '2024-07-30 11:00:00',
    title: '安全检查'
  },
    content: `为进一步规范设备故障处理流程,即日起所有设备报修需通过系统“故障报修”模块提交申请,请勿再使用线下纸质流程。技术支持人员将依据工单优先级及时响应处理。`,
    date: '2024-07-28 14:30:00',
    title: '新增设备报修流程说明'
  }
]);
const commonFunctions = ref<any>([
  {
    completed: false,
    content: `修复用户报告的页面UI显示问题,确保在不同浏览器中显示一致。 `,
    date: '2024-07-30 11:00:00',
    title: '修复UI显示问题'
    menu: '故障报修',
    path: '/repair/repairReq',
    icon: '/static/menu/ico1.png'
  },
  {
    menu: '维修工单',
    path: '/repair/repairRes',
    icon: '/static/menu/ico2.png'
  },
  {
    menu: '点检汇总',
    path: '/inspect/st',
    icon: '/static/menu/ico3.png'
  },
  {
    menu: '点检计划',
    path: '/inspect/plan',
    icon: '/static/menu/ico4.png'
  },
  {
    menu: '保养汇总',
    path: '/maint/st',
    icon: '/static/menu/ico5.png'
  },
  {
    menu: '保养计划',
    path: '/maint/plan',
    icon: '/static/menu/ico6.png'
  },
  {
    menu: '设备台账',
    path: '/equ/ledger',
    icon: '/static/menu/ico7.png'
  }
]);
onMounted(() => {
const todoMenu = ref<any>([
  {
    menu: '故障报修',
    path: '/repair/repairReq',
    icon: '/static/menu/ico1.png'
  },
  {
    menu: '维修任务',
    path: '/repair/repairRes',
    icon: '/static/menu/ico2.png'
  },
  {
    menu: '维修评价',
    path: '/repair/repairRes',
    icon: '/static/menu/ico2.png'
  },
  {
    menu: '点检任务',
    path: '/inspect/st',
    icon: '/static/menu/ico3.png'
  },
  {
    menu: '保养任务',
    path: '/maint/st',
    icon: '/static/menu/ico5.png'
  },
  {
    menu: '状态变更',
    path: '/equ/equStatu',
    icon: '/static/menu/ico5.png'
  },
  {
    menu: '设备台账',
    path: '/equ/ledger',
    icon: '/static/menu/ico7.png'
  }
]);
// å¤‡ä»¶é¢„è­¦
const spareWarn = ref<any>({});
// è®¾å¤‡ä½¿ç”¨çŠ¶æ€
const equStatu = ref<any>({});
// ä»Šæ—¥ç‚¹æ£€
const todayInsp = ref<any>({});
// æœ¬æœˆæ•…éšœ
const monthReq = ref<any>({});
// æœ¬æœˆä¿å…»
const monthMaint = ref<any>({});
const spareMenu = ref<any>([
  {
    menu: '备件台账',
    path: '/spare/index',
    icon: '/static/menu/ico8.png'
  },
  {
    menu: '入库单',
    path: '/spare/in',
    icon: '/static/menu/ico9.png'
  },
  {
    menu: '出库单',
    path: '/spare/out',
    icon: '/static/menu/ico10.png'
  }
]);
function itemClick(item: any) {
  router.push(item.path);
}
function goToKnowledge() {
  router.push('/faultKnow');
}
function initInstChart(data: any) {
  showInstSkeleton.value = false;
  renderInstChart({
    series: [
      {
@@ -99,16 +214,16 @@
          formatter(value: number) {
            switch (value) {
              case 0.125: {
                return 'Grade D';
                return '严重故障';
              }
              case 0.375: {
                return 'Grade C';
                return '急需维护';
              }
              case 0.625: {
                return 'Grade B';
                return '基本可用';
              }
              case 0.875: {
                return 'Grade A';
                return '可靠运行';
              }
              // No default
            }
@@ -130,7 +245,7 @@
        },
        data: [
          {
            value: 0.95,
            value: data?.normal,
            name: '正常设备占比'
          }
        ]
@@ -140,6 +255,12 @@
      trigger: 'item'
    }
  });
}
function initEquStatuChart(data: any) {
  showEquStatuSkeleton.value = false;
  const keyList = Object.keys(data);
  const valueList = Object.values(data);
  renderUseChart({
    tooltip: {
      trigger: 'axis',
@@ -161,13 +282,13 @@
    },
    yAxis: {
      type: 'category',
      data: ['待检', '在用', '停用', '其他', '报废']
      data: keyList
    },
    series: [
      {
        name: '2012',
        name: '2025',
        type: 'bar',
        data: [10, 63, 21, 91, 8],
        data: valueList,
        label: {
          show: true,
          position: 'right'
@@ -176,127 +297,60 @@
      }
    ]
  });
  renderRepairChart({
}
function initInspChart(data: any) {
  showInspSkeleton.value = false;
  const nameList = data?.data.map((item) => item.name);
  renderInspChart({
    tooltip: {
      trigger: 'item'
    },
    title: {
      left: '18%', // åŸºäºŽå®¹å™¨å®½åº¦çš„50%
      top: '35%',
      text: `{a|10个}\n{b|未修复}\n{c|今日新增 +1}`,
      textStyle: {
        rich: {
          a: {
            color: '#4E5766',
            fontSize: 20,
            lineHeight: 30,
            align: 'center'
          },
          b: {
            color: '#1C2029',
            fontSize: 18,
            lineHeight: 30,
            align: 'center'
          },
          c: {
            color: '#ef6666',
            fontSize: 15,
            lineHeight: 30,
            align: 'center'
          }
        }
      }
      trigger: 'item',
      formatter: '{a} <br/>{b}: {c} ({d}%)'
    },
    legend: {
      orient: 'vertical',
      top: 'center',
      right: 'right',
      left: '70%'
      data: nameList
    },
    series: [
      {
        name: 'Access From',
        name: '点检项',
        type: 'pie',
        radius: ['40%', '65%'], // è°ƒæ•´å†…外半径
        center: ['30%', '50%'], // å°†é¥¼å›¾ä¸­å¿ƒå‘左移一点
        avoidLabelOverlap: false,
        itemStyle: {
          borderRadius: 10,
          borderColor: '#fff',
          borderWidth: 2
        },
        selectedMode: 'single',
        radius: [0, '30%'],
        label: {
          show: false,
          position: 'center'
        },
        emphasis: {
          label: {
            show: false,
            fontSize: 40,
            fontWeight: 'bold'
          }
          position: 'inner',
          fontSize: 14
        },
        labelLine: {
          show: false
        },
        data: [
          { value: 10, name: '待接单' },
          { value: 8, name: '待维修' },
          { value: 9, name: '维修中' },
          { value: 16, name: '审核中' },
          { value: 8, name: '其他' }
        ]
      }
    ]
  });
  renderFaultChart({
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'shadow'
      }
        data: data?.sub
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
      {
        name: '点检',
        type: 'pie',
        radius: ['45%', '60%'],
        labelLine: {
          length: 30
    },
    xAxis: [
      {
        type: 'category',
        data: ['smt', '贴片机', '注胶机'],
        axisTick: {
          alignWithLabel: true
        }
      }
    ],
    yAxis: [
      {
        type: 'value'
      }
    ],
    series: [
      {
        name: 'Direct',
        type: 'bar',
        barWidth: 20,
        data: [10, 8, 12],
        label: {
          show: true,
          position: 'top'
        }
          show: true
        },
        data: data?.data
      }
    ]
  });
  renderMaintenChart({
}
function initRepairChart(data: any) {
  showRepairSkeleton.value = false;
  renderRepairChart({
    tooltip: {
      trigger: 'item'
    },
    title: {
      left: '21%', // åŸºäºŽå®¹å™¨å®½åº¦çš„50%
      top: '40%',
      text: `{a|总台数}\n{b|20}`,
      text: `{a|总故障}\n{b|${data?.data?.length}}`,
      textStyle: {
        rich: {
          a: {
@@ -322,7 +376,7 @@
    },
    series: [
      {
        name: 'Access From',
        name: '设备维修',
        type: 'pie',
        radius: ['40%', '65%'], // è°ƒæ•´å†…外半径
        center: ['30%', '50%'], // å°†é¥¼å›¾ä¸­å¿ƒå‘左移一点
@@ -346,15 +400,142 @@
        labelLine: {
          show: false
        },
        data: [
          { value: 10, name: '未保养' },
          { value: 8, name: '待保养' },
          { value: 9, name: '保养中' },
          { value: 16, name: '待接单' }
        ]
        data: data?.data
      }
    ]
  });
}
function initMaintChart(data: any) {
  showMaintSkeleton.value = false;
  renderMaintChart({
    tooltip: {
      trigger: 'item'
    },
    title: {
      left: '21%', // åŸºäºŽå®¹å™¨å®½åº¦çš„50%
      top: '40%',
      text: `{a|总保养}\n{b|${data?.data?.length}}`,
      textStyle: {
        rich: {
          a: {
            color: '#4E5766',
            fontSize: 20,
            lineHeight: 30,
            align: 'center'
          },
          b: {
            color: '#1C2029',
            fontSize: 18,
            lineHeight: 30,
            align: 'center'
          }
        }
      }
    },
    legend: {
      orient: 'vertical',
      top: 'center',
      right: 'right',
      left: '70%'
    },
    series: [
      {
        name: '保养',
        type: 'pie',
        radius: ['40%', '65%'], // è°ƒæ•´å†…外半径
        center: ['30%', '50%'], // å°†é¥¼å›¾ä¸­å¿ƒå‘左移一点
        avoidLabelOverlap: false,
        itemStyle: {
          borderRadius: 10,
          borderColor: '#fff',
          borderWidth: 2
        },
        label: {
          show: false,
          position: 'center'
        },
        emphasis: {
          label: {
            show: false,
            fontSize: 40,
            fontWeight: 'bold'
          }
        },
        labelLine: {
          show: false
        },
        data: data?.data
      }
    ]
  });
}
async function initData() {
  const equResult = await listEqu();
  const repairRes = await listRepairRes();
  const equCount = equResult?.total || 0;
  const rpairTotal = repairRes?.total || 0;
  const instData = { normal: 0 };
  if (rpairTotal > 0 && equCount > 0) {
    equTotal.value = equCount;
    equFaultTotal.value = rpairTotal;
    instData.normal = (equCount - rpairTotal) / equCount;
  }
  initInstChart(instData);
  const todoList = await queryTodoList();
  todoMenu.value = todoMenu.value.map((frontendItem: any) => {
    // æŸ¥æ‰¾å¯¹åº”menu的后端数据
    const backendItem = todoList?.find((item: any) => item.menu === frontendItem.menu);
    // å¦‚果找到了对应的后端数据,则合并之(不覆盖已有的属性)
    if (backendItem) {
      frontendItem = { ...frontendItem, ...backendItem };
    }
    return frontendItem;
  });
  spareWarn.value = await querySpareWarn();
  equStatu.value = await queryEquStatu();
  initEquStatuChart(equStatu.value);
  const tInsp = (await queryTodayInsp()) || {};
  todayInsp.value = tInsp;
  initInspChart(tInsp);
  monthReq.value = (await queryMonthReq()) || {};
  initRepairChart(monthReq.value);
  monthMaint.value = (await queryMonthMainit()) || {};
  initMaintChart(monthMaint.value);
}
onMounted(async () => {
  initData();
});
const todayInspTotal = computed(() => {
  return todayInsp.value?.data?.reduce((sum, item) => sum + item.value, 0);
});
const todayInspFinish = computed(() => {
  return todayInsp.value?.data?.filter((item) => item.name === '已完成').reduce((sum, item) => sum + item.value, 0);
});
const finishReqCount = computed(() => {
  const completedItem = monthReq.value?.data?.find((item) => item.name === '已完成');
  return completedItem ? completedItem.value : 0;
});
const reqCount = computed(() => {
  // å·²å®Œæˆ
  return monthReq.value?.data?.reduce((sum, item) => sum + item.value, 0);
});
const maintCount = computed(() => {
  // å·²å®Œæˆ
  return monthMaint.value?.data?.reduce((sum, item) => sum + item.value, 0);
});
const finishMaintCount = computed(() => {
  const completedItem = monthMaint.value?.data?.find((item) => item.name === '已完成');
  return completedItem ? completedItem.value : 0;
});
</script>
@@ -367,13 +548,15 @@
          <label class="ml-2">常用功能</label>
        </template>
        <Row>
          <Col v-for="i in 9" :span="8" class="flex cursor-pointer flex-col items-center justify-center py-6 hover:bg-gray-100">
            <Avatar size="large" src="/src/assets/logo.png">
              <!--            <template #icon>
                          <UserOutlined />
                        </template>-->
            </Avatar>
            <label class="mt-1">设备台账{{ i }}</label>
          <Col
            v-for="(item, index) in commonFunctions"
            :key="index"
            :span="8"
            class="flex cursor-pointer flex-col items-center justify-center py-6 hover:bg-gray-100"
            @click.stop="itemClick(item)"
          >
            <Image :preview="false" :src="item.icon" :width="35" />
            <label class="mt-1"> {{ item.menu }}</label>
          </Col>
        </Row>
      </Card>
@@ -383,52 +566,59 @@
          <TagTwoTone />
          <span class="ml-2">使用状态</span>
        </template>
        <Skeleton :loading="showInstSkeleton" :paragraph="{ rows: 12, width: 400 }" active>
        <EchartsUI ref="useStatuChart" height="400px" />
        </Skeleton>
      </Card>
      <Card :bordered="false" class="section-height mt-2 rounded-none">
        <template #title>
          <TagTwoTone />
          <span class="ml-2">设备维修</span>
          <span class="ml-2">今日点检</span>
        </template>
        <Skeleton :loading="showInspSkeleton" :paragraph="{ rows: 12, width: 400 }" active>
        <div class="flex w-full flex-col justify-center items-center">
          <div class="flex w-full py-3">
            <div class="flex flex-col justify-center items-center w-1/3">
              <span class="font-bold text-xl">3</span>
              <span>响应超时</span>
                <span class="font-bold text-xl">{{ todayInspTotal }}</span>
                <span>点检批次</span>
            </div>
            <div class="flex flex-col justify-center items-center w-1/3">
              <span class="font-bold text-xl">1</span>
              <span>维修超时</span>
                <span class="font-bold text-xl"></span>
                <span> </span>
            </div>
            <div class="flex flex-col justify-center items-center w-1/3">
              <span class="font-bold text-xl">0</span>
              <span>停机超时</span>
                <span class="font-bold text-xl">{{ todayInspFinish }}</span>
                <span>已完成</span>
            </div>
          </div>
          <EchartsUI ref="repairChart" height="300px" width="400px" />
            <EchartsUI ref="inspChart" height="300px" width="400px" />
        </div>
        </Skeleton>
      </Card>
    </div>
    <div class="mr-2 w-1/3">
      <Card :bordered="false" class="section-height flex justify-center rounded-none p-0">
        <div class="w-full">
        <div class="w-full pt-4">
          <Skeleton :loading="showInstSkeleton" :paragraph="{ rows: 12, width: 400 }" active>
            <div>
          <EchartsUI ref="instChart" width="400px" />
          <div class="flex justify-around pb-5">
            <div>
              <div class="text-center text-4xl">100<label class="ml-1 text-xl font-bold">台</label></div>
                  <div class="text-center text-4xl">{{ equTotal }}<label class="ml-1 text-xl font-bold">台</label></div>
              <div class="text-center font-bold">设备总数</div>
            </div>
            <div>
              <div class="text-center text-4xl text-red-500">5<label class="ml-1 text-xl font-bold">台</label></div>
                  <div class="text-center text-4xl text-red-500">{{ equFaultTotal }}<label class="ml-1 text-xl font-bold">台</label></div>
              <div class="text-center font-bold">故障总数</div>
            </div>
          </div>
            </div>
          </Skeleton>
        </div>
      </Card>
      <Card :bordered="false" class="section-height mt-2 rounded-none">
@@ -472,26 +662,43 @@
      <Card :bordered="false" class="section-height mt-2 rounded-none">
        <template #title>
          <TagTwoTone />
          <span class="ml-2">故障统计</span>
          <span class="ml-2">本月维修</span>
        </template>
        <span class="text-gray-500 text-xs ml-3">故障次数:10</span>
        <EchartsUI  ref="faultChart"  height="360px" />
      </Card>
        <Skeleton :loading="showRepairSkeleton" :paragraph="{ rows: 12, width: 400 }" active>
          <div class="flex w-full flex-col justify-center items-center">
            <div class="flex w-full py-3">
              <div class="flex flex-col justify-center items-center w-1/3">
                <span class="font-bold text-xl">{{ reqCount || 0 }}</span>
                <span>本月报修</span>
              </div>
              <div class="flex flex-col justify-center items-center w-1/3">
                <span class="font-bold text-xl">{{ monthReq?.today?.length || 0 }}</span>
                <span>今日新增</span>
              </div>
              <div class="flex flex-col justify-center items-center w-1/3">
                <span class="font-bold text-xl">{{ finishReqCount }}</span>
                <span>本月完成</span>
              </div>
            </div>
            <EchartsUI ref="repairChart" height="300px" width="400px" />
          </div>
        </Skeleton>
      </Card>
    </div>
    <div class="w-1/3">
      <div class="flex h-14 items-center justify-around rounded-none bg-white p-0">
        <div class="flex h-10 w-28 cursor-pointer items-center justify-around rounded-sm hover:bg-gray-100" style="background: #eaf3fe">
          <Image :preview="false" :width="20" src="/src/assets/logo.png" />
          <span class="mr-2 font-bold" style="color: #2d83f4">故障知识库</span>
          <Image :preview="false" :width="20" src="/static/menu/ico11.png" />
          <span class="mr-2 font-bold" style="color: #2d83f4" @click="goToKnowledge()">故障知识库</span>
        </div>
        <div class="flex h-10 w-28 cursor-pointer items-center justify-around rounded-sm hover:bg-gray-100" style="background: #eaeafe">
          <Image :preview="false" :width="20" src="/src/assets/logo.png" />
          <Image :preview="false" :width="20" src="/static/menu/ico12.png" />
          <span class="mr-2 font-bold" style="color: #5070e0">智能搜索</span>
        </div>
        <div class="flex h-10 w-28 cursor-pointer items-center justify-around rounded-sm hover:bg-gray-100" style="background: #ebf8f7">
          <Image :preview="false" :width="20" src="/src/assets/logo.png" />
          <Image :preview="false" :width="20" src="/static/menu/ico13.png" />
          <span class="mr-2 font-bold" style="color: #32b9af">帮助中心</span>
        </div>
      </div>
@@ -504,36 +711,38 @@
        <div class="todo-title mt-5">设备管理</div>
        <div class="flex w-full flex-wrap justify-between">
          <div
            v-for="index in 12"
            v-for="(item, index) in todoMenu"
            :class="{
              'justify-center': index % 3 == 2,
              'justify-end': index % 3 == 0,
              'mt-2': index > 3
              'justify-center': (index + 1) % 3 == 2,
              'justify-end': (index + 1) % 3 == 0,
              'mt-2': index > 2
            }"
            class="flex w-1/3"
            @click="itemClick(item)"
          >
            <div class="todo-box cursor-pointer hover:bg-gray-100">
              <Image :preview="false" :width="20" src="/src/assets/logo.png" />
              <span>故障审核</span>
              <span>{{ index }}</span>
              <Image :preview="false" :src="item?.icon" :width="20" />
              <span>{{ item.menu }}</span>
              <span>{{ item?.count || 0 }}</span>
            </div>
          </div>
        </div>
        <div class="todo-title mt-20">备件管理</div>
        <div class="todo-title mt-44">备件管理</div>
        <div class="flex w-full flex-wrap justify-between">
          <div
            v-for="index in 3"
            v-for="(item, index) in spareMenu"
            :class="{
              'justify-center': index % 3 == 2,
              'justify-end': index % 3 == 0
              'justify-center': (index + 1) % 3 == 2,
              'justify-end': (index + 1) % 3 == 0
            }"
            class="flex w-1/3"
            @click="itemClick(item)"
          >
            <div class="todo-box border-gray-100">
              <Image :preview="false" :width="20" src="/src/assets/logo.png" />
              <span>故障审核</span>
              <span>{{ index }}</span>
              <Image :preview="false" :src="item?.icon" :width="20" />
              <span>{{ item.menu }}</span>
              <span>{{}}</span>
            </div>
          </div>
        </div>
@@ -548,25 +757,25 @@
          <div class="up-info-box w-1/2 flex h-40">
            <div class="w-1/2 flex items-center h-full justify-center flex-col">
              <div class="w-16">
                <span class="text-xl text-green-600">7</span>
                <span class="text-xl text-green-600">{{ spareWarn?.upper || 0 }}</span>
                <span class="ml-1">种</span>
              </div>
              <div class="w-16">高于上限</div>
            </div>
            <div class="w-1/2 h-full flex items-center justify-center">
              <Image :preview="false" src="/src/assets/img/img-up-limit.png" />
              <Image :preview="false" src="/static/img/img-up-limit.png" />
            </div>
          </div>
          <div class="low-info-box w-1/2 flex h-40">
            <div class="w-1/2 flex items-center h-full justify-center flex-col">
              <div class="w-16">
                <span class="text-xl text-red-600">10</span>
                <span class="text-xl text-red-600">{{ spareWarn?.lower || 0 }}</span>
                <span class="ml-1">种</span>
              </div>
              <div class="w-16">低于下限</div>
            </div>
            <div class="w-1/2 h-full flex items-center justify-center">
              <Image :preview="false" src="/src/assets/img/img-lower-limit.png" />
              <Image :preview="false" src="/static/img/img-lower-limit.png" />
            </div>
          </div>
        </div>
@@ -575,26 +784,28 @@
      <Card :bordered="false" class="section-height mt-2 rounded-none">
        <template #title>
          <TagTwoTone />
          <span class="ml-2">设备保养</span>
          <span class="ml-2">本月保养</span>
        </template>
        <Skeleton :loading="showMaintSkeleton" :paragraph="{ rows: 12, width: 400 }" active>
        <div class="flex w-full flex-col justify-center items-center">
          <div class="flex w-full py-3">
            <div class="flex flex-col justify-center items-center w-1/3">
              <span class="font-bold text-xl">3</span>
              <span>未保养</span>
                <span class="font-bold text-xl">{{ maintCount || 0 }}</span>
                <span>本月保养</span>
            </div>
            <div class="flex flex-col justify-center items-center w-1/3">
              <span class="font-bold text-xl">1</span>
              <span>本月到期</span>
                <span class="font-bold text-xl">{{ maintCount - finishMaintCount }}</span>
                <span>未完成</span>
            </div>
            <div class="flex flex-col justify-center items-center w-1/3">
              <span class="font-bold text-xl">0</span>
              <span>下月到期</span>
                <span class="font-bold text-xl">{{ finishMaintCount }}</span>
                <span>已完成</span>
            </div>
          </div>
          <EchartsUI ref="maintenChart" height="300px" width="400px" />
            <EchartsUI ref="maintChart" height="300px" width="400px" />
        </div>
        </Skeleton>
      </Card>
    </div>
  </div>
eims-ui/packages/@core/base/shared/src/constants/dict-enum.ts
@@ -1,6 +1,5 @@
export enum DictEnum {
  EIMS_EQU_PART = 'eims_equ_part', // è®¾å¤‡éƒ¨ä½
  EIMS_SPARE_UNIT = 'eims_spare_unit', // å¤‡ä»¶è®¡é‡å•位
  EIMS_EQU_UNIT = 'eims_equ_unit', // è®¾å¤‡ç›˜ç‚¹çŠ¶æ€
  EIMS_FAULT_REASON = 'eims_fault_reason', // æ•…障原因
  EIMS_FIXTURE_STATUS = 'eims_fixture_status', // å·¥å…·ï¼ˆæ²»å…·ï¼‰çŠ¶æ€
@@ -11,6 +10,7 @@
  EIMS_INVENTORY_STATU = 'inventory_statu', // è®¾å¤‡ç›˜ç‚¹çŠ¶æ€
  EIMS_MAINT_TYPE = 'eims_maint_type', // ä¿å…»ç±»åž‹
  EIMS_ORDITM_STATUS = 'eims_orditm_status', // ä¿å…»å·¥å•明细完成状态
  EIMS_SPARE_UNIT = 'eims_spare_unit', // å¤‡ä»¶è®¡é‡å•位
  EQU_IMPORT_STATU = 'equ_import_status', // è®¾å¤‡å¯¼å…¥çŠ¶æ€
  FIXTURE_BORROW_RECORD_STATUS = 'fixture_borrow_record_status', // å·¥å…·ï¼ˆæ²»å…·ï¼‰å€Ÿç”¨è®°å½•状态
  FIXTURE_BORROW_STATUS = 'fixture_borrow_status', // å·¥å…·ï¼ˆæ²»å…·ï¼‰å€Ÿç”¨çŠ¶æ€
@@ -21,10 +21,10 @@
  REPAIR_FAULT_TYPE = 'repair_fault_type', // æ•…障类别
  REPAIR_RECORD_HANDLE = 'repair_record_handle', // ç»´ä¿®è®°å½•操作
  REPAIR_REQ_STATUS = 'repair_req_status', // æŠ¥ä¿®çŠ¶æ€
  SPARE_INOUT_TYPE = 'spare_inout_type', // å¤‡ä»¶å‡ºå…¥åº“类型 1-入库单 2-出库单
  REPAIR_REQ_TYPE = 'repair_req_type', // æŠ¥ä¿®ç±»åž‹
  REPAIR_RES_STATUS = 'repair_res_status', // ç»´ä¿®çŠ¶æ€
  REPAIR_URGENCY_LEVEL = 'repair_urgency_level', // æŠ¥ä¿®ç´§æ€¥ç¨‹åº¦
  SPARE_INOUT_TYPE = 'spare_inout_type', // å¤‡ä»¶å‡ºå…¥åº“类型 1-入库单 2-出库单
  SYS_COMMON_STATUS = 'sys_common_status',
  SYS_DEVICE_TYPE = 'sys_device_type', // è®¾å¤‡ç±»åž‹
  SYS_EQU_STATUS = 'sys_equ_status', // è®¾å¤‡çŠ¶æ€
eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java
@@ -10,6 +10,42 @@
     * è®¾å¤‡å•位unit
     */
    String EIMS_EQU_UNIT = "eims_equ_unit";
    /**
     * å·¥å…·å•位
     */
    String EIMS_FIXTURE_UNIT = "eims_fixture_unit";
    /**
     * è®¾å¤‡çŠ¶æ€
     */
    String EIMS_EQU_STATUS = "sys_equ_status";
    interface EIMS_EQU_STATUS_DETAIL {
        /**
         * è¯•用
         */
        String CESHI = "0";
        /**
         * ä½¿ç”¨
         */
        String SHIYONG = "1";
        /**
         * åœç”¨
         */
        String TINGYONG = "2";
        /**
         * æŠ¥åºŸ
         */
        String BAOFEI = "3";
        /**
         * é—²ç½®
         */
        String XIANZHI = "4";
        /**
         * æ–°å¢ž
         */
        String XINZENG = "5";
    }
    /**
     * è®¾å¤‡å¯¼å…¥çŠ¶æ€
@@ -21,6 +57,10 @@
    String REPAIR_REQ_STATUS = "repair_req_status";
    interface REPAIR_REQ_STATUS_DETAIL {
        /**
         * å¾…接单
         */
        String DAIJIEDAN = "0";
        /**
         * å·²æŽ¥å•
         */
@@ -160,6 +200,15 @@
        String Y = "1"; // å·²ç‚¹æ£€
    }
    /**
     *点检结果
     */
    String EIMS_INSPECT_RESULT = "eims_inspect_result";
    interface EIMS_INSPECT_RESULT_DETAIL {
        String N = "2";// å¼‚常
        String Y = "1"; // æ­£å¸¸
    }
    /**
     *备件出入库类型
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/BoardController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,376 @@
package org.dromara.eims.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.DictConstants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.eims.domain.*;
import org.dromara.eims.domain.bo.EimsEquBo;
import org.dromara.eims.domain.vo.EimsInspectStVo;
import org.dromara.eims.domain.vo.EimsMaintOrderVo;
import org.dromara.eims.domain.vo.EimsRepairReqVo;
import org.dromara.eims.mapper.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/eims/board")
public class BoardController {
    private final EimsRepairReqMapper reqMapper;
    private final EimsRepairResMapper resMapper;
    private final EimsInspectStMapper inspStMapper;
    private final EimsInspectRecordMapper inspRecordMapper;
    private final EimsMaintStMapper maintStMapper;
    private final EimsEquStatuMapper equStatuMapper;
    private final EimsSpareMapper spareMapper;
    private final EimsEquMapper equMapper;
    private final EimsMaintOrderMapper maintOrderMapper;
    @GetMapping("/todoList")
    public  R<List<Map<String,Object>>>  todoList() {
        List<Map<String,Object>> result = new ArrayList<>();
        // æŠ¥ä¿®å•-待接单
        LambdaQueryWrapper<EimsRepairReq> reqWrapper = new LambdaQueryWrapper<>();
        // å¾…接单
        reqWrapper.eq(EimsRepairReq::getStatus, DictConstants.REPAIR_REQ_STATUS_DETAIL.DAIJIEDAN);
        Long reqCount = reqMapper.selectCount(reqWrapper);
        Map<String,Object> reqMap = new HashMap<>();
        reqMap.put("menu","故障报修");
        reqMap.put("count",reqCount);
        result.add(reqMap);
        //维修工单-未完成
        LambdaQueryWrapper<EimsRepairRes> resWrapper = new LambdaQueryWrapper<>();
        // æœªå®Œæˆ
        resWrapper.ne(EimsRepairRes::getStatus, DictConstants.REPAIR_RES_STATUS_DETAIL.WANCHENG);
        Long resCount = resMapper.selectCount(resWrapper);
        Map<String,Object> resMap = new HashMap<>();
        resMap.put("menu","维修任务");
        resMap.put("count",resCount);
        result.add(resMap);
        //维修工单-评价
        LambdaQueryWrapper<EimsRepairRes> evWrapper = new LambdaQueryWrapper<>();
        evWrapper.isNull(EimsRepairRes::getFbId);
        Long evCount = resMapper.selectCount(evWrapper);
        Map<String,Object> evMap = new HashMap<>();
        evMap.put("menu","维修评价");
        evMap.put("count",evCount);
        result.add(evMap);
        //点检汇总任务
        LambdaQueryWrapper<EimsInspectSt> inspStWrapper = new LambdaQueryWrapper<>();
        //未点检
        inspStWrapper.eq(EimsInspectSt::getStatus, DictConstants.EIMS_INSPECT_STATUS_DETAIL.N);
        Long inspStCount = inspStMapper.selectCount(inspStWrapper);
        Map<String,Object> inspStMap = new HashMap<>();
        inspStMap.put("menu","点检任务");
        inspStMap.put("count",inspStCount);
        result.add(inspStMap);
        //保养汇总任务
        LambdaQueryWrapper<EimsMaintSt> maintStWrapper = new LambdaQueryWrapper<>();
        //未完成工单汇总
        maintStWrapper.eq(EimsMaintSt::getStatus,DictConstants.MAINT_ORDER_ST_STATUS_DETAIL.N);
        Long maintStCount = maintStMapper.selectCount(maintStWrapper);
        Map<String,Object> maintStMap = new HashMap<>();
        maintStMap.put("menu","保养任务");
        maintStMap.put("count",maintStCount);
        result.add(maintStMap);
        //设备状态变更
        LambdaQueryWrapper<EimsEquStatu>  equStatuWrapper = new LambdaQueryWrapper<>();
        equStatuWrapper.isNotNull(EimsEquStatu::getChangeDate);
        // èŽ·å–å½“å‰æ—¥æœŸ
        LocalDate today = LocalDate.now();
        // æž„造当天的 00:00:00
        LocalDateTime startOfDay = today.atStartOfDay();
        // æž„造当天的 23:59:59
        LocalDateTime endOfDay = today.atTime(23, 59, 59);
        // å®šä¹‰æ ¼å¼åŒ–模板
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // æ ¼å¼åŒ–输出
        String startStr = startOfDay.format(formatter);
        String endStr = endOfDay.format(formatter);
        equStatuWrapper.between(EimsEquStatu::getChangeDate,startStr,endStr);
        Long equStatuCount = equStatuMapper.selectCount(equStatuWrapper);
        Map<String,Object> equStatuMap = new HashMap<>();
        equStatuMap.put("menu","状态变更");
        equStatuMap.put("count",equStatuCount);
        result.add(equStatuMap);
        return R.ok(result);
    }
    @GetMapping("/spareWarn")
    public  R<Map<String,Object>>  spareWarn() {
        Map<String,Object> result = new HashMap<>();
        LambdaQueryWrapper<EimsSpare> spareUpWrapper = new LambdaQueryWrapper<>();
        spareUpWrapper.isNotNull(EimsSpare::getUpperStock)
            .isNotNull(EimsSpare::getActualStock)
            .gt(EimsSpare::getActualStock, 0)
            .gt(EimsSpare::getUpperStock, 0)
            .apply("actual_stock > upper_stock");
        Long upper = spareMapper.selectCount(spareUpWrapper);
        result.put("upper",upper);
        LambdaQueryWrapper<EimsSpare> spareLowWrapper = new LambdaQueryWrapper<>();
        spareLowWrapper.isNotNull(EimsSpare::getLowerStock)
            .isNotNull(EimsSpare::getActualStock)
            .gt(EimsSpare::getActualStock, 0)
            .gt(EimsSpare::getLowerStock, 0)
            .apply("actual_stock < lower_stock");
        Long lower = spareMapper.selectCount(spareLowWrapper);
        result.put("lower",lower);
        return R.ok(result);
    }
    /**
     * æŸ¥è¯¢è®¾å¤‡åœ¨ç”¨çŠ¶æ€
     * @return
     */
    @GetMapping("/equStatu")
    public  R<Map<String,Integer>>  queryEquStatu() {
        List<EimsEqu> eimsEqus = equMapper.selectList();
        Map<String, Integer> result = countStatus(eimsEqus);
        return R.ok(result);
    }
    /**
     * æŸ¥è¯¢ä»Šå¤©ç‚¹æ£€æ•°æ®
     * @return
     */
    @GetMapping("/todayInsp")
    public  R<Map<String,Object>>  todayInsp() {
        Map<String, Object> result = new HashMap<>();
        List<Map<String, Object>> data = new ArrayList<>();
        List<Map<String, Object>> sub = new ArrayList<>();
        // åˆå§‹åŒ–主数据统计项
        data.add(createDataItem("已完成", 0));
        data.add(createDataItem("未完成", 0));
        // åˆå§‹åŒ–子项统计
        sub.add(createSubItem("正常", 0));
        sub.add(createSubItem("异常", 0));
        sub.add(createSubItem("未检", 0));
        result.put("data", data);
        result.put("sub", sub);
        // æŸ¥è¯¢ä»Šæ—¥è®¡åˆ’巡检任务
        LambdaQueryWrapper<EimsInspectSt> inspStWrapper = new LambdaQueryWrapper<>();
        inspStWrapper.eq(EimsInspectSt::getType, "Day");
        inspStWrapper.eq(EimsInspectSt::getPlanTime, DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD));
        List<EimsInspectStVo> inspectStList = inspStMapper.selectVoList(inspStWrapper);
        if (inspectStList == null || inspectStList.isEmpty()) {
            return R.ok(result);
        }
        long completedCount = 0;
        List<String> stIdList = new ArrayList<>();
        for (EimsInspectStVo item : inspectStList) {
            if (DictConstants.EIMS_INSPECT_STATUS_DETAIL.Y.equals(item.getStatus())) {
                completedCount++;
            }
            stIdList.add(item.getStId());
        }
        int total = inspectStList.size();
        data.get(0).put("value", completedCount);
        data.get(1).put("value", total - completedCount);
        // æŸ¥è¯¢å·¡æ£€è®°å½•
        List<EimsInspectRecord> recordList = Collections.emptyList();
        if (!stIdList.isEmpty()) {
            QueryWrapper<EimsInspectRecord> recordWrapper = new QueryWrapper<>();
            recordWrapper.in("st_id", stIdList);
            recordList = inspRecordMapper.selectList(recordWrapper);
        }
        if (recordList == null || recordList.isEmpty()) {
            return R.ok(result);
        }
        long normalCount = 0;
        long abnormalCount = 0;
        for (EimsInspectRecord record : recordList) {
            String status = record.getStatus();
            if (DictConstants.EIMS_INSPECT_RESULT_DETAIL.Y.equals(status)) {
                normalCount++;
            } else if (DictConstants.EIMS_INSPECT_RESULT_DETAIL.N.equals(status)) {
                abnormalCount++;
            }
        }
        int unCheckedCount = recordList.size() - (int) (normalCount + abnormalCount);
        sub.get(0).put("value", normalCount);
        sub.get(1).put("value", abnormalCount);
        sub.get(2).put("value", unCheckedCount);
        return R.ok(result);
    }
    @GetMapping("/monthReq")
    public  R<Map<String,Object>>  monthReq() {
        Map<String,Object> result = new HashMap<>();
        List<Map<String, Object>> statusList = new ArrayList<>();
        LocalDate today = LocalDate.now();
        LambdaQueryWrapper<EimsRepairReq> queryWrapper = new LambdaQueryWrapper<EimsRepairReq>()
            .between(EimsRepairReq::getOccTime,
                today.with(TemporalAdjusters.firstDayOfMonth()),
                today.with(TemporalAdjusters.lastDayOfMonth()));
        List<EimsRepairReqVo> reqList = reqMapper.selectVoList(queryWrapper);
        long todayCount = reqList.stream()
            .filter(r -> r.getReqTime() != null &&
                r.getReqTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().isEqual(LocalDate.now()))
            .count();
        if (reqList != null && !reqList.isEmpty()) {
            // å‡è®¾ reqList å·²ç»ä»Žæ•°æ®åº“查询出来,并且不为空
            Map<String, Long> statusCountMap = reqList.stream()
                .collect(Collectors.groupingBy(
                    item -> {
                        switch (item.getStatus()) {
                            case "0": return "待接单";
                            case "1": return "已接单";
                            case "2": return "维修中";
                            case "3": return "已完成";
                            default: return "未知状态";
                        }
                    },
                    Collectors.counting()
                ));
            // æŒ‰å›ºå®šé¡ºåºæŽ’序生成 List<Map<String, Object>>
            statusList.add(Map.of("name", "待接单",  "value", statusCountMap.getOrDefault("待接单", 0L)));
            statusList.add(Map.of("name", "已接单",  "value", statusCountMap.getOrDefault("已接单", 0L)));
            statusList.add(Map.of("name", "维修中",  "value", statusCountMap.getOrDefault("维修中", 0L)));
            statusList.add(Map.of("name", "已完成",  "value", statusCountMap.getOrDefault("已完成", 0L)));
        }
        result.put("data",statusList);
        result.put("today",todayCount);
      return  R.ok(result);
    }
    @GetMapping("/monthMainit")
    public  R<Map<String,Object>>  monthMainit() {
        Map<String,Object> result = new HashMap<>();
        List<Map<String, Object>> statusList = new ArrayList<>();
        LocalDate today = LocalDate.now();
        LambdaQueryWrapper<EimsMaintOrder> queryWrapper = new LambdaQueryWrapper<EimsMaintOrder>()
            .between(EimsMaintOrder::getPlanTime,
                today.with(TemporalAdjusters.firstDayOfMonth()),
                today.with(TemporalAdjusters.lastDayOfMonth()));
        List<EimsMaintOrderVo> orderList = maintOrderMapper.selectVoList(queryWrapper);
        long todayCount = orderList.stream()
            .filter(r -> r.getPlanTime() != null &&
                r.getPlanTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().isEqual(LocalDate.now()))
            .count();
        if (orderList != null && !orderList.isEmpty()) {
            Map<String, Long> statusCountMap = orderList.stream()
                .collect(Collectors.groupingBy(
                    item -> {
                        switch (item.getStatus()) {
                            case "0": return "待保养";
                            case "1": return "保养中";
                            case "2": return "待验证";
                            case "3": return "已完成";
                            default: return "未知状态";
                        }
                    },
                    Collectors.counting()
                ));
            // æŒ‰å›ºå®šé¡ºåºæŽ’序生成 List<Map<String, Object>>
            statusList.add(Map.of("name", "待保养",  "value", statusCountMap.getOrDefault("待保养", 0L)));
            statusList.add(Map.of("name", "保养中",  "value", statusCountMap.getOrDefault("保养中", 0L)));
            statusList.add(Map.of("name", "待验证",  "value", statusCountMap.getOrDefault("待验证", 0L)));
            statusList.add(Map.of("name", "已完成",  "value", statusCountMap.getOrDefault("已完成", 0L)));
        }
        result.put("data",statusList);
        result.put("today",todayCount);
        return  R.ok(result);
    }
    // è¾…助方法:创建主数据项
    private Map<String, Object> createDataItem(String name, long value) {
        Map<String, Object> item = new HashMap<>();
        item.put("name", name);
        item.put("value", value);
        return item;
    }
    // è¾…助方法:创建子数据项
    private Map<String, Object> createSubItem(String name, long value) {
        Map<String, Object> item = new HashMap<>();
        item.put("name", name);
        item.put("value", value);
        return item;
    }
    /**
     * ç»Ÿè®¡å„状态设备数量
      * @param eimsEqus
     * @return
     */
    public static Map<String, Integer> countStatus(List<EimsEqu> eimsEqus) {
        Map<String, Integer> statusCount = new HashMap<>();
        Map<String, String> statusMap = new HashMap<String, String>() {{
            put("0", "试用");
            put("1", "使用");
            put("2", "停用");
            put("3", "报废");
            put("4", "闲置");
            put("5", "新增");
            put("其他", "其他");
        }};
        for (EimsEqu equ : eimsEqus) {
            String status = equ.getStatus();
            status = (status == null || status.trim().isEmpty()) ? "其他" : status;
            if(statusMap.containsKey(status)){
                statusCount.put(statusMap.get(status), statusCount.getOrDefault(statusMap.get(status), 0) + 1);
            }
        }
        return statusCount;
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsFixtureController.java
@@ -1,11 +1,19 @@
package org.dromara.eims.controller;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.eims.domain.vo.EimsEquImportVo;
import org.dromara.eims.domain.vo.EimsEquVo;
import org.dromara.eims.domain.vo.EimsFixtureImportVo;
import org.dromara.eims.listener.EimsEquImportListener;
import org.dromara.eims.listener.EimsFixtureImportListener;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@@ -21,6 +29,7 @@
import org.dromara.eims.domain.bo.EimsFixtureBo;
import org.dromara.eims.service.IEimsFixtureService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
/**
 * å·¥å…·(治具)台账
@@ -102,4 +111,28 @@
                          @PathVariable Long[] ids) {
        return toAjax(eimsFixtureService.deleteWithValidByIds(List.of(ids), true));
    }
    /**
     * å¯¼å…¥æ•°æ®
     *
     * @param file          å¯¼å…¥æ–‡ä»¶
     * @param updateSupport æ˜¯å¦æ›´æ–°å·²å­˜åœ¨æ•°æ®
     */
    @Log(title = "工具管理", businessType = BusinessType.IMPORT)
    @SaCheckPermission("eims:fixture:import")
    @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Void> importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception {
        ExcelResult<EimsFixtureImportVo> result = ExcelUtil.importExcel(file.getInputStream(), EimsFixtureImportVo.class, new EimsFixtureImportListener(updateSupport));
        return R.ok(result.getAnalysis());
    }
    /**
     * èŽ·å–å¯¼å…¥æ¨¡æ¿
     */
    @PostMapping("/importTemplate")
    public void importTemplate(HttpServletResponse response) {
        ExcelUtil.exportExcel(new ArrayList<>(), "设备数据", EimsFixtureVo.class, response);
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsFixture.java
@@ -1,9 +1,12 @@
package org.dromara.eims.domain;
import org.apache.poi.hpsf.Decimal;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
@@ -116,5 +119,44 @@
     */
    private Long curBorrowId;
    /**
     * æ•°é‡
     */
    private Integer  amount;
    /**
     * å“ç‰Œ
     */
    private String  brand;
    /**
     * å•位
     */
    private String  unit;
    /**
     * ä½¿ç”¨éƒ¨é—¨
     */
    private Long  useDept;
    /**
     * ç®¡ç†äºº
     */
    private Long  manageUser;
    /**
     * ç®¡æŽ§éœ€æ±‚
     */
    private String  isManage;
    /**
     * å•ä»·
     */
    private BigDecimal unitPrice;
    /**
     * åŠŸçŽ‡
     */
    private String power;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsInspectSt.java
@@ -33,6 +33,7 @@
     * æ±‡æ€»ç±»åž‹ Day-日汇总  Month-月汇总
     */
    private String type;
    private String stId;
    /**
     * æ ‡é¢˜
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsFixtureBo.java
@@ -1,6 +1,7 @@
package org.dromara.eims.domain.bo;
import com.alibaba.excel.annotation.ExcelProperty;
import org.apache.poi.hpsf.Decimal;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.eims.domain.EimsFixture;
@@ -11,6 +12,8 @@
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
@@ -120,4 +123,45 @@
    private Long curBorrowId;
    /**
     * æ•°é‡
     */
    private Integer  amount;
    /**
     * å“ç‰Œ
     */
    private String  brand;
    /**
     * å•位
     */
    private String  unit;
    /**
     * ä½¿ç”¨éƒ¨é—¨
     */
    private Long  useDept;
    /**
     * ç®¡ç†äºº
     */
    private Long  manageUser;
    /**
     * ç®¡æŽ§éœ€æ±‚
     */
    private String  isManage;
    /**
     * å•ä»·
     */
    private BigDecimal unitPrice;
    /**
     * åŠŸçŽ‡
     */
    private String power;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsInspectStBo.java
@@ -80,6 +80,9 @@
     * å¤‡æ³¨
     */
    private String remark;
    private String stId;
    //前端界面查看模式   day-日视图   month-月视图
    private String viewMode;
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsFixtureImportVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,148 @@
package org.dromara.eims.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.poi.hpsf.Decimal;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
 * å·¥å…·å¯¹è±¡å¯¼å…¥VO
 *
 * @author zhuguifei
 */
@Data
@NoArgsConstructor
public class EimsFixtureImportVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     *
     */
    private Long id;
    /**
     * æ²»å…·åç§°
     */
    @ExcelProperty(value = "工具名称")
    @ColumnWidth(30)
    private String fixtureName;
    /**
     * åž‹å·
     */
    @ExcelProperty(value = "型号")
    @ColumnWidth(15)
    private String modelNo;
    /**
     * å“ç‰Œ
     */
    @ExcelProperty(value = "品牌")
    @ColumnWidth(20)
    private String  brand;
    /**
     * æ•°é‡
     */
    @ExcelProperty(value = "数量")
    @ColumnWidth(10)
    private Integer  amount;
    /**
     * å•位
     */
    @ExcelProperty(value = "单位", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "eims_fixture_unit")
    @ColumnWidth(10)
    private String  unit;
    /**
     * èµ„产编号
     */
    @ExcelProperty(value = "资产编号")
    @ColumnWidth(20)
    private String assetNo;
    /**
     * ä½¿ç”¨éƒ¨é—¨
     */
    @ExcelProperty(value = "使用部门")
    @ColumnWidth(20)
    private String useDeptStr;
    private Long  useDept;
    /**
     * ç®¡ç†äºº
     */
    @ExcelProperty(value = "管理人")
    @ColumnWidth(12)
    private String  manageUserStr;
    private Long  manageUser;
    /**
     * é‡‡è´­æ—¥æœŸ
     */
    @ExcelProperty(value = "采购日期")
    @ColumnWidth(24)
    @JsonFormat(pattern = "yyyy-MM-dd")
    private String purchaseDateStr;
    private Date purchaseDate;
    /**
     * ä½¿ç”¨æ—¥æœŸ
     */
    @ExcelProperty(value = "使用日期")
    @ColumnWidth(24)
    @JsonFormat(pattern = "yyyy-MM-dd")
    private String deployDateStr;
    private Date deployDate;
    /**
     * ç®¡æŽ§éœ€æ±‚
     */
    @ExcelProperty(value = "管控需求", converter = ExcelDictConvert.class)
    @ColumnWidth(10)
    @ExcelDictFormat(dictType = "eims_fixture_manage")
    private String  isManage;
    /**
     * çŠ¶æ€ï¼ˆå­—å…¸ï¼‰
     */
    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
    @ColumnWidth(10)
    @ExcelDictFormat(dictType = "eims_fixture_status")
    private String status;
    /**
     * å•ä»·
     */
    @ExcelProperty(value = "单价")
    @ColumnWidth(10)
    private String unitPriceStr;
    private BigDecimal unitPrice;
    /**
     * åŠŸçŽ‡
     */
    @ExcelProperty(value = "功率")
    @ColumnWidth(15)
    private String power;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsFixtureVo.java
@@ -1,7 +1,11 @@
package org.dromara.eims.domain.vo;
import java.math.BigDecimal;
import java.util.Date;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.poi.hpsf.Decimal;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
@@ -36,20 +40,124 @@
    /**
     *
     */
    @ExcelProperty(value = "")
    private Long id;
    /**
     * æ²»å…·ç¼–码
     */
    @ExcelProperty(value = "治具编码")
    private String fixtureCode;
    /**
     * æ²»å…·åç§°
     */
    @ExcelProperty(value = "治具名称")
    @ExcelProperty(value = "工具名称")
    @ColumnWidth(30)
    private String fixtureName;
    /**
     * åž‹å·
     */
    @ExcelProperty(value = "型号")
    @ColumnWidth(15)
    private String modelNo;
    /**
     * å“ç‰Œ
     */
    @ExcelProperty(value = "品牌")
    @ColumnWidth(20)
    private String  brand;
    /**
     * æ•°é‡
     */
    @ExcelProperty(value = "数量")
    @ColumnWidth(10)
    private Integer  amount;
    /**
     * å•位
     */
    @ExcelProperty(value = "单位", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "eims_fixture_unit")
    @ColumnWidth(10)
    private String  unit;
    /**
     * èµ„产编号
     */
    @ExcelProperty(value = "资产编号")
    @ColumnWidth(20)
    private String assetNo;
    /**
     * ä½¿ç”¨éƒ¨é—¨
     */
    @ExcelProperty(value = "使用部门")
    @ColumnWidth(20)
    private Long  useDept;
    /**
     * ç®¡ç†äºº
     */
    @ExcelProperty(value = "管理人")
    @ColumnWidth(12)
    private Long  manageUser;
    /**
     * é‡‡è´­æ—¥æœŸ
     */
    @ExcelProperty(value = "采购日期")
    @ColumnWidth(24)
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date purchaseDate;
    /**
     * ä½¿ç”¨æ—¥æœŸ
     */
    @ExcelProperty(value = "使用日期")
    @ColumnWidth(24)
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date deployDate;
    /**
     * ç®¡æŽ§éœ€æ±‚
     */
    @ExcelProperty(value = "管控需求", converter = ExcelDictConvert.class)
    @ColumnWidth(10)
    @ExcelDictFormat(dictType = "eims_fixture_manage")
    private String  isManage;
    /**
     * çŠ¶æ€ï¼ˆå­—å…¸ï¼‰
     */
    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
    @ColumnWidth(10)
    @ExcelDictFormat(dictType = "eims_fixture_status")
    private String status;
    /**
     * å•ä»·
     */
    @ExcelProperty(value = "单价")
    @ColumnWidth(10)
    private BigDecimal unitPrice;
    /**
     * åŠŸçŽ‡
     */
    @ExcelProperty(value = "功率")
    @ColumnWidth(15)
    private String power;
    /**
     * æ²»å…·ç¼–码
     */
    private String fixtureCode;
    /**
@@ -60,14 +168,12 @@
    /**
     * æ²»å…·ï¼ˆå·¥å…·ï¼‰ç±»åž‹
     */
    @ExcelProperty(value = "治具类型")
    @Translation(type = TransConstant.FIXTURE_TYPE_ID_TO_NAME, mapper = "fixtureType")
    private String fixtureTypeName;
    /**
     * æ²»å…·æè¿°
     */
    @ExcelProperty(value = "治具描述")
    private String fixtureDesc;
    /**
@@ -88,8 +194,8 @@
    /**
     * å€Ÿç”¨çŠ¶æ€
     */
    @ExcelProperty(value = "借用状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "fixture_borrow_status")
//    @ExcelProperty(value = "借用状态", converter = ExcelDictConvert.class)
//    @ExcelDictFormat(dictType = "fixture_borrow_status")
    private String borrowStatus;
    /**
@@ -98,55 +204,24 @@
    @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "borrowUser")
    private String borrowUserName;
    /**
     * çŠ¶æ€ï¼ˆå­—å…¸ï¼‰
     */
    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "eims_fixture_status")
    private String status;
    /**
     * èµ„产编号
     */
    @ExcelProperty(value = "资产编号")
    private String assetNo;
    /**
     * åž‹å·
     */
    @ExcelProperty(value = "型号")
    private String modelNo;
    /**
     * è§„æ ¼
     */
    @ExcelProperty(value = "规格")
    private String specNo;
    /**
     * åˆ¶é€ å•†
     */
    @ExcelProperty(value = "制造商")
    private String madeIn;
    /**
     * é‡‡è´­æ—¥æœŸ
     */
    @ExcelProperty(value = "采购日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date purchaseDate;
    /**
     * ä½¿ç”¨æ—¥æœŸ
     */
    @ExcelProperty(value = "使用日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date deployDate;
    /**
     * ä½¿ç”¨å¹´é™
     */
    @ExcelProperty(value = "使用年限")
    private Long serviceLife;
    /**
@@ -161,4 +236,7 @@
    private Long curBorrowId;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsInspectStVo.java
@@ -125,6 +125,8 @@
     */
    private Integer abNormalNum;
    private String stId;
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/listener/EimsFixtureImportListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,265 @@
package org.dromara.eims.listener;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hpsf.Decimal;
import org.dromara.common.core.constant.DictConstants;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.excel.core.ExcelListener;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.eims.domain.EimsFixture;
import org.dromara.eims.domain.bo.EimsEquBo;
import org.dromara.eims.domain.vo.EimsEquImportVo;
import org.dromara.eims.domain.vo.EimsEquVo;
import org.dromara.eims.domain.vo.EimsFixtureImportVo;
import org.dromara.eims.domain.vo.EimsFixtureVo;
import org.dromara.eims.mapper.EimsFixtureMapper;
import org.dromara.eims.service.IEimsEquService;
import org.dromara.eims.service.IEimsFixtureService;
import org.dromara.system.domain.SysDept;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysDeptMapper;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ISysUserService;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * å·¥å…·è‡ªå®šä¹‰å¯¼å…¥
 *
 * @author zhuguifei
 */
@Slf4j
public class EimsFixtureImportListener extends AnalysisEventListener<EimsFixtureImportVo> implements ExcelListener<EimsFixtureImportVo> {
    private final EimsFixtureMapper fixtureMapper;
    private final SysUserMapper sysUserMapper;
    private final SysDeptMapper sysDeptMapper;
    private final DictService dictService;
    // å½“前导入用户
    private final Long operUserId;
    private final Boolean isUpdateSupport;
    //unit字典
    private final Map<String, String> unitDictMap;
    private int successNum = 0;
    private int coverNum = 0;
    private int failureNum = 0;
    private final StringBuilder successMsg = new StringBuilder();
    private final StringBuilder failureMsg = new StringBuilder();
    public EimsFixtureImportListener(Boolean isUpdateSupport) {
        this.fixtureMapper = SpringUtils.getBean(EimsFixtureMapper.class);
        this.sysUserMapper = SpringUtils.getBean(SysUserMapper.class);
        this.sysDeptMapper = SpringUtils.getBean(SysDeptMapper.class);
        this.dictService = SpringUtils.getBean(DictService.class);
        this.isUpdateSupport = isUpdateSupport;
        this.operUserId = LoginHelper.getUserId();
        this.unitDictMap = dictService.getAllDictByDictType(DictConstants.EIMS_FIXTURE_UNIT);
    }
    @Override
    public void invoke(EimsFixtureImportVo fixtureVo, AnalysisContext context) {
        try {
            fixtureVo.setStatus("0");
            // æ ¹æ®èµ„产编号查询工具是否已存在
            LambdaQueryWrapper<EimsFixture> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(EimsFixture::getAssetNo,fixtureVo.getAssetNo());
            EimsFixtureVo eimsFixtureVo = fixtureMapper.selectVoOne(queryWrapper);
            // å¤„理不规则数据
            //1.单位unit-excel导入数据中单位字段不规则,特殊处理
            // normalizeUnit(fixtureVo, unitDictMap);
            //2.购买日期、验收日期 ç‰¹æ®Šå¤„理
            if (StringUtils.isNotEmpty(fixtureVo.getPurchaseDateStr())) {
                try {
                    fixtureVo.setPurchaseDate(DateUtils.parseDate(fixtureVo.getPurchaseDateStr()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (StringUtils.isNotEmpty(fixtureVo.getDeployDateStr())) {
                try {
                    fixtureVo.setDeployDate(DateUtils.parseDate(fixtureVo.getDeployDateStr()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            // ç®¡ç†äººå¤„理
            if (StringUtils.isNotEmpty(fixtureVo.getManageUserStr())) {
                try {
                    LambdaQueryWrapper<SysUser> usrQueryWrapper = new LambdaQueryWrapper<>();
                    usrQueryWrapper.eq(SysUser::getNickName,fixtureVo.getManageUserStr());
                    SysUserVo sysUserVo = sysUserMapper.selectVoOne(usrQueryWrapper);
                    if(sysUserVo!=null){
                        fixtureVo.setManageUser(sysUserVo.getUserId());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            // éƒ¨é—¨å¤„理
            if (StringUtils.isNotEmpty(fixtureVo.getUseDeptStr())) {
                try {
                    LambdaQueryWrapper<SysDept> dptQueryWrapper = new LambdaQueryWrapper<>();
                    dptQueryWrapper.eq(SysDept::getDeptName,fixtureVo.getUseDeptStr());
                    SysDeptVo deptVo = sysDeptMapper.selectVoOne(dptQueryWrapper);
                    if(deptVo!=null){
                        fixtureVo.setUseDept(deptVo.getDeptId());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (StringUtils.isNotEmpty(fixtureVo.getUnitPriceStr())) {
                try {
                    String priceStr = extractNumber(fixtureVo.getUnitPriceStr());
                    int price = 0;
                    if (priceStr != null) {
                        price = Integer.parseInt(priceStr);
                    }
                    if(price>0){
                        fixtureVo.setUnitPrice(new BigDecimal(price));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            // éªŒè¯æ˜¯å¦å­˜åœ¨
            if (ObjectUtil.isNull(eimsFixtureVo)) {
                EimsFixture fixture = BeanUtil.toBean(fixtureVo, EimsFixture.class);
                // TODO æ ¡éªŒ
                //ValidatorUtils.validate(equ);
                //添加字段默认属性
                fixture.setCreateBy(operUserId);
                fixtureMapper.insert(fixture);
                successNum++;
                successMsg.append("<br/>").append(successNum).append("、工具 ").append(fixture.getFixtureName()).append(" å¯¼å…¥æˆåŠŸ");
            } else if (isUpdateSupport) {
                EimsFixture fixture = BeanUtil.toBean(fixtureVo, EimsFixture.class);
                fixture.setId(eimsFixtureVo.getId());
                fixture.setUpdateBy(operUserId);
                fixture.setUpdateTime(new Date());
                fixtureMapper.updateById(fixture);
                coverNum++;
                successMsg.append("<br/>").append(successNum).append("、工具 ").append(fixture.getFixtureName()).append(" æ›´æ–°æˆåŠŸ");
            } else {
                failureNum++;
                failureMsg.append("<br/>").append(failureNum).append("、工具 ").append(eimsFixtureVo.getFixtureName()).append(eimsFixtureVo.getAssetNo()).append(" å·²å­˜åœ¨");
            }
        } catch (Exception e) {
            failureNum++;
            String msg = "<br/>" + failureNum + "、工具 " + fixtureVo.getFixtureName() + " å¯¼å…¥å¤±è´¥ï¼š";
            failureMsg.append(msg).append(e.getMessage());
            log.error(msg, e);
        }
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
    }
    @Override
    public ExcelResult<EimsFixtureImportVo> getExcelResult() {
        return new ExcelResult<>() {
            @Override
            public String getAnalysis() {
                if (failureNum > 0) {
                    failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " æ¡æ•°æ®æ ¼å¼ä¸æ­£ç¡®ï¼Œé”™è¯¯å¦‚下:");
                    throw new ServiceException(failureMsg.toString());
                } else {
                    successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + (successNum + coverNum )+ " æ¡ï¼Œæ•°æ®å¦‚下:");
                }
                return successMsg.toString();
            }
            @Override
            public List<EimsFixtureImportVo> getList() {
                return null;
            }
            @Override
            public List<String> getErrorList() {
                return null;
            }
        };
    }
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
        // å¦‚果是某一个单元格的转换异常 èƒ½èŽ·å–åˆ°å…·ä½“è¡Œå·
        // å¦‚果要获取头的信息 é…åˆinvokeHeadMap使用
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
                excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
        }
    }
    /**
     * æå–æ•°å­—
      * @param input
     * @return
     */
    private static String extractNumber(String input) {
        Pattern NUMBER_PATTERN = Pattern.compile("(\\d+(\\.\\d+)?)");
        Matcher matcher = NUMBER_PATTERN.matcher(input);
        if (matcher.find()) {
            return matcher.group(1); // è¿”回第一个捕获组,即数字部分
        }
        return null;
    }
    /**
     * ä¸ºå¯¼å…¥è®¾å¤‡åŒ¹é…å•位
     *
     * @param fixtureVo
     * @param unitDictMap
     */
    private void normalizeUnit(EimsFixtureImportVo fixtureVo, Map<String, String> unitDictMap) {
        if (fixtureVo == null || StringUtils.isEmpty(fixtureVo.getUnit()) || unitDictMap == null || unitDictMap.isEmpty()) {
            return;
        }
        String originalUnit = fixtureVo.getUnit();
        //避免错误单位
        fixtureVo.setUnit(null);
        for (Map.Entry<String, String> entry : unitDictMap.entrySet()) {
            if (originalUnit.contains(entry.getValue())) {
                fixtureVo.setUnit(entry.getValue());
                break;
            }
        }
    }
}