chenlei 2 tháng trước cách đây
mục cha
commit
7eb4432b6a
29 tập tin đã thay đổi với 1324 bổ sung356 xóa
  1. BIN
      public/templates/14.docx
  2. BIN
      public/templates/5.docx
  3. 20 0
      src/pages/A3_ledger/A32Routing/index.tsx
  4. 9 3
      src/pages/A3_ledger/C1ledger/index.tsx
  5. 133 0
      src/pages/A_workbench/A1dataSta/components/RelicModal/index.tsx
  6. 6 6
      src/pages/A_workbench/A1dataSta/components/Tab1/constants.ts
  7. 206 32
      src/pages/A_workbench/A1dataSta/components/Tab1/index.tsx
  8. 10 0
      src/pages/A_workbench/A1dataSta/components/Tab2/index.module.scss
  9. 278 89
      src/pages/A_workbench/A1dataSta/components/Tab2/index.tsx
  10. 50 0
      src/pages/A_workbench/A1dataSta/components/Tab3/components/StorageModal.tsx
  11. 215 86
      src/pages/A_workbench/A1dataSta/components/Tab3/index.tsx
  12. 5 4
      src/pages/A_workbench/A1dataSta/panel.module.scss
  13. 1 1
      src/pages/A_workbench/A1dataSta/panel.tsx
  14. 4 4
      src/pages/A_workbench/A3flow/data.ts
  15. 6 3
      src/pages/A_workbench/A3flow/index.tsx
  16. 1 1
      src/pages/A_workbench/A3flow/types.ts
  17. 4 4
      src/pages/A_workbench/A4voucher/constants.ts
  18. 1 1
      src/pages/A_workbench/A4voucher/index.tsx
  19. 2 2
      src/pages/A_workbench/A4voucher/types.ts
  20. 5 1
      src/pages/C_goodsManage/C1register/C1look/index.tsx
  21. 5 3
      src/pages/C_goodsManage/C21wealth/index.tsx
  22. 2 6
      src/pages/F_exhibition/F1exhibition/F1edit/index.tsx
  23. 11 7
      src/pages/F_exhibition/F1exhibition/index.tsx
  24. 24 0
      src/store/action/A1dataSta.ts
  25. 7 0
      src/store/action/C1ledger.ts
  26. 9 1
      src/utils/EXBtn.tsx
  27. 69 0
      src/utils/exportExcelUtils.ts
  28. 240 101
      src/utils/exportTemplates.ts
  29. 1 1
      src/utils/select.ts

BIN
public/templates/14.docx


BIN
public/templates/5.docx


+ 20 - 0
src/pages/A3_ledger/A32Routing/index.tsx

@@ -19,6 +19,7 @@ import store from '@/store'
 import A32table from './A32table'
 import { selectObj } from '@/utils/select'
 import { FourTableType } from '@/pages/B_enterTibet/B1collect/type'
