chose-city.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. <template>
  2. <view class="chose-city">
  3. <view>
  4. <view class="status_bar" :style="{height: iStatusBarHeight + 'px'}" v-if="topshow"></view>
  5. <view class="nav" v-if="topshow">城市选择
  6. <image src="../../static/img/goback.png" mode="" class="foback" @click="closeModal"></image>
  7. </view>
  8. </view>
  9. <!-- 城市搜索 -->
  10. <view class="city-search-wrap">
  11. <view class="search">
  12. <view class="l-search">
  13. <view class="icon-search">
  14. <!-- <view class="cuIcon-search"></view> -->
  15. <image src="../../static/img/icon_search.png" mode="" class="cuIcon-search"></image>
  16. </view>
  17. <input class="input-search" type="text" :value="inputValue" placeholder="请输入城市"
  18. placeholder-style="color:#8E8F97" :focus="searchFocus" @input="searchChange" />
  19. <text class="clear-input iconfont icon-icon-test" v-if="isClearBtn" @click="inputValue = ''"></text>
  20. </view>
  21. <view class="r-cancel" @click="closeModal">取消</view>
  22. </view>
  23. <!-- 搜索列表 -->
  24. <view class="reach-content" v-show="inputValue">
  25. <block v-show="searchData.length">
  26. <view class="li" v-for="item in searchData" :key="item.citycode" @click="selectCity(item)">
  27. {{item.name}}
  28. </view>
  29. </block>
  30. <view class="has-no-data" v-show="hasNoData">没有找到匹配数据~</view>
  31. </view>
  32. </view>
  33. <!-- 城市列表 -->
  34. <scroll-view class="scroll-view" scroll-y scroll-with-animation="true" enable-back-to-top="true"
  35. :scroll-into-view="toIndex" @scroll="scrollHandle" v-if="!inputValue">
  36. <view class="block">
  37. <!-- 您所在的地区 -->
  38. <view class="area list-item" id="area">
  39. <view class="title-wrapp">
  40. <view class="c-title">
  41. <text class="l">定位城市</text>
  42. </view>
  43. </view>
  44. <view class="ul">
  45. <view @click="selectCity(citydw)" class="dingweiCity">{{citydw.name}}</view>
  46. <view class="dingBg" @click="againDingWei()">
  47. <image src='../../static/img/icon_location.png' class="icon"></image>
  48. <text class="text">重新定位</text>
  49. </view>
  50. </view>
  51. </view>
  52. <!-- 历史记录 -->
  53. <!-- <view class="area list-item" id="record" v-if="recordList.length">
  54. <view class="title-wrapp">
  55. <view class="c-title">
  56. <text class="l">历史记录</text>
  57. </view>
  58. </view>
  59. <view class="ul">
  60. <view class="li font-clamp" v-for="item in recordList" :key="item.citycode" @click="selectCity(item)">
  61. {{ item.name }}
  62. </view>
  63. </view>
  64. </view> -->
  65. </view>
  66. <!-- 城市列表 -->
  67. <view class="city-list">
  68. <view class="list list-item" v-for="(item, key) of cityList" :key="key" :id="item.nameType">
  69. <view class="c-title">{{ item.nameType }}</view>
  70. <view class="item" v-for="innerItem in item.list" :key="innerItem.citycode"
  71. @click="selectCity(innerItem)">
  72. {{ innerItem.name }}
  73. </view>
  74. </view>
  75. </view>
  76. </scroll-view>
  77. <!-- 字母列表 -->
  78. <view class="alphabet" @touchstart="touchStart" @touchend="touchEnd" @touchmove.stop="touchMove">
  79. <view v-for="(item, index) in alphabet" :key="index" @touchstart="getLetter" @touchend="setLetter"
  80. :id="item">
  81. <view class="item" :class="{ active: currentLetter == item }">
  82. {{ item == 'area' ? '当前' : item == 'record' ? '历史' : item }}
  83. </view>
  84. </view>
  85. </view>
  86. </view>
  87. </template>
  88. <script>
  89. import cityJson from '@/static/dataJson/city.json'
  90. export default {
  91. props: {
  92. topshow: { //参数名
  93. type: Boolean, //定义传值的类型
  94. default: false //参数默认
  95. }
  96. },
  97. data() {
  98. return {
  99. isIPX: null,
  100. regionId: null, // 区域ID
  101. isToggle: true,
  102. isReach: false,
  103. inputValue: '',
  104. searchData: [], // 搜索的数据
  105. isClearBtn: false,
  106. toIndex: '', // 跳转的索引的字母
  107. tipsLetter: '', // 滑动显示字母
  108. timer: null,
  109. hasNoData: false,
  110. searchFocus: false,
  111. letterDetails: [],
  112. currentLetter: 'area', //默认选择
  113. cityArr: [],
  114. recordList: [],
  115. cityList: [],
  116. hasLocation: false,
  117. myCityObj: {},
  118. alphabet: [],
  119. iStatusBarHeight: '',
  120. lng: '',
  121. lat: '',
  122. cityname: '',
  123. cityCode: '',
  124. citydw: {
  125. name: '',
  126. citycode: '',
  127. }
  128. };
  129. },
  130. mounted() {
  131. this.iStatusBarHeight = uni.getSystemInfoSync().statusBarHeight;
  132. this.cityArr = cityJson.data.list
  133. if (this.cityArr && this.cityArr[0]) {
  134. this.cityArr.map(v => {
  135. v.nameType = v.pinyin.substr(0, 1)
  136. })
  137. this.cityList = this.groupArr(this.cityArr, 'nameType')
  138. }
  139. this.recordList = cityJson.data.recordList
  140. this.alphabet = cityJson.data.alphabet
  141. this.getLocation()
  142. },
  143. watch: {
  144. // 城市搜索输入框
  145. inputValue(newVal) {
  146. this.isClearBtn = newVal ? true : false;
  147. if (this.timer) {
  148. clearTimeout(this.timer);
  149. }
  150. if (!this.inputValue) {
  151. this.searchData = [];
  152. return;
  153. }
  154. this.timer = setTimeout(() => {
  155. const result = [];
  156. this.cityList.map(v => {
  157. v.list.forEach((item) => {
  158. if (/^[a-zA-Z]+$/.test(item.pinyin) && item.pinyin.toLowerCase()
  159. .includes(this.inputValue.toLowerCase()) ||
  160. item.name.includes(this.inputValue)) {
  161. result.push(item);
  162. }
  163. });
  164. })
  165. this.searchData = result;
  166. if (this.searchData.length === 0) {
  167. this.hasNoData = true;
  168. } else {
  169. this.hasNoData = false;
  170. }
  171. }, 500);
  172. },
  173. isReach(val) {
  174. this.searchFocus = val;
  175. },
  176. },
  177. methods: {
  178. againDingWei(){
  179. this.getLocation();
  180. },
  181. getLocation() {
  182. const that = this
  183. /* uni.getLocation({
  184. // #ifdef MP-ALIPAY
  185. type: 'gcj02',
  186. // #endif
  187. success(res) {
  188. console.log('---',res)
  189. res.name = res.city
  190. res.citycode = res.cityAdcode
  191. that.myCityObj = res
  192. that.hasLocation = true
  193. uni.setStorageSync('nowCityObj', res)
  194. },
  195. fail(err) {
  196. that.hasLocation = false
  197. uni.showToast({
  198. icon:'none',
  199. title: '获取用户定位失败,请手动选择当前城市'
  200. })
  201. },
  202. }) */
  203. console.log("定位")
  204. uni.getLocation({
  205. type: 'gcj02',
  206. success: function(res) {
  207. console.log(res)
  208. that.lng = res.longitude
  209. that.lat = res.latitude
  210. that.getAdress();
  211. },
  212. fail(err) {
  213. console.log(err)
  214. // that.cityname = uni.getStorageSync("location").cityname;
  215. // that.citydw.name = uni.getStorageSync("location").cityname
  216. // that.citydw.citycode = uni.getStorageSync("location").cityCode
  217. uni.showToast({
  218. icon:'none',
  219. title: '获取用户定位失败,请手动选择当前城市'
  220. })
  221. }
  222. });
  223. },
  224. getAdress() {
  225. var location = this.lng + ',' + this.lat
  226. uni.request({
  227. url: 'https://restapi.amap.com/v3/place/around', //仅为示例,并非真实接口地址。
  228. data: {
  229. key: '064b6a4a8ade55656edcde2f528876de',
  230. location: location,
  231. types: "190000",
  232. extensions: "all",
  233. radius: 100
  234. },
  235. dataType: "json",
  236. success: (res) => {
  237. console.log(res);
  238. this.cityname = res.data.pois[0].cityname;
  239. var cityCode = res.data.pois[0].adcode
  240. cityCode = cityCode.slice(0, -2)
  241. cityCode = cityCode + '00'
  242. this.cityCode = cityCode
  243. this.citydw.name = this.cityname
  244. this.citydw.citycode = this.cityCode
  245. }
  246. });
  247. },
  248. groupArr(list, field) {
  249. var fieldList = [],
  250. att = [];
  251. list.map((e) => {
  252. fieldList.push(e[field])
  253. })
  254. //数组去重
  255. fieldList = fieldList.filter((e, i, self) => {
  256. return self.indexOf(e) == i
  257. })
  258. for (var j = 0; j < fieldList.length; j++) {
  259. //过滤出匹配到的数据
  260. var arr = list.filter((e) => {
  261. return e[field] == fieldList[j];
  262. })
  263. att.push({
  264. nameType: arr[0].nameType,
  265. list: arr
  266. })
  267. }
  268. return att;
  269. },
  270. selectCity(item, type) {
  271. if (type === 'refresh' && !this.hasLocation) {
  272. // 获取定位
  273. return this.getLocation()
  274. }
  275. // console.log('选择的城市:', item);
  276. uni.setStorageSync('myCityObj', item)
  277. this.$emit('selectCity', item)
  278. // 当前项目是需要选择到区域,所以选择城市后回到区县的地方
  279. this.toIndex = 'area';
  280. setTimeout(() => {
  281. this.toIndex = '';
  282. }, 1000);
  283. },
  284. closeModal() {
  285. this.$emit('closeModal')
  286. },
  287. //列表滚动,和右边字母表对应
  288. scrollHandle(e) {
  289. let view = uni.createSelectorQuery().in(this).selectAll('.list-item');
  290. view
  291. .boundingClientRect((d) => {
  292. let top = d[0].top;
  293. d.forEach((item) => {
  294. item.top = item.top - top;
  295. item.bottom = item.bottom - top;
  296. this.letterDetails.push({
  297. id: item.id,
  298. top: item.top,
  299. bottom: item.bottom,
  300. });
  301. });
  302. })
  303. .exec();
  304. const scrollTop = e.detail.scrollTop;
  305. this.letterDetails.some((item) => {
  306. if (scrollTop >= item.top && scrollTop <= item.bottom - 20) {
  307. this.currentLetter = item.id;
  308. //当前固定用的是粘性定位,如果不用粘性定位,在这里设置
  309. return true;
  310. }
  311. });
  312. },
  313. //搜索
  314. searchChange(e) {
  315. let {
  316. value
  317. } = e.detail;
  318. this.inputValue = value;
  319. },
  320. // 触发开始
  321. touchStart(e) {
  322. // console.log(e);
  323. },
  324. //移动时
  325. touchMove(e) {
  326. uni.vibrateShort();
  327. let y = e.touches[0].clientY;
  328. let offsettop = e.currentTarget.offsetTop;
  329. //判断选择区域,只在选择区才会生效
  330. if (y > offsettop) {
  331. let num = parseInt((y - offsettop) / 15); //右边每个字母元素的高度
  332. let letter = this.alphabet[num];
  333. this.tipsLetter = letter;
  334. let curentLetter = this.letterTransform(letter);
  335. uni.showToast({
  336. title: curentLetter,
  337. icon: 'none',
  338. });
  339. }
  340. },
  341. //触发结束
  342. touchEnd() {
  343. this.toIndex = this.tipsLetter;
  344. },
  345. //移动开始获取字母,并放大提示
  346. getLetter(e) {
  347. uni.vibrateShort();
  348. let {
  349. id
  350. } = e.currentTarget;
  351. this.tipsLetter = id;
  352. let curentLetter = this.letterTransform(id);
  353. uni.showToast({
  354. title: curentLetter,
  355. icon: 'none',
  356. });
  357. },
  358. //移动结束设置字母,赋值到toIndex
  359. setLetter() {
  360. this.toIndex = this.tipsLetter;
  361. },
  362. //提示字母转换
  363. letterTransform(letter) {
  364. let str = '';
  365. if (letter == 'area') {
  366. str = '当前';
  367. } else if (letter == 'record') {
  368. str = '历史';
  369. } else {
  370. str = letter;
  371. }
  372. return str;
  373. },
  374. },
  375. }
  376. </script>
  377. <style lang="less" scoped>
  378. .chose-city {
  379. position: fixed;
  380. top: 0;
  381. left: 0;
  382. right: 0;
  383. bottom: 0;
  384. z-index: 999;
  385. background: #fff;
  386. }
  387. .city-search-wrap {
  388. width: 100%;
  389. box-sizing: border-box;
  390. .search {
  391. width: 750rpx;
  392. height: 110rpx;
  393. display: flex;
  394. align-items: center;
  395. font-size: 28rpx;
  396. color: #222;
  397. padding: 14rpx 36rpx;
  398. box-sizing: border-box;
  399. background: #fff;
  400. .l-search {
  401. width: 597rpx;
  402. position: relative;
  403. height: 72rpx;
  404. line-height: 72rpx;
  405. .icon-search {
  406. font-size: 28rpx;
  407. position: absolute;
  408. left: 0rpx;
  409. top: 0;
  410. color: #8e8f97;
  411. font-weight: 700;
  412. height: 72rpx;
  413. line-height: 72rpx;
  414. }
  415. .cuIcon-search {
  416. width: 40rpx;
  417. height: 40rpx;
  418. padding: 20rpx;
  419. }
  420. .input-search {
  421. width: 597rpx;
  422. height: 72rpx;
  423. box-sizing: border-box;
  424. padding: 0 84rpx 0 84rpx;
  425. text-align: left;
  426. background: #f4f5f9;
  427. border-radius: 12rpx;
  428. border: 0;
  429. }
  430. .clear-input {
  431. font-size: 30rpx;
  432. position: absolute;
  433. right: 10rpx;
  434. top: 50%;
  435. transform: translateY(-50%);
  436. padding: 10rpx;
  437. color: #8e8f97;
  438. }
  439. }
  440. .r-cancel {
  441. width: 80rpx;
  442. box-sizing: border-box;
  443. padding-left: 24rpx;
  444. font-size: 28rpx;
  445. height: 72rpx;
  446. line-height: 72rpx;
  447. background: transparent;
  448. border: 0;
  449. color: #519AD2;
  450. }
  451. }
  452. }
  453. .reach-content {
  454. padding-left: 36rpx;
  455. box-sizing: border-box;
  456. .li {
  457. width: 714rpx;
  458. font-size: 28rpx;
  459. height: 100rpx;
  460. line-height: 100rpx;
  461. color: #333;
  462. position: relative;
  463. box-sizing: border-box;
  464. border-bottom: 2rpx solid #F5F5F5;
  465. }
  466. }
  467. .block {
  468. // padding: 0 36rpx;
  469. padding-left: 36rpx;
  470. padding-right: 20rpx;
  471. box-sizing: border-box;
  472. }
  473. .top-search {
  474. line-height: 72rpx;
  475. padding: 14rpx 30rpx 0;
  476. box-sizing: border-box;
  477. margin-bottom: 26rpx;
  478. .item {
  479. background: #F5F5F5;
  480. border-radius: 12rpx;
  481. font-size: 28rpx;
  482. text-align: center;
  483. color: #999999;
  484. /* #ifdef MP-ALIPAY */
  485. height: 72rpx;
  486. line-height: 72rpx;
  487. /* #endif */
  488. text {
  489. padding-left: 20rpx;
  490. color: #c1c2cd;
  491. vertical-align: middle;
  492. position: relative;
  493. top: -4rpx;
  494. }
  495. }
  496. }
  497. .scroll-view {
  498. width: 100%;
  499. height: calc(100vh - 110rpx);
  500. box-sizing: border-box;
  501. }
  502. .area {
  503. margin-bottom: 8rpx;
  504. .title-wrapp {
  505. position: sticky;
  506. top: 0;
  507. left: 0;
  508. background: #fff;
  509. }
  510. .c-title {
  511. width: 100%;
  512. box-sizing: border-box;
  513. font-size: 28rpx;
  514. color: #999999;
  515. margin-bottom: 24rpx;
  516. display: inline-flex;
  517. justify-content: space-between;
  518. align-items: center;
  519. .r {
  520. font-size: 24rpx;
  521. color: #8e8f97;
  522. display: inline-block;
  523. align-items: center;
  524. .iconfont {
  525. font-size: 24rpx;
  526. }
  527. }
  528. }
  529. .dingweiCity {
  530. background-color: #F4F5F7;
  531. border-radius: 49rpx;
  532. width: 140rpx;
  533. height: 64rpx;
  534. text-align: center;
  535. line-height: 64rpx;
  536. font-size: 26rpx;
  537. }
  538. .ul {
  539. display: flex;
  540. flex-wrap: wrap;
  541. justify-content: space-between;
  542. .li {
  543. width: 155rpx;
  544. padding: 0 10rpx;
  545. box-sizing: border-box;
  546. height: 72rpx;
  547. line-height: 68rpx;
  548. text-align: center;
  549. font-size: 32rpx;
  550. color: #333;
  551. border-radius: 8rpx;
  552. margin: 0 18rpx 28rpx 0;
  553. // border: 2rpx solid #E2E2E2;
  554. &:nth-child(4n) {
  555. margin-right: 0;
  556. }
  557. &.now {
  558. width: auto;
  559. padding: 0 32rpx 0 22rpx;
  560. .icon {
  561. width: 33rpx;
  562. height: 33rpx;
  563. background-size: 100%;
  564. vertical-align: middle;
  565. position: relative;
  566. top: -4rpx;
  567. }
  568. .text {
  569. padding-left: 10rpx;
  570. color: #3F90F7;
  571. font-size: 26rpx;
  572. }
  573. }
  574. &.active {
  575. font-weight: 500;
  576. background: #ffde45;
  577. }
  578. }
  579. .hover {
  580. background: #ffde45;
  581. }
  582. }
  583. }
  584. .city-list {
  585. width: 750rpx;
  586. padding-bottom: 50rpx;
  587. .c-title {
  588. height: 60rpx;
  589. line-height: 60rpx;
  590. font-size: 30rpx;
  591. font-weight: 500;
  592. color: #272636;
  593. background: #fff;
  594. box-sizing: border-box;
  595. padding-left: 36rpx;
  596. position: sticky;
  597. top: 0;
  598. left: 0;
  599. z-index: 2;
  600. }
  601. .item {
  602. width: 714rpx;
  603. margin-left: 36rpx;
  604. padding: 0 36rpx 0 0;
  605. height: 100rpx;
  606. line-height: 100rpx;
  607. color: #333;
  608. font-size: 28rpx;
  609. box-sizing: border-box;
  610. border-bottom: 2rpx solid #F5F5F5;
  611. }
  612. }
  613. .alphabet {
  614. position: fixed;
  615. right: 0;
  616. bottom: 12%;
  617. width: calc(750rpx - 680rpx);
  618. text-align: center;
  619. font-size: 20rpx;
  620. font-weight: 700;
  621. color: #8e8f97;
  622. z-index: 99;
  623. .item {
  624. height: 15px;
  625. line-height: 15px;
  626. }
  627. .active {
  628. color: #222;
  629. }
  630. }
  631. .has-no-data {
  632. font-size: 24rpx;
  633. text-align: center;
  634. color: #8e8f97;
  635. margin-top: 50rpx;
  636. }
  637. .nav {
  638. height: 44px;
  639. line-height: 44px;
  640. text-align: center;
  641. font-size: 26rpx;
  642. color: #3C3C3C;
  643. position: relative;
  644. }
  645. .foback {
  646. position: absolute;
  647. width: 15px;
  648. height: 15px;
  649. left: 24rpx;
  650. top: 15px;
  651. }
  652. .dingBg{
  653. display: flex;
  654. align-items: center;
  655. color: #3F90F7;
  656. font-size: 26rpx;
  657. }
  658. .icon{
  659. width: 33rpx;
  660. height: 33rpx;
  661. margin-right: 5rpx;
  662. }
  663. </style>