Просмотр исходного кода

Merge branch 'master' of http://192.168.0.115:3000/shaogen1995/qingDao_goods into master

shaogen1995 10 часов назад
Родитель
Сommit
8b22c11ae0

+ 8 - 0
src/pages/Abench/A1statistics/index.module.scss

@@ -1,5 +1,13 @@
 .A1statistics {
   padding: 24px;
+
+  .A1export {
+    position: absolute;
+    top: -75px;
+    left: 130px;
+    z-index: 1;
+  }
+
   :global {
     .A1_1 {
       display: flex;

+ 121 - 7
src/pages/Abench/A1statistics/index.tsx

@@ -1,7 +1,8 @@
 import React, { useCallback, useEffect, useRef, useState } from 'react'
 import styles from './index.module.scss'
-import { Select } from 'antd'
+import { Button, Select } from 'antd'
 import { iconUrl } from '@/utils/http'
+import * as xlsx from 'xlsx'
 import * as echarts from 'echarts'
 import {
   A1_APIworkCount,
@@ -23,7 +24,6 @@ const DEFAULT_COLORS = ['#c11b2d', '#243220', '#24664b', '#8acfb2', '#806e4c', '
 
 // 先写一些静态的
 type DictItem = { name: string; id: number }
-type TextureItem = { name: string; id: number; count: number }
 type TotalItem = { pcs: number | null; level: string | null }
 
 function A1statistics() {
@@ -156,9 +156,17 @@ function A1statistics() {
     const fetch = async () => {
       try {
         const res = await A1_APIgetDataTextureByTagId(textureTagId)
-        if (res?.code === 0 && Array.isArray(res.data)) {
+        if (
+          res?.code === 0 &&
+          res.data &&
+          typeof res.data === 'object' &&
+          !Array.isArray(res.data)
+        ) {
           setTextureData(
-            (res.data as TextureItem[]).map(({ name, count }) => ({ name, value: count }))
+            Object.entries(res.data as Record<string, number>).map(([name, value]) => ({
+              name,
+              value
+            }))
           )
         } else {
           setTextureData([])
@@ -179,9 +187,17 @@ function A1statistics() {
     const fetch = async () => {
       try {
         const res = await A1_APIgetDataTagByTagId(categoryTagId)
-        if (res?.code === 0 && Array.isArray(res.data)) {
+        if (
+          res?.code === 0 &&
+          res.data &&
+          typeof res.data === 'object' &&
+          !Array.isArray(res.data)
+        ) {
           setCategoryData(
-            (res.data as TextureItem[]).map(({ name, count }) => ({ name, value: count }))
+            Object.entries(res.data as Record<string, number>).map(([name, value]) => ({
+              name,
+              value
+            }))
           )
         } else {
           setCategoryData([])
@@ -379,6 +395,8 @@ function A1statistics() {
       return
     }
 
+    // 有数据时先清除,避免从「暂无数据」切换后 graphic 残留
+    myChart.clear()
     // 计算总数用于百分比
     const total = data.reduce((sum, item) => sum + item.value, 0)
 
@@ -457,9 +475,105 @@ function A1statistics() {
     binInitFu(categoryData, document.querySelector('#echBox4'))
   }, [binInitFu, categoryData])
 
+  // 数据导出
+  const dataExport = useCallback(() => {
+    const wb = xlsx.utils.book_new()
+
+    // 汇总数据
+    const summaryData = [
+      { 指标: '藏品总数', 数值: totalStats?.totalCount ?? '-', 单位: '件/套' },
+      { 指标: '藏品总数量', 数值: totalStats?.totalPcs ?? '-', 单位: '个' },
+      { 指标: '定级文物数量', 数值: totalStats?.leveledCount ?? '-', 单位: '件/套' },
+      { 指标: '一级文物', 数值: totalStats?.level1 ?? 0, 单位: '件/套' },
+      { 指标: '二级文物', 数值: totalStats?.level2 ?? 0, 单位: '件/套' },
+      { 指标: '三级文物', 数值: totalStats?.level3 ?? 0, 单位: '件/套' },
+      { 指标: '一般文物', 数值: totalStats?.level4 ?? 0, 单位: '件/套' },
+      { 指标: `年度新增产品数量(${yearCountYear}年)`, 数值: yearCount ?? '-', 单位: '件/套' }
+    ]
+    const wsSummary = xlsx.utils.json_to_sheet(summaryData)
+    wsSummary['!cols'] = [{ wpx: 180 }, { wpx: 100 }, { wpx: 80 }]
+    xlsx.utils.book_append_sheet(wb, wsSummary, '汇总')
+
+    // 一级分类
+    const level1SheetData = level1Data.map((it, i) => ({
+      序号: i + 1,
+      分类名称: it.name,
+      数量: it.value
+    }))
+    if (level1SheetData.length) {
+      const ws1 = xlsx.utils.json_to_sheet(level1SheetData)
+      ws1['!cols'] = [{ wpx: 60 }, { wpx: 150 }, { wpx: 80 }]
+      xlsx.utils.book_append_sheet(wb, ws1, '一级分类')
+    }
+
+    // 二级分类
+    const level2SheetData = level2Data.map((it, i) => ({
+      序号: i + 1,
+      分类名称: it.name,
+      数量: it.value
+    }))
+    if (level2SheetData.length) {
+      const ws2 = xlsx.utils.json_to_sheet(level2SheetData)
+      ws2['!cols'] = [{ wpx: 60 }, { wpx: 150 }, { wpx: 80 }]
+      xlsx.utils.book_append_sheet(wb, ws2, '二级分类')
+    }
+
+    // 材质统计(当前筛选)
+    const textureSheetData = textureData.map((it, i) => ({
+      序号: i + 1,
+      材质: it.name,
+      数量: it.value
+    }))
+    if (textureSheetData.length) {
+      const ws3 = xlsx.utils.json_to_sheet(textureSheetData)
+      ws3['!cols'] = [{ wpx: 60 }, { wpx: 150 }, { wpx: 80 }]
+      const tagName = (textureOptions.find(t => t.id === textureTagId)?.name ?? '').replace(
+        /[\\/*?:[\]]/g,
+        ''
+      )
+      xlsx.utils.book_append_sheet(wb, ws3, tagName ? `材质_${tagName.slice(0, 20)}` : '材质统计')
+    }
+
+    // 品类统计(当前筛选)
+    const categorySheetData = categoryData.map((it, i) => ({
+      序号: i + 1,
+      品类: it.name,
+      数量: it.value
+    }))
+    if (categorySheetData.length) {
+      const ws4 = xlsx.utils.json_to_sheet(categorySheetData)
+      ws4['!cols'] = [{ wpx: 60 }, { wpx: 150 }, { wpx: 80 }]
+      const tagName = (categoryOptions.find(c => c.id === categoryTagId)?.name ?? '').replace(
+        /[\\/*?:[\]]/g,
+        ''
+      )
+      xlsx.utils.book_append_sheet(wb, ws4, tagName ? `品类_${tagName.slice(0, 20)}` : '品类统计')
+    }
+
+    const fileName = `数据统计_${new Date().toISOString().slice(0, 10)}.xlsx`
+    xlsx.writeFile(wb, fileName)
+  }, [
+    totalStats,
+    yearCount,
+    yearCountYear,
+    level1Data,
+    level2Data,
+    textureData,
+    textureTagId,
+    textureOptions,
+    categoryData,
+    categoryTagId,
+    categoryOptions
+  ])
+
   return (
     <div className={styles.A1statistics}>
-      <div className='pageTitle'>数据统计</div>
+      <div className={styles.A1header}>
+        <div className='pageTitle'>数据统计</div>
+        <Button className={styles.A1export} type='primary' onClick={dataExport}>
+          数据导出
+        </Button>
+      </div>
 
       <div className='A1_1'>
         <div>

+ 2 - 2
src/store/action/Abench/A1.ts

@@ -17,7 +17,7 @@ export const A1_APIgetDictById = () => {
 
 /**
  * 根据 tagId 获取材质统计数据
- * 返回 {name:string; id:number; count:number}[]
+ * 返回 {[string]: number} 例如 {"无机质":1, "单一质地":1}
  */
 export const A1_APIgetDataTextureByTagId = (tagId: number) => {
   return http.get(`cms/workCount/getDataTextureByTagId/${tagId}`)
@@ -25,7 +25,7 @@ export const A1_APIgetDataTextureByTagId = (tagId: number) => {
 
 /**
  * 根据 tagId 获取品类统计数据
- * 返回 {name:string; id:number; count:number}[]
+ * 返回 {[string]: number} 例如 {"无机质":1, "单一质地":1}
  */
 export const A1_APIgetDataTagByTagId = (tagId: number) => {
   return http.get(`cms/workCount/getDataTagByTagId/${tagId}`)