车间能级提升-智能设备管理系统
zhuguifei
2025-04-12 2b3715f1610b4176d7abe33e34542389cef61853
Merge branch 'main' of http://lanpucloud.cn:1111/r/eims-master
已添加9个文件
已修改11个文件
1401 ■■■■ 文件已修改
eims-ui/apps/web-antd/src/api/eims/insp-plan/index.ts 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/maint-plan/index.ts 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/insp-plan/data.tsx 284 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/insp-plan/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/insp-plan/insp-plan-import-modal.vue 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/maint-plan/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/maint-plan/maint-plan-import-modal.vue 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-admin/src/main/resources/template/by.xls 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-admin/src/main/resources/template/dj.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsInspectPlanController.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsMaintPlanController.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/InspectCheckItemVo.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/MaintCheckItemVo.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/listener/EasyExcelCellListener.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/listener/InspectCheckItemImportListener.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/listener/MaintCheckItemImportListener.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsInspectPlanService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsMaintPlanService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsInspectPlanServiceImpl.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsMaintPlanServiceImpl.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/insp-plan/index.ts
@@ -1,14 +1,15 @@
import type { InspectPlanVO } from './model';
import type { ID, IDS } from '#/api/common';
import { commonExport } from '#/api/helper';
import { commonExport, ContentTypeEnum } from '#/api/helper';
import { requestClient } from '#/api/request';
enum Api {
  inspectPlanExport = '/eims/inspectPlan/export',
  inspectPlanList = '/eims/inspectPlan/list',
  root = '/eims/inspectPlan'
  root = '/eims/inspectPlan',
  inspectPlanImport = '/eims/inspectPlan/importData',
  inspectPlanImportTemplate = '/eims/inspectPlan/importTemplate'
}
/**
@@ -61,3 +62,24 @@
export function inspPlanExport(data: any) {
  return commonExport(Api.inspectPlanExport, data);
}
/**
 * ä¸‹è½½ç‚¹æ£€è®¡åˆ’导入模板
 */
export function downloadImportTemplate() {
  return commonExport(Api.inspectPlanImportTemplate);
}
/**
 * ä»Žexcel导入点检计划数据
 * @param data
 * @returns void
 */
export function inspPlanImportData(data: any) {
  return requestClient.post<{ code: number; msg: string }>(Api.inspectPlanImport, data, {
    headers: {
      'Content-Type': ContentTypeEnum.FORM_DATA
    },
    isTransformResponse: false
  });
}
eims-ui/apps/web-antd/src/api/eims/maint-plan/index.ts
@@ -2,13 +2,15 @@
import type { ID, IDS } from '#/api/common';
import { commonExport } from '#/api/helper';
import { commonExport, ContentTypeEnum } from '#/api/helper';
import { requestClient } from '#/api/request';
enum Api {
  maintPlanExport = '/eims/maintPlan/export',
  maintPlanList = '/eims/maintPlan/list',
  root = '/eims/maintPlan'
  root = '/eims/maintPlan',
  maintPlanImport = '/eims/maintPlan/importData',
  maintPlanImportTemplate = '/eims/maintPlan/importTemplate'
}
/**
@@ -59,3 +61,24 @@
export function maintPlanExport(data: any) {
  return commonExport(Api.maintPlanExport, data);
}
/**
 * ä¸‹è½½ä¿å…»è®¡åˆ’导入模板
 */
export function downloadImportTemplate() {
  return commonExport(Api.maintPlanImportTemplate);
}
/**
 * ä»Žexcel导入保养计划数据
 * @param data
 * @returns void
 */
