xyh 3 лет назад
Родитель
Сommit
ad45a96494

+ 14 - 2
packages/app/src/apis/stream.ts

@@ -7,9 +7,11 @@ import {
   GetWarehousingFlowingListParams,
   NoticeListData,
   RawMaterialOutStreamListData,
+  SemiDrawListData,
   SemiInStreamListData,
   SemiManufacturesAddParams,
   SemiManufacturesOutParams,
+  SemiOutStreamListData,
   WarehousingListData,
 } from '@models';
 import {request} from './request';
@@ -122,7 +124,7 @@ export function exportSemiManufacturesInStream(
 /** 半成品出库物料列表 */
 export function getSemiManufacturesDrawList(
   data: GetSemiManufacturesDrawList,
-): BaseListResult<any> {
+): BaseListResult<SemiDrawListData> {
   return request({
     method: 'GET',
     url: '/askGoods/getGoodsHalf',
@@ -130,10 +132,20 @@ export function getSemiManufacturesDrawList(
   });
 }
 
+/** 半成品出库物料详情 */
+export function getSemiManufacturesDrawInfo(
+  id: string,
+): BaseListResult<SemiDrawListData> {
+  return request({
+    method: 'GET',
+    url: '/askGoods/getGoodsHalf',
+    data: {page: '1', limit: '1', id, partType: '半成品'},
+  });
+}
 /** 半成品出库流水列表 */
 export function getSemiManufacturesDrawStream(
   data: GetWarehousingFlowingListParams,
-): BaseListResult<any> {
+): BaseListResult<SemiOutStreamListData> {
   return request({
     method: 'GET',
     url: '/askGoods/getRemovalHalfProduct',

+ 9 - 1
packages/app/src/models/request/stream.ts

@@ -54,7 +54,13 @@ export type SemiManufacturesOutParams = {
   /** 物料id */
   materialId: string;
   /** 出库数量 */
-  num: string;
+  warehousingNum: string;
+  /** 要货单id */
+  askGoodsId: string;
+  /** 用户id */
+  userId: string;
+  /** 物料code */
+  wllbCode: string;
 };
 
 /** 产成品/半成品出库物料列表 */
@@ -64,4 +70,6 @@ export type GetSemiManufacturesDrawList = {
   askGoodsId: string;
   /** 生产订单号 */
   productionCode: string;
+  /** 是否出库 */
+  type: string;
 } & ListParams;

+ 38 - 0
packages/app/src/models/response/stream.ts

@@ -184,3 +184,41 @@ export type SemiInStreamListData = {
   /** 入库时间 */
   scrq: string;
 };
+
+/** 半成品领料单列表 */
+export type SemiDrawListData = {
+  id: string;
+  /** 物料名称 */
+  materialName: string;
+  /** 数量 */
+  num: string;
+  /** 生产订单号 */
+  productionCode: string;
+  /** 要料部门 */
+  department: string;
+  /** 是否出库 */
+  type: string;
+  /** 物料id */
+  materialId: string;
+  /** 要货单id */
+  askGoodsId: string;
+  /** 物料code */
+  materialCode: string;
+};
+
+/** 半成品出库流水 */
+export type SemiOutStreamListData = {
+  id: string;
+  /** 出库数量 */
+  num: string;
+  /** 领用部门 */
+  department: string;
+  /** 物料名称 */
+  materialName: string;
+  /** 出库日期 */
+  scrq: string;
+  /** 出库人 */
+  userName: string;
+  /** 要货单id */
+  askGoodsId: string;
+};

+ 1 - 1
packages/app/src/pages/semi-draw/context.ts

@@ -3,5 +3,5 @@ import {createPageContext, createSearchContext, createTableSearchContext} from '
 export const pageContext = createPageContext();
 export const searchContext = createSearchContext();
 
-export const contextState = {code: ''};
+export const contextState = {code: '', type: ''};
 export const context = createTableSearchContext(contextState);

+ 2 - 2
packages/app/src/pages/semi-draw/filter/hooks.ts

@@ -1,12 +1,12 @@
 import {useContextSection, useTableSearchState} from '@hooks';
 import {context, searchContext} from '../context';
 
-export function useSearch(code: string) {
+export function useSearch(code: string, type: string) {
   const [isSearching] = useTableSearchState(searchContext);
   const dispatch = useContextSection(context, state => state[1]);
 
   function onSearch() {
-    dispatch({type: 'SEARCH', payload: {code}});
+    dispatch({type: 'SEARCH', payload: {code, type}});
   }
 
   return [isSearching, onSearch] as const;

+ 20 - 6
packages/app/src/pages/semi-draw/filter/index.tsx

@@ -1,19 +1,33 @@
-import {FilterField, FilterButtonGroup} from '@components';
+import {FilterField, FilterButtonGroup, FilterSelect} from '@components';
 import {Card, Row} from 'antd';
-import {FC, useState} from 'react';
+import {FC} from 'react';
 import {useSearch} from './hooks';
+import {useFilterField} from '@hooks';
+
+const options = [
+  {label: '全部', value: ''},
+  {label: '未出库', value: '0'},
+  {label: '已出库', value: '1'},
+];
 
 const Filter: FC = function() {
-  const [code, onCodeChange] = useState('');
-  const [isSearching, onSearch] = useSearch(code);
+  const [{code, type}, onChange] = useFilterField({code: '', type: ''});
+  const [isSearching, onSearch] = useSearch(code, type);
 
   return (
     <Card>
       <Row>
-        <FilterField name='semiReportCode' label='生产单号' value={code} onChange={onCodeChange} />
+        <FilterField name='semiReportCode' label='生产单号' value={code} onChange={onChange('code')} />
+        <FilterSelect
+          name='semiReportType'
+          value={type}
+          options={options}
+          onChange={onChange('type')}
+          label='状态'
+        />
 
         <FilterButtonGroup
-          offset={12}
+          offset={6}
           isSearching={isSearching}
           onSearch={onSearch}
         />

+ 61 - 1
packages/app/src/pages/semi-draw/table/hooks.tsx

@@ -1,12 +1,18 @@
 import {useContextSection, useQueryTableList} from '@hooks';
 import {context, pageContext, searchContext} from '../context';
 import {getSemiManufacturesDrawList} from '@apis';
+import {ColumnsType} from 'antd/es/table';
+import {SemiDrawListData} from '@models';
+import {useBoolean} from 'ahooks';
+import {useState} from 'react';
+import {Button} from 'antd';
 
 export function useList() {
-  const params = useContextSection(context, function([{code}]) {
+  const params = useContextSection(context, function([{code, type}]) {
     return {
       partType: '半成品' as const,
       productionCode: code,
+      type,
       askGoodsId: '',
     };
   });
@@ -18,3 +24,57 @@ export function useList() {
     searchContext,
   });
 }
+
+function useClick() {
+  const [visible, {setTrue, setFalse}] = useBoolean();
+  const [id, setId] = useState('');
+
+  function onClick(id: string) {
+    return function() {
+      setId(id);
+      setTrue();
+    };
+  }
+
+  return [{visible, id}, {onClick, onClose: setFalse}] as const;
+}
+
+export function useHandle() {
+  const [{visible, id}, {onClick, onClose}] = useClick();
+
+  const columns: ColumnsType<SemiDrawListData> = [
+    {title: '物料名称', dataIndex: 'materialName', key: 'materialName'},
+    {title: '数量', dataIndex: 'num', key: 'num'},
+    {title: '生产订单号', dataIndex: 'productionCode', key: 'productionCode'},
+    {title: '要料部门', dataIndex: 'department', key: 'department'},
+    {
+      title: '状态',
+      dataIndex: 'type',
+      key: 'type',
+      render(_, {type}) {
+        return type === '0' ? '未出库' : '已出库';
+      },
+    },
+    {
+      title: '操作',
+      dataIndex: 'id',
+      key: 'id',
+      render(_, {id, type}) {
+        return (
+          <>
+            <Button
+              type='link'
+              disabled={type !== '0'}
+              onClick={onClick(id)}
+            >
+              {type === '0' ? '出库' : '已出库'}
+            </Button>
+
+          </>
+        );
+      },
+    },
+  ];
+
+  return [{columns, visible, id}, {onClose}] as const;
+}

+ 24 - 3
packages/app/src/pages/semi-draw/table/index.tsx

@@ -1,9 +1,30 @@
 import {FC} from 'react';
-import {useList} from './hooks';
+import {useHandle, useList} from './hooks';
+import {Card} from 'antd';
+import {Table} from '@components';
+import {pageContext, searchContext} from '../context';
+import PutModal from './modal';
 
 const TableList: FC = function() {
-  useList();
-  return <></>;
+  const [{data, count}, {refetch}] = useList();
+  const [{columns, visible, id}, {onClose}] = useHandle();
+
+  return (
+    <>
+      <Card className='table-wrapper'>
+        <Table
+          data-testid='semi_draw_table'
+          data={data}
+          columns={columns}
+          count={count}
+          pageContext={pageContext}
+          searchContext={searchContext}
+        />
+      </Card>
+
+      <PutModal id={id} onClose={onClose} onFetch={refetch} visible={visible} />
+    </>
+  );
 };
 
 export default TableList;

+ 18 - 0
packages/app/src/pages/semi-draw/table/modal/Info.tsx

@@ -0,0 +1,18 @@
+import {ModalField} from '@components';
+import {FC} from 'react';
+import {useControl, useWatchId} from './hooks';
+
+type Props = {id: string};
+
+const Info: FC<Props> = function({id}) {
+  const control = useControl();
+  useWatchId(id);
+
+  return (
+    <>
+      <ModalField name='semiDrawNum' type='number' control={control} label='出库数量' />
+    </>
+  );
+};
+
+export default Info;

+ 100 - 0
packages/app/src/pages/semi-draw/table/modal/hooks.ts

@@ -0,0 +1,100 @@
+import {getSemiManufacturesDrawInfo, semiManufacturesOut} from '@apis';
+import {yupResolver} from '@hookform/resolvers/yup';
+import {useQueryDataInfo} from '@hooks';
+import {SemiDrawListData} from '@models';
+import {userStore} from '@stores';
+import {useMutation, useQueryClient} from '@tanstack/react-query';
+import {message} from 'antd';
+import {useEffect} from 'react';
+import {useForm, useFormContext} from 'react-hook-form';
+import {number, object} from 'yup';
+import {useStore} from 'zustand';
+
+type FormState = {
+  semiDrawNum: number;
+};
+
+const validate = object({
+  semiDrawNum: number().typeError('请输入数字').min(1, '不能小于1个'),
+});
+
+function useInfoData(id: string) {
+  const client = useQueryClient();
+
+  return function() {
+    return client.getQueryData<SemiDrawListData>([getSemiManufacturesDrawInfo.name, id]);
+  };
+}
+
+function useQuery(onClose: () => void, onFetch: () => void) {
+  return useMutation({
+    mutationFn: semiManufacturesOut,
+    onSuccess({msg}) {
+      if (msg === '200') {
+        onClose();
+        onFetch();
+        message.success('出库成功');
+      }
+    },
+  });
+}
+
+export function useFormState(
+  {visible, id, onClose, onFetch}: {
+    visible: boolean, id: string, onClose: () => void, onFetch: () => void
+  },
+) {
+  const formInstance = useForm<FormState>({
+    defaultValues: {
+      semiDrawNum: 0,
+    },
+    resolver: yupResolver(validate),
+  });
+
+  const {clearErrors, handleSubmit} = formInstance;
+
+  useEffect(function() {
+    visible && clearErrors();
+  }, [visible, clearErrors]);
+
+  const getInfo = useInfoData(id);
+  const {isLoading, mutate} = useQuery(onClose, onFetch);
+  const userId = useStore(userStore, state => String(state.id));
+
+  const onSubmit = handleSubmit(function({semiDrawNum}) {
+    const info = getInfo();
+
+    if (!info) message.error('未获取到信息');
+    else {
+      const {materialId, askGoodsId, materialCode} = info;
+      mutate({
+        materialId,
+        askGoodsId,
+        userId,
+        warehousingNum: String(semiDrawNum),
+        wllbCode: materialCode,
+      });
+    }
+  });
+
+  return [{formInstance, isLoading}, {onSubmit}] as const;
+}
+
+export function useControl() {
+  const {control} = useFormContext<FormState>();
+
+  return control;
+}
+
+export function useWatchId(id: string) {
+  const data = useQueryDataInfo({
+    queryFn: getSemiManufacturesDrawInfo,
+    params: [id],
+    enabled: Boolean(id),
+  });
+  const {setValue} = useFormContext<FormState>();
+
+  useEffect(function() {
+    setValue('semiDrawNum', Number(data?.num ?? '0'));
+  }, [data, setValue]);
+}

+ 37 - 0
packages/app/src/pages/semi-draw/table/modal/index.tsx

@@ -0,0 +1,37 @@
+import {ErrorBoundary, Loading, Modal} from '@components';
+import {FC, Suspense} from 'react';
+import {useFormState} from './hooks';
+import {FormProvider} from 'react-hook-form';
+import Info from './Info';
+
+type Props = {
+  visible: boolean,
+  id: string,
+  onClose: () => void,
+  onFetch: () => void,
+};
+
+const PutModal: FC<Props> = function({visible, id, onClose, onFetch}) {
+  const [{formInstance, isLoading}, {onSubmit}] = useFormState({visible, id, onClose, onFetch});
+
+  return (
+    <Modal
+      visible={visible}
+      title='领料出库'
+      onClose={onClose}
+      onSubmit={onSubmit}
+      testId='semi_draw_modal'
+      isLoading={isLoading}
+    >
+      <FormProvider {...formInstance}>
+        <ErrorBoundary>
+          <Suspense fallback={<Loading tip='正在获取信息' />}>
+            <Info id={id} />
+          </Suspense>
+        </ErrorBoundary>
+      </FormProvider>
+    </Modal>
+  );
+};
+
+export default PutModal;

+ 28 - 0
packages/app/src/pages/semi-out-stream/context.ts

@@ -0,0 +1,28 @@
+import {
+  TableSearchAction,
+  createPageContext,
+  createSearchContext,
+  createTableSearchContext,
+} from '@hooks';
+import {GetWarehousingFlowingListParams, OriginalListParams} from '@models';
+import {Dispatch} from 'react';
+
+export const pageContext = createPageContext();
+export const searchContext = createSearchContext();
+
+export const contextState = {
+  code: '',
+  startTime: '',
+  endTime: '',
+};
+export const context = createTableSearchContext(contextState);
+type State = typeof contextState;
+export function contextSelector(
+  [{code, startTime, endTime}]: [State, Dispatch<TableSearchAction<State>>],
+): OriginalListParams<GetWarehousingFlowingListParams> {
+  return {
+    wllbCode: code,
+    startTime,
+    endTime,
+  };
+}

+ 33 - 0
packages/app/src/pages/semi-out-stream/filter/hooks.ts

@@ -0,0 +1,33 @@
+import {useContextSection, useExportFile, usePage, useTableSearchState} from '@hooks';
+import {context, contextSelector, pageContext, searchContext} from '../context';
+import {exportSemiManufacturesDrawStream} from '@apis';
+
+export function useSearch(code: string, startTime: string, endTime: string) {
+  const [isSearching] = useTableSearchState(searchContext);
+  const dispatch = useContextSection(context, state => state[1]);
+
+  function onSearch() {
+    dispatch({type: 'SEARCH', payload: {startTime, endTime, code}});
+  }
+
+  return [isSearching, onSearch] as const;
+}
+
+export function useExport() {
+  const params = useContextSection(
+    context,
+    contextSelector,
+  );
+  const [isExporting, mutate] = useExportFile(exportSemiManufacturesDrawStream);
+  const [{page, pageSize}] = usePage(pageContext);
+
+  function onExport() {
+    mutate({
+      page: String(page),
+      limit: String(pageSize),
+      ...params,
+    });
+  }
+
+  return [isExporting, onExport] as const;
+}

+ 31 - 0
packages/app/src/pages/semi-out-stream/filter/index.tsx

@@ -0,0 +1,31 @@
+import {FilterButtonGroup, FilterDatePicker, FilterField} from '@components';
+import {useRangeDate} from '@hooks';
+import {Card, Row} from 'antd';
+import {FC, useState} from 'react';
+import {useExport, useSearch} from './hooks';
+
+const Filter: FC = function() {
+  const [code, onCodeChange] = useState('');
+  const [{dates, start, end}, onDatesChange] = useRangeDate();
+  const [isSearching, onSearch] = useSearch(code, start, end);
+  const [isExporting, onExport] = useExport();
+
+  return (
+    <Card>
+      <Row>
+        <FilterField name='semiInCode' label='物料编号' value={code} onChange={onCodeChange} />
+        <FilterDatePicker name='semiInDates' label='出库时间' value={dates} onChange={onDatesChange} />
+
+        <FilterButtonGroup
+          offset={6}
+          isSearching={isSearching}
+          onSearch={onSearch}
+          isExporting={isExporting}
+          onExport={onExport}
+        />
+      </Row>
+    </Card>
+  );
+};
+
+export default Filter;

+ 22 - 0
packages/app/src/pages/semi-out-stream/index.tsx

@@ -0,0 +1,22 @@
+import {PageProvider, SearchProvider, TableSearchProvider} from '@components';
+import {FC} from 'react';
+import {context, contextState, pageContext, searchContext} from './context';
+import Filter from './filter';
+import TableList from './table';
+
+const SemiOutStream: FC = function() {
+  return (
+    <TableSearchProvider context={context} state={contextState}>
+      <PageProvider context={pageContext}>
+        <SearchProvider context={searchContext}>
+          <section className='content-main'>
+            <Filter />
+            <TableList />
+          </section>
+        </SearchProvider>
+      </PageProvider>
+    </TableSearchProvider>
+  );
+};
+
+export default SemiOutStream;

+ 14 - 0
packages/app/src/pages/semi-out-stream/table/hooks.ts

@@ -0,0 +1,14 @@
+import {useContextSection, useQueryTableList} from '@hooks';
+import {context, contextSelector, pageContext, searchContext} from '../context';
+import {getSemiManufacturesDrawStream} from '@apis';
+
+export function useList() {
+  const params = useContextSection(context, contextSelector);
+
+  return useQueryTableList({
+    queryFn: getSemiManufacturesDrawStream,
+    params,
+    pageContext,
+    searchContext,
+  });
+}

+ 36 - 0
packages/app/src/pages/semi-out-stream/table/index.tsx

@@ -0,0 +1,36 @@
+import {Card} from 'antd';
+import {FC} from 'react';
+import {useList} from './hooks';
+import {ColumnsType} from 'antd/es/table';
+import {SemiOutStreamListData} from '@models';
+import {Table} from '@components';
+import {pageContext, searchContext} from '../context';
+
+const columns: ColumnsType<SemiOutStreamListData> = [
+  {title: '物料名称', dataIndex: 'materialName', key: 'materialName'},
+  {title: '出库数量', dataIndex: 'num', key: 'num'},
+  {title: '用户名称', dataIndex: 'userName', key: 'userName'},
+  {title: '出库时间', dataIndex: 'scrq', key: 'scrq'},
+  {title: '要货单', dataIndex: 'askGoodsId', key: 'askGoodsId'},
+  {title: '领用部门', dataIndex: 'department', key: 'department'},
+
+];
+
+const TableList: FC = function() {
+  const [{data, count}] = useList();
+
+  return (
+    <Card className='table-wrapper'>
+      <Table
+        columns={columns}
+        data={data}
+        count={count}
+        pageContext={pageContext}
+        searchContext={searchContext}
+        data-testid='semi_out_stream_table'
+      />
+    </Card>
+  );
+};
+
+export default TableList;

+ 2 - 0
packages/app/src/routes/name.ts

@@ -39,3 +39,5 @@ export const SEMI_REPORT_PATH = '/semi/report';
 export const SEMI_DRAW_PATH = '/semi/draw';
 /** 半成品入库流水 */
 export const SEMI_IN_STREAM = '/stream/semiin';
+/** 半成品出库流水 */
+export const SEMI_OUT_STREAM = '/stream/semiout';

+ 6 - 0
packages/app/src/routes/routes.tsx

@@ -18,6 +18,7 @@ import {
   ROLE_PATH,
   SEMI_DRAW_PATH,
   SEMI_IN_STREAM,
+  SEMI_OUT_STREAM,
   SEMI_REPORT_PATH,
   STORAGE_PATH,
   USER_PATH,
@@ -68,6 +69,10 @@ const SemiInStream = lazy(() => import(
   '@pages/semi-in-stream'
 ));
 const SemiDraw = lazy(() => import(/* webpackChunkName: "semiDraw" */'@pages/semi-draw'));
+const SemiOutStream = lazy(() => import(
+  /* webpackChunkName: "semiOutStream" */
+  '@pages/semi-out-stream'
+));
 
 const routes: RouteObject[] = [
   {
@@ -91,6 +96,7 @@ const routes: RouteObject[] = [
       {path: SEMI_REPORT_PATH, element: <SemiReport />},
       {path: SEMI_DRAW_PATH, element: <SemiDraw />},
       {path: SEMI_IN_STREAM, element: <SemiInStream />},
+      {path: SEMI_OUT_STREAM, element: <SemiOutStream />},
     ],
   },
   {path: LOGIN_PATH, element: <Login />},