+import { EXPORT_WORD_ENUM, exportWordHandler } from '@/utils/exportTemplates'
 
 function A32Routing() {
   const dispatch = useDispatch()
@@ -232,6 +233,21 @@ function A32Routing() {
     [formData, txtChangeFu]
   )
 
+  const deriveFu2 = useCallback(async () => {
+    const obj = {
+      ...formDataOldRef.current,
+      pageNum: 1,
+      pageSize: 99999
+    }
+    obj[treeKey.current as 'name'] = treeAc!
+
+    const res = await A32_APIgetList(obj, true)
+
+    if (res.code === 0) {
+      exportWordHandler(EXPORT_WORD_ENUM.COLLECTION_LEDGER, { collects: res.data.records })
+    }
+  }, [treeAc])
+
   // 点击导出
   const deriveFu = useCallback(async () => {
     const obj = {
@@ -338,6 +354,10 @@ function A32Routing() {
             批量导出
           </Button>
           &emsp;
+          <Button type='primary' onClick={deriveFu2} disabled={!treeAc}>
+            导出藏品总账
+          </Button>
+          &emsp;
           <Button danger={advanced} onClick={() => advancedFu(!advanced)}>
             {advanced ? '收起' : ''}高级搜索
           </Button>

+ 9 - 3
src/pages/A3_ledger/C1ledger/index.tsx

@@ -16,6 +16,8 @@ import C8recycleBin from '../ComPage/C8recycleBin'
 import C4import from '../ComPage/C4import'
 import { selectObj } from '@/utils/select'
 import { FourTableType } from '@/pages/B_enterTibet/B1collect/type'
+import { EXPORT_WORD_ENUM } from '@/utils/exportTemplates'
+import { EXbtnFu } from '@/utils/EXBtn'
 
 function C1ledger() {
   // 是否是藏品查询的藏品信息
@@ -282,9 +284,13 @@ function C1ledger() {
           <Button type='primary' onClick={deriveFu} hidden={antiqueSearch}>
             批量导出
           </Button>
-          <Button type='primary' onClick={deriveFu} hidden={antiqueSearch}>
-            导出藏品总账
-          </Button>
+          &emsp;
+          {!antiqueSearch &&
+            EXbtnFu({ collects: tableInfo.list }, [
+              EXPORT_WORD_ENUM.COLLECTION_LEDGER,
+              EXPORT_WORD_ENUM.COLLECTION_LOG
+            ])}
+          &emsp;
           <Button danger={advanced} onClick={() => advancedFu(!advanced)}>
             {advanced ? '收起' : ''}高级搜索
           </Button>

+ 133 - 0
src/pages/A_workbench/A1dataSta/components/RelicModal/index.tsx

@@ -0,0 +1,133 @@
+import { Z1_APIgetDictZhi } from '@/store/action/Z1dict'
+import { Checkbox, Collapse, CollapseProps, Modal, Tree } from 'antd'
+import { FC, useEffect, useState } from 'react'
+
+export interface RelicModalProps {
+  isModalOpen: boolean
+  checkedIds: string[][]
+  setIsModalOpen: (v: boolean) => void
+  onChange: (v: string[][]) => void
+  onColumnChange?: (v: any[]) => void
+}
+
+const getMatchedItems = (res: any[], checkedIds: string[]) => {
+  const result: { title: string; dataIndex: string }[] = []
+
+  function traverse(nodes: any[]) {
+    for (const node of nodes) {
+      if (checkedIds.includes(node.id || node.value)) {
+        result.push({ title: node.name || node.label, dataIndex: node.id || node.value })
+      }
+      if (node.children) {
+        traverse(node.children)
+      }
+    }
+  }
+
+  traverse(res)
+  return result
+}
+
+export const RelicModal: FC<RelicModalProps> = ({
+  isModalOpen,
+  checkedIds,
+  setIsModalOpen,
+  onChange,
+  onColumnChange
+}) => {
+  const [cate1List, setCate1List] = useState<any[]>([])
+  const [cate2List, setCate2List] = useState<any[]>([])
+  const [cate3List, setCate3List] = useState<any[]>([])
+  const [checked1Ids, setChecked1Ids] = useState<string[]>([])
+  const [checked2Ids, setChecked2Ids] = useState<string[]>([])
+  const [checked3Ids, setChecked3Ids] = useState<string[]>([])
+  const categories: CollapseProps['items'] = [
+    {
+      label: '文物类别',
+      key: 12001,
+      children: (
+        <Checkbox.Group value={checked1Ids} options={cate1List} onChange={v => setChecked1Ids(v)} />
+      )
+    },
+    {
+      label: '馆内分类1',
+      key: 12015,
+      children: (
+        <Checkbox.Group value={checked2Ids} options={cate2List} onChange={v => setChecked2Ids(v)} />
+      )
+    },
+    {
+      label: '馆内分类2',
+      key: 12016,
+      children: (
+        <Tree
+          checkable
+          checkedKeys={checked3Ids}
+          treeData={cate3List}
+          fieldNames={{ title: 'name', key: 'id' }}
+          onCheck={v => setChecked3Ids(v as string[])}
+        />
+      )
+    }
+  ]
+
+  const handleOk = () => {
+    onChange([[...checked1Ids], [...checked2Ids], [...checked3Ids]])
+    setIsModalOpen(false)
+  }
+
+  const handleCancel = () => {
+    setIsModalOpen(false)
+  }
+
+  const getList = async (id: number) => {
+    const res = await Z1_APIgetDictZhi(`${id}`)
+    return res
+  }
+
+  const init = () => {
+    Promise.all([
+      getList(categories[0].key as number),
+      getList(categories[1].key as number),
+      getList(categories[2].key as number)
+    ]).then(res => {
+      setCate1List(res[0].data.map((i: any) => ({ label: i.name, value: i.id })))
+      setCate2List(res[1].data.map((i: any) => ({ label: i.name, value: i.id })))
+      setCate3List(res[2].data)
+    })
+  }
+
+  useEffect(() => {
+    init()
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [])
+
+  useEffect(() => {
+    if (isModalOpen) {
+      setChecked1Ids(checkedIds[0])
+      setChecked2Ids(checkedIds[1])
+      setChecked3Ids(checkedIds[2])
+    }
+  }, [checkedIds, isModalOpen])
+
+  useEffect(() => {
+    onColumnChange?.([
+      ...getMatchedItems(cate1List, checkedIds[0]),
+      ...getMatchedItems(cate2List, checkedIds[1]),
+      ...getMatchedItems(cate3List, checkedIds[2])
+    ])
+  }, [cate1List, cate2List, cate3List, checkedIds, onColumnChange])
+
+  return (
+    <Modal
+      title='统计分类'
+      destroyOnClose
+      width={800}
+      open={isModalOpen}
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <Collapse items={categories} defaultActiveKey={categories.map(i => i.key as number)} />
+    </Modal>
+  )
+}

+ 6 - 6
src/pages/A_workbench/A1dataSta/components/Tab1/constants.ts

@@ -1,8 +1,8 @@
 export const NUMBER_OF_COLLECTION_COLUMNS = [
-  ['txt', '藏品总数', 'num'],
-  ['txt', '一级藏品', 'num'],
-  ['txt', '二级藏品', 'num'],
-  ['txt', '三级藏品', 'num'],
-  ['txt', '未定级', 'num'],
-  ['txt', '一般文物', 'num']
+  ['txt', '藏品总数', 'total'],
+  ['txt', '一级藏品', 'pcsLevel1'],
+  ['txt', '二级藏品', 'pcsLevel2'],
+  ['txt', '三级藏品', 'pcsLevel3'],
+  ['txt', '未定级', 'pcsLevel0'],
+  ['txt', '一般文物', 'pcsLevel4']
 ]

+ 206 - 32
src/pages/A_workbench/A1dataSta/components/Tab1/index.tsx

@@ -1,12 +1,183 @@
-import { FC } from 'react'
+import { FC, useCallback, useEffect, useRef, useState } from 'react'
 import style from './index.module.scss'
 import MyTable from '@/components/MyTable'
 import { NUMBER_OF_COLLECTION_COLUMNS } from './constants'
 import { Button, Select, Table } from 'antd'
+import { A1_APIGetCheck } from '@/store/action/A1dataSta'
+import dayjs from 'dayjs'
+import { RelicModal } from '../RelicModal'
+import { exportTempExcel } from '@/utils/exportExcelUtils'
 
 export const A1Tab1: FC = () => {
+  const [prevYearData, setPrevYearData] = useState<any[]>([])
+  const [curYearData, setCurYearData] = useState<any[]>([])
+  const [addData, setAddData] = useState<any[]>([])
+  const [columns, setColumns] = useState<any[]>([])
+  const [customData, setCustomData] = useState<any[]>([])
+
+  const [relicModalOpen, setRelicModalOpen] = useState(false)
+  const [checkedRelicIds, setCheckedRelicIds] = useState<string[][]>([
+    ['12501', '12502', '12503', '12504', '12505'],
+    [],
+    []
+  ])
+
+  const getDetail = async (year: number) => {
+    const res = await A1_APIGetCheck(year)
+    return JSON.parse(res.data.snap) as Record<string, number>
+  }
+
+  const getYearList = () => {
+    const currentYear = dayjs().year()
+    const years = []
+
+    for (let i = 0; i < 10; i++) {
+      years.push(currentYear - i)
+    }
+
+    return years
+  }
+
+  const years = useRef(getYearList())
+  const [curYear, setCurYear] = useState(years.current[0])
+
+  const initData = useCallback(async () => {
+    const curRes = await getDetail(curYear)
+    const prevRes = await getDetail(curYear - 1)
+    const _addData = {
+      id: 1,
+      totalIncrease: 0,
+      totalDecrease: 0,
+      level0Increase: 0,
+      level0Decrease: 0,
+      level1Increase: 0,
+      level1Decrease: 0,
+      level2Increase: 0,
+      level2Decrease: 0,
+      level3Increase: 0,
+      level3Decrease: 0,
+      level4Increase: 0,
+      level4Decrease: 0
+    }
+
+    const levels = ['total', 'level0', 'level1', 'level2', 'level3', 'level4']
+    levels.forEach(level => {
+      const curProp =
+        level === 'total' ? 'total' : `pcs${level.charAt(0).toUpperCase()}${level.slice(1)}`
+      const diff = curRes[curProp] - prevRes[curProp]
+      if (diff) {
+        // @ts-ignore
+        _addData[`${level}${diff > 0 ? 'Increase' : 'Decrease'}`] = Math.abs(diff)
+      }
+    })
+
+    setCurYearData([
+      {
+        ...curRes,
+        id: 1
+      }
+    ])
+    setPrevYearData([
+      {
+        ...prevRes,
+        id: 1
+      }
+    ])
+    setAddData([_addData])
+  }, [curYear])
+
+  const handleColumnChange = useCallback(
+    (v: any[]) => {
+      const flat = v.flat().map(i => [i.title, i.dataIndex])
+      if (!flat.length || !curYearData.length) return
+      const tempColumns: any[] = []
+      const tempData: Record<string, number> = {
+        id: 1
+      }
+      flat.forEach(arr => {
+        const keys = Object.keys(curYearData[0])
+        tempData[`${arr[1]}Increase`] = 0
+        tempData[`${arr[1]}Decrease`] = 0
+        if (keys.includes(`${arr[1]}`)) {
+          const diff = (curYearData[0]?.[arr[1]] || 0) - (prevYearData[0]?.[arr[1]] || 0)
+          tempData[`${arr[1]}${diff > 0 ? 'Increase' : 'Decrease'}`] = Math.abs(diff)
+        }
+
+        tempColumns.push({
+          title: arr[0],
+          children: [
+            {
+              title: '增',
+              dataIndex: arr[1] + 'Increase',
+              align: 'center'
+            },
+            {
+              title: '减',
+              dataIndex: arr[1] + 'Decrease',
+              align: 'center'
+            }
+          ]
+        })
+      })
+      setColumns(tempColumns)
+      setCustomData([tempData])
+    },
+    [curYearData, prevYearData]
+  )
+
+  const handleExport = () => {
+    exportTempExcel(
+      curYear + '年度核查统计',
+      worksheet => {
+        const levels = ['total', 'level1', 'level2', 'level3', 'level0', 'level4']
+
+        worksheet.addRow(['', ...NUMBER_OF_COLLECTION_COLUMNS.map(i => i[1])])
+        worksheet.addRow(['', ...NUMBER_OF_COLLECTION_COLUMNS.map(i => prevYearData[0][i[2]] || 0)])
+        worksheet.addRow(['', ...NUMBER_OF_COLLECTION_COLUMNS.map(i => i[1])])
+        worksheet.addRow(['', ...NUMBER_OF_COLLECTION_COLUMNS.map(i => curYearData[0][i[2]] || 0)])
+        worksheet.addRow(['', ...NUMBER_OF_COLLECTION_COLUMNS.map(i => i[1])])
+        worksheet.addRow([
+          '',
+          ...levels.map(level => {
+            const curProp =
+              level === 'total' ? 'total' : `pcs${level.charAt(0).toUpperCase()}${level.slice(1)}`
+            const diff = curYearData[0][curProp] - prevYearData[0][curProp]
+            return !isNaN(diff) ? diff : 0
+          })
+        ])
+        worksheet.addRow(['', ...columns.map(i => i.title)])
+        worksheet.addRow([
+          '',
+          ...columns.map(
+            // @ts-ignore
+            i => (customData[`${i.id}Increase`] || 0) - (customData[`${i.id}Decrease`] || 0)
+          )
+        ])
+        worksheet.mergeCells('A1:A2')
+        worksheet.mergeCells('A3:A4')
+        worksheet.mergeCells('A5:A6')
+        worksheet.mergeCells('A7:A8')
+        worksheet.getCell('A1').value = '上一年度馆藏数量'
+        worksheet.getCell('A3').value = '核查年度馆藏数量'
+        worksheet.getCell('A5').value = '馆藏数量增减情况'
+        worksheet.getCell('A7').value = '馆藏文物增加类别明细'
+        ;[20, 10, 10, 10, 10, 10, 10].forEach((width: number, index: number) => {
+          worksheet.getColumn(index + 1).width = width
+        })
+      },
+      [8, Math.max(columns.length, NUMBER_OF_COLLECTION_COLUMNS.length + 1)]
+    )
+  }
+
+  useEffect(() => {
+    initData()
+  }, [initData])
+
   return (
     <>
+      <div style={{ textAlign: 'right' }}>
+        <Button onClick={handleExport}>导出表格</Button>
+      </div>
       <div className={style.header}>
         <h3>上一年度馆藏数量</h3>
       </div>
@@ -15,20 +186,25 @@ export const A1Tab1: FC = () => {
         bordered
         pagination={false}
         columnsTemp={NUMBER_OF_COLLECTION_COLUMNS}
-        list={[]}
+        list={prevYearData}
       />
 
       <div className={style.header}>
         <h3>核查年度馆藏数量</h3>
 
-        <Select placeholder='请选择核查年度' />
+        <Select
+          value={curYear}
+          placeholder='请选择核查年度'
+          options={years.current.map(year => ({ label: year, value: year }))}
+          onChange={v => setCurYear(v)}
+        />
       </div>
       <MyTable
         size='small'
         bordered
         pagination={false}
         columnsTemp={NUMBER_OF_COLLECTION_COLUMNS}
-        list={[]}
+        list={curYearData}
       />
 
       <div className={style.header}>
@@ -36,6 +212,7 @@ export const A1Tab1: FC = () => {
       </div>
       <Table
         size='small'
+        rowKey='id'
         columns={[
           {
             title: '藏品总数',
@@ -102,12 +279,12 @@ export const A1Tab1: FC = () => {
             children: [
               {
                 title: '增',
-                dataIndex: 'unclassifiedIncrease',
+                dataIndex: 'level0Increase',
                 align: 'center'
               },
               {
                 title: '减',
-                dataIndex: 'unclassifiedDecrease',
+                dataIndex: 'level0Decrease',
                 align: 'center'
               }
             ]
@@ -117,18 +294,18 @@ export const A1Tab1: FC = () => {
             children: [
               {
                 title: '增',
-                dataIndex: 'generalIncrease',
+                dataIndex: 'level4Increase',
                 align: 'center'
               },
               {
                 title: '减',
-                dataIndex: 'generalDecrease',
+                dataIndex: 'level4Decrease',
                 align: 'center'
               }
             ]
           }
         ]}
-        dataSource={[]}
+        dataSource={addData}
         bordered
         pagination={false}
       />
@@ -136,30 +313,27 @@ export const A1Tab1: FC = () => {
       <div className={style.header}>
         <h3>馆藏文物增加类别明细</h3>
 
-        <Button type='primary'>设置统计类别</Button>
+        <Button type='primary' onClick={() => setRelicModalOpen(true)}>
+          设置统计分类
+        </Button>
       </div>
-      <Table
-        size='small'
-        columns={[
-          {
-            title: '藏品总数',
-            children: [
-              {
-                title: '增',
-                dataIndex: 'totalIncrease',
-                align: 'center'
-              },
-              {
-                title: '减',
-                dataIndex: 'totalDecrease',
-                align: 'center'
-              }
-            ]
-          }
-        ]}
-        dataSource={[]}
-        bordered
-        pagination={false}
+      {columns.length ? (
+        <Table
+          rowKey='id'
+          size='small'
+          columns={columns}
+          dataSource={customData}
+          bordered
+          pagination={false}
+        />
+      ) : undefined}
+
+      <RelicModal
+        checkedIds={checkedRelicIds}
+        isModalOpen={relicModalOpen}
+        setIsModalOpen={setRelicModalOpen}
+        onChange={setCheckedRelicIds}
+        onColumnChange={handleColumnChange}
       />
     </>
   )

+ 10 - 0
src/pages/A_workbench/A1dataSta/components/Tab2/index.module.scss

@@ -0,0 +1,10 @@
+.A1Tab2 {
+  :global {
+    .C1top {
+      margin-bottom: 15px;
+      display: flex;
+      align-items: center;
+      gap: 15px;
+    }
+  }
+}

+ 278 - 89
src/pages/A_workbench/A1dataSta/components/Tab2/index.tsx

@@ -1,114 +1,294 @@
-import { Table } from 'antd'
-import { FC } from 'react'
+import { A1_APIGetDynamic } from '@/store/action/A1dataSta'
+import { Button, Select, Table } from 'antd'
+import dayjs from 'dayjs'
+import { FC, useCallback, useEffect, useRef, useState } from 'react'
+import style from './index.module.scss'
+import { RelicModal } from '../RelicModal'
+import { exportTempExcel } from '@/utils/exportExcelUtils'
+import { getExcelColumnLetter } from '@/utils/exportWordUtils'
+
+const QUARTER_MAP: Record<number, string> = {
+  1: '第一季度',
+  2: '第二季度',
+  3: '第三季度',
+  4: '第四季度'
+}
 
 export const A1Tab2: FC = () => {
+  const getYearList = () => {
+    const currentYear = dayjs().year()
+    const years = []
+
+    for (let i = 0; i < 10; i++) {
+      years.push(currentYear - i)
+    }
+
+    return years
+  }
+
+  const years = useRef(getYearList())
+  const [curYear, setCurYear] = useState(years.current[0])
+  const [quarter, setQuarter] = useState<number>(1)
+  const [list, setList] = useState<any[]>([])
+  const [sourceData, setSourceData] = useState<any>({})
+
+  const [relicModalOpen, setRelicModalOpen] = useState(false)
+  const [checkedRelicIds, setCheckedRelicIds] = useState<string[][]>([
+    ['12501', '12502', '12503', '12504', '12505'],
+    [],
+    []
+  ])
+
+  const getDetail = useCallback(async () => {
+    const res = await A1_APIGetDynamic({
+      quarter,
+      year: curYear
+    })
+    setSourceData(res.data)
+  }, [curYear, quarter])
+
+  const handleColumnChange = useCallback(
+    (v: any[]) => {
+      const temp: any[] = []
+      Object.keys(sourceData).forEach(id => {
+        if (id === '0')
+          temp.push({
+            name: '全部',
+            id,
+            ...sourceData[id]
+          })
+        const relic = v.find(i => i.dataIndex === id)
+        if (relic) {
+          temp.push({
+            id,
+            ...sourceData[id],
+            name: relic.title
+          })
+        }
+      })
+      setList(temp)
+    },
+    [sourceData]
+  )
+
+  const handleExport = () => {
+    exportTempExcel(
+      curYear + '库存文物动态统计',
+      worksheet => {
+        const line2 = [
+          '',
+          '合计',
+          '新收',
+          '',
+          '',
+          '',
+          '',
+          '其他增加',
+          '提陈',
+          '在陈',
+          '退陈',
+          '合计',
+          '借出',
+          '在借',
+          '退还',
+          '合计'
+        ]
+        worksheet.addRow([
+          '',
+          `${curYear}年${QUARTER_MAP[quarter]}库存数`,
+          `${curYear}年${QUARTER_MAP[quarter]}增加数`
+        ])
+        worksheet.addRow(line2)
+        worksheet.addRow(['', '', '征购', '捐赠', '移交', '上交', '调拨'])
+        worksheet.mergeCells('A1:A3')
+        worksheet.getCell('A1').value = '项目/类目/类别'
+        worksheet.mergeCells('C1:H1')
+        worksheet.mergeCells('I1:L1')
+        worksheet.getCell('I1').value = '陈列'
+        worksheet.mergeCells('M1:P1')
+        worksheet.getCell('M1').value = '借出'
+        worksheet.mergeCells('B2:B3')
+        worksheet.mergeCells('C2:G2')
+        worksheet.mergeCells('H2:H3')
+        worksheet.getRow(1).height = 60
+        line2.forEach((_, index) => {
+          if (index < 8) return
+          console.log(getExcelColumnLetter(index))
+          worksheet.mergeCells(`${getExcelColumnLetter(index)}2:${getExcelColumnLetter(index)}3`)
+        })
+        list.forEach(i => {
+          worksheet.addRow([
+            i.name,
+            i.pcsTotal,
+            i.pcsZj,
+            i.pcsJz,
+            i.pcsYj,
+            i.pcsSj,
+            i.pcsDb,
+            i.pcsOther,
+            i.cl1,
+            i.cl2,
+            i.cl3,
+            i.clTotal,
+            i.jc1,
+            i.jc2,
+            i.jc3,
+            i.jcTotal
+          ])
+        })
+        ;[20].forEach((width: number, index: number) => {
+          worksheet.getColumn(index + 1).width = width
+        })
+      },
+      [3 + list.length, 16]
+    )
+  }
+
+  useEffect(() => {
+    getDetail()
+  }, [getDetail])
+
   return (
-    <>
+    <div className={style.A1Tab2}>
+      <div className='C1top'>
+        <Button type='primary' onClick={() => setRelicModalOpen(true)}>
+          设置统计分类
+        </Button>
+        <Select
+          value={curYear}
+          placeholder='请选择年份'
+          style={{ width: 120 }}
+          options={years.current.map(year => ({ label: year, value: year }))}
+          onChange={v => setCurYear(v)}
+        />
+        <Select
+          value={quarter}
+          placeholder='请选择季度'
+          style={{ width: 120 }}
+          options={[
+            {
+              label: '第一季度',
+              value: 1
+            },
+            {
+              label: '第二季度',
+              value: 2
+            },
+            {
+              label: '第三季度',
+              value: 3
+            },
+            {
+              label: '第四季度',
+              value: 4
+            }
+          ]}
+          onChange={v => setQuarter(v)}
+        />
+        <Button onClick={handleExport}>导出表格</Button>
+      </div>
+
       <Table
+        rowKey='id'
         size='small'
         columns={[
           {
             title: '项目/类目/类别',
-            dataIndex: 'project',
-            rowScope: 'row',
-            onCell: (_, index) => {
-              if (index === 0) return { rowSpan: 2 }
-              if (index === 1) return { rowSpan: 0 }
-              return {}
-            }
+            dataIndex: 'name',
+            rowScope: 'row'
           },
           {
-            title: '2025年第一季度库存数',
+            title: `${curYear}年${QUARTER_MAP[quarter]}库存数`,
             children: [
               {
                 width: 70,
                 title: '合计',
-                dataIndex: 'total'
+                dataIndex: 'pcsTotal'
               }
             ]
           },
           {
-            title: '2025年第一季度增加数',
+            title: `${curYear}年${QUARTER_MAP[quarter]}增加数`,
             children: [
               {
                 title: '新收',
                 children: [
                   {
                     title: '征购',
-                    dataIndex: 'a'
+                    dataIndex: 'pcsZj'
                   },
                   {
                     title: '捐赠',
-                    dataIndex: 'b'
+                    dataIndex: 'pcsJz'
                   },
                   {
                     title: '移交',
-                    dataIndex: 'c'
+                    dataIndex: 'pcsYj'
                   },
                   {
                     title: '上交',
-                    dataIndex: 'd'
+                    dataIndex: 'pcsSj'
                   },
                   {
                     title: '调拨',
-                    dataIndex: 'e'
+                    dataIndex: 'pcsDb'
                   }
                 ]
               },
               {
                 title: '其他增加',
-                dataIndex: 'f'
-              },
-              {
-                title: '调入',
-                dataIndex: 'g'
-              }
-            ]
-          },
-          {
-            title: '2025年第一季度减少数',
-            children: [
-              {
-                title: '调出',
-                dataIndex: 'a'
-              },
-              {
-                title: '拨出',
-                dataIndex: 'b'
-              },
-              {
-                title: '其他减少',
-                dataIndex: 'c'
-              }
-            ]
-          },
-          {
-            title: '2025年第一季度库存数',
-            children: [
-              {
-                title: '合计',
-                dataIndex: 'a'
+                dataIndex: 'pcsOther'
               }
+              // {
+              //   title: '调入',
+              //   dataIndex: 'g'
+              // }
             ]
           },
+          // {
+          //   title: `${curYear}年${QUARTER_MAP[quarter]}减少数`,
+          //   children: [
+          //     {
+          //       title: '调出',
+          //       dataIndex: 'a'
+          //     },
+          //     {
+          //       title: '拨出',
+          //       dataIndex: 'b'
+          //     },
+          //     {
+          //       title: '其他减少',
+          //       dataIndex: 'c'
+          //     }
+          //   ]
+          // },
+          // {
+          //   title: `${curYear}年${QUARTER_MAP[quarter]}库存数`,
+          //   children: [
+          //     {
+          //       title: '合计',
+          //       dataIndex: 'a'
+          //     }
+          //   ]
+          // },
           {
             title: '陈列',
             children: [
               {
                 title: '提陈',
-                dataIndex: 'a'
+                dataIndex: 'cl1'
               },
               {
                 title: '在陈',
-                dataIndex: 'b'
+                dataIndex: 'cl2'
               },
               {
                 title: '退陈',
-                dataIndex: 'c'
+                dataIndex: 'cl3'
               },
               {
                 title: '合计',
-                dataIndex: 'c'
+                dataIndex: 'clTotal'
               }
             ]
           },
@@ -117,57 +297,66 @@ export const A1Tab2: FC = () => {
             children: [
               {
                 title: '借出',
-                dataIndex: 'a'
+                dataIndex: 'jc1'
               },
               {
                 title: '在借',
-                dataIndex: 'b'
+                dataIndex: 'jc2'
               },
               {
                 title: '退还',
-                dataIndex: 'c'
-              },
-              {
-                title: '合计',
-                dataIndex: 'c'
-              }
-            ]
-          },
-          {
-            title: '其他出库',
-            children: [
-              {
-                title: '原出',
-                dataIndex: 'a'
-              },
-              {
-                title: '新出',
-                dataIndex: 'b'
-              },
-              {
-                title: '收回',
-                dataIndex: 'c'
+                dataIndex: 'jc3'
               },
               {
                 title: '合计',
-                dataIndex: 'c'
+                dataIndex: 'jcTotal'
               }
             ]
-          },
-          {
-            title: '备注',
-            dataIndex: 'remark',
-            align: 'center',
-            onCell: (_, index) => {
-              if (index === 0) return { rowSpan: 2 }
-              if (index === 1) return { rowSpan: 0 }
-              return {}
-            }
           }
+          // {
+          //   title: '其他出库',
+          //   children: [
+          //     {
+          //       title: '原出',
+          //       dataIndex: 'a'
+          //     },
+          //     {
+          //       title: '新出',
+          //       dataIndex: 'b'
+          //     },
+          //     {
+          //       title: '收回',
+          //       dataIndex: 'c'
+          //     },
+          //     {
+          //       title: '合计',
+          //       dataIndex: 'c'
+          //     }
+          //   ]
+          // },
+          // {
+          //   title: '备注',
+          //   dataIndex: 'remark',
+          //   align: 'center',
+          //   onCell: (_, index) => {
+          //     if (index === 0) return { rowSpan: 2 }
+          //     if (index === 1) return { rowSpan: 0 }
+          //     return {}
+          //   }
+          // }
         ]}
         bordered
+        dataSource={list}
         pagination={false}
       />
-    </>
+
+      <RelicModal
+        checkedIds={checkedRelicIds}
+        isModalOpen={relicModalOpen}
+        setIsModalOpen={setRelicModalOpen}
+        onChange={setCheckedRelicIds}
+        onColumnChange={handleColumnChange}
+      />
+    </div>
   )
 }

+ 50 - 0
src/pages/A_workbench/A1dataSta/components/Tab3/components/StorageModal.tsx

@@ -0,0 +1,50 @@
+import { TypeD2list } from '@/pages/D_storeManage/D2storSet/type'
+import { Checkbox, Modal } from 'antd'
+import { FC, useEffect, useState } from 'react'
+
+export interface StorageModalProps {
+  value: number[]
+  isModalOpen: boolean
+  list: TypeD2list[]
+  setIsModalOpen: (v: boolean) => void
+  onChange: (v: number[]) => void
+}
+
+export const StorageModal: FC<StorageModalProps> = ({
+  list,
+  value,
+  isModalOpen,
+  setIsModalOpen,
+  onChange
+}) => {
+  const [val, setVal] = useState<number[]>([])
+
+  const handleOk = () => {
+    onChange(val)
+    setIsModalOpen(false)
+  }
+
+  const handleCancel = () => {
+    setIsModalOpen(false)
+  }
+
+  useEffect(() => {
+    setVal(value)
+  }, [value, isModalOpen])
+
+  return (
+    <Modal
+      title='统计库房'
+      destroyOnClose
+      open={isModalOpen}
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <Checkbox.Group
+        value={val}
+        options={list.map(i => ({ label: i.name, value: i.id }))}
+        onChange={v => setVal(v)}
+      />
+    </Modal>
+  )
+}

+ 215 - 86
src/pages/A_workbench/A1dataSta/components/Tab3/index.tsx

@@ -1,98 +1,227 @@
-import { Table } from 'antd'
+import { A1_APIGetStorage } from '@/store/action/A1dataSta'
+import { Button, Select, Table } from 'antd'
+import dayjs from 'dayjs'
 import { isNumber } from 'lodash'
-import { FC } from 'react'
+import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import style from '../Tab2/index.module.scss'
+import { StorageModal } from './components/StorageModal'
+import { D2_APIgetList } from '@/store/action/D2storSet'
+import { useDispatch, useSelector } from 'react-redux'
+import { RootState } from '@/store'
+import { RelicModal } from '../RelicModal'
+import { exportTempExcel } from '@/utils/exportExcelUtils'
+
+const getYearList = () => {
+  const currentYear = dayjs().year()
+  const years = []
+
+  for (let i = 0; i < 10; i++) {
+    years.push(currentYear - i)
+  }
+
+  return years
+}
 
 export const A1Tab3: FC = () => {
+  const dispatch = useDispatch()
+  const years = useRef(getYearList())
+  const [curYear, setCurYear] = useState(years.current[0])
+  const [storageModalOpen, setStorageModalOpen] = useState(false)
+  const [relicModalOpen, setRelicModalOpen] = useState(false)
+  const [checkedRelicIds, setCheckedRelicIds] = useState<string[][]>([
+    ['12501', '12502', '12503', '12504', '12505'],
+    [],
+    []
+  ])
+  const [checkedStorageIds, setCheckedStorageIds] = useState<number[]>([])
+  const { list: storageIdArr } = useSelector((state: RootState) => state.D2storSet.tableInfo)
+  const [list, setList] = useState<any[]>([])
+  const [relicColumns, setRelicColumns] = useState<any[]>([])
+  const columns = useMemo(
+    () => [
+      {
+        width: 100,
+        title: '级别/数量/类别',
+        dataIndex: 'custom',
+        colSpan: 2,
+        onCell: (record: any, index: any) => {
+          const obj = {
+            rowSpan: 0,
+            colSpan: 1
+          }
+          if (index === 0) {
+            obj.rowSpan = 3
+          } else if (isNumber(index) && index > 2) {
+            obj.rowSpan = 1
+            obj.colSpan = 2
+          }
+          return obj
+        }
+      },
+      {
+        width: 100,
+        dataIndex: 'level',
+        colSpan: 0,
+        onCell: (record: any, index: any) => {
+          const obj = {
+            rowSpan: 0,
+            colSpan: 1
+          }
+          if (isNumber(index) && index <= 2) obj.rowSpan = 1
+          return obj
+        }
+      },
+      ...relicColumns.map(i => ({
+        ...i,
+        render: (value?: number) => value ?? 0
+      }))
+    ],
+    [relicColumns]
+  )
+  const dataSource = useRef<any[]>([])
+
+  useEffect(() => {
+    dispatch(D2_APIgetList({ pageNum: 1, pageSize: 99999 }))
+  }, [dispatch])
+
+  const getDetail = useCallback(async () => {
+    if (!checkedStorageIds.length) {
+      setList([])
+      return
+    }
+
+    const res = await A1_APIGetStorage(curYear)
+    const list = res.data.filter((item: any) => checkedStorageIds.includes(item.storageId))
+    let temp = [
+      {
+        id: 1,
+        custom: '珍贵文物',
+        level: '一级'
+      },
+      {
+        id: 2,
+        level: '二级'
+      },
+      {
+        id: 3,
+        level: '三级'
+      },
+      {
+        id: 4,
+        custom: '未定级'
+      },
+      {
+        id: 5,
+        custom: '一般文物'
+      }
+    ]
+    const countMap = list.reduce((acc: any, item: any) => {
+      const key = `${item.dictLevel}_${item.dictType}`
+      acc.set(key, (acc.get(key) || 0) + 1)
+      return acc
+    }, new Map<string, number>())
+
+    temp = temp.map(item => {
+      const matchedKey = [...countMap].find(([key, num]) => {
+        const [dictLevel] = key.split('_')
+        return dictLevel === item.level || dictLevel === item.custom
+      })
+
+      if (matchedKey) {
+        const [key, num] = matchedKey
+        const dictType = key.split('_')[1]
+        return {
+          ...item,
+          [dictType]: num
+        }
+      }
+      return item
+    })
+
+    setList(temp)
+    dataSource.current = res.data
+  }, [checkedStorageIds, curYear])
+
+  const handleExport = async () => {
+    const _columns = columns.filter(i => Boolean(i.title))
+
+    exportTempExcel(
+      curYear + '年度库房藏品',
+      worksheet => {
+        worksheet.addRow(['', ..._columns.map(i => i.title)])
+
+        list.forEach(item => {
+          worksheet.addRow([
+            '',
+            item.level || item.custom,
+            ...checkedRelicIds.flat().map(id => item[id] || 0)
+          ])
+        })
+        ;[10, ..._columns.map(i => 10)].forEach((width: number, index: number) => {
+          worksheet.getColumn(index + 1).width = width
+        })
+
+        worksheet.mergeCells('A1:B1')
+        worksheet.mergeCells('A2:A4')
+        worksheet.mergeCells('A5:B5')
+        worksheet.mergeCells('A6:B6')
+        worksheet.getCell('A1').value = '级别/数量/类别'
+        worksheet.getCell('A2').value = '珍贵文物'
+        worksheet.getCell('A5').value = '未定级'
+        worksheet.getCell('A6').value = '一般文物'
+      },
+      [list.length + 1, _columns.length + 1]
+    )
+  }
+
+  useEffect(() => {
+    getDetail()
+  }, [getDetail])
+
+  useEffect(() => {
+    setCheckedStorageIds(storageIdArr.map(i => i.id))
+  }, [storageIdArr])
+
   return (
-    <>
+    <div className={style.A1Tab2}>
+      <div className='C1top'>
+        <Select
+          value={curYear}
+          placeholder='请选择统计年度'
+          style={{ width: 120 }}
+          options={years.current.map(year => ({ label: year, value: year }))}
+          onChange={v => setCurYear(v)}
+        />
+        <Button type='primary' onClick={() => setRelicModalOpen(true)}>
+          设置统计分类
+        </Button>
+        <Button onClick={() => setStorageModalOpen(true)}>设置统计库房</Button>
+        <Button onClick={handleExport}>导出表格</Button>
+      </div>
+
       <Table
         size='small'
-        columns={[
-          {
-            width: 100,
-            title: '级别/数量/类别',
-            dataIndex: 'custom',
-            colSpan: 2,
-            onCell: (record, index) => {
-              const obj = {
-                rowSpan: 0,
-                colSpan: 1
-              }
-              if (index === 0) {
-                obj.rowSpan = 3
-              } else if (isNumber(index) && index > 2) {
-                obj.rowSpan = 1
-                obj.colSpan = 2
-              }
-              return obj
-            }
-          },
-          {
-            width: 100,
-            dataIndex: 'level',
-            colSpan: 0,
-            onCell: (record, index) => {
-              const obj = {
-                rowSpan: 0,
-                colSpan: 1
-              }
-              if (isNumber(index) && index <= 2) obj.rowSpan = 1
-              return obj
-            }
-          },
-          {
-            title: '库房名称',
-            dataIndex: 'a'
-          },
-          {
-            title: '铜器',
-            dataIndex: 'b'
-          },
-          {
-            title: '金银器',
-            dataIndex: 'c'
-          },
-          {
-            title: '漆器',
-            dataIndex: 'd'
-          },
-          {
-            title: '文具',
-            dataIndex: 'e'
-          }
-        ]}
+        columns={columns}
         rowKey='id'
-        dataSource={[
-          {
-            id: 1,
-            custom: '珍贵文物',
-            level: '一级',
-            a: 'name'
-          },
-          {
-            id: 2,
-            level: '二级',
-            a: 'name2'
-          },
-          {
-            id: 3,
-            level: '三级',
-            a: 'name3'
-          },
-          {
-            id: 4,
-            custom: '未定级',
-            a: 'name'
-          },
-          {
-            id: 5,
-            custom: '一般文物',
-            a: 'name'
-          }
-        ]}
+        dataSource={list}
         pagination={false}
         bordered
       />
-    </>
+
+      <StorageModal
+        list={storageIdArr}
+        value={checkedStorageIds}
+        isModalOpen={storageModalOpen}
+        setIsModalOpen={setStorageModalOpen}
+        onChange={setCheckedStorageIds}
+      />
+      <RelicModal
+        checkedIds={checkedRelicIds}
+        isModalOpen={relicModalOpen}
+        setIsModalOpen={setRelicModalOpen}
+        onChange={setCheckedRelicIds}
+        onColumnChange={setRelicColumns}
+      />
+    </div>
   )
 }

+ 5 - 4
src/pages/A_workbench/A1dataSta/panel.module.scss

@@ -1,9 +1,10 @@
 .A1dataSta {
   background-color: #fff;
   border-radius: 10px;
-  padding: 24px 24px 0;
-  position: relative;
+  padding: 24px;
+}
+
+.A1dataStaWrap {
+  height: 100%;
   overflow: auto;
-  :global {
-  }
 }

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

@@ -8,7 +8,7 @@ function A1dataSta() {
     <div className={styles.A1dataSta}>
       <div className='pageTitle'>数据统计</div>
 
-      <Tabs items={A1DATA_TABS} />
+      <Tabs className={styles.A1dataStaWrap} items={A1DATA_TABS} />
     </div>
   )
 }

+ 4 - 4
src/pages/A_workbench/A3flow/data.ts

@@ -20,7 +20,7 @@ export const DEFAULT_A3FLOW_PARAMS: IA3flowListParams = {
   type: '',
   name: '',
   deptName: '',
-  creatorName: '',
+  userName: '',
   date: '',
   status: '',
   userType: ''
@@ -31,7 +31,7 @@ export const A3FLOW_PARAM_ROWS: A3flowSearchType[] = [
   { name: '业务编号', key: 'num', type: '输入框' },
   { name: '申请名称', key: 'name', type: '输入框' },
   { name: '发起部门', key: 'deptName', type: '输入框' },
-  { name: '发起人', key: 'creatorName', type: '输入框' },
+  { name: '发起人', key: 'userName', type: '输入框' },
   { name: '发起日期范围', key: 'date', type: '日期选择' },
   { name: '申请状态', key: 'status', type: '下拉框', data: selectObj['流程申请状态'] },
   { name: '选择角色', key: 'userType', type: '下拉框', data: selectObj['角色'] }
@@ -73,9 +73,9 @@ export const BUSINESS_DETAIL_PATH_MAP: Record<string, string> = {
   // 藏品注销
   ZX: 'cancel_edit',
   // 藏品盘点
-  PD: 'check',
+  PD: 'check_edit',
   // 人员出入库
-  CR: 'staff',
+  CR: 'staff_edit',
   // 事故登记
   SG: 'accident_edit',
   // 残损登记

+ 6 - 3
src/pages/A_workbench/A3flow/index.tsx

@@ -18,6 +18,7 @@ import { businessTypeObj, statusObj } from '@/utils/tableData'
 import history, { btnFlagFu } from '@/utils/history'
 import MyPopconfirm from '@/components/MyPopconfirm'
 import { filterEmptyStrings } from '@/utils/objects'
+import dayjs from 'dayjs'
 
 const { RangePicker } = DatePicker
 
@@ -65,7 +66,9 @@ function A3flow() {
               <RangePicker
                 format='YYYY-MM-DD'
                 allowClear={true}
-                onChange={(e, dateStrings) => setFormData({ ...formData, [item.key]: dateStrings })}
+                // @ts-ignore
+                value={formData[item.key]}
+                onChange={e => setFormData({ ...formData, [item.key]: e })}
               />
             ) : (
               <Cascader
@@ -122,9 +125,9 @@ function A3flow() {
     const { date, ...rest } = formDataRef.current
     if (Array.isArray(date) && date.length) {
       // @ts-ignore
-      rest.startTime = date[0]
+      rest.startTime = dayjs(date[0]).format('YYYY-MM-DD')
       // @ts-ignore
-      rest.endTime = date[1]
+      rest.endTime = dayjs(date[1]).format('YYYY-MM-DD')
     }
     dispatch(A3_APIList(filterEmptyStrings(rest)))
   }, [dispatch])

+ 1 - 1
src/pages/A_workbench/A3flow/types.ts

@@ -5,7 +5,7 @@ export interface IA3flowListParams {
   type: string
   name: string
   deptName: string
-  creatorName: string
+  userName: string
   date: string
   status: string
   userType: string

+ 4 - 4
src/pages/A_workbench/A4voucher/constants.ts

@@ -5,17 +5,17 @@ import { businessTypeObj } from '@/utils/tableData'
 export const DEFAULT_A4VOUCHER_PARAMS: IA4voucherParams = {
   pageNum: 1,
   pageSize: 10,
-  goodNum: '',
+  type: '',
   fileName: '',
-  moduleName: '',
+  num: '',
   date: []
 }
 
 export const A4VOUCHER_PARAM_ROWS: A4voucherSearchType[] = [
   { name: '凭证名称', key: 'fileName', type: '输入框' },
   { name: '归档日期范围', key: 'date', type: '日期选择' },
-  { name: '业务类型', key: 'moduleName', type: '下拉框', data: selectObj['业务类型'] },
-  { name: '业务编号', key: 'goodNum', type: '输入框' }
+  { name: '业务类型', key: 'type', type: '下拉框', data: selectObj['业务类型'] },
+  { name: '业务编号', key: 'num', type: '输入框' }
 ]
 
 export const A4VOUCHER_TABLE_COLUMNS = [

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

@@ -59,7 +59,7 @@ function A4voucher() {
                 options={item.data}
                 placeholder='全部'
                 allowClear={true}
-                value={formData[item.key as 'goodNum'] ? formData[item.key as 'goodNum'] : null}
+                value={formData[item.key as 'num'] ? formData[item.key as 'num'] : null}
                 onChange={e => setFormData({ ...formData, [item.key]: e })}
               />
             ) : item.type === '日期选择' ? (

+ 2 - 2
src/pages/A_workbench/A4voucher/types.ts

@@ -21,6 +21,6 @@ export interface IA4voucherParams {
   pageNum: number
   fileName: string
   date?: string[]
-  moduleName: string
-  goodNum: string
+  type: string
+  num: string
 }

+ 5 - 1
src/pages/C_goodsManage/C1register/C1look/index.tsx

@@ -24,6 +24,7 @@ import MyPopconfirm from '@/components/MyPopconfirm'
 import { MessageFu } from '@/utils/message'
 import { EXbtnFu } from '@/utils/EXBtn'
 import { C22infoBtnFu } from '../../C22goodEdit/data'
+import { EXPORT_WORD_ENUM } from '@/utils/exportTemplates'
 function C1look() {
   const { key, id } = useParams<any>()
   // key: 3审批 4查看
@@ -248,7 +249,10 @@ function C1look() {
               </Button>
             ) : null}
 
-            {EXbtnFu(topInfo)}
+            {EXbtnFu(topInfo, [
+              EXPORT_WORD_ENUM.COLLECTION_CARD,
+              EXPORT_WORD_ENUM.COLLECTION_ARCHIVES
+            ])}
 
             {C22infoBtnFu(topInfo)['撤回'] ? (
               <MyPopconfirm

+ 5 - 3
src/pages/C_goodsManage/C21wealth/index.tsx

@@ -15,6 +15,7 @@ import { C21WealthSearchType, IC21WealthItem, IC21WealthParams } from './types'
 import { C21_APIdel, C21_APIList } from '@/store/action/C21wealth'
 import { MessageFu } from '@/utils/message'
 import { statusObj } from '@/utils/tableData'
+import dayjs from 'dayjs'
 
 const { RangePicker } = DatePicker
 
@@ -63,7 +64,8 @@ function C21wealth() {
               <RangePicker
                 format='YYYY-MM-DD'
                 allowClear={true}
-                onChange={(e, dateStrings) => setFormData({ ...formData, [item.key]: dateStrings })}
+                value={formData[item.key] as undefined}
+                onChange={e => setFormData({ ...formData, [item.key]: e })}
               />
             ) : (
               <Cascader
@@ -120,9 +122,9 @@ function C21wealth() {
     const { date, ...rest } = formDataRef.current
     if (Array.isArray(date) && date.length) {
       // @ts-ignore
-      rest.startTime = date[0]
+      rest.startTime = dayjs(date[0]).format('YYYY-MM-DD')
       // @ts-ignore
-      rest.endTime = date[1]
+      rest.endTime = dayjs(date[1]).format('YYYY-MM-DD')
     }
     formDataOldRef.current = rest
     dispatch(C21_APIList(rest))

+ 2 - 6
src/pages/F_exhibition/F1exhibition/F1edit/index.tsx

@@ -136,12 +136,8 @@ function F1edit() {
   const lookBtnFu = useCallback(
     async (val: '创建' | '提交' | '撤回') => {
       if (val !== '撤回') {
-        if (!topInfo.goods || (topInfo.goods && topInfo.goods.length === 0)) {
+        if (!snaps || (snaps && snaps.length === 0)) {
           return MessageFu.warning('请添加藏品')
-        } else {
-          if (topInfo.goods.some(v => !v.siteStr || !v.siteId)) {
-            return MessageFu.warning('请选择存放位置')
-          }
         }
       }
 
@@ -547,7 +543,7 @@ function F1edit() {
               </Button>
             ) : null}
 
-            <MyPopconfirm txtK='取消' onConfirm={() => history.push('/exhibiton')} />
+            <MyPopconfirm txtK='取消' onConfirm={() => history.push('/exhibition')} />
           </>
         )}
       </div>

+ 11 - 7
src/pages/F_exhibition/F1exhibition/index.tsx

@@ -17,6 +17,7 @@ import MyTable from '@/components/MyTable'
 import { F1_APIdel, F1_APIList } from '@/store/action/F1exhibition'
 import { MessageFu } from '@/utils/message'
 import { statusObj } from '@/utils/tableData'
+import dayjs from 'dayjs'
 
 const { RangePicker } = DatePicker
 
@@ -65,7 +66,8 @@ function F1exhibition() {
               <RangePicker
                 format='YYYY-MM-DD'
                 allowClear={true}
-                onChange={(e, dateStrings) => setFormData({ ...formData, [item.key]: dateStrings })}
+                value={formData[item.key] as undefined}
+                onChange={e => setFormData({ ...formData, [item.key]: e })}
               />
             ) : (
               <Cascader
@@ -121,11 +123,11 @@ function F1exhibition() {
   const getListFu = useCallback(() => {
     const { date, ...rest } = formDataRef.current
     if (Array.isArray(date) && date.length) {
-      // @ts-ignore
-      rest.startTime = date[0]
-      // @ts-ignore
-      rest.endTime = date[1]
-    }
+          // @ts-ignore
+          rest.startTime = dayjs(date[0]).format('YYYY-MM-DD')
+          // @ts-ignore
+          rest.endTime = dayjs(date[1]).format('YYYY-MM-DD')
+        }
     formDataOldRef.current = rest
     dispatch(F1_APIList(rest))
   }, [dispatch])
@@ -188,7 +190,9 @@ function F1exhibition() {
   const deriveFu = useCallback(async () => {
     const res = await F1_APIList(
       {
-        ...formDataOldRef.current
+        ...formDataOldRef.current,
+        pageNum: 1,
+        pageSize: 99999
       },
       true
     )

+ 24 - 0
src/store/action/A1dataSta.ts

@@ -0,0 +1,24 @@
+import http from '@/utils/http'
+
+/**
+ * 馆藏文物年度核查统计
+ */
+export const A1_APIGetCheck = (year: string | number) => {
+  return http.get(`/cms/report/getCheck/${year}`)
+}
+
+/**
+ * 库存文物动态统计
+ */
+export const A1_APIGetDynamic = (params?: any) => {
+  return http.get('/cms/report/getDynamic', {
+    params
+  })
+}
+
+/**
+ * 年度库房藏品
+ */
+export const A1_APIGetStorage = (year: number) => {
+  return http.get(`/cms/report/getStorage/${year}`)
+}

+ 7 - 0
src/store/action/C1ledger.ts

@@ -57,3 +57,10 @@ export const API_getInfoLogList = (data: any) => {
 export const API_setGoodsCover = (data: any) => {
   return http.post('cms/goods/updateThumb', data)
 }
+
+/**
+ * 总账-藏品提退出入库登记流水账
+ */
+export const API_getGoodsStorage = () => {
+  return http.get('cms/print/goods/storage')
+}

+ 9 - 1
src/utils/EXBtn.tsx

@@ -1,5 +1,6 @@
 import { Button, Dropdown, MenuProps } from 'antd'
 import { EXPORT_TEMPLATE_MAP, EXPORT_WORD_ENUM, exportWordHandler } from './exportTemplates'
+import { API_getGoodsStorage } from '@/store/action/C1ledger'
 
 const items: MenuProps['items'] = []
 const templateKeys = Object.keys(EXPORT_TEMPLATE_MAP).map(i => Number(i)) as EXPORT_WORD_ENUM[]
@@ -21,7 +22,14 @@ export const EXbtnFu = (data?: any, keys = templateKeys) => {
 
   const handleExport: ({ key }: { key: unknown }) => Promise<void> = async ({ key }) => {
     const k = Number(key) as EXPORT_WORD_ENUM
-    exportWordHandler(k, data)
+
+    // 单独处理流水账数据
+    if (k === EXPORT_WORD_ENUM.COLLECTION_LOG) {
+      const res = await API_getGoodsStorage()
+      return exportWordHandler(k, { collects: res.data })
+    }
+
+    return exportWordHandler(k, data)
   }
 
   return menus.length > 1 ? (

+ 69 - 0
src/utils/exportExcelUtils.ts

@@ -0,0 +1,69 @@
+import ExcelJS from 'exceljs'
+
+/**
+ * 导出 excel
+ * @param fileName
+ * @param handler
+ * @param drawBorder [rowLength, colLength]
+ */
+export const exportTempExcel = async (
+  fileName: string,
+  handler: (worksheet: ExcelJS.Worksheet) => void,
+  drawBorder?: [number, number]
+) => {
+  const workbook = new ExcelJS.Workbook()
+  const worksheet = workbook.addWorksheet('Sheet1', {
+    properties: { defaultRowHeight: 30 },
+    views: [{ showGridLines: false }],
+    pageSetup: {
+      horizontalCentered: true,
+      verticalCentered: true,
+      paperSize: 9
+    }
+  })
+
+  handler(worksheet)
+
+  if (drawBorder) {
+    const borderStyle: ExcelJS.Border = {
+      style: 'thin',
+      color: { argb: 'FF000000' }
+    }
+
+    for (let row = 1; row <= drawBorder[0]; row++) {
+      for (let col = 1; col <= drawBorder[1]; col++) {
+        const cell = worksheet.getCell(row, col)
+        cell.border = {
+          top: borderStyle,
+          left: borderStyle,
+          bottom: borderStyle,
+          right: borderStyle
+        }
+      }
+    }
+
+    worksheet.eachRow(row => {
+      row.eachCell(cell => {
+        cell.alignment = {
+          vertical: 'middle',
+          horizontal: 'center',
+          wrapText: true
+        }
+      })
+    })
+  }
+
+  const buffer = await workbook.xlsx.writeBuffer()
+  const blob = new Blob([buffer], {
+    type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+  })
+
+  const url = URL.createObjectURL(blob)
+  const a = document.createElement('a')
+  a.href = url
+  a.download = fileName + '.xlsx'
+  a.click()
+
+  // 释放内存
+  setTimeout(() => URL.revokeObjectURL(url), 100)
+}

+ 240 - 101
src/utils/exportTemplates.ts

@@ -14,6 +14,8 @@ import {
 import { myTableTransferSize } from '@/components/MyTable'
 import dayjs from 'dayjs'
 import { D7CEHCK_COLLECTION_RESULT_OPTIONS } from '@/pages/D_storeManage/D7check/constants'
+import { exportTempExcel } from './exportExcelUtils'
+import { selectObj } from './select'
 
 export enum EXPORT_WORD_ENUM {
   /** 借用藏品点交凭证 */
@@ -55,10 +57,95 @@ export enum EXPORT_WORD_ENUM {
   /** 藏品现状登记 */
   COLLECTION_CURRENT_STATUS = 19,
   /** 馆内展览借用藏品登记台账 */
-  REGISTER_LEDGER = 20
+  REGISTER_LEDGER = 20,
+  /** 藏品总账 */
+  COLLECTION_LEDGER = 21,
+  /** 藏品提退出入库登记流水账 */
+  COLLECTION_LOG = 22
 }
 
 export const EXPORT_TEMPLATE_MAP: Record<EXPORT_WORD_ENUM, ITEMPLATE> = {
+  [EXPORT_WORD_ENUM.COLLECTION_LOG]: {
+    fileName: '藏品提退出入库登记流水账',
+    options: {
+      sheetHeader: [
+        '序号',
+        '藏品总登记号',
+        '分类号',
+        '点交凭证单号',
+        '藏品名称',
+        '计件数量',
+        '文物现状',
+        ['出库记录', '提用人', '点交人', '出库时间'],
+        ['退库记录', '退还人', '点收人', '退库时间'],
+        '备注'
+      ],
+      sheetFilter: [
+        'index',
+        'num',
+        'numType',
+        'orderNum',
+        'name',
+        'pcs',
+        'preserveState',
+        'ckUser1',
+        'ckUser2',
+        'ckDate',
+        'tkUser1',
+        'tkUser2',
+        'tkDate',
+        'snapRtf'
+      ],
+      columnWidths: [5, 14, 8, 10, 15, 10, 10, 10, 10, 14, 10, 10, 14, 10]
+    }
+  },
+  [EXPORT_WORD_ENUM.COLLECTION_LEDGER]: {
+    fileName: '藏品总账',
+    options: {
+      sheetHeader: [
+        ['登记日期', '年', '月', '日'],
+        '藏品总登记号',
+        '分类号',
+        '藏品名称',
+        '时代',
+        ['计件', '单位', '件数'],
+        '尺寸、重量',
+        '质地',
+        '完残情况',
+        '来源',
+        '采集地点',
+        ['入馆日期', '年', '月', '日'],
+        '入馆凭证号',
+        '注销凭证号',
+        '级别',
+        '备注'
+      ],
+      sheetFilter: [
+        'year',
+        'month',
+        'day',
+        'num',
+        'numType',
+        'name',
+        'dictAge',
+        'pcsUnit',
+        '_pcs',
+        'size',
+        'dictTexture',
+        'dictTorn',
+        'source',
+        '',
+        'year2',
+        'month2',
+        'day2',
+        'inHouseNum',
+        '',
+        'dictLevel',
+        'rtf'
+      ],
+      columnWidths: [5, 5, 5, 10, 5, 10, 10, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10]
+    }
+  },
   [EXPORT_WORD_ENUM.COLLECTION_CURRENT_STATUS]: {
     fileName: '藏品现状登记',
     templateName: '16.docx'
@@ -335,11 +422,13 @@ export const exportWordHandler = async (type: EXPORT_WORD_ENUM, data: Record<any
       const good = temp.goods[i]
       good.index = i + 1
       good.rtf && (good.rtf = removeHtmlTags(JSON.parse(good.rtf).txtArr[0].txt))
-      try {
-        good.thumb && (good.thumb = await getBase64Sync(baseURL + good.thumb))
-      } catch (err) {
-        console.log('thumb conversion to base64 faild:', err)
-        good.thumb = ''
+      if (item.options?.sheetFilter.includes('thumb')) {
+        try {
+          good.thumb && (good.thumb = await getBase64Sync(baseURL + good.thumb))
+        } catch (err) {
+          console.log('thumb conversion to base64 faild:', err)
+          good.thumb = ''
+        }
       }
       good.dictAge && (good.dictAge = resJiLianFu(good.dictAge))
       good.pcsUnit && (good.pcsUnit = resJiLianFu(good.pcsUnit))
@@ -347,6 +436,8 @@ export const exportWordHandler = async (type: EXPORT_WORD_ENUM, data: Record<any
         good._pcs = good.pcs
         good.pcs = good.pcs + good.pcsUnit
       }
+      good.accountType &&
+        (good.accountType = selectObj['入藏去向'].find(i => i.value === good.accountType)?.label)
       good.dictTexture3 && (good.dictTexture = resJiLianFu(good.dictTexture3))
       good.dictTorn && (good.dictTorn = resJiLianFu(good.dictTorn))
       good.source && (good.source = resJiLianFu(good.source))
@@ -361,12 +452,12 @@ export const exportWordHandler = async (type: EXPORT_WORD_ENUM, data: Record<any
     temp.goods = []
   }
 
-  // @ts-ignore
   if (item.perLine) {
     // @ts-ignore
     page = calcTablePages(temp.goods, item)
   }
 
+  // 处理业务数据
   switch (type) {
     case EXPORT_WORD_ENUM.BORROW:
       temp = {
@@ -796,113 +887,161 @@ export const exportWordHandler = async (type: EXPORT_WORD_ENUM, data: Record<any
         })
       }
       break
-  }
+    case EXPORT_WORD_ENUM.COLLECTION_LOG:
+      excelHandler = worksheet => {
+        const borderStyle: ExcelJS.Border = {
+          style: 'thin',
+          color: { argb: 'FF000000' }
+        }
 
-  if (!item.templateName) {
-    // 没有templateName则输出excel
-    let mergeHeadNum = 0
-    let headTemp: string[] = []
-    let fatherHeadTemp: {
-      title: string
-      position: string[]
-    }[] = []
-    // 表头需要占据的行数
-    const headRow = Math.max(
-      ...item.options?.sheetHeader.map((head: string | string[]) =>
-        Array.isArray(head) ? head.length - 1 : 1
-      )
-    )
-    const workbook = new ExcelJS.Workbook()
-    const worksheet = workbook.addWorksheet('Sheet1', {
-      properties: { defaultRowHeight: 30 },
-      views: [{ showGridLines: false }],
-      pageSetup: {
-        horizontalCentered: true,
-        verticalCentered: true,
-        paperSize: 9
-      }
-    })
+        for (let row = 1; row <= temp.goods.length + 2; row++) {
+          for (let col = 1; col <= 14; col++) {
+            const cell = worksheet.getCell(row, col)
+            cell.border = {
+              top: borderStyle,
+              left: borderStyle,
+              bottom: borderStyle,
+              right: borderStyle
+            }
+          }
+        }
 
-    item.options?.sheetHeader.forEach((head: string | string[], index: number) => {
-      if (Array.isArray(head)) {
-        fatherHeadTemp.push({
-          title: head.shift() || '',
-          position: [
-            `${getExcelColumnLetter(index + mergeHeadNum)}1`,
-            `${getExcelColumnLetter(index + head.length - 1 + mergeHeadNum)}1`
-          ]
+        worksheet.eachRow(row => {
+          row.eachCell(cell => {
+            cell.alignment = {
+              vertical: 'middle',
+              horizontal: 'center',
+              wrapText: true
+            }
+          })
         })
-        headTemp.push(...head)
-        mergeHeadNum++
-      } else {
-        headTemp.push(head)
       }
-    })
-    worksheet.addRow(headTemp)
-    if (fatherHeadTemp.length) {
-      // 存在需要合并的父级表头
-      worksheet.insertRow(1, [])
-      fatherHeadTemp.forEach(head => {
-        const cell = worksheet.getCell(head.position[0])
-        cell.value = head.title
-        worksheet.mergeCells(`${head.position[0]}:${head.position[1]}`)
+      break
+    case EXPORT_WORD_ENUM.COLLECTION_LEDGER:
+      temp.goods.forEach((i: any) => {
+        const createTime = dayjs(i.createTime)
+        const inHouseTime = dayjs(i.createTime)
+        i.year = createTime.format('YYYY')
+        i.month = createTime.format('MM')
+        i.day = createTime.format('DD')
+        i.year2 = inHouseTime.format('YYYY')
+        i.month2 = inHouseTime.format('MM')
+        i.day2 = inHouseTime.format('DD')
       })
-    }
-    if (headRow > 1) {
-      // 合并表头列
-      headTemp.forEach((head, index) => {
-        const pos = `${getExcelColumnLetter(index)}1:${getExcelColumnLetter(index)}${headRow}`
-        const cell = worksheet.getCell(`${getExcelColumnLetter(index)}1`)
-        if (!cell.isMerged) {
-          worksheet.mergeCells(pos)
-          cell.value = head
+
+      excelHandler = worksheet => {
+        const borderStyle: ExcelJS.Border = {
+          style: 'thin',
+          color: { argb: 'FF000000' }
         }
-      })
-    }
 
-    temp.goods.forEach((good: Record<string, any>, goodIndex: number) => {
-      const _temp: string[] = []
-      item.options?.sheetFilter.forEach((key: string, index: number) => {
-        _temp.push(good[key])
-
-        // if (key === 'thumb' && Boolean(good[key])) {
-        //   // 插入图片
-        //   const imageId = workbook.addImage({
-        //     base64: good[key],
-        //     extension: 'jpeg'
-        //   })
-        //   worksheet.addImage(
-        //     imageId,
-        //     `${getExcelColumnLetter(index)}${goodIndex + 2}:${getExcelColumnLetter(index)}${
-        //       goodIndex + 2
-        //     }`
-        //   )
-        // }
-      })
-      worksheet.addRow(_temp)
-    })
+        for (let row = 1; row <= temp.goods.length + 2; row++) {
+          for (let col = 1; col <= 21; col++) {
+            const cell = worksheet.getCell(row, col)
+            cell.border = {
+              top: borderStyle,
+              left: borderStyle,
+              bottom: borderStyle,
+              right: borderStyle
+            }
+          }
+        }
 
-    // 设置宽度
-    item.options?.columnWidths.forEach((width: number, index: number) => {
-      worksheet.getColumn(index + 1).width = width
-    })
+        worksheet.eachRow(row => {
+          row.eachCell(cell => {
+            cell.alignment = {
+              vertical: 'middle',
+              horizontal: 'center',
+              wrapText: true
+            }
+          })
+        })
+      }
+      break
+  }
 
-    excelHandler(worksheet)
+  if (!item.templateName) {
+    // 没有templateName则输出 excel
+    exportTempExcel(item.fileName, worksheet => {
+      let mergeHeadNum = 0
+      let headTemp: string[] = []
+      let fatherHeadTemp: {
+        title: string
+        position: string[]
+      }[] = []
+      // 表头需要占据的行数
+      const headRow = Math.max(
+        ...item.options?.sheetHeader.map((head: string | string[]) => (Array.isArray(head) ? 2 : 1))
+      )
 
-    const buffer = await workbook.xlsx.writeBuffer()
-    const blob = new Blob([buffer], {
-      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
-    })
+      item.options?.sheetHeader.forEach((head: string | string[], index: number) => {
+        if (Array.isArray(head)) {
+          fatherHeadTemp.push({
+            title: head.shift() || '',
+            position: [
+              `${getExcelColumnLetter(index + mergeHeadNum)}1`,
+              `${getExcelColumnLetter(index + head.length - 1 + mergeHeadNum)}1`
+            ]
+          })
+          headTemp.push(...head)
+          mergeHeadNum += head.length - 1
+        } else {
+          headTemp.push(head)
+        }
+      })
+      worksheet.addRow(headTemp)
+      if (fatherHeadTemp.length) {
+        // 存在需要合并的父级表头
+        worksheet.insertRow(1, [])
+        fatherHeadTemp.forEach(head => {
+          const cell = worksheet.getCell(head.position[0])
+          cell.value = head.title
+          worksheet.mergeCells(`${head.position[0]}:${head.position[1]}`)
+        })
+      }
+      if (headRow > 1) {
+        // 合并表头列
+        headTemp.forEach((head, index) => {
+          const pos = `${getExcelColumnLetter(index)}1:${getExcelColumnLetter(index)}${headRow}`
+          const cell = worksheet.getCell(`${getExcelColumnLetter(index)}1`)
+          if (!cell.isMerged) {
+            worksheet.mergeCells(pos)
+            cell.value = head
+          }
+        })
+      }
+
+      temp.goods.forEach((good: Record<string, any>, goodIndex: number) => {
+        const _temp: string[] = []
+        item.options?.sheetFilter.forEach((key: string, index: number) => {
+          _temp.push(good[key])
 
-    const url = URL.createObjectURL(blob)
-    const a = document.createElement('a')
-    a.href = url
-    a.download = item.fileName + '.xlsx'
-    a.click()
+          // if (key === 'thumb' && Boolean(good[key])) {
+          //   // 插入图片
+          //   const imageId = workbook.addImage({
+          //     base64: good[key],
+          //     extension: 'jpeg'
+          //   })
+          //   worksheet.addImage(
+          //     imageId,
+          //     `${getExcelColumnLetter(index)}${goodIndex + 2}:${getExcelColumnLetter(index)}${
+          //       goodIndex + 2
+          //     }`
+          //   )
+          // }
+        })
+        worksheet.addRow(_temp)
+      })
 
-    // 释放内存
-    setTimeout(() => URL.revokeObjectURL(url), 100)
+      // 设置宽度
+      item.options?.columnWidths.forEach((width: number, index: number) => {
+        worksheet.getColumn(index + 1).width = width
+      })
+
+      excelHandler(worksheet)
+    })
   } else {
+    // 输出 word
     exportWordDocx(`./templates/${item.templateName}`, temp, item.fileName)
   }
 }

+ 1 - 1
src/utils/select.ts

@@ -103,7 +103,7 @@ export const selectObj = {
     { value: 'PD', label: '藏品盘点' },
     { value: 'CR', label: '人员出入库' },
     { value: 'SG', label: '事故登记' },
-    { value: 'CS', label: '残损登记' },
+    // { value: 'CS', label: '残损登记' },
     { value: 'XZ', label: '现状登记' },
     { value: 'XF', label: '文物修复' },
     { value: 'ZL', label: '借展管理' }