import React, { useState, useEffect, useRef, useCallback } from 'react'; import styled from 'styled-components'; import ReactECharts from 'echarts-for-react'; import * as echarts from 'echarts'; import { apiGet } from '../../utils/request'; import { getDefaultDate } from '../../App'; import { PanelContainer } from '../styled/DashboardStyles'; import useGlobalRefresh from '../../hooks/useGlobalRefresh'; // 组件容器样式 const Container = styled(PanelContainer)` width: 100%; height:42vh; padding: 0; position: relative; border-radius: 0.2vw; `; // 组件标题 const PanelHeader = styled.div` position: relative; width: 65%; height: 1.6vw; padding-left: 4%; text-align: left; line-height: 1.6vw; font-size: 0.8vw; color: #CCE6FF; font-family: 'DingTalk JinBuTi', 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif; background: url('/component_header_bg.png') no-repeat; background-size: 100% 100%; `; // 内容容器 const ContentBox = styled.div` flex: 1; display: flex; flex-direction: column; padding: 0.5vw; padding-top: 1.2vw; height: calc(100% - 1.6vw); `; // 设备标题 const DeviceTitle = styled.div` font-size: 1.2vw; color: #CCE6FF; margin-bottom: 1vw; text-align: left; font-family: 'DingTalk JinBuTi', 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif; `; // 图表容器 const ChartContainer = styled.div` flex: 1; display: flex; overflow: hidden; background: url('/kucunbeijing.png') no-repeat; background-size: 100% 100%; background-position: center; `; // 仪表盘容器 const GaugeContainer = styled.div` width: 40%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; position: relative; padding-right:4%; `; // 数据项列表 const DataList = styled.div` width: 60%; display: flex; flex-wrap: wrap; `; // 数据项 const DataItem = styled.div` width: 50%; height: 33.33%; display: flex; align-items: center; padding: 1vw 1vw; `; // 数据项图标 const DataIcon = styled.div` width: 2vw; height: 2vw; border-radius: 0.3vw; display: flex; justify-content: center; align-items: center; margin-right: 1vw; `; // 数据项内容 const DataContent = styled.div` display: flex; flex-direction: column; justify-content: center; align-items: flex-start; `; // 数据项标题 const DataTitle = styled.div` font-size: 0.8vw; color: #CCE6FF; margin-bottom: 0.2vw; `; // 数据项值 const DataValue = styled.div` font-size: 2.3vw; color: #FBFDFF; font-family: 'DingTalk JinBuTi', 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif; `; interface InventoryData { totalStock: number; equipTotalStock: number; equipInputStock: number | null; equipOutputStock: number | null; equipDrugVarietyNum: number; expectedAvailableDays: number; equipOutStockDrugVariety: number; } // 新增类型定义 type EquipTotalStockItem = { name: string; value: number; color?: string; }; interface InventoryManagementProps { selectedEquipCode: string; selectedEquipName: string; } const InventoryManagement: React.FC = ({ selectedEquipCode, selectedEquipName }) => { const chartRef = useRef(null); const [data, setData] = useState({ totalStock: 0, equipTotalStock: 0, equipInputStock: null, equipOutputStock: null, equipDrugVarietyNum: 0, expectedAvailableDays: 0, equipOutStockDrugVariety: 0 }); const [chartSize, setChartSize] = useState({ width: 300, height: 300 }); const [equipTotalStockList, setEquipTotalStockList] = useState([]); // 监听容器大小变化 useEffect(() => { const updateSize = () => { if (chartRef.current) { const container = chartRef.current.ele; if (container) { const newWidth = container.clientWidth; const newHeight = container.clientHeight; // 只有当尺寸真正变化且不为0时才更新状态 if (newWidth > 0 && newHeight > 0 && (Math.abs(newWidth - chartSize.width) > 5 || Math.abs(newHeight - chartSize.height) > 5)) { setChartSize({ width: newWidth, height: newHeight }); } } } }; // 初始更新 setTimeout(updateSize, 300); // 给图表一些渲染时间 // 监听窗口大小变化 window.addEventListener('resize', updateSize); return () => { window.removeEventListener('resize', updateSize); }; }, [chartSize.width, chartSize.height]); const fetchData = useCallback(async () => { try { // 获取默认日期 const defaultDate = getDefaultDate(); // 直接使用选中的设备代码 const equipCode = selectedEquipCode; // 调用新的库存管理接口 const response = await apiGet('/equipMana/equipInventoryInfo', { params: { dateTime: defaultDate, equipCode: equipCode } }); if (response.data && response.data.data) { setData({ totalStock: response.data.data.totalStock || 0, equipTotalStock: response.data.data.equipTotalStock || 0, equipInputStock: response.data.data.equipInputStock, equipOutputStock: response.data.data.equipOutputStock, equipDrugVarietyNum: response.data.data.equipDrugVarietyNum || 0, expectedAvailableDays: response.data.data.expectedAvailableDays || 0, equipOutStockDrugVariety: response.data.data.equipOutStockDrugVariety || 0 }); // 修正:适配equipTotalStockList字段 if (Array.isArray(response.data.data.equipTotalStockList)) { const colorArr = ['#4D88FF', '#FFE980', '#4DE1FF', '#8FADCC', '#FFB980', '#B6A2DE', '#5AB1EF', '#FF6666']; setEquipTotalStockList( response.data.data.equipTotalStockList.map((item: any, idx: number) => ({ name: item.equipCode ? `设备${item.equipCode}` : `设备${idx+1}`, value: item.quantity, color: colorArr[idx % colorArr.length] })) ); } else { setEquipTotalStockList([]); } } } catch (error) { console.error('获取库存数据失败:', error); // 使用模拟数据(仅用于开发) setData({ totalStock: 19352, equipTotalStock: 16856, equipInputStock: null, equipOutputStock: null, equipDrugVarietyNum: 686, expectedAvailableDays: 0.0, equipOutStockDrugVariety: 0 }); setEquipTotalStockList([ { name: '蓝色部分', value: 25, color: '#4D88FF' }, { name: '黄色部分', value: 25, color: '#FFE980' }, { name: '青色部分', value: 25, color: '#4DE1FF' }, { name: '淡蓝色部分', value: 25, color: '#8FADCC' } ]); } }, [selectedEquipCode]); // 组件挂载时获取数据 useEffect(() => { fetchData(); }, [fetchData]); // 当选中的设备代码变化时重新获取数据 useEffect(() => { fetchData(); }, [selectedEquipCode, fetchData]); // 使用全局刷新钩子,指定模块代码'25' useGlobalRefresh(fetchData, '25'); // 获取饼图配置 const getPieOption = () => { // 计算自适应的边框宽度,基于容器大小,但不小于1 const adaptiveBorderWidth = Math.max(1, Math.min(4, chartSize.width * 0.01)); // 计算字体大小,但设置最小值避免过小 const titleFontSize = Math.max(12, chartSize.width * 0.05); const valueFontSize = Math.max(20, chartSize.width * 0.1); const paddingBottom = Math.max(5, chartSize.width * 0.015); // 饼图缩放比例 - 可以调整这个值来改变饼图大小 const pieScale = 0.8; // 1.0为标准大小,可增大或减小 // 环宽度控制参数 const ringWidth = 0.3; // 环宽度,值越大环越宽,值越小环越窄 const outerRadius = 90 * pieScale; // 外圆半径 const innerRadius = outerRadius - (outerRadius * ringWidth); // 内圆半径 // 内圆大小控制参数 const innerCircleScale = 0.45 * pieScale; // 内部黑色圆的大小 // 饼图数据动态生成 const pieData = (equipTotalStockList && equipTotalStockList.length > 0) ? (equipTotalStockList as EquipTotalStockItem[]).map(function(item: EquipTotalStockItem, idx: number) { return { value: item.value, name: item.name, itemStyle: { color: item.color || ['#4D88FF', '#FFE980', '#4DE1FF', '#8FADCC'][idx % 4] } }; }) : []; // 如果没有数据,只显示中心label,不渲染pie片 if (!pieData.length) { return { backgroundColor: 'transparent', series: [ { type: 'pie', radius: ['0%', '100%'], center: ['50%', '50%'], label: { position: 'center', formatter: function() { return '{title|总库存}\n{value|' + data.totalStock + '}'; }, rich: { title: { color: '#CCE6FF', fontSize: titleFontSize, padding: [0, 0, paddingBottom, 0] }, value: { color: '#fff', fontSize: valueFontSize, fontWeight: 'bold', fontFamily: 'DingTalk JinBuTi, PingFang SC, Microsoft YaHei, Arial, sans-serif' } } }, data: [{ value: 100, name: '背景', itemStyle: { color: '#041529' } }], itemStyle: { color: '#041529' }, labelLine: { show: false }, emphasis: { scale: false } } ] }; } return { backgroundColor: 'transparent', tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)', textStyle: { fontSize: Math.max(12, chartSize.width * 0.04) } }, series: [ { type: 'pie', radius: [`${innerRadius}%`, `${outerRadius}%`], center: ['50%', '50%'], startAngle: 0, avoidLabelOverlap: false, label: { show: false }, emphasis: { scale: false }, itemStyle: { borderWidth: adaptiveBorderWidth, borderColor: '#041529' }, data: pieData }, { type: 'pie', radius: ['0', `${innerCircleScale * 100}%`], center: ['50%', '50%'], label: { position: 'center', formatter: function() { return '{title|总库存}\n{value|' + data.totalStock + '}'; }, rich: { title: { color: '#CCE6FF', fontSize: titleFontSize, padding: [0, 0, paddingBottom, 0] }, value: { color: '#fff', fontSize: valueFontSize, fontWeight: 'bold', fontFamily: 'DingTalk JinBuTi, PingFang SC, Microsoft YaHei, Arial, sans-serif' } } }, itemStyle: { color: '#041529' }, data: [ { value: 100, name: '背景' } ] } ] }; }; return ( 库存管理 设备: {selectedEquipName} 库存图标 库存 {data.equipTotalStock} 在库品种数图标 设备在库品种数 {data.equipDrugVarietyNum} 设备入库盒数图标 设备入库盒数 {data.equipInputStock || 0} 设备出库盒数图标 设备出库盒数 {data.equipOutputStock || 0} 预估可消耗天数图标 预估可消耗天数 {data.expectedAvailableDays} 缺药率图标 缺药率>90%品种数 {data.equipOutStockDrugVariety} ); }; export default InventoryManagement;