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.

690 lines
18 KiB

1 week ago
  1. <template>
  2. <view class="service-container">
  3. <view class="select-address">
  4. <view class="normal-bolb-text">
  5. 选择区域
  6. </view>
  7. <view class="select-address-value">
  8. <picker mode="multiSelector" @columnchange="columnchange" @change="bindMultiPickerColumnChange" :value="multiIndex"
  9. :range="multiArray">
  10. <view v-if="!isSelected" class="unselected">
  11. <img src="/static/images/details/add.svg" style="width: 20px;height: 20px;" alt="">
  12. <text class="unselected-text">点击选择区域</text>
  13. </view>
  14. <view v-else class="selected">
  15. <view class="selected-text">
  16. <text>{{multiArray[0][multiIndex[0]]}}</text>
  17. <text style="margin-left: 10px;">{{district}}</text>
  18. </view>
  19. <img src="/static/images/details/edit.svg" style="width: 18px;height: 18px;" alt="">
  20. </view>
  21. </picker>
  22. </view>
  23. </view>
  24. <view class="detailed-address">
  25. <view class="normal-bolb-text">
  26. 详细地址
  27. </view>
  28. <view class="detailed-address-value">
  29. <input type="text" placeholder="需具体xx区xx街道xx小区xx室" v-model="detailedAddress">
  30. </view>
  31. </view>
  32. <view class="pet-info">
  33. <view class="normal-bolb-text">
  34. 是否需要增加宠物数量
  35. </view>
  36. <view class="pet-info-tips">
  37. <view class="pet-info-tips-text">
  38. <img src="/static/images/details/tips.svg" style="width: 12px;height: 12px;" alt="">
  39. <text style="margin-left: 5px;">专业喂养套餐最多可服务3只猫或2只小型犬(不含遛狗)</text>
  40. </view>
  41. <view class="pet-info-tips-text">
  42. <img src="/static/images/details/tips.svg" style="width: 12px;height: 12px;" alt="">
  43. <text style="margin-left: 5px;">专业遛狗套餐最多可服务1只中型犬或2只小型犬(含喂养)</text>
  44. </view>
  45. </view>
  46. <view class="pet-info-number">
  47. <view v-for="(pet,index) in petPrices" class="pet-info-for" :key="index">
  48. <view class="normal-text">
  49. {{pet.petTypeName}}
  50. </view>
  51. <view class="pet-price-number">
  52. <view class="pet-price">
  53. <text style="font-size: 16px;">{{pet.price}}</text>/
  54. </view>
  55. <uni-number-box class="pet-number" @change="e=>changeNumber(e,index)" :value="pet.quantity"
  56. :min="0" :max="999" background="#fff">
  57. </uni-number-box>
  58. </view>
  59. <view v-if="index+1<petPrices.length" class="line" style="background-color: #FFE8C6;"></view>
  60. </view>
  61. </view>
  62. </view>
  63. <view class="content-container">
  64. <view class="normal-bolb-text">
  65. 服务频率
  66. </view>
  67. <view class="line" style="background-color: #EFEFEF;"></view>
  68. <view class="service-frequency-value">
  69. <uni-data-checkbox selectedColor="#FFB13F" v-model="selectedFrequency" :localdata="frequency"
  70. @change="changeFrequency"></uni-data-checkbox>
  71. </view>
  72. </view>
  73. <view class="content-container">
  74. <view class="content-header">
  75. <view class="normal-bolb-text">
  76. 预约日期 <text style="color: #999;font-size: 14px;margin-left: 5px;font-weight: normal;">(可选择多日)</text>
  77. </view>
  78. <text style="color: #A94F20;font-size: 14px;">总共选择{{selectedDate.length}}</text>
  79. </view>
  80. <view class="line" style="background-color: #EFEFEF;"></view>
  81. <uni-calendar class="uni-calendar--hook" :selected="selectedDate" :startDate="startDate" :endDate="endDate"
  82. @change="change" :showMonth="false" />
  83. </view>
  84. <view class="content-container">
  85. <view class="normal-bolb-text">
  86. 选择预约时间
  87. </view>
  88. <view class="line" style="background-color: #EFEFEF;"></view>
  89. <view>
  90. <view v-if="selectedFrequency == 'twice_a_day'" style="color: #999;font-size: 14px;margin-bottom:14px;">
  91. 第一次服务时间
  92. </view>
  93. <view class="time-select">
  94. <view v-for="(item,index) in timeList"
  95. :class="['base-time',item.value == serviceTimeFirst?'time-selected':'time-unselected']"
  96. :key="index" @click="selectedTime(item.value)">
  97. <view style="font-size:14px;width: 100%;text-align: center;">{{item.value}}</view>
  98. <!-- <view style="font-size:12px;"
  99. :class="[item.value == serviceTimeFirst?'selected-color':'unselected-color']">
  100. {{item.status}}
  101. </view> -->
  102. </view>
  103. </view>
  104. </view>
  105. <view v-if="selectedFrequency == 'twice_a_day'">
  106. <view style="color: #999;font-size: 14px;margin:14px 0;">
  107. 第二次服务时间
  108. </view>
  109. <view class="time-select">
  110. <view v-for="item in timeList"
  111. :class="['base-time', item.value == serviceTimeFirst? 'time-disable': item.value == serviceTimeSecond? 'time-selected':'time-unselected']"
  112. @click="selectedSecondTime(item.value)" :key="item.value">
  113. <view style="font-size:14px;width: 100%;text-align: center;">{{item.value}}</view>
  114. <!-- <view style="font-size:12px;"
  115. :class="[item.value == serviceTimeFirst? 'disabled-color' : item.value == serviceTimeSecond?'selected-color':'unselected-color']">
  116. {{item.status}}
  117. </view> -->
  118. </view>
  119. </view>
  120. </view>
  121. </view>
  122. <view class="payment">
  123. <view class="total-price">
  124. <text class="total-price-text">应付价格</text>
  125. <text class="total-price-value">{{totalPrice}}</text>
  126. </view>
  127. <button class="payment-btn" @click="goNext">下一步</button>
  128. </view>
  129. <Kefu></Kefu>
  130. </view>
  131. </template>
  132. <script>
  133. import Kefu from '../common/kefu.vue'
  134. import {
  135. getDictList,
  136. getCity
  137. } from '@/api/system/user.js'
  138. export default {
  139. data() {
  140. return {
  141. multiArray: [],
  142. isSelected: false,
  143. cityList:[],
  144. multiIndex: [0, 0],
  145. district: '',
  146. detailedAddress: '',
  147. petPrices: [{
  148. type: 'cat',
  149. petTypeName: '猫',
  150. price: 10,
  151. quantity: 0
  152. },
  153. {
  154. type: 'small_dog',
  155. petTypeName: '小/中型犬',
  156. price: 10,
  157. quantity: 0
  158. },
  159. {
  160. type: 'large_dog',
  161. petTypeName: '大型犬',
  162. price: 20,
  163. quantity: 0
  164. }
  165. ],
  166. frequency: [{
  167. text: '一天一次',
  168. value: 'once_a_day'
  169. }, {
  170. text: '一天两次',
  171. value: 'twice_a_day'
  172. }],
  173. selectedFrequency: 'once_a_day',
  174. basePrice: 0,
  175. currentPrice: 0,
  176. totalPrice: 0,
  177. selectedDate: [],
  178. startDate: '',
  179. endDate: '',
  180. serviceTimeFirst: '',
  181. serviceTimeSecond: '',
  182. timeList: [{
  183. value: '08:00',
  184. status: '空闲',
  185. },
  186. {
  187. value: '09:00',
  188. status: '空闲',
  189. },
  190. {
  191. value: '10:00',
  192. status: '空闲',
  193. }, {
  194. value: '11:00',
  195. status: '空闲',
  196. },
  197. {
  198. value: '12:00',
  199. status: '空闲',
  200. },
  201. {
  202. value: '13:00',
  203. status: '空闲',
  204. },
  205. {
  206. value: '14:00',
  207. status: '空闲',
  208. },
  209. {
  210. value: '15:00',
  211. status: '空闲',
  212. },
  213. {
  214. value: '16:00',
  215. status: '空闲',
  216. }, {
  217. value: '17:00',
  218. status: '空闲',
  219. },
  220. {
  221. value: '18:00',
  222. status: '空闲',
  223. },
  224. {
  225. value: '19:00',
  226. status: '空闲',
  227. },
  228. {
  229. value: '20:00',
  230. status: '空闲',
  231. }
  232. ]
  233. };
  234. },
  235. components: {
  236. Kefu
  237. },
  238. onLoad: function(option) {
  239. uni.setLocale('zh')
  240. this.basePrice = option.price;
  241. this.currentPrice = option.price;
  242. console.log(option.price); //打印出上个页面传递的参数。
  243. },
  244. mounted() {
  245. this.getCalendarDate();
  246. this.getCityList()
  247. },
  248. methods: {
  249. getCityList() {
  250. getCity().then(res => {
  251. if (res.code == 200) {
  252. console.log('city', JSON.parse(res.msg));
  253. this.cityList = JSON.parse(res.msg)
  254. const cityLabels = this.cityList.map(item =>item.city)
  255. this.multiArray[0] = cityLabels
  256. this.multiArray[1] = this.cityList[0].region.map(e=> e.region)
  257. } else {
  258. this.$modal.showToast('获取城市失败,请联系系统管理员!');
  259. }
  260. })
  261. },
  262. columnchange(e){
  263. console.log(e)
  264. // 当滚动切换一级分类时,为当前的一级分类添加它的子类
  265. if (e.detail.column == 0) {
  266. const currentCity = this.cityList[e.detail.value]
  267. // #ifdef H5
  268. // 在小程序中直接赋值无效 H5 可直接赋值
  269. this.multiArray[1] = currentCity.region.map(e=>e.region)
  270. // #endif
  271. // #ifdef MP-WEIXIN
  272. // 在 H5 环境下 $set 会导致一级分类无法滚动, 小程序正常运行
  273. this.$set(this.multiArray, 1, currentCity.region.map(e=>e.region))
  274. // #endif
  275. this.multiIndex=[e.detail.value,0]
  276. }
  277. },
  278. bindMultiPickerColumnChange(e) {
  279. console.log('值为:' + e.detail.value)
  280. this.multiIndex = e.detail.value
  281. this.district = this.multiArray[1][this.multiIndex[1]]
  282. this.isSelected = true
  283. this.$forceUpdate()
  284. },
  285. changeNumber(e, i) {
  286. const outerObj = this.petPrices[i];
  287. outerObj.quantity = e;
  288. console.log(this.petPrices);
  289. this.getTotalPrice()
  290. },
  291. changeFrequency() {
  292. if (this.selectedFrequency === 'twice_a_day') {
  293. this.currentPrice = this.basePrice * 2 - 20;
  294. } else {
  295. this.currentPrice = this.basePrice;
  296. }
  297. this.getTotalPrice()
  298. },
  299. selectedTime(value) {
  300. this.serviceTimeFirst = value;
  301. this.serviceTimeSecond = '';
  302. },
  303. selectedSecondTime(value) {
  304. if (value != this.serviceTimeFirst) {
  305. this.serviceTimeSecond = value;
  306. }
  307. },
  308. getTotalPrice() {
  309. let currentPetPrice = 0
  310. for (let i = 0; i < this.petPrices.length; i++) {
  311. currentPetPrice += this.petPrices[i].quantity * this.petPrices[i].price;
  312. }
  313. let oneDayPrice = +currentPetPrice + +this.currentPrice
  314. this.totalPrice = oneDayPrice * this.selectedDate.length;
  315. },
  316. pdDate() {
  317. let currentDate = new Date();
  318. let year = currentDate.getFullYear();
  319. let month = currentDate.getMonth() + 1 < 10 ? '0' + (currentDate.getMonth() + 1) : currentDate.getMonth() +
  320. 1; // 月份从 0 开始,所以需要加 1
  321. let day = currentDate.getDate() + 1 < 10 ? '0' + (currentDate.getDate() + 1) : currentDate.getDate() + 1;
  322. let hour = currentDate.getHours();
  323. // 将日期格式化为字符串
  324. let nextDate = year + '-' + month + '-' + day;
  325. let currentTime = hour + ':00'
  326. // 将当前日期加一天
  327. if (this.selectedDate.find(item => item.date == nextDate)) {
  328. if ((this.serviceTimeFirst && currentTime > this.serviceTimeFirst) || (this.serviceTimeSecond &&
  329. currentTime > this.serviceTimeSecond)) {
  330. return false
  331. }
  332. }
  333. return true
  334. },
  335. goNext() {
  336. // 选择地址
  337. if (!this.isSelected) {
  338. this.$modal.showToast('请选择地址');
  339. return;
  340. }
  341. //详细地址
  342. if (!this.detailedAddress) {
  343. this.$modal.showToast('请填写详细地址');
  344. return;
  345. }
  346. //预定日期
  347. if (this.selectedDate.length < 1) {
  348. this.$modal.showToast('请至少选择一个预约日期');
  349. return;
  350. }
  351. //服务频次
  352. if (!((this.selectedFrequency == 'once_a_day' && this.serviceTimeFirst) ||
  353. (this.selectedFrequency == 'twice_a_day' && this.serviceTimeFirst && this.serviceTimeSecond))) {
  354. this.$modal.showToast('请选择预约时间');
  355. return;
  356. }
  357. if (!this.pdDate()) {
  358. this.$modal.showToast('首单可以服务的时间需距离当前时间24h以上,请重新选择时间');
  359. return;
  360. }
  361. this.$globalData.submitData.totalPrice = this.totalPrice;
  362. this.$globalData.submitData.address.province = this.multiArray[0][this.multiIndex[0]];
  363. this.$globalData.submitData.address.city = this.multiArray[0][this.multiIndex[0]];
  364. this.$globalData.submitData.address.district = this.district;
  365. this.$globalData.submitData.address.detailAddress = this.detailedAddress;
  366. this.$globalData.submitData.service.serviceFrequency = this.selectedFrequency;
  367. this.$globalData.submitData.service.serviceDate = this.selectedDate.map(item => item.date);
  368. this.$globalData.submitData.service.serviceTimeFirst = this.serviceTimeFirst;
  369. this.$globalData.submitData.service.serviceTimeSecond = this.serviceTimeSecond;
  370. this.$globalData.submitData.service.pet = this.petPrices.map(item => {
  371. if (item.quantity > 0) {
  372. return {
  373. type: item.type,
  374. quantity: item.quantity
  375. }
  376. }
  377. });
  378. this.setItemPrices()
  379. uni.navigateTo({
  380. url: "/pages/details/augmentedProduct"
  381. });
  382. },
  383. setItemPrices() {
  384. let itemPrices = [{
  385. itemType: '基础服务费',
  386. price: this.basePrice,
  387. quantity: this.selectedDate.length,
  388. unit: '次'
  389. }];
  390. for (let i = 0; i < this.petPrices.length; i++) {
  391. if (this.petPrices[i].quantity > 0) {
  392. let itemPrice = {
  393. itemType: '额外宠物' + '-' + this.petPrices[i].petTypeName,
  394. price: this.petPrices[i].price,
  395. quantity: this.petPrices[i].quantity * this.selectedDate.length,
  396. unit: '只'
  397. }
  398. itemPrices.push(itemPrice)
  399. }
  400. }
  401. this.$globalData.servicePrices = itemPrices
  402. },
  403. change(e) {
  404. console.log('change 返回:', e)
  405. // 选中日期
  406. const selectedValue = this.selectedDate.find(item => item.date === e.fulldate)
  407. if (selectedValue) {
  408. // 存在则移除
  409. this.selectedDate = this.selectedDate.filter(item => item.date !== e.fulldate);
  410. } else {
  411. this.selectedDate.push({
  412. date: e.fulldate,
  413. info: '预定'
  414. })
  415. }
  416. this.getTotalPrice();
  417. console.log(this.selectedDate)
  418. },
  419. getCalendarDate() {
  420. let tomorrow = new Date()
  421. tomorrow.setDate(tomorrow.getDate() + 2);
  422. this.startDate = this.formatDate(tomorrow);
  423. // 获取三个月后的日期
  424. let threeMonthsLater = new Date();
  425. threeMonthsLater.setMonth(threeMonthsLater.getMonth() + 3);
  426. this.endDate = this.formatDate(threeMonthsLater);
  427. },
  428. formatDate(date) {
  429. let year = date.getFullYear();
  430. let month = (date.getMonth() + 1).toString().padStart(2, '0');
  431. let day = date.getDate().toString().padStart(2, '0');
  432. return year + '-' + month + '-' + day;
  433. }
  434. },
  435. }
  436. </script>
  437. <style lang="scss">
  438. .service-container {
  439. background-color: #f5f5f7;
  440. .normal-bolb-text {
  441. font-size: 16px;
  442. font-weight: bold;
  443. line-height: 16px;
  444. color: #333;
  445. }
  446. .content-header {
  447. display: flex;
  448. justify-content: space-between;
  449. align-items: center;
  450. }
  451. .normal-text {
  452. font-size: 16px;
  453. color: #333;
  454. }
  455. .line {
  456. height: 1px;
  457. width: 100%;
  458. margin: 16px 0;
  459. }
  460. .select-address {
  461. background-color: #ffffff;
  462. width: 100%;
  463. padding: 14px 20px;
  464. .select-address-value {
  465. margin-top: 15px;
  466. .unselected {
  467. height: 60px;
  468. border-radius: 4px;
  469. border: 1px dashed #FFB13F;
  470. background: #FFFDF7;
  471. display: flex;
  472. justify-content: center;
  473. align-items: center;
  474. .unselected-text {
  475. color: #A94F20;
  476. font-size: 16px;
  477. margin-left: 10px;
  478. }
  479. }
  480. .selected {
  481. display: flex;
  482. justify-content: space-between;
  483. align-items: flex-end;
  484. padding-top: 14px;
  485. border-top: 1px solid #efefef;
  486. .selected-text {
  487. font-size: 16px;
  488. color: #333;
  489. }
  490. }
  491. }
  492. }
  493. .detailed-address {
  494. margin-top: 10px;
  495. background-color: #ffffff;
  496. padding: 14px 20px;
  497. .detailed-address-value {
  498. margin-top: 14px;
  499. border: 1px solid #efefef;
  500. border-radius: 4px;
  501. padding: 10px;
  502. }
  503. }
  504. .pet-info {
  505. margin-top: 10px;
  506. background-color: #ffffff;
  507. padding: 14px 20px;
  508. .pet-info-tips {
  509. display: flex;
  510. align-items: center;
  511. flex-wrap: wrap;
  512. background-color: #FFF1E3;
  513. width: 100%;
  514. height: 46px;
  515. padding: 6px 10px;
  516. margin: 14px 0;
  517. border-radius: 4px;
  518. .pet-info-tips-text {
  519. display: flex;
  520. align-items: center;
  521. color: #A94F20;
  522. font-size: 12px;
  523. line-height: 18px;
  524. width: 100%;
  525. }
  526. }
  527. .pet-info-number {
  528. padding: 16px;
  529. background-color: #FFFCF2;
  530. border: 1px solid #FFE8C6;
  531. border-radius: 4px;
  532. .pet-info-for {
  533. display: flex;
  534. justify-content: space-between;
  535. align-items: center;
  536. flex-wrap: wrap;
  537. .pet-price-number {
  538. display: flex;
  539. align-items: center;
  540. .pet-price {
  541. font-size: 14px;
  542. color: #FF530A;
  543. line-height: 16px;
  544. margin-right: 12px;
  545. }
  546. .pet-number {
  547. border: 1px solid #FFE8C6;
  548. border-radius: 4px;
  549. }
  550. }
  551. }
  552. }
  553. }
  554. .content-container {
  555. margin-top: 10px;
  556. background-color: #ffffff;
  557. padding: 14px 20px;
  558. }
  559. .time-select {
  560. display: flex;
  561. flex-wrap: wrap;
  562. justify-content: flex-start;
  563. .base-time {
  564. width: 25%;
  565. height: 60px;
  566. padding: 12px;
  567. display: flex;
  568. align-items: center;
  569. justify-content: center;
  570. flex-wrap: wrap;
  571. box-sizing: border-box;
  572. margin: -0.5px;
  573. }
  574. .time-unselected {
  575. border: 1px solid #FFE8C6;
  576. background-color: #FFFCF2;
  577. color: #333;
  578. }
  579. .time-selected {
  580. background-color: #FFBF60;
  581. border: 1px solid #FFB13F;
  582. color: #fff
  583. }
  584. .time-disable {
  585. background-color: #eee;
  586. border: 1px solid #FFE8C6;
  587. color: #999;
  588. }
  589. .unselected-color {
  590. color: #6ECD41;
  591. }
  592. .selected-color {
  593. color: #A94F20;
  594. }
  595. .disabled-color {
  596. color: #A1A1A1
  597. }
  598. }
  599. .payment {
  600. height: 56px;
  601. width: 100%;
  602. margin-top: 10px;
  603. padding: 10px 20px;
  604. background-color: #ffffff;
  605. display: flex;
  606. justify-content: space-between;
  607. align-items: center;
  608. .total-price-text {
  609. color: #333;
  610. font-size: 16px;
  611. font-weight: blob;
  612. line-height: 16px;
  613. }
  614. .total-price-value {
  615. color: #FF530A;
  616. font-size: 22px;
  617. font-weight: blob;
  618. line-height: 16px;
  619. }
  620. .payment-btn {
  621. width: 140px;
  622. height: 38px;
  623. border-radius: 6px;
  624. background: #FFB13F;
  625. color: #fff;
  626. font-size: 16px;
  627. margin: 0;
  628. display: flex;
  629. align-items: center;
  630. justify-content: center;
  631. }
  632. }
  633. }
  634. </style>