Reserve.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. /**
  2. * Reserve页面
  3. * @邠心vbe on 2021/03/24
  4. */
  5. import React, { Component } from 'react'
  6. import { Image, Pressable, StyleSheet, Text, View } from 'react-native'
  7. import Button from '../../components/Button';
  8. import { ChargeStyle, EnterStationDialog, SelectableIcon, TypeImage } from './Charging';
  9. import Payment from '../wallet/Payment';
  10. import apiCharge from '../../api/apiCharge';
  11. import Dialog from '../../components/Dialog';
  12. import { PageList } from '../Router';
  13. import { CancelReserveDialog } from './InfoDialog';
  14. import TextView from '../../components/TextView';
  15. export default class Reserve extends Component {
  16. constructor(props) {
  17. super(props);
  18. this.state= {
  19. total: 0,
  20. leftId: 0,
  21. refreshId: 0,
  22. checkIndex: -1,
  23. available: false,
  24. showReserve: false,
  25. stationInfo: {},
  26. checkConnector: {},
  27. userReserve: {},
  28. timeLeft: '',
  29. showCancelDialog: false,
  30. showStationDialog: false
  31. }
  32. }
  33. componentDidMount() {
  34. this.setState({
  35. stationInfo: this.props.stationInfo
  36. }, () => this.init());
  37. }
  38. componentDidUpdate() {
  39. if (this.props.visible && this.props.refreshId != this.state.refreshId) {
  40. this.stopCountdown(true);
  41. this.setState({
  42. refreshId: this.props.refreshId,
  43. stationInfo: this.props.stationInfo
  44. }, () => this.init());
  45. }
  46. }
  47. init() {
  48. if (this.state.stationInfo.rateList && this.state.stationInfo.rateList.length > 0) {
  49. for (var i = 0; i < this.state.stationInfo.rateList.length; i++) {
  50. const item = this.state.stationInfo.rateList[i]
  51. if (item.available) {
  52. this.setState({
  53. checkIndex: i,
  54. checkConnector: item,
  55. })
  56. break;
  57. }
  58. }
  59. this.setState({
  60. total: this.props.stationInfo.rateList.length,
  61. showReserve: this.props.stationInfo.enableReservation ? true : false
  62. })
  63. this.getReserve();
  64. //this.refreshAvailable();
  65. } else {
  66. this.setState({
  67. showReserve: false
  68. })
  69. }
  70. }
  71. //刷新可用充电接口
  72. refreshAvailable() {
  73. const info = this.state.stationInfo
  74. const all = info?.allConnector;
  75. /*if (info.siteType == 'Private') {
  76. this.setState({
  77. isPrivate: true
  78. })
  79. }*/
  80. if (all) {
  81. this.setState({
  82. available: !all.available > 0
  83. });
  84. }
  85. }
  86. checkChange(index) {
  87. if (this.state.checkIndex !== index) {
  88. this.setState({
  89. checkIndex: index,
  90. checkConnector: this.props.stationInfo.rateList[index],
  91. })
  92. }
  93. }
  94. getAvailable(type) {
  95. let count = type;
  96. if (typeof type === 'string') {
  97. count = type == "AC" ? this.state.stationInfo.acConnector : this.state.stationInfo.dcConnector;
  98. }
  99. if (count) {
  100. return count.available + '/' + count.all;
  101. } else {
  102. return '0/0';
  103. }
  104. }
  105. getReserve() {
  106. apiCharge.getUserReserve(this.state.stationInfo.id).then(res => {
  107. if (res.data.reservePk && res.data.reserveEndTimeTimestamp > 0) {
  108. this.setState({
  109. userReserve: res.data
  110. }, () => this.startCountdown());
  111. } else {
  112. this.stopCountdown();
  113. }
  114. }).catch((err) => {
  115. this.stopCountdown();
  116. });
  117. }
  118. onReserve() {
  119. Dialog.showProgressDialog();
  120. apiCharge.reserveCharge({
  121. sitePk: this.state.stationInfo.id,
  122. chargeTypePk: this.state.checkConnector.chargeTypePk
  123. }).then(res => {
  124. Dialog.dismissLoading();
  125. toastShort('Reserved successfully!');
  126. this.props.onRefresh();
  127. this.getReserve();
  128. }).catch((err) => {
  129. Dialog.dismissLoading();
  130. toastShort(err)
  131. });
  132. }
  133. onCancel() {
  134. if (this.state.userReserve.reservePk) {
  135. Dialog.showProgressDialog();
  136. apiCharge.cancelReserve(this.state.userReserve.reservePk).then(res => {
  137. Dialog.dismissLoading();
  138. this.props.onRefresh();
  139. toastShort($t('common.cancelSuccess'));
  140. this.getReserve();
  141. }).catch((err) => {
  142. Dialog.dismissLoading();
  143. toastShort(err)
  144. });
  145. }
  146. }
  147. cancelReserve() {
  148. this.setState({
  149. showCancelDialog: true
  150. });
  151. /*Dialog.showDialog({
  152. title: 'Cancel Reservation',
  153. message: 'Are you sure you want to cancel reservation?',
  154. ok: 'CONFIRM',
  155. callback: (btn => {
  156. if (btn == "ok") {
  157. this.onCancel();
  158. }
  159. })
  160. })*/
  161. }
  162. startCountdown() {
  163. if (this.state.userReserve.reserveEndTimeTimestamp > 0) {
  164. if (!this.props.visible && this.props.onReserve)
  165. this.props.onReserve()
  166. const leftId = this.state.leftId;
  167. this.countdown(leftId);
  168. } else {
  169. this.stopCountdown(false, true);
  170. }
  171. }
  172. countdown(leftId) {
  173. if (leftId != this.state.leftId) {
  174. console.log(leftId, this.state.leftId);
  175. return;
  176. }
  177. const now = new Date().getTime();
  178. let left = this.state.userReserve.reserveEndTimeTimestamp - now;
  179. let s = 0, m = 0, h = 0;
  180. if (left > 1000) {
  181. s = left / 1000;
  182. if (s > 60) {
  183. m = s / 60;
  184. s = s % 60;
  185. if (m > 60) {
  186. h = m / 60;
  187. m = m % 60;
  188. }
  189. }
  190. } else {
  191. this.stopCountdown(false, true)
  192. }
  193. this.setState({
  194. timeLeft: this.formatNumber(h) + ' : ' + this.formatNumber(m) + ' : ' + this.formatNumber(s)
  195. });
  196. setTimeout(() => {
  197. this.countdown(leftId);
  198. }, 1000);
  199. }
  200. formatNumber(ins) {
  201. const num = parseInt(ins)
  202. if (num > 0) {
  203. if (num < 10) {
  204. return '0' + num;
  205. } else {
  206. return num;
  207. }
  208. } else {
  209. return '00';
  210. }
  211. }
  212. stopCountdown(just, refresh) {
  213. if (just) {
  214. this.setState({
  215. leftId: this.state.leftId + 1
  216. });
  217. } else {
  218. this.setState({
  219. leftId: this.state.leftId + 1,
  220. userReserve: {}
  221. });
  222. }
  223. if (refresh) {
  224. this.props.onRefresh();
  225. }
  226. }
  227. enterStatioinId() {
  228. if (this.props.onEnterStation) {
  229. this.props.onEnterStation();
  230. }
  231. }
  232. render() {
  233. return (
  234. <View
  235. style={{
  236. paddingLeft: 16,
  237. paddingRight: 16,
  238. ...(this.props.style || {})
  239. }}>
  240. { this.state.showReserve
  241. ? (
  242. this.state.userReserve.reservePk
  243. ? this.countdownView()
  244. : this.reserveView()
  245. )
  246. : <View style={[{height: $vh(50)}, ui.flexvc]}>
  247. <Text style={{color: textPrimary, fontSize: 14}}>Reservation is not available for this site.</Text>
  248. </View>
  249. }
  250. <CancelReserveDialog
  251. visible={this.state.showCancelDialog}
  252. onClose={confirm => {
  253. this.setState({
  254. showCancelDialog: false
  255. });
  256. if (confirm) {
  257. this.onCancel();
  258. }
  259. }}/>
  260. <EnterStationDialog
  261. visible={this.state.showStationDialog}
  262. stationId={this.state.stationInfo.id}
  263. onConfirm={() => this.enterStatioinId()}
  264. onClose={() => {
  265. this.setState({
  266. showStationDialog: false
  267. });
  268. }}
  269. />
  270. </View>
  271. )
  272. }
  273. //预定页面
  274. reserveView() {
  275. return (
  276. <>
  277. <Text style={styles.title}>Choose Connector</Text>
  278. <View style={styles.listView}>
  279. { this.state.total > 0
  280. ? this.state.stationInfo.rateList.map((item, index) => {
  281. const _type = item.type?.indexOf('AC') >= 0 ? 'AC' : 'DC';
  282. return (
  283. <Pressable
  284. key={index}
  285. style={[ChargeStyle.stationInfoView, index > 0 ? ChargeStyle.itemDivide : {}]}
  286. onPress={() => {
  287. if (item.available) {
  288. this.checkChange(index)
  289. }
  290. }
  291. }>
  292. <SelectableIcon selected={index == this.state.checkIndex}>
  293. <Image
  294. style={ChargeStyle.infoIcon}
  295. source={_type == "AC" ? TypeImage.AC : TypeImage.DC}/>
  296. </SelectableIcon>
  297. <View style={ChargeStyle.infoGroup}>
  298. <Text style={ChargeStyle.infoTitle}>Type</Text>
  299. <Text style={ChargeStyle.infoText}>{item.type}</Text>
  300. </View>
  301. <View style={ChargeStyle.infoGroup}>
  302. <Text style={ChargeStyle.infoTitle}>Power</Text>
  303. <Text style={ChargeStyle.infoText}>{item.power}</Text>
  304. </View>
  305. <View style={ChargeStyle.infoGroup}>
  306. <Text style={ChargeStyle.infoTitle}>Available/Total</Text>
  307. <Text style={ChargeStyle.infoBoldNumber}>{this.getAvailable(_type)}</Text>
  308. </View>
  309. { index == this.state.checkIndex
  310. ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusSelected]}>Selected</Text>
  311. : (item.available
  312. ? <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>Available</TextView>
  313. : <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>Unavailable</TextView>
  314. )
  315. }
  316. </Pressable>
  317. )
  318. }) : null
  319. }
  320. </View>
  321. { this.state.checkConnector.available
  322. ? <>
  323. <Text style={styles.title}>Choose Rate</Text>
  324. <View style={styles.listView}>
  325. <View style={ChargeStyle.stationInfoView}>
  326. <SelectableIcon selected={true}>
  327. <Image
  328. style={ChargeStyle.infoIcon}
  329. source={require('../../images/charge/ic-type-rate.png')}/>
  330. </SelectableIcon>
  331. <Text style={ChargeStyle.rateText}>Rate</Text>
  332. <Text style={[ChargeStyle.ratePrice]}>{this.state.checkConnector.rates}</Text>
  333. <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusSelected]}>Selected</Text>
  334. </View>
  335. </View>
  336. </>
  337. : <View style={{height: 60}}></View>
  338. }
  339. <Text style={styles.title}>Choose Payment Method</Text>
  340. <Payment refreshId={this.state.refreshId}/>
  341. <Button
  342. style={styles.buttonView}
  343. elevation={1.5}
  344. text='Reserve'
  345. disabled={this.state.available}
  346. onClick={() => this.onReserve()}
  347. />
  348. </>
  349. )
  350. }
  351. //倒计时页面
  352. countdownView() {
  353. let info = this.state.userReserve.siteRate
  354. return (
  355. <>
  356. <Text style={styles.title}>Your Selection</Text>
  357. { info &&
  358. <View style={styles.listView}>
  359. <View style={ChargeStyle.stationInfoView}>
  360. <Image
  361. style={ChargeStyle.infoIcon}
  362. source={info.type?.indexOf('AC') >= 0 ? TypeImage.AC : TypeImage.DC}/>
  363. <View style={ChargeStyle.infoGroup}>
  364. <Text style={ChargeStyle.infoTitle}>Type</Text>
  365. <Text style={ChargeStyle.infoText}>{info.type}</Text>
  366. </View>
  367. <View style={ChargeStyle.infoGroup}>
  368. <Text style={ChargeStyle.infoTitle}>Power</Text>
  369. <Text style={ChargeStyle.infoText}>{info.power}</Text>
  370. </View>
  371. <View style={ChargeStyle.infoGroup}>
  372. <Text style={ChargeStyle.infoTitle}>Available/Total</Text>
  373. <Text style={ChargeStyle.infoBoldNumber}>{this.getAvailable(info.connectorCount)}</Text>
  374. </View>
  375. <SelectableIcon selected={true}/>
  376. </View>
  377. <View style={[ChargeStyle.stationInfoView, ChargeStyle.itemDivide]}>
  378. <Image
  379. style={ChargeStyle.infoIcon}
  380. source={require('../../images/charge/ic-type-rate.png')}/>
  381. <Text style={ChargeStyle.rateText}>Rate</Text>
  382. <Text style={[ChargeStyle.ratePrice]}>{info.rates}</Text>
  383. <SelectableIcon selected={true}/>
  384. </View>
  385. </View>
  386. }
  387. <Text style={styles.timeleftText}>Reservation time left</Text>
  388. <View style={styles.timeleftView}>
  389. <Text style={styles.timeleft}>{this.state.timeLeft}</Text>
  390. </View>
  391. <View style={styles.cancelView}>
  392. <Button
  393. text='Cancel Reservation'
  394. textColor={textButton}
  395. style={styles.cancelButton}
  396. viewStyle={styles.cancelButtonView}
  397. onClick={() => this.cancelReserve()}
  398. />
  399. </View>
  400. <Text style={styles.title}>Choose Payment Method</Text>
  401. <Payment refreshId={this.state.refreshId}/>
  402. <View style={styles.buttonGroup}>
  403. <Button
  404. style={styles.buttonLeft}
  405. text='Scan QR'
  406. disabled={this.state.available}
  407. onClick={() => {
  408. startPage(PageList.scanqr, {actionDetail: false, id: this.state.stationInfo.id});
  409. }}/>
  410. <Button
  411. style={styles.buttonRight}
  412. text='Enter Station ID'
  413. disabled={this.state.available}
  414. onClick={() => {
  415. this.setState({
  416. showStationDialog: true
  417. });
  418. //startPage(PageList.summary)
  419. }
  420. }/>
  421. </View>
  422. </>
  423. )
  424. }
  425. }
  426. const styles = StyleSheet.create({
  427. title: {
  428. color: '#000',
  429. fontSize: 15,
  430. fontWeight: 'bold',
  431. paddingTop: 16,
  432. paddingBottom: 16
  433. },
  434. listView: {
  435. padding: 8,
  436. borderRadius: 8,
  437. backgroundColor: '#F5F5F5'
  438. },
  439. buttonView: {
  440. marginTop: 32,
  441. marginBottom: 32
  442. },
  443. timeleftText: {
  444. color: '#000',
  445. fontSize: 16,
  446. fontWeight: 'bold',
  447. paddingTop: 16,
  448. paddingBottom: 8,
  449. textAlign: 'center'
  450. },
  451. timeleftView: {
  452. alignItems: 'center',
  453. justifyContent: 'center',
  454. marginBottom: 16
  455. },
  456. timeleft: {
  457. color: '#FF2E00',
  458. fontSize: 18,
  459. fontWeight: 'bold'
  460. },
  461. cancelView: {
  462. flexDirection: 'row',
  463. justifyContent: 'center',
  464. paddingTop: 8,
  465. paddingBottom: 8
  466. },
  467. cancelButton: {
  468. borderRadius: 10,
  469. backgroundColor: '#ED4A4A'
  470. },
  471. cancelButtonView: {
  472. height: 48,
  473. paddingLeft: 16,
  474. paddingRight: 16,
  475. alignItems: 'center',
  476. flexDirection: 'row'
  477. },
  478. buttonGroup: {
  479. marginTop: 16,
  480. marginBottom: 24,
  481. alignItems: 'center',
  482. flexDirection: 'row'
  483. },
  484. buttonLeft: {
  485. flex: 1,
  486. elevation: 1.5,
  487. },
  488. buttonRight: {
  489. flex: 1,
  490. marginLeft: 16,
  491. elevation: 1.5
  492. },
  493. })