ConsumedMedicineTop8Chart.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import React, { useState, useEffect } from 'react';
  2. import styled from 'styled-components';
  3. import { apiGet } from '../../utils/request';
  4. import { useDefaultDate, getSelectedMonthRange } from '../../App';
  5. import useGlobalRefresh from '../../hooks/useGlobalRefresh';
  6. // 组件容器样式 - 确保完全填充Grid单元格
  7. const ChartWrapper = styled.div`
  8. width: 100%;
  9. height: 100%;
  10. position: relative;
  11. overflow: hidden;
  12. border: 1px solid #2980B9;
  13. box-sizing: border-box;
  14. display: flex;
  15. flex-direction: column;
  16. `;
  17. // 组件标题 - 使用与其他模块相同的样式
  18. const PanelHeader = styled.div`
  19. position: relative;
  20. width: 95%;
  21. height: 1.6vw;
  22. min-height: 1.6vw;
  23. max-height: 1.6vw;
  24. padding-left: 5.5%;
  25. text-align: left;
  26. line-height: 1.6vw;
  27. font-size: 0.8vw;
  28. color: #E6F2FF;
  29. font-family: 'DingTalk JinBuTi', 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
  30. background: url('/component_header_bg.png') no-repeat;
  31. background-size: 100% 100%;
  32. flex-shrink: 0;
  33. `;
  34. // 表格容器 - 占据剩余空间,确保与其他模块一致
  35. const TableContainer = styled.div`
  36. width: 100%;
  37. flex: 1;
  38. overflow: hidden;
  39. `;
  40. // 表格样式
  41. const Table = styled.table`
  42. width: 100%;
  43. height: 100%;
  44. border-collapse: collapse;
  45. font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif !important;
  46. `;
  47. // 表头样式
  48. const TableHeader = styled.thead`
  49. tr {
  50. background:#051833;
  51. }
  52. th {
  53. padding: 0.3vw 1.2vw;
  54. font-size: 0.7vw;
  55. font-weight: normal;
  56. color: #CCE6FF;
  57. white-space: nowrap;
  58. font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif !important;
  59. &:first-child {
  60. width: 35%;
  61. text-align: left;
  62. padding-left: 3.2vw; /* 增加左边距,与药品名称文字对齐 */
  63. }
  64. &:nth-child(2) {
  65. width: 20%;
  66. text-align: left;
  67. }
  68. &:nth-child(3) {
  69. width: 45%;
  70. text-align: left;
  71. }
  72. }
  73. `;
  74. // 表体样式
  75. const TableBody = styled.tbody`
  76. tr {
  77. &:nth-child(odd) {
  78. background: #162840;
  79. }
  80. &:nth-child(even) {
  81. background: #051833;
  82. }
  83. &:hover {
  84. background: rgba(6, 30, 93, 0.6);
  85. }
  86. }
  87. td {
  88. padding: 0.2vw 1.2vw;
  89. font-size: 0.65vw;
  90. color: #FFFFFF;
  91. white-space: nowrap;
  92. overflow: hidden;
  93. text-overflow: ellipsis;
  94. font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif !important;
  95. &:first-child {
  96. text-align: left;
  97. color: #FFFFFF;
  98. }
  99. &:nth-child(2) {
  100. text-align: left;
  101. }
  102. &:nth-child(3) {
  103. text-align: left;
  104. }
  105. }
  106. `;
  107. // 排名号码样式
  108. const RankNumber = styled.span<{ rank: string }>`
  109. display: inline-block;
  110. min-width: 1.5vw;
  111. text-align: center;
  112. font-size: 0.7vw;
  113. font-weight: bold;
  114. margin-right: 0.5vw;
  115. font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif !important;
  116. color: ${props => {
  117. const rankNum = parseInt(props.rank);
  118. if (rankNum <= 3) return '#FF4D6A'; // 前三名红色
  119. return '#CCE6FF'; // 剩余浅蓝色
  120. }};
  121. `;
  122. // 数据类型定义
  123. interface MedicineData {
  124. rank: string;
  125. drugName: string;
  126. drugSpec: string;
  127. firmName: string;
  128. amount: number;
  129. }
  130. const ConsumedMedicineTop8Chart: React.FC = () => {
  131. const [data, setData] = useState<MedicineData[]>([]);
  132. const [loading, setLoading] = useState<boolean>(true);
  133. const { defaultDateTime, selectedMonth } = useDefaultDate();
  134. // 获取数据
  135. const fetchData = async () => {
  136. try {
  137. setLoading(true);
  138. // 使用选中月份获取时间范围,支持月份选择功能
  139. const { startTime, endTime } = getSelectedMonthRange(defaultDateTime, selectedMonth);
  140. const response = await apiGet('/stageStats/drugsConsumptionTop', {
  141. params: {
  142. startTime,
  143. endTime
  144. }
  145. });
  146. if (response.data && response.data.data && Array.isArray(response.data.data)) {
  147. // 最多只取前8条数据
  148. const limitedData = response.data.data.slice(0, 8);
  149. setData(limitedData);
  150. } else {
  151. console.warn('消耗药品Top8 - 接口返回格式不符合预期:', response.data);
  152. // 设置为空数组,不使用模拟数据
  153. setData([]);
  154. }
  155. } catch (error) {
  156. console.error('获取消耗药品Top8数据失败:', error);
  157. // 设置为空数组,不使用模拟数据
  158. setData([]);
  159. } finally {
  160. setLoading(false);
  161. }
  162. };
  163. // 组件挂载时和依赖变化时获取数据
  164. useEffect(() => {
  165. fetchData();
  166. }, [defaultDateTime, selectedMonth]);
  167. // 使用刷新Hook,配置对应的模块代码
  168. useGlobalRefresh(fetchData, '41');
  169. return (
  170. <ChartWrapper>
  171. <PanelHeader>消耗药品Top8</PanelHeader>
  172. <TableContainer>
  173. {loading ? (
  174. <div style={{
  175. display: 'flex',
  176. justifyContent: 'center',
  177. alignItems: 'center',
  178. height: '100%',
  179. color: '#CCE6FF',
  180. fontSize: '0.8vw'
  181. }}>
  182. 数据加载中...
  183. </div>
  184. ) : data.length === 0 ? (
  185. <div style={{
  186. display: 'flex',
  187. justifyContent: 'center',
  188. alignItems: 'center',
  189. height: '100%',
  190. color: '#CCE6FF',
  191. fontSize: '0.8vw'
  192. }}>
  193. 暂无数据
  194. </div>
  195. ) : (
  196. <Table>
  197. <TableHeader>
  198. <tr>
  199. <th>药品名称</th>
  200. <th>药品规格</th>
  201. <th>厂商</th>
  202. </tr>
  203. </TableHeader>
  204. <TableBody>
  205. {data.map((item, index) => (
  206. <tr key={index}>
  207. <td>
  208. <RankNumber rank={item.rank}>
  209. {String(item.rank).padStart(2, '0')}
  210. </RankNumber>
  211. {item.drugName}
  212. </td>
  213. <td>{item.drugSpec}</td>
  214. <td>{item.firmName}</td>
  215. </tr>
  216. ))}
  217. </TableBody>
  218. </Table>
  219. )}
  220. </TableContainer>
  221. </ChartWrapper>
  222. );
  223. };
  224. export default ConsumedMedicineTop8Chart;