City.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import React, { Component } from 'react';
  2. import { NavBar, SearchBar, WhiteSpace, List, Toast, Icon } from 'antd-mobile';
  3. import { SearchMask, IndexNav, Indicator, PositionCity, SearchArea } from '../../components';
  4. import { getLocalCity } from '../../services/locationServices';
  5. import {
  6. LASTEST_CITY,
  7. getAllCities,
  8. saveLocalStorageCity,
  9. getLocalStorageCity,
  10. searchCityByName,
  11. transformCityMenuData
  12. } from '../../services/cityServices';
  13. import { throttle } from '../../utils';
  14. import './city.css';
  15. const Item = List.Item;
  16. const searchCity = throttle(searchCityByName);
  17. class City extends Component {
  18. constructor(props) {
  19. super(props);
  20. this.state = {
  21. localCity: '北京',
  22. hotCity: [],
  23. latestCity: [],
  24. city: {},
  25. labels: [],
  26. searchCities: [],
  27. moving: false,
  28. indicator: '',
  29. loading: false,
  30. searchArea: false
  31. };
  32. }
  33. componentDidMount = () => {
  34. this.setState(() => {
  35. Toast.loading('Loading...');
  36. return { loading: true };
  37. });
  38. Promise.all([getLocalCity(), getAllCities(), getLocalStorageCity(LASTEST_CITY)]).then(result => {
  39. const localCity = result[0] && result[0].replace(/市/, '');
  40. const city = result[1].afterFilterAllCity.allCity;
  41. const labels = result[1].afterFilterAllCity.validateLetters;
  42. const hotCity = result[1].hotCity;
  43. const latestCity = result[2];
  44. this.setState(() => {
  45. Toast.hide();
  46. return {
  47. localCity,
  48. city,
  49. hotCity,
  50. labels,
  51. latestCity,
  52. loading: false
  53. };
  54. });
  55. });
  56. }
  57. onSearchInput = async value => {
  58. if (!value) {
  59. this.hideMenuDialog();
  60. return;
  61. }
  62. const { labels, city } = this.state;
  63. const cities = await searchCity(value, labels, city);
  64. this.setState({
  65. searchArea: true,
  66. searchCities: transformCityMenuData(cities)
  67. });
  68. }
  69. hideMenuDialog = () => {
  70. this.setState({
  71. searchArea: false,
  72. searchCities: []
  73. });
  74. }
  75. renderLocalCity = () => {
  76. const { localCity, latestCity } = this.state;
  77. return <PositionCity
  78. title={ '定位/最近访问' }
  79. position
  80. positionCity={ localCity }
  81. city={ latestCity }
  82. onSelectCity={ this.onSelectCity } />
  83. }
  84. renderHotCity = () => {
  85. const { hotCity } = this.state;
  86. return <PositionCity
  87. title={ '热门城市' }
  88. position={ false }
  89. city={ hotCity }
  90. onSelectCity={ this.onSelectCity } />
  91. }
  92. renderCities = () => {
  93. const { city } = this.state;
  94. return Object.keys(city).map(letter => {
  95. const cList = city[letter];
  96. return (
  97. <div key={ letter } ref={ element => this[`section${letter}`] = element }>
  98. <List renderHeader={ () => letter } className="select-city-list">
  99. {
  100. cList.length > 0 && cList.map(c => {
  101. return <Item key={ c.id } onClick={ () => this.onSelectCity(c) }>{ c.city }</Item>
  102. })
  103. }
  104. </List>
  105. </div>
  106. );
  107. });
  108. }
  109. renderSearchCity = () => {
  110. const { searchCities } = this.state;
  111. return <SearchArea city={ searchCities } onChange={ this.onChangeMenu } />
  112. }
  113. onChangeMenu = value => {
  114. this.hideMenuDialog();
  115. this.onSelectCity(value[0]);
  116. }
  117. onSelectCity = (city) => {
  118. if (this.state.moving) {
  119. return;
  120. }
  121. const { history, location } = this.props;
  122. const { cityCode, ...rest } = location.state;
  123. const backCity = typeof city === 'string' ? city : city.city;
  124. rest[`city${cityCode}`] = backCity;
  125. saveLocalStorageCity(LASTEST_CITY, backCity);
  126. history.push({
  127. pathname: '/',
  128. state: { ...rest }
  129. });
  130. }
  131. onNavChange = (nav) => {
  132. this.setState(() => {
  133. const label = nav.label ? nav.label : this.state.label;
  134. const moving = nav.moving ? nav.moving : this.state.moving;
  135. this[`section${label}`] && this[`section${label}`].scrollIntoView();
  136. return {
  137. moving,
  138. indicator: label
  139. }
  140. });
  141. }
  142. render() {
  143. const { labels, indicator, moving, loading, searchArea } = this.state;
  144. if (loading) {
  145. return null;
  146. }
  147. return (
  148. <div className="city">
  149. <div className="city-top">
  150. <NavBar
  151. className="navbar"
  152. mode="dark"
  153. icon={ <Icon type="left" /> }
  154. leftContent="返回"
  155. onLeftClick={ () => this.props.history.goBack() }
  156. >选择城市</NavBar>
  157. <SearchBar
  158. placeholder="请输入你要选择的城市"
  159. maxLength={ 8 }
  160. onChange={ this.onSearchInput } />
  161. </div>
  162. <div className="city-list">
  163. <div className="city-search-area">
  164. { searchArea && this.renderSearchCity() }
  165. </div>
  166. <SearchMask show={ searchArea } city={ this.state.searchCities } />
  167. <div className="city-list-content">
  168. { this.renderLocalCity() }
  169. <WhiteSpace size="md" />
  170. { this.renderHotCity() }
  171. { this.renderCities() }
  172. <Indicator indicator={ indicator } />
  173. </div>
  174. <div className="city-label">
  175. <IndexNav labels={ labels } moving={ moving } onNavChange={ this.onNavChange } />
  176. </div>
  177. </div>
  178. </div>
  179. );
  180. }
  181. }
  182. export default City;