Explorar el Código

feat: F5staff

chenlei hace 14 horas
padre
commit
5c31aafdfb

+ 16 - 0
src/pages/Fstorehouse/F2moveStorage/F2edit/index.module.scss

@@ -0,0 +1,16 @@
+.F2edit {
+  :global {
+    .F2editStorage {
+      width: 100%;
+      border-top: 1px solid #ccc;
+      padding: 15px;
+      display: flex;
+      align-items: center;
+      gap: 15px;
+    }
+    .F2editStorageTitle {
+      font-weight: 700;
+      font-size: 18px;
+    }
+  }
+}

+ 253 - 0
src/pages/Fstorehouse/F2moveStorage/F2edit/index.tsx

@@ -0,0 +1,253 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import { useParams } from 'react-router-dom'
+import styles from './index.module.scss'
+import EditTop from '@/pages/Zother/EditTop'
+import EditBtn from '@/pages/Zother/EditBtn'
+import { InfoProvider, useInfo } from '@/pages/Zother/InfoContext'
+import { rowArrTemp } from '@/pages/Zother/data'
+import SonGoodsList from '@/pages/Zother/SonGoodsList'
+import { Button, Select } from 'antd'
+import { selectObj } from '@/utils/dataChange'
+import FileArchive from '@/pages/Zother/FileArchive'
+import { F1_APIgetShelfList, F1_APIgetStorageList } from '@/store/action/Fstorehouse/F1inStorage'
+import {
+  F2_APIgetClueList,
+  F2_APIgetShelfEmptyList,
+  F2API_obj
+} from '@/store/action/Fstorehouse/F2moveStorage'
+
+const rowArr = rowArrTemp('移库')
+
+function F2editContent() {
+  const { info } = useInfo() as { info: any }
+  const { key } = useParams<any>()
+  const canEdit = useMemo(() => ['1', '2'].includes(key), [key])
+  const sonGoodsListRef = useRef<any>(null)
+  const [outStorage, setoutStorage] = useState<any>(null)
+  const [inStorage, setinStorage] = useState<any>(null)
+  const [allWarehouseList, setAllWarehouseList] = useState<any[]>([])
+  const warehouseOptions = useMemo(() => {
+    return allWarehouseList.map((i: any) => ({
+      label: i.name,
+      value: i.id,
+      managerUser: i.managerUser
+    }))
+  }, [allWarehouseList])
+  const [shelfList, setShelfList] = useState<any[]>([])
+
+  const handleOutStorageChange = useCallback(async (value: string, option: any) => {
+    setoutStorage(option)
+  }, [])
+
+  const handleInStorageChange = useCallback(async (value: string, option: any) => {
+    setinStorage(option)
+    const res = await F1_APIgetShelfList({ storageId: value })
+    setShelfList(
+      res.data.map((i: any) => ({
+        label: [i.layer1, i.layer2, i.layer3, i.layer4].filter(i => Boolean(i)).join('-'),
+        value: i.id
+      }))
+    )
+  }, [])
+
+  const handleAutoAssignShelf = async () => {
+    const snaps = sonGoodsListRef.current?.snaps ?? []
+    if (!snaps.length) return
+    const res = await F2_APIgetShelfEmptyList({
+      storageId: outStorage?.value,
+      limit: snaps.length
+    })
+    const idList: { id: number }[] = Array.isArray(res) ? res : (res?.data ?? [])
+    const form = sonGoodsListRef.current?.tableRef?.current?.form
+    if (!form) return
+    const values: Record<string, number | undefined> = {}
+    snaps.forEach((snap: any, i: number) => {
+      values[`${snap.id}-storageInId`] = idList[i]?.id
+    })
+    form.setFieldsValue(values)
+  }
+
+  const getStorageList = useCallback(async () => {
+    const res = await F1_APIgetStorageList()
+    setAllWarehouseList(res.data.records)
+  }, [])
+
+  useEffect(() => {
+    getStorageList()
+  }, [getStorageList])
+
+  useEffect(() => {
+    if (!info?.storageId || !allWarehouseList.length) return
+    const opt = warehouseOptions.find((o: any) => o.value === info.storageId)
+    const outOpt = warehouseOptions.find((o: any) => o.value === info.snaps[0]?.storageOutId)
+    if (opt && outOpt) {
+      setoutStorage(outOpt)
+      setinStorage(opt)
+      F1_APIgetShelfList({ storageId: info.storageId }).then(res => {
+        setShelfList(
+          res.data.map((i: any) => ({
+            label: [i.layer1, i.layer2, i.layer3, i.layer4].filter(i => Boolean(i)).join('-'),
+            value: i.id
+          }))
+        )
+      })
+    }
+  }, [info?.storageId, allWarehouseList.length, warehouseOptions, info?.snaps])
+
+  const verifyBackFu = (info: any) => {
+    if (!outStorage) return { flag: true, txt: '请选择入库库房' }
+    const form = sonGoodsListRef.current?.tableRef?.current?.form
+    if (!form) return { flag: true, txt: '请添加藏品' }
+    const values = form.getFieldsValue()
+    const snaps = sonGoodsListRef.current?.snaps ?? []
+    const hasEmptyStorage = snaps.some((snap: any) => !values[`${snap.id}-storageInId`])
+    if (hasEmptyStorage) return { flag: true, txt: '请为藏品选择库房位置' }
+    return { flag: false, txt: '' }
+  }
+
+  const getExtraData = (info: any, snaps: any[]) => {
+    const form = sonGoodsListRef.current?.tableRef?.current?.form
+    const values = form?.getFieldsValue() ?? {}
+    const snapsArr = snaps.map((v: any) => {
+      const flag = v.pageType === 'clue' && !v.clueId
+      const siteId = values[`${v.id}-storageInId`] ?? null
+      const siteLoc = shelfList.find((s: any) => s.value === siteId)?.label ?? null
+      return {
+        goodId: v.isNew || flag ? null : v.id,
+        orderId: info.id,
+        storageOutId: outStorage?.value ?? null,
+        storageInId: inStorage?.value ?? null,
+        snap: JSON.stringify({
+          ...v,
+          id: v.isNew || flag ? null : v.id,
+          siteLoc,
+          siteId,
+          storageInId: siteId
+        })
+      }
+    })
+    return {
+      storageId: inStorage?.value ?? undefined,
+      extraInfo:
+        inStorage?.value === outStorage?.value
+          ? inStorage.label
+          : `${outStorage.label} -> ${inStorage.label}`,
+      snaps: snapsArr
+    }
+  }
+
+  return (
+    <div className={styles.F2edit} id='editBox'>
+      <div className='editMain'>
+        {/* 顶部 */}
+        {/* TODO: 借展归还待完善 */}
+        <EditTop
+          pageTxt='申请信息'
+          rowArr={rowArr}
+          APIobj={F2API_obj}
+          fileUpInfo={{ myUrl: 'cms/orderSite/move/upload', dirCode: 'moveStorage' }}
+        />
+
+        <div className='F2editStorage'>
+          <p className='F2editStorageTitle'>*移出库房</p>
+          <Select
+            style={{ width: 200 }}
+            options={warehouseOptions}
+            placeholder='请选择'
+            value={outStorage?.value}
+            onChange={handleOutStorageChange}
+            disabled={!canEdit}
+          />
+
+          <p className='F2editStorageTitle'>*移入库房</p>
+          <Select
+            style={{ width: 200 }}
+            options={warehouseOptions}
+            placeholder='请选择'
+            value={inStorage?.value}
+            onChange={handleInStorageChange}
+            disabled={!canEdit}
+          />
+        </div>
+
+        {/* 藏品清单 */}
+        <SonGoodsList
+          ref={sonGoodsListRef}
+          needEdit={true}
+          btnTxt='选择藏品'
+          addShow={false}
+          customRightBtn={
+            <Button
+              disabled={!inStorage?.value || !canEdit}
+              type='primary'
+              ghost
+              onClick={handleAutoAssignShelf}
+            >
+              自动分配空置位置
+            </Button>
+          }
+          goodsSonTable={[
+            ['txt', '藏品登记号', 'num'],
+            ['img', '封面', 'thumb'],
+            ['txtCTag', '藏品标签', 'tagDictId'],
+            ['txt', '藏品名称', 'name'],
+            ['select', '级别', 'level', selectObj['藏品级别']],
+            ['txtC', '类别', 'typeDictId'],
+            ['txtC', '年代', 'ageDictId'],
+            ['txtC', '质地', 'textureDictId'],
+            ['select', '完残程度', 'tornLevel', selectObj['完残程度']],
+            ['ping', '数量', 'pcs', 'pcsUnitDictId'],
+            [
+              'custom',
+              '移入位置',
+              'storageInId',
+              {
+                render(_readOnly?: boolean) {
+                  return (
+                    <Select
+                      allowClear
+                      options={shelfList}
+                      disabled={!canEdit}
+                      placeholder='请选择'
+                    />
+                  )
+                }
+              }
+            ]
+          ]}
+          fileUpInfo={{ myUrl: 'cms/orderSite/in/upload', dirCode: 'inStorageGoods' }}
+          selectApi={F2_APIgetClueList}
+          isClueSelect={false}
+          clueShowBtnDisabled={!outStorage?.value}
+          selectGoodsParams={{
+            storageId: outStorage?.value
+          }}
+        />
+
+        {/* 附件归档 */}
+        <FileArchive />
+
+        {/* 底部按钮 */}
+        <EditBtn
+          path='/moveStorage'
+          APIobj={F2API_obj}
+          checkListTxt='请添加藏品'
+          verifyBackFu={verifyBackFu}
+          getExtraData={getExtraData}
+        />
+      </div>
+    </div>
+  )
+}
+
+function F2edit() {
+  return (
+    <InfoProvider>
+      <F2editContent />
+    </InfoProvider>
+  )
+}
+
+const MemoF2edit = React.memo(F2edit)
+
+export default MemoF2edit

