车间能级提升-智能设备管理系统
2b3715f1610b4176d7abe33e34542389cef61853..d40d81aa421c7cdb959556fedffef71fc62cde80
2025-04-16 zhuguifei
完成备件模块
d40d81 对比 | 目录
2025-04-16 zhuguifei
完成备件模块
609b91 对比 | 目录
已修改27个文件
已添加13个文件
1676 ■■■■■ 文件已修改
eims-ui/apps/web-antd/.env.production 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/spare-inout/model.d.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/index.ts 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/model.d.ts 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/api/eims/spare/index.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/components/basis-sub-table.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/components/spare-modal.vue 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-in/data.tsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-in/index.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-in/spare-in-drawer.vue 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-inoutdt/data.tsx 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-out/data.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-out/index.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-out/select-spare-table.vue 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare-out/spare-out-drawer.vue 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare/data.tsx 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/spare/index.vue 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-admin/src/main/resources/application-prod.yml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareInoutController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareInoutdtController.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsSpareInoutdt.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareBo.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareInoutBo.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareInoutdtBo.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareInoutVo.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareInoutdtVo.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareVo.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareInoutMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareInoutdtMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareMapper.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareInoutService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareInoutdtService.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareInoutServiceImpl.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareInoutdtServiceImpl.java 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsSpareInoutdtMapper.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsSpareMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/.env.production
@@ -19,7 +19,7 @@
VITE_GLOB_API_URL=/prod-api
# å…¨å±€åР坆开关(即开启了加解密功能才会生效 ä¸æ˜¯å…¨éƒ¨æŽ¥å£åР坆 éœ€è¦å’ŒåŽç«¯å¯¹åº”)
VITE_GLOB_ENABLE_ENCRYPT=true
VITE_GLOB_ENABLE_ENCRYPT=false
# RSA公钥 è¯·æ±‚加密使用 æ³¨æ„è¿™ä¸¤ä¸ªæ˜¯ä¸¤å¯¹RSA公私钥 è¯·æ±‚加密-后端解密是一对 å“åº”解密-后端加密是一对
VITE_GLOB_RSA_PUBLIC_KEY=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
# RSA私钥 å“åº”解密使用 æ³¨æ„è¿™ä¸¤ä¸ªæ˜¯ä¸¤å¯¹RSA公私钥 è¯·æ±‚加密-后端解密是一对 å“åº”解密-后端加密是一对
@@ -28,5 +28,5 @@
VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
# å¼€å¯SSE
VITE_GLOB_SSE_ENABLE=true
VITE_GLOB_SSE_ENABLE=false
eims-ui/apps/web-antd/src/api/eims/spare-inout/model.d.ts
@@ -20,6 +20,11 @@
  chargeUser: number;
  /**
   * ç»åŠžéƒ¨é—¨
   */
  chargeDept: number;
  /**
   * å·¥å•类型(1-入库单  2-出库单) å­—å…¸
   */
  type: string;
@@ -38,4 +43,8 @@
   * å¤‡æ³¨
   */
  remark: string;
  /**
   * å‡ºå…¥åº“选择的备件列表
   */
  spareList: any;
}
eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
import type { IDS, PageQuery, PageResult } from '#/api/common';
import type { SpareInoutdtVO } from '#/api/eims/spare-inoutdt/model';
import { commonExport } from '#/api/helper';
import { requestClient } from '#/api/request';
enum Api {
  root = '/eims/spareInoutdt',
  spareInoutdtExport = '/eims/spareInoutdt/export',
  spareInoutdtList = '/eims/spareInoutdt/list'
}
/**
 * æŸ¥è¯¢ã€å¤‡ä»¶å‡ºå…¥åº“明细】列表
 * @param query
 * @returns {*}
 */
export function listSpareInoutdt(params?: PageQuery) {
  return requestClient.get<PageResult<SpareInoutdtVO>>(Api.spareInoutdtList, { params });
}
/**
 * æŸ¥è¯¢ã€å¤‡ä»¶å‡ºå…¥åº“明细】详细
 * @param spareInoutdtId
 */
export function getSpareInoutdt(spareInoutdtId: any) {
  return requestClient.get<SpareInoutdtVO>(`${Api.root}/${spareInoutdtId}`);
}
/**
 * æ–°å¢žã€å¤‡ä»¶å‡ºå…¥åº“明细】
 * @param data
 */
export function addSpareInoutdt(data: any) {
  return requestClient.postWithMsg<void>(Api.root, data);
}
/**
 * ä¿®æ”¹ã€å¤‡ä»¶å‡ºå…¥åº“明细】
 * @param data
 */
export function updateSpareInoutdt(data: any) {
  return requestClient.putWithMsg<void>(Api.root, data);
}
/**
 * åˆ é™¤ã€å¤‡ä»¶å‡ºå…¥åº“明细】
 * @param spareInoutdtIds
 */
export function delSpareInoutdt(spareInoutdtIds: IDS) {
  return requestClient.deleteWithMsg<void>(`${Api.root}/${spareInoutdtIds}`);
}
/**
 * å¯¼å‡ºã€å¤‡ä»¶å‡ºå…¥åº“明细】
 * @param data
 */
