shaogen1995 11 месяцев назад
Родитель
Сommit
24884e3760

+ 36 - 7
src/components/UpXlsx.tsx

@@ -1,4 +1,5 @@
 import { API_upFile } from '@/store/action/layout'
+import { A2AListCamerasType } from '@/types'
 import { fileDomInitialFu } from '@/utils/domShow'
 import { MessageFu } from '@/utils/message'
 import { notification } from 'antd'
@@ -7,11 +8,13 @@ import { forwardRef, useImperativeHandle } from 'react'
 
 type Props = {
   url: string
-  xlsxResInfoFu: (info: any) => void
+  xlsxResInfoFu: (arr: A2AListCamerasType[]) => void
   ref: any //当前自己的ref,给父组件调用
+  dirCode: string
+  existSn: string[] //cameraSn集合,用来过滤表格
 }
 
-function UpXlsx({ url, xlsxResInfoFu }: Props, ref: any) {
+function UpXlsx({ url, xlsxResInfoFu, dirCode, existSn }: Props, ref: any) {
   const [api, contextHolder] = notification.useNotification()
 
   // 点击上传附件按钮
@@ -36,7 +39,7 @@ function UpXlsx({ url, xlsxResInfoFu }: Props, ref: any) {
         const fd = new FormData()
         // 把files添加进FormData对象(‘photo’为后端需要的字段)
         fd.append('type', 'doc')
-        fd.append('dirCode', 'A1list')
+        fd.append('dirCode', dirCode)
         fd.append('file', filesInfo)
         fd.append('isDb', 'false')
 
@@ -48,13 +51,39 @@ function UpXlsx({ url, xlsxResInfoFu }: Props, ref: any) {
           if (res.code === 0) {
             // MessageFu.success('上传成功!')
             const { msg, error } = res.data
+
+            const errorTit = error && error.length ? JSON.stringify(error) : ''
+
             api.info({
               message: msg,
-              description: JSON.stringify(error || ''),
+              description: errorTit,
               duration: 0,
-              placement: 'top'
+              placement: 'topLeft'
             })
-            xlsxResInfoFu(res.data)
+
+            const arr = res.data.camera || []
+            const temp: string[] = []
+            const arrRes: A2AListCamerasType[] = []
+
+            arr.forEach((v: any, i: number) => {
+              if (v.id) v.idxx = v.id
+              else v.idxx = Date.now() - i * 100
+              if (existSn.includes(v.cameraSn)) {
+                temp.push(v.cameraSn)
+              } else {
+                arrRes.push(v)
+              }
+            })
+
+            xlsxResInfoFu(arrRes)
+
+            if (temp.length) {
+              api.warning({
+                message: 'SN码' + JSON.stringify(temp) + '重复,已过滤',
+                duration: 0,
+                placement: 'top'
+              })
+            }
           }
           fileDomInitialFu()
         } catch (error) {
@@ -62,7 +91,7 @@ function UpXlsx({ url, xlsxResInfoFu }: Props, ref: any) {
         }
       }
     },
-    [api, url, xlsxResInfoFu]
+    [api, dirCode, existSn, url, xlsxResInfoFu]
   )
 
   const myInputClickFu = useCallback(() => {

+ 8 - 14
src/components/ZRichTexts/index.tsx

@@ -46,14 +46,10 @@ function ZRichTexts({ check, dirCode, isLook, myUrl, topType }: Props, ref: any)
   const isTxtFlag = useMemo(() => {
     let flag = false
 
-    // 不是按章节发布,检查第一个富文本
-    const txt = sectionArr[0].txt.toHTML()
-
-    const txtRes = txt.replaceAll(' ', '').replaceAll('</p><p>', '')
-
-    if (txtRes === '') flag = true
-
-    if (txtRes.split('></p>').length - 1 === 1 && !txtRes.includes('class="media-')) flag = true
+    const txt = sectionArr[0].txt.toText()
+    const txtHtml = sectionArr[0].txt.toHTML()
+    const txtRes = txt.replaceAll('\n', '').replaceAll(' ', '')
+    if (!txtRes && !txtHtml.includes('class="media-wrap')) flag = true
 
     return flag
   }, [sectionArr])
@@ -157,12 +153,10 @@ function ZRichTexts({ check, dirCode, isLook, myUrl, topType }: Props, ref: any)
   // 单个富文本是否输入完整
   const isOneTxtFlag = useCallback((txt: any) => {
     let flag = false
-    const txtRes: string = txt.toHTML()
-    const txtRes2 = txtRes.replaceAll(' ', '').replaceAll('</p><p>', '')
-
-    if (txtRes2 === '') flag = true
-
-    if (txtRes2.split('></p>').length - 1 === 1 && !txtRes2.includes('class="media-')) flag = true
+    const txt2 = txt.toText()
+    const txtHtml = txt.toHTML()
+    const txtRes = txt2.replaceAll('\n', '').replaceAll(' ', '')
+    if (!txtRes && !txtHtml.includes('class="media-wrap')) flag = true
 
     return flag
   }, [])

+ 7 - 1
src/pages/A1Camera/index.tsx

@@ -416,7 +416,13 @@ function A1Camera() {
           <a href='/xlsx/相机列表导入模板.xlsx' download>
             <Button type='primary'>下载导入模块</Button>
           </a>
-          <UpXlsx url='cms/camera/upload/excel' ref={upXlsxRef} xlsxResInfoFu={() => getListFu()} />
+          <UpXlsx
+            url='cms/camera/upload/excel'
+            ref={upXlsxRef}
+            xlsxResInfoFu={() => getListFu()}
+            dirCode='cameraXlsx'
+            existSn={[]}
+          />
           {/* 上传xlsx */}
           <Button type='primary' onClick={() => upXlsxRef.current?.myInputClickFu()}>
             导入表格

+ 3 - 9
src/pages/A2Abusiness/AddBusiness/index.tsx

@@ -40,14 +40,6 @@ function AddBusiness({ topType, openInfo, closeFu, upTableFu, addTableFu }: Prop
   // 点击表格里面新增的数据
   const [tableAdd, setTableAdd] = useState<A1addType>({ id: 0, txt: '' })
 
-  const getListFu = useCallback(
-    (info: any) => {
-      const arr = info.camera || []
-      setTableList([...arr, ...tableList])
-    },
-    [tableList]
-  )
-
   // 表格点击删除
   const delTableFu = useCallback(
     (idxx: string) => {
@@ -382,7 +374,9 @@ function AddBusiness({ topType, openInfo, closeFu, upTableFu, addTableFu }: Prop
               <UpXlsx
                 url='cms/order/upload/excel'
                 ref={upXlsxRef}
-                xlsxResInfoFu={info => getListFu(info)}
+                xlsxResInfoFu={arr => setTableList([...arr, ...tableList])}
+                dirCode='businessXlsx'
+                existSn={tableList.map(v => v.cameraSn)}
               />
 
               <Button type='primary' onClick={() => upXlsxRef.current?.myInputClickFu()}>

+ 13 - 3
src/pages/A2Abusiness/index.tsx

@@ -1,7 +1,13 @@
 import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
 import { useDispatch, useSelector } from 'react-redux'
-import { A2AFromDataType, A2AtopTypeArr, TopTypeType } from './data'
+import {
+  A2AFromDataType,
+  A2AtopTypeArr,
+  A2AtopTypeEditLabel,
+  A2AtopTypeObj,
+  TopTypeType
+} from './data'
 import { A2A_APIgetlist, A2A_APIgetlistDerive, A2A_APIremove } from '@/store/action/A2Abusiness'
 import { RootState } from '@/store'
 import { MessageFu } from '@/utils/message'
@@ -304,6 +310,10 @@ function A2Abusiness() {
     }
     setTableSelect({ ...tableSelect, dateStart, dateEnd })
   }
+  // 第二个信息标题
+  const tit2 = useMemo(() => {
+    return Reflect.get(A2AtopTypeObj, topType)
+  }, [topType])
 
   return (
     <div className={styles.A2Abusiness}>
@@ -351,12 +361,12 @@ function A2Abusiness() {
           </div>
 
           <div className='A2AtopRow'>
-            <span>租赁日期:</span>
+            <span>{A2AtopTypeEditLabel(topType)}日期:</span>
             <RangePicker key={inputKey} onChange={timeChange} />
           </div>
 
           <div className='A2AtopRow'>
-            <span>租赁方名称:</span>
+            <span>{tit2}名称:</span>
             <Input
               key={inputKey}
               maxLength={20}

+ 111 - 0
src/pages/A2Blogistics/AddLogistics/index.module.scss

@@ -0,0 +1,111 @@
+.AddLogistics {
+  position: absolute;
+  z-index: 10;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: #fff;
+  border-radius: 10px;
+  padding: 24px;
+
+  :global {
+    .A2BaddMain {
+      overflow-y: auto;
+      width: 100%;
+      height: 100%;
+
+      .A2Btit {
+        width: calc(100% - 30px);
+        font-weight: 700;
+        color: var(--themeColor);
+        font-size: 18px;
+        padding: 12px 0;
+        border-top: 1px solid #ccc;
+      }
+
+      .A2Btit2 {
+        & > div {
+          width: 1000px;
+          display: flex;
+          justify-content: space-between;
+          .ant-btn {
+            margin-left: 15px;
+          }
+        }
+      }
+      .A2Btit3 {
+        padding: 24px 0;
+      }
+      .tableListBox {
+        width: 1000px;
+        position: relative;
+        padding-bottom: 40px;
+        .tableNumBox {
+          right: 0 !important;
+          bottom: 10px !important;
+        }
+      }
+
+      .ant-form {
+        // width: 800px;
+        .ant-row {
+          width: 1000px;
+          .ant-form-item-label {
+            width: 110px;
+          }
+        }
+      }
+      .A2BaddBtn {
+        position: absolute;
+        top: 50%;
+        left: 1145px;
+        transform: translateY(-50%);
+        .ant-row {
+          width: auto;
+        }
+      }
+      .formBox {
+        display: flex;
+        margin-bottom: 24px;
+        width: 1000px;
+        .formBoxLL {
+          width: 110px;
+          text-align: right;
+          & > span {
+            color: #ff4d4f;
+          }
+        }
+        .formBoxRR {
+          width: calc(100% - 110px);
+          .ant-btn {
+            margin-right: 20px;
+          }
+        }
+      }
+      .formBox2 {
+        margin-bottom: -10px;
+        .formBoxLL {
+          position: relative;
+          top: 4px;
+        }
+      }
+    }
+    .A2BtableBoxAdd {
+      position: absolute;
+      top: 0;
+      left: 0;
+      z-index: 100;
+      width: 100%;
+      height: 100%;
+      padding: 100px 200px;
+      background-color: rgba(0, 0, 0, 0.6);
+      border-radius: 10px;
+      & > div {
+        position: relative;
+        width: 100%;
+        height: 100%;
+      }
+    }
+  }
+}

+ 284 - 0
src/pages/A2Blogistics/AddLogistics/index.tsx

@@ -0,0 +1,284 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { A2BaddType } from '../data'
+import { A2AListCamerasType } from '@/types'
+import { A1addType } from '@/pages/A1Camera/data'
+import { Button, DatePicker, Form, FormInstance, Input, Table, Tag, Tooltip } from 'antd'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { A2B_APIadd, A2B_APIgetInfo } from '@/store/action/A2Blogistics'
+import dayjs from 'dayjs'
+import { MessageFu } from '@/utils/message'
+import UpXlsx from '@/components/UpXlsx'
+import AddCamera from '@/pages/A1Camera/AddCamera'
+import TextArea from 'antd/lib/input/TextArea'
+
+type Props = {
+  openInfo: A2BaddType
+  closeFu: () => void
+  upTableFu: () => void
+  addTableFu: () => void
+  orderNumTemp?: string //当从业务订单详情中,点击“新增出库“”新增入库“时,需带入业务订单编号
+}
+
+function AddLogistics({ openInfo, closeFu, upTableFu, addTableFu, orderNumTemp }: Props) {
+  // 关联业务订单
+  const [orderNum, setOrderNum] = useState('')
+
+  useEffect(() => {
+    if (orderNumTemp) setOrderNum(orderNumTemp)
+  }, [orderNumTemp])
+
+  // 表格数据
+  const [tableList, setTableList] = useState<A2AListCamerasType[]>([])
+
+  // 点击表格里面新增的数据
+  const [tableAdd, setTableAdd] = useState<A1addType>({ id: 0, txt: '' })
+
+  // 表格点击删除
+  const delTableFu = useCallback(
+    (idxx: string) => {
+      setTableList(tableList.filter(v => v.idxx !== idxx))
+    },
+    [tableList]
+  )
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: '相机类型',
+        render: (item: A2AListCamerasType) =>
+          item.cameraType ? (item.cameraType === 'KK' ? '看看' : '看见') : '(空)'
+      },
+      {
+        title: 'SN码',
+        render: (item: A2AListCamerasType) => (
+          <>
+            {item.cameraSn}
+            {item.id ? (
+              ''
+            ) : (
+              <Tooltip title='此SN编码未录入在相机列表中'>
+                &nbsp;
+                <Tag>new</Tag>
+              </Tooltip>
+            )}
+          </>
+        )
+      },
+      {
+        title: '备注',
+        render: (item: A2AListCamerasType) => item.remark || '(空)'
+      },
+      {
+        title: '操作',
+        render: (item: A2AListCamerasType) => (
+          <MyPopconfirm txtK='删除' onConfirm={() => delTableFu(item.idxx)} />
+        )
+      }
+    ]
+  }, [delTableFu])
+
+  // 表单的ref
+  const FormBoxRef = useRef<FormInstance>(null)
+
+  // 通过id获取详情
+  const getInfoFu = useCallback(async (id: number) => {
+    const res = await A2B_APIgetInfo(id)
+    if (res.code === 0) {
+      const data = res.data
+
+      // 回显表格
+      setTableList(
+        data.cameras.map((v: any) => ({
+          ...v,
+          idxx: v.id
+        }))
+      )
+
+      const obj = {
+        ...data,
+        city: data.zlProvince + '-' + data.zlCity,
+        dateStart: data.dateStart ? dayjs(data.dateStart) : null
+      }
+
+      FormBoxRef.current?.setFieldsValue(obj)
+    }
+  }, [])
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {}, [])
+
+  // 通过校验点击确定
+  const onFinish = useCallback(
+    async (value: any) => {
+      // 日期处理
+      let dateStart = ''
+
+      if (value.dateStart) dateStart = dayjs(value.dateStart).format('YYYY-MM-DD')
+
+      const obj = {
+        ...value,
+        id: openInfo.txt === '新增' ? null : openInfo.id,
+        cameras: tableList,
+        dateStart,
+        type: openInfo.type,
+        orderNum
+      }
+
+      // if (1 + 1 === 2) {
+      //   console.log(obj)
+
+      //   return
+      // }
+
+      const res = await A2B_APIadd(obj)
+      if (res.code === 0) {
+        MessageFu.success(openInfo.txt + '成功!')
+        openInfo.txt === '新增' ? addTableFu() : upTableFu()
+        closeFu()
+      }
+    },
+    [addTableFu, closeFu, openInfo.id, openInfo.txt, openInfo.type, orderNum, tableList, upTableFu]
+  )
+
+  useEffect(() => {
+    if (openInfo.txt === '新增') {
+    } else getInfoFu(openInfo.id)
+  }, [getInfoFu, openInfo])
+
+  // 上传xlsx 的ref
+  const upXlsxRef = useRef<any>(null)
+
+  return (
+    <div className={styles.AddLogistics}>
+      <div className='A2BaddMain'>
+        <Form
+          scrollToFirstError={true}
+          ref={FormBoxRef}
+          // labelCol={{ span: 4 }}
+          name='basicWai'
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete='off'
+        >
+          <div className='A2Btit'>订单信息</div>
+
+          <Form.Item
+            label={openInfo.type === 'CK' ? '发货日期' : '收货日期'}
+            name='dateStart'
+            rules={[{ required: true, message: '请选择日期!' }]}
+          >
+            <DatePicker placeholder='请选择日期' />
+          </Form.Item>
+
+          <Form.Item label='收货地址' name='address'>
+            <Input maxLength={50} showCount placeholder='请输入内容' />
+          </Form.Item>
+
+          <Form.Item
+            label='物流编号'
+            name='logisticsNum'
+            rules={[{ required: true, message: '请输入内容!' }]}
+            getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
+          >
+            <Input maxLength={20} showCount placeholder='请输入内容' />
+          </Form.Item>
+
+          <Form.Item label='备注' name='rtf'>
+            <TextArea maxLength={200} showCount placeholder='请输入内容' />
+          </Form.Item>
+
+          <div className='A2Btit A2Btit3'>业务订单</div>
+
+          <div className='formBox'>
+            <div className='formBoxLL'>关联业务订单:</div>
+            <div className='formBoxRR'>
+              <Input
+                value={orderNum}
+                onChange={e => setOrderNum(e.target.value.replace(/\s+/g, ''))}
+                maxLength={12}
+                showCount
+                placeholder='请输入内容'
+              />
+            </div>
+          </div>
+
+          {/* 确定和取消按钮 */}
+          <Form.Item className='A2BaddBtn'>
+            <Button type='primary' htmlType='submit'>
+              提交
+            </Button>
+            <br />
+            <br />
+            <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+          </Form.Item>
+        </Form>
+
+        <div className='A2Btit A2Btit2'>
+          <div>
+            <div>相机清单</div>
+            <div>
+              <a href='/xlsx/相机列表导入模板.xlsx' download>
+                <Button type='primary'>下载模块</Button>
+              </a>
+              <UpXlsx
+                url='cms/logistics/upload/excel'
+                ref={upXlsxRef}
+                xlsxResInfoFu={arr => setTableList([...arr, ...tableList])}
+                dirCode='logisticsXlsx'
+                existSn={tableList.map(v => v.cameraSn)}
+              />
+
+              <Button type='primary' onClick={() => upXlsxRef.current?.myInputClickFu()}>
+                批量导入
+              </Button>
+              <Button
+                type='primary'
+                onClick={() => {
+                  if (tableList.length >= 200) return MessageFu.warning('最多支持200条数据!')
+                  setTableAdd({ id: -1, txt: '新增' })
+                }}
+              >
+                新增
+              </Button>
+            </div>
+          </div>
+        </div>
+
+        {/* --------------表格-------------- */}
+        <div className='tableListBox'>
+          <Table
+            size='small'
+            dataSource={tableList}
+            columns={columns}
+            rowKey='idxx'
+            pagination={false}
+          />
+          {/*  右下角的列表数量 */}
+          <div className='tableNumBox'>
+            共 <span>{tableList.length}</span> 条数据
+          </div>
+        </div>
+      </div>
+      {/* 点击表格里面的新增 出来的页面 */}
+      {tableAdd.id ? (
+        <div className='A2BtableBoxAdd'>
+          <div>
+            <AddCamera
+              isLoc={true}
+              existSn={tableList.map(v => v.cameraSn)}
+              openInfo={tableAdd}
+              closeFu={() => setTableAdd({ id: 0, txt: '' })}
+              upTableFu={() => {}}
+              addTableFu={info => setTableList([info, ...tableList])}
+            />
+          </div>
+        </div>
+      ) : null}
+    </div>
+  )
+}
+
+const MemoAddLogistics = React.memo(AddLogistics)
+
+export default MemoAddLogistics

+ 29 - 0
src/pages/A2Blogistics/data.tsx

@@ -0,0 +1,29 @@
+import { TopTypeType } from '../A2Abusiness/data'
+
+export type A2BFromTypeType = 'CK' | 'RK'
+
+export type A2BFromDataType = {
+  num: string //订单编号
+  type: A2BFromTypeType | '' //类型, CK:出库 | RK:入库
+  orderNum: string //关联业务订单号
+  orderType: TopTypeType | '' //关联业务类型; ZL:租赁 | XS:销售 | BX:报修 | DS:定损 | WX:维修
+
+  dateStart: string //发货开始日期 - yyyy-mm-dd
+  dateEnd: string //收货结束日期 - yyyy-mm-dd
+
+  pageSize: number
+  pageNum: number
+}
+
+export const A2BFromTypeArr: { value: A2BFromTypeType | ''; label: string }[] = [
+  { label: '全部', value: '' },
+  { label: '出库', value: 'CK' },
+  { label: '入库', value: 'RK' }
+]
+
+// 新增编辑的 type
+export type A2BaddType = {
+  id: number
+  txt: '' | '新增' | '编辑'
+  type: A2BFromTypeType | ''
+}

+ 45 - 0
src/pages/A2Blogistics/index.module.scss

@@ -1,4 +1,49 @@
 .A2Blogistics {
+  position: relative;
   :global {
+    .A2Btop {
+      border-radius: 10px;
+      background-color: #fff;
+      padding: 15px 24px 1px 24px;
+
+      .A2BtopSon {
+        display: flex;
+        margin-bottom: 15px;
+
+        .ant-btn {
+          margin-right: 15px;
+        }
+
+        .A2BtopRow {
+          margin-right: 20px;
+        }
+      }
+      .A2BtopSon2 {
+        justify-content: space-between;
+        & > div {
+          display: flex;
+        }
+        .ant-btn {
+          margin-left: 15px;
+          margin-right: 0px;
+        }
+      }
+    }
+
+    .tableMain {
+      border-radius: 10px;
+      margin-top: 15px;
+      height: calc(100% - 128px);
+      background-color: #fff;
+
+      .ant-table-body {
+        height: 580px;
+        overflow-y: auto !important;
+        overflow-y: overlay !important;
+        .ant-table-cell {
+          padding: 8px !important;
+        }
+      }
+    }
   }
 }

+ 390 - 2
src/pages/A2Blogistics/index.tsx

@@ -1,9 +1,397 @@
-import React from 'react'
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
+import { Button, DatePicker, Input, Select, Table } from 'antd'
+import { A2BaddType, A2BFromDataType, A2BFromTypeArr } from './data'
+import { useDispatch, useSelector } from 'react-redux'
+import { A2B_APIgetlist, A2B_APIgetlistDerive, A2B_APIremove } from '@/store/action/A2Blogistics'
+import ExportJsonExcel from 'js-export-excel'
+import { RootState } from '@/store'
+import { MessageFu } from '@/utils/message'
+import dayjs from 'dayjs'
+import { A2BListType } from '@/types'
+import history from '@/utils/history'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { A2AtopTypeArr } from '../A2Abusiness/data'
+import AddLogistics from './AddLogistics'
+
+const tableSelectBase: A2BFromDataType = {
+  num: '',
+  type: '',
+  orderNum: '',
+  orderType: '',
+  dateStart: '',
+  dateEnd: '',
+  pageSize: 10,
+  pageNum: 1
+}
+
 function A2Blogistics() {
+  const dispatch = useDispatch()
+
+  // 筛选和分页
+  const [tableSelect, setTableSelect] = useState(tableSelectBase)
+
+  const tableSelectRef = useRef({} as A2BFromDataType)
+
+  useEffect(() => {
+    tableSelectRef.current = { ...tableSelect }
+  }, [tableSelect])
+
+  // 点击搜索的 时间戳
+  const [timeKey, setTimeKey] = useState(-1)
+
+  // 发送接口的函数
+  const getListFu = useCallback(() => {
+    const obj = {
+      ...tableSelectRef.current
+    }
+    dispatch(A2B_APIgetlist(obj))
+  }, [dispatch])
+
+  useEffect(() => {
+    getListFu()
+  }, [getListFu, timeKey])
+
+  // 输入框的改变
+  const txtChangeFu = useCallback(
+    (txt: string, key: 'num' | 'orderNum') => {
+      setTableSelect({ ...tableSelect, [key]: txt })
+    },
+    [tableSelect]
+  )
+
+  // 点击搜索
+  const clickSearch = useCallback(() => {
+    setTableSelect({ ...tableSelect, pageNum: 1 })
+    setTimeout(() => {
+      setTimeKey(Date.now())
+    }, 50)
+  }, [tableSelect])
+
+  // 点击重置
+  const [inputKey, setInputKey] = useState(1)
+  const resetSelectFu = useCallback(() => {
+    // 把2个输入框和时间选择器清空
+    setInputKey(Date.now())
+    setTableSelect(tableSelectBase)
+    setTimeout(() => {
+      setTimeKey(Date.now())
+    }, 50)
+  }, [])
+
+  // 2个日期选择框的改变
+  const timeChange1 = useCallback(
+    (_: any, dateString: string) => {
+      let dateStart = ''
+      if (dateString) dateStart = dateString + ' 00:00:00'
+      setTableSelect({ ...tableSelect, dateStart })
+    },
+    [tableSelect]
+  )
+
+  const timeChange2 = useCallback(
+    (_: any, dateString: string) => {
+      let dateEnd = ''
+      if (dateString) dateEnd = dateString + ' 23:59:59'
+      setTableSelect({ ...tableSelect, dateEnd })
+    },
+    [tableSelect]
+  )
+
+  // 从仓库获取列表
+  const A2BTableList = useSelector((state: RootState) => state.A2Blogistics.A2BTableList)
+  // 页码变化
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      setTableSelect({ ...tableSelect, pageNum, pageSize })
+      setTimeout(() => {
+        setTimeKey(Date.now())
+      }, 50)
+    },
+    [tableSelect]
+  )
+
+  // 点击删除
+  const delByIdFu = useCallback(
+    async (id: number) => {
+      const res = await A2B_APIremove(id)
+      if (res.code === 0) {
+        MessageFu.success('删除成功!')
+        getListFu()
+      }
+    },
+    [getListFu]
+  )
+
+  // 点击导出
+  const deriveFu = useCallback(async () => {
+    if (A2BTableList.total > 30000)
+      return MessageFu.warning('只支持导出最多30000条数据。请增加筛选条件,并重新尝试')
+
+    if (A2BTableList.list.length === 0) return MessageFu.warning('当前搜索条件没有数据!')
+    const name = '物流订单' + dayjs(new Date()).format('YYYY-MM-DD HH:mm')
+
+    const res = await A2B_APIgetlistDerive({
+      ...tableSelect,
+      pageNum: 1,
+      pageSize: 99999
+    })
+
+    if (res.code === 0) {
+      if (res.data.records.length <= 0) return MessageFu.warning('当前搜索条件没有数据!')
+
+      const option = {
+        fileName: name,
+        datas: [
+          {
+            sheetData: res.data.records.map((item: A2BListType) => ({
+              num: item.num || '空',
+              creatorName: item.creatorName || '空',
+              updateTime: item.updateTime || '空'
+            })),
+            sheetName: name,
+            sheetFilter: [
+              'num',
+              'dingNum',
+              'dateStart',
+              'dateEnd',
+              'zlName',
+              'myCity',
+              'zlUser',
+              'zlPhone',
+              'pcs',
+              'creatorName',
+              'updateTime'
+            ],
+            sheetHeader: [
+              '订单编号',
+              '钉钉审批号',
+              '租赁日期',
+              '预计归还日期',
+              '租赁方名称',
+              '地区',
+              '负责人',
+              '联系方式',
+              '设备台数',
+              '编辑人',
+              '更新日期'
+            ],
+            columnWidths: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
+          }
+        ]
+      }
+
+      const toExcel = new ExportJsonExcel(option) //new
+      toExcel.saveExcel() //保存
+    }
+  }, [A2BTableList.list.length, A2BTableList.total, tableSelect])
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: '订单编号',
+        render: (item: A2BListType) => item.num || '(空)'
+      },
+      {
+        title: '订单类型',
+        render: (item: A2BListType) => (item.type === 'CK' ? '出库' : '入库')
+      },
+      {
+        title: '关联业务订单号',
+        render: (item: A2BListType) => item.orderNum || '(空)'
+      },
+      {
+        title: '关联业务类型',
+        render: (item: A2BListType) =>
+          A2AtopTypeArr.find(v => v.key === item.orderType)?.name || '(空)'
+      },
+      {
+        title: '发货日期',
+        render: (item: A2BListType) => (item.type === 'CK' ? item.dateStart || '(空)' : '/')
+      },
+      {
+        title: '收货日期',
+        render: (item: A2BListType) => (item.type === 'RK' ? item.dateStart || '(空)' : '/')
+      },
+      {
+        title: '编辑人',
+        render: (item: A2BListType) => item.creatorName || '(空)'
+      },
+      {
+        title: '更新日期',
+        render: (item: A2BListType) => item.updateTime || '(空)'
+      },
+
+      {
+        title: '备注',
+        render: (item: A2BListType) =>
+          item.rtf ? (
+            item.rtf.length >= 50 ? (
+              <span style={{ cursor: 'pointer' }} title={item.rtf}>
+                {item.rtf.substring(0, 50) + '...'}
+              </span>
+            ) : (
+              item.rtf
+            )
+          ) : (
+            '(空)'
+          )
+      },
+
+      {
+        title: '操作',
+        render: (item: A2BListType) => (
+          <>
+            <Button size='small' type='text' onClick={() => history.push(`/lookLog/${item.id}`)}>
+              查看
+            </Button>
+            <Button
+              size='small'
+              type='text'
+              onClick={() => setOpenInfo({ id: item.id, txt: '编辑', type: item.type })}
+            >
+              编辑
+            </Button>
+            <MyPopconfirm txtK='删除' onConfirm={() => delByIdFu(item.id)} />
+          </>
+        )
+      }
+    ]
+  }, [delByIdFu])
+
+  // 点击新增和编辑的数据
+  const [openInfo, setOpenInfo] = useState<A2BaddType>({ id: 0, txt: '', type: '' })
+
+  // 左上角的 title
+  const leftTitle = useMemo(() => {
+    const obj = {
+      '': '物流订单',
+      新增: '物流订单-新增',
+      编辑: '物流订单-编辑'
+    }
+    return obj[openInfo.txt]
+  }, [openInfo.txt])
+
   return (
     <div className={styles.A2Blogistics}>
-      <h1>A2Blogistics</h1>
+      <div className='pageTitle'>
+        {leftTitle}
+        {openInfo.type ? (openInfo.type === 'CK' ? '(出库)' : '(入库)') : ''}
+      </div>
+      {/* 顶部筛选 */}
+      <div className='A2Btop'>
+        <div className='A2BtopSon'>
+          <div className='A2BtopRow'>
+            <span>订单编号:</span>
+            <Input
+              key={inputKey}
+              maxLength={12}
+              style={{ width: 190 }}
+              placeholder='请输入内容,最多12字'
+              allowClear
+              onChange={e => txtChangeFu(e.target.value, 'num')}
+            />
+          </div>
+
+          <div className='A2BtopRow'>
+            <span>订单类型:</span>
+            <Select
+              style={{ width: 190 }}
+              value={tableSelect.type}
+              onChange={e => setTableSelect({ ...tableSelect, type: e })}
+              options={A2BFromTypeArr}
+            />
+          </div>
+
+          <div className='A2BtopRow'>
+            <span>关联业务订单号:</span>
+            <Input
+              key={inputKey}
+              maxLength={12}
+              style={{ width: 190 }}
+              placeholder='请输入内容,最多12字'
+              allowClear
+              onChange={e => txtChangeFu(e.target.value, 'orderNum')}
+            />
+          </div>
+
+          <div className='A2BtopRow'>
+            <span>关联业务类型:</span>
+            <Select
+              style={{ width: 190 }}
+              value={tableSelect.orderType}
+              onChange={e => setTableSelect({ ...tableSelect, orderType: e })}
+              options={[
+                { value: '', label: '全部' },
+                ...A2AtopTypeArr.map(v => ({ value: v.key, label: v.name }))
+              ]}
+            />
+          </div>
+        </div>
+
+        <div className='A2BtopSon A2BtopSon2'>
+          <div>
+            <div className='A2BtopRow'>
+              <span>发货日期:</span>
+              <DatePicker style={{ width: 190 }} key={inputKey} onChange={timeChange1} />
+            </div>
+
+            <div className='A2BtopRow'>
+              <span>收货日期:</span>
+              <DatePicker style={{ width: 190 }} key={inputKey} onChange={timeChange2} />
+            </div>
+          </div>
+
+          <div>
+            <Button onClick={resetSelectFu}>重置</Button>
+            <Button type='primary' onClick={clickSearch}>
+              查询
+            </Button>
+            <Button type='primary' onClick={() => setOpenInfo({ id: -1, txt: '新增', type: 'RK' })}>
+              新增入库单
+            </Button>
+            <Button type='primary' onClick={() => setOpenInfo({ id: -1, txt: '新增', type: 'CK' })}>
+              新增出库单
+            </Button>
+            <Button type='primary' onClick={deriveFu}>
+              导出表格
+            </Button>
+          </div>
+        </div>
+      </div>
+      {/* 表格主体 */}
+      <div className='tableMain'>
+        <Table
+          scroll={{ y: 580 }}
+          dataSource={A2BTableList.list}
+          columns={columns}
+          rowKey='id'
+          pagination={{
+            showQuickJumper: true,
+            position: ['bottomCenter'],
+            showSizeChanger: true,
+            current: tableSelect.pageNum,
+            pageSize: tableSelect.pageSize,
+            total: A2BTableList.total,
+            onChange: paginationChange()
+          }}
+        />
+      </div>
+
+      {/*  右下角的列表数量 */}
+      <div className='tableNumBox'>
+        共 <span>{A2BTableList.total}</span> 条数据
+      </div>
+
+      {/* 新增 编辑 出来的页面 */}
+      {openInfo.id ? (
+        <AddLogistics
+          openInfo={openInfo}
+          closeFu={() => setOpenInfo({ id: 0, txt: '', type: '' })}
+          upTableFu={getListFu}
+          addTableFu={resetSelectFu}
+        />
+      ) : null}
     </div>
   )
 }