+ 122 - 1
src/pages/Fstorehouse/F2moveStorage/index.tsx

@@ -1,9 +1,130 @@
-import React from 'react'
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
 import styles from './index.module.scss'
+import TableList from '@/pages/Zother/TableList'
+import { F2_APIgetList } from '@/store/action/Fstorehouse/F2moveStorage'
+import { tableListAuditBtnFu } from '@/utils/authority'
+import { moveStorageTableC } from '@/utils/tableData'
+import { Button } from 'antd'
+import { baseFormData } from '@/pages/Zother/data'
+import { RootState } from '@/store'
+import { F1_APIgetStorageList } from '@/store/action/Fstorehouse/F1inStorage'
+import { selectObj } from '@/utils/dataChange'
+import { useSelector } from 'react-redux'
+import { useHistory } from 'react-router-dom'
+
+const F2baseFormData = baseFormData()
+
 function F2moveStorage() {
 function F2moveStorage() {
+  const tableListRef = useRef<any>(null)
+  const history = useHistory()
+  // 从仓库拿数据
+  const tableInfo = useSelector((state: RootState) => state.F2moveStorage.tableInfo)
+  const [allWarehouseList, setAllWarehouseList] = useState<any[]>([])
+  const warehouseOptions = useMemo(() => {
+    return allWarehouseList.map((i: any) => ({
+      label: i.name,
+      value: i.id
+    }))
+  }, [allWarehouseList])
+  const SEARCH_DOM = useMemo(
+    () => [
+      {
+        type: 'time',
+        key: ['startTime', 'endTime'],
+        placeholder: `移库时间`
+      },
+      {
+        type: 'select',
+        key: 'storageId',
+        placeholder: `相关库房`,
+        options: warehouseOptions
+      },
+      {
+        type: 'input',
+        key: 'searchKey',
+        placeholder: `请输入申请编号、发起人或藏品编号`
+      },
+      {
+        type: 'time',
+        key: ['businessStartTime', 'businessEndTime'],
+        placeholder: `发起日期`
+      },
+      {
+        type: 'select',
+        key: 'status',
+        placeholder: `申请状态`,
+        options: selectObj['藏品入库申请状态']
+      }
+    ],
+    [warehouseOptions]
+  )
+
+  const dataExport = () => {
+    console.log('数据导出了')
+  }
+
+  const getStorageList = async () => {
+    const res = await F1_APIgetStorageList()
+    setAllWarehouseList(res.data.records)
+  }
+
+  const tableBtnFu = useCallback(
+    (id: number | null, key: string) => {
+      history.push(`/moveStorage_edit/${key}/${id}`)
+    },
+    [history]
+  )
+
+  useEffect(() => {
+    getStorageList()
+  }, [])
+
   return (
   return (
     <div className={styles.F2moveStorage}>
     <div className={styles.F2moveStorage}>
       <div className='pageTitle'>藏品移库</div>
       <div className='pageTitle'>藏品移库</div>
+
+      <TableList
+        ref={tableListRef}
+        baseFormData={F2baseFormData}
+        getListAPI={F2_APIgetList}
+        pageKey='position'
+        tableInfo={tableInfo}
+        columnsTemp={moveStorageTableC}
+        rightBtnWidth={340}
+        yHeight={585}
+        searchDom={SEARCH_DOM}
+        storyTableListToprr={({ clickSearch, resetSelectFu }) => (
+          <>
+            <Button type='primary' ghost onClick={() => tableBtnFu(null, '1')}>
+              发起申请
+            </Button>
+            <Button type='primary' onClick={dataExport}>
+              数据导出
+            </Button>
+            <Button type='primary' onClick={clickSearch}>
+              查询
+            </Button>
+            <Button onClick={resetSelectFu}>重置</Button>
+          </>
+        )}
+        storyTableLastBtn={[
+          {
+            title: '操作',
+            render: (item: any) => (
+              <>
+                <Button type='text' onClick={() => tableBtnFu(item.id, '4')}>
+                  查看
+                </Button>
+                {tableListAuditBtnFu(item) ? (
+                  <Button size='small' type='text' onClick={() => tableBtnFu(item.id, '3')}>
+                    审批
+                  </Button>
+                ) : null}
+              </>
+            )
+          }
+        ]}
+      />
     </div>
     </div>
   )
   )
 }
 }