export function spareInoutdtExport(data: any) {
  return commonExport(Api.spareInoutdtExport, data);
}
eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/model.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
export interface SpareInoutdtVO {
  /**
   *
   */
  id: number | string;
  /**
   * å‡ºåº“单或入库单id
   */
  inoutId: number | string;
  /**
   * å¤‡ä»¶id
   */
  spareId: number | string;
  /**
   * ä¹‹å‰åº“å­˜
   */
  beforeStock: number;
  /**
   * å®žé™…库存
   */
  actualStock: number;
  /**
   * æ•°é‡
   */
  quantity: number;
  /**
   * å•ä»·
   */
  unitPrice: number;
  /**
   * é‡‘额
   */
  amount: number;
  /**
   * å¤‡æ³¨
   */
  remark: string;
}
eims-ui/apps/web-antd/src/api/eims/spare/index.ts
@@ -5,6 +5,7 @@
import { requestClient } from '#/api/request';
enum Api {
  inoutList = '/eims/spare/listInout',
  root = '/eims/spare',
  spareExport = '/eims/spare/export',
  spareList = '/eims/spare/list'
@@ -20,6 +21,10 @@
  return requestClient.get<PageResult<SpareVO>>(Api.spareList, { params });
}
export function listInout(params?: PageQuery) {
  return requestClient.get<PageResult<any>>(Api.inoutList, { params });
}
/**
 * æŸ¥è¯¢ã€å¤‡ä»¶å°è´¦ã€‘详细
 * @param spareId
eims-ui/apps/web-antd/src/views/eims/components/basis-sub-table.vue
@@ -16,6 +16,7 @@
const columns = props?.columns?.filter((i) => i.field !== 'action');
const gridOptions: VxeGridProps = {
  size: 'mini',
  checkboxConfig: {
    // é«˜äº®
    highlight: true,
eims-ui/apps/web-antd/src/views/eims/components/spare-modal.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
<script setup lang="ts">
import type { VxeGridProps } from '#/adapter/vxe-table';
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { DictEnum } from '@vben/constants';
import { message } from 'ant-design-vue';
import { renderDict } from '#/utils/render';
import InnerView from '#/views/eims/spare/index.vue';
const emit = defineEmits<{ updateSelect: [any] }>();
const [BasicModal, modalApi] = useVbenModal({
  fullscreenButton: false,
  draggable: true,
  onCancel: handleCancel,
  onConfirm: handleConfirm
});
const innerView = ref();
async function handleConfirm() {
  try {
    modalApi.modalLoading(true);
    const tableSelect = innerView.value.tableSelect();
    const eList = tableSelect.filter((item: any) => !item.actualStock && item.actualStock <= 0);
    // æ£€æµ‹é€‰æ‹©çš„备件库存是否正常
    if (eList.length > 0) {
      message.error('存在库存不足备件,请重新选择');
      return false;
    }
    emit('updateSelect', tableSelect);
    await handleCancel();
  } catch (error) {
    console.error(error);
  } finally {
    modalApi.modalLoading(false);
  }
}
async function handleCancel() {
  modalApi.close();
}
</script>
<template>
  <BasicModal :fullscreen-button="true" class="w-[800px]">
    <InnerView ref="innerView" />
  </BasicModal>
</template>
<style scoped></style>
eims-ui/apps/web-antd/src/views/eims/spare-in/data.tsx
@@ -118,6 +118,17 @@
    label: '供应商'
  },
  {
    component: 'Input',
    fieldName: 'openSpare',
    label: '选择备件',
    formItemClass: 'col-span-1 w-[80px]'
  },
  {
    component: 'Input',
    fieldName: 'outSpareList',
    label: ''
  },
  {
    component: 'TreeSelect',
    // åœ¨drawer里更新 è¿™é‡Œä¸éœ€è¦é»˜è®¤çš„componentProps
    defaultValue: undefined,
eims-ui/apps/web-antd/src/views/eims/spare-in/index.vue
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import { onMounted } from 'vue';
import { onMounted, ref } from 'vue';
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
import { $t } from '@vben/locales';
@@ -16,6 +16,9 @@
import { columns, querySchema } from './data';
import drawer from './spare-in-drawer.vue';
import { columns as inoutCol } from '#/views/eims/spare-inoutdt/data';
import { listSpareInoutdt } from '#/api/eims/spare-inoutdt';
import BasisSubTable from '#/views/eims/components/basis-sub-table.vue';
const formOptions: VbenFormProps = {
  commonConfig: {
@@ -69,12 +72,16 @@
  },
  id: 'spre-inout-index'
};
const inoutId = ref<string>();
const [BasicTable, tableApi] = useVbenVxeGrid({
  formOptions,
  gridOptions,
  gridEvents: {
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams)
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams),
    cellClick: (e: any) => {
      const { row } = e;
      inoutId.value = row.id;
    }
  }
});
@@ -190,8 +197,8 @@
<template>
  <Page :auto-content-height="true">
    <div class="flex h-full gap-[8px]">
      <BasicTable class="flex-1 overflow-hidden" table-title="备件入库单列表">
    <div class="flex h-full gap-[8px] flex-col">
      <BasicTable class="h-2/3" table-title="备件入库单列表">
        <template #toolbar-tools>
          <Space>
            <a-button v-access:code="['eims:spareInout:export']" @click="handleDownloadExcel">
@@ -231,6 +238,14 @@
          </Space>
        </template>
      </BasicTable>
      <BasisSubTable
        :columns="inoutCol"
        :list-api="listSpareInoutdt"
        :req-value="inoutId"
        class="h-1/3"
        req-key="inoutId"
        title="入库明细"
      />
    </div>
    <Drawer @reload="tableApi.query()" />
  </Page>
eims-ui/apps/web-antd/src/views/eims/spare-in/spare-in-drawer.vue
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
import { useVbenDrawer, useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { addFullName, cloneDeep, getPopupContainer } from '@vben/utils';
@@ -11,7 +11,72 @@
import { drawerSchema } from './data';
import CodeInput from '#/views/eims/components/code-input.vue';
import spareModal from '#/views/eims/components/spare-modal.vue';
import SelectSpareTable from '#/views/eims/spare-out/select-spare-table.vue';
import { message } from 'ant-design-vue';
import type { VxeGridProps } from '#/adapter/vxe-table';
import { renderDict } from '#/utils/render';
import { DictEnum } from '@vben/constants';
/**
 * å‡ºåº“单选择的备件数据
 */
const outSpareList = ref([]);
const selectSpareTable = ref();
const outCol: VxeGridProps['columns'] = [
  {
    field: 'action',
    slots: { default: 'action' },
    title: '删除',
    width: 60
  },
  {
    title: '备件名称',
    field: 'name',
    width: 180
  },
  {
    title: '备件编码',
    field: 'code',
    width: 120
  },
  {
    title: '备件型号',
    field: 'modelNo',
    width: 100
  },
  {
    title: '计量单位',
    field: 'unit',
    slots: {
      default: ({ row }) => {
        if (!row.unit || row.unit === '') {
          return '';
        }
        return renderDict(row.unit, DictEnum.EIMS_SPARE_UNIT);
      }
    },
    width: 80
  },
  {
    title: '实际库存',
    field: 'actualStock',
    width: 100
  },
  {
    title: '数量',
    field: 'quantity',
    editRender: {
      name: 'input'
    },
    width: 80
  },
  {
    title: '参考价',
    field: 'referPrice',
    width: 90
  }
];
const emit = defineEmits<{ reload: [] }>();
const isUpdate = ref(false);
@@ -39,6 +104,7 @@
    if (!isOpen) {
      return null;
    }
    outSpareList.value = [];
    drawerApi.drawerLoading(true);
    const { id } = drawerApi.getData() as { id?: number | string };
    isUpdate.value = !!id;
@@ -48,6 +114,10 @@
    if (isUpdate.value && id) {
      const record = await getSpareInout(id);
      await formApi.setValues(record);
      outSpareList.value = record?.spareList;
      if (isUpdate.value && record.chargeDept) {
        await setupUserOptions(record.chargeDept);
      }
    }
    drawerApi.drawerLoading(false);
@@ -127,7 +197,15 @@
    if (!valid) {
      return;
    }
    const selectSpareList = selectSpareTable.value.tableData();
    // æ£€æµ‹æ˜¯å¦è¾“入出库数量
    const eList = selectSpareList.filter((item: any) => !item.quantity || item.quantity <= 0);
    if (selectSpareList.length<= 0 || eList.length > 0) {
      message.error('入库数量为空,请检查!');
      return false;
    }
    const data = cloneDeep(await formApi.getValues());
    data.spareList = selectSpareList;
    await (isUpdate.value ? updateSpareInout(data) : addSpareInout(data));
    emit('reload');
    await handleCancel();
@@ -142,14 +220,42 @@
  drawerApi.close();
  await formApi.resetForm();
}
// å¤‡ä»¶modal
const [SpareModal, spareModalApi] = useVbenModal({
  connectedComponent: spareModal,
  draggable: true,
  title: '选择备件'
});
function handleSpareModal() {
  spareModalApi.setData({});
  spareModalApi.open();
}
/**
 * é€‰æ‹©çš„备件
 * @param spareList
 */
function selectSpare(spareList: any) {
  outSpareList.value = spareList;
}
</script>
<template>
  <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
  <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[1000px]">
    <BasicForm>
      <template #orderCode="slotProps">
        <CodeInput v-bind="slotProps" :disabled="isUpdate" prefix="RK" />
      </template>
      <template #openSpare="slotProps">
        <a-button type="primary" v-bind="slotProps" :disabled="isUpdate" @click.stop="handleSpareModal">添加备件</a-button>
      </template>
      <template #outSpareList>
        <SelectSpareTable ref="selectSpareTable" :columns="outCol" :data="outSpareList" :is-update="isUpdate" />
      </template>
    </BasicForm>
    <SpareModal class="w-[1200px]" @update-select="selectSpare" />
  </BasicDrawer>
