chose-city.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  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. }
  218. });
  219. },
  220. getAdress() {
  221. var location = this.lng + ',' + this.lat
  222. uni.request({
  223. url: 'https://restapi.amap.com/v3/place/around', //仅为示例,并非真实接口地址。
  224. data: {
  225. key: '064b6a4a8ade55656edcde2f528876de',
  226. location: location,
  227. types: "190000",
  228. extensions: "all",
  229. radius: 100
  230. },
  231. dataType: "json",
  232. success: (res) => {
  233. console.log(res);
  234. this.cityname = res.data.pois[0].cityname;
  235. var cityCode = res.data.pois[0].adcode
  236. cityCode = cityCode.slice(0, -2)
  237. cityCode = cityCode + '00'
  238. this.cityCode = cityCode
  239. this.citydw.name = this.cityname
  240. this.citydw.citycode = this.cityCode
  241. }
  242. });
  243. },
  244. groupArr(list, field) {
  245. var fieldList = [],
  246. att = [];
  247. list.map((e) => {
  248. fieldList.push(e[field])
  249. })
  250. //数组去重
  251. fieldList = fieldList.filter((e, i, self) => {
  252. return self.indexOf(e) == i
  253. })
  254. for (var j = 0; j < fieldList.length; j++) {
  255. //过滤出匹配到的数据
  256. var arr = list.filter((e) => {
  257. return e[field] == fieldList[j];
  258. })
  259. att.push({
  260. nameType: arr[0].nameType,
  261. list: arr
  262. })
  263. }
  264. return att;
  265. },
  266. selectCity(item, type) {
  267. if (type === 'refresh' && !this.hasLocation) {
  268. // 获取定位
  269. return this.getLocation()
  270. }
  271. // console.log('选择的城市:', item);
  272. uni.setStorageSync('myCityObj', item)
  273. this.$emit('selectCity', item)
  274. // 当前项目是需要选择到区域,所以选择城市后回到区县的地方
  275. this.toIndex = 'area';
  276. setTimeout(() => {
  277. this.toIndex = '';
  278. }, 1000);
  279. },
  280. closeModal() {
  281. this.$emit('closeModal')
  282. },
  283. //列表滚动,和右边字母表对应
  284. scrollHandle(e) {
  285. let view = uni.createSelectorQuery().in(this).selectAll('.list-item');
  286. view
  287. .boundingClientRect((d) => {
  288. let top = d[0].top;
  289. d.forEach((item) => {
  290. item.top = item.top - top;
  291. item.bottom = item.bottom - top;
  292. this.letterDetails.push({
  293. id: item.id,
  294. top: item.top,
  295. bottom: item.bottom,
  296. });
  297. });
  298. })
  299. .exec();
  300. const scrollTop = e.detail.scrollTop;
  301. this.letterDetails.some((item) => {
  302. if (scrollTop >= item.top && scrollTop <= item.bottom - 20) {
  303. this.currentLetter = item.id;
  304. //当前固定用的是粘性定位,如果不用粘性定位,在这里设置
  305. return true;
  306. }
  307. });
  308. },
  309. //搜索
  310. searchChange(e) {
  311. let {
  312. value
  313. } = e.detail;
  314. this.inputValue = value;
  315. },
  316. // 触发开始
  317. touchStart(e) {
  318. // console.log(e);
  319. },
  320. //移动时
  321. touchMove(e) {
  322. uni.vibrateShort();
  323. let y = e.touches[0].clientY;
  324. let offsettop = e.currentTarget.offsetTop;
  325. //判断选择区域,只在选择区才会生效
  326. if (y > offsettop) {
  327. let num = parseInt((y - offsettop) / 15); //右边每个字母元素的高度
  328. let letter = this.alphabet[num];
  329. this.tipsLetter = letter;
  330. let curentLetter = this.letterTransform(letter);
  331. uni.showToast({
  332. title: curentLetter,
  333. icon: 'none',
  334. });
  335. }
  336. },
  337. //触发结束
  338. touchEnd() {
  339. this.toIndex = this.tipsLetter;
  340. },
  341. //移动开始获取字母,并放大提示
  342. getLetter(e) {
  343. uni.vibrateShort();
  344. let {
  345. id
  346. } = e.currentTarget;
  347. this.tipsLetter = id;
  348. let curentLetter = this.letterTransform(id);
  349. uni.showToast({
  350. title: curentLetter,
  351. icon: 'none',
  352. });
  353. },
  354. //移动结束设置字母,赋值到toIndex
  355. setLetter() {
  356. this.toIndex = this.tipsLetter;
  357. },
  358. //提示字母转换
  359. letterTransform(letter) {
  360. let str = '';
  361. if (letter == 'area') {
  362. str = '当前';
  363. } else if (letter == 'record') {
  364. str = '历史';
  365. } else {
  366. str = letter;
  367. }
  368. return str;
  369. },
  370. },
  371. }
  372. </script>
  373. <style lang="less" scoped>
  374. .chose-city {
  375. position: fixed;
  376. top: 0;
  377. left: 0;
  378. right: 0;
  379. bottom: 0;
  380. z-index: 999;
  381. background: #fff;
  382. }
  383. .city-search-wrap {
  384. width: 100%;
  385. box-sizing: border-box;
  386. .search {
  387. width: 750rpx;
  388. height: 110rpx;
  389. display: flex;
  390. align-items: center;
  391. font-size: 28rpx;
  392. color: #222;
  393. padding: 14rpx 36rpx;
  394. box-sizing: border-box;
  395. background: #fff;
  396. .l-search {
  397. width: 597rpx;
  398. position: relative;
  399. height: 72rpx;
  400. line-height: 72rpx;
  401. .icon-search {
  402. font-size: 28rpx;
  403. position: absolute;
  404. left: 0rpx;
  405. top: 0;
  406. color: #8e8f97;
  407. font-weight: 700;
  408. height: 72rpx;
  409. line-height: 72rpx;
  410. }
  411. .cuIcon-search {
  412. width: 40rpx;
  413. height: 40rpx;
  414. padding: 20rpx;
  415. }
  416. .input-search {
  417. width: 597rpx;
  418. height: 72rpx;
  419. box-sizing: border-box;
  420. padding: 0 84rpx 0 84rpx;
  421. text-align: left;
  422. background: #f4f5f9;
  423. border-radius: 12rpx;
  424. border: 0;
  425. }
  426. .clear-input {
  427. font-size: 30rpx;
  428. position: absolute;
  429. right: 10rpx;
  430. top: 50%;
  431. transform: translateY(-50%);
  432. padding: 10rpx;
  433. color: #8e8f97;
  434. }
  435. }
  436. .r-cancel {
  437. width: 80rpx;
  438. box-sizing: border-box;
  439. padding-left: 24rpx;
  440. font-size: 28rpx;
  441. height: 72rpx;
  442. line-height: 72rpx;
  443. background: transparent;
  444. border: 0;
  445. color: #519AD2;
  446. }
  447. }
  448. }
  449. .reach-content {
  450. padding-left: 36rpx;
  451. box-sizing: border-box;
  452. .li {
  453. width: 714rpx;
  454. font-size: 28rpx;
  455. height: 100rpx;
  456. line-height: 100rpx;
  457. color: #333;
  458. position: relative;
  459. box-sizing: border-box;
  460. border-bottom: 2rpx solid #F5F5F5;
  461. }
  462. }
  463. .block {
  464. // padding: 0 36rpx;
  465. padding-left: 36rpx;
  466. padding-right: 20rpx;
  467. box-sizing: border-box;
  468. }
  469. .top-search {
  470. line-height: 72rpx;
  471. padding: 14rpx 30rpx 0;
  472. box-sizing: border-box;
  473. margin-bottom: 26rpx;
  474. .item {
  475. background: #F5F5F5;
  476. border-radius: 12rpx;
  477. font-size: 28rpx;
  478. text-align: center;
  479. color: #999999;
  480. /* #ifdef MP-ALIPAY */
  481. height: 72rpx;
  482. line-height: 72rpx;
  483. /* #endif */
  484. text {
  485. padding-left: 20rpx;
  486. color: #c1c2cd;
  487. vertical-align: middle;
  488. position: relative;
  489. top: -4rpx;
  490. }
  491. }
  492. }
  493. .scroll-view {
  494. width: 100%;
  495. height: calc(100vh - 110rpx);
  496. box-sizing: border-box;
  497. }
  498. .area {
  499. margin-bottom: 8rpx;
  500. .title-wrapp {
  501. position: sticky;
  502. top: 0;
  503. left: 0;
  504. background: #fff;
  505. }
  506. .c-title {
  507. width: 100%;
  508. box-sizing: border-box;
  509. font-size: 28rpx;
  510. color: #999999;
  511. margin-bottom: 24rpx;
  512. display: inline-flex;
  513. justify-content: space-between;
  514. align-items: center;
  515. .r {
  516. font-size: 24rpx;
  517. color: #8e8f97;
  518. display: inline-block;
  519. align-items: center;
  520. .iconfont {
  521. font-size: 24rpx;
  522. }
  523. }
  524. }
  525. .dingweiCity {
  526. background-color: #F4F5F7;
  527. border-radius: 49rpx;
  528. width: 140rpx;
  529. height: 64rpx;
  530. text-align: center;
  531. line-height: 64rpx;
  532. font-size: 26rpx;
  533. }
  534. .ul {
  535. display: flex;
  536. flex-wrap: wrap;
  537. justify-content: space-between;
  538. .li {
  539. width: 155rpx;
  540. padding: 0 10rpx;
  541. box-sizing: border-box;
  542. height: 72rpx;
  543. line-height: 68rpx;
  544. text-align: center;
  545. font-size: 32rpx;
  546. color: #333;
  547. border-radius: 8rpx;
  548. margin: 0 18rpx 28rpx 0;
  549. // border: 2rpx solid #E2E2E2;
  550. &:nth-child(4n) {
  551. margin-right: 0;
  552. }
  553. &.now {
  554. width: auto;
  555. padding: 0 32rpx 0 22rpx;
  556. .icon {
  557. width: 33rpx;
  558. height: 33rpx;
  559. background-size: 100%;
  560. vertical-align: middle;
  561. position: relative;
  562. top: -4rpx;
  563. }
  564. .text {
  565. padding-left: 10rpx;
  566. color: #3F90F7;
  567. font-size: 26rpx;
  568. }
  569. }
  570. &.active {
  571. font-weight: 500;
  572. background: #ffde45;
  573. }
  574. }
  575. .hover {
  576. background: #ffde45;
  577. }
  578. }
  579. }
  580. .city-list {
  581. width: 750rpx;
  582. padding-bottom: 50rpx;
  583. .c-title {
  584. height: 60rpx;
  585. line-height: 60rpx;
  586. font-size: 30rpx;
  587. font-weight: 500;
  588. color: #272636;
  589. background: #fff;
  590. box-sizing: border-box;
  591. padding-left: 36rpx;
  592. position: sticky;
  593. top: 0;
  594. left: 0;
  595. z-index: 2;
  596. }
  597. .item {
  598. width: 714rpx;
  599. margin-left: 36rpx;
  600. padding: 0 36rpx 0 0;
  601. height: 100rpx;
  602. line-height: 100rpx;
  603. color: #333;
  604. font-size: 28rpx;
  605. box-sizing: border-box;
  606. border-bottom: 2rpx solid #F5F5F5;
  607. }
  608. }
  609. .alphabet {
  610. position: fixed;
  611. right: 0;
  612. bottom: 12%;
  613. width: calc(750rpx - 680rpx);
  614. text-align: center;
  615. font-size: 20rpx;
  616. font-weight: 700;
  617. color: #8e8f97;
  618. z-index: 99;
  619. .item {
  620. height: 15px;
  621. line-height: 15px;
  622. }
  623. .active {
  624. color: #222;
  625. }
  626. }
  627. .has-no-data {
  628. font-size: 24rpx;
  629. text-align: center;
  630. color: #8e8f97;
  631. margin-top: 50rpx;
  632. }
  633. .nav {
  634. height: 44px;
  635. line-height: 44px;
  636. text-align: center;
  637. font-size: 26rpx;
  638. color: #3C3C3C;
  639. position: relative;
  640. }
  641. .foback {
  642. position: absolute;
  643. width: 15px;
  644. height: 15px;
  645. left: 24rpx;
  646. top: 15px;
  647. }
  648. .dingBg{
  649. display: flex;
  650. align-items: center;
  651. color: #3F90F7;
  652. font-size: 26rpx;
  653. }
  654. .icon{
  655. width: 33rpx;
  656. height: 33rpx;
  657. margin-right: 5rpx;
  658. }
  659. </style>