宜轩到家/服务到家第三版,换个颜色
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.

552 lines
12 KiB

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