feat: 下载文件功能,全局loading组件封装,导出盘点表初步

This commit is contained in:
louis 2024-03-07 14:11:29 +08:00
parent 7c6ead4c66
commit 963ad25032
12 changed files with 186 additions and 15 deletions

View File

@ -47,6 +47,7 @@
"dayjs": "~1.11.10",
"echarts": "^5.5.0",
"file-saver": "~2.0.5",
"js-file-download": "^0.4.12",
"lodash-es": "~4.17.21",
"mitt": "~3.0.1",
"nprogress": "~1.0.0-1",
@ -78,13 +79,12 @@
"@vitejs/plugin-vue": "~5.0.4",
"@vitejs/plugin-vue-jsx": "~3.1.0",
"@vue/tsconfig": "^0.5.1",
"commitizen": "~4.3.0",
"cliui": "^8.0.1",
"cz-customizable": "^7.0.0",
"commitizen": "~4.3.0",
"conventional-changelog-cli": "~4.1.0",
"standard-version": "^9.5.0",
"core-js": "^3.36.0",
"cross-env": "~7.0.3",
"cz-customizable": "^7.0.0",
"eslint": "~8.57.0",
"eslint-config-prettier": "~9.1.0",
"eslint-define-config": "~2.1.0",
@ -103,6 +103,7 @@
"prettier": "~3.2.5",
"pretty-quick": "~4.0.0",
"rimraf": "~5.0.5",
"standard-version": "^9.5.0",
"stylelint": "~16.2.1",
"stylelint-config-property-sort-order-smacss": "^10.0.0",
"stylelint-config-recommended": "~14.0.0",

View File

@ -32,6 +32,9 @@ dependencies:
file-saver:
specifier: ~2.0.5
version: 2.0.5
js-file-download:
specifier: ^0.4.12
version: 0.4.12
lodash-es:
specifier: ~4.17.21
version: 4.17.21
@ -7727,6 +7730,10 @@ packages:
resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
dev: true
/js-file-download@0.4.12:
resolution: {integrity: sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==}
dev: false
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}

View File

