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.

1759 lines
74 KiB

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="service-new container">
  3. <view style="background-color: #FFF4E5; margin: 10px; padding: 10px; border-radius: 8rpx;">
  4. <view style="display: flex;">
  5. <image style="width: 40rpx; height: 40rpx;" slot='cover'
  6. src="https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/petServiceOrder/OrderIcon.png">
  7. </image>
  8. <text style="color: #A94F20; font-size: 28rpx; font-weight: 500;">
  9. 下单方式
  10. <text v-if="buyInfo.teacher">指定喂养员 - {{ buyInfo.teacher.userName }}</text>
  11. <text v-else>系统派单</text>
  12. </text>
  13. </view>
  14. <view style="margin-top: 10rpx;">
  15. <text v-if="buyInfo.teacher" style="color: #A94F20; font-size: 24rpx; font-weight: 400;">
  16. (若伴宠师1小时内无响应或拒绝接单则系统派单)
  17. </text>
  18. </view>
  19. </view>
  20. <view>
  21. <uni-card padding=0 :is-shadow="false">
  22. <view class="service-new-title" slot="title">
  23. <view class="service-new-title-left">
  24. <view class="service-new-flag"></view>
  25. <view>爱宠选择</view>
  26. </view>
  27. </view>
  28. <view class="split-line"></view>
  29. <view class="service-new-pet-content">
  30. <scroll-view scroll-x class="scroll-view">
  31. <view class="scroll-content">
  32. <view v-for="item in currentPets" :key="item.id" class="service-new-pet-item ellipsis"
  33. :class="{ 'pet-selected': item.id === currentPetId, 'pet-unselect': item.id !== currentPetId }"
  34. @click="selectPet(item)">
  35. {{ item.name }}
  36. </view>
  37. </view>
  38. </scroll-view>
  39. </view>
  40. </uni-card>
  41. </view>
  42. <view>
  43. <uni-card padding=0 :is-shadow="false">
  44. <view class="service-new-title" slot="title">
  45. <view class="service-new-title-left">
  46. <view class="service-new-flag"></view>
  47. <view>服务日期</view>
  48. </view>
  49. </view>
  50. <view class="split-line"></view>
  51. <view class="service-new-pet-content">
  52. <scroll-view scroll-y class="scroll-view-vertical">
  53. <view v-for="(month, monthIndex) in sortedGroupedDates" :key="monthIndex" class="month-group">
  54. <view class="month-title">{{ month.monthLabel }}</view>
  55. <scroll-view scroll-x class="scroll-view-horizontal">
  56. <view class="dates-wrapper">
  57. <view v-for="(day, dayIndex) in month.dates" :key="dayIndex" class="calendar-day"
  58. :class="{ 'day-selected': isDateSelected(day) }" @click="selectDate(day)">
  59. <image :src="isDateSelected(day) ?
  60. 'https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/newOrder/updated.png' :
  61. 'https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/newOrder/update_un.png'" class="calendar-bg" />
  62. <text class="day-text"
  63. :class="{ 'day-highlighted': isCurrentSelected(day), 'day-normal': !isCurrentSelected(day) }">
  64. {{ day.date < 10 ? '0' + day.date : day.date }}</text>
  65. </view>
  66. </view>
  67. </scroll-view>
  68. </view>
  69. </scroll-view>
  70. </view>
  71. </uni-card>
  72. </view>
  73. <view>
  74. <uni-card padding=0 :is-shadow="false">
  75. <view class="service-new-title" slot="title">
  76. <view class="service-new-title-left">
  77. <view class="service-new-flag"></view>
  78. <view>期望时间 <span class="time-slot-time-tip">*请勾选您可以接受的上门时间段</span></view>
  79. </view>
  80. </view>
  81. <view class="split-line"></view>
  82. <view class="service-new-pet-content">
  83. <view class="time-slots">
  84. <view v-for="slot in timeSlots" :key="slot.id" class="time-slot-item"
  85. @click="selectTimeSlot(slot.id)">
  86. <image :src="slot.selected ? slot.activeIcon : slot.inactiveIcon" class="time-slot-icon" />
  87. <view class="time-slot-info">
  88. <text class="time-slot-time">{{ slot.time }}</text>
  89. </view>
  90. </view>
  91. </view>
  92. </view>
  93. </uni-card>
  94. </view>
  95. <view>
  96. <uni-card padding=0 :is-shadow="false">
  97. <view class="service-new-title" slot="title">
  98. <view class="service-new-title-left">
  99. <view class="service-new-flag"></view>
  100. <view>服务选择</view>
  101. </view>
  102. <view style="display: flex; justify-content: flex-end; align-items: center; ">
  103. <view style="margin-right: 10rpx;color: #AAAAAA;">爱宠{{ currentPetId && getCurrentPetName() }}
  104. 全日期保持一致</view>
  105. <u-switch space="2" :value="isAllSame" activeColor="#f9ae3d" size="18" @change="isAllSameChange"
  106. inactiveColor="rgb(230, 230, 230)">
  107. </u-switch>
  108. </view>
  109. </view>
  110. <view class="split-line"></view>
  111. <view class="service-new-pet-content">
  112. <view class="service-item">
  113. <view class="service-item-header">
  114. <image src="https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/newOrder/service.png"
  115. class="service-icon"></image>
  116. <text class="service-title">基础服务</text>
  117. </view>
  118. <view class="service-list">
  119. <!-- <view style="border-radius: 8rpx" :style="{'background-color': needFeed.length > 0 ? '#FFFCF2' : '#F9F9F9', 'border': needFeed.length > 0 ? '1px solid #FFBF60' : 'none'}" >
  120. <view style="padding: 30rpx 0; margin: 0 20rpx; ">
  121. <u-checkbox-group
  122. v-model="needFeed"
  123. iconPlacement="right"
  124. placement="column">
  125. <u-checkbox activeColor="#FFBF60" label="专业喂养" shape="circle"></u-checkbox>
  126. </u-checkbox-group>
  127. </view>
  128. </view> -->
  129. <view class="service-list" style="border-radius: 8rpx;margin-top: 28rpx;">
  130. <view class="service-row" style="background-color: #FFECCD">
  131. <view class="service-row-content"
  132. :style="{ 'border-bottom': feedCount > 1 ? '1rpx solid #ECBFA8' : 'none' }">
  133. <text class="service-name" style="color: #A94F20;">一天多次</text>
  134. <view class="service-price">
  135. <u-number-box slot="right-icon" :value="feedCount" @change="feedCountChange"
  136. step="1" :min="1" :max="3">
  137. <view slot="minus" class="minus">
  138. <u-icon name="minus-circle-fill" size="20" color="#FFBF60"></u-icon>
  139. </view>
  140. <text slot="input" style="width: 40rpx;text-align: center;"
  141. class="input">{{ feedCount }}</text>
  142. <view slot="plus" class="plus">
  143. <u-icon name="plus-circle-fill" color="#FFBF60" size="20"></u-icon>
  144. </view>
  145. </u-number-box>
  146. </view>
  147. </view>
  148. </view>
  149. <view v-if="feedCount > 1" class="service-row" style="background-color: #FFECCD">
  150. <view class="service-row-content">
  151. <text class="service-name" style="color: #A94F20;">额外服务费</text>
  152. <view class="service-price" style="color:#A94F20">
  153. ¥{{ (feedCount > 2 ? (isHoliday(currentMonthDay.fullDate) ? holidayPrice : normalPrice) * price_config.multiService.three.price : (isHoliday(currentMonthDay.fullDate) ? holidayPrice : normalPrice) * price_config.multiService.two.price).toFixed(2) }}
  154. </view>
  155. </view>
  156. </view>
  157. </view>
  158. </view>
  159. </view>
  160. <view class="service-item">
  161. <view class="service-item-header">
  162. <image src="https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/newOrder/love.png"
  163. class="service-icon"></image>
  164. <text class="service-title">定制服务</text>
  165. </view>
  166. <view class="service-list" style="border-radius: 8rpx;">
  167. <view class="service-row" v-for="(item, index) in customServices" :key="item.skuId">
  168. <view class="service-row-content"
  169. :style="{ 'border-bottom': index !== customServices.length - 1 ? '1rpx solid #EFEFEF' : 'none' }">
  170. <text class="service-name" style="">{{ item.name }}</text>
  171. <view class="service-price">
  172. <text
  173. style="font-size: 32rpx; color: #FFBF60; margin-right: 20rpx;">¥{{ item.price }}/</text>
  174. <u-number-box slot="right-icon" :value="item.quantity" :name="item.name"
  175. @change="customServiceCountChange" step="1" :min="0" :max="10">
  176. <view slot="minus" class="minus">
  177. <u-icon name="minus-circle-fill" size="20" color="#FFBF60"></u-icon>
  178. </view>
  179. <text slot="input" style="width: 40rpx;text-align: center;"
  180. class="input">{{ item.quantity }}</text>
  181. <view slot="plus" class="plus">
  182. <u-icon name="plus-circle-fill" color="#FFBF60" size="20"></u-icon>
  183. </view>
  184. </u-number-box>
  185. </view>
  186. </view>
  187. </view>
  188. </view>
  189. </view>
  190. </view>
  191. </uni-card>
  192. </view>
  193. <view v-if="!showPriceDetails" class="details-subscribe">
  194. <view style="display: flex;justify-content: space-between;">
  195. <view style="height: 80rpx;">
  196. <view>
  197. <text style="color: #333333;">订单总价 </text>
  198. <text style="color: #FF530A; font-size: 40rpx;">¥{{ Number(totalPrice).toFixed(2) }} <text
  199. style="font-size: 32rpx;"></text></text>
  200. </view>
  201. <view style="display: flex; justify-content: flex-end;" @click="togglePriceDetails">
  202. <text v-if="currentDayPrice" style="color:#FF530A; font-size: 22rpx;"> 当日价格¥{{ Number(currentDayPrice).toFixed(2)
  203. }}|</text>
  204. <text style="color:#FF530A; font-size: 22rpx;margin-left: 10rpx;">明细</text>
  205. <u-icon name="arrow-up" color="#FF530A" size="10"></u-icon>
  206. </view>
  207. </view>
  208. <view style="display: flex;">
  209. <u-button color="#FFF4E4" customStyle="width: 200rpx; color: #FFAA48; margin-right: 20rpx;"
  210. text="上一步" @click="goBack"></u-button>
  211. <u-button color="#FFBF60" customStyle="width: 200rpx; color: #FFF;" text="确认下单"
  212. @click="goNext"></u-button>
  213. </view>
  214. </view>
  215. </view>
  216. <!-- 遮罩层 -->
  217. <view v-if="showPriceDetails" class="price-details-overlay">
  218. <view class="price-details">
  219. <view class="price-details-header">
  220. <text
  221. style="text-align: center; width: 100%; font-size: 32rpx; font-weight: 500; color: #333333;">价格明细</text>
  222. <u-icon name="close" @click="togglePriceDetails"></u-icon>
  223. </view>
  224. <view style="text-align: center; width: 100%; font-size: 28rpx; color: #AAAAAA; margin: 20rpx 0;">
  225. 实际支付金额以确认下单页为准</view>
  226. <view class="split-line"></view>
  227. <scroll-view class="price-details-body" scroll-y>
  228. <view v-for="(item, index) in priceDetails" :key="index">
  229. <view class="price-details-item">
  230. <view v-if="showName(item)" style="font-size: 28rpx; color: #333333;">{{ item.name }}</view>
  231. <view class="price-details-item-price-total">
  232. <view style="display: flex;" v-for="(item2, index) in item.item" :key="index">
  233. <view style="display: flex; align-items: center;">
  234. <view v-if="item2.date && item2.quantity"
  235. class="price-details-item-price-total-item">{{ item2.date }}</view>
  236. <view v-if="item2.itemName != item.name && item2.quantity"
  237. class="price-details-item-price-total-item">{{ item2.itemName }}</view>
  238. <view v-if="item2.quantity" class="price-details-item-price-total-item">
  239. ¥{{ item2.price }} × {{ item2.quantity }} {{ item2.unit }}</view>
  240. </view>
  241. <view v-if="item2.quantity"
  242. style="color: #333333; margin-left: 20rpx; margin: auto 0; width: 100rpx; text-align: right; font-size: 28rpx;">
  243. ¥{{ item2.price * item2.quantity }}</view>
  244. </view>
  245. </view>
  246. </view>
  247. </view>
  248. </scroll-view>
  249. <view style="position: absolute; bottom: 0; left: 0; right: 0; padding: 20rpx;">
  250. <view class="split-line"></view>
  251. <view class="price-details-item" style="margin: 20rpx 0;">
  252. <view style="font-size: 32rpx; color: #333333; font-weight: 500;">合计</view>
  253. <view style="font-size: 32rpx; color: #333333; font-weight: 500;">¥{{ Number(totalPrice).toFixed(2) }}</view>
  254. </view>
  255. <view class="split-line"></view>
  256. </view>
  257. </view>
  258. <view class="details-subscribe">
  259. <view style="display: flex;justify-content: space-between;">
  260. <view style="height: 80rpx;">
  261. <view>
  262. <text style="color: #333333;">订单总价 </text>
  263. <text style="color: #FF530A; font-size: 40rpx;">¥{{ Number(totalPrice).toFixed(2) }} <text
  264. style="font-size: 32rpx;"></text></text>
  265. </view>
  266. <view style="display: flex; justify-content: flex-end;" @click="togglePriceDetails">
  267. <text v-if="currentDayPrice" style="color:#FF530A; font-size: 22rpx;"> 当日价格¥{{
  268. Number(currentDayPrice).toFixed(2) }}|</text>
  269. <text style="color:#FF530A; font-size: 22rpx;margin-left: 10rpx;">明细</text>
  270. <u-icon name="arrow-up" color="#FF530A" size="10"></u-icon>
  271. </view>
  272. </view>
  273. <view style="display: flex;">
  274. <u-button color="#FFF4E4" customStyle="width: 200rpx; color: #FFAA48; margin-right: 20rpx;"
  275. text="上一步" @click="goBack"></u-button>
  276. <u-button color="#FFBF60" customStyle="width: 200rpx; color: #FFF;" text="确认下单"
  277. @click="goNext"></u-button>
  278. </view>
  279. </view>
  280. </view>
  281. </view>
  282. </view>
  283. </template>
  284. <script>
  285. import {
  286. getProductList,
  287. getOpenId
  288. } from "@/api/system/user"
  289. import { setToken, setOpenIdKey } from '@/utils/auth'
  290. import positionMixin from '../../mixins/position';
  291. export default {
  292. mixins: [positionMixin],
  293. data() {
  294. return {
  295. basePrice: 75,
  296. baseProduct: '专业喂养',
  297. // 节假日价格配置
  298. holidayPrice: 75,
  299. normalPrice: 75,
  300. distancePrice: 1,
  301. holidayDate: [],
  302. currentPets: [],
  303. currentPetId: '',
  304. currentMonthDay: {},
  305. selectedDates: [],
  306. groupedDates: [],
  307. timeSlots: [
  308. {
  309. id: 'MORNING',
  310. name: '早上',
  311. time: '08:00-12:00',
  312. selected: false,
  313. activeIcon: 'https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/newOrder/morning.png',
  314. inactiveIcon: 'https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/newOrder/morning_un.png'
  315. },
  316. {
  317. id: 'AFTERNOON',
  318. name: '午后',
  319. time: '12:00-18:00',
  320. selected: false,
  321. activeIcon: 'https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/newOrder/noon.png',
  322. inactiveIcon: 'https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/newOrder/noon_un.png'
  323. },
  324. {
  325. id: 'EVENING',
  326. name: '晚间',
  327. time: '18:00-22:00',
  328. selected: false,
  329. activeIcon: 'https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/newOrder/night.png',
  330. inactiveIcon: 'https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/newOrder/night_un.png'
  331. }
  332. ],
  333. feedCount: 1,
  334. // customServices原始值
  335. customServicesStr: '',
  336. customServices: [
  337. // { name: '陪玩(20分钟)', price: 20, quantity: 0 },
  338. // { name: '活动区吸毛', price: 20, quantity: 0 },
  339. // { name: '毛发梳理', price: 15, quantity: 0 },
  340. // { name: '身体清洁(如眼、耳、鼻等)', price: 5, quantity: 0 },
  341. // { name: '指甲修剪', price: 10, quantity: 0 },
  342. // { name: '会员深度清洁', price: 20, quantity: 0 },
  343. // { name: '喂药', price: 15, quantity: 0 },
  344. // { name: '上药', price: 25, quantity: 0 }
  345. ],
  346. showPriceDetails: false, // 控制价格明细的显示
  347. priceDetails: [],
  348. totalPrice: 0,
  349. isAllSame: false,
  350. newOrderData: [],
  351. // 提前熟悉费用
  352. needPreFamiliarizeCost: 0,
  353. // 基础服务总费用
  354. baseServiceTotalCost: 0,
  355. // 额外宠物总费用
  356. additionalTotalCost: 0,
  357. // 多次服务总费用
  358. multServicesTotalCost: 0,
  359. // 定制服务总费用
  360. customServicesTotalCost: 0,
  361. // 一天两次日期
  362. oneDayTwoTimesDates: [],
  363. // 一天三次日期
  364. oneDayThreeTimesDates: [],
  365. // 宠物类型次数统计
  366. petTypeCounts: [],
  367. // 定制服务单项总次数统计
  368. customServiceItemCount: [],
  369. // 当日价格
  370. currentDayPrice: 0,
  371. freePetConfig : {
  372. cat: { freeCount: 1 },
  373. smallDog: { freeCount: 0 },
  374. mediumDog: { freeCount: 0 },
  375. largeDog: { freeCount: 0 }
  376. },
  377. }
  378. },
  379. onLoad: function (option) {
  380. this.$globalData.newOrderData.distancePrice = this.calculateDistancePrice()
  381. },
  382. mounted() {
  383. console.log('this.$globalData', this.$globalData)
  384. this.currentPets = this.$globalData.newOrderData.currentPets;
  385. if (this.currentPets.length < 1) {
  386. uni.reLaunch({
  387. url: '/pages/index'
  388. });
  389. return
  390. }
  391. if (this.$globalData.mainSku && this.$globalData.mainSku[0]) {
  392. this.basePrice = this.$globalData.mainSku[0].price
  393. this.baseProduct = this.$globalData.mainSku[0].name
  394. }
  395. // 初始化价格配置
  396. this.initPriceConfig()
  397. this.getProductList()
  398. },
  399. watch: {
  400. currentPetId: {
  401. immediate: true,
  402. handler(newId) {
  403. if (newId) {
  404. this.updateGroupedDates()
  405. }
  406. }
  407. }
  408. },
  409. methods: {
  410. companionLevelPrice(){
  411. let companionLevel = this.$globalData.newOrderData.companionLevel
  412. let price = Number(companionLevel.paramValueText) * this.isAddPrice()
  413. return price * this.$store.state.memberRate
  414. },
  415. //判断当前选中的地址是否加价
  416. isAddPrice(){
  417. let currentAddress = this.$globalData.newOrderData.currentAddress || {}
  418. let defaultPrice = 1
  419. try{
  420. defaultPrice = this.price_config.cityConfig.priceRates.default
  421. }catch(e){
  422. defaultPrice = 1
  423. }
  424. if(!this.price_config.cityConfig || !currentAddress.province || !currentAddress.city){
  425. return defaultPrice
  426. }
  427. let addressList = this.price_config.cityConfig.priceRates || []
  428. for(let key in addressList){
  429. if((currentAddress.province + currentAddress.city).includes(key)){
  430. return addressList[key]
  431. }
  432. }
  433. return defaultPrice
  434. },
  435. //计算距离价格
  436. calculateDistancePrice() {
  437. if(!this.buyInfo.teacher || !this.buyInfo.teacher.appletAddresseList){
  438. return
  439. }
  440. let addressList = this.buyInfo.teacher.appletAddresseList
  441. let d = this.calculateDistanceAddressList(addressList, {
  442. latitude: this.$globalData.newOrderData.latitude,
  443. longitude: this.$globalData.newOrderData.longitude,
  444. })[0]?.distance || 0
  445. return d * this.distancePrice
  446. },
  447. getCustomServicesByOrder(pet, date){
  448. // 获取所有当前可用的定制服务
  449. const currentCustomServices = JSON.parse(this.customServicesStr || '[]')
  450. const customServices = []
  451. // 遍历所有当前定制服务
  452. currentCustomServices.forEach(service => {
  453. let quantity = 0 // 默认数量为0
  454. // 如果存在原订单数据,查找对应的数量
  455. if(this.$globalData.newOrderData.originalOrderData) {
  456. const originalOrderData = this.$globalData.newOrderData.originalOrderData
  457. if(originalOrderData.orderItemList && originalOrderData.orderItemList.length > 0) {
  458. // 查找原订单中对应的定制服务项目
  459. const originalItem = originalOrderData.orderItemList.find(item => {
  460. // 定制服务的categoryId为78,且不是主产品,且skuId匹配
  461. if(item.productCategoryId === 78 && item.isMainProduct !== 1 && item.skuId === service.skuId) {
  462. // 查找对应的服务记录,匹配宠物和日期
  463. const matchingService = originalOrderData.orderServiceList.find(serviceRecord => {
  464. return serviceRecord.id === item.orderServiceId &&
  465. serviceRecord.petId === pet.id &&
  466. serviceRecord.serviceDate === date
  467. })
  468. return !!matchingService
  469. }
  470. return false
  471. })
  472. if(originalItem) {
  473. quantity = originalItem.quantity
  474. }
  475. }
  476. }
  477. // 添加定制服务到结果中
  478. customServices.push({
  479. skuId: service.skuId,
  480. name: service.name,
  481. price: service.price,
  482. quantity: quantity,
  483. isMainProduct: false
  484. })
  485. })
  486. return customServices
  487. },
  488. initNewOrderData() {
  489. const needPreFamiliarize = this.$globalData.newOrderData.needPreFamiliarize.length > 0
  490. const pets = []
  491. // 初始化当前宠物全日期保持一致
  492. this.currentPets = this.currentPets.map(pet => {
  493. pet.isAllSame = false
  494. return pet
  495. })
  496. this.currentPets.map(pet => {
  497. // 单个宠物服务日期列表
  498. const serviceDateList = pet.selectedDate.map(item => item.date)
  499. serviceDateList.forEach(date => {
  500. // 获取原订单中该宠物在该日期的定制服务
  501. const originalCustomServices = this.getCustomServicesByOrder(pet, date)
  502. pets.push({
  503. petId: pet.id,
  504. serviceDate: date,
  505. name: pet.name,
  506. gender: pet.gender,
  507. petType: pet.petType,
  508. bodyType: pet.bodyType,
  509. photo: pet.photo,
  510. feedCount: 1,
  511. selectedTimeSlots: [],
  512. customServices: originalCustomServices,
  513. //额外费用
  514. additionalCost: 0
  515. })
  516. })
  517. })
  518. this.newOrderData = {
  519. needPreFamiliarize: needPreFamiliarize, // 是否需要提前熟悉 40元/次 参与计算
  520. pets: pets
  521. }
  522. // 默认选择第一个宠物
  523. this.selectPet(this.currentPets[0] || {})
  524. this.calculateTotalPrice()
  525. },
  526. // 选择宠物
  527. selectPet(pet) {
  528. this.currentPetId = pet.id
  529. // 默认选择当前宠物的最小日期
  530. if (pet.selectedDate?.length > 1) {
  531. const minDate = pet.selectedDate.reduce((min, date) => {
  532. const currentDate = new Date(date.date);
  533. const minDate = new Date(min.date);
  534. return currentDate < minDate ? date : min;
  535. }, pet.selectedDate[0]);
  536. // 当前选择日期
  537. this.currentMonthDay = {
  538. petId: this.currentPetId,
  539. date: new Date(minDate.date).getDate(),
  540. fullDate: minDate.date
  541. }
  542. } else {
  543. this.currentMonthDay = {
  544. petId: this.currentPetId,
  545. date: new Date(pet.selectedDate[0].date).getDate(),
  546. fullDate: pet.selectedDate[0].date
  547. }
  548. }
  549. this.getCurrentPetIsAllSame()
  550. this.selectDate(this.currentMonthDay)
  551. },
  552. selectDate(day) {
  553. // 当前选择日期
  554. this.currentMonthDay = day
  555. const selectedDay = this.selectedDates?.find(item => item.fullDate === day.fullDate && item
  556. .currentPetId === day.petId)
  557. if (!selectedDay) {
  558. this.selectedDates.push(day)
  559. }
  560. // 还原当前选择日期是否已选择期望时间
  561. const pets = this.newOrderData?.pets
  562. const selectedPet = pets.find(pet => pet.petId === this.currentPetId && pet.serviceDate === this
  563. .currentMonthDay.fullDate);
  564. const selectedTimeSlots = selectedPet ? selectedPet.selectedTimeSlots : [];
  565. if (selectedTimeSlots.length > 0) {
  566. this.timeSlots.forEach(slot => {
  567. if (selectedTimeSlots.includes(slot.id)) {
  568. slot.selected = true
  569. } else {
  570. slot.selected = false
  571. }
  572. })
  573. } else {
  574. this.timeSlots.forEach(slot => {
  575. slot.selected = false
  576. })
  577. }
  578. // 还原当前选择日期喂食次数
  579. this.feedCount = selectedPet ? selectedPet.feedCount : 1
  580. // 还原当前选择日期是否已选择定制服务
  581. this.customServices = selectedPet.customServices.length > 0 ? selectedPet.customServices : JSON.parse(this.customServicesStr)
  582. /*
  583. if (selectedPet && selectedPet.customServices.length > 0) {
  584. // 如果当前宠物已有定制服务(可能来自原订单),直接使用
  585. this.customServices = selectedPet.customServices
  586. } else {
  587. // 否则使用默认的定制服务列表
  588. this.customServices = JSON.parse(this.customServicesStr)
  589. // 如果存在原订单数据,尝试获取该宠物在该日期的定制服务
  590. if (this.$globalData.newOrderData.originalOrderData) {
  591. const currentPet = this.currentPets.find(pet => pet.id === this.currentPetId)
  592. if (currentPet) {
  593. const originalCustomServices = this.getCustomServicesByOrder(currentPet, this.currentMonthDay.fullDate)
  594. if (originalCustomServices.length > 0) {
  595. this.customServices = originalCustomServices
  596. }
  597. }
  598. }
  599. }
  600. */
  601. this.getCurrentDayPrice(this.currentMonthDay.fullDate)
  602. },
  603. isDateSelected(day) {
  604. const selectedDay = this.selectedDates.find(item => item.fullDate === day.fullDate && this.currentPetId ===
  605. item.petId)
  606. if (selectedDay) {
  607. return true
  608. }
  609. return false
  610. },
  611. isCurrentSelected(day) {
  612. return this.currentMonthDay.fullDate === day.fullDate && this.currentPetId === day.petId
  613. },
  614. updateGroupedDates() {
  615. const currentPet = this.currentPets.find(pet => pet.id === this.currentPetId)
  616. if (!currentPet) return
  617. // 按月份分组日期
  618. const grouped = currentPet.selectedDate.reduce((acc, dateObj) => {
  619. const date = new Date(dateObj.date)
  620. const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`
  621. const monthLabel = `${date.getFullYear()}${date.getMonth() + 1}`
  622. if (!acc[monthKey]) {
  623. acc[monthKey] = {
  624. monthLabel,
  625. dates: []
  626. }
  627. }
  628. acc[monthKey].dates.push({
  629. petId: currentPet.id,
  630. date: date.getDate(),
  631. fullDate: dateObj.date
  632. })
  633. return acc
  634. }, {})
  635. // 转换为数组并排序
  636. this.groupedDates = Object.values(grouped).sort((a, b) => {
  637. return a.monthLabel.localeCompare(b.monthLabel)
  638. })
  639. },
  640. selectTimeSlot(slotId) {
  641. const slot = this.timeSlots.find(slot => slot.id === slotId)
  642. if (slot) {
  643. slot.selected = !slot.selected
  644. }
  645. // 将期望时间添加到当前宠物
  646. const selectedTimeSlots = this.timeSlots.filter(slot => slot.selected)
  647. if (this.isAllSame) {
  648. // 如果保持一致,则更新该宠物所有期望时间
  649. this.newOrderData.pets.forEach(pet => {
  650. pet.selectedTimeSlots = selectedTimeSlots.map(item => item.id)
  651. })
  652. } else {
  653. const currentPetIndex = this.newOrderData.pets.findIndex(pet => pet.petId === this.currentPetId
  654. && pet.serviceDate === this.currentMonthDay.fullDate)
  655. if (currentPetIndex !== -1) {
  656. this.newOrderData.pets[currentPetIndex].selectedTimeSlots = selectedTimeSlots.map(item => item.id)
  657. }
  658. }
  659. },
  660. feedCountChange(e) {
  661. this.feedCount = e.value
  662. if (this.isAllSame) {
  663. // 如果保持一致,则更新该宠物的喂食次数
  664. this.newOrderData.pets.forEach(pet => {
  665. if (pet.petId === this.currentPetId) {
  666. pet.feedCount = this.feedCount
  667. }
  668. })
  669. } else {
  670. const pets = this.newOrderData.pets
  671. const currentPetIndex = pets.findIndex(pet => pet.petId === this.currentPetId && pet.serviceDate ===
  672. this.currentMonthDay.fullDate)
  673. if (currentPetIndex !== -1) {
  674. this.newOrderData.pets[currentPetIndex].feedCount = this.feedCount
  675. }
  676. }
  677. this.calculateTotalPrice()
  678. },
  679. customServiceCountChange(val) {
  680. console.log(val)
  681. this.customServices.forEach(item => {
  682. if (item.name === val.name) {
  683. item.quantity = val.value
  684. }
  685. })
  686. if (this.isAllSame) {
  687. // 如果保持一致,则更新当前宠物的定制服务次数
  688. this.newOrderData.pets.forEach(pet => {
  689. if (pet.petId === this.currentPetId) {
  690. pet.customServices = JSON.parse(JSON.stringify(this.customServices))
  691. }
  692. })
  693. } else {
  694. const pets = this.newOrderData.pets
  695. const currentPetIndex = pets.findIndex(pet => pet.petId === this.currentPetId && pet.serviceDate ===
  696. this.currentMonthDay.fullDate)
  697. if (currentPetIndex !== -1) {
  698. this.newOrderData.pets[currentPetIndex].customServices = JSON.parse(JSON.stringify(this
  699. .customServices))
  700. }
  701. }
  702. this.calculateTotalPrice()
  703. },
  704. togglePriceDetails() {
  705. this.showPriceDetails = !this.showPriceDetails; // 切换价格明细的显示状态
  706. if (this.showPriceDetails) {
  707. this.priceDetailDate()
  708. }
  709. },
  710. calculateTotalPrice() {
  711. // 1. 订单维度:是否需要提前熟悉
  712. if (this.$globalData.newOrderData.needPreFamiliarize && this.$globalData.newOrderData.needPreFamiliarize.length > 0) {
  713. this.needPreFamiliarizeCost = this.price_config.preFamiliarize.price
  714. } else {
  715. this.needPreFamiliarizeCost = 0
  716. }
  717. // 2. 日期维度
  718. // 2.1 基础服务费用 - 根据节假日和非节假日分别计算
  719. const uniqueDates = new Set(this.newOrderData.pets.map(item => item.serviceDate)); // 去重日期
  720. let baseServiceTotalCost = 0;
  721. uniqueDates.forEach(date => {
  722. const isHolidayDate = this.isHoliday(date);
  723. const dayPrice = isHolidayDate ? parseFloat(this.holidayPrice) : parseFloat(this.normalPrice);
  724. baseServiceTotalCost += dayPrice;
  725. });
  726. this.baseServiceTotalCost = baseServiceTotalCost;
  727. // 2.2 额外宠物费用
  728. // petType为dog 且bodyType包含 '大型' 则为大型犬,40元/次
  729. // petType为dog 且bodyType包含 '中型' 则为中型犬,30元/次
  730. // petType为dog 且bodyType包含 '小型' 则为小型犬,15元/次
  731. // petType为cat 则为猫,10元/次
  732. this.newOrderData.pets.forEach(pet => {
  733. pet.additionalCost = this.calculatePetCost(pet)
  734. // if (pet.petType === 'dog' && pet.bodyType.includes('大型')) {
  735. // pet.additionalCost = 40
  736. // } else if (pet.petType === 'dog' && pet.bodyType.includes('中型')) {
  737. // pet.additionalCost = 30
  738. // } else if (pet.petType === 'dog' && pet.bodyType.includes('小型')) {
  739. // pet.additionalCost = 15
  740. // } else if (pet.petType === 'cat') {
  741. // pet.additionalCost = 10
  742. // }
  743. })
  744. // const largeDogArray = this.newOrderData.pets.filter(pet => pet.petType === 'dog' && pet.bodyType.includes('大型'));
  745. // this.additionalTotalCost = this.additionalTotalCost + (largeDogArray.length * 40)
  746. // // 非大型犬
  747. // const otherPets = this.newOrderData.pets.filter(pet => pet.petType !== 'dog' && !pet.bodyType.includes('大型'));
  748. // 宠物日期分组
  749. const dailyPets = [];
  750. // 按日期分组宠物
  751. this.newOrderData.pets.forEach(pet => {
  752. const serviceDate = pet.serviceDate;
  753. if (!dailyPets[serviceDate]) {
  754. dailyPets[serviceDate] = [];
  755. }
  756. dailyPets[serviceDate].push(pet);
  757. });
  758. const petTypeCounts = []
  759. let additionalTotalCost = 0
  760. // 计算每日额外宠物费用
  761. for (const date in dailyPets) {
  762. const pets = dailyPets[date];
  763. const largeDogCount = pets.filter(pet => pet.petType === 'dog' && pet.bodyType.includes('大型')).length;
  764. let mediumDogCount = pets.filter(pet => pet.petType === 'dog' && pet.bodyType.includes('中型')).length;
  765. let smallDogCount = pets.filter(pet => pet.petType === 'dog' && pet.bodyType.includes('小型')).length;
  766. let catCount = pets.filter(pet => pet.petType === 'cat').length;
  767. let additionalCost = 0
  768. // 总宠物费用
  769. let totalPetCost = pets.reduce((acc, pet) => {
  770. return acc + this.calculatePetCost(pet);
  771. }, 0);
  772. // 如果总宠物费用>30
  773. // const freeQuota = this.price_config.freeQuota
  774. // if (totalPetCost > Number(freeQuota.threshold)) {
  775. // freeQuota.rules.forEach(rule => {
  776. // if (rule.type === 'cat' && catCount >= rule.count) {
  777. // additionalCost = totalPetCost - Number(rule.freeAmount)
  778. // catCount = catCount - rule.count
  779. // }else if(rule.type === 'smallDog' && smallDogCount >= rule.count){
  780. // additionalCost = totalPetCost - Number(rule.freeAmount)
  781. // smallDogCount = smallDogCount - rule.count
  782. // }else if(rule.type === 'mediumDog' && mediumDogCount >= rule.count){
  783. // additionalCost = totalPetCost - Number(rule.freeAmount)
  784. // mediumDogCount = mediumDogCount - rule.count
  785. // }else{
  786. // additionalCost = totalPetCost - Number(rule.freeAmount)
  787. // }
  788. // })
  789. // // if (totalPetCost > 30) {
  790. // // // 如果猫数量>=3
  791. // // if (catCount >= 3) {
  792. // // additionalCost = totalPetCost - 30
  793. // // catCount = catCount - 3
  794. // // } else if (smallDogCount >= 2) {
  795. // // additionalCost = totalPetCost - 30
  796. // // smallDogCount = smallDogCount - 2
  797. // // } else if (mediumDogCount >= 1) {
  798. // // additionalCost = totalPetCost - 30
  799. // // mediumDogCount = mediumDogCount - 1
  800. // // } else {
  801. // // additionalCost = totalPetCost - 25
  802. // // catCount = catCount - 1
  803. // // smallDogCount = smallDogCount - 1
  804. // // }
  805. // }
  806. petTypeCounts.push({
  807. date,
  808. largeDogCount,
  809. mediumDogCount,
  810. smallDogCount,
  811. catCount
  812. })
  813. // 根据 freePetConfig 配置计算免费宠物数量
  814. const freePetConfig = this.price_config.freePetConfig || this.freePetConfig
  815. // 如果有大型犬,超过免费数量的收费
  816. if (largeDogCount > freePetConfig.largeDog.freeCount) {
  817. const chargeableCount = largeDogCount - freePetConfig.largeDog.freeCount
  818. additionalCost += (this.calculatePetCost({
  819. petType: 'dog',
  820. bodyType: '大型',
  821. }) * chargeableCount)
  822. }
  823. // 如果有中型犬,超过免费数量的收费
  824. if (mediumDogCount > freePetConfig.mediumDog.freeCount) {
  825. const chargeableCount = mediumDogCount - freePetConfig.mediumDog.freeCount
  826. additionalCost += (this.calculatePetCost({
  827. petType: 'dog',
  828. bodyType: '中型',
  829. }) * chargeableCount)
  830. }
  831. // 如果有小型犬,超过免费数量的收费
  832. if (smallDogCount > freePetConfig.smallDog.freeCount) {
  833. const chargeableCount = smallDogCount - freePetConfig.smallDog.freeCount
  834. additionalCost += (this.calculatePetCost({
  835. petType: 'dog',
  836. bodyType: '小型',
  837. }) * chargeableCount)
  838. }
  839. // 如果有猫,超过免费数量的收费
  840. if (catCount > freePetConfig.cat.freeCount) {
  841. const chargeableCount = catCount - freePetConfig.cat.freeCount
  842. additionalCost += (this.calculatePetCost({
  843. petType: 'cat',
  844. bodyType: '小型',
  845. }) * chargeableCount)
  846. }
  847. // 如果有额外费用,添加到总额外费用
  848. if (additionalCost > 0) {
  849. additionalTotalCost += additionalCost;
  850. }
  851. }
  852. this.additionalTotalCost = additionalTotalCost
  853. this.petTypeCounts = petTypeCounts
  854. // 2.3 当日多次服务总价
  855. // 计算每日多次服务次数费用 - 基础价格乘以倍数
  856. let multServicesTotalCost = 0
  857. let oneDayTwoTimesDates = []
  858. let oneDayThreeTimesDates = []
  859. for (const date in dailyPets) {
  860. const maxFeedCount = Math.max(...dailyPets[date].map(pet => pet.feedCount));
  861. const isHolidayDate = this.isHoliday(date);
  862. const dayPrice = isHolidayDate ? parseFloat(this.holidayPrice) : parseFloat(this.normalPrice);
  863. if (maxFeedCount === 2) {
  864. multServicesTotalCost += dayPrice * this.price_config.multiService.two.price; // 基础价格 × 2次倍数
  865. oneDayTwoTimesDates.push(date)
  866. } else if (maxFeedCount === 3) {
  867. multServicesTotalCost += dayPrice * this.price_config.multiService.three.price; // 基础价格 × 3次倍数
  868. oneDayThreeTimesDates.push(date)
  869. }
  870. }
  871. this.oneDayTwoTimesDates = oneDayTwoTimesDates
  872. this.oneDayThreeTimesDates = oneDayThreeTimesDates
  873. this.multServicesTotalCost = multServicesTotalCost
  874. // 3. 定制服务
  875. //定制服务单项总次数统计
  876. const customServiceItemCount = JSON.parse(this.customServicesStr)
  877. let customServicesTotalCost = 0
  878. this.newOrderData.pets.forEach(pet => {
  879. const customServices = pet.customServices
  880. // 定制服务费用
  881. customServices.forEach(service => {
  882. const itemIndex = customServiceItemCount.findIndex(item => item.name === service.name)
  883. if (itemIndex !== -1) {
  884. customServiceItemCount[itemIndex].quantity += service.quantity
  885. }
  886. customServicesTotalCost += service.price * service.quantity;
  887. })
  888. });
  889. this.customServiceItemCount = customServiceItemCount
  890. this.customServicesTotalCost = customServicesTotalCost
  891. // 再来一单费用
  892. const moreOrderPrice = this.$globalData.newOrderData.moreOrderPrice || 0;
  893. this.totalPrice = Number(this.needPreFamiliarizeCost)
  894. + Number(this.baseServiceTotalCost)
  895. + Number(this.additionalTotalCost)
  896. + Number(this.multServicesTotalCost)
  897. + Number(this.customServicesTotalCost)
  898. + Number(moreOrderPrice); // 更新总价
  899. this.getCurrentDayPrice(this.currentMonthDay.fullDate)
  900. },
  901. calculatePetCost(pet) {
  902. // 宠物额外费用 不计算大型犬
  903. // let petExtra = this.price_config.petExtra
  904. // let petCost = 0;
  905. // if (pet.petType === 'cat') {
  906. // petCost += Number(petExtra.cat.price); // 猫额外费用
  907. // } else if (pet.petType === 'dog' && pet.bodyType.includes('小型')) {
  908. // petCost += Number(petExtra.smallDog.price); // 小型犬额外费用
  909. // } else if (pet.petType === 'dog' && pet.bodyType.includes('中型')) {
  910. // petCost += Number(petExtra.mediumDog.price); // 中型犬额外费用
  911. // }
  912. // console.log('calculatePetCost', pet, petCost, petExtra);
  913. let petExtra = this.price_config.petExtra || {}
  914. let petCost = 0;
  915. if (pet.petType === 'cat' && petExtra.cat) {
  916. petCost += Number(petExtra.cat.price || 0); // 猫额外费用
  917. } else if (pet.petType === 'dog' && pet.bodyType.includes('小型') && petExtra.smallDog) {
  918. petCost += Number(petExtra.smallDog.price || 0); // 小型犬额外费用
  919. } else if (pet.petType === 'dog' && pet.bodyType.includes('中型') && petExtra.mediumDog) {
  920. petCost += Number(petExtra.mediumDog.price || 0); // 中型犬额外费用
  921. } else if (pet.petType === 'dog' && pet.bodyType.includes('大型') && petExtra.largeDog) {
  922. petCost += Number(petExtra.largeDog.price || 0); // 大型犬额外费用
  923. }
  924. // console.log('calculatePetCost', pet, petCost, petExtra);
  925. return petCost;
  926. },
  927. goNext() {
  928. console.log('this.$globalData.newOrderData', this.$globalData.newOrderData)
  929. this.calculateTotalPrice(); // 计算总价
  930. this.showPriceDetails = false;
  931. this.$globalData.newOrderData.currentPetsByDay = this.newOrderData.pets
  932. this.$globalData.newOrderData.totalPrice = this.totalPrice
  933. uni.navigateTo({
  934. url: '/pages/newOrder/confirmOrder'
  935. });
  936. },
  937. goBack() {
  938. this.showPriceDetails = false
  939. let len = getCurrentPages().length;
  940. this.loading = false
  941. if (len >= 2) {
  942. uni.navigateBack();
  943. } else {
  944. uni.redirectTo({
  945. url: '/pages/newOrder/serviceNew'
  946. });
  947. }
  948. },
  949. // 价格明细数据
  950. priceDetailDate() {
  951. const priceDetails = []
  952. if (this.newOrderData.needPreFamiliarize) {
  953. priceDetails.push({
  954. name: '提前熟悉',
  955. item: [{
  956. itemName: '提前熟悉',
  957. price: this.price_config.preFamiliarize.price,
  958. quantity: this.newOrderData.needPreFamiliarize ? 1 : 0,
  959. unit: '次'
  960. },]
  961. })
  962. }
  963. // 伴宠师等级价格已合并到基础服务价格中,不再单独显示
  964. // 分别统计节假日和非节假日的天数
  965. const uniqueDates = new Set(this.newOrderData.pets.map(item => item.serviceDate));
  966. let holidayCount = 0;
  967. let normalCount = 0;
  968. uniqueDates.forEach(date => {
  969. if (this.isHoliday(date)) {
  970. holidayCount++;
  971. } else {
  972. normalCount++;
  973. }
  974. });
  975. const baseServiceItems = [];
  976. if (normalCount > 0) {
  977. baseServiceItems.push({
  978. itemName: '非节假日 | ',
  979. price: parseFloat(this.normalPrice),
  980. quantity: normalCount,
  981. unit: '天'
  982. });
  983. }
  984. if (holidayCount > 0) {
  985. baseServiceItems.push({
  986. itemName: '节假日 | ',
  987. price: parseFloat(this.holidayPrice),
  988. quantity: holidayCount,
  989. unit: '天'
  990. });
  991. }
  992. if (baseServiceItems.length > 0) {
  993. priceDetails.push({
  994. name: this.baseProduct,
  995. item: baseServiceItems
  996. });
  997. }
  998. if (this.oneDayTwoTimesDates.length > 0 || this.oneDayThreeTimesDates.length > 0) {
  999. const multiServiceItems = []
  1000. // 计算1天2次的价格明细
  1001. this.oneDayTwoTimesDates.forEach(date => {
  1002. const isHolidayDate = this.isHoliday(date)
  1003. const dayPrice = isHolidayDate ? parseFloat(this.holidayPrice) : parseFloat(this.normalPrice)
  1004. multiServiceItems.push({
  1005. itemName: `1天2次 (${isHolidayDate ? '节假日' : '非节假日'})`,
  1006. price: dayPrice * this.price_config.multiService.two.price,
  1007. quantity: 1,
  1008. unit: '天',
  1009. date: date
  1010. })
  1011. })
  1012. // 计算1天3次的价格明细
  1013. this.oneDayThreeTimesDates.forEach(date => {
  1014. const isHolidayDate = this.isHoliday(date)
  1015. const dayPrice = isHolidayDate ? parseFloat(this.holidayPrice) : parseFloat(this.normalPrice)
  1016. multiServiceItems.push({
  1017. itemName: `1天3次 (${isHolidayDate ? '节假日' : '非节假日'})`,
  1018. price: dayPrice * this.price_config.multiService.three.price,
  1019. quantity: 1,
  1020. unit: '天',
  1021. date: date
  1022. })
  1023. })
  1024. // 按日期排序
  1025. multiServiceItems.sort((a, b) => new Date(a.date) - new Date(b.date))
  1026. priceDetails.push({
  1027. name: '上门次数',
  1028. item: multiServiceItems
  1029. })
  1030. }
  1031. // 过滤宠物类型数量>0的数组
  1032. const additionalCostItem = []
  1033. const petTypeArr = this.petTypeCounts.filter(item => item.largeDogCount > 0 || item.mediumDogCount > 0 ||
  1034. item.smallDogCount > 0 || item.catCount > 0)
  1035. // 假设largeDogCount=1 mediumDogCount=2 smallDogCount=3 catCount=4 则生成4条数据
  1036. petTypeArr.forEach(item => {
  1037. // 获取免费宠物配置
  1038. const freePetConfig = this.price_config.freePetConfig || this.freePetConfig
  1039. // 只显示收费的宠物数量(扣除免费数量)
  1040. const chargeableLargeDogCount = Math.max(0, item.largeDogCount - freePetConfig.largeDog.freeCount)
  1041. const chargeableMediumDogCount = Math.max(0, item.mediumDogCount - freePetConfig.mediumDog.freeCount)
  1042. const chargeableSmallDogCount = Math.max(0, item.smallDogCount - freePetConfig.smallDog.freeCount)
  1043. const chargeableCatCount = Math.max(0, item.catCount - freePetConfig.cat.freeCount)
  1044. if (chargeableLargeDogCount > 0) {
  1045. additionalCostItem.push({
  1046. itemName: '大型犬',
  1047. price: this.calculatePetCost({
  1048. petType: 'dog',
  1049. bodyType: '大型',
  1050. }),
  1051. quantity: chargeableLargeDogCount,
  1052. unit: '只',
  1053. date: item.date
  1054. })
  1055. }
  1056. if (chargeableMediumDogCount > 0) {
  1057. additionalCostItem.push({
  1058. itemName: '中型犬',
  1059. price: this.calculatePetCost({
  1060. petType: 'dog',
  1061. bodyType: '中型',
  1062. }),
  1063. quantity: chargeableMediumDogCount,
  1064. unit: '只',
  1065. date: item.date
  1066. })
  1067. }
  1068. if (chargeableSmallDogCount > 0) {
  1069. additionalCostItem.push({
  1070. itemName: '小型犬',
  1071. price: this.calculatePetCost({
  1072. petType: 'dog',
  1073. bodyType: '小型',
  1074. }),
  1075. quantity: chargeableSmallDogCount,
  1076. unit: '只',
  1077. date: item.date
  1078. })
  1079. }
  1080. if (chargeableCatCount > 0) {
  1081. additionalCostItem.push({
  1082. itemName: '猫猫',
  1083. price: this.calculatePetCost({
  1084. petType: 'cat',
  1085. }),
  1086. quantity: chargeableCatCount,
  1087. unit: '只',
  1088. date: item.date
  1089. })
  1090. }
  1091. })
  1092. // 按日期先后顺序排序
  1093. additionalCostItem.sort((a, b) => new Date(a.date) - new Date(b.date))
  1094. if (additionalCostItem.length > 0) {
  1095. priceDetails.push({
  1096. name: '额外宠物费用',
  1097. item: additionalCostItem
  1098. })
  1099. }
  1100. const customServiceItem = this.customServiceItemCount.filter(item => item.quantity > 0).map(service => {
  1101. return { itemName: service.name, price: service.price, quantity: service.quantity, unit: '次' }
  1102. })
  1103. if (customServiceItem.length > 0) {
  1104. priceDetails.push({
  1105. name: '定制服务',
  1106. item: customServiceItem
  1107. })
  1108. }
  1109. // 再来一单费用
  1110. const moreOrderPrice = this.$globalData.newOrderData.moreOrderPrice || 0;
  1111. if (moreOrderPrice > 0) {
  1112. priceDetails.push({
  1113. name: '再来一单',
  1114. item: [{
  1115. itemName: '再来一单费用',
  1116. price: moreOrderPrice,
  1117. quantity: 1,
  1118. unit: '次'
  1119. }]
  1120. })
  1121. }
  1122. this.priceDetails = priceDetails
  1123. this.calculateTotalPrice()
  1124. },
  1125. showName(item) {
  1126. return item.item.some(item2 => item2.quantity > 0)
  1127. },
  1128. isAllSameChange(e) {
  1129. if (e) {
  1130. this.isAllSame = true
  1131. this.setCurrentPetIsAllSame()
  1132. // 当前宠物全日期保持一致
  1133. this.newOrderData.pets.forEach(pet => {
  1134. if (pet.petId === this.currentPetId) {
  1135. pet.selectedTimeSlots = this.timeSlots.filter(slot => slot.selected).map(item => item.id)
  1136. pet.feedCount = this.feedCount
  1137. pet.customServices = this.customServices
  1138. }
  1139. })
  1140. this.calculateTotalPrice()
  1141. } else {
  1142. this.isAllSame = false
  1143. this.setCurrentPetIsAllSame()
  1144. }
  1145. },
  1146. // 初始化价格配置
  1147. initPriceConfig() {
  1148. let priceConfig = this.$store.state.price_config
  1149. console.log('价格配置:', priceConfig)
  1150. // 获取伴宠师等级价格和城市倍率
  1151. let companionPrice = this.$globalData.newOrderData.companionLevelPrice || 0
  1152. let cityPriceRate = this.$globalData.newOrderData.cityPriceRate || 1
  1153. if(priceConfig.basePrice && priceConfig.basePrice.holiday){
  1154. this.holidayPrice = ((priceConfig.basePrice.holiday * this.$store.state.memberRate * cityPriceRate) + companionPrice).toFixed(2)
  1155. }
  1156. if(priceConfig.basePrice && priceConfig.basePrice.normal){
  1157. this.normalPrice = ((priceConfig.basePrice.normal * this.$store.state.memberRate * cityPriceRate) + companionPrice).toFixed(2)
  1158. }
  1159. if(priceConfig.holidays && priceConfig.holidays.length > 0){
  1160. this.holidayDate = priceConfig.holidays
  1161. }else{
  1162. this.holidayDate = []
  1163. }
  1164. },
  1165. // 判断是否为节假日
  1166. isHoliday(date) {
  1167. return this.holidayDate.some(holiday => holiday === date)
  1168. },
  1169. getProductList() {
  1170. getProductList({
  1171. "publishStatus": 1,
  1172. "categoryId": 78,
  1173. "needSku": true
  1174. }).then(response => {
  1175. if (response && response.content && response.content.length > 0) {
  1176. this.customServices = response.content.map(item => {
  1177. const skus = item.skus
  1178. if (skus && skus.length > 0) {
  1179. let productSku = {
  1180. "skuId": skus[0].id,
  1181. "price": (skus[0].price * this.$store.state.memberRate).toFixed(2),
  1182. "name": item.name,
  1183. "quantity": 0,
  1184. "isMainProduct": false
  1185. }
  1186. return productSku
  1187. }
  1188. })
  1189. this.customServicesStr = JSON.stringify(this.customServices)
  1190. // 初始化新订单数据
  1191. this.initNewOrderData()
  1192. } else {
  1193. uni.showToast('获取主产品失败,请联系管理员')
  1194. }
  1195. console.log(response);
  1196. })
  1197. },
  1198. login() {
  1199. uni.login({
  1200. provider: 'weixin',
  1201. success: (loginRes) => {
  1202. this.getOpenId(loginRes.code)
  1203. },
  1204. fail: function (error) {
  1205. // 授权失败处理
  1206. uni.showToast('授权失败,请授权后再试')
  1207. }
  1208. });
  1209. },
  1210. getOpenId(code) {
  1211. getOpenId(code).then(res => {
  1212. if (res.code == 200 && res.data) {
  1213. let resData = JSON.parse(res.data)
  1214. let token = resData.token;
  1215. let openId = resData.openId;
  1216. setOpenIdKey(openId)
  1217. if (token) {
  1218. setToken(token)
  1219. }
  1220. }
  1221. })
  1222. },
  1223. getCurrentDayPrice(currentDay) {
  1224. const currentDayPets = this.newOrderData.pets.filter(pet => pet.serviceDate === currentDay)
  1225. // 根据是否为节假日确定基础服务价格
  1226. const isHolidayDate = this.isHoliday(currentDay)
  1227. const baseServiceCost = isHolidayDate ? parseFloat(this.holidayPrice) : parseFloat(this.normalPrice)
  1228. const largeDogCount = currentDayPets.filter(pet => pet.petType === 'dog' && pet.bodyType.includes('大型')).length;
  1229. let mediumDogCount = currentDayPets.filter(pet => pet.petType === 'dog' && pet.bodyType.includes('中型')).length;
  1230. let smallDogCount = currentDayPets.filter(pet => pet.petType === 'dog' && pet.bodyType.includes('小型')).length;
  1231. let catCount = currentDayPets.filter(pet => pet.petType === 'cat').length;
  1232. const additionalCostItem = []
  1233. let additionalCost = 0
  1234. // 单天总宠物费用
  1235. let totalPetCost = currentDayPets.reduce((acc, pet) => {
  1236. return acc + this.calculatePetCost(pet);
  1237. }, 0);
  1238. // 根据 freePetConfig 配置计算免费宠物数量
  1239. const freePetConfig = this.price_config.freePetConfig || {
  1240. cat: { freeCount: 1 },
  1241. smallDog: { freeCount: 0 },
  1242. mediumDog: { freeCount: 0 },
  1243. largeDog: { freeCount: 0 }
  1244. }
  1245. // 计算收费的宠物数量(扣除免费数量)
  1246. const chargeableLargeDogCount = Math.max(0, largeDogCount - freePetConfig.largeDog.freeCount)
  1247. const chargeableMediumDogCount = Math.max(0, mediumDogCount - freePetConfig.mediumDog.freeCount)
  1248. const chargeableSmallDogCount = Math.max(0, smallDogCount - freePetConfig.smallDog.freeCount)
  1249. const chargeableCatCount = Math.max(0, catCount - freePetConfig.cat.freeCount)
  1250. // 计算额外宠物费用
  1251. if (chargeableLargeDogCount > 0) {
  1252. additionalCost += (this.calculatePetCost({
  1253. petType: 'dog',
  1254. bodyType: '大型',
  1255. }) * chargeableLargeDogCount)
  1256. additionalCostItem.push({ itemName: '大型犬', price: this.calculatePetCost({
  1257. petType: 'dog',
  1258. bodyType: '大型',
  1259. }), quantity: chargeableLargeDogCount, unit: '只' })
  1260. }
  1261. if (chargeableMediumDogCount > 0) {
  1262. additionalCost += (this.calculatePetCost({
  1263. petType: 'dog',
  1264. bodyType: '中型',
  1265. }) * chargeableMediumDogCount)
  1266. additionalCostItem.push({ itemName: '中型犬', price: this.calculatePetCost({
  1267. petType: 'dog',
  1268. bodyType: '中型',
  1269. }), quantity: chargeableMediumDogCount, unit: '只' })
  1270. }
  1271. if (chargeableSmallDogCount > 0) {
  1272. additionalCost += (this.calculatePetCost({
  1273. petType: 'dog',
  1274. bodyType: '小型',
  1275. }) * chargeableSmallDogCount)
  1276. additionalCostItem.push({ itemName: '小型犬', price: this.calculatePetCost({
  1277. petType: 'dog',
  1278. bodyType: '小型',
  1279. }), quantity: chargeableSmallDogCount, unit: '只' })
  1280. }
  1281. if (chargeableCatCount > 0) {
  1282. additionalCost += (this.calculatePetCost({
  1283. petType: 'cat',
  1284. }) * chargeableCatCount)
  1285. additionalCostItem.push({ itemName: '猫猫', price: this.calculatePetCost({
  1286. petType: 'cat',
  1287. }), quantity: chargeableCatCount, unit: '只' })
  1288. }
  1289. // 当日多次服务次数 - 基础价格乘以倍数
  1290. let multServicesTotalCost = 0
  1291. const maxFeedCount = Math.max(...currentDayPets.map(pet => pet.feedCount));
  1292. if (maxFeedCount === 2) {
  1293. multServicesTotalCost += baseServiceCost * this.price_config.multiService.two.price; // 基础价格 × 2次倍数
  1294. } else if (maxFeedCount === 3) {
  1295. multServicesTotalCost += baseServiceCost * this.price_config.multiService.three.price; // 基础价格 × 3次倍数
  1296. }
  1297. // 所有宠物定制服务费用
  1298. const customServiceCost = currentDayPets.reduce((acc, pet) => acc + this.calculatePetCustomServiceCost(pet), 0)
  1299. const totalCost = baseServiceCost + additionalCost + multServicesTotalCost + customServiceCost
  1300. this.currentDayPrice = totalCost
  1301. },
  1302. calculatePetCustomServiceCost(pet) {
  1303. const customServiceCost = pet.customServices.reduce((acc, item) => acc + item.price * item.quantity, 0)
  1304. return customServiceCost
  1305. },
  1306. getCurrentPetName() {
  1307. const currentPet = this.currentPets.find(pet => pet.id === this.currentPetId)
  1308. return currentPet.name
  1309. },
  1310. setCurrentPetIsAllSame() {
  1311. const currentPet = this.currentPets.find(pet => pet.id === this.currentPetId)
  1312. currentPet.isAllSame = this.isAllSame
  1313. },
  1314. getCurrentPetIsAllSame() {
  1315. const currentPet = this.currentPets.find(pet => pet.id === this.currentPetId)
  1316. this.isAllSame = currentPet.isAllSame
  1317. },
  1318. },
  1319. computed: {
  1320. sortedGroupedDates() {
  1321. return this.groupedDates.sort((a, b) => {
  1322. const dateA = new Date(a.monthLabel.replace('年', '-').replace('月', '-01'))
  1323. const dateB = new Date(b.monthLabel.replace('年', '-').replace('月', '-01'))
  1324. return dateA - dateB
  1325. })
  1326. }
  1327. }
  1328. }
  1329. </script>
  1330. <style scoped lang="less">
  1331. .container {
  1332. position: relative;
  1333. height: 100%;
  1334. padding-bottom: 160rpx;
  1335. .details-subscribe {
  1336. background-color: #FFFFFF;
  1337. padding: 10px;
  1338. width: 100%;
  1339. height: 160rpx;
  1340. position: fixed;
  1341. bottom: 0;
  1342. z-index: 100;
  1343. .details-btn {
  1344. width: 100%;
  1345. border-radius: 6px;
  1346. background: #FFB13F;
  1347. font-size: 16px;
  1348. color: #FFFFFF;
  1349. }
  1350. }
  1351. }
  1352. .service-new {
  1353. .service-new-flag {
  1354. width: 8rpx;
  1355. height: 32rpx;
  1356. background: #FFBF60;
  1357. border-radius: 30rpx 30rpx 30rpx 30rpx;
  1358. margin-right: 10rpx;
  1359. }
  1360. .split-line {
  1361. width: 100%;
  1362. height: 1rpx;
  1363. background: #EFEFEF;
  1364. }
  1365. .service-new-title {
  1366. display: flex;
  1367. font-weight: 500;
  1368. font-size: 28rpx;
  1369. color: #333333;
  1370. line-height: 33rpx;
  1371. margin: 42rpx 0 30rpx;
  1372. justify-content: space-between;
  1373. .service-new-title-left {
  1374. display: flex;
  1375. align-items: center;
  1376. }
  1377. }
  1378. .service-new-pet-content {
  1379. padding: 20rpx 0;
  1380. }
  1381. .service-new-pet-item {
  1382. height: 96rpx;
  1383. width: 184rpx;
  1384. border-radius: 8rpx;
  1385. padding: 0 20rpx;
  1386. margin-right: 40rpx;
  1387. display: flex;
  1388. align-items: center;
  1389. justify-content: center;
  1390. font-size: 28rpx;
  1391. flex-shrink: 0;
  1392. }
  1393. .scroll-content {
  1394. display: inline-flex;
  1395. }
  1396. .scroll-view {
  1397. width: 100%;
  1398. white-space: nowrap;
  1399. }
  1400. .pet-selected {
  1401. background: #FFBF60;
  1402. color: #FFFFFF;
  1403. }
  1404. .pet-unselect {
  1405. background: #F5F5F7;
  1406. color: #7D8196;
  1407. }
  1408. }
  1409. .calendar-wrapper {
  1410. display: flex;
  1411. gap: 20rpx;
  1412. padding: 20rpx;
  1413. }
  1414. .scroll-view {
  1415. width: 100%;
  1416. white-space: nowrap;
  1417. }
  1418. .calendar-day {
  1419. position: relative;
  1420. width: 80rpx;
  1421. height: 80rpx;
  1422. display: flex;
  1423. align-items: center;
  1424. justify-content: center;
  1425. cursor: pointer;
  1426. margin-right: 20rpx;
  1427. flex-shrink: 0;
  1428. }
  1429. .calendar-bg {
  1430. position: absolute;
  1431. width: 88rpx;
  1432. height: 78rpx;
  1433. z-index: 1;
  1434. }
  1435. .day-text {
  1436. position: relative;
  1437. z-index: 2;
  1438. top: 5rpx;
  1439. }
  1440. .day-highlighted {
  1441. font-size: 38rpx;
  1442. font-weight: bold;
  1443. color: #FFBF60;
  1444. }
  1445. .day-normal {
  1446. color: #3E3A39;
  1447. font-size: 32rpx;
  1448. }
  1449. .month-group {
  1450. margin-bottom: 30rpx;
  1451. .month-title {
  1452. font-size: 32rpx;
  1453. color: #666;
  1454. margin-bottom: 20rpx;
  1455. }
  1456. .dates-wrapper {
  1457. display: inline-flex;
  1458. }
  1459. }
  1460. .scroll-view-vertical {
  1461. height: 100%;
  1462. }
  1463. .scroll-view-horizontal {
  1464. width: 100%;
  1465. white-space: nowrap;
  1466. }
  1467. .time-slots {
  1468. display: flex;
  1469. justify-content: space-around;
  1470. }
  1471. .time-slot-item {
  1472. display: flex;
  1473. flex-direction: column;
  1474. align-items: center;
  1475. width: 200rpx;
  1476. cursor: pointer;
  1477. }
  1478. .time-slot-icon {
  1479. width: 160rpx;
  1480. height: 160rpx;
  1481. margin-bottom: 10rpx;
  1482. }
  1483. .time-slot-info {
  1484. display: flex;
  1485. flex-direction: column;
  1486. align-items: center;
  1487. }
  1488. .time-slot-name {
  1489. font-size: 28rpx;
  1490. color: #333;
  1491. margin-bottom: 8rpx;
  1492. }
  1493. .time-slot-time {
  1494. font-size: 24rpx;
  1495. color: #666;
  1496. }
  1497. .time-slot-time-tip {
  1498. background-color: #FFECCD;
  1499. color: #A94F20;
  1500. margin-left: 10rpx;
  1501. padding: 5rpx 10rpx;
  1502. font-size: 20rpx;
  1503. }
  1504. .service-item {
  1505. margin-bottom: 20rpx;
  1506. /* 每个服务项之间的间距 */
  1507. }
  1508. .service-item-header {
  1509. display: flex;
  1510. align-items: center;
  1511. margin-bottom: 10rpx;
  1512. /* 标题与服务列表之间的间距 */
  1513. }
  1514. .service-icon {
  1515. width: 40rpx;
  1516. /* 图标大小 */
  1517. height: 40rpx;
  1518. /* 图标大小 */
  1519. margin-right: 10rpx;
  1520. /* 图标与标题之间的间距 */
  1521. }
  1522. .service-title {
  1523. font-size: 28rpx;
  1524. /* 服务标题字体大小 */
  1525. font-weight: bold;
  1526. /* 服务标题加粗 */
  1527. }
  1528. .service-row {
  1529. display: flex;
  1530. align-items: center;
  1531. /* 垂直居中对齐 */
  1532. flex-wrap: wrap;
  1533. background-color: #F9F9F9;
  1534. }
  1535. .service-row-content {
  1536. display: flex;
  1537. justify-content: space-between;
  1538. /* 每行的上下内边距 */
  1539. width: 100%;
  1540. padding: 32rpx 0rpx;
  1541. margin: 0 20rpx;
  1542. }
  1543. .service-name {
  1544. width: 50%;
  1545. white-space: nowrap;
  1546. overflow: hidden;
  1547. text-overflow: ellipsis;
  1548. font-size: 28rpx;
  1549. color: #333;
  1550. /* 服务名称颜色 */
  1551. }
  1552. .service-price {
  1553. display: flex;
  1554. align-items: center;
  1555. /* 垂直居中对齐 */
  1556. }
  1557. .service-count {
  1558. display: flex;
  1559. align-items: center;
  1560. /* 垂直居中对齐 */
  1561. margin-left: 10rpx;
  1562. /* 价格数量之间的间距 */
  1563. }
  1564. .price-details {
  1565. position: fixed;
  1566. /* 固定位置 */
  1567. bottom: 160rpx;
  1568. /* 根据需要调整位置 */
  1569. left: 0;
  1570. right: 0;
  1571. background: #FFF;
  1572. /* 背景颜色 */
  1573. padding: 10px 10px 0 10px;
  1574. /* 内边距 */
  1575. border-radius: 8rpx 8rpx 0 0;
  1576. /* 圆角 */
  1577. z-index: 1000;
  1578. /* 确保在其他元素之上 */
  1579. min-height: 600rpx;
  1580. /* 固定高度 */
  1581. overflow: hidden;
  1582. /* 隐藏超出部分 */
  1583. }
  1584. .price-details-header {
  1585. display: flex;
  1586. justify-content: space-between;
  1587. align-items: center;
  1588. }
  1589. .price-details-body {
  1590. margin-top: 10px;
  1591. max-height: 300rpx;
  1592. /* 留出头部空间 */
  1593. overflow-y: auto;
  1594. /* 允许上下滚动 */
  1595. }
  1596. .price-details-overlay {
  1597. position: fixed;
  1598. /* 固定位置 */
  1599. top: 0;
  1600. left: 0;
  1601. right: 0;
  1602. bottom: 0;
  1603. background: rgba(0, 0, 0, 0.5);
  1604. /* 半透明背景 */
  1605. z-index: 999;
  1606. /* 确保在其他元素之上 */
  1607. display: flex;
  1608. justify-content: center;
  1609. align-items: center;
  1610. }
  1611. .price-details-item {
  1612. display: flex;
  1613. justify-content: space-between;
  1614. margin-bottom: 20rpx;
  1615. .price-details-item-price-total {
  1616. display: flex;
  1617. width: 70%;
  1618. justify-content: flex-end;
  1619. flex-wrap: wrap;
  1620. }
  1621. .price-details-item-price-total-item {
  1622. color: #999999;
  1623. text-align: right;
  1624. font-size: 28rpx;
  1625. margin-right: 20rpx;
  1626. }
  1627. }
  1628. </style>