export function maintPlanImportData(data: any) {
  return requestClient.post<{ code: number; msg: string }>(Api.maintPlanImport, data, {
    headers: {
      'Content-Type': ContentTypeEnum.FORM_DATA
    },
    isTransformResponse: false
  });
}
eims-ui/apps/web-antd/src/views/eims/insp-plan/data.tsx
@@ -82,51 +82,51 @@
    field: 'inspName',
    minWidth: 200
  },
  {
    title: '点检类型',
    field: 'inspType',
    minWidth: 120,
    slots: {
      default: ({ row }) => {
        return renderDict(row.inspType, DictEnum.EIMS_INSPECT_TYPE);
      }
    }
  },
  {
    title: '点检人',
    field: 'inspUserName',
    minWidth: 100
  },
  {
    title: '循环周期',
    field: 'inspCycleUnitName',
    minWidth: 80
  },
  {
    title: '时间计算规则',
    field: 'inspRule',
    minWidth: 140,
    slots: {
      default: ({ row }) => {
        return renderDict(row.inspRule, DictEnum.MAINT_TIME_RULE);
      }
    }
  },
  {
    title: '首次执行时间',
    field: 'inspFirstTime',
    minWidth: 160
  },
  {
    title: '上次执行时间',
    field: 'inspLastTime',
    minWidth: 160
  },
  {
    title: '下次执行时间',
    field: 'inspNextTime',
    minWidth: 160
  },
  // {
  //   title: '点检类型',
  //   field: 'inspType',
  //   minWidth: 120,
  //   slots: {
  //     default: ({ row }) => {
  //       return renderDict(row.inspType, DictEnum.EIMS_INSPECT_TYPE);
  //     }
  //   }
  // },
  // {
  //   title: '点检人',
  //   field: 'inspUserName',
  //   minWidth: 100
  // },
  // {
  //   title: '循环周期',
  //   field: 'inspCycleUnitName',
  //   minWidth: 80
  // },
  // {
  //   title: '时间计算规则',
  //   field: 'inspRule',
  //   minWidth: 140,
  //   slots: {
  //     default: ({ row }) => {
  //       return renderDict(row.inspRule, DictEnum.MAINT_TIME_RULE);
  //     }
  //   }
  // },
  // {
  //   title: '首次执行时间',
  //   field: 'inspFirstTime',
  //   minWidth: 160
  // },
  // {
  //   title: '上次执行时间',
  //   field: 'inspLastTime',
  //   minWidth: 160
  // },
  // {
  //   title: '下次执行时间',
  //   field: 'inspNextTime',
  //   minWidth: 160
  // },
  {
    title: '创建时间',
    field: 'createTime',
@@ -169,75 +169,75 @@
    fieldName: 'inspName',
    label: '点检项'
  },
  {
    component: 'RadioGroup',
    componentProps: {
      buttonStyle: 'solid',
      options: getDictOptions(DictEnum.EIMS_INSPECT_TYPE),
      optionType: 'button'
    },
    fieldName: 'inspType',
    defaultValue: '1',
    label: '点检类型'
  },
  // {
  //   component: 'RadioGroup',
  //   componentProps: {
  //     buttonStyle: 'solid',
  //     options: getDictOptions(DictEnum.EIMS_INSPECT_TYPE),
  //     optionType: 'button'
  //   },
  //   fieldName: 'inspType',
  //   defaultValue: '1',
  //   label: '点检类型'
  // },
  {
    component: 'Input',
    fieldName: 'inspDesc',
    label: '点检说明'
  },
  {
    component: 'InputNumber',
    fieldName: 'inspCycle',
    label: '点检周期',
    formItemClass: 'col-span-1',
    componentProps: {
      min: 1
    }
  },
  {
    component: 'Select',
    componentProps: {
      getPopupContainer,
      options: getDictOptions(DictEnum.MAINT_CYCLE_UNIT)
    },
    fieldName: 'inspCycleUnit',
    formItemClass: 'col-span-1 w-[80px]',
    labelWidth: 0,
    label: ''
  },
  {
    component: 'Select',
    componentProps: {
      getPopupContainer,
      options: getDictOptions(DictEnum.MAINT_TIME_RULE)
    },
    fieldName: 'inspRule',
    label: '点检规则 '
  },
  {
    component: 'Input',
    fieldName: 'inspUser',
    label: '用户id',
    dependencies: {
      show: () => false,
      triggerFields: ['']
    }
  },
  {
    component: 'Input',
    fieldName: 'inspUserName',
    label: '点检人'
  },
  {
    component: 'Input',
    fieldName: 'inspDept',
    label: '部门id',
    dependencies: {
      show: () => false,
      triggerFields: ['']
    }
  },
  // {
  //   component: 'InputNumber',
  //   fieldName: 'inspCycle',
  //   label: '点检周期',
  //   formItemClass: 'col-span-1',
  //   componentProps: {
  //     min: 1
  //   }
  // },
  // {
  //   component: 'Select',
  //   componentProps: {
  //     getPopupContainer,
  //     options: getDictOptions(DictEnum.MAINT_CYCLE_UNIT)
  //   },
  //   fieldName: 'inspCycleUnit',
  //   formItemClass: 'col-span-1 w-[80px]',
  //   labelWidth: 0,
  //   label: ''
  // },
  //
  // {
  //   component: 'Select',
  //   componentProps: {
  //     getPopupContainer,
  //     options: getDictOptions(DictEnum.MAINT_TIME_RULE)
  //   },
  //   fieldName: 'inspRule',
  //   label: '点检规则 '
  // },
  // {
  //   component: 'Input',
  //   fieldName: 'inspUser',
  //   label: '用户id',
  //   dependencies: {
  //     show: () => false,
  //     triggerFields: ['']
  //   }
  // },
  // {
  //   component: 'Input',
  //   fieldName: 'inspUserName',
  //   label: '点检人'
  // },
  // {
  //   component: 'Input',
  //   fieldName: 'inspDept',
  //   label: '部门id',
  //   dependencies: {
  //     show: () => false,
  //     triggerFields: ['']
  //   }
  // },
  {
    component: 'RadioGroup',
    componentProps: {
@@ -249,39 +249,39 @@
    defaultValue: '0',
    label: '状态'
  },
  {
    component: 'DatePicker',
    componentProps: {
      format: 'YYYY-MM-DD',
      showTime: false,
      valueFormat: 'YYYY-MM-DD',
      getPopupContainer
    },
    fieldName: 'inspFirstTime',
    label: '首次点检时间'
  },
  {
    component: 'DatePicker',
    componentProps: {
      format: 'YYYY-MM-DD',
      showTime: false,
      valueFormat: 'YYYY-MM-DD',
      getPopupContainer
    },
    fieldName: 'inspLastTime',
    label: '上次点检时间'
  },
  {
    component: 'DatePicker',
    componentProps: {
      format: 'YYYY-MM-DD',
      showTime: false,
      valueFormat: 'YYYY-MM-DD',
      getPopupContainer
    },
    fieldName: 'inspNextTime',
    label: '下次点检时间',
  },
  // {
  //   component: 'DatePicker',
  //   componentProps: {
  //     format: 'YYYY-MM-DD',
  //     showTime: false,
  //     valueFormat: 'YYYY-MM-DD',
  //     getPopupContainer
  //   },
  //   fieldName: 'inspFirstTime',
  //   label: '首次点检时间'
  // },
  // {
  //   component: 'DatePicker',
  //   componentProps: {
  //     format: 'YYYY-MM-DD',
  //     showTime: false,
  //     valueFormat: 'YYYY-MM-DD',
  //     getPopupContainer
  //   },
  //   fieldName: 'inspLastTime',
  //   label: '上次点检时间'
  // },
  // {
  //   component: 'DatePicker',
  //   componentProps: {
  //     format: 'YYYY-MM-DD',
  //     showTime: false,
  //     valueFormat: 'YYYY-MM-DD',
  //     getPopupContainer
  //   },
  //   fieldName: 'inspNextTime',
  //   label: '下次点检时间',
  // },
  {
    component: 'Textarea',
    fieldName: 'remark',
eims-ui/apps/web-antd/src/views/eims/insp-plan/index.vue
@@ -3,7 +3,7 @@
import { onMounted, ref } from 'vue';
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
import { Page, useVbenDrawer, useVbenModal, type VbenFormProps } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { addFullName, getPopupContainer, getVxePopupContainer } from '@vben/utils';
@@ -19,6 +19,7 @@
import { columns, querySchema } from './data';
import inspPlanDrawer from './insp-plan-drawer.vue';
import inspPlanImportModal from './insp-plan-import-modal.vue';
defineExpose({
  tableSelect
@@ -94,6 +95,17 @@
const [InspRecordDrawer, inspRecordDrawerApi] = useVbenDrawer({
  connectedComponent: inspRecordDrawer
});
/**
 * å¯¼å…¥
 */
const [InspPlanImportModal, inspPlanImportModalApi] = useVbenModal({
  connectedComponent: inspPlanImportModal
});
function handleImport() {
  inspPlanImportModalApi.open();
}
function handleAdd() {
  inspPlanDrawerApi.setData({});
@@ -251,6 +263,9 @@
            <a-button v-access:code="['eims:inspectPlan:export']" @click="handleDownloadExcel">
              {{ $t('pages.common.export') }}
            </a-button>
            <a-button v-access:code="['eims:inspectPlan:import']" @click="handleImport">
              {{ $t('pages.common.import') }}
            </a-button>
            <a-button
              :disabled="!vxeCheckboxChecked(tableApi)"
              danger
@@ -298,5 +313,6 @@
    </div>
    <InspPlanDrawer @reload="tableApi.query()" />
    <InspRecordDrawer @reload="tableApi.query()" />
    <InspPlanImportModal @reload="tableApi.query()" />
  </Page>
</template>
eims-ui/apps/web-antd/src/views/eims/insp-plan/insp-plan-import-modal.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,112 @@
<script setup lang="ts">
import type { UploadFile } from 'ant-design-vue/es/upload/interface';
import { h, ref, unref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { ExcelIcon, InBoxIcon } from '@vben/icons';
import { Modal, Switch, Upload } from 'ant-design-vue';
import { downloadImportTemplate, inspPlanImportData } from '#/api/eims/insp-plan';
import { commonDownloadExcel } from '#/utils/file/download';
const emit = defineEmits<{ reload: [] }>();
const UploadDragger = Upload.Dragger;
const [BasicModal, modalApi] = useVbenModal({
  onCancel: handleCancel,
  onConfirm: handleSubmit,
});
const fileList = ref<UploadFile[]>([]);
const checked = ref(false);
async function handleSubmit() {
  try {
    modalApi.modalLoading(true);
    if (fileList.value.length !== 1) {
      handleCancel();
      return;
    }
    const data = {
      file: fileList.value[0]!.originFileObj as Blob,
      updateSupport: unref(checked),
    };
    const { code, msg } = await inspPlanImportData(data);
    let modal = Modal.success;
    if (code === 200) {
      emit('reload');
    } else {
      emit('reload');
      modal = Modal.error;
    }
    handleCancel();
    modal({
      content: h('div', {
        class: 'max-h-[260px] overflow-y-auto',
        innerHTML: msg, // åŽå°å·²ç»å¤„理xss问题
      }),
      title: '提示',
    });
  } catch (error) {
    console.warn(error);
    modalApi.close();
  } finally {
    modalApi.modalLoading(false);
  }
}
function handleCancel() {
  modalApi.close();
  fileList.value = [];
  checked.value = false;
}
</script>
<template>
  <BasicModal
    :close-on-click-modal="false"
    :fullscreen-button="false"
    title="点检计划导入"
  >
    <!-- z-index不设置会遮挡模板下载loading -->
    <!-- æ‰‹åŠ¨å¤„ç† è€Œä¸æ˜¯æ”¾å…¥æ–‡ä»¶å°±ä¸Šä¼  -->
    <UploadDragger
      v-model:file-list="fileList"
      :before-upload="() => false"
      :max-count="1"
      :show-upload-list="true"
      accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
    >
      <p class="ant-upload-drag-icon flex items-center justify-center">
        <InBoxIcon class="text-primary size-[48px]" />
      </p>
      <p class="ant-upload-text">点击或者拖拽到此处上传文件</p>
    </UploadDragger>
    <div class="mt-2 flex flex-col gap-2">
      <div class="flex items-center gap-2">
        <span>允许导入xlsx, xls文件</span>
        <a-button
          type="link"
          @click="commonDownloadExcel(downloadImportTemplate, '点检计划导入模板')"
        >
          <div class="flex items-center gap-[4px]">
            <ExcelIcon />
            <span>下载模板</span>
          </div>
        </a-button>
      </div>
      <div class="flex items-center gap-2">
        <span class="text-red-500">⚠️特别注意⚠️:请下载模版保持导入文件表头和模版一致后导入</span>
      </div>
<!--      <div class="flex items-center gap-2">-->
<!--        <span :class="{ 'text-red-500': checked }">-->
<!--          æ˜¯å¦æ›´æ–°/覆盖已存在的点检计划数据-->
<!--        </span>-->
<!--        <Switch v-model:checked="checked" />-->
<!--      </div>-->
    </div>
  </BasicModal>
</template>
eims-ui/apps/web-antd/src/views/eims/maint-plan/index.vue
@@ -3,7 +3,7 @@
import { onMounted, ref } from 'vue';
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
import { Page, useVbenDrawer, useVbenModal, type VbenFormProps } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { addFullName, getPopupContainer, getVxePopupContainer } from '@vben/utils';
@@ -19,6 +19,7 @@
import { columns, querySchema } from './data';
import maintPlanDrawer from './maint-plan-drawer.vue';
import maintPlanImportModal from './maint-plan-import-modal.vue';
defineExpose({
@@ -93,6 +94,17 @@
const [MaintOrderDrawer, maintOrderDrawerApi] = useVbenDrawer({
  connectedComponent: maintOrderDrawer,
});
/**
 * å¯¼å…¥
 */
const [MaintPlanImportModal, maintPlanImportModalApi] = useVbenModal({
  connectedComponent: maintPlanImportModal
});
function handleImport() {
  maintPlanImportModalApi.open();
}
function handleAdd() {
  maintPlanDrawerApi.setData({});
@@ -250,6 +262,9 @@
            <a-button v-access:code="['eims:maintPlan:export']" @click="handleDownloadExcel">
              {{ $t('pages.common.export') }}
            </a-button>
            <a-button v-access:code="['eims:maintPlan:import']" @click="handleImport">
              {{ $t('pages.common.import') }}
            </a-button>
            <a-button
              :disabled="!vxeCheckboxChecked(tableApi)"
              danger
@@ -297,5 +312,6 @@
    </div>
    <MaintPlanDrawer @reload="tableApi.query()" />
    <MaintOrderDrawer @reload="tableApi.query()" />
    <MaintPlanImportModal @reload="tableApi.query()" />
  </Page>
</template>
eims-ui/apps/web-antd/src/views/eims/maint-plan/maint-plan-import-modal.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,112 @@
<script setup lang="ts">
import type { UploadFile } from 'ant-design-vue/es/upload/interface';
import { h, ref, unref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { ExcelIcon, InBoxIcon } from '@vben/icons';
import { Modal, Switch, Upload } from 'ant-design-vue';
import { downloadImportTemplate, maintPlanImportData } from '#/api/eims/maint-plan';
import { commonDownloadExcel } from '#/utils/file/download';
const emit = defineEmits<{ reload: [] }>();
const UploadDragger = Upload.Dragger;
const [BasicModal, modalApi] = useVbenModal({
  onCancel: handleCancel,
  onConfirm: handleSubmit,
});
const fileList = ref<UploadFile[]>([]);
const checked = ref(false);
async function handleSubmit() {
  try {
    modalApi.modalLoading(true);
    if (fileList.value.length !== 1) {
      handleCancel();
      return;
    }
    const data = {
      file: fileList.value[0]!.originFileObj as Blob,
      updateSupport: unref(checked),
    };
    const { code, msg } = await maintPlanImportData(data);
    let modal = Modal.success;
    if (code === 200) {
      emit('reload');
    } else {
      emit('reload');
      modal = Modal.error;
    }
    handleCancel();
    modal({
      content: h('div', {
        class: 'max-h-[260px] overflow-y-auto',
        innerHTML: msg, // åŽå°å·²ç»å¤„理xss问题
      }),
      title: '提示',
    });
  } catch (error) {
    console.warn(error);
    modalApi.close();
  } finally {
    modalApi.modalLoading(false);
  }
}
function handleCancel() {
  modalApi.close();
  fileList.value = [];
  checked.value = false;
}
</script>
<template>
  <BasicModal
    :close-on-click-modal="false"
    :fullscreen-button="false"
    title="保养计划导入"
  >
    <!-- z-index不设置会遮挡模板下载loading -->
    <!-- æ‰‹åŠ¨å¤„ç† è€Œä¸æ˜¯æ”¾å…¥æ–‡ä»¶å°±ä¸Šä¼  -->
    <UploadDragger
      v-model:file-list="fileList"
      :before-upload="() => false"
      :max-count="1"
      :show-upload-list="true"
      accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
    >
      <p class="ant-upload-drag-icon flex items-center justify-center">
        <InBoxIcon class="text-primary size-[48px]" />
      </p>
      <p class="ant-upload-text">点击或者拖拽到此处上传文件</p>
    </UploadDragger>
    <div class="mt-2 flex flex-col gap-2">
      <div class="flex items-center gap-2">
        <span>允许导入xlsx, xls文件</span>
        <a-button
          type="link"
          @click="commonDownloadExcel(downloadImportTemplate, '保养计划导入模板')"
        >
          <div class="flex items-center gap-[4px]">
            <ExcelIcon />
            <span>下载模板</span>
          </div>
        </a-button>
      </div>
      <div class="flex items-center gap-2">
        <span class="text-red-500">⚠️特别注意⚠️:请下载模版保持导入文件表头和模版一致后导入</span>
      </div>
<!--      <div class="flex items-center gap-2">-->
<!--        <span :class="{ 'text-red-500': checked }">-->
<!--          æ˜¯å¦æ›´æ–°/覆盖已存在的保养计划数据-->
<!--        </span>-->
<!--        <Switch v-model:checked="checked" />-->
<!--      </div>-->
    </div>
  </BasicModal>
</template>
eims/ruoyi-admin/src/main/resources/template/by.xls
Binary files differ
eims/ruoyi-admin/src/main/resources/template/dj.xlsx
Binary files differ
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsInspectPlanController.java
@@ -1,11 +1,18 @@
package org.dromara.eims.controller;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import jakarta.servlet.ServletOutputStream;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.core.io.ClassPathResource;
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 +28,7 @@
import org.dromara.eims.domain.bo.EimsInspectPlanBo;
import org.dromara.eims.service.IEimsInspectPlanService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
/**
 * ç‚¹æ£€è®¡åˆ’
@@ -103,4 +111,49 @@
                          @PathVariable Long[] ids) {
        return toAjax(eimsInspectPlanService.deleteWithValidByIds(List.of(ids), true));
    }
    /**
     * å¯¼å…¥æ•°æ®
     *
     * @param file          å¯¼å…¥æ–‡ä»¶
     * @param updateSupport æ˜¯å¦æ›´æ–°å·²å­˜åœ¨æ•°æ®
     */
    @Log(title = "点检计划", businessType = BusinessType.IMPORT)
    @SaCheckPermission("eims:inspectPlan:import")
    @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Void> importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception {
        // ExcelResult<InspectCheckItemVo> result = ExcelUtil.importExcel(file.getInputStream(), InspectCheckItemVo.class, new InspectCheckItemImportListener(updateSupport));
        String res = eimsInspectPlanService.importData(file, updateSupport);
        return R.ok(res);
    }
    /**
     * èŽ·å–å¯¼å…¥æ¨¡æ¿
     */
    @PostMapping("/importTemplate")
    public void importTemplate(HttpServletResponse response) throws UnsupportedEncodingException {
        // è®¾ç½®å“åº”类型
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // è®¾ç½®æ–‡ä»¶å
        String fileName = URLEncoder.encode("点检计划导入模板", "UTF-8");
        response.setHeader("Content-Disposition",
            "attachment;filename=" + fileName + ".xlsx");
        // è¯»å–模板文件
        ClassPathResource templateResource = new ClassPathResource("template/dj.xlsx");
        try (InputStream inputStream = templateResource.getInputStream();
             ServletOutputStream outputStream = response.getOutputStream()) {
            // æµæ‹·è´
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            throw new RuntimeException("模板文件读取失败", e);
        }
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsMaintPlanController.java
@@ -1,11 +1,17 @@
package org.dromara.eims.controller;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import jakarta.servlet.ServletOutputStream;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.core.io.ClassPathResource;
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 +27,7 @@
import org.dromara.eims.domain.bo.EimsMaintPlanBo;
import org.dromara.eims.service.IEimsMaintPlanService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
/**
 * ä¿å…»è®¡åˆ’
@@ -65,7 +72,7 @@
    @SaCheckPermission("eims:maintPlan:query")
    @GetMapping("/{id}")
    public R<EimsMaintPlanVo> getInfo(@NotNull(message = "主键不能为空")
                                     @PathVariable Long id) {
                                      @PathVariable Long id) {
        return R.ok(eimsMaintPlanService.queryById(id));
    }
@@ -103,4 +110,55 @@
                          @PathVariable Long[] ids) {
        return toAjax(eimsMaintPlanService.deleteWithValidByIds(List.of(ids), true));
    }
    /**
     * å¯¼å…¥æ•°æ®
     *
     * @param file          å¯¼å…¥æ–‡ä»¶
     * @param updateSupport æ˜¯å¦æ›´æ–°å·²å­˜åœ¨æ•°æ®
     */
    @Log(title = "保养计划", businessType = BusinessType.IMPORT)
    @SaCheckPermission("eims:maintPlan:import")
    @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Void> importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception {
        //ExcelResult<MaintCheckItemVo> result = ExcelUtil.importExcel(file.getInputStream(), MaintCheckItemVo.class, new MaintCheckItemImportListener(updateSupport));
        String res = eimsMaintPlanService.importData(file, updateSupport);
        return R.ok(res);
    }
    /**
     * èŽ·å–å¯¼å…¥æ¨¡æ¿
     */
    @PostMapping("/importTemplate")
    public void importTemplate(HttpServletResponse response) throws IOException {
        // è®¾ç½®å“åº”类型
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // è®¾ç½®æ–‡ä»¶å
        String fileName = URLEncoder.encode("保养计划导入模板", "UTF-8");
        response.setHeader("Content-Disposition",
            "attachment;filename=" + fileName + ".xls");
        // è¯»å–模板文件
        ClassPathResource templateResource = new ClassPathResource("template/by.xls");
        try (InputStream inputStream = templateResource.getInputStream();
             ServletOutputStream outputStream = response.getOutputStream()) {
            // æµæ‹·è´
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            throw new RuntimeException("模板文件读取失败", e);
        }
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/InspectCheckItemVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package org.dromara.eims.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import java.io.Serial;
import java.io.Serializable;
/**
 * ç‚¹æ£€é¡¹ç›®å¯¼å…¥VO
 *
 * @author zhuguifei
 */
@Data
@NoArgsConstructor
public class InspectCheckItemVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ç‚¹æ£€é¡¹åç§°
     */
    @ExcelProperty(value = "内容")
    private String itemName;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/MaintCheckItemVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package org.dromara.eims.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
 * ä¿å…»ç‚¹æ£€é¡¹ç›®å®žä½“
 */
@Data
public class MaintCheckItemVo {
    /**
     * å®žæ–½é¡¹ç›®
     */
    @ExcelProperty("实施项目")
    private String itemName;
    /**
     * å‘¨æœŸï¼ˆæœˆï¼‰
     */
    @ExcelProperty("周期")
    private String period;
    /**
     * 1月检查状态
     */
    @ExcelProperty("1月")
    private String january;
    /**
     * 2月检查状态
     */
    @ExcelProperty("2月")
    private String february;
    /**
     * 3月检查状态
     */
    @ExcelProperty("3月")
    private String march;
    /**
     * 4月检查状态
     */
    @ExcelProperty("4月")
    private String april;
    /**
     * 5月检查状态
     */
    @ExcelProperty("5月")
    private String may;
    /**
     * 6月检查状态
     */
    @ExcelProperty("6月")
    private String june;
    /**
     * 7月检查状态
     */
    @ExcelProperty("7月")
    private String july;
    /**
     * 8月检查状态
     */
    @ExcelProperty("8月")
    private String august;
    /**
     * 9月检查状态
     */
    @ExcelProperty("9月")
    private String september;
    /**
     * 10月检查状态
     */
    @ExcelProperty("10月")
    private String october;
    /**
     * 11月检查状态
     */
    @ExcelProperty("11月")
    private String november;
    /**
     * 12月检查状态
     */
    @ExcelProperty("12月")
    private String december;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/listener/EasyExcelCellListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package org.dromara.eims.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import java.util.Map;
public class EasyExcelCellListener implements ReadListener<Map<Integer,String>> {
    private String cellValue;
    private int targetRow;
    private int targetColumn;
    private boolean isCellRead = false;
    public EasyExcelCellListener(int targetRow, int targetColumn) {
        this.targetRow = targetRow;
        this.targetColumn = targetColumn;
    }
    @Override
    //invoke方法的参数类型跟随实现的ReadListener的类型
    public void invoke(Map<Integer,String> map, AnalysisContext analysisContext) {
        if(isCellRead){
            return;
        }
        int currentRow = analysisContext.readRowHolder().getRowIndex()+1;
        if(currentRow == targetRow ){
            cellValue = map.get(targetColumn-1);
            isCellRead = true;
        }
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    }
    public String getCellValue() {
        return cellValue;
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/listener/InspectCheckItemImportListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,147 @@
package org.dromara.eims.listener;
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 lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.SpringUtils;
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.vo.EimsEquVo;
import org.dromara.eims.domain.vo.InspectCheckItemVo;
import org.dromara.eims.service.IEimsEquService;
import java.util.ArrayList;
import java.util.List;
/**
 * ç‚¹æ£€é¡¹ç›®å¯¼å…¥ç›‘听器
 *
 * @author zhuguifei
 */
@Data
@Slf4j
public class InspectCheckItemImportListener extends AnalysisEventListener<InspectCheckItemVo> implements ExcelListener<InspectCheckItemVo> {
    private final IEimsEquService equService;
    private final DictService dictService;
    private final Long operUserId;
    private final Boolean isUpdateSupport;
    /**
     * æˆåŠŸæ¡æ•°
     */
    private Integer successCount = 0;
    /**
     * å¤±è´¥æ¡æ•°
     */
    private Integer failureCount = 0;
    /**
     * å¯¼å…¥æˆåŠŸæ•°æ®åˆ—è¡¨
     */
    private List<InspectCheckItemVo> successList = new ArrayList<>();
    /**
     * å¯¼å…¥å¤±è´¥æ•°æ®åˆ—表
     */
    private List<InspectCheckItemVo> failureList = new ArrayList<>();
    private final StringBuilder successMsg = new StringBuilder();
    private final StringBuilder failureMsg = new StringBuilder();
    public InspectCheckItemImportListener(Boolean isUpdateSupport) {
        this.equService = SpringUtils.getBean(IEimsEquService.class);
        this.dictService = SpringUtils.getBean(DictService.class);
        this.isUpdateSupport = isUpdateSupport;
        this.operUserId = LoginHelper.getUserId();
    }
    @Override
    public void invoke(InspectCheckItemVo data, AnalysisContext context) {
        try {
            // æ•°æ®æ ¡éªŒ
            if (!validateData(data)) {
                failureList.add(data);
                failureCount++;
                failureMsg.append("<br/>").append(failureCount).append("、点检项 ")
                    .append(data.getItemName()).append(" æ•°æ®æ ¡éªŒå¤±è´¥");
                return;
            }
            successList.add(data);
            successCount++;
            successMsg.append("<br/>").append(successCount).append("、点检项 ")
                .append(data.getItemName()).append(" å¯¼å…¥æˆåŠŸ");
        } catch (Exception e) {
            failureList.add(data);
            failureCount++;
            String msg = "<br/>" + failureCount + "、点检项 " + data.getItemName() + " å¯¼å…¥å¤±è´¥ï¼š";
            failureMsg.append(msg).append(e.getMessage());
            log.error("导入点检项目失败:", e);
        }
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("点检项目导入完成,成功:{}条,失败:{}条", successCount, failureCount);
    }
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        log.error("导入异常", exception);
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException e = (ExcelDataConvertException) exception;
            failureMsg.append("<br/>").append("第").append(e.getRowIndex()).append("行,第").append(e.getColumnIndex())
                .append("列解析异常:").append(e.getCause().getMessage());
        } else {
            failureMsg.append(exception.getMessage());
        }
    }
    @Override
    public ExcelResult<InspectCheckItemVo> getExcelResult() {
        return new ExcelResult<InspectCheckItemVo>() {
            @Override
            public List<InspectCheckItemVo> getList() {
                return successList;
            }
            @Override
            public List<String> getErrorList() {
                return List.of();
            }
            @Override
            public String getAnalysis() {
                if (successCount > 0) {
                    successMsg.insert(0, "恭喜您,共 " + successCount + " æ¡æ•°æ®å¯¼å…¥æˆåŠŸï¼");
                }
                if (failureCount > 0) {
                    failureMsg.insert(0, "很抱歉,共 " + failureCount + " æ¡æ•°æ®å¯¼å…¥å¤±è´¥ï¼");
                }
                return successMsg + failureMsg.toString();
            }
        };
    }
    /**
     * æ ¡éªŒæ•°æ®
     *
     * @param data å¾…校验数据
     * @return æ ¡éªŒç»“æžœ
     */
    private boolean validateData(InspectCheckItemVo data) {
        if (data.getItemName() == null || data.getItemName().trim().isEmpty()) {
            return false;
        }
        return true;
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/listener/MaintCheckItemImportListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,118 @@
package org.dromara.eims.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.excel.core.ExcelListener;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.eims.domain.vo.MaintCheckItemVo;
import java.util.ArrayList;
import java.util.List;
/**
 * ä¿å…»ç‚¹æ£€é¡¹ç›®å¯¼å…¥ç›‘听器
 *
 * @author Lion Li
 */
@Data
@Slf4j
public class MaintCheckItemImportListener extends AnalysisEventListener<MaintCheckItemVo> implements ExcelListener<MaintCheckItemVo> {
    /**
     * æˆåŠŸæ¡æ•°
     */
    private Integer successCount = 0;
    /**
     * å¤±è´¥æ¡æ•°
     */
    private Integer failureCount = 0;
    /**
     * å¯¼å…¥æˆåŠŸæ•°æ®åˆ—è¡¨
     */
    private List<MaintCheckItemVo> successList = new ArrayList<>();
    /**
     * å¯¼å…¥å¤±è´¥æ•°æ®åˆ—表
     */
    private List<MaintCheckItemVo> failureList = new ArrayList<>();
    public MaintCheckItemImportListener(Boolean isUpdateSupport) {
        super();
    }
    @Override
    public void invoke(MaintCheckItemVo data, AnalysisContext context) {
        try {
            // æ•°æ®æ ¡éªŒ
            if (!validateData(data)) {
                failureList.add(data);
                failureCount++;
                return;
            }
            successList.add(data);
            successCount++;
        } catch (Exception e) {
            failureList.add(data);
            failureCount++;
            log.error("导入保养点检项目失败:", e);
        }
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("保养点检项目导入完成,成功:{}条,失败:{}条", successCount, failureCount);
    }
    /**
     * æ ¡éªŒæ•°æ®
     *
     * @param data å¾…校验数据
     * @return æ ¡éªŒç»“æžœ
     */
    private boolean validateData(MaintCheckItemVo data) {
        if (data.getItemName() == null || data.getItemName().trim().isEmpty()) {
            return false;
        }
        if (data.getPeriod() == null || data.getPeriod().trim().isEmpty()) {
            return false;
        }
        // æ ¡éªŒ1-12月份状态值
        return validateMonthStatus(data.getJanuary())
            && validateMonthStatus(data.getFebruary())
            && validateMonthStatus(data.getMarch())
            && validateMonthStatus(data.getApril())
            && validateMonthStatus(data.getMay())
            && validateMonthStatus(data.getJune())
            && validateMonthStatus(data.getJuly())
            && validateMonthStatus(data.getAugust())
            && validateMonthStatus(data.getSeptember())
            && validateMonthStatus(data.getOctober())
            && validateMonthStatus(data.getNovember())
            && validateMonthStatus(data.getDecember());
    }
    /**
     * æ ¡éªŒæœˆä»½çŠ¶æ€å€¼
     *
     * @param status çŠ¶æ€å€¼
     * @return æ ¡éªŒç»“æžœ
     */
    private boolean validateMonthStatus(String status) {
        // çŠ¶æ€å€¼å¯ä»¥ä¸ºç©º
        if (status == null || status.trim().isEmpty()) {
            return true;
        }
        // çŠ¶æ€å€¼å¿…é¡»ç¬¦åˆè§„èŒƒï¼ˆæ ¹æ®å®žé™…ä¸šåŠ¡éœ€æ±‚å®šä¹‰æœ‰æ•ˆçš„çŠ¶æ€å€¼ï¼‰
        return true;
    }
    @Override
    public ExcelResult<MaintCheckItemVo> getExcelResult() {
        return null;
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsInspectPlanService.java
@@ -4,7 +4,9 @@
import org.dromara.eims.domain.bo.EimsInspectPlanBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
@@ -75,4 +77,5 @@
     */
    TableDataInfo<EimsInspectPlanVo> queryPageListCustom(EimsInspectPlanBo bo, PageQuery pageQuery);
    String importData(MultipartFile file, boolean updateSupport) throws IOException;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsMaintPlanService.java
@@ -4,7 +4,10 @@
import org.dromara.eims.domain.bo.EimsMaintPlanBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
@@ -74,4 +77,6 @@
     * @return ä¿å…»è®¡åˆ’分页列表
     */
    TableDataInfo<EimsMaintPlanVo> queryPageListCustom(EimsMaintPlanBo bo, PageQuery pageQuery);
    String importData(MultipartFile inputStream, boolean updateSupport) throws IOException;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsInspectPlanServiceImpl.java
@@ -1,6 +1,9 @@
package org.dromara.eims.service.impl;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -9,7 +12,13 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.eims.domain.EimsEqu;
import org.dromara.eims.domain.vo.EimsEquVo;
import org.dromara.eims.domain.vo.EimsMaintPlanVo;
import org.dromara.eims.domain.vo.InspectCheckItemVo;
import org.dromara.eims.listener.EasyExcelCellListener;
import org.dromara.eims.listener.InspectCheckItemImportListener;
import org.dromara.eims.mapper.EimsEquMapper;
import org.dromara.system.domain.SysDept;
import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.mapper.SysDeptMapper;
@@ -19,11 +28,10 @@
import org.dromara.eims.domain.EimsInspectPlan;
import org.dromara.eims.mapper.EimsInspectPlanMapper;
import org.dromara.eims.service.IEimsInspectPlanService;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.io.IOException;
import java.util.*;
/**
 * ç‚¹æ£€è®¡åˆ’Service业务层处理
@@ -37,6 +45,7 @@
    private final EimsInspectPlanMapper baseMapper;
    private final SysDeptMapper sysDeptMapper;
    private final EimsEquMapper equMapper;
    /**
@@ -196,5 +205,58 @@
        return baseMapper.deleteByIds(ids) > 0;
    }
    @Override
    public String importData(MultipartFile file, boolean updateSupport) throws IOException, IOException {
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        // ç‚¹æ£€é¡¹ç›®åˆ—表(假设存在对应的点检项VO)
        InspectCheckItemImportListener checkItemImportListener = new InspectCheckItemImportListener(updateSupport);
        EasyExcel.read(file.getInputStream(), InspectCheckItemVo.class, checkItemImportListener).headRowNumber(3).sheet().doRead();
        List<InspectCheckItemVo> successList = checkItemImportListener.getSuccessList();
        // è¯»å–固定资产编号(假设位置不同)
        EasyExcelCellListener assetNoListener = new EasyExcelCellListener(2, 23);
        EasyExcel.read(file.getInputStream(), assetNoListener).headRowNumber(0).sheet().doReadSync();
        String assetNo = Optional.ofNullable(assetNoListener.getCellValue())
            .map(value -> {
                int colonIndex = Math.max(value.indexOf(":"), value.indexOf(":")); // åˆå¹¶å†’号处理
                return colonIndex != -1 ? value.substring(colonIndex + 1) : value;
            })
            .map(String::trim)
            .orElseThrow(() -> new ServiceException("导入失败,无法读取固定资产编号"));
        // æŸ¥è¯¢è®¾å¤‡ä¿¡æ¯
        QueryWrapper<EimsEqu> query = new QueryWrapper<>();
        query.eq("asset_no", assetNo);
        EimsEquVo equVo = equMapper.selectVoOne(query);
        if (equVo == null) throw new ServiceException("设备未找到,请先在设备台帐中添加");
        for (InspectCheckItemVo itemVo : successList) {
            if ("设备状态卡状态".equals(itemVo.getItemName())) break;
            EimsInspectPlanBo bo = new EimsInspectPlanBo();
            bo.setEquId(equVo.getEquId());
            bo.setInspName(itemVo.getItemName());
            bo.setStatus("0");
            bo.setInspType("1");
            bo.setInspRule("0");
            if (!insertByBo(bo)) {
                failureNum++;
                failureMsg.append(failureNum).append("、导入失败<br>");
            } else {
                successNum++;
                successMsg.append(successNum).append("、导入成功<br>");
            }
        }
        return failureNum > 0 ? failureMsg.toString() : successMsg.toString();
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsMaintPlanServiceImpl.java
@@ -1,8 +1,8 @@
package org.dromara.eims.service.impl;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.SneakyThrows;
import org.dromara.common.core.constant.DictConstants;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
@@ -12,7 +12,12 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.eims.domain.vo.EimsRepairResVo;
import org.dromara.eims.domain.EimsEqu;
import org.dromara.eims.domain.vo.EimsEquVo;
import org.dromara.eims.domain.vo.MaintCheckItemVo;
import org.dromara.eims.listener.EasyExcelCellListener;
import org.dromara.eims.listener.MaintCheckItemImportListener;
import org.dromara.eims.mapper.EimsEquMapper;
import org.dromara.system.domain.SysDept;
import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.mapper.SysDeptMapper;
@@ -22,7 +27,9 @@
import org.dromara.eims.domain.EimsMaintPlan;
import org.dromara.eims.mapper.EimsMaintPlanMapper;
import org.dromara.eims.service.IEimsMaintPlanService;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.*;
/**
@@ -37,6 +44,7 @@
    private final EimsMaintPlanMapper baseMapper;
    private final SysDeptMapper sysDeptMapper;
    private final EimsEquMapper equMapper;
    /**
     * æŸ¥è¯¢ä¿å…»è®¡åˆ’
@@ -194,4 +202,165 @@
    }
    public String importData(MultipartFile is, boolean updateSupport) throws IOException {
    int successNum = 0;
    int failureNum = 0;
    StringBuilder successMsg = new StringBuilder();
    StringBuilder failureMsg = new StringBuilder();
        // ä¿å…»é¡¹ç›®åˆ—表
        MaintCheckItemImportListener checkItemImportListener = new MaintCheckItemImportListener(updateSupport);
        EasyExcel.read(is.getInputStream(), MaintCheckItemVo.class, checkItemImportListener).headRowNumber(4).sheet().doRead();
        List<MaintCheckItemVo> successList = checkItemImportListener.getSuccessList();
        // è¯»å–固定资产编号
        EasyExcelCellListener readListener = new EasyExcelCellListener(3, 1);
        EasyExcel.read(is.getInputStream(), readListener).headRowNumber(0).sheet().doReadSync();
        String assetNo = Optional.ofNullable(readListener.getCellValue())
            .map(value -> {
                int colonIndex = Math.max(value.indexOf(":"), value.indexOf(":")); // åˆå¹¶å†’号处理
                return colonIndex != -1 ? value.substring(colonIndex + 1) : value;
            })
            .map(String::trim)
            .orElseThrow(() -> new ServiceException("导入失败,无法读取固定资产编号"));
        // è¯»å–保养计划年份
        EasyExcelCellListener readYearListener = new EasyExcelCellListener(2, 3);
        EasyExcel.read(is.getInputStream(), readYearListener).headRowNumber(0).sheet().doReadSync();
        String yearStr = readYearListener.getCellValue();
        String year = yearStr.replaceAll("[^\\d]", ""); // åŽ»é™¤éžæ•°å­—å­—ç¬¦
        year = (year.length() == 4) ? year : DateUtils.getDate().substring(0,4);
        QueryWrapper<EimsEqu> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("asset_no", assetNo);
        EimsEquVo eimsEquVo = equMapper.selectVoOne(queryWrapper);
        if (eimsEquVo == null) throw new ServiceException("导入失败,设备未找到请在设备台帐中添加");
        // æœˆä»½å­—段处理优化
        String[] monthFields = {"january","february","march","april","may","june",
                              "july","august","september","october","november","december"};
        for (MaintCheckItemVo itemVo : successList) {
            if ("执行人签名".equals(itemVo.getItemName())) break;
            EimsMaintPlanBo maintPlanBo = new EimsMaintPlanBo();
            maintPlanBo.setEquId(eimsEquVo.getEquId());
            maintPlanBo.setMaintName(itemVo.getItemName());
            maintPlanBo.setStatus("0");
            maintPlanBo.setMaintType("1");
            maintPlanBo.setMaintRule("0");
            // æ·»åŠ period校验
            String period = itemVo.getPeriod();
            if (StringUtils.isBlank(period)) {
                failureNum++;
                failureMsg.append(failureNum).append("、周期字段为空<br>");
                continue;
            }
            try {
                if (period.length() > 1) {
                    maintPlanBo.setMaintCycle(Long.parseLong(period.substring(0, period.length() - 1)));
                    String substring = period.substring(period.length() - 1);
                    // è½¬æ¢å‘¨æœŸå•位,M转换为3,D转换为1,Y转换为5,W转换为2,Q转换为4
                    switch (substring) {
                        case "M":
                            substring = "3";
                            break;
                        case "D":
                            substring = "1";
                            break;
                        case "Y":
                            substring = "5";
                            break;
                        case "W":
                            substring = "2";
                            break;
                        case "Q":
                            substring = "4";
                            break;
                        default:
                    }
                    maintPlanBo.setMaintCycleUnit(substring);
                } else {
                    maintPlanBo.setMaintCycle(Long.parseLong(period));
                    maintPlanBo.setMaintCycleUnit("");
                }
            } catch (NumberFormatException e) {
                failureNum++;
                failureMsg.append(failureNum).append("、无效的周期格式:").append(period).append("<br>");
                continue;
            }
            // æœˆä»½åˆ¤æ–­ä¼˜åŒ–
            for (int i = 0; i < monthFields.length; i++) {
                try {
                    String monthValue = (String) MaintCheckItemVo.class
                        .getMethod("get" + StringUtils.capitalize(monthFields[i]))
                        .invoke(itemVo);
                    if (StringUtils.isNotBlank(monthValue)) {
                        String month = String.format("%02d", i+1); // ä¿è¯ä¸¤ä½æœˆä»½
                        maintPlanBo.setMaintFirstTime(DateUtils.parseDate(year + "-" + month + "-01"));
                        break;
                    }
                } catch (Exception e) {
                    // åå°„异常处理
                    failureNum++;
                    failureMsg.append(failureNum).append("、月份字段访问异常<br>");
                    continue;
                }
            }
            if (maintPlanBo.getMaintFirstTime() != null) {
                Date firstTime = maintPlanBo.getMaintFirstTime();
                Date nextTime = calcNextTime(firstTime, maintPlanBo.getMaintCycle().intValue(), 1);
                maintPlanBo.setMaintNextTime(nextTime);
            }
            if (!insertByBo(maintPlanBo)) {
                failureNum++;
                failureMsg.append(failureNum).append("、设备:").append(eimsEquVo.getEquName()).append(",导入失败<br>");
            } else {
                successNum++;
                successMsg.append("<br/>").append(successNum).append("、设备:").append(eimsEquVo.getEquName()).append(",导入成功");
            }
        }
    if (failureNum > 0) {
        failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " æ¡æ•°æ®æ ¼å¼ä¸æ­£ç¡®ï¼Œé”™è¯¯å¦‚下:");
        return failureMsg.toString();
    } else {
        successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " æ¡ï¼Œæ•°æ®å¦‚下:");
        return successMsg.toString();
    }
}
private Date calcNextTime(Date firstTime, int intervalMonths, int initialOffset) {
    if (intervalMonths <= 0) {
        throw new IllegalArgumentException("Interval months must be positive");
    }
    if (firstTime == null) {
        throw new IllegalArgumentException("First time cannot be null");
    }
    Date current = new Date();
    int adjustmentCount = initialOffset;
    Date workingDate = (Date) firstTime.clone();
    while (workingDate.before(current)) {
        adjustmentCount++;
        workingDate = DateUtils.addMonths(workingDate, intervalMonths);
    }
    return (Date) workingDate.clone();
}
}