+ 45 - 0
src/store/action/A2Blogistics.ts

@@ -0,0 +1,45 @@
+import http from '@/utils/http'
+import { AppDispatch } from '..'
+/**
+ * 获取 物流订单 表格列表(存到仓库)
+ */
+export const A2B_APIgetlist = (data: any): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('cms/logistics/pageList', data)
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total
+      }
+      dispatch({ type: 'A2B/getList', payload: obj })
+    }
+  }
+}
+
+/**
+ * 获取 物流订单 表格列表(导出表格)
+ */
+export const A2B_APIgetlistDerive = (data: any) => {
+  return http.post('cms/logistics/pageList', data)
+}
+
+/**
+ * 物流订单 删除单个表格数据
+ */
+export const A2B_APIremove = (id: number) => {
+  return http.get(`cms/logistics/remove/${id}`)
+}
+
+/**
+ * 物流订单 新增/修改
+ */
+export const A2B_APIadd = (data: any) => {
+  return http.post('cms/logistics/save', data)
+}
+
+/**
+ * 物流订单 通过id获取详情
+ */
+export const A2B_APIgetInfo = (id: number) => {
+  return http.get(`cms/logistics/detail/${id}`)
+}

+ 27 - 0
src/store/reducer/A2Blogistics.ts

@@ -0,0 +1,27 @@
+import { A2BListType } from '@/types'
+
+// 初始化状态
+const initState = {
+  // 列表数据
+  A2BTableList: {
+    list: [] as A2BListType[],
+    total: 0
+  }
+}
+
+// 定义 action 类型
+type Props = {
+  type: 'A2B/getList'
+  payload: { list: A2BListType[]; total: number }
+}
+
+// reducer
+export default function Reducer(state = initState, action: Props) {
+  switch (action.type) {
+    // 获取列表数据
+    case 'A2B/getList':
+      return { ...state, A2BTableList: action.payload }
+    default:
+      return state
+  }
+}

