TabReserve.js 16 KB

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