Home.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. /**
  2. * 首页通用页
  3. * @邠心vbe on 2021/08/13
  4. */
  5. import React, { Component } from 'react';
  6. import { View, StyleSheet, BackHandler, Linking, AppState } from 'react-native';
  7. import app from '../../../app.json'
  8. import { i18nUtil } from '../../i18n';
  9. import apiBase from '../../api/apiBase';
  10. import apiStation from '../../api/apiStation';
  11. import Dialog from '../../components/Dialog';
  12. import utils from '../../utils/utils';
  13. import { SettingUtil } from '../Settings';
  14. import MyStatusBar from '../../components/MyStatusBar';
  15. import LocationPermission from './maps/LocationPermission';
  16. import MapUI from './MapUI';
  17. import MapUILumi from './MapUILumi';
  18. import storage from '../../utils/storage';
  19. const KEY_STORE_LIST = "map_stop_list"
  20. const KEY_STORE_GPS = "map_location_gps"
  21. export default class HomePage extends Component {
  22. constructor(props) {
  23. super(props);
  24. this.state = {
  25. region: {
  26. latitude: 1.3532623163977149,
  27. longitude: 103.87092316860532,
  28. latitudeDelta: 0.0922,
  29. longitudeDelta: 0.0421
  30. },
  31. lastRegion: {},
  32. mapReady: false,
  33. showStation: false,
  34. stationId: "",
  35. stationInfo: {},
  36. stopList: [],
  37. hasPermission: false,
  38. permissionDenied: false,
  39. pinMessage: {
  40. visible: false
  41. }
  42. };
  43. this.isHide = true;
  44. this.denied = true;
  45. this.backSeconds = 0;
  46. this.refreshTime = 0;
  47. this.settingInfo = {}
  48. this.viewIndex = -1;
  49. this.filter = {
  50. parkingFee: 'ALL',
  51. connectorType: ''
  52. };
  53. this.refresh = false;
  54. this.stateListener;
  55. this.forceUpdateDialog = undefined;
  56. this.locationListener = undefined;
  57. }
  58. componentDidMount() {
  59. const navigation = this.props.navigation;
  60. if (this.locationListener) {
  61. this.locationListener.removeListener();
  62. this.locationListener = undefined;
  63. }
  64. if (!isIOS) {
  65. this.locationListener = new LocationPermission.LocationListener();
  66. if (this.locationListener) {
  67. this.locationListener.addListener();
  68. }
  69. }
  70. navigation.addListener('focus', () => {
  71. //console.log('------Home------', "Focus")
  72. this.onCloseInfo();
  73. SettingUtil.getSettings(set => {
  74. //console.log("获取设置信息", set);
  75. this.settingInfo = set;
  76. this.init();
  77. })
  78. this.isHide = false;
  79. /*if (!app.isWhitelabel) {
  80. MyStatusBar.setStatusBarThemes(MyStatusBar.DARK_STYLE, colorLight);
  81. }*/
  82. MyStatusBar.setTranslucentStatusBar();
  83. });
  84. navigation.addListener('blur', () => {
  85. //toastShort('onStop')
  86. this.isHide = true;
  87. /*if (app.isLumiWhitelabel) {
  88. MyStatusBar.setStatusBarThemes(MyStatusBar.DARK_STYLE, colorLight);
  89. } else {
  90. MyStatusBar.setStatusBarThemes(themeStatusBar, colorPrimaryDark);
  91. }*/
  92. setTimeout(() => {
  93. MyStatusBar.setStatusBarTheme(MyStatusBar.DEFAULT_STYLE);
  94. }, 50);
  95. setTimeout(() => {
  96. navigation.closeDrawer();
  97. }, 500);
  98. });
  99. this.stateListener = AppState.addEventListener("change", state => {
  100. if (state == 'active' && this.forceUpdateDialog) {
  101. this.showUpdateDialog(this.forceUpdateDialog);
  102. }
  103. });
  104. BackHandler.addEventListener('hardwareBackPress', this.toExit)
  105. this.isHide = false;
  106. this.checkUpdateVersion();
  107. }
  108. componentWillUnmount() {
  109. /*console.log("componentWillUnmount")
  110. if (this.unsubscribe) {
  111. this.unsubscribe();
  112. }*/
  113. this.backSeconds = 0;
  114. if (this.stateListener) {
  115. this.stateListener.remove();
  116. }
  117. BackHandler.removeEventListener("hardwareBackPress", this.toExit);
  118. }
  119. init() {
  120. if (this.state.stopList.length == 0 && !this.refresh) {
  121. storage.getStorage(KEY_STORE_LIST).then(data => {
  122. //console.log("获取站点缓存", "成功");
  123. if (data) {
  124. const list = JSON.parse(data);
  125. this.setState({
  126. stopList: list
  127. }, () => {
  128. this.checkPermission2Geo(true);
  129. })
  130. } else {
  131. this.checkPermission2Geo(true);
  132. }
  133. }).catch(err => {
  134. console.log("获取站点缓存-err", err);
  135. this.checkPermission2Geo(true);
  136. })
  137. storage.getStorage(KEY_STORE_GPS).then(data => {
  138. //console.log("获取GPS缓存", data);
  139. if (data) {
  140. const location = JSON.parse(data);
  141. if (location.latitude) {
  142. this.setState({
  143. lastRegion: location
  144. });
  145. }
  146. }
  147. }).catch(err => {
  148. console.log("获取GPS缓存-err", err);
  149. })
  150. this.getPinMessage()
  151. }
  152. }
  153. toExit = () => {
  154. //console.log("beforeRemove", this.isHide);
  155. if (this.state.stationInfo?.id) {
  156. this.setState({
  157. stationInfo: {}
  158. });
  159. return true;
  160. }
  161. if (!this.isHide) {
  162. if (isIOS) {
  163. //e.preventDefault();
  164. } else {
  165. let time = new Date().getTime();
  166. //console.log("beforeRemove2", time, this.backSeconds);
  167. if (time - this.backSeconds < 2000) {
  168. BackHandler.exitApp();
  169. } else {
  170. toastShort($t("home.backAgainTips"));
  171. this.backSeconds = time;
  172. //e.preventDefault();
  173. }
  174. }
  175. return true;
  176. }
  177. }
  178. checkGPS() {
  179. if (this.locationListener) {
  180. this.locationListener.check();
  181. }
  182. }
  183. onMapReady() {
  184. this.setState({
  185. mapReady: true
  186. }, () => {
  187. this.init();
  188. });
  189. }
  190. checkUpdateVersion() {
  191. apiBase.checkUpdate().then(res => {
  192. if (res.data.versionCode > app.versionCode) {
  193. var desc = JSON.parse(res.data?.versionDesc ?? "{}");
  194. if (desc) {
  195. const upLog = i18nUtil.analyzeLocaleData(desc);
  196. if (typeof upLog == "string") {
  197. const prps = {
  198. title: $t("home.versionUpdate"),
  199. message: $t("home.newVersionName") + res.data.versionName + "\n\n" + upLog,
  200. showCancel: !(res.data.force),
  201. ok: $t("home.updateNow"),
  202. cancel: $t("home.later"),
  203. callback: btn => {
  204. if (btn == Dialog.BUTTON_OK) {
  205. //TODO 跳转到应用商店
  206. const uri = isIOS
  207. ? app.storeUrl.ios
  208. : app.storeUrl.android
  209. Linking.openURL(uri);
  210. }
  211. }
  212. }
  213. if (res.data.force) {
  214. prps.onBackPress = () => {
  215. }
  216. this.forceUpdateDialog = prps;
  217. }
  218. this.showUpdateDialog(prps);
  219. }
  220. }
  221. }
  222. }).catch(err => {
  223. })
  224. }
  225. showUpdateDialog(options) {
  226. if (Dialog.isShowing()) {
  227. setTimeout(() => {
  228. this.showUpdateDialog(options)
  229. }, 500);
  230. return;
  231. }
  232. Dialog.showDialog(options)
  233. }
  234. getPermission() {
  235. LocationPermission.requestPermission(hasPermission => {
  236. if (hasPermission) {
  237. this.setState({
  238. hasPermission: true
  239. });
  240. this.checkPermission2Geo();
  241. } else {
  242. this.noPermissionSite();
  243. }
  244. })
  245. }
  246. checkPermission2Geo(refresh, button) {
  247. if (button && this.state.lastRegion.latitude) {
  248. this.setState({
  249. region: {...this.state.lastRegion}
  250. });
  251. }
  252. if (this.refresh == refresh) {
  253. return;
  254. }
  255. if (refresh) {
  256. //避免关闭自动移动地图后无法点击按钮移动地图
  257. this.refresh = true
  258. }
  259. console.log("checkPermission2Geo", refresh, button);
  260. this.checkGPS();
  261. if (this.state.hasPermission) {
  262. this.infoGeoLocation();
  263. } else {
  264. LocationPermission.checkPermission((hasPermission, canRequestPermission) => {
  265. if (hasPermission) {
  266. this.setState({
  267. hasPermission: true
  268. });
  269. this.infoGeoLocation();
  270. } else {
  271. if (canRequestPermission) {
  272. if (this.denied) {
  273. this.denied = false;
  274. this.getPermission();
  275. } else {
  276. //避免多次请求
  277. this.denied = true;
  278. this.noPermissionSite();
  279. }
  280. } else {
  281. this.noPermissionSite();
  282. }
  283. }
  284. })
  285. }
  286. }
  287. noPermissionSite() {
  288. console.log("未获取权限也获取站点列表");
  289. this.setState({
  290. hasPermission: false,
  291. permissionDenied: true
  292. });
  293. let first = this.state.stopList.length == 0;//是否第一次加载
  294. this.getStationList(this.state.region, first);
  295. }
  296. /**
  297. * 获取定位数据
  298. * @param {*} again
  299. */
  300. infoGeoLocation(again) {
  301. if (this.state.mapReady) {
  302. navigator.geolocation.getCurrentPosition(location => {
  303. let region = {
  304. latitude: location.coords.latitude,
  305. longitude: location.coords.longitude,
  306. latitudeDelta: 0.0922,
  307. longitudeDelta: 0.0421
  308. }
  309. this.saveGPSCache(region);
  310. //console.log("getGeoLocation", region);
  311. //if (this.state.stopList.length == 0) { //只加载一次
  312. let time = new Date().getTime();
  313. let interval = this.settingInfo.refreshInterval * 1000;//重复加载时间
  314. let first = this.state.stopList.length == 0;//是否第一次加载
  315. if (first || this.refresh || time - this.refreshTime > interval) { //一分钟(10秒)加载一次
  316. this.getStationList(region, first || this.refresh);
  317. this.refreshTime = time;
  318. } else if (this.settingInfo.moveMyLocation) {
  319. this.setState({
  320. region: region
  321. });
  322. }
  323. }, error => {
  324. console.info("getGeoLocation", error);
  325. if (!again) {
  326. setTimeout(() => {
  327. this.infoGeoLocation(true);
  328. }, 2000);
  329. }
  330. });
  331. } else {
  332. setTimeout(() => {
  333. this.infoGeoLocation(true);
  334. }, 1000);
  335. }
  336. }
  337. findFilter(data) {
  338. this.filter = data;
  339. console.log("筛选站点", data);
  340. this.getStationList();
  341. }
  342. /**
  343. * 获取所有充电桩
  344. * @param {Location} region 当前位置
  345. * @param {boolean} first 是否初始化
  346. */
  347. getStationList(region, first) {
  348. //Dialog.showProgressDialog();
  349. if (getUserId()) {
  350. this.filter.operaUserId = getUserId()
  351. }
  352. if (region && (this.settingInfo.moveMyLocation || first)) {
  353. this.setState({
  354. region: region
  355. });
  356. }
  357. apiStation.getAllStation(this.filter).then(res => {
  358. Dialog.dismissLoading();
  359. if (res.data.sites) {
  360. const list = [];
  361. res.data.sites.forEach(item => {
  362. let available = false
  363. if (item.allConnector && item.allConnector.available) {
  364. available = true
  365. }
  366. list.push({
  367. id: item.sitePk,
  368. name: item.siteName,
  369. //address: item.siteAddress,
  370. available: available,
  371. siteType: item.siteType,
  372. latitude: item.locationLatitude,
  373. longitude: item.locationLongitude,
  374. favorite: item?.favorite ? true : false,
  375. hasLabel: item?.hasLabel,
  376. upcoming: item?.upcoming
  377. /*acConnector: item.acConnector,
  378. allConnector: item.allConnector,
  379. dcConnector: item.dcConnector,
  380. distance: utils.getDistance(item.distance)*/
  381. });
  382. });
  383. this.setState({
  384. stopList: list
  385. });
  386. if (this.filter.connectorType == "" && this.filter.parkingFee == "ALL") {
  387. storage.setStorageJson(KEY_STORE_LIST, list);
  388. }
  389. }
  390. this.refresh = false;
  391. }).catch(err => {
  392. Dialog.dismissLoading();
  393. toastShort(err);
  394. /*this.setState({
  395. stopList: []
  396. });*/
  397. this.refresh = false;
  398. })
  399. }
  400. //点击Marker获取StaionInfo
  401. viewChargeStation(id) {
  402. //console.log('info', this.state.stopList[index]);
  403. //const stationInfo = this.state.stopList[index];
  404. if (this.state.stationId == id) {
  405. return;
  406. }
  407. this.setState({
  408. stationId: id,
  409. stationInfo: {},
  410. showStation: true
  411. });
  412. if (app.modules.bookmarks) {
  413. for (let i = 0; i < this.state.stopList.length; i++) {
  414. let info = this.state.stopList[i];
  415. if (info.id == id) {
  416. this.viewIndex = i;
  417. break;
  418. }
  419. }
  420. }
  421. if (this.state.hasPermission) {
  422. navigator.geolocation.getCurrentPosition(location => {
  423. let region = {
  424. latitude: location.coords.latitude,
  425. longitude: location.coords.longitude,
  426. latitudeDelta: 0.0922,
  427. longitudeDelta: 0.0421
  428. }
  429. setTimeout(() => {
  430. this.getStationInfo(id, region);
  431. }, 500);
  432. /*if (this.settingInfo.alwaysLocation) {
  433. this.setState({
  434. region: region
  435. });
  436. }*/
  437. });
  438. } else {
  439. //无定位权限仍然显示
  440. setTimeout(() => {
  441. this.getStationInfo(id, this.state.region);
  442. }, 500);
  443. }
  444. //startPage(PageList.chargeDetail, {...this.state.stopList[index]});
  445. }
  446. getStationInfo(id, location) {
  447. apiStation.getStationRate({
  448. sitePk: id,
  449. lat: location?.latitude,
  450. lng: location?.longitude
  451. }).then(res => {
  452. if (res.data.sitePk) {
  453. var info = utils.getSiteInfo(res.data);
  454. this.setState({
  455. stationInfo: info
  456. });
  457. } else {
  458. this.setState({
  459. stationInfo: {}
  460. });
  461. }
  462. }).catch(err => {
  463. this.setState({
  464. stationInfo: {}
  465. });
  466. toastShort(err);
  467. });
  468. }
  469. onCloseInfo() {
  470. this.viewIndex = -1;
  471. this.setState({
  472. stationId: "",
  473. stationInfo: {},
  474. showStation: false
  475. });
  476. }
  477. favoriteSite() {
  478. if (this.state.stationInfo?.id) {
  479. Dialog.showProgressDialog();
  480. apiStation.bookmarkSite(this.state.stationInfo.id).then(res => {
  481. const info = {...this.state.stationInfo, favorite: !this.state.stationInfo.favorite}
  482. if (this.viewIndex >= 0) {
  483. const list = [...this.state.stopList]
  484. const inf = {...list[this.viewIndex], favorite: info.favorite};
  485. list[this.viewIndex] = inf;
  486. this.setState({
  487. stopList: list,
  488. stationInfo: info
  489. });
  490. } else {
  491. this.setState({
  492. stationInfo: info
  493. });
  494. }
  495. }).catch(err => {
  496. toastShort(err);
  497. console.log(err)
  498. }).finally(() => {
  499. Dialog.dismissLoading();
  500. })
  501. }
  502. }
  503. getPinMessage() {
  504. apiBase.getPinMessage().then(res => {
  505. if (res.data && res.data.pinTitle) {
  506. const pin = {
  507. visible: true,
  508. title: res.data.pinTitle,
  509. message: res.data.pinContent
  510. }
  511. this.setState({
  512. pinMessage: pin
  513. })
  514. } else {
  515. this.setState({
  516. pinMessage: {
  517. visible: false
  518. }
  519. })
  520. }
  521. }).catch(err => {
  522. this.setState({
  523. pinMessage: {
  524. visible: false
  525. }
  526. })
  527. })
  528. }
  529. hidePermissionPanel() {
  530. this.setState({
  531. permissionDenied: false
  532. })
  533. }
  534. saveGPSCache(location={}) {
  535. if (location.latitude) {
  536. storage.setStorageJson(KEY_STORE_GPS, location);
  537. this.setState({
  538. lastRegion: location
  539. });
  540. }
  541. }
  542. render() {
  543. return (
  544. <View style={ui.flex1}>
  545. { app.isLumiWhitelabel
  546. ? <MapUILumi
  547. state={this.state}
  548. navigation={this.props.navigation}
  549. onFilter={data => this.findFilter(data)}
  550. onMapReady={() => this.onMapReady()}
  551. onFavorite={() => this.favoriteSite()}
  552. onCloseInfo={() => this.onCloseInfo()}
  553. onLocation={() => this.checkPermission2Geo(true, true)}
  554. useApplesMap={this.settingInfo.useApplesMap}
  555. showUserLocation={this.state.hasPermission && this.settingInfo.alwaysLocation}
  556. viewChargeStation={id => this.viewChargeStation(id)}/>
  557. : <MapUI
  558. state={this.state}
  559. navigation={this.props.navigation}
  560. onFilter={data => this.findFilter(data)}
  561. onMapReady={() => this.onMapReady()}
  562. onFavorite={() => this.favoriteSite()}
  563. onCloseInfo={() => this.onCloseInfo()}
  564. onLocation={() => this.checkPermission2Geo(true, true)}
  565. useApplesMap={this.settingInfo.useApplesMap}
  566. showUserLocation={this.state.hasPermission && this.settingInfo.alwaysLocation}
  567. viewChargeStation={id => this.viewChargeStation(id)}
  568. pinMessage={this.state.pinMessage}
  569. />
  570. }
  571. <LocationPermission.VIEW
  572. visible={this.state.permissionDenied}
  573. onView={() => this.hidePermissionPanel()}/>
  574. <View style={styles.drawerLeftTouchView}></View>
  575. </View>
  576. );
  577. }
  578. }
  579. const styles = StyleSheet.create({
  580. drawerLeftTouchView: {
  581. top: 0,
  582. left: 0,
  583. bottom: 0,
  584. width: 4,
  585. zIndex: 5,
  586. position: 'absolute',
  587. backgroundColor: 'rgba(0,0,0,0)'
  588. }
  589. })