feat: 产品根据项目进行库存数量维护

This commit is contained in:
louis 2024-03-11 13:41:59 +08:00
parent 568d77d178
commit 9764ead573
9 changed files with 135 additions and 187 deletions

View File

@ -1387,50 +1387,18 @@ declare namespace API {
}; };
type MaterialsInventoryEntity = { type MaterialsInventoryEntity = {
/** 公司名称 */ /** 项目 */
companyName: string; project?: ProjectEntity;
/** 产品名称(字典) */ /** 产品 */
product: number; product: ProductEntity;
/** 单位(字典) */ /** 单价 */
unit: number; unitPrice: number;
/** 之前的库存数量 */ /** 数量 */
previousInventoryQuantity: number; quantity: number;
/** 之前的单价 */
previousUnitPrice: number;
/** 之前的金额 */
previousAmount: number;
/** 入库时间 */
inventoryTime: Date;
/** 入库数量 */
inventoryQuantity: number;
/** 入库单价*/
inventoryUnitPrice: number;
/** 入库金额 */
inventoryAmount: number;
/** 出库时间 */
outime: Date;
/** 出库数量 */
outQuantity: number;
/** 出库单价 */
outUnitPrice: number;
/** 出库金额 */
outAmount: number;
/** 现在的结存数量 */
currentInventoryQuantity: number;
/** 现在的单价 */
currentUnitPrice: number;
/** 现在的金额 */
currentAmount: number;
/** 经办人 */ /** 经办人 */
agent: string; agent: string;
/** 领料单号 */
issuanceNumber?: number;
/** 项目 */
project: ProjectEntity;
/** 备注 */ /** 备注 */
remark: string; remark: string;
/** 附件 */
files: StorageInfo[];
id: number; id: number;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
@ -1616,6 +1584,8 @@ declare namespace API {
name: string; name: string;
/** 所属公司 */ /** 所属公司 */
company: CompanyEntity; company: CompanyEntity;
/** 产品备注 */
remark: string;
/** 单位 */ /** 单位 */
unit: DictItemEntity; unit: DictItemEntity;
/** 是否删除 */ /** 是否删除 */
@ -1631,6 +1601,8 @@ declare namespace API {
name: string; name: string;
/** 所属公司 */ /** 所属公司 */
companyId?: number; companyId?: number;
/** 产品备注 */
remark: string;
/** 单位 */ /** 单位 */
unitId?: number; unitId?: number;
fileIds?: number[]; fileIds?: number[];
@ -1785,7 +1757,8 @@ declare namespace API {
}; };
type MaterialsInOutInfoParams = { type MaterialsInOutInfoParams = {
id: number; id?: number;
inventoryNumber?: string;
}; };
type MaterialsInOutListParams = { type MaterialsInOutListParams = {

View File

@ -180,7 +180,6 @@ export function exportJson2Excel({
const wsName = 'SheetJS'; const wsName = 'SheetJS';
const wb = new Workbook(), const wb = new Workbook(),
ws = sheetFromArrayOfArrays(data); ws = sheetFromArrayOfArrays(data);
if (merges.length > 0) { if (merges.length > 0) {
if (!ws['!merges']) ws['!merges'] = []; if (!ws['!merges']) ws['!merges'] = [];
merges.forEach((item) => { merges.forEach((item) => {

View File

@ -1,7 +1,7 @@
import type { FormSchema } from '@/components/core/schema-form/'; import type { FormSchema } from '@/components/core/schema-form/';
import Api from '@/api'; import Api from '@/api';
import { debounce } from 'lodash-es'; import { debounce, isEmpty } from 'lodash-es';
import { MaterialsInOutEnum } from '@/enums/materialsInventoryEnum'; import { MaterialsInOutEnum } from '@/enums/materialsInventoryEnum';
export const formSchemas = (isEdit?: boolean): FormSchema<API.MaterialsInOutEntity>[] => [ export const formSchemas = (isEdit?: boolean): FormSchema<API.MaterialsInOutEntity>[] => [
{ {
@ -28,7 +28,7 @@ export const formSchemas = (isEdit?: boolean): FormSchema<API.MaterialsInOutEnti
label: '库存编号', label: '库存编号',
vIf: ({ formModel }) => formModel.inOrOut === MaterialsInOutEnum.Out, vIf: ({ formModel }) => formModel.inOrOut === MaterialsInOutEnum.Out,
colProps: { colProps: {
span: 16, span: 24,
}, },
helpMessage: '出库必须选择入库时的编号', helpMessage: '出库必须选择入库时的编号',
componentProps: ({ formInstance, schema, formModel }) => ({ componentProps: ({ formInstance, schema, formModel }) => ({
@ -66,6 +66,16 @@ export const formSchemas = (isEdit?: boolean): FormSchema<API.MaterialsInOutEnti
} }
}, },
}, },
onSelect: async (inventoryNumber: string) => {
const { items = [] } = await Api.materialsInOut.materialsInOutList({
inventoryNumber,
isCreateOut: true,
});
if (!isEmpty(items)) {
const { inOrOut, id, ...ext } = items[0] as API.MaterialsInOutEntity;
formInstance?.setFieldsValue(ext);
}
},
onSearch: debounce(async (keyword) => { onSearch: debounce(async (keyword) => {
schema.loading = true; schema.loading = true;
const newSchema = { const newSchema = {
@ -141,8 +151,12 @@ export const formSchemas = (isEdit?: boolean): FormSchema<API.MaterialsInOutEnti
colProps: { colProps: {
span: 12, span: 12,
}, },
vIf: ({ formModel }) =>
formModel.inOrOut === MaterialsInOutEnum.In || isEdit || formModel.inventoryNumber,
rules: [{ required: true, type: 'number' }],
helpMessage: '如未找到对应项目,请先去项目管理添加项目。', helpMessage: '如未找到对应项目,请先去项目管理添加项目。',
componentProps: ({ formInstance, schema, formModel }) => ({ componentProps: ({ formInstance, schema, formModel }) => ({
disabled: Object.is(formModel.inOrOut, MaterialsInOutEnum.Out) || isEdit,
showSearch: true, showSearch: true,
filterOption: false, filterOption: false,
fieldNames: { fieldNames: {
@ -165,6 +179,17 @@ export const formSchemas = (isEdit?: boolean): FormSchema<API.MaterialsInOutEnti
request: () => { request: () => {
return getProjectOptions(); return getProjectOptions();
}, },
// request: {
// watchFields: ['framework'],
// options: {
// immediate: true,
// },
// callback: async ({ formModel }) => {
// console.log('values', formModel);
// formModel['lib'] = undefined;
// return fetchLibData(formModel['framework']);
// },
// },
onSearch: debounce(async (keyword) => { onSearch: debounce(async (keyword) => {
schema.loading = true; schema.loading = true;
const newSchema = { const newSchema = {
@ -224,7 +249,7 @@ export const formSchemas = (isEdit?: boolean): FormSchema<API.MaterialsInOutEnti
label: '领料单号', label: '领料单号',
field: 'issuanceNumber', field: 'issuanceNumber',
component: 'Input', component: 'Input',
vIf:isEdit, vIf: ({ formModel }) => formModel.inOrOut === MaterialsInOutEnum.Out,
colProps: { colProps: {
span: 12, span: 12,
}, },
@ -265,7 +290,7 @@ const getInventoryNumberOptions = async (inventoryNumber?: string): Promise<Labe
}); });
return ( return (
result?.map((item) => ({ result?.map((item) => ({
label: item.inventoryNumber, label: `${item.inventoryNumber} (${item.project?.name || '-'}) (${item.product?.company?.name || '-'}) (${item.product?.name || '-'})`,
value: item.inventoryNumber, value: item.inventoryNumber,
})) || [] })) || []
); );

View File

@ -18,13 +18,7 @@
> >
新增 新增
</a-button> </a-button>
<a-button
type="primary"
:disabled="!$auth('app:materials_inventory:export')"
@click="exportMI()"
>
导出原材料盘点表
</a-button>
</template> </template>
</DynamicTable> </DynamicTable>
</div> </div>
@ -43,7 +37,7 @@
import { useFormModal, useModal } from '@/hooks/useModal'; import { useFormModal, useModal } from '@/hooks/useModal';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';
import { formSchemas } from './formSchemas'; import { formSchemas } from './formSchemas';
import { exportSchemas } from './exportSchema';
import AttachmentManage from '@/components/business/attachment-manage/index.vue'; import AttachmentManage from '@/components/business/attachment-manage/index.vue';
import AttachmentUpload from '@/components/business/attachment-upload/index.vue'; import AttachmentUpload from '@/components/business/attachment-upload/index.vue';
import fileDownload from 'js-file-download'; import fileDownload from 'js-file-download';
@ -53,7 +47,7 @@
}); });
const [DynamicTable, dynamicTableInstance] = useTable({ formProps: { autoSubmitOnEnter: true } }); const [DynamicTable, dynamicTableInstance] = useTable({ formProps: { autoSubmitOnEnter: true } });
const [showModal] = useFormModal(); const [showModal] = useFormModal();
const [showExportModal] = useFormModal();
const [fnModal] = useModal(); const [fnModal] = useModal();
const isUploadPopupVisiable = ref(false); const isUploadPopupVisiable = ref(false);
@ -107,35 +101,7 @@
}, },
]; ];
}); });
const exportMI = async () => {
const { time } = unref<TableQueryItem>(
dynamicTableInstance?.queryFormRef?.formModel as TableQueryItem,
);
const [formRef] = await showExportModal({
modalProps: {
title: `导出条件选择`,
width: '50%',
okText: '导出',
onFinish: async (values) => {
const response = await Api.materialsInventory.materialsInventoryExport(values);
const { time } = values;
fileDownload(response, `${dayjs(time).format('YYYY年MM月')}.xls`);
},
},
formProps: {
labelWidth: 100,
schemas: exportSchemas,
},
});
// auto fill export time fields
if (time) {
formRef?.setFieldsValue({
time,
});
}
};
const openAttachmentUploadModal = async (record: TableListItem) => { const openAttachmentUploadModal = async (record: TableListItem) => {
isUploadPopupVisiable.value = true; isUploadPopupVisiable.value = true;
fnModal.show({ fnModal.show({

View File

@ -9,10 +9,29 @@ export type TableListItem = API.MaterialsInventoryEntity;
export type TableColumnItem = TableColumn<TableListItem>; export type TableColumnItem = TableColumn<TableListItem>;
const dictStore = useDictStore(); const dictStore = useDictStore();
export const baseColumns: TableColumnItem[] = [ export const baseColumns: TableColumnItem[] = [
{
title: '所属项目',
width: 180,
dataIndex: 'project',
customRender: ({ record }) => {
return record?.project?.name || '';
},
},
{
title: '所属公司',
width: 180,
dataIndex: 'product',
customRender: ({ record }) => {
return record?.product?.company?.name || '';
},
},
{ {
title: '产品名称', title: '产品名称',
width: 180, width: 180,
dataIndex: 'product', dataIndex: 'product',
customRender: ({ record }) => {
return record?.product?.name || '';
},
}, },
{ {
title: '单位', title: '单位',
@ -28,10 +47,7 @@ export const baseColumns: TableColumnItem[] = [
}, },
}, },
customRender: ({ record }) => { customRender: ({ record }) => {
return dictStore.getDictItemsByCode(DictEnum.Unit)?.length return record?.product?.unit?.label || '';
? dictStore.getDictItemsByCode(DictEnum.Unit).find((item) => item.id === record.unit)
?.label || ''
: '';
}, },
}, },
{ {
@ -45,11 +61,9 @@ export const baseColumns: TableColumnItem[] = [
hideInSearch: true, hideInSearch: true,
width: 80, width: 80,
dataIndex: 'unitPrice', dataIndex: 'unitPrice',
customRender: ({ record }) => {
return parseFloat(record.unitPrice) || 0;
}, },
{
title: '备注',
width: 80,
dataIndex: 'remark',
}, },
]; ];

View File

@ -2,7 +2,7 @@
<div v-if="columns?.length"> <div v-if="columns?.length">
<DynamicTable <DynamicTable
row-key="id" row-key="id"
header-title="原材料盘点" header-title="原材料库存"
title-tooltip="" title-tooltip=""
:data-request="Api.materialsInventory.materialsInventoryList" :data-request="Api.materialsInventory.materialsInventoryList"
:columns="columns" :columns="columns"
@ -12,12 +12,12 @@
<template #toolbar> <template #toolbar>
<a-button <a-button
type="primary" type="primary"
:disabled="!$auth('system:role:create')" :disabled="!$auth('app:materials_inventory:export')"
@click="openEditModal({})" @click="exportMI()"
>
导出原材料盘点表
</a-button></template
> >
新增
</a-button>
</template>
</DynamicTable> </DynamicTable>
</div> </div>
</template> </template>
@ -27,35 +27,29 @@
import { baseColumns, type TableColumnItem, type TableListItem } from './columns'; import { baseColumns, type TableColumnItem, type TableListItem } from './columns';
import Api from '@/api/'; import Api from '@/api/';
import { useDictStore } from '@/store/modules/dict'; import { useDictStore } from '@/store/modules/dict';
import { onMounted, ref, type FunctionalComponent } from 'vue'; import { onMounted, ref, type FunctionalComponent, unref } from 'vue';
import { DictEnum } from '@/enums/dictEnum'; import { DictEnum } from '@/enums/dictEnum';
import { useFormModal, useModal } from '@/hooks/useModal'; import { useFormModal, useModal } from '@/hooks/useModal';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';
import { exportSchemas } from './exportSchema';
import dayjs from 'dayjs';
import fileDownload from 'js-file-download';
defineOptions({ defineOptions({
name: 'MaterialsInventory', name: 'MaterialsInventory',
}); });
const [DynamicTable, dynamicTableInstance] = useTable(); const [DynamicTable, dynamicTableInstance] = useTable();
const [fnModal] = useModal(); const [showExportModal] = useFormModal();
const isUploadPopupVisiable = ref(false);
let columns = ref<TableColumnItem[]>(); let columns = ref<TableColumnItem[]>();
onMounted(() => { onMounted(() => {
columns.value = [ columns.value = [
...baseColumns, ...baseColumns,
{
title: '附件',
width: 50,
maxWidth: 50,
hideInSearch: true,
dataIndex: 'files',
customRender: ({ record }) => <FilesRender {...record} />,
},
{ {
title: '操作', title: '操作',
maxWidth: 80, maxWidth: 60,
width: 80, width: 60,
minWidth: 80, minWidth: 60,
dataIndex: 'ACTION', dataIndex: 'ACTION',
hideInSearch: true, hideInSearch: true,
actions: ({ record }) => [ actions: ({ record }) => [
@ -79,46 +73,38 @@
onConfirm: () => delRowConfirm(record.id), onConfirm: () => delRowConfirm(record.id),
}, },
}, },
{
icon: 'ant-design:cloud-upload-outlined',
tooltip: '上传附件',
onClick: () => openAttachmentUploadModal(record),
},
], ],
}, },
]; ];
}); });
const exportMI = async () => {
const { time } = unref<{ time: string; projectId: number }>(
dynamicTableInstance?.queryFormRef?.formModel as { time: string; projectId: number },
);
const openAttachmentUploadModal = async (record: TableListItem) => { const [formRef] = await showExportModal({
// isUploadPopupVisiable.value = true; modalProps: {
// fnModal.show({ title: `导出条件选择`,
// width: 800, width: '50%',
// title: `: ${record.contractNumber}`, okText: '导出',
// content: () => { onFinish: async (values) => {
// return ( const response = await Api.materialsInventory.materialsInventoryExport(values);
// <AttachmentUpload const { time } = values;
// onClose={handleUploadClose} fileDownload(response, `${dayjs(time).format('YYYY.MM.盘点表')}.xls`);
// afterUploadCallback={(files) => { },
// afterUploadCallback(files, record.id); },
// }} formProps: {
// ></AttachmentUpload> labelWidth: 100,
// ); schemas: exportSchemas,
// }, },
// destroyOnClose: true, });
// open: isUploadPopupVisiable.value,
// footer: null, // auto fill export time fields
// }); if (time) {
}; formRef?.setFieldsValue({
const handleUploadClose = (hasSuccess: boolean) => { time,
fnModal.hide(); });
isUploadPopupVisiable.value = false; }
};
const afterUploadCallback = async (
files: { filename: { path: string; id: number } }[],
id: number,
) => {
await Api.contract.contractUpdate({ id }, { fileIds: files.map((item) => item.filename.id) });
dynamicTableInstance?.reload();
}; };
/** /**
@ -160,42 +146,14 @@
await Api.contract.contractDelete({ id: record }); await Api.contract.contractDelete({ id: record });
dynamicTableInstance?.reload(); dynamicTableInstance?.reload();
}; };
const FilesRender: FunctionalComponent<TableListItem> = (contract: TableListItem) => {
const [fnModal] = useModal();
return (
<Button
type="link"
onClick={() => {
openFilesManageModal(fnModal, contract);
}}
>
{contract.files?.length || 0}
</Button>
);
};
const openFilesManageModal = (fnModal, contract: TableListItem) => {
// const fileIds = contract.files?.map((item) => item.id) || [];
// fnModal.show({
// width: 1200,
// title: ``,
// content: () => {
// return (
// <AttachmentManage
// fileIds={fileIds}
// onDelete={(unlinkIds) => unlinkAttachments(contract.id, unlinkIds)}
// ></AttachmentManage>
// );
// },
// destroyOnClose: true,
// footer: null,
// });
};
const unlinkAttachments = async (id: number, unlinkIds: number[]) => {
await Api.contract.unlinkAttachments({ id }, { fileIds: unlinkIds });
dynamicTableInstance?.reload();
};
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped></style>
import dayjs from 'dayjs'; import fileDownload from 'js-file-download'; import type { TableQueryItem
} from '../in-out/columns'; import { exportSchemas } from '../in-out/exportSchema'; import dayjs
from 'dayjs'; import fileDownload from 'js-file-download'; import type { TableQueryItem } from
'../in-out/columns'; import { exportSchemas } from './exportSchema'; import dayjs from 'dayjs';
import fileDownload from 'js-file-download'; import type { TableQueryItem } from
'../in-out/columns'; import { exportSchemas } from './exportSchema'; import dayjs from 'dayjs';
import fileDownload from 'js-file-download'; import type { TableQueryItem } from
'../in-out/columns';

View File

@ -18,8 +18,13 @@ export const baseColumns: TableColumnItem[] = [
{ {
title: '单位', title: '单位',
dataIndex: 'unit', dataIndex: 'unit',
width:80,
customRender: ({ record }) => { customRender: ({ record }) => {
return record?.unit?.label || ''; return record?.unit?.label || '';
}, },
}, },
{
title: '备注',
dataIndex: 'remark',
},
]; ];

View File

@ -86,6 +86,14 @@ export const formSchemas: FormSchema<API.ProductDto>[] = [
}, 500), }, 500),
}), }),
}, },
{
field: 'remark',
component: 'InputTextArea',
label: '备注',
colProps: {
span: 24,
},
},
]; ];
const getCompanyOptions = async (keyword?: string): Promise<LabelValueOptions> => { const getCompanyOptions = async (keyword?: string): Promise<LabelValueOptions> => {