</template>
eims-ui/apps/web-antd/src/views/eims/spare-inoutdt/data.tsx
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
import type { VxeGridProps } from '#/adapter/vxe-table';
import { type FormSchemaGetter } from '#/adapter/form';
import { renderDict } from '#/utils/render';
import { DictEnum } from '@vben/constants';
export const querySchema: FormSchemaGetter = () => [];
export const columns: VxeGridProps['columns'] = [
  { type: 'checkbox', width: 60, fixed: 'left' },
  {
    title: '备件名称',
    field: 'spareName',
    minWidth: 120
  },
  {
    title: '备件编号',
    field: 'spareCode',
    minWidth: 120
  },
  {
    title: '规格型号',
    field: 'modelNo',
    minWidth: 100
  },
  {
    title: '计量单位',
    field: 'unit',
    sortable: true,
    slots: {
      default: ({ row }) => {
        if (!row.unit || row.unit === '') {
          return '';
        }
        return renderDict(row.unit, DictEnum.EIMS_SPARE_UNIT);
      }
    },
    width: 100
  },
  {
    title: '之前库存',
    field: 'beforeStock',
    minWidth: 100
  },
  {
    title: '当前库存',
    field: 'actualStock',
    minWidth: 100
  },
  {
    title: '数量',
    field: 'quantity',
    minWidth: 80
  },
  {
    title: '单价',
    field: 'unitPrice',
    minWidth: 80
  },
  {
    title: '金额',
    field: 'amount',
    minWidth: 80
  }
];
export const drawerSchema: FormSchemaGetter = () => [];
eims-ui/apps/web-antd/src/views/eims/spare-out/data.tsx
@@ -84,6 +84,8 @@
  }
];
export const drawerSchema: FormSchemaGetter = () => [
  {
    component: 'Input',
@@ -118,6 +120,17 @@
    label: '客户'
  },
  {
    component: 'Input',
    fieldName: 'openSpare',
    label: '选择备件',
    formItemClass: 'col-span-1 w-[80px]'
  },
  {
    component: 'Input',
    fieldName: 'outSpareList',
    label: ''
  },
  {
    component: 'TreeSelect',
    // åœ¨drawer里更新 è¿™é‡Œä¸éœ€è¦é»˜è®¤çš„componentProps
    defaultValue: undefined,
eims-ui/apps/web-antd/src/views/eims/spare-out/index.vue
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import { onMounted } from 'vue';
import { onMounted, ref } from 'vue';
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
import { $t } from '@vben/locales';
@@ -16,6 +16,9 @@
import { columns, querySchema } from './data';
import drawer from './spare-out-drawer.vue';
import { columns as inoutCol } from '#/views/eims/spare-inoutdt/data';
import { listSpareInoutdt } from '#/api/eims/spare-inoutdt';
import BasisSubTable from '#/views/eims/components/basis-sub-table.vue';
const formOptions: VbenFormProps = {
  commonConfig: {
@@ -69,12 +72,16 @@
  },
  id: 'spre-inout-index'
};
const inoutId = ref<string>();
const [BasicTable, tableApi] = useVbenVxeGrid({
  formOptions,
  gridOptions,
  gridEvents: {
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams)
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams),
    cellClick: (e: any) => {
      const { row } = e;
      inoutId.value = row.id;
    }
  }
});
@@ -190,8 +197,8 @@
<template>
  <Page :auto-content-height="true">
    <div class="flex h-full gap-[8px]">
      <BasicTable class="flex-1 overflow-hidden" table-title="备件出库单列表">
    <div class="flex h-full gap-[8px] flex-col">
      <BasicTable class="h-2/3" table-title="备件出库单列表">
        <template #toolbar-tools>
          <Space>
            <a-button v-access:code="['eims:spareInout:export']" @click="handleDownloadExcel">
@@ -231,6 +238,14 @@
          </Space>
        </template>
      </BasicTable>
      <BasisSubTable
        :columns="inoutCol"
        :list-api="listSpareInoutdt"
        :req-value="inoutId"
        class="h-1/3"
        req-key="inoutId"
        title="出库明细"
      />
    </div>
    <Drawer @reload="tableApi.query()" />
  </Page>
eims-ui/apps/web-antd/src/views/eims/spare-out/select-spare-table.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,121 @@
<script setup lang="ts">
import type { Recordable } from '@vben/types';
import { reactive, ref, watch } from 'vue';
import { $t } from '@vben/locales';
import { getVxePopupContainer } from '@vben/utils';
import { Popconfirm, Space } from 'ant-design-vue';
import { useVbenVxeGrid, type VxeGridProps, vxeSortEvent } from '#/adapter/vxe-table';
interface Props {
  title?: string;
  columns?: VxeGridProps['columns'];
  data: any;
  isUpdate?: boolean;
}
const props = defineProps<Props>();
const responsiveData = reactive(props.data);
defineExpose({
  tableData
});
watch(
  () => props.data,
  (data) => {
    responsiveData.splice(0, responsiveData.length, ...data);
  }
);
const gridOptions: VxeGridProps = {
  checkboxConfig: {
    // é«˜äº®
    highlight: true,
    // ç¿»é¡µæ—¶ä¿ç•™é€‰ä¸­çŠ¶æ€
    reserve: true
    // ç‚¹å‡»è¡Œé€‰ä¸­
    // trigger: 'row'
  },
  columns: props.columns,
  height: 'auto',
  keepSource: true,
  data: responsiveData,
  pagerConfig: {
    enabled: false
  },
  toolbarConfig: {
    enabled: false
  },
  rowConfig: {
    isHover: true,
    keyField: 'id'
  },
  sortConfig: {
    // è¿œç¨‹æŽ’序
    remote: true,
    // æ”¯æŒå¤šå­—段排序 é»˜è®¤å…³é—­
    multiple: true
  },
  editConfig: {
    mode: 'cell',
    trigger: 'click'
  },
  id: 'local-table'
};
const [BasicTable, tableApi] = useVbenVxeGrid({
  gridOptions,
  gridEvents: {
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams)
  }
});
function handleDelete(row: Recordable<any>) {
  const index = responsiveData.findIndex((item: any) => item.id === row.id);
  if (index !== -1) {
    responsiveData.splice(index, 1);
  }
}
// é€‰ä¸­æ•°æ®
function tableData() {
  return tableApi.grid.getData();
}
/**
 * TODO åŽç»­æ‰©å±•点击事件
 */
const slotName = ref<string>('equName');
</script>
<template>
  <div class="w-full h-min">
    <BasicTable :table-title="title" size="small">
      <template #[slotName]="{ row }">
        <Space>
          <span>{{ row[slotName] }}</span>
        </Space>
      </template>
      <template #action="{ row }">
        <Space>
          <Popconfirm :get-popup-container="getVxePopupContainer" placement="left" title="确认删除?" @confirm="handleDelete(row)">
            <ghost-button :disabled="isUpdate" danger @click.stop="">
              {{ $t('pages.common.delete') }}
            </ghost-button>
          </Popconfirm>
        </Space>
      </template>
    </BasicTable>
  </div>
</template>
<style lang="scss" scoped>
:deep(.p-2) {
  padding: 0;
}
</style>
eims-ui/apps/web-antd/src/views/eims/spare-out/spare-out-drawer.vue
@@ -1,18 +1,88 @@
<script setup lang="ts">
import type { VxeGridProps } from '#/adapter/vxe-table';
import { computed, ref } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
import { useVbenDrawer, useVbenModal } from '@vben/common-ui';
import { DictEnum } from '@vben/constants';
import { $t } from '@vben/locales';
import { addFullName, cloneDeep, getPopupContainer } from '@vben/utils';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { addSpareInout, getSpareInout, updateSpareInout } from '#/api/eims/spare-inout';
import { getDeptTree, userList } from '#/api/system/user';
import { renderDict } from '#/utils/render';
import CodeInput from '#/views/eims/components/code-input.vue';
import spareModal from '#/views/eims/components/spare-modal.vue';
import { drawerSchema } from './data';
import CodeInput from '#/views/eims/components/code-input.vue';
import SelectSpareTable from './select-spare-table.vue';
const emit = defineEmits<{ reload: [] }>();
/**
 * å‡ºåº“单选择的备件数据
 */
