TabReserve.js 16 KB

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