宠物详情页面更新,优化了这个页面的动画效果,新增小刷新动画
This commit is contained in:
parent
10335b8b2c
commit
7638324bb9
16
.idea/addr.iml
Normal file
16
.idea/addr.iml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="TemplatesService">
|
||||||
|
<option name="TEMPLATE_FOLDERS">
|
||||||
|
<list>
|
||||||
|
<option value="$MODULE_DIR$/acdr-ui/node_modules/conventional-changelog-angular/templates" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</module>
|
7
.idea/codeStyles/Project.xml
Normal file
7
.idea/codeStyles/Project.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<ScalaCodeStyleSettings>
|
||||||
|
<option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
|
||||||
|
</ScalaCodeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||||
|
</state>
|
||||||
|
</component>
|
@ -2,7 +2,7 @@
|
|||||||
<view
|
<view
|
||||||
:class="[
|
:class="[
|
||||||
'pos-fixed z-999 left-[1px] flex gap-[5px] items-center content-center justify-center',
|
'pos-fixed z-999 left-[1px] flex gap-[5px] items-center content-center justify-center',
|
||||||
isH5 ? 'top-[20px]' : 'top-[40px]',
|
isH5 ? 'top-[3vw]' : 'top-[6vw]',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<image src="/static/addresscell/location.png" class="w-[30px]" mode="widthFix" />
|
<image src="/static/addresscell/location.png" class="w-[30px]" mode="widthFix" />
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view
|
<wd-overlay :show="modelValue">
|
||||||
class="loading-overlay"
|
<view class="flex flex-col items-center justify-center content-center w-full h-full">
|
||||||
:catchtouchmove="modelValue"
|
|
||||||
@click.stop="stopEvent"
|
|
||||||
v-if="modelValue"
|
|
||||||
>
|
|
||||||
<view class="loading-animation">
|
<view class="loading-animation">
|
||||||
<view
|
<view
|
||||||
v-for="color in colors"
|
v-for="color in colors"
|
||||||
@ -14,53 +10,53 @@
|
|||||||
></view>
|
></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</wd-overlay>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, watchEffect } from 'vue'
|
import { defineProps } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: Boolean,
|
modelValue: Boolean,
|
||||||
|
timeout: {
|
||||||
|
// 添加timeout属性来控制加载时间
|
||||||
|
type: Number,
|
||||||
|
default: 5000, // 默认5秒后自动关闭
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const stopEvent = (event) => {
|
const emit = defineEmits(['update:modelValue'])
|
||||||
event.preventDefault() // 阻止默认行为
|
|
||||||
event.stopPropagation() // 阻止事件冒泡
|
|
||||||
}
|
|
||||||
|
|
||||||
const preventTouchMove = (event) => {
|
let timer = null
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
// H5和APP端清除点击事件
|
|
||||||
// #ifdef H5 || APP-PLUS
|
|
||||||
watchEffect(() => {
|
|
||||||
if (props.modelValue) {
|
|
||||||
document.addEventListener('touchmove', preventTouchMove, { passive: false })
|
|
||||||
} else {
|
|
||||||
document.removeEventListener('touchmove', preventTouchMove)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
const colors = ['#FF6347', '#4682B4', '#32CD32', '#FFD700', '#FF69B4', '#00FA9A']
|
const colors = ['#FF6347', '#4682B4', '#32CD32', '#FFD700', '#FF69B4', '#00FA9A']
|
||||||
|
|
||||||
|
// 定时器函数,超过指定时间自动关闭加载界面
|
||||||
|
const startTimeout = () => {
|
||||||
|
clearTimeout(timer)
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
stopLoading() // 超时后关闭加载
|
||||||
|
}, props.timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopLoading = () => {
|
||||||
|
// 触发关闭加载的逻辑,可以通过 emit 事件或者父组件控制
|
||||||
|
clearTimeout(timer)
|
||||||
|
emit('update:modelValue', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
// 开始加载时启动定时器
|
||||||
|
startTimeout()
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnload(() => {
|
||||||
|
// 清理定时器,防止内存泄漏
|
||||||
|
clearTimeout(timer)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.loading-overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 1000;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
pointer-events: auto; /* 添加这行代码 */
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-animation {
|
.loading-animation {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
68
acdr-ui/src/components/LocalLoader.vue
Normal file
68
acdr-ui/src/components/LocalLoader.vue
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<view class="w-full h-[20px] flex flex-col items-center" v-if="modelValue">
|
||||||
|
<view class="loading-animation">
|
||||||
|
<view
|
||||||
|
v-for="color in colors"
|
||||||
|
:key="color"
|
||||||
|
:style="{ backgroundColor: color }"
|
||||||
|
class="ball"
|
||||||
|
></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineProps } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: Boolean,
|
||||||
|
})
|
||||||
|
|
||||||
|
const colors = ['#FF6347', '#4682B4', '#32CD32', '#FFD700', '#FF69B4', '#00FA9A']
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.loading-animation {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ball {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: bounce 1.2s infinite ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ball:nth-child(1) {
|
||||||
|
animation-delay: -0.32s;
|
||||||
|
}
|
||||||
|
.ball:nth-child(2) {
|
||||||
|
animation-delay: -0.16s;
|
||||||
|
}
|
||||||
|
.ball:nth-child(3) {
|
||||||
|
animation-delay: -0.13s;
|
||||||
|
}
|
||||||
|
.ball:nth-child(4) {
|
||||||
|
animation-delay: -0.07s;
|
||||||
|
}
|
||||||
|
.ball:nth-child(5) {
|
||||||
|
animation-delay: -0.04s;
|
||||||
|
}
|
||||||
|
.ball:nth-child(6) {
|
||||||
|
animation-delay: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
0%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -94,7 +94,7 @@ const clickmap = () => {
|
|||||||
|
|
||||||
// 获取自己的地理位置
|
// 获取自己的地理位置
|
||||||
const getLocation = () => {
|
const getLocation = () => {
|
||||||
uni.showLoading({ title: '正在获取定位' })
|
// uni.showLoading({ title: '正在获取定位' })
|
||||||
uni.getLocation({
|
uni.getLocation({
|
||||||
type: 'gcj02',
|
type: 'gcj02',
|
||||||
timeout: 1000,
|
timeout: 1000,
|
||||||
@ -114,7 +114,7 @@ const getLocation = () => {
|
|||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
uni.hideLoading()
|
// uni.hideLoading()
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '位置信息获取失败(请确定定位功能是否打开)',
|
content: '位置信息获取失败(请确定定位功能是否打开)',
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<EmptyState v-else />
|
<LocalLoader v-model="loading" />
|
||||||
|
<EmptyState v-if="services.length == 0 && !loading" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js" setup>
|
<script lang="js" setup>
|
||||||
@ -21,12 +22,14 @@ import { imgUrl } from '@/utils/commUtils'
|
|||||||
import { getNearbyServices } from '@/service/personalService'
|
import { getNearbyServices } from '@/service/personalService'
|
||||||
import { getLocation } from '@/service/mapService'
|
import { getLocation } from '@/service/mapService'
|
||||||
import EmptyState from '@/components/EmptyState.vue'
|
import EmptyState from '@/components/EmptyState.vue'
|
||||||
|
import LocalLoader from '@/components/LocalLoader.vue'
|
||||||
|
|
||||||
const toDetail = (item) => {
|
const toDetail = (item) => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/service/detail?id=' + item.id,
|
url: '/pages/service/detail?id=' + item.id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
const services = ref([])
|
const services = ref([])
|
||||||
|
|
||||||
@ -39,7 +42,9 @@ const getQuickServices = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onLoad(async () => {
|
onLoad(async () => {
|
||||||
|
loading.value = true
|
||||||
await getQuickServices()
|
await getQuickServices()
|
||||||
|
loading.value = false
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<Banner />
|
<Banner />
|
||||||
<AddressCell />
|
<AddressCell />
|
||||||
<!-- 这里放搜索框 -->
|
<!-- 这里放搜索框 -->
|
||||||
<view class="w-[70vw] pos-absolute top-[3vw] right-0">
|
<view :class="['w-[70vw] pos-absolute right-0', isH5 ? 'top-[1vw]' : 'top-[3vw]']">
|
||||||
<Search />
|
<Search />
|
||||||
</view>
|
</view>
|
||||||
<loading-animation v-model="isLoading" />
|
<loading-animation v-model="isLoading" />
|
||||||
@ -86,6 +86,7 @@ import { httpGet } from '@/utils/http'
|
|||||||
import Tabbar from '@/components/Tabbar.vue'
|
import Tabbar from '@/components/Tabbar.vue'
|
||||||
import AddressCell from '@/components/AddressCell.vue'
|
import AddressCell from '@/components/AddressCell.vue'
|
||||||
import Search from '@/components/Search.vue'
|
import Search from '@/components/Search.vue'
|
||||||
|
import { isH5 } from '@/utils/platform'
|
||||||
|
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const petInfo = ref({})
|
const petInfo = ref({})
|
||||||
|
@ -30,8 +30,8 @@
|
|||||||
<view class="bg-white p-4">
|
<view class="bg-white p-4">
|
||||||
<text class="text-2xl font-bold">{{ serviceData.userName }}</text>
|
<text class="text-2xl font-bold">{{ serviceData.userName }}</text>
|
||||||
<view class="flex items-center mt-2">
|
<view class="flex items-center mt-2">
|
||||||
<text class="text-sm text-gray-500 mr-2">认证1年10个月</text>
|
<text class="text-sm text-gray-500 mr-2">认证{{ serviceData.certificationTime }}</text>
|
||||||
<text class="text-sm text-gray-500">服务过200+次</text>
|
<text class="text-sm text-gray-500">服务过 {{ serviceData.serviceNumber }} 次</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="flex items-center mt-2">
|
<view class="flex items-center mt-2">
|
||||||
<wd-icon name="location" size="20" class="text-[#ffc107]"></wd-icon>
|
<wd-icon name="location" size="20" class="text-[#ffc107]"></wd-icon>
|
||||||
@ -82,20 +82,22 @@
|
|||||||
<!-- 用户评价 -->
|
<!-- 用户评价 -->
|
||||||
<view class="bg-white p-4">
|
<view class="bg-white p-4">
|
||||||
<text class="text-lg font-bold mb-2">用户评价</text>
|
<text class="text-lg font-bold mb-2">用户评价</text>
|
||||||
<view class="mb-4">
|
<view class="mb-4" v-if="serviceData.comment">
|
||||||
<view class="flex items-center mb-2">
|
<view class="flex items-center mb-2">
|
||||||
<text class="text-pink-500 text-lg">5.0</text>
|
<text class="text-pink-500 text-lg">{{ serviceData.comment.star }}</text>
|
||||||
<view class="flex items-center ml-2">
|
<view class="flex items-center ml-2">
|
||||||
<wd-icon name="star-on" size="20" class="text-[#ffc107]"></wd-icon>
|
<wd-icon
|
||||||
<wd-icon name="star-on" size="20" class="text-[#ffc107]"></wd-icon>
|
v-for="num in serviceData.comment.star"
|
||||||
<wd-icon name="star-on" size="20" class="text-[#ffc107]"></wd-icon>
|
name="star-on"
|
||||||
<wd-icon name="star-on" size="20" class="text-[#ffc107]"></wd-icon>
|
size="20"
|
||||||
<wd-icon name="star-on" size="20" class="text-[#ffc107]"></wd-icon>
|
class="text-[#ffc107]"
|
||||||
|
></wd-icon>
|
||||||
</view>
|
</view>
|
||||||
<text class="text-gray-500 ml-2">(14条评论)</text>
|
<text class="text-gray-500 ml-2">({{ serviceData.comment.commentNum }}条评论)</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="text-gray-600">阿落超级细心!!下次一定还找阿落!!超级安心!</text>
|
<text class="text-gray-600">{{ serviceData.comment.comment }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="text-gray-500 pt-2 w-full text-center" v-else>暂无评论</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 底部地图展示 -->
|
<!-- 底部地图展示 -->
|
||||||
@ -132,14 +134,14 @@
|
|||||||
<!-- 预约弹窗 -->
|
<!-- 预约弹窗 -->
|
||||||
<view
|
<view
|
||||||
v-if="showReservationModal"
|
v-if="showReservationModal"
|
||||||
class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50"
|
class="z-999 fixed inset-0 flex items-center justify-center bg-black bg-opacity-50"
|
||||||
>
|
>
|
||||||
<view class="bg-white rounded-lg p-4 w-11/12">
|
<view class="bg-white rounded-lg p-4 w-11/12">
|
||||||
<view class="text-lg font-bold mb-4">选择预约信息</view>
|
<view class="text-lg font-bold mb-4">选择预约信息</view>
|
||||||
|
|
||||||
<!-- 服务宠物选择 -->
|
<!-- 服务宠物选择 -->
|
||||||
<text class="text-sm text-gray-500 mb-2">服务宠物</text>
|
<text class="text-sm text-gray-500 mb-2">服务宠物</text>
|
||||||
<view class="flex space-x-4 mb-4 scroll-x overflow-x-auto" scroll-x>
|
<view v-if="pets.length > 0" class="flex space-x-4 mb-4 scroll-x overflow-x-auto" scroll-x>
|
||||||
<view
|
<view
|
||||||
v-for="pet in pets"
|
v-for="pet in pets"
|
||||||
:key="pet.id"
|
:key="pet.id"
|
||||||
@ -154,6 +156,11 @@
|
|||||||
<text class="text-sm">{{ pet.name }}</text>
|
<text class="text-sm">{{ pet.name }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view v-else class="pet-item add-pet" @click="toPath('/pages/pet/pet-add-page')">
|
||||||
|
<w-avatar :size="80" class="pet-avatar add-avatar">
|
||||||
|
<view class="add-icon">+</view>
|
||||||
|
</w-avatar>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 预约时间 -->
|
<!-- 预约时间 -->
|
||||||
<text class="text-sm text-gray-500 mb-2">预约时间</text>
|
<text class="text-sm text-gray-500 mb-2">预约时间</text>
|
||||||
@ -180,7 +187,11 @@
|
|||||||
{{ selectedAddress.display || '请选择地址' }}
|
{{ selectedAddress.display || '请选择地址' }}
|
||||||
</view>
|
</view>
|
||||||
</picker>
|
</picker>
|
||||||
<view v-else>请选择地址</view>
|
<view v-else class="pet-item add-pet" @click="toPath('/pages/address/index')">
|
||||||
|
<w-avatar :size="80" class="pet-avatar add-avatar">
|
||||||
|
<view class="add-icon">+</view>
|
||||||
|
</w-avatar>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<view class="fixed bottom-0 left-0 w-full p-4 bg-white shadow-up">
|
<view class="fixed bottom-0 left-0 w-full p-4 bg-white shadow-up">
|
||||||
@ -201,15 +212,18 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<LoadingAnimation v-model="loading" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js" setup>
|
<script lang="js" setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { httpGet } from '@/utils/http'
|
import { httpGet } from '@/utils/http'
|
||||||
import { baseUrl, toast } from '@/utils/commUtils'
|
import { baseUrl, toast, toPath } from '@/utils/commUtils'
|
||||||
import { pay } from '@/logic/pay'
|
import { pay } from '@/logic/pay'
|
||||||
import TopBar from '@/components/TopBar.vue'
|
import TopBar from '@/components/TopBar.vue'
|
||||||
import Map from '@/components/Map.vue'
|
import Map from '@/components/Map.vue'
|
||||||
|
import LoadingAnimation from '@/components/LoadingAnimation.vue'
|
||||||
|
|
||||||
// 获取当前日期
|
// 获取当前日期
|
||||||
const nowday = new Date()
|
const nowday = new Date()
|
||||||
@ -217,7 +231,7 @@ const year = nowday.getFullYear()
|
|||||||
const month = (nowday.getMonth() + 1).toString().padStart(2, '0') // 月份从0开始,需加1
|
const month = (nowday.getMonth() + 1).toString().padStart(2, '0') // 月份从0开始,需加1
|
||||||
const day = nowday.getDate().toString().padStart(2, '0')
|
const day = nowday.getDate().toString().padStart(2, '0')
|
||||||
const reservationDate = ref(`${year}-${month}-${day}`)
|
const reservationDate = ref(`${year}-${month}-${day}`)
|
||||||
|
const loading = ref(false)
|
||||||
const serviceData = ref({})
|
const serviceData = ref({})
|
||||||
const pets = ref([])
|
const pets = ref([])
|
||||||
const addressList = ref([])
|
const addressList = ref([])
|
||||||
@ -264,12 +278,12 @@ const setDateRange = () => {
|
|||||||
weekFromToday.value = weekLater.toISOString().split('T')[0]
|
weekFromToday.value = weekLater.toISOString().split('T')[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the date change event
|
// 选择时间
|
||||||
const handleDateChange = (e) => {
|
const handleDateChange = (e) => {
|
||||||
reservationDate.value = e.detail.value
|
reservationDate.value = e.detail.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the address change event
|
// 选择地址
|
||||||
const handleAddressChange = (e) => {
|
const handleAddressChange = (e) => {
|
||||||
selectedAddress.value = addressList.value[e.detail.value]
|
selectedAddress.value = addressList.value[e.detail.value]
|
||||||
}
|
}
|
||||||
@ -301,6 +315,8 @@ const confirmReservation = async () => {
|
|||||||
|
|
||||||
// 页面加载时获取服务详情数据、宠物信息和地址信息并初始化地图
|
// 页面加载时获取服务详情数据、宠物信息和地址信息并初始化地图
|
||||||
onLoad(async (options) => {
|
onLoad(async (options) => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
if (!options.id) {
|
if (!options.id) {
|
||||||
toast('该服务不存在!')
|
toast('该服务不存在!')
|
||||||
// 返回到上一级页面
|
// 返回到上一级页面
|
||||||
@ -316,16 +332,16 @@ onLoad(async (options) => {
|
|||||||
if (serviceResponse.code === 200) {
|
if (serviceResponse.code === 200) {
|
||||||
serviceData.value = serviceResponse.data
|
serviceData.value = serviceResponse.data
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({ title: '加载服务数据失败', icon: 'none' })
|
toast(serviceResponse.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取宠物信息
|
// 获取宠物信息
|
||||||
const petsResponse = await httpGet('/petInfo/my')
|
const petsResponse = await httpGet('/petInfo/my')
|
||||||
if (petsResponse.code === 200) {
|
if (petsResponse.code === 200) {
|
||||||
pets.value = petsResponse.data
|
pets.value = petsResponse.data
|
||||||
selectedPetId.value = pets.value[0].id
|
if (pets.value.length !== 0) selectedPetId.value = pets.value[0].id
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({ title: '加载宠物数据失败', icon: 'none' })
|
toast(petsResponse.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取地址信息
|
// 获取地址信息
|
||||||
@ -337,12 +353,47 @@ onLoad(async (options) => {
|
|||||||
})
|
})
|
||||||
selectedAddress.value = addressList.value[0]
|
selectedAddress.value = addressList.value[0]
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({ title: '加载地址数据失败', icon: 'none' })
|
toast(addressResponse.message)
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
toast('获取服务详情失败!')
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = false
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.pet-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pet-avatar {
|
||||||
|
border: 2px solid #fcd038;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-avatar {
|
||||||
|
width: 102rpx;
|
||||||
|
height: 102rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 2px dashed #fcd038;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pet-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-icon {
|
||||||
|
font-size: 36px;
|
||||||
|
line-height: 45px;
|
||||||
|
color: #fcd038;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.options {
|
.options {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
@ -94,4 +94,5 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
return this.update(null, updateWrapper);
|
return this.update(null, updateWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,15 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
|||||||
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
||||||
import com.yskj.acdr.common.response.GlobalResponse;
|
import com.yskj.acdr.common.response.GlobalResponse;
|
||||||
import com.yskj.acdr.master.address.service.ServiceAddressService;
|
import com.yskj.acdr.master.address.service.ServiceAddressService;
|
||||||
|
import com.yskj.acdr.master.order.entity.Order;
|
||||||
|
import com.yskj.acdr.master.order.orderenum.OrderState;
|
||||||
|
import com.yskj.acdr.master.order.service.OrderService;
|
||||||
import com.yskj.acdr.master.personal.entity.PersonalService;
|
import com.yskj.acdr.master.personal.entity.PersonalService;
|
||||||
import com.yskj.acdr.master.personal.service.PersonalServiceService;
|
import com.yskj.acdr.master.personal.service.PersonalServiceService;
|
||||||
import com.yskj.acdr.master.pet.entity.PetInfo;
|
import com.yskj.acdr.master.pet.entity.PetInfo;
|
||||||
|
import com.yskj.acdr.master.pet.entity.PetSpecialistCertificate;
|
||||||
import com.yskj.acdr.master.pet.service.PetInfoService;
|
import com.yskj.acdr.master.pet.service.PetInfoService;
|
||||||
|
import com.yskj.acdr.master.pet.service.PetSpecialistCertificateService;
|
||||||
import com.yskj.acdr.master.user.entity.Users;
|
import com.yskj.acdr.master.user.entity.Users;
|
||||||
import com.yskj.acdr.master.user.service.UsersService;
|
import com.yskj.acdr.master.user.service.UsersService;
|
||||||
import io.swagger.annotations.*;
|
import io.swagger.annotations.*;
|
||||||
@ -21,6 +26,9 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -51,6 +59,12 @@ public class PersonalServiceController {
|
|||||||
@Resource
|
@Resource
|
||||||
private ServiceAddressService mapService;
|
private ServiceAddressService mapService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PetSpecialistCertificateService pscs;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private OrderService orderService;
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/push")
|
@PostMapping("/push")
|
||||||
public GlobalResponse<Boolean> push(
|
public GlobalResponse<Boolean> push(
|
||||||
@ -140,6 +154,30 @@ public class PersonalServiceController {
|
|||||||
* @param id 查询服务id
|
* @param id 查询服务id
|
||||||
* @author linghe
|
* @author linghe
|
||||||
* 展示宠物个人服务详细信息
|
* 展示宠物个人服务详细信息
|
||||||
|
* 返回如下数据:
|
||||||
|
* // 将这些数据都打散装配到Map当中
|
||||||
|
* var map = MapUtil.<String, Object>builder()
|
||||||
|
* .put("serviceId", p.getId())
|
||||||
|
* .put("serviceName", p.getServiceName())
|
||||||
|
* .put("bgUrl", p.getUrl())
|
||||||
|
* .put("description", p.getDescription())
|
||||||
|
* .put("serviceType", p.getType())
|
||||||
|
* .put("latitude", p.getLatitude())
|
||||||
|
* .put("longitude", p.getLongitude())
|
||||||
|
* .put("address", p.getAddress())
|
||||||
|
* .put("serviceState", p.getState())
|
||||||
|
* .put("createTime", p.getCreateTime())
|
||||||
|
* .put("updateTime", p.getUpdateTime())
|
||||||
|
* .put("price", p.getPrice())
|
||||||
|
* .put("serviceUserId", p.getUserId()) // 服务者ID
|
||||||
|
* .put("userName", user.getNickname())
|
||||||
|
* .put("userAvatar", user.getAvatar())
|
||||||
|
* .put("pets", pets)
|
||||||
|
* // 添加认证时间
|
||||||
|
* .put("certificationTime", calculateTimeDifference(LocalDateTime.now(), psc.getCreateTime()))
|
||||||
|
* .put("serviceNumber", psc.getServiceNumber())
|
||||||
|
* .put("comments", comments)
|
||||||
|
* .map();
|
||||||
*/
|
*/
|
||||||
@GetMapping("/service/{id}")
|
@GetMapping("/service/{id}")
|
||||||
public GlobalResponse<Map<String, Object>> personalDetail(@PathVariable Long id) {
|
public GlobalResponse<Map<String, Object>> personalDetail(@PathVariable Long id) {
|
||||||
@ -160,6 +198,49 @@ public class PersonalServiceController {
|
|||||||
"profileUrl", petInfo.getProfileUrl()))
|
"profileUrl", petInfo.getProfileUrl()))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
// 获取该宠托师其他信息(服务次数、认证年龄)
|
||||||
|
PetSpecialistCertificate psc = pscs.getOne(new LambdaQueryWrapper<PetSpecialistCertificate>()
|
||||||
|
.eq(PetSpecialistCertificate::getUserId, userId)
|
||||||
|
// 小于过期时间
|
||||||
|
.gt(PetSpecialistCertificate::getExpiredTime, LocalDateTime.now()));
|
||||||
|
if (psc == null) {
|
||||||
|
return GlobalResponse.failure("该用户没有宠物师证书,或者宠物师证书被吊销!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取该宠托师以往的订单评论信息, 只获取历史数据的前一条
|
||||||
|
// 获取服务次数
|
||||||
|
long commitNums = orderService.count(new LambdaQueryWrapper<Order>()
|
||||||
|
.eq(Order::getPersonalServiceUserId, userId)
|
||||||
|
.eq(Order::getState, OrderState.EVALUATED)
|
||||||
|
.isNotNull(Order::getFeedback)
|
||||||
|
.isNotNull(Order::getStar));
|
||||||
|
|
||||||
|
// 获取之后获取该用户的评论信息
|
||||||
|
Order commentsOrder = null;
|
||||||
|
if(commitNums > 0) {
|
||||||
|
commentsOrder = orderService.lambdaQuery()
|
||||||
|
.eq(Order::getPersonalServiceUserId, userId)
|
||||||
|
.eq(Order::getState, OrderState.EVALUATED)
|
||||||
|
.isNotNull(Order::getFeedback)
|
||||||
|
.isNotNull(Order::getStar)
|
||||||
|
.orderByDesc(Order::getStar)
|
||||||
|
.orderByDesc(Order::getCreateTime)
|
||||||
|
.last("LIMIT 1")
|
||||||
|
.one();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> comments = null;
|
||||||
|
if (commentsOrder != null) {
|
||||||
|
comments = new HashMap<>();
|
||||||
|
Users users = usersService.getById(commentsOrder.getUserId());
|
||||||
|
comments.put("star", commentsOrder.getStar());
|
||||||
|
comments.put("comment", commentsOrder.getFeedback());
|
||||||
|
comments.put("commentNum", commitNums);
|
||||||
|
comments.put("userName", users.getNickname());
|
||||||
|
comments.put("userAvatar", users.getAvatar());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 将这些数据都打散装配到Map当中
|
// 将这些数据都打散装配到Map当中
|
||||||
var map = MapUtil.<String, Object>builder()
|
var map = MapUtil.<String, Object>builder()
|
||||||
.put("serviceId", p.getId())
|
.put("serviceId", p.getId())
|
||||||
@ -175,14 +256,43 @@ public class PersonalServiceController {
|
|||||||
.put("updateTime", p.getUpdateTime())
|
.put("updateTime", p.getUpdateTime())
|
||||||
.put("price", p.getPrice())
|
.put("price", p.getPrice())
|
||||||
.put("serviceUserId", p.getUserId()) // 服务者ID
|
.put("serviceUserId", p.getUserId()) // 服务者ID
|
||||||
.put("userName", user.getName())
|
.put("userName", user.getNickname())
|
||||||
.put("userAvatar", user.getAvatar())
|
.put("userAvatar", user.getAvatar())
|
||||||
.put("pets", pets)
|
.put("pets", pets)
|
||||||
|
// 添加认证时间
|
||||||
|
.put("certificationTime", calculateTimeDifference(LocalDateTime.now(), psc.getCreateTime()))
|
||||||
|
.put("serviceNumber", psc.getServiceNumber())
|
||||||
|
.put("comments", comments)
|
||||||
.map();
|
.map();
|
||||||
|
|
||||||
return GlobalResponse.success(map);
|
return GlobalResponse.success(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String calculateTimeDifference(LocalDateTime startDate, LocalDateTime endDate) {
|
||||||
|
// 计算年差
|
||||||
|
long years = ChronoUnit.YEARS.between(startDate, endDate);
|
||||||
|
LocalDateTime tempDateTime = startDate.plusYears(years);
|
||||||
|
|
||||||
|
// 计算月差
|
||||||
|
long months = ChronoUnit.MONTHS.between(tempDateTime, endDate);
|
||||||
|
tempDateTime = tempDateTime.plusMonths(months);
|
||||||
|
|
||||||
|
// 计算天差
|
||||||
|
long days = ChronoUnit.DAYS.between(tempDateTime, endDate);
|
||||||
|
|
||||||
|
// 如果天数小于一个月,按一个月计算
|
||||||
|
if (days >= 0 && days < 30) {
|
||||||
|
months++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (years == 0) {
|
||||||
|
return months + "个月";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回 xx年xx月 格式
|
||||||
|
return years + "年" + months + "个月";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据用户的位置获取附近服务的位置。
|
* 根据用户的位置获取附近服务的位置。
|
||||||
*
|
*
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.yskj.acdr.master.pet.controller;
|
package com.yskj.acdr.master.pet.controller;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.hutool.core.collection.ListUtil;
|
||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
@ -162,6 +163,7 @@ public class PetController {
|
|||||||
public GlobalResponse<PetInfo> index() {
|
public GlobalResponse<PetInfo> index() {
|
||||||
List<PetInfo> one = petInfoService.list(new LambdaQueryWrapper<PetInfo>()
|
List<PetInfo> one = petInfoService.list(new LambdaQueryWrapper<PetInfo>()
|
||||||
.eq(PetInfo::getUserId, StpUtil.getLoginIdAsLong()));
|
.eq(PetInfo::getUserId, StpUtil.getLoginIdAsLong()));
|
||||||
|
if (one == null || one.isEmpty()) return GlobalResponse.success();
|
||||||
return GlobalResponse.success(one.getFirst());
|
return GlobalResponse.success(one.getFirst());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user