const outSpareList = ref([]);
const selectSpareTable = ref();
const outCol: VxeGridProps['columns'] = [
  {
    field: 'action',
    slots: { default: 'action' },
    title: '删除',
    width: 60
  },
  {
    title: '备件名称',
    field: 'name',
    width: 180
  },
  {
    title: '备件编码',
    field: 'code',
    width: 120
  },
  {
    title: '备件型号',
    field: 'modelNo',
    width: 100
  },
  {
    title: '计量单位',
    field: 'unit',
    slots: {
      default: ({ row }) => {
        if (!row.unit || row.unit === '') {
          return '';
        }
        return renderDict(row.unit, DictEnum.EIMS_SPARE_UNIT);
      }
    },
    width: 80
  },
  {
    title: '实际库存',
    field: 'actualStock',
    width: 100
  },
  {
    title: '数量',
    field: 'quantity',
    editRender: {
      name: 'input'
    },
    width: 80
  },
  {
    title: '参考价',
    field: 'referPrice',
    width: 90
  }
];
const isUpdate = ref(false);
const title = computed(() => {
@@ -42,12 +112,18 @@
    drawerApi.drawerLoading(true);
    const { id } = drawerApi.getData() as { id?: number | string };
    isUpdate.value = !!id;
    outSpareList.value = [];
    // åˆå§‹åŒ–
    await setupDeptSelect();
    // æ›´æ–° && èµ‹å€¼
    if (isUpdate.value && id) {
      const record = await getSpareInout(id);
      await formApi.setValues(record);
      // æ›´æ–°å‡ºåº“单的备件明细
      outSpareList.value = record?.spareList;
      if (isUpdate.value && record.chargeDept) {
        await setupUserOptions(record.chargeDept);
      }
    }
    drawerApi.drawerLoading(false);
@@ -104,7 +180,7 @@
          /** æ ¹æ®éƒ¨é—¨ID加载用户 */
          await setupUserOptions(deptId);
          /** å˜åŒ–后需要重新选择用户 */
          formModel.operatorId = undefined;
          formModel.chargeUser = undefined;
        },
        placeholder: '请选择',
        showSearch: true,
@@ -120,6 +196,7 @@
    }
  ]);
}
async function handleConfirm() {
  try {
    drawerApi.drawerLoading(true);
@@ -127,7 +204,15 @@
    if (!valid) {
      return;
    }
    const selectSpareList = selectSpareTable.value.tableData();
    // æ£€æµ‹æ˜¯å¦è¾“入出库数量
    const eList = selectSpareList.filter((item: any) => !item.quantity || item.quantity <= 0 || item.quantity > item.actualStock);
    if (selectSpareList.length<= 0 ||eList.length > 0) {
      message.error('出库数量为空或大于库存,请检查!');
      return false;
    }
    const data = cloneDeep(await formApi.getValues());
    data.spareList = selectSpareList;
    await (isUpdate.value ? updateSpareInout(data) : addSpareInout(data));
    emit('reload');
    await handleCancel();
@@ -142,14 +227,43 @@
  drawerApi.close();
  await formApi.resetForm();
}
// å¤‡ä»¶modal
const [SpareModal, spareModalApi] = useVbenModal({
  connectedComponent: spareModal,
  draggable: true,
  title: '选择备件'
});
function handleSpareModal() {
  spareModalApi.setData({});
  spareModalApi.open();
}
/**
 * é€‰æ‹©çš„备件
 * @param spareList
 */
function selectSpare(spareList: any) {
  outSpareList.value = spareList;
}
</script>
<template>
  <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]">
  <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[1000px]">
    <BasicForm>
      <template #orderCode="slotProps">
        <CodeInput v-bind="slotProps" :disabled="isUpdate" prefix="CK" />
      </template>
      <template #openSpare="slotProps">
        <a-button type="primary" v-bind="slotProps" :disabled="isUpdate" @click.stop="handleSpareModal">添加备件</a-button>
      </template>
      <template #outSpareList>
        <SelectSpareTable ref="selectSpareTable" :columns="outCol" :data="outSpareList" :is-update="isUpdate" />
      </template>
    </BasicForm>
    <SpareModal class="w-[1200px]" @update-select="selectSpare" />
  </BasicDrawer>