+ 77 - 0
src/pages/Fstorehouse/F5staff/F5edit/components/StaffModal.tsx

@@ -0,0 +1,77 @@
+import React, { useEffect } from 'react'
+import { Form, Input, Modal } from 'antd'
+import { decodeIfEncoded } from '@/utils/pass'
+
+export type StaffFormValues = {
+  name: string
+  papers: string
+  phone: string
+  unit: string
+}
+
+type Props = {
+  open: boolean
+  editingStaff: Partial<StaffFormValues> | null
+  onOk: (values: StaffFormValues) => void
+  onCancel: () => void
+}
+
+function StaffModal({ open, editingStaff, onOk, onCancel }: Props) {
+  const [form] = Form.useForm<StaffFormValues>()
+
+  useEffect(() => {
+    if (open) {
+      if (editingStaff) {
+        form.setFieldsValue({
+          name: editingStaff.name ?? '',
+          papers: decodeIfEncoded(editingStaff.papers ?? ''),
+          phone: decodeIfEncoded(editingStaff.phone ?? ''),
+          unit: editingStaff.unit ?? ''
+        })
+      } else {
+        form.resetFields()
+      }
+    }
+  }, [open, editingStaff, form])
+
+  const handleOk = () => {
+    form.validateFields().then(values => {
+      onOk(values)
+      form.resetFields()
+    })
+  }
+
+  const handleCancel = () => {
+    form.resetFields()
+    onCancel()
+  }
+
+  return (
+    <Modal
+      destroyOnClose
+      title={editingStaff ? '编辑人员' : '新增人员'}
+      open={open}
+      onOk={handleOk}
+      onCancel={handleCancel}
+      okText='确定'
+      cancelText='取消'
+    >
+      <Form form={form} layout='vertical'>
+        <Form.Item label='姓名' name='name' rules={[{ required: true, message: '请输入姓名' }]}>
+          <Input placeholder='请输入姓名' />
+        </Form.Item>
+        <Form.Item label='证件信息' name='papers'>
+          <Input placeholder='请输入证件信息' />
+        </Form.Item>
+        <Form.Item label='联系方式' name='phone'>
+          <Input placeholder='请输入联系方式' />
+        </Form.Item>
+        <Form.Item label='所在单位' name='unit'>
+          <Input placeholder='请输入所在单位' />
+        </Form.Item>
+      </Form>
+    </Modal>
+  )
+}
+
+export default StaffModal