@ -28,6 +28,19 @@ export async function materialsInventoryList(
});
}
/** 导出原材料盘点表 GET /api/materials-inventory/export*/
export async function materialsInventoryExport(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.MaterialsInventoryExportParams,
options?: RequestOptions,
) {
const { ...queryParams } = params;
return request(`/api/materials-inventory/export`, {
method: 'GET',
params: { ...queryParams },
...(options || { responseType: 'blob', isReturnResult: false }),
});
}
/** 新增原材料盘点 POST /api/materials-inventory */
export async function materialsInventoryCreate(
body: API.MaterialsInventoryDto,

View File

@ -1361,6 +1361,11 @@ declare namespace API {
id: number;
};
type MaterialsInventoryExportParams = {
time: string;
projectId: number;
};
type MaterialsInventoryInfoParams = {
id: number;
};

View File

@ -6,3 +6,4 @@ export { useI18n } from './useI18n';
export { useOnline } from './useOnline';
export { useTime } from './useTime';
export { useSortable } from './useSortable';
export { useGlobalLoading } from './useGlobalLoading';

View File

@ -0,0 +1,28 @@
import { createVNode, render, h, onMounted, onUnmounted } from 'vue';
import { Spin as ASpin } from 'ant-design-vue';
export function useGlobalLoading() {
let loadingElement: HTMLElement | null = null;
const showLoading = () => {
if (!loadingElement) {
loadingElement = document.createElement('div');
loadingElement.id = 'global-loading';
document.body.appendChild(loadingElement);
const spinVNode = h(ASpin, { spinning: true });
render(spinVNode, loadingElement);
}
};
const hideLoading = () => {
if (loadingElement) {
document.body.removeChild(loadingElement);
loadingElement = null;
}
};
onUnmounted(hideLoading);
return { showLoading, hideLoading };
}

View File

@ -34,7 +34,6 @@ export function useFormModal<T extends object = Recordable>() {
const onSubmit = async (values) => {
await modalProps?.onFinish?.(values);
// formRef.value?.resetFields();
ModalRender.hide();
};

View File

@ -5,3 +5,17 @@
@import './transition.less';
@import './common.less';
@import './antdv.override.less';
#global-loading {
display: flex;
position: fixed;
z-index: 9999;
top: 0;
right: 0;
left: 0;
align-items: center;
justify-content: center;
width: 100%;
height: 100vh;
background: #0004;
}

View File

@ -52,9 +52,12 @@ service.interceptors.request.use(
service.interceptors.response.use(
(response: AxiosResponse<BaseResponse>) => {
const res = response.data;
// if the custom code is not 200, it is judged as an error.
// 若是文件下载直接跳过解析
if (response.request.responseType === 'blob') {
return response;
}
if (res.code !== ResultEnum.SUCCESS) {
// if the custom code is not 200, it is judged as an error.
$message.error(res.message || UNKNOWN_ERROR);
// Illegal token
if ([1101, 1105].includes(res.code)) {

View File

@ -0,0 +1,72 @@
import type { FormSchema } from '@/components/core/schema-form/';
import Api from '@/api';
import { debounce } from 'lodash-es';
export const exportSchemas: FormSchema<API.MaterialsInventoryExportParams>[] = [
{
field: 'projectId',
component: 'Select',
label: '项目',
colProps: {
span: 12,
},
required: true,
helpMessage: '如未找到对应项目,请先去项目管理添加项目。',
componentProps: ({ formInstance, schema, formModel }) => ({
showSearch: true,
filterOption: false,
fieldNames: {
label: 'label',
value: 'value',
},
getPopupContainer: () => document.body,
defaultActiveFirstOption: true,
onClear: async () => {
const newSchema = {
field: schema.field,
componentProps: {
options: [] as LabelValueOptions,
},
};
const options = await getProjectOptions().finally(() => (schema.loading = false));
newSchema.componentProps.options = options;
formInstance?.updateSchema([newSchema]);
},
request: () => {
return getProjectOptions();
},
onSearch: debounce(async (keyword) => {
schema.loading = true;
const newSchema = {
field: schema.field,
componentProps: {
options: [] as LabelValueOptions,
},
};
formInstance?.updateSchema([newSchema]);
const options = await getProjectOptions(keyword).finally(() => (schema.loading = false));
newSchema.componentProps.options = options;
formInstance?.updateSchema([newSchema]);
}, 500),
}),
},
{
label: '时间',
field: 'time',
component: 'MonthPicker',
required: true,
colProps: {
span: 12,
},
},
];
const getProjectOptions = async (keyword?: string): Promise<LabelValueOptions> => {
const { items: result } = await Api.project.projectList({ pageSize: 100, name: keyword });
return (
result?.map((item) => ({
label: `${item.name}`,
value: item.id,
})) || []
);
};

View File

@ -3,7 +3,7 @@
<DynamicTable
row-key="id"
header-title="原材料出入库记录"
title-tooltip=""
title-tooltip="要创建入库记录,必须创建入库产品,产品所属公司。"
:data-request="Api.materialsInOut.materialsInOutList"
:columns="columns"
bordered
@ -20,7 +20,7 @@
</a-button>
<a-button
type="primary"
:disabled="!$auth('materials_inventory:history_in_out:export')"
:disabled="!$auth('app:materials_inventory:export')"
@click="exportMI()"
>
导出原材料盘点表
@ -41,10 +41,15 @@
import Api from '@/api/';
import { onMounted, ref, type FunctionalComponent, unref } from 'vue';
import { useFormModal, useModal } from '@/hooks/useModal';
import { Button, message } from 'ant-design-vue';
import { Button } from 'ant-design-vue';
import { formSchemas } from './formSchemas';
import { exportSchemas } from './exportSchema';
import AttachmentManage from '@/components/business/attachment-manage/index.vue';
import AttachmentUpload from '@/components/business/attachment-upload/index.vue';
import fileDownload from 'js-file-download';
import { useGlobalLoading } from '@/hooks';
import { waitTime } from '@/utils/common';
import dayjs from 'dayjs';
defineOptions({
name: 'MaterialsInOut',
});
@ -107,10 +112,30 @@
const { time } = unref<TableQueryItem>(
dynamicTableInstance?.queryFormRef?.formModel as TableQueryItem,
);
if (!time) {
message.warn('请先在查询区选择导出月份时间');
const [formRef] = await showModal({
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月')}.xlsx`);
},
},
formProps: {
labelWidth: 100,
schemas: exportSchemas,
},
});
// auto fill export search fields
if (time) {
formRef?.setFieldsValue({
time,
});
}
console.log('exportMI');
};
const openAttachmentUploadModal = async (record: TableListItem) => {
isUploadPopupVisiable.value = true;

View File

@ -147,6 +147,9 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
// minifyInternalExports: false,
//TODO fix circular imports
manualChunks(id) {
if (id.includes('/node_modules/')) {
return 'vendor';
}
if (id.includes('/src/locales/helper.ts')) {
return 'vendor';
} else if (id.includes('ant-design-vue')) {