</template>
eims-ui/apps/web-antd/src/views/eims/spare/data.tsx
@@ -1,11 +1,13 @@
import type { VxeGridProps } from '#/adapter/vxe-table';
import { DictEnum } from '@vben/constants';
import { getPopupContainer } from '@vben/utils';
import { Tag } from 'ant-design-vue';
import { type FormSchemaGetter } from '#/adapter/form';
import { getDictOptions } from '#/utils/dict';
import { renderDict } from '#/utils/render';
import { getPopupContainer } from '@vben/utils';
export const querySchema: FormSchemaGetter = () => [
  {
@@ -84,7 +86,7 @@
    sortable: true,
    slots: {
      default: ({ row }) => {
        if (row.unit === null || row.unit === '') {
        if (!row.unit || row.unit === '') {
          return '';
        }
        return renderDict(row.unit, DictEnum.EIMS_SPARE_UNIT);
@@ -138,6 +140,87 @@
  }
];
export const inoutCol: VxeGridProps['columns'] = [
  {
    title: '出入库单号',
    field: 'orderCode',
    width: 180
  },
  {
    title: '日期',
    field: 'orderTime',
    width: 180
  },
  {
    title: '方向',
    field: 'type1',
    width: 80,
    slots: {
      default: ({ row }) => {
        const type = row.type;
        switch (type) {
          case '1': {
            return <Tag color="green"> å…¥åº“ </Tag>;
          }
          case '2': {
            return <Tag color="blue"> å‡ºåº“ </Tag>;
          }
          // No default
        }
        return '';
      }
    }
  },
  {
    title: '类型',
    field: 'type',
    width: 100,
    slots: {
      default: ({ row }) => {
        if (!row.type || row.type === '') {
          return '';
        }
        return renderDict(row.type, DictEnum.SPARE_INOUT_TYPE);
      }
    }
  },
  {
    title: '入库',
    field: 'inQuantity',
    width: 120,
    slots: {
      default: ({ row }) => {
        return row.type && row.type === '1' ? row.quantity : '';
      }
    }
  },
  {
    title: '出库',
    field: 'outQuantity',
    width: 120,
    slots: {
      default: ({ row }) => {
        return row.type && row.type === '2' ? row.quantity : '';
      }
    }
  },
  {
    title: '库存',
    field: 'actualStock',
    width: 80
  },
  {
    title: '单价',
    field: 'unitPrice',
    width: 80
  },
  {
    title: '金额',
    field: 'amount',
    width: 80
  }
];
export const drawerSchema: FormSchemaGetter = () => [
  {
    component: 'Input',
@@ -173,7 +256,7 @@
      show: () => false,
      triggerFields: ['imgUrl']
    },
    label: '备件预览',
    label: '备件预览'
  },
  {
    component: 'Input',
eims-ui/apps/web-antd/src/views/eims/spare/index.vue
@@ -10,11 +10,12 @@
import { Image, Modal, Popconfirm, Space } from 'ant-design-vue';
import { useVbenVxeGrid, vxeCheckboxChecked, type VxeGridProps, vxeSortEvent } from '#/adapter/vxe-table';
import { delSpare, listSpare, spareExport } from '#/api/eims/spare';
import { delSpare, listInout, listSpare, spareExport } from '#/api/eims/spare';
import { configInfoByKey } from '#/api/system/config';
import { commonDownloadExcel } from '#/utils/file/download';
import BasisSubTable from '#/views/eims/components/basis-sub-table.vue';
import { columns, querySchema } from './data';
import { columns, inoutCol, querySchema } from './data';
import spareDrawer from './spare-drawer.vue';
import SpareTypeTree from './spare-type-tree.vue';
@@ -59,7 +60,7 @@
  height: 'auto',
  keepSource: true,
  pagerConfig: {
    enabled: false,
    enabled: false
  },
  proxyConfig: {
    enabled: true,
@@ -84,7 +85,7 @@
    keyField: 'id'
  },
  columnConfig: {
    resizable: true,
    resizable: true
  },
  sortConfig: {
    // è¿œç¨‹æŽ’序
@@ -94,12 +95,16 @@
  },
  id: 'eims-spare-index'
};
const id = ref<string>();
const [BasicTable, tableApi] = useVbenVxeGrid({
  formOptions,
  gridOptions,
  gridEvents: {
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams)
    sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams),
    cellClick: (e: any) => {
      const { row } = e;
      id.value = row.id;
    }
  }
});
@@ -163,51 +168,56 @@
  <Page :auto-content-height="true">
    <div class="flex h-full gap-[8px]">
      <SpareTypeTree v-model:select-type-id="selectTypeId" class="w-[260px]" @reload="() => tableApi.reload()" @select="() => tableApi.reload()" />
      <BasicTable class="flex-1 overflow-hidden" table-title="备件台账">
        <template #toolbar-tools>
          <Space>
            <a-button v-access:code="['eims:spare:export']" @click="handleDownloadExcel">
              {{ $t('pages.common.export') }}
            </a-button>
            <a-button
              :disabled="!vxeCheckboxChecked(tableApi)"
              danger
              type="primary"
              v-access:code="['eims:spare:remove']"
              @click="handleMultiDelete"
            >
              {{ $t('pages.common.delete') }}
            </a-button>
            <a-button type="primary" v-access:code="['eims:spare:add']" @click="handleAdd">
              {{ $t('pages.common.add') }}
            </a-button>
          </Space>
        </template>
      <div class="flex-1 overflow-hidden">
        <div class="flex h-full gap-[8px] flex-col">
          <BasicTable class="h-2/3" table-title="备件台账">
            <template #toolbar-tools>
              <Space>
                <a-button v-access:code="['eims:spare:export']" @click="handleDownloadExcel">
                  {{ $t('pages.common.export') }}
                </a-button>
                <a-button
                  :disabled="!vxeCheckboxChecked(tableApi)"
                  danger
                  type="primary"
                  v-access:code="['eims:spare:remove']"
                  @click="handleMultiDelete"
                >
                  {{ $t('pages.common.delete') }}
                </a-button>
                <a-button type="primary" v-access:code="['eims:spare:add']" @click="handleAdd">
                  {{ $t('pages.common.add') }}
                </a-button>
              </Space>
            </template>
        <template #imgUrl="{ row }">
          <Image v-if="preview && isImageFile(row.imgUrl)" :src="row.imgUrl" height="38px" />
          <span v-else>{{ row.imgUrl }}</span>
        </template>
            <template #imgUrl="{ row }">
              <Image v-if="preview && isImageFile(row.imgUrl)" :src="row.imgUrl" height="38px" />
              <span v-else>{{ row.imgUrl }}</span>
            </template>
        <template #action="{ row }">
          <Space>
            <ghost-button v-access:code="['eims:spare:edit']" @click.stop="handleEdit(row)">
              {{ $t('pages.common.edit') }}
            </ghost-button>
            <Popconfirm :get-popup-container="getVxePopupContainer" placement="left" title="确认删除?" @confirm="handleDelete(row)">
              <ghost-button danger v-access:code="['eims:spare:remove']" @click.stop="">
                {{ $t('pages.common.delete') }}
              </ghost-button>
            </Popconfirm>
          </Space>
        </template>
      </BasicTable>
            <template #action="{ row }">
              <Space>
                <ghost-button v-access:code="['eims:spare:edit']" @click.stop="handleEdit(row)">
                  {{ $t('pages.common.edit') }}
                </ghost-button>
                <Popconfirm :get-popup-container="getVxePopupContainer" placement="left" title="确认删除?" @confirm="handleDelete(row)">
                  <ghost-button danger v-access:code="['eims:spare:remove']" @click.stop="">
                    {{ $t('pages.common.delete') }}
                  </ghost-button>
                </Popconfirm>
              </Space>
            </template>
          </BasicTable>
          <BasisSubTable :columns="inoutCol" :list-api="listInout" :req-value="id" class="h-1/3" req-key="id" title="出入库明细" />
        </div>
      </div>
    </div>
    <SpareDrawer @reload="tableApi.query()" />
  </Page>
</template>
<style>
<style lang="scss" scoped>
/* ç»Ÿä¸€æ‰€æœ‰åˆ—的边框和行高 */
.vxe-table--body .vxe-body--row .vxe-body--column {
  height: 56px !important;
eims/ruoyi-admin/src/main/resources/application-prod.yml
@@ -48,9 +48,9 @@
          driverClassName: com.mysql.cj.jdbc.Driver
          # jdbc æ‰€æœ‰å‚数配置参考 https://lionli.blog.csdn.net/article/details/122018562
          # rewriteBatchedStatements=true æ‰¹å¤„理优化 å¤§å¹…提升批量插入更新删除性能(对数据库有性能损耗 ä½¿ç”¨æ‰¹é‡æ“ä½œåº”考虑性能问题)
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
          url: jdbc:mysql://localhost:3306/eims?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
          username: root
          password: root
          password: 123456
        # ä»Žåº“数据源
        slave:
          lazy: true
@@ -103,7 +103,7 @@
    # æ•°æ®åº“索引
    database: 0
    # redis å¯†ç å¿…须配置
    password: ruoyi123
    #password: ruoyi123
    # è¿žæŽ¥è¶…æ—¶æ—¶é—´
    timeout: 10s
    # æ˜¯å¦å¼€å¯ssl
eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java
@@ -161,4 +161,16 @@
    }
    /**
     *备件出入库类型
     */
    String SPARE_INOUT_TYPE = "spare_inout_type";
    interface SPARE_INOUT_TYPE_DETAIL {
        String RK = "1";// é‡‡è´­å…¥åº“
        String CK = "2"; // é¢†ç”¨å‡ºåº“
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareController.java
@@ -6,6 +6,8 @@
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@@ -45,6 +47,12 @@
        return eimsSpareService.queryPageList(bo, pageQuery);
    }
    @SaCheckPermission("eims:spare:list")
    @GetMapping("/listInout")
    public TableDataInfo<EimsSpareInoutdtVo> listInout(EimsSpareBo bo, PageQuery pageQuery) {
        return eimsSpareService.querySpareInoutList(bo, pageQuery);
    }
    /**
     * å¯¼å‡ºå¤‡ä»¶å°è´¦åˆ—表
     */
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareInoutController.java
@@ -45,6 +45,7 @@
        return eimsSpareInoutService.queryPageList(bo, pageQuery);
    }
    /**
     * å¯¼å‡ºå¤‡ä»¶å‡ºå…¥åº“列表
     */
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/controller/EimsSpareInoutdtController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
package org.dromara.eims.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.bo.EimsSpareInoutdtBo;
import org.dromara.eims.service.IEimsSpareInoutdtService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/eims/spareInoutdt")
public class EimsSpareInoutdtController extends BaseController {
    private final IEimsSpareInoutdtService eimsSpareInoutdtService;
    /**
     * æŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“明细列表
     */
    @SaCheckPermission("eims:spareInoutdt:list")
    @GetMapping("/list")
    public TableDataInfo<EimsSpareInoutdtVo> list(EimsSpareInoutdtBo bo, PageQuery pageQuery) {
        return eimsSpareInoutdtService.queryPageList(bo, pageQuery);
    }
    /**
     * å¯¼å‡ºå¤‡ä»¶å‡ºå…¥åº“明细列表
     */
    @SaCheckPermission("eims:spareInoutdt:export")
    @Log(title = "备件出入库明细", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(EimsSpareInoutdtBo bo, HttpServletResponse response) {
        List<EimsSpareInoutdtVo> list = eimsSpareInoutdtService.queryList(bo);
        ExcelUtil.exportExcel(list, "备件出入库明细", EimsSpareInoutdtVo.class, response);
    }
    /**
     * èŽ·å–å¤‡ä»¶å‡ºå…¥åº“æ˜Žç»†è¯¦ç»†ä¿¡æ¯
     *
     * @param id ä¸»é”®
     */
    @SaCheckPermission("eims:spareInoutdt:query")
    @GetMapping("/{id}")
    public R<EimsSpareInoutdtVo> getInfo(@NotNull(message = "主键不能为空")
                                     @PathVariable Long id) {
        return R.ok(eimsSpareInoutdtService.queryById(id));
    }
    /**
     * æ–°å¢žå¤‡ä»¶å‡ºå…¥åº“明细
     */
    @SaCheckPermission("eims:spareInoutdt:add")
    @Log(title = "备件出入库明细", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody EimsSpareInoutdtBo bo) {
        return toAjax(eimsSpareInoutdtService.insertByBo(bo));
    }
    /**
     * ä¿®æ”¹å¤‡ä»¶å‡ºå…¥åº“明细
     */
    @SaCheckPermission("eims:spareInoutdt:edit")
    @Log(title = "备件出入库明细", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody EimsSpareInoutdtBo bo) {
        return toAjax(eimsSpareInoutdtService.updateByBo(bo));
    }
    /**
     * åˆ é™¤å¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param ids ä¸»é”®ä¸²
     */
    @SaCheckPermission("eims:spareInoutdt:remove")
    @Log(title = "备件出入库明细", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ids) {
        return toAjax(eimsSpareInoutdtService.deleteWithValidByIds(List.of(ids), true));
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/EimsSpareInoutdt.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package org.dromara.eims.domain;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.math.BigDecimal;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细对象 eims_spare_inoutdt
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("eims_spare_inoutdt")
public class EimsSpareInoutdt extends BaseEntity {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     *
     */
    @TableId(value = "id")
    private Long id;
    /**
     * å‡ºåº“单或入库单id
     */
    private Long inoutId;
    /**
     * å¤‡ä»¶id
     */
    private Long spareId;
    /**
     * ä¹‹å‰åº“å­˜
     */
    private Long beforeStock;
    /**
     * å®žé™…库存
     */
    private Long actualStock;
    /**
     * æ•°é‡
     */
    private Long quantity;
    /**
     * å•ä»·
     */
    private BigDecimal unitPrice;
    /**
     * é‡‘额
     */
    private BigDecimal amount;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareBo.java
@@ -114,4 +114,11 @@
    private String remark;
    /**
     * å‡ºåº“入库数量
     */
    private Long quantity;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareInoutBo.java
@@ -9,7 +9,10 @@
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.eims.domain.vo.EimsSpareVo;
/**
 * å¤‡ä»¶å‡ºå…¥åº“业务对象 eims_spare_inout
@@ -69,5 +72,9 @@
     */
    private String remark;
    //出入库选择的备件明细
    private List<EimsSpareBo> spareList;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/bo/EimsSpareInoutdtBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,77 @@
package org.dromara.eims.domain.bo;
import org.dromara.eims.domain.EimsSpareInoutdt;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细业务对象 eims_spare_inoutdt
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = EimsSpareInoutdt.class, reverseConvertGenerate = false)
public class EimsSpareInoutdtBo extends BaseEntity {
    /**
     *
     */
    @NotNull(message = "不能为空", groups = { EditGroup.class })
    private Long id;
    /**
     * å‡ºåº“单或入库单id
     */
    @NotNull(message = "出库单或入库单id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long inoutId;
    /**
     * å¤‡ä»¶id
     */
    @NotNull(message = "备件id不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long spareId;
    /**
     * ä¹‹å‰åº“å­˜
     */
    @NotNull(message = "之前库存不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long beforeStock;
    /**
     * å®žé™…库存
     */
    @NotNull(message = "实际库存不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long actualStock;
    /**
     * æ•°é‡
     */
    @NotNull(message = "数量不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long quantity;
    /**
     * å•ä»·
     */
    @NotNull(message = "单价不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long unitPrice;
    /**
     * é‡‘额
     */
    @NotNull(message = "金额不能为空", groups = { AddGroup.class, EditGroup.class })
    private Long amount;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareInoutVo.java
@@ -11,11 +11,12 @@
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.eims.domain.bo.EimsSpareBo;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
@@ -87,5 +88,6 @@
    @ExcelProperty(value = "备注")
    private String remark;
    //出入库选择的备件明细
    private List<EimsSpareVo> spareList;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareInoutdtVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package org.dromara.eims.domain.vo;
import org.dromara.eims.domain.EimsSpareInoutdt;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细视图对象 eims_spare_inoutdt
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = EimsSpareInoutdt.class)
public class EimsSpareInoutdtVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     *
     */
    @ExcelProperty(value = "")
    private Long id;
    /**
     * å‡ºåº“单或入库单id
     */
    @ExcelProperty(value = "出库单或入库单id")
    private Long inoutId;
    /**
     * å¤‡ä»¶id
     */
    @ExcelProperty(value = "备件id")
    private Long spareId;
    /**
     * ä¹‹å‰åº“å­˜
     */
    @ExcelProperty(value = "之前库存")
    private Long beforeStock;
    /**
     * å®žé™…库存
     */
    @ExcelProperty(value = "实际库存")
    private Long actualStock;
    /**
     * æ•°é‡
     */
    @ExcelProperty(value = "数量")
    private Long quantity;
    /**
     * å•ä»·
     */
    @ExcelProperty(value = "单价")
    private BigDecimal unitPrice;
    /**
     * é‡‘额
     */
    @ExcelProperty(value = "金额")
    private BigDecimal amount;
    /**
     * å¤‡æ³¨
     */
    @ExcelProperty(value = "备注")
    private String remark;
    //备件
    private String spareName;
    private String spareCode;
    private String modelNo;
    //出入库单
    private String orderCode;
    private Date orderTime;
    private String type;
    private String unit;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/domain/vo/EimsSpareVo.java
@@ -142,6 +142,9 @@
     */
    @ExcelProperty(value = "备注")
    private String remark;
    /**
     * å‡ºåº“入库数量
     */
    private Long quantity;
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareInoutMapper.java
@@ -1,8 +1,15 @@
package org.dromara.eims.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.eims.domain.EimsSpareInout;
import org.dromara.eims.domain.EimsSpareInoutdt;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
/**
 * å¤‡ä»¶å‡ºå…¥åº“Mapper接口
@@ -12,4 +19,5 @@
 */
public interface EimsSpareInoutMapper extends BaseMapperPlus<EimsSpareInout, EimsSpareInoutVo> {
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareInoutdtMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package org.dromara.eims.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.eims.domain.EimsMaintSt;
import org.dromara.eims.domain.EimsSpareInoutdt;
import org.dromara.eims.domain.vo.EimsMaintStVo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细Mapper接口
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
public interface EimsSpareInoutdtMapper extends BaseMapperPlus<EimsSpareInoutdt, EimsSpareInoutdtVo> {
    Page<EimsSpareInoutdtVo> selectSpareInoutdtList(@Param("page") Page<EimsSpareInoutdtVo> page, @Param(Constants.WRAPPER) Wrapper<EimsSpareInoutdt> queryWrapper);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/mapper/EimsSpareMapper.java
@@ -1,6 +1,13 @@
package org.dromara.eims.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.eims.domain.EimsSpare;
import org.dromara.eims.domain.EimsSpareInout;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.vo.EimsSpareVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
@@ -11,5 +18,5 @@
 * @date 2025-03-20
 */
public interface EimsSpareMapper extends BaseMapperPlus<EimsSpare, EimsSpareVo> {
    Page<EimsSpareInoutdtVo> selectSpareInoutList(@Param("page") Page<EimsSpareInoutdtVo> page, @Param(Constants.WRAPPER) Wrapper<EimsSpare> queryWrapper);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareInoutService.java
@@ -65,4 +65,8 @@
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareInoutdtService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package org.dromara.eims.service;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.bo.EimsSpareInoutdtBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细Service接口
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
public interface IEimsSpareInoutdtService {
    /**
     * æŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param id ä¸»é”®
     * @return å¤‡ä»¶å‡ºå…¥åº“明细
     */
    EimsSpareInoutdtVo queryById(Long id);
    /**
     * åˆ†é¡µæŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“明细列表
     *
     * @param bo        æŸ¥è¯¢æ¡ä»¶
     * @param pageQuery åˆ†é¡µå‚æ•°
     * @return å¤‡ä»¶å‡ºå…¥åº“明细分页列表
     */
    TableDataInfo<EimsSpareInoutdtVo> queryPageList(EimsSpareInoutdtBo bo, PageQuery pageQuery);
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„备件出入库明细列表
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return å¤‡ä»¶å‡ºå…¥åº“明细列表
     */
    List<EimsSpareInoutdtVo> queryList(EimsSpareInoutdtBo bo);
    /**
     * æ–°å¢žå¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param bo å¤‡ä»¶å‡ºå…¥åº“明细
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    Boolean insertByBo(EimsSpareInoutdtBo bo);
    /**
     * ä¿®æ”¹å¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param bo å¤‡ä»¶å‡ºå…¥åº“明细
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    Boolean updateByBo(EimsSpareInoutdtBo bo);
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤å¤‡ä»¶å‡ºå…¥åº“明细信息
     *
     * @param ids     å¾…删除的主键集合
     * @param isValid æ˜¯å¦è¿›è¡Œæœ‰æ•ˆæ€§æ ¡éªŒ
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/IEimsSpareService.java
@@ -1,5 +1,8 @@
package org.dromara.eims.service;
import org.dromara.eims.domain.bo.EimsSpareInoutBo;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.vo.EimsSpareVo;
import org.dromara.eims.domain.bo.EimsSpareBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -65,4 +68,13 @@
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
    /**
     * æ ¹æ®å¤‡ä»¶æŸ¥è¯¢å‡ºå…¥åº“明细
     * @param bo
     * @param pageQuery
     * @return
     */
    TableDataInfo<EimsSpareInoutdtVo> querySpareInoutList(EimsSpareBo bo, PageQuery pageQuery);
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareInoutServiceImpl.java
@@ -1,5 +1,7 @@
package org.dromara.eims.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.dromara.common.core.constant.DictConstants;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -8,16 +10,24 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.eims.domain.EimsSpare;
import org.dromara.eims.domain.EimsSpareInoutdt;
import org.dromara.eims.domain.bo.EimsSpareBo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.vo.EimsSpareVo;
import org.dromara.eims.mapper.EimsSpareInoutdtMapper;
import org.dromara.eims.mapper.EimsSpareMapper;
import org.springframework.stereotype.Service;
import org.dromara.eims.domain.bo.EimsSpareInoutBo;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.eims.domain.EimsSpareInout;
import org.dromara.eims.mapper.EimsSpareInoutMapper;
import org.dromara.eims.service.IEimsSpareInoutService;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
 * å¤‡ä»¶å‡ºå…¥åº“Service业务层处理
@@ -30,6 +40,8 @@
public class EimsSpareInoutServiceImpl implements IEimsSpareInoutService {
    private final EimsSpareInoutMapper baseMapper;
    private final EimsSpareInoutdtMapper inoutdtMapper;
    private final EimsSpareMapper spareMapper;
    /**
     * æŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“
@@ -39,7 +51,33 @@
     */
    @Override
    public EimsSpareInoutVo queryById(Long id){
        return baseMapper.selectVoById(id);
        EimsSpareInoutVo eimsSpareInoutVo = baseMapper.selectVoById(id);
        QueryWrapper<EimsSpareInoutdt> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(EimsSpareInoutdt::getInoutId, id);
        List<EimsSpareInoutdtVo> dtVos = inoutdtMapper.selectVoList(queryWrapper);
        if(!dtVos.isEmpty()){
            Map<Long, Long> map = dtVos.stream()
                .collect(Collectors.toMap(
                    EimsSpareInoutdtVo::getSpareId,
                    EimsSpareInoutdtVo::getQuantity
                ));
            List<Long> spareIdList = dtVos.stream()
                .map(EimsSpareInoutdtVo::getSpareId) // èŽ·å– spareId å­—段
                .toList();
            List<EimsSpareVo> eimsSpareListVos = spareMapper.selectVoBatchIds(spareIdList);
            for (EimsSpareVo spareVo : eimsSpareListVos) {
                // å‡è®¾ quantity çš„值是一个固定值(例如 10)
                spareVo.setQuantity(map.get(spareVo.getId()));
            }
            eimsSpareInoutVo.setSpareList(eimsSpareListVos);
        }
        return eimsSpareInoutVo;
    }
    /**
@@ -55,6 +93,7 @@
        Page<EimsSpareInoutVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„备件出入库列表
@@ -81,19 +120,73 @@
        return lqw;
    }
    /**
     * æ–°å¢žå¤‡ä»¶å‡ºå…¥åº“
     *
     * @param bo å¤‡ä»¶å‡ºå…¥åº“
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean insertByBo(EimsSpareInoutBo bo) {
    public synchronized Boolean insertByBo(EimsSpareInoutBo bo) {
        EimsSpareInout add = MapstructUtils.convert(bo, EimsSpareInout.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        //入库+库存  å‡ºåº“-库存  ï¼ˆé»˜è®¤å…¥åº“)
        int  OperationType;
         //出库
         if(bo.getType().equals(DictConstants.SPARE_INOUT_TYPE_DETAIL.CK)){
            OperationType =  -1;
        } else {
             OperationType = 1;
         }
        List<EimsSpareBo> spareList = Optional.ofNullable(bo.getSpareList()).orElse(new ArrayList<>());
        // æ’入出库明细
        List<EimsSpareInoutdt> dtList = spareList.stream()
            .map(eimsSpareBo -> {
                EimsSpareInoutdt dt = new EimsSpareInoutdt();
                dt.setInoutId(add.getId());
                dt.setSpareId(eimsSpareBo.getId());
                dt.setBeforeStock(eimsSpareBo.getActualStock());
                // æ³¨æ„å…¥åº“出库
                dt.setActualStock(eimsSpareBo.getActualStock() + (eimsSpareBo.getQuantity() * OperationType));
                dt.setQuantity(eimsSpareBo.getQuantity());
                // è®¾ç½®å•价和金额
                Optional.ofNullable(eimsSpareBo.getReferPrice()).ifPresent(referPrice -> {
                    dt.setUnitPrice(referPrice);
                    dt.setAmount(referPrice.multiply(BigDecimal.valueOf(eimsSpareBo.getQuantity())));
                });
                return dt;
            })
            .toList();
        // æ‰¹é‡æ’入数据
        if (!dtList.isEmpty()) {
            inoutdtMapper.insertBatch(dtList);
        }
        // æ›´æ–°å¤‡ä»¶çš„库存
        List<EimsSpare> updateSpareList = spareList.stream().map(spareBo -> {
            EimsSpare spare = new EimsSpare();
            spare.setId(spareBo.getId());
            spare.setActualStock(spareBo.getActualStock() + (spareBo.getQuantity() * OperationType));
            // è®¾ç½®å•价和金额
            Optional.ofNullable(spareBo.getReferPrice()).ifPresent(referPrice -> {
                spare.setStockAmount(referPrice.multiply(BigDecimal.valueOf(spare.getActualStock())));
            });
            return spare;
        }).toList();
        if (!updateSpareList.isEmpty()) {
            spareMapper.updateBatchById(updateSpareList);
        }
        return flag;
    }
@@ -132,4 +225,6 @@
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareInoutdtServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,138 @@
package org.dromara.eims.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.eims.domain.vo.EimsMaintStVo;
import org.springframework.stereotype.Service;
import org.dromara.eims.domain.bo.EimsSpareInoutdtBo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.dromara.eims.domain.EimsSpareInoutdt;
import org.dromara.eims.mapper.EimsSpareInoutdtMapper;
import org.dromara.eims.service.IEimsSpareInoutdtService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
 * å¤‡ä»¶å‡ºå…¥åº“明细Service业务层处理
 *
 * @author zhuguifei
 * @date 2025-04-11
 */
@RequiredArgsConstructor
@Service
public class EimsSpareInoutdtServiceImpl implements IEimsSpareInoutdtService {
    private final EimsSpareInoutdtMapper baseMapper;
    /**
     * æŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param id ä¸»é”®
     * @return å¤‡ä»¶å‡ºå…¥åº“明细
     */
    @Override
    public EimsSpareInoutdtVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢å¤‡ä»¶å‡ºå…¥åº“明细列表
     *
     * @param bo        æŸ¥è¯¢æ¡ä»¶
     * @param pageQuery åˆ†é¡µå‚æ•°
     * @return å¤‡ä»¶å‡ºå…¥åº“明细分页列表
     */
    @Override
    public TableDataInfo<EimsSpareInoutdtVo> queryPageList(EimsSpareInoutdtBo bo, PageQuery pageQuery) {
        Page<EimsSpareInoutdtVo> result = baseMapper.selectSpareInoutdtList(pageQuery.build(), buildWrapper(bo));
        return TableDataInfo.build(result);
    }
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„备件出入库明细列表
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return å¤‡ä»¶å‡ºå…¥åº“明细列表
     */
    @Override
    public List<EimsSpareInoutdtVo> queryList(EimsSpareInoutdtBo bo) {
        LambdaQueryWrapper<EimsSpareInoutdt> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }
    private LambdaQueryWrapper<EimsSpareInoutdt> buildQueryWrapper(EimsSpareInoutdtBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<EimsSpareInoutdt> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getInoutId() != null, EimsSpareInoutdt::getInoutId, bo.getInoutId());
        lqw.eq(bo.getSpareId() != null, EimsSpareInoutdt::getSpareId, bo.getSpareId());
        return lqw;
    }
    private QueryWrapper<EimsSpareInoutdt> buildWrapper(EimsSpareInoutdtBo bo) {
        Map<String, Object> params = bo.getParams();
        QueryWrapper<EimsSpareInoutdt> qw = Wrappers.query();
        qw.eq("io.id",bo.getInoutId());
        return qw;
    }
    /**
     * æ–°å¢žå¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param bo å¤‡ä»¶å‡ºå…¥åº“明细
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    @Override
    public Boolean insertByBo(EimsSpareInoutdtBo bo) {
        EimsSpareInoutdt add = MapstructUtils.convert(bo, EimsSpareInoutdt.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }
    /**
     * ä¿®æ”¹å¤‡ä»¶å‡ºå…¥åº“明细
     *
     * @param bo å¤‡ä»¶å‡ºå…¥åº“明细
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    @Override
    public Boolean updateByBo(EimsSpareInoutdtBo bo) {
        EimsSpareInoutdt update = MapstructUtils.convert(bo, EimsSpareInoutdt.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     */
    private void validEntityBeforeSave(EimsSpareInoutdt entity){
        //TODO åšä¸€äº›æ•°æ®æ ¡éªŒ,如唯一约束
    }
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤å¤‡ä»¶å‡ºå…¥åº“明细信息
     *
     * @param ids     å¾…删除的主键集合
     * @param isValid æ˜¯å¦è¿›è¡Œæœ‰æ•ˆæ€§æ ¡éªŒ
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
}
eims/ruoyi-modules/lb-eims/src/main/java/org/dromara/eims/service/impl/EimsSpareServiceImpl.java
@@ -1,5 +1,6 @@
package org.dromara.eims.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -8,6 +9,10 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.eims.domain.EimsSpareInout;
import org.dromara.eims.domain.bo.EimsSpareInoutBo;
import org.dromara.eims.domain.vo.EimsSpareInoutVo;
import org.dromara.eims.domain.vo.EimsSpareInoutdtVo;
import org.springframework.stereotype.Service;
import org.dromara.eims.domain.bo.EimsSpareBo;
import org.dromara.eims.domain.vo.EimsSpareVo;
@@ -56,6 +61,13 @@
        return TableDataInfo.build(result);
    }
    @Override
    public TableDataInfo<EimsSpareInoutdtVo> querySpareInoutList(EimsSpareBo bo, PageQuery pageQuery) {
        Page<EimsSpareInoutdtVo> result = baseMapper.selectSpareInoutList(pageQuery.build(), buildWrapper(bo));
        return TableDataInfo.build(result);
    }
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„备件台账列表
     *
@@ -79,6 +91,13 @@
        lqw.like(StringUtils.isNotBlank(bo.getSupplier()), EimsSpare::getSupplier, bo.getSupplier());
        lqw.eq(StringUtils.isNotBlank(bo.getUnit()), EimsSpare::getUnit, bo.getUnit());
        return lqw;
    }
    private QueryWrapper<EimsSpare> buildWrapper(EimsSpareBo bo) {
        Map<String, Object> params = bo.getParams();
        QueryWrapper<EimsSpare> qw = Wrappers.query();
        qw.eq( "sp.id", bo.getId());
        return qw;
    }
    /**
@@ -132,4 +151,6 @@
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
}
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsSpareInoutdtMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.eims.mapper.EimsSpareInoutdtMapper">
    <resultMap type="org.dromara.eims.domain.vo.EimsSpareInoutdtVo" id="SpareInoutdtVoResult">
    </resultMap>
    <select id="selectSpareInoutdtList" resultMap="SpareInoutdtVoResult">
        SELECT dt.*, sp.name spareName, sp.code spareCode, sp.model_no modelNo, sp.unit unit, io.order_code orderCode
        FROM eims_spare_inoutdt dt
                 LEFT JOIN eims_spare_inout io on dt.inout_id = io.id
                 LEFT JOIN eims_spare sp on dt.spare_id = sp.id
            ${ew.getCustomSqlSegment}
    </select>
</mapper>
eims/ruoyi-modules/lb-eims/src/main/resources/mapper/eims/EimsSpareMapper.xml
@@ -3,5 +3,13 @@
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.eims.mapper.EimsSpareMapper">
    <resultMap type="org.dromara.eims.domain.vo.EimsSpareInoutdtVo" id="SpareInoutdtVoResult">
    </resultMap>
    <select id="selectSpareInoutList" resultMap="SpareInoutdtVoResult">
        SELECT io.*, dt.quantity, dt.actual_stock, dt.unit_price, dt.amount
        FROM eims_spare_inout io
                 JOIN eims_spare_inoutdt dt on io.id = dt.inout_id
                 JOIN eims_spare sp on dt.spare_id = sp.id
            ${ew.getCustomSqlSegment}
    </select>
</mapper>