+ 2 - 0
src/pages/Fstorehouse/F5staff/F5edit/components/index.ts

@@ -0,0 +1,2 @@
+export { default as StaffModal } from './StaffModal'
+export type { StaffFormValues } from './StaffModal'

+ 22 - 0
src/pages/Fstorehouse/F5staff/F5edit/index.module.scss

@@ -0,0 +1,22 @@
+.F5edit {
+  :global {
+    .F5editStorage {
+      width: 100%;
+      border-top: 1px solid #ccc;
+      padding: 15px;
+      display: flex;
+      align-items: center;
+      gap: 15px;
+    }
+    .F5editStorageTitle {
+      font-weight: 700;
+      font-size: 18px;
+    }
+    .F5editStorageTitleBox {
+      width: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+    }
+  }
+}

+ 266 - 0
src/pages/Fstorehouse/F5staff/F5edit/index.tsx

@@ -0,0 +1,266 @@
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import { useParams } from 'react-router-dom'
+import styles from './index.module.scss'
+import EditTop from '@/pages/Zother/EditTop'
+import EditBtn from '@/pages/Zother/EditBtn'
+import { InfoProvider, useInfo } from '@/pages/Zother/InfoContext'
+import { F1_APIgetStorageList } from '@/store/action/Fstorehouse/F1inStorage'
+import { Button, Select, Space } from 'antd'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import FileArchive from '@/pages/Zother/FileArchive'
+import { F5API_obj } from '@/store/action/Fstorehouse/F5staff'
+import { getDictFu } from '@/utils/dataChange'
+import encodeStr, { getStaffDisplayPapers, getStaffDisplayPhone } from '@/utils/pass'
+import MyTable from '@/components/MyTable'
+import { StaffModal } from './components'
+
+const rowArr = [
+  {
+    name: `人员出入库`,
+    type: 'txt'
+  },
+  {
+    name: `出入日期`,
+    must: true,
+    key: 'date',
+    type: 'DatePicker'
+  },
+  {
+    name: '申请编号',
+    must: true,
+    key: 'num',
+    type: 'Input',
+    noNull: true //不允许输入空格
+  },
+  {
+    name: '申请类型',
+    key: 'dictIdApply',
+    type: 'Cascader',
+    options: getDictFu('人员出入库')
+  },
+  {
+    name: '事由说明',
+    full: true,
+    key: 'reason',
+    type: 'TextArea'
+  },
+  {
+    name: '备注',
+    full: true,
+    key: 'remark',
+    type: 'TextArea'
+  }
+]
+
+function F5editContent() {
+  const { info, setSnapsFu, snaps } = useInfo() as {
+    info: any
+    setSnapsFu: (snaps: any[]) => void
+    snaps: any[]
+  }
+  const { key } = useParams<any>()
+  const canEdit = useMemo(() => ['1', '2'].includes(key), [key])
+  const [selectStorage, setSelectStorage] = useState<any>(null)
+  const [allWarehouseList, setAllWarehouseList] = useState<any[]>([])
+  const warehouseOptions = useMemo(() => {
+    return allWarehouseList.map((i: any) => ({
+      label: i.name,
+      value: i.id,
+      managerUser: i.managerUser
+    }))
+  }, [allWarehouseList])
+
+  const handleStorageChange = useCallback(async (value: string, option: any) => {
+    setSelectStorage(option)
+  }, [])
+
+  const getStorageList = useCallback(async () => {
+    const res = await F1_APIgetStorageList()
+    setAllWarehouseList(res.data.records)
+  }, [])
+
+  useEffect(() => {
+    getStorageList()
+  }, [getStorageList])
+
+  const [staffModalOpen, setStaffModalOpen] = useState(false)
+  const [editingStaff, setEditingStaff] = useState<any>(null)
+  const staffList = snaps
+
+  const openAddStaff = useCallback(() => {
+    setEditingStaff(null)
+    setStaffModalOpen(true)
+  }, [])
+
+  const openEditStaff = useCallback((item: any) => {
+    setEditingStaff(item)
+    setStaffModalOpen(true)
+  }, [])
+
+  const handleStaffModalOk = useCallback(
+    (values: { name: string; papers: string; phone: string; unit: string }) => {
+      const staffItem = {
+        name: values.name,
+        papers: encodeStr(values.papers),
+        phone: encodeStr(values.phone),
+        unit: values.unit
+      }
+      if (editingStaff) {
+        const next = staffList.map((v: any) =>
+          (v.idTemp ?? v.id) === (editingStaff.idTemp ?? editingStaff.id)
+            ? { ...v, ...staffItem }
+            : v
+        )
+        setSnapsFu(next as any)
+      } else {
+        setSnapsFu([...staffList, { ...staffItem, idTemp: Date.now() }] as any)
+      }
+      setStaffModalOpen(false)
+    },
+    [editingStaff, staffList, setSnapsFu]
+  )
+
+  const handleDeleteStaff = useCallback(
+    (item: any) => {
+      setSnapsFu(
+        staffList.filter((v: any) => (v.idTemp ?? v.id) !== (item.idTemp ?? item.id)) as any
+      )
+    },
+    [staffList, setSnapsFu]
+  )
+
+  const verifyBackFu = (info: any) => {
+    if (!selectStorage) return { flag: true, txt: '请选择相关库房' }
+    if (!staffList.length) return { flag: true, txt: '请添加人员' }
+    return { flag: false, txt: '' }
+  }
+
+  const getExtraData = (info: any, snaps: any[]) => {
+    const snapsArr = snaps.map((v: any) => ({
+      orderId: info.id,
+      snap: JSON.stringify({
+        name: v.name,
+        papers: v.papers,
+        phone: v.phone,
+        unit: v.unit
+      })
+    }))
+    return {
+      storageId: selectStorage?.value ?? undefined,
+      snaps: snapsArr
+    }
+  }
+
+  useEffect(() => {
+    if (!info?.storageId || !allWarehouseList.length) return
+    const opt = warehouseOptions.find((o: any) => o.value === info.storageId)
+    if (opt) {
+      setSelectStorage(opt)
+    }
+  }, [info?.storageId, allWarehouseList.length, warehouseOptions])
+
+  return (
+    <div className={styles.F5edit} id='editBox'>
+      <div className='editMain'>
+        <EditTop
+          pageTxt='人员出入库'
+          rowArr={rowArr}
+          APIobj={F5API_obj}
+          fileUpInfo={{ myUrl: 'cms/orderSite/user/upload', dirCode: 'staff' }}
+        />
+
+        <div className='F5editStorage'>
+          <p className='F5editStorageTitle'>*相关库房</p>
+          <Select
+            style={{ width: 200 }}
+            options={warehouseOptions}
+            placeholder='请选择'
+            value={selectStorage?.value}
+            onChange={handleStorageChange}
+            disabled={!canEdit}
+          />
+        </div>
+
+        <div className='F5editStorage' style={{ flexDirection: 'column' }}>
+          <div className='F5editStorageTitleBox'>
+            <p className='F5editStorageTitle'>人员信息</p>
+            <Button type='primary' onClick={openAddStaff} disabled={!canEdit}>
+              新增人员
+            </Button>
+          </div>
+          <MyTable
+            classKey='F5staff'
+            list={staffList}
+            columnsTemp={[
+              ['txt', '姓名', 'name'],
+              ['custom', '证件信息', (item: any) => getStaffDisplayPapers(item.papers)],
+              ['custom', '联系方式', (item: any) => getStaffDisplayPhone(item.phone)],
+              ['txt', '所在单位', 'unit']
+            ]}
+            lastBtn={
+              canEdit
+                ? [
+                    {
+                      title: '操作',
+                      render: (item: any) => (
+                        <Space>
+                          <Button size='small' type='link' onClick={() => openEditStaff(item)}>
+                            编辑
+                          </Button>
+                          <MyPopconfirm
+                            txtK='删除'
+                            onConfirm={() => handleDeleteStaff(item)}
+                            Dom={
+                              <Button size='small' type='link' danger>
+                                删除
+                              </Button>
+                            }
+                          />
+                        </Space>
+                      )
+                    }
+                  ]
+                : []
+            }
+            pagingInfo={false}
+            rowKey='idTemp'
+          />
+        </div>
+
+        <StaffModal
+          open={staffModalOpen}
+          editingStaff={editingStaff}
+          onOk={handleStaffModalOk}
+          onCancel={() => {
+            setStaffModalOpen(false)
+            setEditingStaff(null)
+          }}
+        />
+
+        {/* 附件归档 */}
+        <FileArchive />
+
+        {/* 底部按钮 */}
+        <EditBtn
+          path='/staff'
+          APIobj={F5API_obj}
+          checkListTxt='请添加人员'
+          verifyBackFu={verifyBackFu}
+          getExtraData={getExtraData}
+        />
+      </div>
+    </div>
+  )
+}
+
+function F5edit() {
+  return (
+    <InfoProvider>
+      <F5editContent />
+    </InfoProvider>
+  )
+}
+
+const MemoF5edit = React.memo(F5edit)
+
+export default MemoF5edit

