chose-city.vue 16 KB

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