You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

657 lines
14 KiB

8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
  1. <template>
  2. <view class="home">
  3. <view class="home-top">
  4. <view class="search">
  5. <view @click="showSelectArea" class="left-area">
  6. <image src="@/static/home/address-icon.png"></image>
  7. <view class="area">{{ area }}</view>
  8. <image src="../../static/home/arrow-icon.png" mode="aspectFit"></image>
  9. <view class="parting-line">|</view>
  10. </view>
  11. <view class="center-area">
  12. <image src="@/static/home/search-icon.png"></image>
  13. <van-field @click="searchAddress" v-model="queryParams.title" center placeholder="请选择地区" />
  14. </view>
  15. <view class="right-area">
  16. <view @click="searchAddress" class="search-button">
  17. 搜索
  18. </view>
  19. </view>
  20. </view>
  21. </view>
  22. <view class="home-content">
  23. <!-- 轮播图 -->
  24. <view class="banner b-relative">
  25. <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
  26. <van-swipe-item v-for="item in bannerList" :key="item.id">
  27. <!-- <image class="banner-image" :src="item.image" mode="widthFix"></image> -->
  28. <image class="banner-image" src="/static/ms/banner.png" mode="widthFix"></image>
  29. </van-swipe-item>
  30. </van-swipe>
  31. </view>
  32. <!-- tab栏 -->
  33. <view class="tab">
  34. <van-tabs v-model:active="active" color="#EF8C94" background="transparent">
  35. <van-tab title="男士套餐"></van-tab>
  36. <van-tab title="女士套餐"></van-tab>
  37. <!-- 后面这三个用于解决bug -->
  38. <van-tab disabled></van-tab>
  39. <van-tab disabled></van-tab>
  40. <van-tab disabled></van-tab>
  41. </van-tabs>
  42. </view>
  43. <!-- 套餐 -->
  44. <view class="combo">
  45. <view class="combo-top">
  46. <view class="combo-title">女士套餐</view>
  47. <view class="more">
  48. <text>更多</text>
  49. <image src="../../static/home/more.png" mode="widthFix"></image>
  50. </view>
  51. </view>
  52. <view class="combo-list">
  53. <view v-for="(item,index) in projectList" :key="index" class="combo-item">
  54. <image :src="item.image" mode="aspectFill"></image>
  55. <view class="server-name">{{ item.title }}</view>
  56. <view class="time-tag">
  57. <view class="combo-time">
  58. <image src="@/static/home/time-icon.png"></image>
  59. <view>{{ item.times }}分钟</view>
  60. </view>
  61. <view class="tag">限时特价</view>
  62. </view>
  63. </view>
  64. </view>
  65. </view>
  66. <!-- 近期热销 -->
  67. <view v-if="projectList.length > 0" class="server-list">
  68. <view class="server-list-title">
  69. <view class="title">近期热销</view>
  70. <view class="tag">推荐必抢</view>
  71. </view>
  72. <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" ref="list"
  73. @load="onLoad">
  74. <view v-for="item in projectList" class="server-item" @click="toServiceDetail(item.id)">
  75. <view class="img-box">
  76. <image :src="item.image" mode="aspectFill"></image>
  77. </view>
  78. <view class="server-info">
  79. <view class="server-title">{{ item.title }}</view>
  80. <view class="time-coupon">
  81. <image src="@/static/home/time-icon.png"></image>
  82. <view class="time">{{ item.times }}分钟</view>
  83. <!-- <view class="coupon">{{ item.subTitle }}</view> -->
  84. </view>
  85. <view class="price">
  86. <view class="current-price">
  87. <text class="unit"></text>{{ item.price }}
  88. </view>
  89. <view class="original-price">
  90. <text class="unit"></text>{{ item.oldPrice }}
  91. </view>
  92. </view>
  93. <view class="sales-volume">
  94. <image src="@/static/icons/icon1.png"></image>
  95. <view class="desc">已售出{{ item.payNum }}+</view>
  96. </view>
  97. </view>
  98. <view class="selective-technician">
  99. <view @click.stop="selectTechnician(item.id)" class="btn">
  100. 选择技师
  101. </view>
  102. </view>
  103. </view>
  104. </van-list>
  105. </view>
  106. <van-empty v-else image="/static/empty/data.png" image-size="400rpx" description="暂无项目" />
  107. </view>
  108. <selectArea :show="showAeraPro" @close="closeAreaPro" @select="selectArea"></selectArea>
  109. </view>
  110. </template>
  111. <script>
  112. import selectArea from '../../components/selectArea.vue';
  113. import Position from '@/utils/position.js'
  114. export default {
  115. components: {
  116. selectArea
  117. },
  118. data() {
  119. return {
  120. bannerList: [],
  121. projectList: [],
  122. queryParams: {
  123. pageNo: 1,
  124. pageSize: 10,
  125. title: ''
  126. },
  127. loading: false,
  128. finished: false,
  129. technicianList: [],
  130. showAeraPro: false,
  131. area: '',
  132. active : 0
  133. }
  134. },
  135. onShow() {
  136. this.getBanner()
  137. this.getProject()
  138. this.getLocation()
  139. },
  140. methods: {
  141. //list列表滑动到底部自动新增数据列表
  142. onLoad() {
  143. this.queryParams.pageSize += 10;
  144. this.getProject()
  145. },
  146. //获取banner
  147. getBanner() {
  148. this.$api('getBanner', {}, res => {
  149. this.bannerList = res.result;
  150. })
  151. },
  152. //获取项目列表
  153. getProject() {
  154. this.$api('getProjectList', this.queryParams, res => {
  155. if (res.code == 200) {
  156. this.projectList = res.result.records;
  157. } else {
  158. this.finished = true
  159. }
  160. if (this.queryParams.pageSize > res.result.total) {
  161. this.finished = true
  162. }
  163. this.loading = false
  164. })
  165. },
  166. //获取技师详情
  167. selectTechnician(id) {
  168. this.$api('getProjectDetail', {
  169. id
  170. }, res => {
  171. if (res.code == 200) {
  172. uni.navigateTo({
  173. url: `/pages/technician/selectTechnician?serviceId=${id}`
  174. })
  175. sessionStorage.setItem('technicianList', JSON.stringify(res.result.tenPageList))
  176. }
  177. })
  178. },
  179. //跳转技师详情
  180. toServiceDetail(id) {
  181. uni.navigateTo({
  182. url: '/pages/technician/subscribeService?id=' + id
  183. })
  184. },
  185. //显示选择地区
  186. showSelectArea() {
  187. this.showAeraPro = true;
  188. },
  189. //关闭选择地区
  190. closeAreaPro() {
  191. this.showAeraPro = false;
  192. },
  193. //选择了地区信息
  194. selectArea(area) {
  195. this.area = area;
  196. this.showAeraPro = false;
  197. //后端逻辑
  198. this.updateSessionPositon(area)
  199. },
  200. //搜索地址
  201. searchAddress() {
  202. Position.getLocation(res => {
  203. Position.selectAddress(res.longitude, res.latitude, success => {
  204. let address = this.extractProvinceAndCity(success)
  205. this.queryParams.title = address.city
  206. })
  207. })
  208. },
  209. //提取用户选择的地址信息(省市县信息)
  210. extractProvinceAndCity(res) { //提取用户选择的地址信息(省市)
  211. if (!res.address && res.name) { //用户直接选择城市的逻辑
  212. return {
  213. province: '',
  214. city: res.name
  215. };
  216. }
  217. if (res.address) { //用户选择了详细地址,要从详细地址中提取出省市县信息
  218. // 使用正则表达式匹配省市县
  219. const regex = /(?<province>[\u4e00-\u9fa5]+?省)(?<city>[\u4e00-\u9fa5]+?(?:市|自治州|盟|地区))/;
  220. const match = res.address.match(regex);
  221. if (match) { // 如果匹配成功,则返回省和市的信息
  222. return {
  223. province: match.groups.province,
  224. city: match.groups.city
  225. };
  226. }
  227. }
  228. return { //用户没选择地址就点了确定按钮
  229. province: '',
  230. city: ''
  231. }
  232. },
  233. //获取用户详细地址(省市县)
  234. getLocation() {
  235. Position.getLocationDetail().then(res => {
  236. sessionStorage.setItem("position", JSON.stringify(res))
  237. this.area = res.addressDetail.district
  238. })
  239. },
  240. //初始化用户所在地区
  241. initUserArea() {
  242. if (!sessionStorage.getItem('position')) return this.getLocation()
  243. let positionInfo = JSON.parse(sessionStorage.getItem('position'))
  244. this.area = positionInfo.addressDetail.district
  245. },
  246. //更新用户所在区域(更新区县信息)
  247. updateSessionPositon(area) {
  248. if (sessionStorage.getItem('position')) {
  249. let position = JSON.parse(sessionStorage.getItem('position'))
  250. position.addressDetail.district = area
  251. sessionStorage.setItem('position', JSON.stringify(position))
  252. }
  253. }
  254. }
  255. }
  256. </script>
  257. <style lang="scss" scoped>
  258. .home {
  259. width: 750rpx;
  260. background: #F5F5F5;
  261. margin: 0 auto;
  262. .home-top {
  263. height: 350rpx;
  264. padding-top: 60rpx;
  265. .search {
  266. height: 82rpx;
  267. width: 710rpx;
  268. background: #FFFFFF;
  269. margin: 0px auto;
  270. border-radius: 41rpx;
  271. box-sizing: border-box;
  272. padding: 0 15rpx;
  273. display: flex;
  274. align-items: center;
  275. justify-content: space-between;
  276. .left-area,
  277. .center-area {
  278. display: flex;
  279. align-items: center;
  280. }
  281. .left-area {
  282. max-width: 160rpx;
  283. image {
  284. flex-shrink: 0;
  285. width: 26rpx;
  286. height: 26rpx;
  287. }
  288. .area {
  289. font-size: 24rpx;
  290. display: -webkit-box;
  291. -webkit-line-clamp: 2;
  292. /* 限制显示两行 */
  293. -webkit-box-orient: vertical;
  294. overflow: hidden;
  295. text-overflow: ellipsis;
  296. color: #292929;
  297. }
  298. .parting-line {
  299. flex-shrink: 0;
  300. font-size: 26rpx;
  301. color: #ccc;
  302. margin: 0rpx 5rpx;
  303. }
  304. }
  305. .center-area {
  306. display: flex;
  307. flex-wrap: nowrap;
  308. align-items: center;
  309. width: calc(100% - 290rpx);
  310. image {
  311. width: 26rpx;
  312. height: 26rpx;
  313. }
  314. .van-field {
  315. background-color: transparent;
  316. box-sizing: border-box;
  317. height: 82rpx;
  318. line-height: 82rpx;
  319. width: calc(100% - 30rpx);
  320. padding: 0rpx 10rpx 0rpx 0rpx;
  321. input {
  322. height: 82rpx;
  323. font-size: 60rpx;
  324. }
  325. }
  326. }
  327. .right-area {
  328. .search-button {
  329. background: #F6BEC3;
  330. height: 60rpx;
  331. width: 130rpx;
  332. font-size: 26rpx;
  333. border-radius: 35rpx;
  334. color: white;
  335. display: flex;
  336. align-items: center;
  337. justify-content: center;
  338. }
  339. }
  340. }
  341. }
  342. .home-content {
  343. width: calc(100% - 40rpx);
  344. margin: -240rpx 20rpx 0rpx 20rpx;
  345. .banner {
  346. box-sizing: border-box;
  347. .my-swipe {
  348. width: 100%;
  349. margin: 0px auto;
  350. border-radius: 20rpx;
  351. overflow: hidden;
  352. .van-swipe-item {
  353. width: 100%;
  354. .banner-image {
  355. width: 100%;
  356. image {
  357. width: 100%;
  358. }
  359. }
  360. }
  361. }
  362. }
  363. .tab{
  364. margin: 20rpx 0rpx;
  365. }
  366. .combo {
  367. background: #fff;
  368. border-radius: 20rpx;
  369. box-sizing: border-box;
  370. padding: 20rpx;
  371. overflow: hidden;
  372. .combo-top {
  373. display: flex;
  374. align-items: center;
  375. justify-content: space-between;
  376. .combo-title {
  377. font-size: 26rpx;
  378. }
  379. .more {
  380. display: flex;
  381. align-items: center;
  382. font-size: 20rpx;
  383. color: #666666;
  384. image {
  385. width: 10rpx;
  386. margin-left: 10rpx;
  387. }
  388. }
  389. }
  390. .combo-list {
  391. display: flex;
  392. flex-wrap: nowrap;
  393. overflow-x: auto;
  394. margin-top: 30rpx;
  395. padding: 0rpx;
  396. &::-webkit-scrollbar {
  397. width: 0;
  398. height: 0;
  399. }
  400. .combo-item {
  401. width: 190rpx;
  402. flex-shrink: 0;
  403. margin-right: 15rpx;
  404. .server-name{
  405. white-space: nowrap;
  406. overflow: hidden;
  407. text-overflow: ellipsis;
  408. margin: 10rpx 0rpx;
  409. }
  410. .time-tag {
  411. display: flex;
  412. justify-content: space-between;
  413. align-items: center;
  414. .combo-time{
  415. display: flex;
  416. align-items: center;
  417. color: #B8B8B8;
  418. font-size: 20rpx;
  419. }
  420. image {
  421. width: 22rpx;
  422. height: 22rpx;
  423. margin-right: 5rpx;
  424. }
  425. }
  426. .tag{
  427. font-size: 20rpx;
  428. color: #D34430;
  429. }
  430. image {
  431. width: 100%;
  432. height: 190rpx;
  433. border-radius: 10rpx;
  434. }
  435. }
  436. }
  437. }
  438. .server-list {
  439. padding-bottom: 80rpx;
  440. .server-list-title {
  441. display: flex;
  442. margin: 30rpx 0rpx;
  443. .title {
  444. font-size: 30rpx;
  445. font-weight: bold;
  446. }
  447. .tag {
  448. background: #F29E45;
  449. padding: 5rpx 10rpx;
  450. border-radius: 20rpx;
  451. color: white;
  452. font-size: 20rpx;
  453. margin-left: 20rpx;
  454. }
  455. }
  456. .server-item {
  457. display: flex;
  458. flex-wrap: wrap;
  459. justify-content: space-between;
  460. background: white;
  461. border-radius: 15rpx;
  462. box-sizing: border-box;
  463. padding: 15rpx;
  464. margin: 20rpx 0rpx;
  465. .img-box {
  466. width: 150rpx;
  467. height: 150rpx;
  468. border-radius: 10rpx;
  469. overflow: hidden;
  470. image {
  471. width: 100%;
  472. height: 100%;
  473. }
  474. }
  475. .server-info {
  476. display: flex;
  477. flex-direction: column;
  478. justify-content: space-around;
  479. width: calc(100% - 330rpx);
  480. box-sizing: border-box;
  481. padding: 0 10rpx;
  482. .server-title {}
  483. .time-coupon,
  484. .price {
  485. display: flex;
  486. flex-wrap: wrap;
  487. align-items: center;
  488. }
  489. .time-coupon {
  490. font-size: 26rpx;
  491. image {
  492. width: 22rpx;
  493. height: 22rpx;
  494. }
  495. .time {
  496. color: #B8B8B8;
  497. margin-left: 6rpx;
  498. }
  499. .coupon {
  500. display: flex;
  501. justify-content: center;
  502. align-items: center;
  503. background: #F29E45;
  504. color: white;
  505. width: 140rpx;
  506. height: 45rpx;
  507. border-radius: 10rpx;
  508. margin-left: 10rpx;
  509. }
  510. }
  511. .price {
  512. display: flex;
  513. align-items: center;
  514. color: #B8B8B8;
  515. .current-price {
  516. font-size: 30rpx;
  517. font-weight: 600;
  518. color: #D34430;
  519. margin-right: 5rpx;
  520. }
  521. .unit {
  522. font-size: 20rpx;
  523. }
  524. }
  525. .sales-volume {
  526. display: flex;
  527. align-items: center;
  528. color: #B8B8B8;
  529. font-size: 26rpx;
  530. image {
  531. width: 23rpx;
  532. height: 23rpx;
  533. }
  534. }
  535. }
  536. .selective-technician {
  537. display: flex;
  538. flex-wrap: wrap;
  539. align-items: center;
  540. width: 170rpx;
  541. .btn {
  542. display: flex;
  543. align-items: center;
  544. justify-content: center;
  545. height: 60rpx;
  546. width: 170rpx;
  547. border-radius: 40rpx;
  548. color: white;
  549. background: #EF8C94;
  550. }
  551. }
  552. }
  553. }
  554. }
  555. }
  556. </style>