Procházet zdrojové kódy

feat: 增加接口日志

xyh před 2 roky
rodič
revize
14d5bf10d1

+ 3 - 2
packages/app/src/apis/dictionary.ts

@@ -37,11 +37,12 @@ export function getDictionaryList(data: GetDictionaryListParams): DictionaryList
 /** 获取字典详情内容 */
 export function getDictionaryInfo(
   type: DictionaryParamsType,
-  id: string,
+  id?: string,
+  code?: string,
 ): BaseListResult<DictionaryData> {
   return request({
     method: 'GET',
-    data: {page: '1', limit: '1', type, tldId: id},
+    data: {page: '1', limit: '1', type, tldId: id, code},
     url: `${BASE_URL}/getDictionaryPage`,
   });
 }

+ 13 - 0
packages/app/src/apis/queryList.ts

@@ -6,6 +6,8 @@ import {
   ReserveWarningListData,
   GetStockListParams,
   StockListData,
+  GetGSInterfaceListParams,
+  GSInterfaceData,
 } from '@models';
 import {request} from './request';
 
@@ -41,3 +43,14 @@ export function getStockList(data: GetStockListParams): BaseListResult<StockList
     data,
   });
 }
+
+/** gs接口日志查询 */
+export function getGSInterfaceList(
+  data: GetGSInterfaceListParams,
+): BaseListResult<GSInterfaceData> {
+  return request({
+    method: 'GET',
+    url: `${BASE_URL}/getAccess`,
+    data,
+  });
+}

+ 3 - 0
packages/app/src/components/modal-field/Field.tsx