+ 122 - 1
src/pages/Fstorehouse/F5staff/index.tsx

@@ -1,9 +1,130 @@
-import React from 'react'
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
 import styles from './index.module.scss'
+import TableList from '@/pages/Zother/TableList'
+import { F5_APIgetList } from '@/store/action/Fstorehouse/F5staff'
+import { tableListAuditBtnFu } from '@/utils/authority'
+import { staffTableC } from '@/utils/tableData'
+import { Button } from 'antd'
+import { baseFormData } from '@/pages/Zother/data'
+import { RootState } from '@/store'
+import { F1_APIgetStorageList } from '@/store/action/Fstorehouse/F1inStorage'
+import { selectObj } from '@/utils/dataChange'
+import { useSelector } from 'react-redux'
+import { useHistory } from 'react-router-dom'
+
+const F5baseFormData = baseFormData()
+
 function F5staff() {
 function F5staff() {
+  const tableListRef = useRef<any>(null)
+  const history = useHistory()
+  // 从仓库拿数据
+  const tableInfo = useSelector((state: RootState) => state.F5staff.tableInfo)
+  const [allWarehouseList, setAllWarehouseList] = useState<any[]>([])
+  const warehouseOptions = useMemo(() => {
+    return allWarehouseList.map((i: any) => ({
+      label: i.name,
+      value: i.id
+    }))
+  }, [allWarehouseList])
+  const SEARCH_DOM = useMemo(
+    () => [
+      {
+        type: 'time',
+        key: ['startTime', 'endTime'],
+        placeholder: `出入时间`
+      },
+      {
+        type: 'select',
+        key: 'storageId',
+        placeholder: `相关库房`,
+        options: warehouseOptions
+      },
+      {
+        type: 'input',
+        key: 'searchKey',
+        placeholder: `请输入申请编号、发起人或藏品编号`
+      },
+      {
+        type: 'time',
+        key: ['businessStartTime', 'businessEndTime'],
+        placeholder: `发起日期`
+      },
+      {
+        type: 'select',
+        key: 'status',
+        placeholder: `申请状态`,
+        options: selectObj['藏品入库申请状态']
+      }
+    ],
+    [warehouseOptions]
+  )
+
+  const dataExport = () => {
+    console.log('数据导出了')
+  }
+
+  const getStorageList = async () => {
+    const res = await F1_APIgetStorageList()
+    setAllWarehouseList(res.data.records)
+  }
+
+  const tableBtnFu = useCallback(
+    (id: number | null, key: string) => {
+      history.push(`/staff_edit/${key}/${id}`)
+    },
+    [history]
+  )
+
+  useEffect(() => {
+    getStorageList()
+  }, [])
+
   return (
   return (
     <div className={styles.F5staff}>
     <div className={styles.F5staff}>
       <div className='pageTitle'>人员出入库</div>
       <div className='pageTitle'>人员出入库</div>
+
+      <TableList
+        ref={tableListRef}
+        baseFormData={F5baseFormData}
+        getListAPI={F5_APIgetList}
+        pageKey='position'
+        tableInfo={tableInfo}
+        columnsTemp={staffTableC}
+        rightBtnWidth={340}
+        yHeight={585}
+        searchDom={SEARCH_DOM}
+        storyTableListToprr={({ clickSearch, resetSelectFu }) => (
+          <>
+            <Button type='primary' ghost onClick={() => tableBtnFu(null, '1')}>
+              发起申请
+            </Button>
+            <Button type='primary' onClick={dataExport}>
+              数据导出
+            </Button>
+            <Button type='primary' onClick={clickSearch}>
+              查询
+            </Button>
+            <Button onClick={resetSelectFu}>重置</Button>
+          </>
+        )}
+        storyTableLastBtn={[
+          {
+            title: '操作',
+            render: (item: any) => (
+              <>
+                <Button type='text' onClick={() => tableBtnFu(item.id, '4')}>
+                  查看
+                </Button>
+                {tableListAuditBtnFu(item) ? (
+                  <Button size='small' type='text' onClick={() => tableBtnFu(item.id, '3')}>
+                    审批
+                  </Button>
+                ) : null}
+              </>
+            )
+          }
+        ]}
+      />
     </div>
     </div>
   )
   )
 }
 }

