Overview.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /**
  2. * 钱包概述页面
  3. * @邠心vbe on 2021/05/08
  4. */
  5. import React, { Component } from 'react';
  6. import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
  7. import { ElevationObject } from '../../components/Button';
  8. import {VictoryAxis, VictoryBar, VictoryArea, VictoryChart, VictoryTheme, VictoryLabel} from 'victory-native';
  9. import apiWallet from '../../api/apiWallet';
  10. import Svg, { Defs, LinearGradient, Stop } from 'react-native-svg';
  11. import utils from '../../utils/utils';
  12. const chartThemes = "#6672B8"; //配置柱状图颜色
  13. export default class Overview extends Component {
  14. constructor(props) {
  15. super(props);
  16. this.state = {
  17. glanceData: {},
  18. monthData: [],
  19. weekdayData: [],
  20. weekIndex: 0,
  21. monthIndex: 0,
  22. chartReady: false,
  23. };
  24. this.nowDataString = new Date().toDateString()
  25. this.refreshing = false;
  26. this.enableChartArea = true;
  27. }
  28. componentDidMount() {
  29. this.getOverview();
  30. }
  31. componentDidUpdate() {
  32. if (this.props.shown) {
  33. if (this.props.refresh && !this.refreshing) {
  34. this.refreshing = true;
  35. this.getOverview();
  36. } else if (!this.state.chartReady) {
  37. setTimeout(() => {
  38. this.setState({
  39. chartReady: true
  40. })
  41. }, 100);
  42. }
  43. } else if (this.state.chartReady) {
  44. this.setState({
  45. chartReady: false
  46. })
  47. }
  48. }
  49. getOverview() {
  50. apiWallet.getOverviewData().then(res => {
  51. var glanceData = {}
  52. var weekdayData = []
  53. var monthData = []
  54. var weekIndex = 0
  55. var monthIndex = 0
  56. if (res.data) {
  57. if (res.data.atAGlance) {
  58. glanceData = res.data.atAGlance
  59. }
  60. if (res.data.statisticsForThisWeek) {
  61. res.data.statisticsForThisWeek.forEach((item, index) => {
  62. if (this.nowDataString.indexOf(item.x) >= 0) {
  63. weekIndex = index;
  64. }
  65. //item.y += index + 2;
  66. item.label = currency + item.y;
  67. item.title = item.dateTimeStr + ' | ' + item.label + ' | ' + item.power + 'kw';
  68. weekdayData.push(item);
  69. });
  70. }
  71. if (res.data.pastSixMonths) {
  72. res.data.pastSixMonths.forEach((item, index) => {
  73. if (this.nowDataString.indexOf(item.x) >= 0) {
  74. monthIndex = index;
  75. }
  76. //item.y += index + 3;
  77. item.y0 = 0;
  78. item.label = currency + item.y;
  79. item.title = item.x + ' | ' + item.label + ' | ' + item.power + 'kw';
  80. if (this.enableChartArea) {
  81. if (index == 0) {
  82. item.label = " " + currency + item.y
  83. } else if (index == res.data.pastSixMonths.length - 1) {
  84. item.label = currency + item.y + " "
  85. }
  86. item.y += (item.y * 10 + 5);
  87. }
  88. monthData.push(item);
  89. });
  90. }
  91. }
  92. this.setState({
  93. glanceData: glanceData,
  94. weekIndex: weekIndex,
  95. monthIndex: monthIndex,
  96. weekdayData: weekdayData,
  97. monthData: monthData
  98. }, () => {
  99. this.setState({
  100. chartReady: true
  101. })
  102. });
  103. this.stopRefresh();
  104. }).catch(err => {
  105. toastShort(err);
  106. this.stopRefresh();
  107. });
  108. }
  109. stopRefresh() {
  110. if (this.props.refreshed) {
  111. this.props.refreshed();
  112. }
  113. this.refreshing = false;
  114. }
  115. barTheme = (active) => ({
  116. data: {
  117. fill: ({datum, index}) => {
  118. return (datum.y > 0 ? (index == active ? colorAccent : chartThemes) : "#999999");
  119. }
  120. },
  121. labels: {
  122. fontSize: 12,
  123. fill: ({index}) => (index == active ? "#000" : "#555")
  124. }
  125. })
  126. areaTheme = () => ({
  127. data: {
  128. //fill: chartThemes,
  129. fill: chartThemes,
  130. fillOpacity: 0.7,
  131. stroke: colorPrimary,
  132. strokeWidth: 1
  133. },
  134. labels: {
  135. fontSize: 10,
  136. fill: textSecondary
  137. }
  138. })
  139. getFill = ({datum, index}) => (datum.y > 0 ? (index == this.state.monthIndex ? colorPrimary : chartThemes) : "#999999");
  140. render() {
  141. return (
  142. <View style={this.props.shown ? ui.flex1 : styles.hide}>
  143. { this.props.atAglance &&
  144. <View style={styles.glanceView}>
  145. <Text style={styles.glanceTitle}>At a glance</Text>
  146. <View style={styles.overviewRow}>
  147. <View style={ui.flex1}>
  148. <Text style={styles.valueText}>{this.state.glanceData.averageCharge ?? 0}</Text>
  149. <Text style={styles.titleText}>kWh/Week</Text>
  150. <Text style={styles.subTitleText}>Average Charge</Text>
  151. </View>
  152. <View style={ui.flex1}>
  153. <Text style={styles.valueText}>{this.state.glanceData.averageSpend ?? 0}</Text>
  154. <Text style={styles.titleText}>{currency}/Week</Text>
  155. <Text style={styles.subTitleText}>Average Spend</Text>
  156. </View>
  157. <View style={ui.flex1}>
  158. <Text style={styles.valueText}>{utils.hour2HHmm(this.state.glanceData.averageTime)}</Text>
  159. <Text style={styles.titleText}>Hr/Week</Text>
  160. <Text style={styles.subTitleText}>Average Time</Text>
  161. </View>
  162. </View>
  163. </View>
  164. }
  165. <View style={styles.statisticView}>
  166. <View style={ui.flexcw}>
  167. <Text style={styles.sectionTitle}>For the week of</Text>
  168. {/* <Text style={styles.linkText}>1st Jan to 8th Jan </Text> */}
  169. </View>
  170. <View style={styles.overviewRow}>
  171. <View style={ui.flex1}>
  172. {/* <Text style={styles.valueText}>{this.state.glanceData.averageCharge ?? 0}</Text> */}
  173. <Text style={styles.titleText}>{this.state.glanceData.averageCharge ?? 0} kWh/Week</Text>
  174. <Text style={styles.subTitleText}>Average Charge</Text>
  175. </View>
  176. <View style={styles.overviewDivide}></View>
  177. <View style={ui.flex1}>
  178. {/* <Text style={styles.valueText}>{this.state.glanceData.averageSpend ?? 0}</Text> */}
  179. <Text style={styles.titleText}>{currency}{this.state.glanceData.averageSpend ?? 0}/Week</Text>
  180. <Text style={styles.subTitleText}>Average Spend</Text>
  181. </View>
  182. <View style={styles.overviewDivide}></View>
  183. <View style={ui.flex1}>
  184. {/* <Text style={styles.valueText}>{utils.hour2HHmm(this.state.glanceData.averageTime)}</Text> */}
  185. <Text style={styles.titleText}>{utils.hour2HHmm(this.state.glanceData.averageTime)}/Week</Text>
  186. <Text style={styles.subTitleText}>Average Time</Text>
  187. </View>
  188. </View>
  189. </View>
  190. <View style={ui.flex1}>
  191. <View style={styles.statisticView}>
  192. <Text style={styles.sectionTitle}>Statistics for this week</Text>
  193. <Text style={styles.statisticTitle}>{this.state.weekdayData[this.state.weekIndex]?.title}</Text>
  194. <Svg height={200}>
  195. { this.state.chartReady &&
  196. <VictoryChart
  197. theme={VictoryTheme.material}
  198. animate={animate}
  199. height={200}
  200. padding={{top: 50, left: 32, right: 60, bottom: 50}}>
  201. <VictoryAxis style={axisTheme}/>
  202. <VictoryBar
  203. barWidth={25}
  204. style={this.barTheme(this.state.weekIndex)}
  205. data={this.state.weekdayData}
  206. events={[{
  207. target: "data",
  208. eventHandlers: {
  209. onPress: () => {
  210. return [{
  211. target: "data",
  212. mutation: (props) => {
  213. this.setState({
  214. weekIndex: props.index
  215. });
  216. }
  217. }];
  218. }
  219. }
  220. }]}
  221. />
  222. </VictoryChart>
  223. }
  224. </Svg>
  225. </View>
  226. { this.state.monthData.length > 0 &&
  227. <View style={styles.statisticView}>
  228. <Text style={styles.sectionTitle}>Statistics for the past 6 months</Text>
  229. <Text style={styles.statisticTitle}>{this.state.monthData[this.state.monthIndex].title}</Text>
  230. {/* <svg style={{height: 0}}>
  231. <defs>
  232. <linearGradient id="myGradient" gradientUnits="userSpaceOnUse">
  233. <stop stopColor={chartThemes} stopOpacity="0.8"/>
  234. <stop offset="1" stopColor={chartThemes} stopOpacity="0"/>
  235. </linearGradient>
  236. </defs>
  237. </svg> */}
  238. { this.enableChartArea
  239. ? <Svg height={200}>
  240. { this.state.chartReady &&
  241. <VictoryChart
  242. theme={VictoryTheme.material}
  243. height={200}
  244. padding={{top: 50, left: 30, right: 60, bottom: 50}}>
  245. <VictoryAxis style={axisTheme}/>
  246. <VictoryArea
  247. style={this.areaTheme()}
  248. data={this.state.monthData}
  249. theme={VictoryTheme.material}
  250. labels={({ datum }) => datum.y}
  251. labelComponent={<VictoryLabel dy={10}/>}
  252. />
  253. </VictoryChart>
  254. }
  255. </Svg>
  256. : <Svg height={200}>
  257. <VictoryChart
  258. theme={VictoryTheme.material}
  259. height={200}
  260. padding={{top: 50, left: 30, right: 62, bottom: 50}}>
  261. <VictoryAxis style={axisTheme}/>
  262. <VictoryBar
  263. barWidth={28}
  264. data={this.state.monthData}
  265. style={this.barTheme(this.state.monthIndex)}
  266. events={[{
  267. target: "data",
  268. eventHandlers: {
  269. onPress: () => {
  270. return [{
  271. target: "data",
  272. mutation: (props) => {
  273. this.setState({
  274. monthIndex: props.index
  275. });
  276. }
  277. }];
  278. }
  279. }
  280. }]}
  281. />
  282. </VictoryChart>
  283. </Svg>
  284. }
  285. </View>
  286. }
  287. </View>
  288. </View>
  289. );
  290. }
  291. }
  292. const animate = {
  293. duration: 500,
  294. onLoad: {
  295. duration: 200
  296. }
  297. }
  298. const axisTheme = {
  299. axis: {stroke: "#fff"},
  300. grid: {strokeWidth: 0},
  301. ticks: {size: 0},
  302. tickLabels: {color: '#666', fontSize: 12, padding: 8},
  303. }
  304. const styles = StyleSheet.create({
  305. hide: {
  306. display: 'none'
  307. },
  308. glanceView: {
  309. marginTop: 16,
  310. marginLeft: 16,
  311. marginRight: 16,
  312. marginBottom: 4,
  313. ...ElevationObject(2),
  314. overflow: 'hidden',
  315. borderTopLeftRadius: 6,
  316. borderTopRightRadius: 6,
  317. backgroundColor: colorLight
  318. },
  319. glanceTitle: {
  320. color: textPrimary,
  321. fontSize: 16,
  322. paddingTop: 4,
  323. paddingLeft: 16,
  324. paddingBottom: 4,
  325. backgroundColor: '#C4C8DF'
  326. },
  327. overviewRow: {
  328. paddingTop: 16,
  329. paddingBottom: 10,
  330. alignItems: 'center',
  331. flexDirection: 'row'
  332. },
  333. overviewDivide: {
  334. width: 1,
  335. height: 12,
  336. backgroundColor: '#D9D9D9'
  337. },
  338. valueText: {
  339. color: '#000',
  340. fontSize: 22,
  341. paddingBottom: 8,
  342. textAlign: 'center'
  343. },
  344. titleText: {
  345. color: textPrimary,
  346. fontSize: 14,
  347. textAlign: 'center'
  348. },
  349. subTitleText: {
  350. color: textCancel,
  351. fontSize: 12,
  352. textAlign: 'center'
  353. },
  354. statisticView: {
  355. marginTop: 0,
  356. paddingTop: 16,
  357. backgroundColor: colorLight
  358. },
  359. sectionTitle: {
  360. color: textPrimary,
  361. fontSize: 14,
  362. paddingLeft: 16,
  363. fontWeight: 'bold'
  364. },
  365. statisticTitle: {
  366. color: textPrimary,
  367. fontSize: 14,
  368. paddingTop: 24,
  369. paddingBottom: 8,
  370. textAlign: 'center'
  371. },
  372. statisticChart: {
  373. height: 120
  374. },
  375. linkText: {
  376. ...ui.link,
  377. fontSize: 14,
  378. paddingRight: 16
  379. }
  380. })