@@ -12,6 +12,7 @@ type Props = {
   disabled?: boolean;
   required?: boolean;
   onBlur?: () => void;
+  tip?: string;
 } & UseControllerProps<any, any>;
 
 const OperationField: FC<Props> = function ({
@@ -23,6 +24,7 @@ const OperationField: FC<Props> = function ({
   disabled,
   required,
   onBlur,
+  tip,
 }) {
   return (
     <Row gutter={12}>
@@ -55,6 +57,7 @@ const OperationField: FC<Props> = function ({
                   ref={ref}
                   type={type}
                 />
+                {tip && <p className={css.textTip}>{tip}</p>}
                 <p className={classNames([css.errorTips, {[css.errorTipsHidden]: !errorTips}])}>
                   {errorTips}
                 </p>

+ 3 - 0
packages/app/src/components/modal-field/Select.tsx

@@ -13,6 +13,7 @@ type Props = {
   required?: boolean;
   showSearch?: boolean;
   bindInBody?: boolean;
+  tip?: string;
 } & UseControllerProps<any, any>;
 
 const ModalSelect: FC<Props> = function ({
@@ -24,6 +25,7 @@ const ModalSelect: FC<Props> = function ({
   required = true,
   showSearch,
   bindInBody = true,
+  tip,
 }) {
   return (
     <Row className='full-width' gutter={12}>
@@ -62,6 +64,7 @@ const ModalSelect: FC<Props> = function ({
                     bindInBody ? void 0 : (node: HTMLElement) => node.parentNode as HTMLElement
                   }
                 />
+                {tip && <p className={css.textTip}>{tip}</p>}
                 <p className={classNames([css.errorTips, {[css.errorTipsHidden]: !errorTips}])}>
                   {errorTips}
                 </p>

+ 10 - 2
packages/app/src/components/modal-field/index.module.css

@@ -1,7 +1,8 @@
 .error-tips {
-  height: 14px;
+  min-height: 18px;
   margin: 4px 0 8px;
   font-size: 14px;
+  line-height: 18px;
   color: var(--error-color);
 }
 
@@ -32,7 +33,7 @@
 }
 
 .filed-width {
-  width: 260px;
+  width: 100%;
 }
 
 .area-filed {
@@ -63,3 +64,10 @@
     inset-inline-end: 0 !important;
   }
 }
+
+.text-tip {
+  margin: 4px 0 8px;
+  font-size: 14px;
+  line-height: 18px;
+  color: #999;
+}

+ 1 - 0
packages/app/src/components/table/index.tsx

@@ -30,6 +30,7 @@ const Table: FC<Props<any>> = function <T extends Record<string, any>>(props: Pr
   const [isSearching] = useTableSearchState(searchContext);
   const colWidth = calcColumnsWidth(columns);
   const scrollX = colWidth > 0 ? {x: colWidth} : void 0;
+  console.log(colWidth);
 
   return (
     <OriTable

+ 36 - 2
packages/app/src/hooks/use-options/index.ts

@@ -1,8 +1,14 @@
-import {getAllRoleList, getAllStorage, getAllUser, getDictionaryOptions} from '@apis';
+import {
+  getAllRoleList,
+  getAllStorage,
+  getAllUser,
+  getDictionaryInfo,
+  getDictionaryOptions,
+} from '@apis';
 import {BaseResult, DictionaryData, DictionaryParamsType, StorageListData} from '@models';
 import {useQuery} from '@tanstack/react-query';
 import {useLatest} from 'ahooks';
-import {useMemo, useState} from 'react';
+import {useCallback, useMemo, useState} from 'react';
 import {debounce} from 'lodash-es';
 
 function useQueryOptions<T extends {id: number | string}>(options: {
@@ -153,3 +159,31 @@ export function useDictionarySelect(
 
   return [data, onSearch] as const;
 }
+
+export function useDictionaryBlur(type: DictionaryParamsType, code: () => string) {
+  const [value, setValue] = useState('');
+  const getCode = useLatest(code);
+
+  const onBlur = useCallback(
+    function () {
+      setValue(getCode.current?.());
+    },
+    [getCode],
+  );
+
+  const {data} = useQuery(
+    [getDictionaryInfo.name, value, 'useDictionaryBlur'],
+    async function () {
+      const data = await getDictionaryInfo(type, '', value);
+
+      if (data.msg === '200') {
+        return data.data.list.length ? data.data.list[0].name : '';
+      }
+
+      return '';
+    },
+    {initialData: '', enabled: Boolean(value)},
+  );
+
+  return [data, onBlur] as const;
+}

+ 20 - 0
packages/app/src/models/request/queryList.ts

@@ -21,3 +21,23 @@ export type GetStockListParams = {
   /** 物料tldid */
   materialId: string;
 } & ListParams;
+
+export type GSInterfaceType =
+  | '质检信息'
+  | '字典信息'
+  | '采购信息'
+  | '生产领料信息'
+  | '非生产领料信息'
+  | '报工单'
+  | '交货单'
+  | '移库单'
+  | '出库回传'
+  | '入库回传';
+
+/** 获取gs接口日志 */
+export type GetGSInterfaceListParams = {
+  /** 接口类型 */
+  type: GSInterfaceType;
+  /** 0接入 1接出  */
+  accessType: '0' | '1';
+} & ListParams;

+ 24 - 0
packages/app/src/models/response/queryList.ts

@@ -62,3 +62,27 @@ export type StockListData = {
   /** 物料数量 */
   sum: number;
 };
+
+/** GS接口信息 */
+export type GSInterfaceData = {
+  /**
+   * 0接入1接出
+   */
+  accessType: '0' | '1';
+  /**
+   * 数据
+   */
+  data: string;
+  /**
+   * 主键
+   */
+  id: string;
+  /**
+   * 时间
+   */
+  scrq: string;
+  /**
+   * 类型
+   */
+  type: string;
+};

+ 10 - 0
packages/app/src/pages/gs-interface-log/context.ts

@@ -0,0 +1,10 @@
+import {createPageContext, createSearchContext, createTableSearchContext} from '@hooks';
+import {GSInterfaceType} from '@models';
+
+export const pageContext = createPageContext();
+export const searchContext = createSearchContext();
+export const contextState = {
+  type: '' as GSInterfaceType,
+  accessType: '' as '0' | '1',
+};
+export const context = createTableSearchContext(contextState);

+ 16 - 0
packages/app/src/pages/gs-interface-log/filter/hooks.ts

@@ -0,0 +1,16 @@
+import {useContextSection} from '@hooks';
+import {context} from '../context';
+import {GSInterfaceType} from '@models';
+
+export function useSearch(type: string, accessType: string) {
+  const dispatch = useContextSection(context, state => state[1]);
+
+  function onSearch() {
+    dispatch({
+      type: 'SEARCH',
+      payload: {type: type as GSInterfaceType, accessType: accessType as '0' | '1'},
+    });
+  }
+
+  return onSearch;
+}

+ 50 - 0
packages/app/src/pages/gs-interface-log/filter/index.tsx

@@ -0,0 +1,50 @@
+import {FilterButtonGroup, FilterSelect} from '@components';
+import {useContextSection, useFilterField} from '@hooks';
+import {Card, Row} from 'antd';
+import {FC} from 'react';
+import {searchContext} from '../context';
+import {useSearch} from './hooks';
+
+const interfaceOptions = [
+  {label: '全部', value: ''},
+  {label: '质检信息', value: '质检信息'},
+  {label: '字典信息', value: '字典信息'},
+  {label: '采购信息', value: '采购信息'},
+  {label: '生产领料信息', value: '生产领料信息'},
+  {label: '非生产领料信息', value: '非生产领料信息'},
+  {label: '报工单', value: '报工单'},
+  {label: '交货单', value: '交货单'},
+  {label: '移库单', value: '移库单'},
+  {label: '入库回传', value: '入库回传'},
+  {label: '出库回传', value: '出库回传'},
+];
+
+const Filter: FC = function () {
+  const [{type, accessType}, onChange] = useFilterField({type: '', accessType: ''});
+  const {refetch, isSearching} = useContextSection(searchContext, state => state[0]);
+  const onSearch = useSearch(type, accessType);
+
+  return (
+    <Card>
+      <Row>
+        <FilterSelect
+          name='dictionaryName'
+          label='接口名称'
+          value={type}
+          onChange={onChange('type')}
+          options={interfaceOptions}
+          showSearch
+        />
+
+        <FilterButtonGroup
+          isSearching={isSearching}
+          onSearch={onSearch}
+          onRefresh={refetch}
+          offset={12}
+        />
+      </Row>
+    </Card>
+  );
+};
+
+export default Filter;

+ 22 - 0
packages/app/src/pages/gs-interface-log/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 GSInterfaceLog: 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 GSInterfaceLog;

+ 14 - 0
packages/app/src/pages/gs-interface-log/table/hooks.ts

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

+ 41 - 0
packages/app/src/pages/gs-interface-log/table/index.tsx

@@ -0,0 +1,41 @@
+import {Table} from '@components';
+import {Card} from 'antd';
+import {FC} from 'react';
+import {useList} from './hooks';
+import {ColumnsType} from 'antd/es/table';
+import {GSInterfaceData} from '@models';
+import {MAXIMAL_TABLE_WIDTH, MIDDLE_TABLE_WIDTH, NORMAL_TABLE_WIDTH} from '@utils';
+import {pageContext, searchContext} from '../context';
+
+const columns: ColumnsType<GSInterfaceData> = [
+  {title: '接口名称', dataIndex: 'type', key: 'type', width: NORMAL_TABLE_WIDTH},
+  {title: '接口参数', dataIndex: 'data', key: 'data', width: MAXIMAL_TABLE_WIDTH},
+  {
+    title: '接口类型',
+    dataIndex: 'accessType',
+    key: 'accessType',
+    width: NORMAL_TABLE_WIDTH,
+    render(_, {accessType}) {
+      return accessType === '1' ? '接出' : '接入';
+    },
+  },
+  {title: '接口时间', dataIndex: 'scrq', key: 'scrq', width: MIDDLE_TABLE_WIDTH},
+];
+
+const TableList: FC = function () {
+  const [{data, count}] = useList();
+
+  return (
+    <Card className='table-wrapper'>
+      <Table
+        columns={columns}
+        data={data}
+        count={count}
+        pageContext={pageContext}
+        searchContext={searchContext}
+      />
+    </Card>
+  );
+};
+
+export default TableList;

+ 7 - 7
packages/app/src/pages/matter/table/modal/Info.tsx

@@ -1,19 +1,19 @@
-import {ModalSelect} from '@components';
+import {ModalField, ModalSelect} from '@components';
 import {FC} from 'react';
-import {useControl, useWatchId} from './hooks';
-import {useDictionaryOptions, useStorageOptions} from '@hooks';
+import {useFormContextState, useWatchId} from './hooks';
+import {useDictionaryBlur, useStorageOptions} from '@hooks';
 
 type Props = {id: string};
 
 const Info: FC<Props> = function ({id}) {
-  const control = useControl();
-  const matterOtions = useDictionaryOptions('物料字典');
+  const {control, getValues} = useFormContextState();
   const storageOtions = useStorageOptions(false, state => state.storageLocationCode);
-  useWatchId(id);
+  const [tip, onBlur] = useDictionaryBlur('物料字典', () => getValues('wllbCode'));
+  useWatchId(id, onBlur);
 
   return (
     <>
-      <ModalSelect name='wllbCode' label='物料' control={control} data={matterOtions} showSearch />
+      <ModalField name='wllbCode' label='物料编号' control={control} tip={tip} onBlur={onBlur} />
       <ModalSelect
         name='storageLocationCode'
         label='库位'

+ 5 - 6
packages/app/src/pages/matter/table/modal/hooks.ts

@@ -58,13 +58,11 @@ export function useFormState({
   return [{formInstance, isLoading}, {onSubmit}] as const;
 }
 
-export function useControl() {
-  const {control} = useFormContext<FormState>();
-
-  return control;
+export function useFormContextState() {
+  return useFormContext<FormState>();
 }
 
-export function useWatchId(id: string) {
+export function useWatchId(id: string, onBlur: () => void) {
   const {setValue} = useFormContext<FormState>();
 
   const data = useQueryDataInfo({
@@ -77,7 +75,8 @@ export function useWatchId(id: string) {
     function () {
       setValue('wllbCode', data?.wllbCode ?? '');
       setValue('storageLocationCode', data?.storageLocationCode ?? '');
+      onBlur();
     },
-    [data, setValue],
+    [data, onBlur, setValue],
   );
 }

+ 3 - 0
packages/app/src/routes/index.tsx

@@ -34,6 +34,7 @@ import {
   STOCK_PATH,
   MATERIAL_PRINT_PATH,
   DICTIONARY_PATH,
+  GS_INTERFACE_LOG_PATH,
 } from './name';
 import {
   Container,
@@ -62,6 +63,7 @@ import {
   PurchaseOrderTimeout,
   Stock,
   Dictionary,
+  GSInterfaceLog,
 } from './routes';
 import DeadProduct from '@pages/dead-product';
 import PurchaseOrder from '@pages/purchase-order';
@@ -102,6 +104,7 @@ export const routes: RouteObject[] = [
       {path: STOCK_PATH, element: <Stock />},
       {path: MATERIAL_PRINT_PATH, element: <MaterialPrint />},
       {path: DICTIONARY_PATH, element: <Dictionary />},
+      {path: GS_INTERFACE_LOG_PATH, element: <GSInterfaceLog />},
     ],
   },
   {path: LOGIN_PATH, element: <Login />},

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

@@ -87,3 +87,6 @@ export type DictionaryParams =
   | 'type';
 /** 字典页面 */
 export const DICTIONARY_PATH = '/dictionary/:type';
+
+/** gs接口日志 */
+export const GS_INTERFACE_LOG_PATH = '/log/gsinterface';

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

@@ -192,3 +192,6 @@ export const MateriamPrint = lazy(
 export const Dictionary = lazy(
   () => import(/*webpackChunkName: "dicionary" */ '@pages/dictionary'),
 );
+export const GSInterfaceLog = lazy(
+  () => import(/* webpackChunkName: "gsInterfaceLog" */ '@pages/gs-interface-log'),
+);

+ 5 - 1
packages/app/src/styles/iconfont.css

@@ -1,6 +1,6 @@
 @font-face {
   font-family: 'iconfont'; /* Project id 3960504 */
-  src: url('iconfont.ttf?t=1679364216910') format('truetype');
+  src: url('iconfont.ttf?t=1679625075392') format('truetype');
 }
 
 .iconfont {
@@ -11,6 +11,10 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.gongzuoguanli::before {
+  content: '\e7ca';
+}
+
 .fenxibaobiao::before {
   content: '\e7c8';
 }

binární
packages/app/src/styles/iconfont.ttf


+ 4 - 0
packages/app/src/styles/index.css

@@ -167,3 +167,7 @@ img {
   display: flex;
   flex-direction: column;
 }
+
+.ant-table-cell {
+  overflow-wrap: anywhere !important;
+}

+ 1 - 1
packages/app/src/utils/constants.ts

@@ -12,7 +12,7 @@ export const MODAL_PAGE_SIZE_LIST = ['5', ...PAGE_SIZE_LIST];
 export const NETWORK_URL =
   process.env.NODE_ENV === 'development'
     ? // 147
-      'http://192.168.118:9560'
+      'http://192.168.147:9560'
     : 'http://8.142.144.205:9560';
 export const E2E_NETWORK_URL = 'http://e2e.test.cn';
 /** 导出错误提示 */