SearchV3.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /**
  2. * V3版搜索页
  3. * @邠心vbe on 2024/05/11
  4. */
  5. import React, { Component } from 'react';
  6. import { View, Text, StyleSheet, TextInput, FlatList, Image } from 'react-native';
  7. import apiStation from '../../api/apiStation';
  8. import Dialog from '../../components/Dialog';
  9. import utils from '../../utils/utils';
  10. import { PageList } from '../Router';
  11. import ListViewV3 from './ListViewV3';
  12. export default class SearchV3 extends Component {
  13. constructor(props) {
  14. super(props);
  15. this.state = {
  16. isSearch: false,
  17. searchResult: [{id:0}],
  18. searchWorld: "",
  19. latlng: {
  20. lat: 0.00001,
  21. lng: 0.00001
  22. }
  23. };
  24. }
  25. componentDidMount() {
  26. this.getGeoLocation();
  27. }
  28. changeWord(word) {
  29. this.setState({
  30. searchWorld: word
  31. });
  32. }
  33. getGeoLocation() {
  34. navigator.geolocation.getCurrentPosition(location => {
  35. let latlng = {
  36. lat: location.coords.latitude,
  37. lng: location.coords.longitude
  38. }
  39. this.setState({
  40. latlng: latlng
  41. })
  42. this.searchStation(latlng);
  43. }, error => {
  44. console.info("[Search] getGeoLocation", error);
  45. this.searchStation(this.state.latlng);
  46. });
  47. }
  48. searchStation(latlng, time=100) {
  49. this.setState({
  50. isSearch: true
  51. });
  52. latlng.siteName = this.state.searchWorld
  53. apiStation.searchStation(latlng).then(res => {
  54. if (res.data.sites) {
  55. const list = [];
  56. res.data.sites.forEach(item => {
  57. list.push({
  58. id: item.sitePk,
  59. name: item.siteName,
  60. address: item.siteAddress,
  61. latitude: item.locationLatitude,
  62. longitude: item.locationLongitude,
  63. acConnector: item.acConnector,
  64. allConnector: item.allConnector,
  65. dcConnector: item.dcConnector,
  66. siteType: item.siteType,
  67. favorite: item.favorite,
  68. upcoming: item.upcoming,
  69. labels: item.siteLabels,
  70. distance: utils.getDistance(item.distance),
  71. serviceProvider: item.serviceProvider
  72. });
  73. });
  74. setTimeout(() => {
  75. this.setState({
  76. isSearch: false,
  77. searchResult: list
  78. });
  79. }, time);
  80. }
  81. }).catch(err => {
  82. console.log('searchStation-err', err);
  83. this.setState({
  84. isSearch: false,
  85. searchResult: []
  86. });
  87. });
  88. }
  89. favoriteSite(index, info) {
  90. if (info?.id) {
  91. Dialog.showProgressDialog();
  92. apiStation.bookmarkSite(info.id).then(res => {
  93. if (index >= 0) {
  94. const list = [...this.state.searchResult];
  95. list[index].favorite = !info.favorite;
  96. this.setState({
  97. searchResult: list
  98. });
  99. }
  100. }).catch(err => {
  101. toastShort(err);
  102. }).finally(() => {
  103. Dialog.dismissLoading();
  104. })
  105. }
  106. }
  107. intoStation(info) {
  108. if (info.upcoming) {
  109. toastShort($t("home.upcoming"))
  110. } else {
  111. utils.toChargeDetailPage(info.id, 'search', PageList.search);
  112. //startPage(PageList.chargeDetailPage, {stationInfo: info, action: 'search', from: PageList.search});
  113. }
  114. }
  115. listItem = (props) => {
  116. return (
  117. <ListViewV3
  118. {...props}
  119. onPress={() => this.intoStation(props.item)}
  120. onFavorite={() => this.favoriteSite(props.index, props.item)}/>
  121. )
  122. }
  123. render() {
  124. return (
  125. <View style={styles.container}>
  126. <View style={styles.searchView}>
  127. <Feather
  128. name={'search'}
  129. size={20}
  130. color={textSecondary}/>
  131. <TextInput
  132. style={styles.searchInput}
  133. autoFocus={true}
  134. maxLength={50}
  135. numberOfLines={1}
  136. returnKeyType={'search'}
  137. clearButtonMode={'while-editing'}
  138. placeholder={"Search by Postal Code, Address, Site Name"}
  139. placeholderTextColor={textPlacehoder}
  140. value={this.state.searchWorld}
  141. onChangeText={text => this.changeWord(text)}
  142. onSubmitEditing={() => {
  143. //this.getGeoLocation();
  144. this.searchStation(this.state.latlng, 300);
  145. }}/>
  146. { utils.isNotEmpty(this.state.searchWorld) &&
  147. <MaterialCommunityIcons
  148. name="close-circle"
  149. size={20}
  150. color={textCancel}
  151. onPress={() => this.changeWord("")}/>
  152. }
  153. </View>
  154. { this.state.isSearch
  155. ? <View style={styles.searchingView}>
  156. <Image
  157. style={styles.seachingIcon}
  158. source={require('../../images/icon/search-loading.gif')}/>
  159. </View>
  160. : <FlatList
  161. style={styles.listView}
  162. data={this.state.searchResult}
  163. renderItem={this.listItem}
  164. keyExtractor={item => item.id}
  165. keyboardShouldPersistTaps="always"
  166. ListEmptyComponent={<Text style={styles.noResult}>{$t('home.noSearch')}</Text>}
  167. />
  168. }
  169. </View>
  170. );
  171. }
  172. }
  173. const styles = StyleSheet.create({
  174. container: {
  175. flex: 1,
  176. backgroundColor: pageBackground
  177. },
  178. searchView: {
  179. margin: 16,
  180. ...$padding(2, 16),
  181. borderRadius: 60,
  182. borderWidth: 1,
  183. borderColor: '#E5E5E5',
  184. alignItems: 'center',
  185. flexDirection: 'row',
  186. backgroundColor: '#F5F5F5'
  187. },
  188. searchInput: {
  189. flex: 1,
  190. color: textPrimary,
  191. ...$padding(6, 8),
  192. fontSize: 15,
  193. marginLeft: 4,
  194. lineHeight: 20
  195. },
  196. searchingView: {
  197. padding: 16,
  198. alignItems: 'center'
  199. },
  200. seachingIcon: {
  201. width: 60,
  202. height: 60
  203. },
  204. noResult: {
  205. color: '#999',
  206. fontSize: 14,
  207. padding: 20,
  208. textAlign: 'center',
  209. },
  210. listView: {
  211. flex: 1
  212. },
  213. itemView: {
  214. alignItems: 'center',
  215. flexDirection: 'row',
  216. borderBottomWidth: 1,
  217. borderBottomColor: '#eee'
  218. },
  219. stationInfo: {
  220. flex: 1,
  221. padding: 16
  222. },
  223. nameView: {
  224. paddingTop: 3,
  225. alignItems: 'center',
  226. flexDirection: 'row'
  227. },
  228. stationName: {
  229. color: textPrimary,
  230. fontSize: 18,
  231. fontWeight: 'bold'
  232. },
  233. stationAddress: {
  234. color: '#666',
  235. fontSize: 14,
  236. paddingBottom: 8
  237. },
  238. infoStatus: {
  239. fontSize: 10,
  240. paddingTop: 3,
  241. paddingLeft: 8,
  242. paddingRight: 8,
  243. paddingBottom: 3,
  244. borderRadius: 5,
  245. marginLeft: 12,
  246. },
  247. selected: {
  248. color: textPrimary,
  249. backgroundColor: colorAccent
  250. },
  251. available: {
  252. color: textLight,
  253. backgroundColor: '#90DB0A'
  254. },
  255. unavailable: {
  256. color: '#999',
  257. fontSize: 10.5,
  258. paddingTop: 7,
  259. paddingLeft: 9,
  260. paddingRight: 9,
  261. paddingBottom: 7,
  262. backgroundColor: '#CCC'
  263. },
  264. connectView: {
  265. paddingTop: 4,
  266. paddingBottom: 4,
  267. alignItems: 'center',
  268. flexDirection: 'row'
  269. },
  270. directView: {
  271. zIndex: 1,
  272. paddingRight: 16,
  273. alignItems: 'center'
  274. },
  275. distanceText: {
  276. color: textPrimary,
  277. fontSize: 12,
  278. paddingTop: 2
  279. }
  280. });