Charging.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. /**
  2. * 充电中的页面组件
  3. * @邠心vbe on 2021/04/13
  4. */
  5. import React, { useRef, useEffect, useState } from 'react';
  6. import { Animated, View, Easing, StyleSheet, Image, Text, ImageBackground, TextInput } from 'react-native';
  7. import VbeRadialGradient from '../../components/VbeRadialGradient';
  8. import Modal from 'react-native-modal';
  9. import { ModalProps } from '../../components/BottomModal';
  10. import ChargeItemSelect from '../../icons/ChargeItemSelect';
  11. import { DialogMaxWidth } from './InfoDialog';
  12. import { QRResult } from './QRScan';
  13. export const circleSize = $vw(50.66) < 300 ? $vw(50.66) : 300;
  14. const batterySize = 0.463 * circleSize;
  15. const batteryWidth = 0.659*batterySize;
  16. export const TypeImage = {
  17. AC: require('../../images/charge/ic-type-ac.png'),
  18. DC: require('../../images/charge/ic-type-dc.png'),
  19. CHADEMO: require('../../images/charge/ic-type-chademo.png')
  20. }
  21. export const TypeImageList = [
  22. {
  23. name: "AC",
  24. nameScope: 'charging.AC',
  25. key: 'AC',
  26. icon: require('../../images/charge/ic-type-ac.png')
  27. }, {
  28. name: "DC",
  29. nameScope:'charging.DC',
  30. key: 'DC',
  31. icon: require('../../images/charge/ic-type-dc.png')
  32. }, /*{
  33. name: 'Chademo',
  34. nameScope: 'charging.Chademo',
  35. key: 'CHADEMO',
  36. icon: require('../../images/charge/ic-type-chademo.png')
  37. }*/
  38. ]
  39. export const getConnectTypeByKey = (key) => {
  40. for (let item of TypeImageList) {
  41. if (item.key == key) {
  42. return item;
  43. }
  44. }
  45. }
  46. export const CircleAnimate = ({isStart = false}) => {
  47. var rotate = useRef(new Animated.Value(0)).current;
  48. const spins = () => {
  49. Animated.timing(rotate, {
  50. toValue: 1,
  51. duration: 10000,
  52. easing: Easing.linear,
  53. useNativeDriver: true
  54. }).start(() => {
  55. rotate.setValue(0);
  56. spins();
  57. });
  58. }
  59. useEffect(() => {
  60. if (isStart) {
  61. spins();
  62. }
  63. }, [rotate]);
  64. const spin = rotate.interpolate({
  65. inputRange: [0, 1],
  66. outputRange: ['0deg', '360deg']
  67. })
  68. return (
  69. <Animated.View
  70. style={[
  71. styles.chargeCircle, {
  72. transform: [{
  73. rotate: spin
  74. }]
  75. }
  76. ]}>
  77. { isStart &&
  78. <View style={{ width: 25, height: 25, marginTop: -11}}>
  79. <VbeRadialGradient
  80. x="50%" y="50%" rx="50%" ry="50%"
  81. colorList={[
  82. {offset: '0%', color: '#FFF', opacity: .8},
  83. {offset: '40%', color: '#FFF', opacity: .5},
  84. {offset: '70%', color: '#FFF', opacity: .3},
  85. {offset: '100%', color: '#FFF', opacity: 0}
  86. ]}/>
  87. </View>
  88. }
  89. </Animated.View>
  90. );
  91. }
  92. export const BatteryView = ({soc, isPending, isCharging}) => {
  93. var [powerPercent, setPercent] = useState(-1);
  94. /*var [powerText, setText] = useState(0);
  95. const autoCharge = () => {
  96. setTimeout(() => {
  97. const p = powerPercent + 0.001
  98. setPercent(Number(p.toFixed(4)))
  99. setText((powerPercent * 100).toFixed(0))
  100. if (powerPercent >= 1) {
  101. onComplete();
  102. }
  103. }, 50 + powerPercent * 100);
  104. }
  105. useEffect(() => {
  106. if (run && powerPercent <= 1) {
  107. autoCharge();
  108. }
  109. }, [powerPercent])*/
  110. useEffect(() => {
  111. if (isCharging) {
  112. try {
  113. var s = '-1' + soc;
  114. var d = '' + parseInt(s);
  115. d = d.replace('-1', '');
  116. if (d) {
  117. setPercent(parseInt(d))
  118. } else {
  119. setPercent(-1);
  120. }
  121. } catch (e) {
  122. setPercent(-1);
  123. }
  124. }
  125. }, [soc]);
  126. const getOpacity = (unit) => {
  127. var op = 1 * (powerPercent + unit);
  128. return op < 1 ? op : 1;
  129. }
  130. const getHeight = () => {
  131. if (powerPercent > 1) {
  132. return batterySize * powerPercent / 100;
  133. } else if (powerPercent > 0) {
  134. return batterySize * powerPercent;
  135. } else {
  136. return 0
  137. }
  138. }
  139. return (
  140. isCharging
  141. ? <View style={ui.center}>
  142. <ImageBackground
  143. style={{
  144. width: circleSize,
  145. height: circleSize,
  146. margin: 32,
  147. padding: 32,
  148. alignItems: 'center',
  149. justifyContent: 'center'
  150. }}
  151. source={require('../../images/charge/ic-charge-circle.png')}>
  152. <View style={styles.chargingView}>
  153. <View style={styles.plusLeftView}>
  154. <Image
  155. style={[styles.plusMiddle, { opacity: getOpacity(0.5)}]}
  156. source={require('../../images/charge/ic-plus-middle.png')}/>
  157. <Image
  158. style={[styles.plusSmall, { opacity: getOpacity(0.4)}]}
  159. source={require('../../images/charge/ic-plus-small.png')}/>
  160. </View>
  161. <View style={styles.batteryView}>
  162. <View style={[styles.batteryIcon]}>
  163. <Image
  164. style={styles.batteryIcon}
  165. source={require('../../images/charge/ic-battery-0.png')}/>
  166. <View style={[styles.batteryLayer, { height: getHeight() }]}>
  167. <Image
  168. style={[styles.batteryIcon]}
  169. source={require('../../images/charge/ic-battery-1.png')}/>
  170. </View>
  171. </View>
  172. { isPending
  173. ? <Text style={styles.batterySoc}>Initiating...</Text>
  174. : powerPercent != -1
  175. ? <Text style={styles.batteryPercent}>{powerPercent}%</Text>
  176. : <Text style={styles.batterySoc}>{'In Charging'}</Text>
  177. }
  178. </View>
  179. <View style={styles.plusRightView}>
  180. <Image
  181. style={[styles.plusLarge, { opacity: getOpacity(0.6)}]}
  182. source={require('../../images/charge/ic-plus-large.png')}/>
  183. </View>
  184. </View>
  185. <CircleAnimate isStart={true}/>
  186. </ImageBackground>
  187. </View>
  188. : <View style={styles.completeView}>
  189. <Image
  190. style={styles.disconnectIcon}
  191. source={require('../../images/charge/charge-complete.png')}/>
  192. <Text style={styles.completeTip}>Please disconnect and return connector to charging station</Text>
  193. </View>
  194. );
  195. }
  196. export const EnterStationDialog = ({visible, stationId, onConfirm, onClose}) => {
  197. var [inputStationId, setInput] = useState('')
  198. const enterStatioinId= () => {
  199. //console.log(inputStationId);
  200. if (inputStationId) {
  201. QRResult.applyInputStation(inputStationId, stationId, (success, err) => {
  202. setInput('')
  203. if (success) {
  204. if (onConfirm) onConfirm()
  205. } else if (err) {
  206. toastShort(err)
  207. }
  208. if (onClose) onClose()
  209. });
  210. } else {
  211. toastShort('Please input Station ID')
  212. }
  213. }
  214. return (
  215. <Modal
  216. isVisible={visible}
  217. {...ModalProps}
  218. onBackdropPress={() => onClose}
  219. onBackButtonPress={() => onClose}>
  220. <View style={styles.stationDialog}>
  221. <Text style={styles.stationInputTitle}>Enter Station ID</Text>
  222. <TextInput
  223. style={styles.stationInput}
  224. defaultValue={inputStationId}
  225. placeholder='e.g: EVCTD0002-1'
  226. placeholderTextColor={textPlacehoder}
  227. onChangeText={text => setInput(text)}
  228. />
  229. <View style={styles.dialogButtons}>
  230. <Button
  231. textSize={17}
  232. style={styles.buttonCancel}
  233. text='Close'
  234. onClick={onClose}/>
  235. <Button
  236. textSize={17}
  237. style={styles.buttonOK}
  238. text='Confirm'
  239. onClick={() => enterStatioinId()}/>
  240. </View>
  241. </View>
  242. </Modal>
  243. )
  244. }
  245. const styles = StyleSheet.create({
  246. chargingView: {
  247. width: circleSize,
  248. height: circleSize,
  249. flexDirection: 'row',
  250. alignItems: 'center'
  251. },
  252. chargeCircle: {
  253. top: 0,
  254. left: 0,
  255. zIndex: 10,
  256. width: circleSize,
  257. height: circleSize,
  258. position: 'absolute',
  259. alignItems: 'center',
  260. borderRadius: circleSize
  261. },
  262. batteryView: {
  263. paddingTop: 8,
  264. paddingLeft: 12,
  265. paddingRight: 12,
  266. alignItems: 'center',
  267. justifyContent: 'center'
  268. },
  269. batteryIcon: {
  270. width: batteryWidth,
  271. height: batterySize,
  272. position: 'relative',
  273. justifyContent: 'flex-end',
  274. },
  275. batteryLayer: {
  276. left: 0,
  277. right: 0,
  278. height: 0,
  279. width: batteryWidth,
  280. overflow: 'hidden',
  281. position: 'absolute',
  282. justifyContent: 'flex-end',
  283. },
  284. batteryPercent: {
  285. color: textPrimary,
  286. fontSize: 22,
  287. textAlign: 'center',
  288. paddingTop: 10,
  289. paddingBottom: 8
  290. },
  291. batterySoc: {
  292. color: textPrimary,
  293. fontSize: 18,
  294. textAlign: 'center',
  295. paddingTop: 10,
  296. paddingBottom: 10
  297. },
  298. plusLeftView: {
  299. flex: 1,
  300. height: batterySize + 10,
  301. marginBottom: 12,
  302. alignItems: 'flex-end',
  303. justifyContent: 'space-around'
  304. },
  305. plusRightView: {
  306. flex: 1,
  307. justifyContent: 'center'
  308. },
  309. plusLarge: {
  310. width: 24,
  311. height: 24
  312. },
  313. plusMiddle: {
  314. width: 18,
  315. height: 18
  316. },
  317. plusSmall: {
  318. width: 12,
  319. height: 12,
  320. marginBottom: 8
  321. },
  322. selectView: {
  323. position: 'relative'
  324. },
  325. selectIcon: {
  326. width: 18,
  327. height: 18
  328. },
  329. selectIconAbs: {
  330. top: -2,
  331. right: -4,
  332. zIndex: 2,
  333. position: 'absolute'
  334. },
  335. completeView: {
  336. height: circleSize,
  337. marginBottom: 16,
  338. alignItems: 'center',
  339. justifyContent: 'center'
  340. },
  341. disconnectIcon: {
  342. width: 100,
  343. height: 100
  344. },
  345. completeTip: {
  346. width: circleSize,
  347. color: textPrimary,
  348. fontSize: 16,
  349. paddingLeft: 16,
  350. paddingRight: 16,
  351. textAlign: 'center'
  352. },
  353. stationDialog: {
  354. padding: 16,
  355. marginLeft: 'auto',
  356. marginRight: 'auto',
  357. borderRadius: isIOS ? 20 : 3,
  358. width: DialogMaxWidth,
  359. backgroundColor: colorLight
  360. },
  361. stationInput: {
  362. ...$padding(4, 10),
  363. minHeight: 40,
  364. borderRadius: 3,
  365. backgroundColor: '#F0F0F0'
  366. },
  367. stationInputTitle: {
  368. fontSize: 15,
  369. textAlign: 'center',
  370. paddingBottom: 16
  371. },
  372. dialogButtons: {
  373. paddingTop: 16,
  374. paddingBottom: 8,
  375. flexDirection: 'row'
  376. },
  377. buttonOK: {
  378. flex: 1,
  379. marginLeft: 4,
  380. },
  381. buttonCancel: {
  382. flex: 1,
  383. backgroundColor: '#eee'
  384. }
  385. });
  386. export const SelectableIcon = ({selected, children}) => {
  387. return (
  388. <View style={styles.selectView}>
  389. {selected &&
  390. <View style={[styles.selectIcon, children && styles.selectIconAbs]}>
  391. <ChargeItemSelect size={18} selected={true}/>
  392. </View>
  393. }
  394. {children}
  395. </View>
  396. );
  397. }
  398. export const ChargeStyle = StyleSheet.create({
  399. stationInfoView: {
  400. padding: 8,
  401. alignItems: 'center',
  402. flexDirection: 'row',
  403. justifyContent: 'space-between'
  404. },
  405. infoGroup: {
  406. ...$padding(4, 8),
  407. alignItems: 'center'
  408. },
  409. infoTitle: {
  410. color: '#999',
  411. fontSize: 12
  412. },
  413. infoText: {
  414. color: textPrimary,
  415. fontSize: 12,
  416. paddingTop: 4
  417. },
  418. infoBoldNumber: {
  419. color: textPrimary,
  420. fontSize: 14,
  421. paddingTop: 3,
  422. fontWeight: 'bold'
  423. },
  424. infoStatus: {
  425. fontSize: 12,
  426. borderRadius: 4,
  427. ...$padding(4, 10)
  428. },
  429. infoIcon: {
  430. width: 38,
  431. height: 38
  432. },
  433. itemDivide: {
  434. borderTopWidth: 1,
  435. borderTopColor: '#eee'
  436. },
  437. statusSelected: {
  438. color: textPrimary,
  439. ...$padding(4, 11),
  440. backgroundColor: colorAccent
  441. },
  442. statusAvailable: {
  443. color: textLight,
  444. backgroundColor: '#90DB0A'
  445. },
  446. statusUnavailable: {
  447. color: '#999',
  448. fontSize: 10,
  449. ...$padding(5, 8),
  450. backgroundColor: '#CCC'
  451. },
  452. statusWarning: {
  453. color: textLight,
  454. backgroundColor: colorAccent
  455. },
  456. rateText: {
  457. color: textPrimary,
  458. fontSize: 14,
  459. },
  460. ratePrice: {
  461. color: '#000',
  462. fontSize: 14,
  463. },
  464. authText: {
  465. color: '#000',
  466. fontSize: 12,
  467. paddingTop: 2
  468. }
  469. });