home.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. <template>
  2. <view class="container">
  3. <!-- 搜索框区域 -->
  4. <uv-row custom-style="margin: 10px 0px" gutter="10">
  5. <picker @change="bindPickerChange" range-key="name" :value="placeIndex" :range="placeList">
  6. <view class="address">
  7. <view class="address_text">{{ placeList[placeIndex].name }}</view>
  8. <img src="../../static/index/bottom.png" />
  9. </view>
  10. </picker>
  11. <view class="search">
  12. <view class="add">
  13. <image class="img" src="../../static/index/search.png" mode="aspectFit"></image>
  14. </view>
  15. <input class="inp" type="text" v-model="keywords" placeholder="请输入关键字搜索" />
  16. <view class="btnSearch" @click="searchHandler">搜索</view>
  17. </view>
  18. </uv-row>
  19. <!-- 名宿列表区域 -->
  20. <view class="body" v-if="hotelList.length">
  21. <!-- 每一个名宿区域 -->
  22. <view class="item" v-for="item in hotelList" :key="item.id" @click="goPageDetail(item)">
  23. <image class="item-img" :src="item.coverImg" mode="scaleToFill"></image>
  24. <view class="descrition">
  25. <text class="title">{{ item.hotel_name }}</text>
  26. <text class="type">{{ item.hTypeName }}</text>
  27. <text class="distance" v-if="showdDistance">距您直线{{ item.distance }}公里</text>
  28. <view class="detail">
  29. <img class="img" src="../../static/index/hotel.png" />
  30. <view class="price">
  31. <text class="txt1">¥{{ item.min_price }}</text>
  32. <text class="txt2">起</text>
  33. </view>
  34. </view>
  35. </view>
  36. </view>
  37. </view>
  38. <!-- 没有数据时展示的页面 -->
  39. <view class="noData" v-else>
  40. <img src="../../static/images/noData.png" />
  41. 暂无数据
  42. </view>
  43. <!-- 领取优惠券弹窗区域 -->
  44. <uv-popup ref="popup" mode="center" :closeOnClickOverlay="false" bgColor="none">
  45. <view class="popupClass">
  46. <img src="../../static/index/popup_bg.png" />
  47. <!-- 立即前往区域 -->
  48. <view class="btn_go" @click="handleGo"></view>
  49. <!-- 关闭按钮区域 -->
  50. <view class="btn_close" @click="handleClose"></view>
  51. </view>
  52. </uv-popup>
  53. </view>
  54. </template>
  55. <script>
  56. export default {
  57. data() {
  58. return {
  59. // 是否显示距离差
  60. showdDistance: false,
  61. // 搜索框绑定数据
  62. keywords: '',
  63. // 民宿列表数组
  64. hotelList: [],
  65. // 地区数组
  66. placeList: [
  67. {
  68. name: '靖安县'
  69. }
  70. ],
  71. // 当前选择地区索引
  72. placeIndex: 0,
  73. // 当前页
  74. page: 1,
  75. // 每页多少条
  76. rows: 6,
  77. // 总条数
  78. total: null,
  79. // 用户定位经度
  80. myLng: 0,
  81. // 用户定位纬度
  82. myLat: 0
  83. }
  84. },
  85. onLoad() {
  86. this.getLocation()
  87. this.getTownList()
  88. // this.$refs.popup.open()
  89. },
  90. // 页面下拉回调
  91. onPullDownRefresh() {
  92. setTimeout(() => {
  93. this.hotelList = []
  94. this.page = 1
  95. this.getLocation()
  96. uni.stopPullDownRefresh()
  97. }, 2000)
  98. },
  99. // 页面下拉到底部回调
  100. onReachBottom() {
  101. if (this.hotelList.length < this.total) {
  102. this.page++
  103. this.getHotelList()
  104. } else {
  105. uni.showToast({
  106. title: '没有更多数据了',
  107. icon: 'none'
  108. })
  109. }
  110. },
  111. methods: {
  112. // 获取乡镇集合
  113. async getTownList() {
  114. const res = await this.$myRequest({
  115. url: '/mhotel/hotelqueryList.action',
  116. method: 'get',
  117. data: {
  118. code: 10
  119. }
  120. })
  121. // console.log(res)
  122. if (res.code === 200) {
  123. this.placeList = [...this.placeList, ...res.data]
  124. }
  125. },
  126. // 获取用户当前位置
  127. getLocation() {
  128. uni.getSetting({
  129. success: (res) => {
  130. if (!res.authSetting['scope.userLocation']) {
  131. uni.authorize({
  132. scope: 'scope.userLocation',
  133. success: (res) => {
  134. // 授权成功
  135. uni.getLocation({
  136. type: 'gcj02',
  137. success: (res) => {
  138. this.myLat = res.latitude
  139. this.myLng = res.longitude
  140. this.showdDistance = true
  141. this.getHotelList()
  142. }
  143. })
  144. },
  145. fail: () => {
  146. uni.showModal({
  147. content: '获取定位权限失败将会影响使用部分功能,是否去设置打开?',
  148. confirmText: '确认',
  149. cancelText: '取消',
  150. success: (res) => {
  151. if (res.confirm) {
  152. uni.openSetting({
  153. success: (res) => {
  154. console.log(res)
  155. this.getLocation()
  156. }
  157. })
  158. } else {
  159. this.showdDistance = false
  160. this.getHotelList()
  161. uni.showToast({
  162. title: '获取定位权限失败',
  163. icon: 'none'
  164. })
  165. }
  166. }
  167. })
  168. }
  169. })
  170. } else {
  171. uni.getLocation({
  172. type: 'gcj02',
  173. success: (res) => {
  174. this.myLat = res.latitude
  175. this.myLng = res.longitude
  176. this.showdDistance = true
  177. this.getHotelList()
  178. }
  179. })
  180. }
  181. }
  182. })
  183. },
  184. // 获取民宿列表
  185. async getHotelList() {
  186. const res = await this.$myRequest({
  187. url: '/mhotel/ahphomePage.action',
  188. data: {
  189. queryValue: this.keywords,
  190. page: this.page,
  191. rows: this.rows,
  192. hotel_township: this.placeList[this.placeIndex].id || '',
  193. userId: uni.getStorageSync('userInfo') ? uni.getStorageSync('userInfo').id : ''
  194. }
  195. })
  196. // console.log(res)
  197. if (res.code === 200) {
  198. this.hotelList = [...this.hotelList, ...res.data.pageList]
  199. this.total = res.data.total
  200. // 如果定位成功则获取和民宿之间的距离
  201. if (this.showdDistance && this.hotelList.length) {
  202. this.hotelList.forEach((ele) => {
  203. let lat = ele.hpositionWens.split(',')[0]
  204. let lng = ele.hpositionWens.split(',')[1]
  205. ele.distance = this.calculateDistance(lat, lng)
  206. })
  207. }
  208. }
  209. },
  210. // 计算两个点之间的距离
  211. calculateDistance(lat, lng) {
  212. let centerLat = lat
  213. let centerLng = lng
  214. let red1 = (this.myLat * Math.PI) / 180.0
  215. let red2 = (centerLat * Math.PI) / 180.0
  216. let a = red1 - red2
  217. let b = (this.myLng * Math.PI) / 180.0 - (centerLng * Math.PI) / 180.0
  218. let R = 6378137
  219. let distance = R * 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(red1) * Math.cos(red2) * Math.pow(Math.sin(b / 2), 2)))
  220. let res = (distance / 1000).toFixed(2) * 1
  221. return res
  222. },
  223. // 搜索按钮点击回调
  224. searchHandler() {
  225. this.hotelList = []
  226. this.page = 1
  227. this.getHotelList()
  228. },
  229. // 点击每一个名宿卡片回调
  230. goPageDetail(item) {
  231. uni.navigateTo({
  232. url: `/pages/detail/detail?id=${item.id}&distance=${item.distance}`
  233. })
  234. },
  235. // 选择地区时的回调
  236. bindPickerChange(e) {
  237. this.placeIndex = e.detail.value
  238. this.hotelList = []
  239. this.page = 1
  240. this.getHotelList()
  241. },
  242. // 立即前往按钮回调
  243. handleGo() {
  244. console.log(111)
  245. },
  246. // 优惠券弹窗关闭按钮回调
  247. handleClose() {
  248. this.$refs.popup.close()
  249. }
  250. }
  251. }
  252. </script>
  253. <style lang="scss" scoped>
  254. .container {
  255. display: flex;
  256. flex-direction: column;
  257. width: 750rpx;
  258. min-height: 100vh;
  259. padding: 0 30rpx;
  260. box-sizing: border-box;
  261. background-color: #f7f7f7;
  262. .address {
  263. display: flex;
  264. width: 152rpx;
  265. font-size: 28rpx;
  266. .address_text {
  267. width: 104rpx;
  268. text-align: center;
  269. overflow: hidden;
  270. white-space: nowrap;
  271. text-overflow: ellipsis;
  272. }
  273. img {
  274. width: 48rpx;
  275. height: 48rpx;
  276. }
  277. }
  278. .search {
  279. display: flex;
  280. justify-content: space-between;
  281. align-items: center;
  282. width: 538rpx;
  283. height: 80rpx;
  284. opacity: 1;
  285. border-radius: 70px;
  286. background-color: #fff;
  287. .add {
  288. display: flex;
  289. justify-content: center;
  290. align-items: center;
  291. margin-left: 10rpx;
  292. width: 60rpx;
  293. font-size: 50rpx;
  294. height: 60rpx;
  295. line-height: 60rpx;
  296. color: rgba(30, 125, 251, 1);
  297. .img {
  298. width: 30rpx;
  299. height: 30rpx;
  300. }
  301. }
  302. .inp {
  303. height: 60rpx;
  304. line-height: 60rpx;
  305. flex-grow: 1;
  306. font-size: 28rpx;
  307. }
  308. .btnSearch {
  309. width: 100rpx;
  310. text-align: center;
  311. margin-right: 10rpx;
  312. height: 60rpx;
  313. line-height: 60rpx;
  314. opacity: 1;
  315. font-size: 28rpx;
  316. font-weight: 400;
  317. height: 2rem;
  318. color: #096562;
  319. }
  320. }
  321. .body {
  322. display: flex;
  323. justify-content: space-between;
  324. flex-wrap: wrap;
  325. .item {
  326. width: 335rpx;
  327. box-sizing: border-box;
  328. margin-bottom: 20rpx;
  329. .item-img {
  330. width: 100%;
  331. height: 223rpx;
  332. border-radius: 18rpx 18rpx 0 0;
  333. box-sizing: border-box;
  334. }
  335. .descrition {
  336. display: flex;
  337. flex-direction: column;
  338. width: 100%;
  339. border-radius: 0 0 18rpx 18rpx;
  340. box-sizing: border-box;
  341. background: rgba(255, 255, 255, 1);
  342. margin-top: -10rpx;
  343. .title {
  344. font-size: 28rpx;
  345. font-weight: 600;
  346. padding: 20rpx 20rpx 10rpx;
  347. color: rgba(0, 0, 0, 1);
  348. overflow: hidden;
  349. white-space: nowrap;
  350. text-overflow: ellipsis;
  351. }
  352. .type {
  353. padding: 5rpx 20rpx;
  354. height: 40rpx;
  355. font-size: 24rpx;
  356. color: #a6a6a6;
  357. }
  358. .distance {
  359. padding: 10rpx 20rpx;
  360. font-size: 24rpx;
  361. color: #a6a6a6;
  362. }
  363. .detail {
  364. display: flex;
  365. flex-direction: row;
  366. justify-content: space-between;
  367. align-items: center;
  368. padding: 0 20rpx 20rpx 20rpx;
  369. color: rgba(0, 0, 0, 1);
  370. .img {
  371. width: 40rpx;
  372. height: 40rpx;
  373. }
  374. .price {
  375. .txt1 {
  376. font-size: 36rpx;
  377. font-weight: 600;
  378. color: rgba(255, 87, 51, 1);
  379. }
  380. .txt2 {
  381. font-size: 24rpx;
  382. font-weight: 400;
  383. color: #a6a6a6;
  384. }
  385. }
  386. .score {
  387. font-size: 24rpx;
  388. font-weight: 400;
  389. padding-top: 10rpx;
  390. color: rgba(166, 166, 166, 1);
  391. }
  392. }
  393. }
  394. }
  395. }
  396. .noData {
  397. display: flex;
  398. flex-direction: column;
  399. justify-content: center;
  400. align-items: center;
  401. img {
  402. margin-top: 150rpx;
  403. width: 600rpx;
  404. height: 600rpx;
  405. }
  406. }
  407. .popupClass {
  408. position: relative;
  409. width: 481rpx;
  410. height: 764rpx;
  411. img {
  412. width: 100%;
  413. height: 665rpx;
  414. }
  415. .btn_go {
  416. position: absolute;
  417. top: 415rpx;
  418. left: 40rpx;
  419. width: 396rpx;
  420. height: 76rpx;
  421. border-radius: 43rpx;
  422. }
  423. .btn_close {
  424. position: absolute;
  425. top: 586rpx;
  426. left: 212rpx;
  427. width: 58rpx;
  428. height: 58rpx;
  429. border-radius: 50%;
  430. }
  431. }
  432. }
  433. </style>