+ 12 - 0
src/pages/Layout/data.ts

@@ -412,6 +412,12 @@ export const routerSon: RouterTypeRow[] = [
     Com: React.lazy(() => import('../Fstorehouse/F1inStorage/F1edit'))
     Com: React.lazy(() => import('../Fstorehouse/F1inStorage/F1edit'))
   },
   },
   {
   {
+    id: 1020,
+    name: '藏品移库-详情页',
+    path: '/moveStorage_edit/:key/:id',
+    Com: React.lazy(() => import('../Fstorehouse/F2moveStorage/F2edit'))
+  },
+  {
     id: 1030,
     id: 1030,
     name: '藏品出库-详情页',
     name: '藏品出库-详情页',
     path: '/outStorage_edit/:key/:id',
     path: '/outStorage_edit/:key/:id',
@@ -422,5 +428,11 @@ export const routerSon: RouterTypeRow[] = [
     name: '藏品盘点-详情页',
     name: '藏品盘点-详情页',
     path: '/check_edit/:key/:id',
     path: '/check_edit/:key/:id',
     Com: React.lazy(() => import('../Fstorehouse/F4check/F4edit'))
     Com: React.lazy(() => import('../Fstorehouse/F4check/F4edit'))
+  },
+  {
+    id: 1050,
+    name: '人员出入库-详情页',
+    path: '/staff_edit/:key/:id',
+    Com: React.lazy(() => import('../Fstorehouse/F5staff/F5edit'))
   }
   }
 ]
 ]

+ 1 - 1
src/pages/Zother/EditTop/index.tsx

@@ -125,7 +125,7 @@ function EditTop({ rowArr, pageTxt, APIobj, fileUpInfo, moreDom }: Props) {
           const obj = JSON.parse(v.snap || '{}')
           const obj = JSON.parse(v.snap || '{}')
 
 
           const flag = v.pageType === 'clue' && !v.clueId
           const flag = v.pageType === 'clue' && !v.clueId
-          if (obj.id === null || obj.isNew || flag)
+          if (obj.id == null || obj.isNew || flag)
             obj.id = ['藏品登记', '藏品入馆'].includes(pageTxt) ? v.goodId : Date.now() + i
             obj.id = ['藏品登记', '藏品入馆'].includes(pageTxt) ? v.goodId : Date.now() + i
 
 
           obj.idTemp = obj.id
           obj.idTemp = obj.id

+ 45 - 0
src/store/action/Fstorehouse/F2moveStorage.ts

@@ -0,0 +1,45 @@
+import { AppDispatch } from '@/store'
+import http from '@/utils/http'
+import { APIbase } from '../layout'
+
+/**
+ * 藏品移库 - 分页列表
+ */
+export const F2_APIgetList = (data: any): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('cms/orderSite/move/page', data)
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total
+      }
+
+      dispatch({ type: 'F2/getList', payload: obj })
+    }
+  }
+}
+
+/**
+ * 藏品总账列表-分页
+ */
+export const F2_APIgetClueList = (data: any) => {
+  return http.post('cms/orderSite/move/good/page', data)
+}
+
+export const F2API_obj = {
+  创建订单: () => APIbase('get', 'cms/orderSite/move/create'),
+  获取详情: (id: number) => APIbase('get', `cms/orderSite/move/detail/${id}`),
+  草稿: (data: any) => APIbase('post', `cms/orderSite/move/saveDraft`, data),
+  发起: (data: any) => APIbase('post', `cms/orderSite/move/saveApply`, data),
+  重新发起: (id: number) => APIbase('get', `cms/orderSite/move/reissue/${id}`),
+  审批: (data: any) => APIbase('post', `cms/orderSite/move/audit`, data),
+  撤回: (id: number) => APIbase('get', `cms/orderSite/move/revocation/${id}`),
+  删除: (id: number) => APIbase('get', `cms/orderSite/move/remove/${id}`)
+}
+
+/**
+ * 自动分配空置仓位
+ */
+export const F2_APIgetShelfEmptyList = (params: any) => {
+  return http.get('cms/orderSite/move/getStorageEmpty', { params })
+}

