Overview.js 14 KB

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