shaogen1995 4 months ago
parent
commit
93bdfb67c2

+ 1 - 0
package.json

@@ -19,6 +19,7 @@
     "dayjs": "^1.11.10",
     "echarts": "^5.6.0",
     "js-base64": "^3.7.3",
+    "js-export-excel": "^1.1.4",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
     "react-redux": "^8.0.4",

+ 1 - 1
src/pages/A_workbench/A1dataSta/index.tsx

@@ -115,7 +115,7 @@ function A1dataSta() {
           </div>
           <div className='A1box1ll2'>
             <div className='A1card'>
-              <p>征集线索</p>
+              <p>藏品征集</p>
               <div>124件</div>
             </div>
             <div className='A1card'>

+ 118 - 36
src/pages/B_enterTibet/B3_4page/B3edit/main.tsx

@@ -4,7 +4,7 @@ import { useParams } from 'react-router-dom'
 import { FourTableType, TypeB3PageSta } from '../type'
 import { Button } from 'antd'
 import MyPopconfirm from '@/components/MyPopconfirm'
-import history from '@/utils/history'
+import history, { btnFlagFu2 } from '@/utils/history'
 import B3goodsTable from '../../B3goodsTable'
 import B3flowTable from '../../B3flowTable'
 import { B3TiaoObjUrl, B3TitObjKey } from './data'
@@ -13,11 +13,14 @@ import { MessageFu } from '@/utils/message'
 import {
   FourAPI_audit,
   FourAPI_create,
+  FourAPI_del,
   FourAPI_getInfo,
+  FourAPI_revocation,
   FourAPI_saveApply,
   FourAPI_saveCreate,
   FourAPI_saveDraft
 } from '@/store/action/FourAll'
+import { EXbtnFu } from '@/utils/EXBtn'
 
 type Props = {
   pageSta: TypeB3PageSta
@@ -59,11 +62,15 @@ function B3editMain({ pageSta }: Props) {
     // pageKey 1 2 3 4 入馆 入藏 登记 删除
   }, [creatFu, getInfoFu, id, key])
 
-  useEffect(() => {
+  const scrollTopFu = useCallback(() => {
     // key改变的时候,滚动到顶部  并且更新数据
     const dom = document.querySelector('#B3aTop') as HTMLDivElement
     if (dom) dom.scrollTop = 0
-  }, [key])
+  }, [])
+
+  useEffect(() => {
+    scrollTopFu()
+  }, [key, scrollTopFu])
 
   // 点击按钮调用子组件的方法获取数据
   const topRef = useRef<any>(null)
@@ -81,7 +88,6 @@ function B3editMain({ pageSta }: Props) {
       const obj = {
         ...resData.formData,
         fileIds: resData.filesRes.join(','),
-        // goodsIds: null,
         rtf: resData.rtf1
       }
       if (!resData.formData.name) return MessageFu.warning('申请名称不能为空')
@@ -145,8 +151,7 @@ function B3editMain({ pageSta }: Props) {
       const auditObj = resData.audit || {}
 
       if (!auditObj.auditSta) {
-        const dom = document.querySelector('#B3aTop') as HTMLDivElement
-        if (dom) dom.scrollTop = 0
+        scrollTopFu()
         return MessageFu.warning('请选择审批结果')
       }
 
@@ -166,7 +171,7 @@ function B3editMain({ pageSta }: Props) {
 
       // 编辑逻辑
     } else editBtnOk(obj, resData.formData.id)
-  }, [editBtnOk, pageKey, pageSta])
+  }, [editBtnOk, pageKey, pageSta, scrollTopFu])
 
   // 点击取消
   const btnX = useCallback(() => {
@@ -179,45 +184,122 @@ function B3editMain({ pageSta }: Props) {
   //     创建: '在当前页,改状态 草稿-待提交',
   //     提交: '在当前页,改状态 待提交-待审批/已完成',
   //     撤回: '在当前页,改状态 待审批/待提交',
-  //     审批: '在当前页,看下个审批环节还是不是要我审批,是就按钮还才存在。刷新页面状态。如果不是,按钮就没了,也要刷新状态',
+  //     审批: '跳审批页',
   //     编辑: '跳编辑页',
-  //     重新提交: '当前页,审批不通过-待审批。极端情况-我是发起人 又是审批人 只有一个审批环节=》已完成'
+  //     重新提交: '当前页,审批不通过-待审批'
   //   }
   // }, [])
 
-  // 查看模式下的按钮 待完善
+  // 查看的按钮创建-提交-撤回
+  const lookBtnFu = useCallback(
+    async (val: '创建' | '提交' | '撤回') => {
+      // 从顶部组件中拿到数据
+      const resData = topRef.current?.resData()
+      const resData2 = goodsTableRef.current?.resData()
+      const obj = {
+        ...resData.formData,
+        fileIds: resData.filesRes.join(','),
+        goodsIds: resData2.tableIds,
+        rtf: resData.rtf1
+      }
+
+      const obkFu = {
+        创建: await FourAPI_saveCreate(pageKey, obj),
+        提交: await FourAPI_saveApply(pageKey, obj),
+        撤回: await FourAPI_revocation(pageKey, id)
+      }
+
+      const res = obkFu[val]
+      if (res.code === 0) {
+        scrollTopFu()
+        MessageFu.success(val + '成功')
+        getInfoFu(Number(id))
+      }
+    },
+    [getInfoFu, id, pageKey, scrollTopFu]
+  )
+
+  // 查看模式点击删除
+  const delFu = useCallback(async () => {
+    const res = await FourAPI_del(pageKey, id)
+    if (res.code === 0) {
+      MessageFu.success('删除成功')
+      btnX()
+    }
+  }, [btnX, id, pageKey])
+
+  // 查看模式点击审批 编辑
+  const lookJumpFu = useCallback(
+    (val: '审批' | '编辑') => {
+      const url = Reflect.get(B3TiaoObjUrl, pageKey)
+      history.push(`${url}_edit/${val === '审批' ? 3 : 2}/${id}/${pageKey}`)
+      MessageFu.success(`已跳转至${val}页面`)
+    },
+    [id, pageKey]
+  )
+
+  // 查看模式下的按钮
   const lookBtn = useMemo(() => {
     return (
       <>
-        <Button type='primary'>创建</Button>
-        <Button type='primary'>提交</Button>
-        <MyPopconfirm
-          txtK='撤回'
-          onConfirm={() => {}}
-          Dom={
-            <Button type='primary' danger>
-              撤回
-            </Button>
-          }
-        />
-
-        <Button type='primary'>审批</Button>
-        <Button type='primary'>编辑</Button>
-        <Button type='primary'>重新提交</Button>
-        <Button type='primary'>导出</Button>
-        <MyPopconfirm
-          txtK='删除'
-          onConfirm={() => {}}
-          Dom={
-            <Button type='primary' danger>
-              删除
-            </Button>
-          }
-        />
+        {btnFlagFu2(topInfo)['创建'] ? (
+          <Button type='primary' onClick={() => lookBtnFu('创建')}>
+            创建
+          </Button>
+        ) : null}
+        {btnFlagFu2(topInfo)['提交'] ? (
+          <Button type='primary' onClick={() => lookBtnFu('提交')}>
+            提交
+          </Button>
+        ) : null}
+
+        {btnFlagFu2(topInfo)['撤回'] ? (
+          <MyPopconfirm
+            txtK='撤回'
+            onConfirm={() => lookBtnFu('撤回')}
+            Dom={
+              <Button type='primary' danger>
+                撤回
+              </Button>
+            }
+          />
+        ) : null}
+
+        {btnFlagFu2(topInfo)['审批'] ? (
+          <Button type='primary' onClick={() => lookJumpFu('审批')}>
+            审批
+          </Button>
+        ) : null}
+        {btnFlagFu2(topInfo)['编辑'] ? (
+          <Button type='primary' onClick={() => lookJumpFu('编辑')}>
+            编辑
+          </Button>
+        ) : null}
+
+        {btnFlagFu2(topInfo)['重新提交'] ? (
+          <Button type='primary' onClick={() => lookBtnFu('提交')}>
+            重新提交
+          </Button>
+        ) : null}
+
+        {btnFlagFu2(topInfo)['导出'] ? EXbtnFu() : null}
+
+        {btnFlagFu2(topInfo)['删除'] ? (
+          <MyPopconfirm
+            txtK='删除'
+            onConfirm={() => delFu()}
+            Dom={
+              <Button type='primary' danger>
+                删除
+              </Button>
+            }
+          />
+        ) : null}
+
         <Button onClick={btnX}>返回</Button>
       </>
     )
-  }, [btnX])
+  }, [btnX, delFu, lookBtnFu, lookJumpFu, topInfo])
 
   return (
     <div className={styles.B3editMain}>

+ 73 - 18
src/pages/B_enterTibet/B3_4page/index.tsx

@@ -1,17 +1,18 @@
 import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
 import { Button, DatePicker, Input, Select } from 'antd'
-import { TypeB3Form } from './type'
+import { FourTableType, TypeB3Form } from './type'
 import dayjs from 'dayjs'
 import MyTable from '@/components/MyTable'
 import MyPopconfirm from '@/components/MyPopconfirm'
-import { B3tableC } from '@/utils/tableData'
-import history from '@/utils/history'
+import { B3tableC, statusObj } from '@/utils/tableData'
+import history, { btnFlagFu } from '@/utils/history'
 import { useDispatch, useSelector } from 'react-redux'
 import { FourAPI_del, FourAPI_getList, FourKeyType } from '@/store/action/FourAll'
 import { selectObj } from '@/utils/select'
 import { RootState } from '@/store'
 import { MessageFu } from '@/utils/message'
+import ExportJsonExcel from 'js-export-excel'
 const { RangePicker } = DatePicker
 
 // 待完善 字段参数名字
@@ -67,6 +68,7 @@ function B34page({ pageSta }: Props) {
 
   const [formData, setFormData] = useState(B3baseFormData)
   const formDataRef = useRef(B3baseFormData)
+  const formDataOldRef = useRef(B3baseFormData)
 
   useEffect(() => {
     formDataRef.current = formData
@@ -99,6 +101,7 @@ function B34page({ pageSta }: Props) {
 
   // 封装发送请求的函数
   const getListFu = useCallback(() => {
+    formDataOldRef.current = { ...formDataRef.current }
     dispatch(FourAPI_getList(formDataRef.current, pagekey))
   }, [dispatch, pagekey])
 
@@ -162,23 +165,35 @@ function B34page({ pageSta }: Props) {
   )
 
   const tableLastBtn = useMemo(() => {
-    //   看状态和账号角色显示按钮 待完善
     return [
       {
         title: '操作',
-        render: (item: any) => {
-          return (
+        render: (item: FourTableType) => {
+          let obj = btnFlagFu(item)
+          return !Object.values(obj).some(Boolean) ? (
+            '-'
+          ) : (
             <>
-              <Button size='small' type='text' onClick={() => btnFu(item.id, '2')}>
-                编辑
-              </Button>
-              <Button size='small' type='text' onClick={() => btnFu(item.id, '3')}>
-                审批
-              </Button>
-              <Button size='small' type='text' onClick={() => btnFu(item.id, '4')}>
-                查看
-              </Button>
-              <MyPopconfirm txtK='删除' onConfirm={() => delTableFu(item.id)} />
+              {obj['编辑'] ? (
+                <Button size='small' type='text' onClick={() => btnFu(item.id, '2')}>
+                  编辑
+                </Button>
+              ) : null}
+
+              {obj['审批'] ? (
+                <Button size='small' type='text' onClick={() => btnFu(item.id, '3')}>
+                  审批
+                </Button>
+              ) : null}
+              {obj['查看'] ? (
+                <Button size='small' type='text' onClick={() => btnFu(item.id, '4')}>
+                  查看
+                </Button>
+              ) : null}
+
+              {obj['删除'] ? (
+                <MyPopconfirm txtK='删除' onConfirm={() => delTableFu(item.id)} />
+              ) : null}
             </>
           )
         }
@@ -186,6 +201,44 @@ function B34page({ pageSta }: Props) {
     ]
   }, [btnFu, delTableFu])
 
+  // 点击导出
+  const deriveFu = useCallback(async () => {
+    const name = '藏品' + pageSta + dayjs(new Date()).format('YYYY-MM-DD HH:mm')
+
+    const res = await FourAPI_getList(
+      {
+        ...formDataOldRef.current,
+        pageNum: 1,
+        pageSize: 99999
+      },
+      pagekey,
+      true
+    )
+
+    if (res.code === 0) {
+      if (res.data.records.length <= 0) return MessageFu.warning('当前搜索条件没有数据!')
+
+      const option = {
+        fileName: name,
+        datas: [
+          {
+            sheetData: res.data.records.map((v: FourTableType) => ({
+              ...v,
+              status: statusObj[v.status as 1]
+            })),
+            sheetName: name,
+            sheetFilter: ['num', 'name', 'deptName', 'creatorName', 'createTime', 'status'],
+            sheetHeader: ['业务单号', '申请名称', '发起部门', '发送人', '发起日期', '申请状态'],
+            columnWidths: [10, 10, 10, 10, 10, 10]
+          }
+        ]
+      }
+
+      const toExcel = new ExportJsonExcel(option) //new
+      toExcel.saveExcel() //保存
+    }
+  }, [pageSta, pagekey])
+
   return (
     <div className={styles.B34page}>
       <div className='pageTitle'>藏品{pageSta}</div>
@@ -205,8 +258,10 @@ function B34page({ pageSta }: Props) {
           ))}
         </div>
         <div className='B3toprr'>
-          {/* 待完善 */}
-          <Button type='primary'>批量导出</Button>&emsp;
+          <Button type='primary' onClick={deriveFu}>
+            批量导出
+          </Button>
+          &emsp;
           <Button type='primary' onClick={() => btnFu('null', '1')}>
             新增
           </Button>

+ 8 - 0
src/pages/B_enterTibet/B3_4page/type.d.ts

@@ -47,4 +47,12 @@ export type FourTableType = {
   type: string
   typeName: string
   updateTime: string
+  // 历史审核人
+  auditUserIds: string
+  // 抄送人
+  copyUserIds: string
+  // 申请人
+  creatorId: number
+  // 当前节点审批人
+  currentAuditUserIds: string
 }

+ 1 - 3
src/pages/D_storeManage/D1storage/D1Loc/index.module.scss

@@ -10,9 +10,7 @@
       margin-top: 15px;
       height: calc(100% - 24px);
       display: flex;
-      .D1Lmainll {
-        width: 300px;
-      }
+
       .D1Lmainrr {
         margin-left: 20px;
         width: calc(100% - 320px);

+ 125 - 10
src/pages/D_storeManage/D1storage/D1Loc/index.tsx

@@ -1,26 +1,34 @@
-import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
-import { Button, Checkbox } from 'antd'
+import { Button, Checkbox, Input, Tree, TreeDataNode } from 'antd'
 import MyTable from '@/components/MyTable'
 import { D1tableC } from '@/utils/tableData'
-// import { D1_APIgetTree } from '@/store/action/D1storage'
+import { D1_APIgetSiteList, D1_APIgetTree } from '@/store/action/D1storage'
+import { filterTreeByName } from '@/utils/history'
+import { TypeZ1dict } from '@/pages/Z_system/Z1dict/type'
+import { D1siteListType } from '../type'
 
 type Props = {
   lookFu: (val: string[]) => void
 }
 
 function D1Loc({ lookFu }: Props) {
+  const [loding, setLoding] = useState(false)
+
   const [isNull, setIsNull] = useState(false)
 
+  const [dictList, setDictList] = useState<TypeZ1dict[]>([])
+
   const getTreeFu = useCallback(async () => {
-    // const res = await D1_APIgetTree()
-    // if (res.code === 0) {
-    //   console.log(123, res)
-    // }
+    const res = await D1_APIgetTree()
+    if (res.code === 0) {
+      setDictList(res.data)
+      setLoding(true)
+      if (res.data && res.data.length) setAcShu(res.data[0].id)
+    }
   }, [])
 
   useEffect(() => {
-    // 待完善 isNull 的值来获取数据 没有接入后端接口
     getTreeFu()
   }, [getTreeFu])
 
@@ -39,9 +47,89 @@ function D1Loc({ lookFu }: Props) {
     ]
   }, [lookFu])
 
+  const [value, setValue] = useState('')
+  const [value2, setValue2] = useState('')
+
+  const timeRef = useRef(-1)
+  const valueChange = useCallback((val: string) => {
+    setValue(val.trim())
+    clearTimeout(timeRef.current)
+    timeRef.current = window.setTimeout(() => {
+      setValue2(val.trim())
+    }, 500)
+  }, [])
+
+  // value变化的时候获取所有dom 设置隐藏
+  const treeDataTemp = useMemo(() => {
+    if (value2) {
+      return filterTreeByName(dictList, value2)
+    } else return dictList
+  }, [dictList, value2])
+
+  // 搜索高亮
+  const treeData = useMemo(() => {
+    const loop = (dataTemp: any[]): TreeDataNode[] => {
+      const data = dataTemp || []
+
+      return data.map(item => {
+        const strTitle = ((item.num ? item.num + ' - ' : '') + item.name) as string
+
+        const index = strTitle.indexOf(value)
+
+        const beforeStr = strTitle.substring(0, index)
+        const afterStr = strTitle.slice(index + value.length)
+        const title =
+          index > -1 ? (
+            <span key={item.id}>
+              {beforeStr}
+              <span className='site-tree-search-value'>{value}</span>
+              {afterStr}
+            </span>
+          ) : (
+            <span key={item.id} className='hiddenTree'>
+              {strTitle}
+            </span>
+          )
+        if (item.children) {
+          return { title, key: item.id, children: loop(item.children) }
+        }
+        return {
+          title,
+          key: item.id
+        }
+      })
+    }
+
+    return loop(treeDataTemp)
+  }, [treeDataTemp, value])
+
+  // 右边表格
+  const [table, setTable] = useState<D1siteListType[]>([])
+
+  // 当前选中的树节点ID
+  const [acShu, setAcShu] = useState(0)
+
+  const getTableList = useCallback(async (id: number) => {
+    const res = await D1_APIgetSiteList(id)
+    if (res.code === 0) {
+      setTable(res.data)
+    }
+  }, [])
+
+  useEffect(() => {
+    if (acShu) getTableList(acShu)
+  }, [acShu, getTableList])
+
+  // 点击树节点
+  const onSelect = (id: any) => {
+    // console.log('点击树节点', id)
+    if (id[0]) setAcShu(id[0])
+  }
+
   return (
     <div className={styles.D1Loc}>
       <div className='D1Ltop'>
+        {/* 待完善 */}
         <Checkbox checked={isNull} onChange={e => setIsNull(e.target.checked)}>
           仅查看空置库位
         </Checkbox>
@@ -49,13 +137,40 @@ function D1Loc({ lookFu }: Props) {
 
       <div className='D1Lmain'>
         <div className='D1Lmainll'>
-          {1 + 1 === 2 ? <div className='D1null'>暂无数据</div> : '树列表'}
+          <div className='D1Lmainll1'>
+            <Input
+              style={{ width: 300 }}
+              placeholder='请输入需要搜索仓库或区域名称'
+              maxLength={30}
+              value={value}
+              onChange={e => valueChange(e.target.value)}
+            />
+          </div>
+
+          {treeDataTemp && treeDataTemp.length ? (
+            <div className='D1Lmainll2'>
+              <Tree
+                // 默认全部展开
+                defaultExpandAll={true}
+                // 数据
+                treeData={treeData}
+                // 自定义字段
+                // fieldNames={{ title: 'name', key: 'id', children: 'children' }}
+                // 选中
+                selectedKeys={[acShu]}
+                // 点击
+                onSelect={onSelect}
+              />
+            </div>
+          ) : null}
+
+          {loding && treeDataTemp.length === 0 ? <div className='D1null'>暂无数据</div> : null}
         </div>
         <div className='D1Lmainrr'>
           {/* 表格 */}
           <MyTable
             yHeight={690}
-            list={[{ id: 1, name1: 'A' }]}
+            list={table}
             columnsTemp={D1tableC}
             lastBtn={tableLastBtn}
             pagingInfo={false}

+ 1 - 3
src/pages/D_storeManage/D1storage/D1goods/index.module.scss

@@ -21,9 +21,7 @@
       margin-top: 15px;
       height: calc(100% - 24px);
       display: flex;
-      .D1Gmainll {
-        width: 300px;
-      }
+
       .D1Gmainrr {
         margin-left: 20px;
         width: calc(100% - 320px);

+ 16 - 0
src/pages/D_storeManage/D1storage/index.module.scss

@@ -24,6 +24,22 @@
       & > div {
         width: 100%;
         height: 100%;
+
+        .D1Lmainll {
+          width: 300px;
+          .D1Lmainll1 {
+            margin-bottom: 10px;
+          }
+          .D1Lmainll2 {
+            height: calc(100% - 42px);
+            overflow-y: auto;
+          }
+        }
+        .site-tree-search-value {
+          color: red;
+          font-weight: 700;
+        }
+
         .D1null {
           width: 100%;
           height: 80%;

+ 126 - 3
src/pages/D_storeManage/D1storage/index.tsx

@@ -1,8 +1,11 @@
-import React, { useRef, useState } from 'react'
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
-import { Button } from 'antd'
+import { Button, Input, Tree, TreeDataNode } from 'antd'
 import D1Loc from './D1Loc'
 import D1goods from './D1goods'
+import { filterTreeByName } from '@/utils/history'
+import { TypeZ1dict } from '@/pages/Z_system/Z1dict/type'
+import { D1_APIgetTree } from '@/store/action/D1storage'
 
 type TypeTopBtn = '按库位查看' | '按藏品查看'
 
@@ -11,11 +14,131 @@ const topBtnArr: TypeTopBtn[] = ['按库位查看', '按藏品查看']
 function D1storage() {
   const [topAc, setTopAc] = useState<TypeTopBtn>('按库位查看')
 
+  // 库位传递参数给藏品
   const lookData = useRef<string[]>([])
 
+  // 有关树------
+  const [loding, setLoding] = useState(false)
+
+  const [dictList, setDictList] = useState<TypeZ1dict[]>([])
+
+  const getTreeFu = useCallback(async () => {
+    const res = await D1_APIgetTree()
+    if (res.code === 0) {
+      setDictList(res.data)
+      setLoding(true)
+      if (res.data && res.data.length) setAcShu(res.data[0].id)
+    }
+  }, [])
+
+  useEffect(() => {
+    getTreeFu()
+  }, [getTreeFu])
+
+  // 当前选中的树节点ID
+  const [acShu, setAcShu] = useState(0)
+
+  // 点击树节点
+  const onSelect = (id: any) => {
+    // console.log('点击树节点', id)
+    if (id[0]) setAcShu(id[0])
+  }
+
+  const [value, setValue] = useState('')
+  const [value2, setValue2] = useState('')
+
+  const timeRef = useRef(-1)
+  const valueChange = useCallback((val: string) => {
+    setValue(val.trim())
+    clearTimeout(timeRef.current)
+    timeRef.current = window.setTimeout(() => {
+      setValue2(val.trim())
+    }, 500)
+  }, [])
+
+  // value变化的时候获取所有dom 设置隐藏
+  const treeDataTemp = useMemo(() => {
+    if (value2) {
+      return filterTreeByName(dictList, value2)
+    } else return dictList
+  }, [dictList, value2])
+
+  // 搜索高亮
+  const treeData = useMemo(() => {
+    const loop = (dataTemp: any[]): TreeDataNode[] => {
+      const data = dataTemp || []
+
+      return data.map(item => {
+        const strTitle = ((item.num ? item.num + ' - ' : '') + item.name) as string
+
+        const index = strTitle.indexOf(value)
+
+        const beforeStr = strTitle.substring(0, index)
+        const afterStr = strTitle.slice(index + value.length)
+        const title =
+          index > -1 ? (
+            <span key={item.id}>
+              {beforeStr}
+              <span className='site-tree-search-value'>{value}</span>
+              {afterStr}
+            </span>
+          ) : (
+            <span key={item.id} className='hiddenTree'>
+              {strTitle}
+            </span>
+          )
+        if (item.children) {
+          return { title, key: item.id, children: loop(item.children) }
+        }
+        return {
+          title,
+          key: item.id
+        }
+      })
+    }
+
+    return loop(treeDataTemp)
+  }, [treeDataTemp, value])
+
+  // 数列表
+  const TreeDom = useMemo(() => {
+    return (
+      <div className='D1Lmainll'>
+        <div className='D1Lmainll1'>
+          <Input
+            style={{ width: 300 }}
+            placeholder='请输入需要搜索仓库或区域名称'
+            maxLength={30}
+            value={value}
+            onChange={e => valueChange(e.target.value)}
+          />
+        </div>
+
+        {treeDataTemp && treeDataTemp.length ? (
+          <div className='D1Lmainll2'>
+            <Tree
+              // 默认全部展开
+              defaultExpandAll={true}
+              // 数据
+              treeData={treeData}
+              // 自定义字段
+              // fieldNames={{ title: 'name', key: 'id', children: 'children' }}
+              // 选中
+              selectedKeys={[acShu]}
+              // 点击
+              onSelect={onSelect}
+            />
+          </div>
+        ) : null}
+
+        {loding && treeDataTemp.length === 0 ? <div className='D1null'>暂无数据</div> : null}
+      </div>
+    )
+  }, [])
+
   return (
     <div className={styles.D1storage}>
-      <div className='pageTitle'>库房管理</div>
+      <div className='pageTitle'>库管理</div>
       <div className='D1top'>
         <span className='D1topllTxt'>仓库列表</span>
         {topBtnArr.map(v => (

+ 14 - 0
src/pages/D_storeManage/D1storage/type.d.ts

@@ -0,0 +1,14 @@
+export type D1siteListType = {
+  createTime: string
+  creatorId?: any
+  creatorName: string
+  description: string
+  id: number
+  layer1: number
+  layer2: number
+  layer3: number
+  regionId: number
+  regionName: string
+  storageId: number
+  updateTime: string
+}

+ 2 - 2
src/pages/D_storeManage/D2storSet/D2site/D2Sarea.tsx

@@ -45,8 +45,8 @@ function D2Sarea({ sId, closeFu }: Props) {
   // 输入框变化
   const inputChangeFu = useCallback(
     (id: string, val: string, key: 'name' | 'description') => {
-      let valRes = key === 'name' ? val.toUpperCase() : val
-      valRes = valRes.trim()
+      // let valRes = key === 'name' ? val.toUpperCase() : val
+      let valRes = val.trim()
 
       const newList = list.map(v => ({
         ...v,

+ 2 - 2
src/pages/Layout/data.ts

@@ -112,11 +112,11 @@ const tabLeftArr: RouterType = [
   },
   {
     id: 4,
-    name: '库管理',
+    name: '库管理',
     son: [
       {
         id: 410,
-        name: '库管理',
+        name: '库管理',
         path: '/storage',
         Com: React.lazy(() => import('../D_storeManage/D1storage'))
       },

+ 1 - 1
src/pages/Y_goodsDetails/Y1cathet/Y1main.tsx

@@ -50,7 +50,7 @@ function Y1main({ sId }: Props) {
           {tabAc === '藏品信息' ? <Y11com info={info} /> : null}
           {tabAc === '库存信息' ? <Y22com info={info} /> : null}
           {tabAc === '藏品附件' ? <Y33com sId={sId} /> : null}
-          {tabAc === '藏品日志' ? <Y44com info={info} /> : null}
+          {tabAc === '藏品日志' ? <Y44com sId={sId} /> : null}
         </div>
       ) : null}
     </div>

+ 2 - 3
src/pages/Y_goodsDetails/Y1cathet/Y44com.tsx

@@ -5,7 +5,6 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import { TypeY44form } from './type'
 import { B3_4inputKeyArr } from '@/pages/B_enterTibet/B3_4page'
 import dayjs from 'dayjs'
-import { C1GoodType } from '@/pages/C_goodsManage/C1ledger/type'
 const { RangePicker } = DatePicker
 
 type InputKeyType = 'aaaa' | 'bbbb' | 'cccc' | 'dddd'
@@ -26,10 +25,10 @@ const baseFormData: TypeY44form = {
 
 type Props = {
   isLook?: boolean
-  info: C1GoodType
+  sId: number
 }
 
-function Y44com({ isLook, info }: Props) {
+function Y44com({ isLook, sId }: Props) {
   const [formData, setFormData] = useState(baseFormData)
   const formDataRef = useRef(baseFormData)
 

+ 2 - 2
src/pages/Y_goodsDetails/Y2look/index.tsx

@@ -119,7 +119,7 @@ function Y2look() {
           </Dropdown>
           &emsp;
           <Dropdown menu={{ items: items2 }} placement='bottom' arrow>
-            <Button type='primary'>库管理</Button>
+            <Button type='primary'>库管理</Button>
           </Dropdown>
           &emsp;
           <Dropdown menu={{ items: items3 }} placement='bottom' arrow>
@@ -135,7 +135,7 @@ function Y2look() {
           {tabAc === '藏品信息' ? <Y11com info={info} /> : null}
           {tabAc === '库存信息' ? <Y22com info={info} isLook={true} /> : null}
           {tabAc === '藏品附件' ? <Y33com sId={sId} isLook={true} /> : null}
-          {tabAc === '藏品日志' ? <Y44com info={info} isLook={true} /> : null}
+          {tabAc === '藏品日志' ? <Y44com sId={sId} isLook={true} /> : null}
         </div>
       ) : null}
     </div>

+ 20 - 3
src/pages/Z_system/Z1dict/index.tsx

@@ -9,6 +9,7 @@ import { TypeZ1dict } from './type'
 import Z1add, { Z1AddInfoType } from './Z1add'
 import MyPopconfirm from '@/components/MyPopconfirm'
 import { MessageFu } from '@/utils/message'
+import { filterTreeByName } from '@/utils/history'
 
 function Z1dict() {
   const [loding, setLoding] = useState(false)
@@ -42,7 +43,7 @@ function Z1dict() {
     setTopId(value)
   }, [])
 
-  const { dictAll, dictList: treeDataTemp } = useSelector((state: RootState) => state.Z1dict)
+  const { dictAll, dictList } = useSelector((state: RootState) => state.Z1dict)
 
   // 点击重置
   const resetFu = useCallback(
@@ -54,6 +55,7 @@ function Z1dict() {
       } else dispatch(Z1_APIgetDict(topId[1]))
 
       setValue('')
+      setValue2('')
     },
     [dispatch, topId]
   )
@@ -69,11 +71,24 @@ function Z1dict() {
   }
 
   const [value, setValue] = useState('')
+  const [value2, setValue2] = useState('')
 
+  const timeRef = useRef(-1)
   const valueChange = useCallback((val: string) => {
-    setValue(val)
+    setValue(val.trim())
+    clearTimeout(timeRef.current)
+    timeRef.current = window.setTimeout(() => {
+      setValue2(val.trim())
+    }, 500)
   }, [])
 
+  // value变化的时候获取所有dom 设置隐藏
+  const treeDataTemp = useMemo(() => {
+    if (value2) {
+      return filterTreeByName(dictList, value2)
+    } else return dictList
+  }, [dictList, value2])
+
   // 搜索高亮
   const treeData = useMemo(() => {
     const loop = (dataTemp: any[]): TreeDataNode[] => {
@@ -94,7 +109,9 @@ function Z1dict() {
               {afterStr}
             </span>
           ) : (
-            <span key={item.id}>{strTitle}</span>
+            <span key={item.id} className='hiddenTree'>
+              {strTitle}
+            </span>
           )
         if (item.children) {
           return { title, key: item.id, children: loop(item.children) }

+ 3 - 0
src/pages/Z_system/Z1dict/type.d.ts

@@ -3,8 +3,11 @@ export type TypeZ1dict = {
   parentId: string
   sort: number
   name: string
+  num: string
   description: any
   ancestor: string
   level: number
   children?: TypeZ1dict[]
+
+  __keep?: boolean
 }

+ 2 - 13
src/pages/Z_system/Z6user/index.tsx

@@ -11,7 +11,7 @@ import { Z6tableC } from '@/utils/tableData'
 import MyPopconfirm from '@/components/MyPopconfirm'
 import { getUserListAPI, userPassResetAPI, userRemoveAPI } from '@/store/action/Z6user'
 import { D4_APIgetTree } from '@/store/action/Z4organization'
-import { treeLastIdFindFatherFu } from './data'
+import { buMenRes } from '@/utils/history'
 
 const baseFormData: UserTableAPIType = {
   pageNum: 1,
@@ -166,17 +166,6 @@ function Z6user() {
     ]
   }, [delTableFu, openEditPageFu, resetPassFu])
 
-  // 处理所属部门数据
-  const resList = useMemo(() => {
-    let arr = tableInfo.list.map((v: any) => ({
-      ...v,
-      deptNameRes: v.deptId
-        ? treeLastIdFindFatherFu(treeData, v.deptId + '', 'name').join(' / ')
-        : '-'
-    }))
-    return arr
-  }, [tableInfo.list, treeData])
-
   return (
     <div className={styles.Z6user}>
       <div className='pageTitle'>用户管理</div>
@@ -227,7 +216,7 @@ function Z6user() {
       <div className='tableBox'>
         <MyTable
           yHeight={630}
-          list={resList}
+          list={buMenRes(tableInfo.list)}
           columnsTemp={Z6tableC}
           lastBtn={tableLastBtn}
           pageNum={formData.pageNum}

+ 9 - 2
src/store/action/D1storage.ts

@@ -1,8 +1,15 @@
 import http from '@/utils/http'
 
 /**
- * 库管理-获取树列表
+ * 库管理-获取树列表
  */
 export const D1_APIgetTree = () => {
-  return http.get('cms/storage/st/getTree')
+  return http.get('cms/site/getTree')
+}
+
+/**
+ * 分库管理 - 按库位查看-列表
+ */
+export const D1_APIgetSiteList = (id: number) => {
+  return http.get(`cms/site/page/${id}`)
 }

+ 14 - 0
src/store/action/FourAll.ts

@@ -103,6 +103,20 @@ export const FourAPI_del = (key: FourKeyType, id: number) => {
 }
 
 /**
+ * 撤回
+ */
+export const FourAPI_revocation = (key: FourKeyType, id: number) => {
+  const urlObj = {
+    1: `cms/orderHouse/revocation/${id}`,
+    2: `cms/orderHide/revocation/${id}`,
+    3: `cms/register/revocation/${id}`,
+    4: `cms/待完善删除/revocation/${id}`
+  }
+  const url = urlObj[key]
+  return http.get(url)
+}
+
+/**
  * 点击保存(编辑) 提交(审批)
  */
 export const FourAPI_saveApply = (key: FourKeyType, data: any) => {

+ 40 - 0
src/utils/EXBtn.tsx

@@ -0,0 +1,40 @@
+import { Button, Dropdown, MenuProps } from 'antd'
+
+// 待完善
+
+const items: MenuProps['items'] = [
+  {
+    key: '1',
+    label: <span className='Y2xia'>借用藏品点交凭证文件</span>
+  },
+  {
+    key: '2',
+    label: <span className='Y2xia'>藏品馆内提退凭单</span>
+  },
+  {
+    key: '3',
+    label: <span className='Y2xia'>分库藏品提退出入库记录单</span>
+  },
+  {
+    key: '4',
+    label: <span className='Y2xia'>分库藏品入库记录单</span>
+  },
+  {
+    key: '5',
+    label: <span className='Y2xia'>入馆凭证</span>
+  },
+  {
+    key: '6',
+    label: <span className='Y2xia'>藏品图片及相关数字化信息使用申请单</span>
+  }
+]
+
+// 查看详情的导出页面
+
+export const EXbtnFu = () => {
+  return (
+    <Dropdown menu={{ items }} placement='top' arrow>
+      <Button type='primary'>导出</Button>
+    </Dropdown>
+  )
+}

+ 211 - 4
src/utils/history.ts

@@ -2,17 +2,19 @@ import { TypeZ1dict } from '@/pages/Z_system/Z1dict/type'
 import { treeLastIdFindFatherFu } from '@/pages/Z_system/Z6user/data'
 import store from '@/store'
 import { createHashHistory } from 'history'
+import { getTokenInfo } from './storage'
+import { FourTableType } from '@/pages/B_enterTibet/B3_4page/type'
+import { TypeZ4Tree } from '@/pages/Z_system/Z4organization/type'
 const history = createHashHistory()
 export default history
 
-// 所有藏品详情 全部新页面打开
+// -------------------所有藏品详情 全部新页面打开-------------------
 export const openGoodsInfoFu = (id: number, src?: string) => {
   window.open(src ? src : `/#/goodsLook/${id}`, '_blank')
 }
 
+// -------------------级联回显-------------------
 let dictAll: TypeZ1dict[] = []
-
-// 级联回显
 export const resJiLianFu = (idTemp: string, isNull?: string) => {
   if (idTemp) {
     if (dictAll && dictAll.length === 0) {
@@ -30,7 +32,7 @@ export const resJiLianFu = (idTemp: string, isNull?: string) => {
   } else return isNull || '(空)'
 }
 
-// 富文本回显
+// -------------------富文本回显-------------------
 export const textFu = (val: string) => {
   let TxtRes = ''
   try {
@@ -49,3 +51,208 @@ export const textFu = (val: string) => {
 
   return TxtRes
 }
+
+// -------------------树结构的搜索过滤-------------------
+export const filterTreeByName = (tree: TypeZ1dict[], searchTemp: string): TypeZ1dict[] => {
+  const searchKey = searchTemp.toUpperCase()
+
+  const dfs = (node: TypeZ1dict): TypeZ1dict | null => {
+    // 先递归处理子节点(深度优先)
+    const filteredChildren = (node.children?.map(dfs).filter(Boolean) as TypeZ1dict[]) || []
+
+    // 判断当前节点是否匹配或子节点有匹配项
+
+    const txt = node.name.toUpperCase() + (node.num || '').toUpperCase()
+
+    const isSelfMatch = txt.includes(searchKey)
+
+    // console.log('pppppppp', isSelfMatch, searchKey, node.num)
+
+    const hasChildMatch = filteredChildren.length > 0
+
+    if (isSelfMatch || hasChildMatch) {
+      return {
+        ...node,
+        children: hasChildMatch ? filteredChildren : undefined
+      }
+    }
+    return null
+  }
+
+  return tree.map(dfs).filter(Boolean) as TypeZ1dict[]
+}
+
+// -------------------处理所属部门数据-------------------
+let buMenTree: TypeZ4Tree[] = []
+export const buMenRes = (list: any[]) => {
+  if (buMenTree && buMenTree.length === 0) {
+    buMenTree = store.getState().Z4organization.treeData
+  }
+  let arr = list.map((v: any) => ({
+    ...v,
+    deptNameRes: v.deptId
+      ? treeLastIdFindFatherFu(buMenTree, v.deptId + '', 'name').join(' / ')
+      : '-'
+  }))
+  return arr
+}
+
+// -------------------列表页面的按钮权限-------------------
+export type btnFlagTxtType = '查看' | '编辑' | '删除' | '审批' | '盘点'
+export const btnFlagFu = (item: FourTableType) => {
+  //申请单状态     申请人id      当前节点审批人     历史审批人       抄送人
+  const { status, creatorId, currentAuditUserIds, auditUserIds, copyUserIds } = item
+
+  let flagObj: { [K in btnFlagTxtType]: boolean } = {
+    查看: false,
+    编辑: false,
+    删除: false,
+    审批: false,
+    盘点: false
+  }
+
+  const myId = (getTokenInfo().user || { id: 0 }).id
+
+  //  当前节点审批人
+  const arr3 = (currentAuditUserIds || '').split(',').map(v => Number(v))
+  // 历史审批人
+  const arr4 = (auditUserIds || '').split(',').map(v => Number(v))
+  // 抄送人
+  const arr5 = (copyUserIds || '').split(',').map(v => Number(v))
+
+  const obj = {
+    1: () => {
+      //草稿
+      // 申请人是我自己 =》查看   编辑    删除
+      if (myId === creatorId) flagObj['查看'] = flagObj['编辑'] = flagObj['删除'] = true
+    },
+    6: () => {
+      //待提交
+      // 申请人是我自己 =》查看   编辑
+      if (myId === creatorId) flagObj['查看'] = flagObj['编辑'] = true
+    },
+    2: () => {
+      //待审批
+      // 申请人是我自己 =》查看
+      if (myId === creatorId) flagObj['查看'] = true
+      // 历史审批人有我 =》查看
+      if (arr4.includes(myId)) flagObj['查看'] = true
+      // 当前审批人有我 =》审批
+      if (arr3.includes(myId)) flagObj['审批'] = true
+      // 抄送人有我 =》查看
+      if (arr5.includes(myId)) flagObj['查看'] = true
+    },
+    3: () => {
+      //审批不通过
+      // 申请人是我自己 =》查看
+      if (myId === creatorId) flagObj['查看'] = true
+      // 历史审批人有我 =》查看
+      if (arr4.includes(myId)) flagObj['查看'] = true
+      // 抄送人有我 =》查看
+      if (arr5.includes(myId)) flagObj['查看'] = true
+    },
+    4: () => {
+      //已完成
+      // 申请人是我自己 =》查看
+      if (myId === creatorId) flagObj['查看'] = true
+      // 历史审批人有我 =》查看
+      if (arr4.includes(myId)) flagObj['查看'] = true
+      // 抄送人有我 =》查看
+      if (arr5.includes(myId)) flagObj['查看'] = true
+    },
+    5: () => {
+      //待盘点
+      // 申请人是我自己 =》查看 编辑 盘点
+      if (myId === creatorId) flagObj['查看'] = flagObj['编辑'] = flagObj['盘点'] = true
+    }
+  }
+  if (Reflect.get(obj, status)) Reflect.get(obj, status)()
+
+  // return flagObj
+  // 待完善
+  return { 查看: true, 编辑: true, 删除: true, 审批: true, 盘点: true }
+}
+
+// -------------------详情页面的按钮权限-------------------
+export type btnFlagTxtType2 =
+  | '创建'
+  | '提交'
+  | '撤回'
+  | '审批'
+  | '编辑'
+  | '重新提交'
+  | '导出'
+  | '删除'
+  | '盘点'
+export const btnFlagFu2 = (item: FourTableType) => {
+  //申请单状态     申请人id      当前节点审批人
+  const { status, creatorId, currentAuditUserIds } = item
+
+  let flagObj: { [K in btnFlagTxtType2]: boolean } = {
+    创建: false,
+    提交: false,
+    撤回: false,
+    审批: false,
+    编辑: false,
+    重新提交: false,
+    导出: false,
+    删除: false,
+    盘点: false
+  }
+
+  const myId = (getTokenInfo().user || { id: 0 }).id
+
+  //  当前节点审批人
+  const arr3 = (currentAuditUserIds || '').split(',').map(v => Number(v))
+
+  const obj = {
+    1: () => {
+      //草稿
+      // 我是申请人
+      if (creatorId === myId) flagObj['创建'] = flagObj['编辑'] = flagObj['删除'] = true
+    },
+    6: () => {
+      //待提交
+      // 我是申请人
+      if (creatorId === myId) flagObj['提交'] = flagObj['编辑'] = flagObj['删除'] = true
+    },
+    2: () => {
+      //待审批
+      // 我是申请人
+      if (creatorId === myId) flagObj['撤回'] = true
+      // 当前审批人有我 =》审批
+      if (arr3.includes(myId)) flagObj['审批'] = true
+    },
+    3: () => {
+      //审批不通过
+      // 我是申请人
+      if (creatorId === myId) flagObj['编辑'] = flagObj['重新提交'] = true
+    },
+    4: () => {
+      //已完成
+      // 我是申请人
+      if (creatorId === myId) flagObj['导出'] = true
+    },
+    5: () => {
+      //待盘点
+      // 我是申请人
+      if (creatorId === myId)
+        flagObj['提交'] = flagObj['盘点'] = flagObj['编辑'] = flagObj['删除'] = true
+    }
+  }
+  if (Reflect.get(obj, status)) Reflect.get(obj, status)()
+
+  return flagObj
+  // 待完善
+  // return {
+  //   创建: true,
+  //   提交: true,
+  //   撤回: true,
+  //   审批: true,
+  //   编辑: true,
+  //   重新提交: true,
+  //   导出: true,
+  //   删除: true,
+  //   盘点: true
+  // }
+}

+ 14 - 15
src/utils/tableData.ts

@@ -14,16 +14,6 @@
 //     ["text", "创建日期",'description', 50,A],
 //   ];
 
-// 申请状态
-export const statusObj = {
-  1: '草稿',
-  6: '待提交',
-  2: '待审批',
-  3: '审批不通过',
-  4: '已完成',
-  5: '待盘点'
-}
-
 // 待完善
 export const B1TableC = [
   ['txt', '线索名称', 'description'],
@@ -66,7 +56,15 @@ export const D1GtableC = [
   ['txt', '库位说明', 'description']
 ]
 
-// 待完善
+// 申请状态
+export const statusObj = {
+  1: '草稿',
+  6: '待提交',
+  2: '待审批',
+  3: '审批不通过',
+  4: '已完成',
+  5: '待盘点'
+}
 export const B3tableC = [
   ['txt', '业务单号', 'num'],
   ['txt', '申请名称', 'name'],
@@ -112,11 +110,12 @@ export const C4tableC = [
 
 // 待完善
 export const D1tableC = [
-  ['txt', '区域名称', 'name1'],
-  ['txt', '排架', 'userName'],
-  ['txt', '层数', 'userName'],
-  ['txt', '层格', 'userName'],
+  ['txt', '区域名称', 'regionName'],
+  ['txt', '排架', 'layer1'],
+  ['txt', '层数', 'layer2'],
+  ['txt', '层格', 'layer3'],
   ['txt', '库位说明', 'description'],
+  // 待完善
   ['txt', '是否空置', 'userName']
 ]
 

+ 123 - 0
yarn.lock

@@ -2799,6 +2799,19 @@ adjust-sourcemap-loader@^4.0.0:
     loader-utils "^2.0.0"
     regex-parser "^2.2.11"
 
+adler-32@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/adler-32/-/adler-32-1.2.0.tgz#6a3e6bf0a63900ba15652808cb15c6813d1a5f25"
+  integrity sha512-/vUqU/UY4MVeFsg+SsK6c+/05RZXIHZMGJA+PX5JyWI0ZRcBpupnRuPLU/NXXoFwMYCPCoxIfElM2eS+DUXCqQ==
+  dependencies:
+    exit-on-epipe "~1.0.1"
+    printj "~1.1.0"
+
+adler-32@~1.3.0:
+  version "1.3.1"
+  resolved "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz#1dbf0b36dda0012189a32b3679061932df1821e2"
+  integrity sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==
+
 agent-base@6:
   version "6.0.2"
   resolved "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@@ -3391,6 +3404,11 @@ binary-extensions@^2.0.0:
   resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
   integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
 
+blob.js@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/blob.js/-/blob.js-1.0.1.tgz#547b449b252c855313e837b53d15b41d000ea1d2"
+  integrity sha512-TkPuWPeCHBbN+LWFg7BlXdSh6stRxwmAbuirKfiiHTMmo/uQfKFQMx2jrxVUkueKRiG+Tc7Os1Zn618Yc0MZpg==
+
 bluebird@^3.7.2:
   version "3.7.2"
   resolved "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
@@ -3583,6 +3601,14 @@ case-sensitive-paths-webpack-plugin@^2.4.0:
   resolved "https://registry.npmmirror.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4"
   integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==
 
+cfb@^1.1.4:
+  version "1.2.2"
+  resolved "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz#94e687628c700e5155436dac05f74e08df23bc44"
+  integrity sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==
+  dependencies:
+    adler-32 "~1.3.0"
+    crc-32 "~1.2.0"
+
 chalk@^2.4.1, chalk@^2.4.2:
   version "2.4.2"
   resolved "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
@@ -3693,6 +3719,14 @@ coa@^2.0.2:
     chalk "^2.4.1"
     q "^1.1.2"
 
+codepage@~1.14.0:
+  version "1.14.0"
+  resolved "https://registry.npmmirror.com/codepage/-/codepage-1.14.0.tgz#8cbe25481323559d7d307571b0fff91e7a1d2f99"
+  integrity sha512-iz3zJLhlrg37/gYRWgEPkaFTtzmnEv1h+r7NgZum2lFElYQPi0/5bnmuDfODHxfp0INEfnRqyfyeIJDbb7ahRw==
+  dependencies:
+    commander "~2.14.1"
+    exit-on-epipe "~1.0.1"
+
 collect-v8-coverage@^1.0.0:
   version "1.0.2"
   resolved "https://registry.npmmirror.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9"
@@ -3759,6 +3793,16 @@ commander@^8.3.0:
   resolved "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
   integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
 
+commander@~2.14.1:
+  version "2.14.1"
+  resolved "https://registry.npmmirror.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa"
+  integrity sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==
+
+commander@~2.17.1:
+  version "2.17.1"
+  resolved "https://registry.npmmirror.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
+  integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
+
 common-path-prefix@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmmirror.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0"
@@ -3902,6 +3946,11 @@ cosmiconfig@^7.0.0:
     path-type "^4.0.0"
     yaml "^1.10.0"
 
+crc-32@~1.2.0:
+  version "1.2.2"
+  resolved "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff"
+  integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
+
 cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
   version "7.0.3"
   resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -5012,6 +5061,11 @@ execa@^5.0.0:
     signal-exit "^3.0.3"
     strip-final-newline "^2.0.0"
 
+exit-on-epipe@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692"
+  integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==
+
 exit@^0.1.2:
   version "0.1.2"
   resolved "https://registry.npmmirror.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
@@ -5150,6 +5204,11 @@ file-loader@^6.2.0:
     loader-utils "^2.0.0"
     schema-utils "^3.0.0"
 
+file-saver@^1.3.3:
+  version "1.3.8"
+  resolved "https://registry.npmmirror.com/file-saver/-/file-saver-1.3.8.tgz#e68a30c7cb044e2fb362b428469feb291c2e09d8"
+  integrity sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==
+
 filelist@^1.0.4:
   version "1.0.4"
   resolved "https://registry.npmmirror.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
@@ -5290,6 +5349,11 @@ forwarded@0.2.0:
   resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
   integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
 
+frac@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz#3d74f7f6478c88a1b5020306d747dc6313c74d0b"
+  integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==
+
 fraction.js@^4.3.7:
   version "4.3.7"
   resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
@@ -6768,6 +6832,16 @@ js-cookie@^2.x.x:
   resolved "https://registry.npmmirror.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
   integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
 
+js-export-excel@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.npmmirror.com/js-export-excel/-/js-export-excel-1.1.4.tgz#65ebeb278010f301bee26bba909d7ffec7f28877"
+  integrity sha512-19m7e3Gnn4CRfHXoFrLYj4fFfJ/KpvI7HRRn25p4GXYD+AlTV+1oU24NH6S904Ksi44tSx7futxhouOPAQ22oQ==
+  dependencies:
+    blob.js "^1.0.1"
+    file-saver "^1.3.3"
+    script-loader "0.7.2"
+    xlsx "0.16.3"
+
 "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -8342,6 +8416,11 @@ pretty-format@^29.0.0, pretty-format@^29.7.0:
     ansi-styles "^5.0.0"
     react-is "^18.0.0"
 
+printj@~1.1.0:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222"
+  integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==
+
 process-nextick-args@~2.0.0:
   version "2.0.1"
   resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -8457,6 +8536,11 @@ raw-body@2.5.1:
     iconv-lite "0.4.24"
     unpipe "1.0.0"
 
+raw-loader@~0.5.1:
+  version "0.5.1"
+  resolved "https://registry.npmmirror.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
+  integrity sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q==
+
 rc-cascader@~3.21.2:
   version "3.21.2"
   resolved "https://registry.npmmirror.com/rc-cascader/-/rc-cascader-3.21.2.tgz#3421841131cdc15157201fefd955da31f409ac85"
@@ -9408,6 +9492,13 @@ screenfull@^5.0.0:
   resolved "https://registry.npmmirror.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba"
   integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==
 
+script-loader@0.7.2:
+  version "0.7.2"
+  resolved "https://registry.npmmirror.com/script-loader/-/script-loader-0.7.2.tgz#2016db6f86f25f5cf56da38915d83378bb166ba7"
+  integrity sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA==
+  dependencies:
+    raw-loader "~0.5.1"
+
 scroll-into-view-if-needed@^3.1.0:
   version "3.1.0"
   resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz#fa9524518c799b45a2ef6bbffb92bcad0296d01f"
@@ -9676,6 +9767,13 @@ sprintf-js@~1.0.2:
   resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
   integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
 
+ssf@~0.11.2:
+  version "0.11.2"
+  resolved "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz#0b99698b237548d088fc43cdf2b70c1a7512c06c"
+  integrity sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==
+  dependencies:
+    frac "~1.1.2"
+
 stable@^0.1.8:
   version "0.1.8"
   resolved "https://registry.npmmirror.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
@@ -10747,11 +10845,21 @@ which@^2.0.1:
   dependencies:
     isexe "^2.0.0"
 
+wmf@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz#7d19d621071a08c2bdc6b7e688a9c435298cc2da"
+  integrity sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==
+
 word-wrap@~1.2.3:
   version "1.2.5"
   resolved "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
   integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
 
+word@~0.3.0:
+  version "0.3.0"
+  resolved "https://registry.npmmirror.com/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961"
+  integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
+
 workbox-background-sync@6.6.1:
   version "6.6.1"
   resolved "https://registry.npmmirror.com/workbox-background-sync/-/workbox-background-sync-6.6.1.tgz#08d603a33717ce663e718c30cc336f74909aff2f"
@@ -10964,6 +11072,21 @@ ws@^8.13.0:
   resolved "https://registry.npmmirror.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4"
   integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==
 
+xlsx@0.16.3:
+  version "0.16.3"
+  resolved "https://registry.npmmirror.com/xlsx/-/xlsx-0.16.3.tgz#7a91a75eb939db4961122da6f949b8a8f0c8af1a"
+  integrity sha512-LInZ1OK6vpe+Em8XDZ5gDH3WixARwxI7UWc+3chLeafI6gUwECEgL43k4Tjbs1uRfkxpM7wQFy5DLE0hFBRqRw==
+  dependencies:
+    adler-32 "~1.2.0"
+    cfb "^1.1.4"
+    codepage "~1.14.0"
+    commander "~2.17.1"
+    crc-32 "~1.2.0"
+    exit-on-epipe "~1.0.1"
+    ssf "~0.11.2"
+    wmf "~1.0.1"
+    word "~0.3.0"
+
 xml-name-validator@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"