choose-location.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. <template>
  2. <view class="my-address">
  3. <!-- 定位区 -->
  4. <view class="search-box">
  5. <tui-icon @click="handleBack" name="arrowleft" :size="25" color="#00"></tui-icon>
  6. <view class="search-wrapper">
  7. <tui-icon class="search-icon" name="search" :size="20"></tui-icon>
  8. <input v-model="searchCity" type="text" placeholder="请输入所在城市" />
  9. <tui-icon v-if="searchCity" name="close" :size="20" @click="handleClearSearch"></tui-icon>
  10. </view>
  11. <button class="uni-btn" v-if="searchCity" @click="handleSearchCity">搜索</button>
  12. </view>
  13. <view @click="handleGetCurrentAddress" class="current-address">
  14. <text class="current-address-text">
  15. 当前:{{ $store.getters.currentCity ? $store.getters.currentCity : $store.getters.currentShopCity ?
  16. $store.getters.currentShopCity : '定位失败,重新定位' }}
  17. </text>
  18. <view><tui-icon :size="16" color="#000" name="location"></tui-icon>
  19. <text>重新定位</text>
  20. </view>
  21. </view>
  22. <!-- 热门城市 -->
  23. <view class="hot-city">
  24. <view class="title">热门城市</view>
  25. <tui-grid unlined>
  26. <block v-for="(item, index) in hotCities" :key="index">
  27. <tui-grid-item :cell="3" @click="confirmChooseAddress(item, true)">
  28. <text class="tui-grid-label">{{ item.level === 4 ? item.town : item.level === 3 ? item.distinguish : item.city
  29. }}</text>
  30. </tui-grid-item>
  31. </block>
  32. </tui-grid>
  33. </view>
  34. <!-- tabs 标签页 -->
  35. <tui-tabs :tabs="tabs" selectedColor="#e95d20" sliderBgColor="#e95d20" itemWidth="30%" :currentTab="currentTab"
  36. @change="handleChangeTab"></tui-tabs>
  37. <!-- 标签页 -->
  38. <view class="wrapper-container" v-if="cityList.length">
  39. <swiper disable-touch @change="handleChangeSwiper" :current="currentTab" class="swiper">
  40. <swiper-item class="" item-id="">
  41. <view class="address-list-wrapper">
  42. <tui-index-list activeKeyColor="#e95d20" activeColor="#e95d20" activeKeyBackground="#fff"
  43. :list-data="cityList">
  44. <template v-slot:item="{ entity }">
  45. <tui-list-cell padding="16rpx 30rpx" v-for="(item, index) in entity" :key="index">
  46. <view class="tui-list__item"
  47. style="display: flex; align-items: center; justify-content: space-between; padding-right: 40upx;"
  48. @click="handleChooseCity(item)">
  49. <view :id="'item' + item.name" class="tui-name">{{ item.name }}</view>
  50. <view class="button-wrapper" style="display: flex;" v-if="isUnLimit">
  51. <button @click.stop="handleChooseCurrentAddress(item, 'city')" class="uni-btn"
  52. style="padding: 10upx 20upx; background-color: #f3f3f3; color: #3d3d3d;">确定</button>
  53. <button @click.stop="handleChooseCity(item)" class="uni-btn"
  54. style="padding: 10upx 20upx; background-color: #f3f3f3; color: #3d3d3d;">选择区/县</button>
  55. </view>
  56. </view>
  57. </tui-list-cell>
  58. </template>
  59. </tui-index-list>
  60. </view>
  61. </swiper-item>
  62. <swiper-item>
  63. <view class="choose-cities">
  64. <tui-grid unlined v-if="!isUnLimit">
  65. <block v-for="(item, index) in currentDistinguishData" :key="index">
  66. <tui-grid-item class="grid-item" :class="{
  67. active: tabs[1].name === item.name.slice(0, 3) + '...'
  68. }" :cell="3" @click="handleChooseTown(item)">
  69. <text class="tui-grid-label">{{ item.name }}</text>
  70. </tui-grid-item>
  71. </block>
  72. </tui-grid>
  73. <view v-else>
  74. <tui-list-cell padding="16rpx 30rpx" v-for="(item, index) in currentDistinguishData" :key="index">
  75. <view class="tui-list__item"
  76. style="display: flex; align-items: center; justify-content: space-between; padding-right: 40upx;"
  77. @click="handleChooseTown(item)">
  78. <view style="width: 340upx; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;"
  79. :id="'item' + item.name" class="tui-name">{{ item.name }}</view>
  80. <view class="button-wrapper" style="display: flex;" v-if="isUnLimit">
  81. <button @click.stop="handleChooseCurrentAddress(item, 'distinguish')" class="uni-btn"
  82. style="padding: 10upx 20upx; background-color: #f3f3f3; color: #3d3d3d;">确定</button>
  83. <button @click.stop="handleChooseTown(item)" class="uni-btn"
  84. style="padding: 10upx 20upx; background-color: #f3f3f3; color: #3d3d3d;">选镇/街道</button>
  85. </view>
  86. </view>
  87. </tui-list-cell>
  88. </view>
  89. </view>
  90. </swiper-item>
  91. <swiper-item>
  92. <view class="choose-cities">
  93. <tui-grid unlined>
  94. <block v-for="(item, index) in currentTownData" :key="index">
  95. <tui-grid-item :cell="3" @click="confirmChooseAddress(item)">
  96. <text class="tui-grid-label">{{ item.name }}</text>
  97. </tui-grid-item>
  98. </block>
  99. </tui-grid>
  100. </view>
  101. </swiper-item>
  102. </swiper>
  103. </view>
  104. <view class="no-data" v-else> 暂无数据~ </view>
  105. <tui-popup :duration="500" :modeClass="['fade-in']" :styles="styles" :show="showAuthPopupVisible"
  106. @click="showAuthPopupVisible = false">
  107. <view class="address-text">
  108. <tui-icon name="gps" :size="30" color="#e95d20"></tui-icon>
  109. "团蜂"想访问您的地理位置,将根据你的地理位置提供准确的收货地址,社区服务地址,查看附近商家及门店等功能
  110. </view>
  111. </tui-popup>
  112. <tui-toast ref="toast"></tui-toast>
  113. </view>
  114. </template>
  115. <script>
  116. import { T_SELECTED_ADDRESS } from 'constant';
  117. import { hotCities } from './data';
  118. export default {
  119. data() {
  120. return {
  121. currentTab: 0,
  122. cityList: [],
  123. searchValue: '',
  124. isShowLoading: true,
  125. showAuthPopupVisible: false,
  126. isUnLimit: false, // 是否不限制选择层级
  127. styles: {
  128. position: 'fixed',
  129. bottom: 0,
  130. top: 0,
  131. left: 0,
  132. right: 0,
  133. display: 'flex',
  134. 'justify-content': 'center',
  135. 'align-items': 'flex-start',
  136. 'background-color': 'rgba(0, 0, 0, 0.5)',
  137. padding: '50rpx 0 0 0'
  138. },
  139. tabs: [
  140. {
  141. name: '所在城市'
  142. },
  143. {
  144. name: '区/县'
  145. },
  146. {
  147. name: '镇/街道'
  148. }
  149. ],
  150. mainHeight: 0,
  151. currentTab: 0,
  152. currentDistinguishData: null,
  153. currentTownData: null,
  154. searchCity: '',
  155. allCityData: {},
  156. hotCities: Object.freeze(hotCities),
  157. backUrl: null,
  158. eventName: '',
  159. clickNum: 0,
  160. isDoubleClickTimer: null
  161. };
  162. },
  163. methods: {
  164. getData() {
  165. uni.showLoading();
  166. const _this = this;
  167. import('./cities.json').then((res) => {
  168. for (const key in res) {
  169. _this.cityList.push(res[key]);
  170. }
  171. _this.allCityData = Object.freeze(_this.cityList);
  172. _this.isShowLoading = false;
  173. uni.hideLoading();
  174. });
  175. },
  176. changeTab(e) {
  177. this.currentTab = e.index;
  178. },
  179. handleSearchCity() {
  180. this.currentTab = 0;
  181. this.currentDistinguishData = null;
  182. this.currentTownData = null;
  183. let data = JSON.parse(JSON.stringify(this.allCityData));
  184. data = data.filter((item) => {
  185. item.data = item.data && item.data.filter((cities) => cities.name.includes(this.searchCity));
  186. return item.data && item.data.length;
  187. });
  188. this.cityList = data;
  189. },
  190. handleBack(mean) {
  191. if (this.backUrl) {
  192. uni.switchTab({ url: '/' });
  193. return;
  194. }
  195. uni.navigateBack({
  196. delta: 2,
  197. success: mean === 'success' && this.eventName ? uni.$emit(this.eventName) : () => { }
  198. })
  199. },
  200. handleClearSearch() {
  201. this.searchCity = '';
  202. },
  203. handleChooseCity(chooseAddressInfo) {
  204. this.currentDistinguishData = chooseAddressInfo.children;
  205. this.currentTownData = null;
  206. this.currentTab = 1;
  207. this.tabs[0].name = chooseAddressInfo.name.slice(0, 3) + '...';
  208. this.tabs[0].select = chooseAddressInfo.name;
  209. // this.handleBack()
  210. },
  211. // 直接选择当前地址
  212. async handleChooseCurrentAddress(chooseAddressInfo, type) {
  213. const dispatchData = {
  214. city: '',
  215. distinguish: '',
  216. town: ''
  217. }
  218. if (type === 'city') {
  219. dispatchData.city = chooseAddressInfo.name
  220. } else if (type === 'distinguish') {
  221. dispatchData.city = this.tabs[0].select
  222. dispatchData.distinguish = chooseAddressInfo.name
  223. }
  224. try {
  225. uni.showLoading()
  226. await this.$store.dispatch('location/getDetailAddress', dispatchData)
  227. this.ttoast('修改成功');
  228. setTimeout(() => {
  229. if (this.backUrl) {
  230. uni.redirectTo({
  231. url: this.backUrl
  232. });
  233. } else {
  234. this.handleBack('success');
  235. }
  236. }, 1000);
  237. } finally {
  238. uni.hideLoading();
  239. }
  240. },
  241. // 重置timer
  242. resetTimer() {
  243. clearTimeout(this.isDoubleClickTimer)
  244. this.isDoubleClickTimer = null
  245. this.clickNum = 0
  246. },
  247. // 滑动swiper
  248. handleChangeSwiper(e) {
  249. const nextIndex = e.detail.current;
  250. if (nextIndex === 1 && !this.currentDistinguishData) {
  251. this.currentTab = nextIndex - 1;
  252. return;
  253. }
  254. this.currentTab = nextIndex;
  255. },
  256. // 开始定位
  257. handleGetCurrentAddress() {
  258. // #ifdef APP
  259. const appAuthorizeSetting = uni.getAppAuthorizeSetting();
  260. if (appAuthorizeSetting.locationAuthorized !== 'authorized') {
  261. this.showAuthPopupVisible = true;
  262. this.$store.dispatch('location/getCurrentLocation');
  263. } else {
  264. this.$store.dispatch('location/getCurrentLocation');
  265. }
  266. // #endif
  267. // #ifdef H5
  268. this.$store.dispatch('location/getCurrentLocation');
  269. // #endif
  270. },
  271. // 切换tab
  272. handleChangeTab(info) {
  273. if (info.index === 1 && !this.currentDistinguishData) {
  274. this.ttoast({
  275. type: 'fail',
  276. title: '请选择所在城市'
  277. });
  278. return;
  279. }
  280. if (info.index === 2 && !this.currentTownData) {
  281. this.ttoast({
  282. type: 'fail',
  283. title: '请选择所在区县'
  284. });
  285. return;
  286. }
  287. this.currentTab = info.index;
  288. },
  289. // 选择区县
  290. handleChooseTown(data) {
  291. this.currentTownData = data.children;
  292. this.currentTab = 2;
  293. this.tabs[1].name = data.name.slice(0, 3) + '...';
  294. this.tabs[1].select = data.name;
  295. },
  296. // 选择定位
  297. async confirmChooseAddress(data, isHot) {
  298. uni.showLoading();
  299. if (isHot) {
  300. uni.setStorageSync(T_SELECTED_ADDRESS, {
  301. type: 'hot',
  302. data: data
  303. });
  304. await this.$store.dispatch('location/getDetailAddress', data);
  305. } else {
  306. await this.$store.dispatch('location/getDetailAddress', {
  307. city: this.tabs[0].select,
  308. distinguish: this.tabs[1].select,
  309. town: data.name
  310. });
  311. uni.setStorageSync(T_SELECTED_ADDRESS, {
  312. type: 'detail',
  313. data: {
  314. city: this.tabs[0].select,
  315. distinguish: this.tabs[1].select,
  316. town: data.name
  317. }
  318. });
  319. }
  320. uni.hideLoading();
  321. this.ttoast('修改成功');
  322. setTimeout(() => {
  323. if (this.backUrl) {
  324. uni.redirectTo({
  325. url: this.backUrl
  326. });
  327. } else {
  328. this.handleBack('success');
  329. }
  330. }, 1000);
  331. },
  332. },
  333. mounted() {
  334. this.getData();
  335. },
  336. onLoad(params) {
  337. this.eventName = params.eventName || ''
  338. const backUrl = params.backUrl;
  339. this.isUnLimit = params.isUnLimit === 'true'
  340. if (backUrl) {
  341. this.backUrl = backUrl.replaceAll('_', '?').replaceAll('|', '/');
  342. }
  343. console.log(backUrl);
  344. },
  345. watch: {
  346. searchCity(val) {
  347. if (!val) {
  348. this.currentTab = 0;
  349. this.currentDistinguishData = null;
  350. this.currentTownData = null;
  351. this.cityList = this.allCityData;
  352. }
  353. }
  354. }
  355. };
  356. </script>
  357. <style lang="scss" scoped>
  358. .my-address {
  359. width: 100vw;
  360. min-height: 100vh;
  361. background-color: #f3f3f3;
  362. .search-box {
  363. display: flex;
  364. align-items: center;
  365. justify-content: space-between;
  366. width: 100%;
  367. height: 100upx;
  368. padding: 20upx;
  369. box-sizing: border-box;
  370. background-color: #fff;
  371. .search-wrapper {
  372. height: 100%;
  373. background-color: #f3f3f3;
  374. border-radius: 100px;
  375. display: flex;
  376. align-items: center;
  377. flex: 1;
  378. padding: 0 20upx;
  379. input {
  380. flex: 1;
  381. font-size: 24upx;
  382. }
  383. .search-icon {
  384. padding-right: 16upx;
  385. margin-right: 16upx !important;
  386. border-right: 1upx solid #d2d2d2;
  387. }
  388. }
  389. }
  390. .current-address {
  391. display: flex;
  392. align-items: center;
  393. justify-content: space-between;
  394. padding: 20upx;
  395. box-sizing: border-box;
  396. background-color: #fff;
  397. background-color: #fff;
  398. font-size: 24upx;
  399. margin-bottom: 40upx;
  400. .current-address-text {
  401. display: flex;
  402. align-items: center;
  403. width: 300upx;
  404. overflow: hidden;
  405. white-space: nowrap;
  406. text-overflow: ellipsis;
  407. }
  408. view {
  409. display: flex;
  410. align-items: center;
  411. text {
  412. margin-left: 10upx;
  413. }
  414. }
  415. }
  416. .hot-city {
  417. background-color: #fff;
  418. margin-bottom: 30upx;
  419. .title {
  420. width: 100%;
  421. position: relative;
  422. display: flex;
  423. align-items: center;
  424. background: #ffffff;
  425. color: rgb(102, 102, 102);
  426. font-size: 13px;
  427. height: 30px;
  428. padding: 0px 10px;
  429. font-weight: bold;
  430. font-size: 26upx;
  431. }
  432. .city-list {
  433. display: flex;
  434. align-items: center;
  435. flex-wrap: wrap;
  436. padding: 20upx;
  437. box-sizing: border-box;
  438. padding-left: 40upx;
  439. .hot-city-item {
  440. font-size: 24upx;
  441. color: #000;
  442. padding: 10upx 20upx;
  443. border: 1upx solid #adadad;
  444. margin-right: 30upx;
  445. margin-bottom: 20upx;
  446. border-radius: 10upx;
  447. transition: all 350ms;
  448. &:active {
  449. background-color: #e8e8e8;
  450. }
  451. }
  452. }
  453. }
  454. /deep/ .tui-title__item {
  455. background-color: #fff !important;
  456. }
  457. }
  458. .address-text {
  459. display: flex;
  460. align-items: center;
  461. justify-content: center;
  462. width: 600upx;
  463. padding: 26upx;
  464. background-color: #fff;
  465. border-radius: 20upx;
  466. font-size: 32upx;
  467. line-height: 1.5;
  468. /deep/ .tui-icon {
  469. margin-right: 10upx !important;
  470. }
  471. }
  472. .uni-btn {
  473. font-size: 28upx;
  474. margin-left: 10upx;
  475. color: rgb(233, 93, 32);
  476. }
  477. .no-data {
  478. height: 300upx;
  479. text-align: center;
  480. line-height: 400upx;
  481. color: #ccc;
  482. font-size: 28upx;
  483. }
  484. .wrapper-container {
  485. width: 100%;
  486. height: calc(100vh - 292upx);
  487. // background-color: #f40;
  488. overflow: hidden;
  489. .swiper {
  490. height: calc(100vh - 292upx);
  491. }
  492. /deep/ .tui-scroll__view {
  493. height: calc(100vh - 292upx) !important;
  494. }
  495. }
  496. .choose-cities {
  497. width: 100%;
  498. height: 100%;
  499. }
  500. /deep/ .tui-tabs-item {
  501. width: 160rpx;
  502. white-space: nowrap;
  503. overflow: hidden;
  504. text-overflow: ellipsis;
  505. }
  506. /deep/ .tui-grid {
  507. text-align: center;
  508. }
  509. .grid-item.active {
  510. background-color: #e95d20 !important;
  511. color: #fff;
  512. }
  513. .tui-grid-label {
  514. font-size: 28upx;
  515. width: 100px;
  516. overflow: hidden;
  517. text-overflow: ellipsis;
  518. white-space: nowrap;
  519. display: inline-block;
  520. }
  521. </style>