TabReserve.js 15 KB

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