From 609b918b24e8dbbe75bf2eaef7a532308d83a708 Mon Sep 17 00:00:00 2001 From: zhuguifei <zhuguifei@zhuguifeideiMac.local> Date: 星期三, 16 四月 2025 10:06:47 +0800 Subject: [PATCH] 完成备件模块 --- eims-ui/apps/web-antd/src/views/eims/spare-in/spare-in-drawer.vue | 110 +++++++ eims-ui/apps/web-antd/src/views/eims/spare-out/spare-out-drawer.vue | 122 +++++++ eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/index.ts | 61 ++++ eims-ui/apps/web-antd/.env.production | 4 eims-ui/apps/web-antd/src/views/eims/spare-out/index.vue | 25 + eims-ui/apps/web-antd/src/views/eims/spare-inoutdt/data.tsx | 65 ++++ eims-ui/apps/web-antd/src/api/eims/spare/index.ts | 5 eims-ui/apps/web-antd/src/views/eims/components/basis-sub-table.vue | 1 eims/ruoyi-admin/src/main/resources/application-prod.yml | 6 eims-ui/apps/web-antd/src/api/eims/spare-inout/model.d.ts | 9 eims-ui/apps/web-antd/src/views/eims/components/spare-modal.vue | 54 +++ eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java | 12 eims-ui/apps/web-antd/src/views/eims/spare/data.tsx | 89 +++++ eims-ui/apps/web-antd/src/views/eims/spare-out/select-spare-table.vue | 121 ++++++++ eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/model.d.ts | 46 +++ eims-ui/apps/web-antd/src/views/eims/spare/index.vue | 98 +++-- eims-ui/apps/web-antd/src/views/eims/spare-in/index.vue | 25 + eims-ui/apps/web-antd/src/views/eims/spare-out/data.tsx | 13 eims-ui/apps/web-antd/src/views/eims/spare-in/data.tsx | 11 19 files changed, 809 insertions(+), 68 deletions(-) diff --git a/eims-ui/apps/web-antd/.env.production b/eims-ui/apps/web-antd/.env.production index 4086f92..00537ff 100644 --- a/eims-ui/apps/web-antd/.env.production +++ b/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鍏閽� 璇锋眰鍔犲瘑-鍚庣瑙e瘑鏄竴瀵� 鍝嶅簲瑙e瘑-鍚庣鍔犲瘑鏄竴瀵� VITE_GLOB_RSA_PUBLIC_KEY=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ== # RSA绉侀挜 鍝嶅簲瑙e瘑浣跨敤 娉ㄦ剰杩欎袱涓槸涓ゅRSA鍏閽� 璇锋眰鍔犲瘑-鍚庣瑙e瘑鏄竴瀵� 鍝嶅簲瑙e瘑-鍚庣鍔犲瘑鏄竴瀵� @@ -28,5 +28,5 @@ VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e # 寮�鍚疭SE -VITE_GLOB_SSE_ENABLE=true +VITE_GLOB_SSE_ENABLE=false diff --git a/eims-ui/apps/web-antd/src/api/eims/spare-inout/model.d.ts b/eims-ui/apps/web-antd/src/api/eims/spare-inout/model.d.ts index ae55af8..22e1665 100644 --- a/eims-ui/apps/web-antd/src/api/eims/spare-inout/model.d.ts +++ b/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; } diff --git a/eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/index.ts b/eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/index.ts new file mode 100644 index 0000000..712f532 --- /dev/null +++ b/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); +} diff --git a/eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/model.d.ts b/eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/model.d.ts new file mode 100644 index 0000000..b2759f2 --- /dev/null +++ b/eims-ui/apps/web-antd/src/api/eims/spare-inoutdt/model.d.ts @@ -0,0 +1,46 @@ +export interface SpareInoutdtVO { + /** + * + */ + id: number | string; + + /** + * 鍑哄簱鍗曟垨鍏ュ簱鍗昳d + */ + inoutId: number | string; + + /** + * 澶囦欢id + */ + spareId: number | string; + + /** + * 涔嬪墠搴撳瓨 + */ + beforeStock: number; + + /** + * 瀹為檯搴撳瓨 + */ + actualStock: number; + + /** + * 鏁伴噺 + */ + quantity: number; + + /** + * 鍗曚环 + */ + unitPrice: number; + + /** + * 閲戦 + */ + amount: number; + + /** + * 澶囨敞 + */ + remark: string; +} diff --git a/eims-ui/apps/web-antd/src/api/eims/spare/index.ts b/eims-ui/apps/web-antd/src/api/eims/spare/index.ts index b5886ed..2fb6930 100644 --- a/eims-ui/apps/web-antd/src/api/eims/spare/index.ts +++ b/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 diff --git a/eims-ui/apps/web-antd/src/views/eims/components/basis-sub-table.vue b/eims-ui/apps/web-antd/src/views/eims/components/basis-sub-table.vue index d2d3732..7487ac4 100644 --- a/eims-ui/apps/web-antd/src/views/eims/components/basis-sub-table.vue +++ b/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, diff --git a/eims-ui/apps/web-antd/src/views/eims/components/spare-modal.vue b/eims-ui/apps/web-antd/src/views/eims/components/spare-modal.vue new file mode 100644 index 0000000..e282745 --- /dev/null +++ b/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> diff --git a/eims-ui/apps/web-antd/src/views/eims/spare-in/data.tsx b/eims-ui/apps/web-antd/src/views/eims/spare-in/data.tsx index 3d02eb4..aa0b0c5 100644 --- a/eims-ui/apps/web-antd/src/views/eims/spare-in/data.tsx +++ b/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', // 鍦╠rawer閲屾洿鏂� 杩欓噷涓嶉渶瑕侀粯璁ょ殑componentProps defaultValue: undefined, diff --git a/eims-ui/apps/web-antd/src/views/eims/spare-in/index.vue b/eims-ui/apps/web-antd/src/views/eims/spare-in/index.vue index 7a054c4..f2f8183 100644 --- a/eims-ui/apps/web-antd/src/views/eims/spare-in/index.vue +++ b/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> diff --git a/eims-ui/apps/web-antd/src/views/eims/spare-in/spare-in-drawer.vue b/eims-ui/apps/web-antd/src/views/eims/spare-in/spare-in-drawer.vue index 263c7ac..919183e 100644 --- a/eims-ui/apps/web-antd/src/views/eims/spare-in/spare-in-drawer.vue +++ b/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> diff --git a/eims-ui/apps/web-antd/src/views/eims/spare-inoutdt/data.tsx b/eims-ui/apps/web-antd/src/views/eims/spare-inoutdt/data.tsx new file mode 100644 index 0000000..d205daf --- /dev/null +++ b/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 = () => []; diff --git a/eims-ui/apps/web-antd/src/views/eims/spare-out/data.tsx b/eims-ui/apps/web-antd/src/views/eims/spare-out/data.tsx index dd1dbc0..9b09695 100644 --- a/eims-ui/apps/web-antd/src/views/eims/spare-out/data.tsx +++ b/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', // 鍦╠rawer閲屾洿鏂� 杩欓噷涓嶉渶瑕侀粯璁ょ殑componentProps defaultValue: undefined, diff --git a/eims-ui/apps/web-antd/src/views/eims/spare-out/index.vue b/eims-ui/apps/web-antd/src/views/eims/spare-out/index.vue index 764ec4a..fb9b3b9 100644 --- a/eims-ui/apps/web-antd/src/views/eims/spare-out/index.vue +++ b/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> diff --git a/eims-ui/apps/web-antd/src/views/eims/spare-out/select-spare-table.vue b/eims-ui/apps/web-antd/src/views/eims/spare-out/select-spare-table.vue new file mode 100644 index 0000000..a84011c --- /dev/null +++ b/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> diff --git a/eims-ui/apps/web-antd/src/views/eims/spare-out/spare-out-drawer.vue b/eims-ui/apps/web-antd/src/views/eims/spare-out/spare-out-drawer.vue index ebd82a4..8d4219c 100644 --- a/eims-ui/apps/web-antd/src/views/eims/spare-out/spare-out-drawer.vue +++ b/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> diff --git a/eims-ui/apps/web-antd/src/views/eims/spare/data.tsx b/eims-ui/apps/web-antd/src/views/eims/spare/data.tsx index 8228ae6..709705a 100644 --- a/eims-ui/apps/web-antd/src/views/eims/spare/data.tsx +++ b/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', diff --git a/eims-ui/apps/web-antd/src/views/eims/spare/index.vue b/eims-ui/apps/web-antd/src/views/eims/spare/index.vue index 1682ab2..0cb214a 100644 --- a/eims-ui/apps/web-antd/src/views/eims/spare/index.vue +++ b/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; diff --git a/eims/ruoyi-admin/src/main/resources/application-prod.yml b/eims/ruoyi-admin/src/main/resources/application-prod.yml index ee68d30..4045cac 100644 --- a/eims/ruoyi-admin/src/main/resources/application-prod.yml +++ b/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 # 鏄惁寮�鍚痵sl diff --git a/eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java b/eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java index ea61622..9bbaa6c 100644 --- a/eims/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/DictConstants.java +++ b/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"; // 棰嗙敤鍑哄簱 + } + + + + } -- Gitblit v1.9.3