+ 31 - 0
src/store/action/Fstorehouse/F5staff.ts

@@ -0,0 +1,31 @@
+import { AppDispatch } from '@/store'
+import http from '@/utils/http'
+import { APIbase } from '../layout'
+
+/**
+ * 人员出入库 - 分页列表
+ */
+export const F5_APIgetList = (data: any): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('cms/orderSite/user/page', data)
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total
+      }
+
+      dispatch({ type: 'F5/getList', payload: obj })
+    }
+  }
+}
+
+export const F5API_obj = {
+  创建订单: () => APIbase('get', 'cms/orderSite/user/create'),
+  获取详情: (id: number) => APIbase('get', `cms/orderSite/user/detail/${id}`),
+  草稿: (data: any) => APIbase('post', `cms/orderSite/user/saveDraft`, data),
+  发起: (data: any) => APIbase('post', `cms/orderSite/user/saveApply`, data),
+  重新发起: (id: number) => APIbase('get', `cms/orderSite/user/reissue/${id}`),
+  审批: (data: any) => APIbase('post', `cms/orderSite/user/audit`, data),
+  撤回: (id: number) => APIbase('get', `cms/orderSite/user/revocation/${id}`),
+  删除: (id: number) => APIbase('get', `cms/orderSite/user/remove/${id}`)
+}

+ 28 - 0
src/store/reducer/Fstorehouse/F2moveStorage.ts

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

+ 28 - 0
src/store/reducer/Fstorehouse/F5staff.ts

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

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

@@ -20,8 +20,10 @@ import G1accident from './Gmaintain/G1accident'
 import G2actuality from './Gmaintain/G2actuality'
 import G2actuality from './Gmaintain/G2actuality'
 import G3repair from './Gmaintain/G3repair'
 import G3repair from './Gmaintain/G3repair'
 import F1inStorage from './Fstorehouse/F1inStorage'
 import F1inStorage from './Fstorehouse/F1inStorage'
+import F2moveStorage from './Fstorehouse/F2moveStorage'
 import F3outStorage from './Fstorehouse/F3outStorage'
 import F3outStorage from './Fstorehouse/F3outStorage'
 import F4check from './Fstorehouse/F4check'
 import F4check from './Fstorehouse/F4check'
+import F5staff from './Fstorehouse/F5staff'
 import I1storageSet from './Isystem/I1storageSet'
 import I1storageSet from './Isystem/I1storageSet'
 import I2dict from './Isystem/I2dict'
 import I2dict from './Isystem/I2dict'
 import I3numSet from './Isystem/I3numSet'
 import I3numSet from './Isystem/I3numSet'
