Heatmap.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import { useEffect, useState } from "react";
  2. import { useIntl } from "react-intl";
  3. import { message, Select, Typography } from "antd";
  4. import { get } from "../../request";
  5. import { IUserOperationDailyResponse } from "../api/Exp";
  6. interface IOptions {
  7. value: string;
  8. label: string;
  9. }
  10. interface IDailyData {
  11. date: string;
  12. commits: number;
  13. year: number;
  14. month: number;
  15. day: number;
  16. week: number;
  17. }
  18. interface IWidget {
  19. studioName?: string;
  20. }
  21. const Widget = ({ studioName }: IWidget) => {
  22. const [dailyData, setDailyData] = useState<IDailyData[]>([]);
  23. const [year, setYear] = useState<IOptions[]>([]);
  24. const thisYear = new Date().getFullYear();
  25. const [currYear, setCurrYear] = useState<string>(thisYear.toString());
  26. const intl = useIntl();
  27. useEffect(() => {
  28. get<IUserOperationDailyResponse>(
  29. `/v2/user-operation-daily?view=user-year&studio_name=${studioName}&year=2021`
  30. ).then((json) => {
  31. if (json.ok) {
  32. //找到起止年份
  33. if (json.data.rows.length > 0) {
  34. const yearStart = new Date(json.data.rows[0].date_int).getFullYear();
  35. const yearEnd = new Date(
  36. json.data.rows[json.data.rows.length - 1].date_int
  37. ).getFullYear();
  38. let yearOption: IOptions[] = [];
  39. for (let index = yearStart; index <= yearEnd; index++) {
  40. yearOption.push({
  41. value: index.toString(),
  42. label: index.toString(),
  43. });
  44. }
  45. setYear(yearOption);
  46. }
  47. const data = json.data.rows.map((item) => {
  48. const date = new Date(item.date_int);
  49. const oneJan = new Date(date.getFullYear(), 0, 1);
  50. const week = Math.ceil(
  51. ((date.getTime() - oneJan.getTime()) / 86400000 +
  52. oneJan.getDay() +
  53. 1) /
  54. 7
  55. );
  56. return {
  57. date:
  58. date.getFullYear() +
  59. "-" +
  60. (date.getMonth() + 1) +
  61. "-" +
  62. date.getDate(),
  63. year: date.getFullYear(),
  64. month: date.getMonth() + 1,
  65. day: date.getDay(),
  66. week: week,
  67. commits: item.duration / 1000 / 60,
  68. };
  69. });
  70. console.log("data", data);
  71. setDailyData(data);
  72. } else {
  73. message.error(json.message);
  74. }
  75. });
  76. }, [studioName]);
  77. const yAxisLabel = new Array(7).fill(1).map((item, id) => {
  78. return (
  79. <div key={id} style={{ display: "inline-block", width: "5em" }}>
  80. <Typography.Text>
  81. {intl.formatMessage({ id: `labels.week.${id}` })}
  82. </Typography.Text>
  83. </div>
  84. );
  85. });
  86. const dayColor = ["#9be9a8", "#40c463", "#30a14e", "#216e39"];
  87. const weeks = new Array(54).fill(1);
  88. const heatmap = weeks.map((item, week) => {
  89. const days = new Array(7).fill(1);
  90. return (
  91. <div key={week}>
  92. {days.map((itemDay, day) => {
  93. const time = dailyData.find(
  94. (value) =>
  95. value.year === parseInt(currYear) &&
  96. value.week === week &&
  97. value.day === day
  98. )?.commits;
  99. const color = time
  100. ? time > 120
  101. ? dayColor[0]
  102. : time > 60
  103. ? dayColor[1]
  104. : time > 30
  105. ? dayColor[2]
  106. : time > 5
  107. ? dayColor[3]
  108. : "rgba(0,0,0,0)"
  109. : "rgba(0,0,0,0)";
  110. return (
  111. <div
  112. key={day}
  113. style={{
  114. display: "inline-block",
  115. width: 12,
  116. height: 12,
  117. backgroundColor: `rgba(128,128,128,0.2)`,
  118. margin: 0,
  119. borderRadius: 2,
  120. outline: "1px solid gray",
  121. }}
  122. >
  123. <div
  124. style={{
  125. width: 12,
  126. height: 12,
  127. backgroundColor: color,
  128. }}
  129. ></div>
  130. </div>
  131. );
  132. })}
  133. </div>
  134. );
  135. });
  136. return (
  137. <div style={{ width: 1000 }}>
  138. <div style={{ textAlign: "right" }} key="toolbar">
  139. <Select
  140. defaultValue={thisYear.toString()}
  141. style={{ width: 120 }}
  142. onChange={(value: string) => {
  143. console.log(`selected ${value}`);
  144. setCurrYear(value);
  145. }}
  146. options={year}
  147. />
  148. </div>
  149. <div style={{ display: "flex" }} key="map">
  150. <div style={{ width: "5em" }} key="label">
  151. {yAxisLabel}
  152. </div>
  153. {heatmap}
  154. </div>
  155. </div>
  156. );
  157. };
  158. export default Widget;