+ 2 - 0
src/store/reducer/index.ts

@@ -5,6 +5,7 @@ import { combineReducers } from 'redux'
 import A0Layout from './layout'
 import A1Camera from './A1Camera'
 import A2Abusiness from './A2Abusiness'
+import A2Blogistics from './A2Blogistics'
 import A2Psychz from './A2Psychz'
 import B1Plan from './B1Plan'
 import B2Scene from './B2Scene'
@@ -18,6 +19,7 @@ const rootReducer = combineReducers({
   A0Layout,
   A1Camera,
   A2Abusiness,
+  A2Blogistics,
   A2Psychz,
   B1Plan,
   B2Scene,

+ 20 - 0
src/types/api/A2Blogistics.ts

@@ -0,0 +1,20 @@
+import { A2BFromTypeType } from '@/pages/A2Blogistics/data'
+import { A2AListCamerasType } from './A2Abusiness'
+import { TopTypeType } from '@/pages/A2Abusiness/data'
+
+export type A2BListType = {
+  cameraIds: string
+  cameras?: A2AListCamerasType[]
+  createTime: string
+  creatorId: number
+  creatorName: string
+  dateStart: string
+  id: number
+  logisticsNum: string
+  num: string
+  orderNum: string
+  orderType: TopTypeType
+  rtf: string
+  type: A2BFromTypeType
+  updateTime: string
+}

+ 1 - 0
src/types/index.d.ts

@@ -1,6 +1,7 @@
 export * from './api/layot'
 export * from './api/A1Camera'
 export * from './api/A2Abusiness'
+export * from './api/A2Blogistics'
 export * from './api/A2Psychz'
 export * from './api/B1Plan'
 export * from './api/B2Scene'