@@ -56,8 +58,10 @@ const rootReducer = combineReducers({
   G3repair,
   G3repair,
 
 
   F1inStorage,
   F1inStorage,
+  F2moveStorage,
   F3outStorage,
   F3outStorage,
   F4check,
   F4check,
+  F5staff,
   I1storageSet,
   I1storageSet,
   I2dict,
   I2dict,
   I3numSet,
   I3numSet,

+ 122 - 85
src/utils/pass.ts

@@ -1,100 +1,137 @@
 function randomWord(randomFlag: boolean, min: number, max: number = 15) {
 function randomWord(randomFlag: boolean, min: number, max: number = 15) {
-  let str = "";
-  let range = min;
+  let str = ''
+  let range = min
   const arr = [
   const arr = [
-    "0",
-    "1",
-    "2",
-    "3",
-    "4",
-    "5",
-    "6",
-    "7",
-    "8",
-    "9",
-    "a",
-    "b",
-    "c",
-    "d",
-    "e",
-    "f",
-    "g",
-    "h",
-    "i",
-    "j",
-    "k",
-    "l",
-    "m",
-    "n",
-    "o",
-    "p",
-    "q",
-    "r",
-    "s",
-    "t",
-    "u",
-    "v",
-    "w",
-    "x",
-    "y",
-    "z",
-    "A",
-    "B",
-    "C",
-    "D",
-    "E",
-    "F",
-    "G",
-    "H",
-    "I",
-    "J",
-    "K",
-    "L",
-    "M",
-    "N",
-    "O",
-    "P",
-    "Q",
-    "R",
-    "S",
-    "T",
-    "U",
-    "V",
-    "W",
-    "X",
-    "Y",
-    "Z",
-  ];
+    '0',
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    'a',
+    'b',
+    'c',
+    'd',
+    'e',
+    'f',
+    'g',
+    'h',
+    'i',
+    'j',
+    'k',
+    'l',
+    'm',
+    'n',
+    'o',
+    'p',
+    'q',
+    'r',
+    's',
+    't',
+    'u',
+    'v',
+    'w',
+    'x',
+    'y',
+    'z',
+    'A',
+    'B',
+    'C',
+    'D',
+    'E',
+    'F',
+    'G',
+    'H',
+    'I',
+    'J',
+    'K',
+    'L',
+    'M',
+    'N',
+    'O',
+    'P',
+    'Q',
+    'R',
+    'S',
+    'T',
+    'U',
+    'V',
+    'W',
+    'X',
+    'Y',
+    'Z'
+  ]
   // 随机产生
   // 随机产生
   if (randomFlag) {
   if (randomFlag) {
-    range = Math.round(Math.random() * (max - min)) + min;
+    range = Math.round(Math.random() * (max - min)) + min
   }
   }
   for (var i = 0; i < range; i++) {
   for (var i = 0; i < range; i++) {
-    const pos = Math.round(Math.random() * (arr.length - 1));
-    str += arr[pos];
+    const pos = Math.round(Math.random() * (arr.length - 1))
+    str += arr[pos]
   }
   }
-  return str;
+  return str
 }
 }
 
 
-const encodeStr = (str: string, strv = "") => {
-  const NUM = 2;
-  const front = randomWord(false, 8);
-  const middle = randomWord(false, 8);
-  const end = randomWord(false, 8);
+const encodeStr = (str: string, strv = '') => {
+  const NUM = 2
+  const front = randomWord(false, 8)
+  const middle = randomWord(false, 8)
+  const end = randomWord(false, 8)
 
 
-  const str1 = str.substring(0, NUM);
-  const str2 = str.substring(NUM);
+  const str1 = str.substring(0, NUM)
+  const str2 = str.substring(NUM)
 
 
   if (strv) {
   if (strv) {
-    const strv1 = strv.substring(0, NUM);
-    const strv2 = strv.substring(NUM);
-    return [
-      front + str2 + middle + str1 + end,
-      front + strv2 + middle + strv1 + end,
-    ];
+    const strv1 = strv.substring(0, NUM)
+    const strv2 = strv.substring(NUM)
+    return [front + str2 + middle + str1 + end, front + strv2 + middle + strv1 + end]
   }
   }
 
 
-  return front + str2 + middle + str1 + end;
-};
+  return front + str2 + middle + str1 + end
+}
+
+const decodeOne = (str: string) => {
+  const str1 = str.substring(str.length - 10, str.length - 8)
+  const str2 = str.substring(8, str.length - 18)
+  return str1 + str2
+}
+
+const decodeStr = (encoded: string | [string, string]) => {
+  if (Array.isArray(encoded)) {
+    return [decodeOne(encoded[0]), decodeOne(encoded[1])]
+  }
+  return decodeOne(encoded)
+}
+
+/** 判断是否为 encodeStr 加密后的字符串(长度>=26) */
+const isEncoded = (str: string) => str && str.length >= 26
+
+/** 解密或返回原值(兼容未加密的旧数据) */
+export const decodeIfEncoded = (str: string): string => (isEncoded(str) ? decodeOne(str) : str)
+
+/** 脱敏:papers 保留前2位、后2位 */
+export const maskPapers = (str: string) => {
+  if (!str) return str
+  if (str.length <= 4) return str
+  return str.substring(0, 2) + '*'.repeat(str.length - 4) + str.substring(str.length - 2)
+}
+
+/** 脱敏:phone 保留前3位、后4位 */
+export const maskPhone = (str: string) => {
+  if (!str) return str
+  if (str.length <= 7) return str
+  return str.substring(0, 3) + '*'.repeat(str.length - 7) + str.substring(str.length - 4)
+}
+
+/** 列表显示:papers 解密后脱敏 */
+export const getStaffDisplayPapers = (val: string) => maskPapers(decodeIfEncoded(val || ''))
+
+/** 列表显示:phone 解密后脱敏 */
+export const getStaffDisplayPhone = (val: string) => maskPhone(decodeIfEncoded(val || ''))
 
 
-export default encodeStr;
+export default encodeStr
+export { decodeStr }

+ 22 - 0
src/utils/tableData.ts

@@ -245,3 +245,25 @@ export const resourceTableC = [
   ['txt', '发起日期', 'date'],
   ['txt', '发起日期', 'date'],
   ['select', '申请状态', 'status', selectObj['藏品入库申请状态']]
   ['select', '申请状态', 'status', selectObj['藏品入库申请状态']]
 ]
 ]
+
+// 藏品移库
+export const moveStorageTableC = [
+  ['txt', '移库日期', 'createTime'],
+  ['txt', '移库库房', 'extraInfo'],
+  ['txt', '申请编号', 'num'],
+  ['txt', '发起部门', 'deptName'],
+  ['txt', '发起人', 'creatorName'],
+  ['txt', '发起日期', 'date'],
+  ['select', '申请状态', 'status', selectObj['藏品入库申请状态']]
+]
+
+// 人员出入库
+export const staffTableC = [
+  ['txt', '出入日期', 'createTime'],
+  ['txt', '相关库房', 'storageName'],
+  ['txt', '申请编号', 'num'],
+  ['txt', '发起部门', 'deptName'],
+  ['txt', '发起人', 'creatorName'],
+  ['txt', '发起日期', 'date'],
+  ['select', '申请状态', 'status', selectObj['藏品入库申请状态']]
+]