TabReserve.js 16 KB

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