- 新增frequency.js接口文件,包含日订单列表查询、详情获取和打卡功能 - 修改myOrdersManage页面,使用新版日订单接口替代旧接口 - 更新timelineService组件,适配新版数据结构和展示逻辑 - 重构打卡页面,支持多宠物和多服务项目的打卡记录 - 调整环境配置,将默认环境改为develop并更新本地开发地址master
| @ -0,0 +1,40 @@ | |||||
| import request from '@/utils/request' | |||||
| // 新版本日订单查询日订单列表 | |||||
| export function appletOrderDateFrequencyList(params) { | |||||
| return request({ | |||||
| headers: { | |||||
| "isToken": true | |||||
| }, | |||||
| url: "/applet/appletOrderDateFrequency/list", | |||||
| method: 'get', | |||||
| params | |||||
| }) | |||||
| } | |||||
| // 新版本日订单查询详情 | |||||
| export function appletOrderDateFrequencyById(id) { | |||||
| return request({ | |||||
| headers: { | |||||
| "isToken": true | |||||
| }, | |||||
| url: "/applet/appletOrderDateFrequency/" + id, | |||||
| method: 'get', | |||||
| }) | |||||
| } | |||||
| // 新版本日订单打卡 | |||||
| export function appletOrderDateFrequencyCheck(data) { | |||||
| return request({ | |||||
| headers: { | |||||
| "isToken": true | |||||
| }, | |||||
| url: "/applet/appletOrderDateFrequency/check", | |||||
| method: 'post', | |||||
| data | |||||
| }) | |||||
| } | |||||
| export default { | |||||
| appletOrderDateFrequencyList, | |||||
| appletOrderDateFrequencyById, | |||||
| appletOrderDateFrequencyCheck, | |||||
| } | |||||
| @ -0,0 +1,462 @@ | |||||
| <template> | |||||
| <!-- <view>打卡</view> --> | |||||
| <view class="box box-size"> | |||||
| <view class="top box-size" :style="{borderRadius:'16rpx'}"> | |||||
| <view class="form-title"> | |||||
| 个人准备 | |||||
| </view> | |||||
| <view class="mt32 ml10"> | |||||
| <span :style="{fontSize:'30rpx',fontWeight:'400'}">手套照片</span> | |||||
| <span :style="{fontSize:'26rpx',color:'#C7C7C7'}">(至少1张)</span> | |||||
| </view> | |||||
| <view class="level"> | |||||
| <view class="mt20 ml10"> | |||||
| <up-upload :fileList="fileList.glove" @afterRead="afterRead" @delete="deletePic" name="glove" | |||||
| multiple :maxCount="2" width="131rpx" height="131rpx" :disabled="isRead"> | |||||
| <image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}"> | |||||
| </image> | |||||
| </up-upload> | |||||
| </view> | |||||
| </view> | |||||
| <view class="mt32 ml10"> | |||||
| <span :style="{fontSize:'30rpx',fontWeight:'400'}">鞋套照片</span> | |||||
| <span :style="{fontSize:'26rpx',color:'#C7C7C7'}">(至少1张)</span> | |||||
| </view> | |||||
| <view class="level"> | |||||
| <view class="mt20 ml10"> | |||||
| <up-upload :fileList="fileList.ShoeCover" @afterRead="afterRead" @delete="deletePic" | |||||
| name="ShoeCover" multiple :maxCount="2" width="131rpx" height="131rpx" :disabled="isRead"> | |||||
| <image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}"> | |||||
| </image> | |||||
| </up-upload> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| <view class="top mt24 box-size" :style="{borderRadius:'16rpx'}"> | |||||
| <view class="form-title"> | |||||
| 宠物状态记录 | |||||
| </view> | |||||
| <view v-for="(pet, index) in petList" :key="index"> | |||||
| <view class="mt32 ml10"> | |||||
| <span :style="{fontSize:'30rpx',fontWeight:'400'}">{{ pet.title }}照片</span> | |||||
| <span :style="{fontSize:'26rpx',color:'#C7C7C7'}">(至少2张)</span> | |||||
| </view> | |||||
| <view class="level"> | |||||
| <view class="mt20 ml10 mr20"> | |||||
| <up-upload :fileList="fileList['pet' + index]" @afterRead="afterRead" @delete="deletePic" | |||||
| :name="'pet' + index" multiple :maxCount="2" width="131rpx" height="131rpx" :disabled="isRead"> | |||||
| <image src="/static/images/ydd/add_photo.png" mode="" | |||||
| :style="{width:'131rpx',height:'131rpx'}"> | |||||
| </image> | |||||
| </up-upload> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| <view class="top mt24 box-size" :style="{borderRadius:'16rpx'}"> | |||||
| <view class="form-title"> | |||||
| 基础服务记录 | |||||
| </view> | |||||
| <view class="mt32 ml10" :style="{fontSize:'30rpx',fontWeight:'400'}">粮碗前后对比 </view> | |||||
| <view class="level"> | |||||
| <view class="mt20 ml10"> | |||||
| <up-upload :fileList="fileList.foodA" @afterRead="afterRead" @delete="deletePic" name="foodA" | |||||
| multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead"> | |||||
| <image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}"> | |||||
| </image> | |||||
| </up-upload> | |||||
| <view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}">前</view> | |||||
| </view> | |||||
| <view class="mt20 ml10 ml28"> | |||||
| <up-upload :fileList="fileList.foodB" @afterRead="afterRead" @delete="deletePic" name="foodB" | |||||
| multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead"> | |||||
| <image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}"> | |||||
| </image> | |||||
| </up-upload> | |||||
| <view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}">后</view> | |||||
| </view> | |||||
| </view> | |||||
| <view class="mt32 ml10" :style="{fontSize:'30rpx',fontWeight:'400'}">水碗前后对比 </view> | |||||
| <view class="level"> | |||||
| <view class="mt20 ml10"> | |||||
| <up-upload :fileList="fileList.waterA" @afterRead="afterRead" @delete="deletePic" name="waterA" | |||||
| multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead"> | |||||
| <image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}"> | |||||
| </image> | |||||
| </up-upload> | |||||
| <view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}">前</view> | |||||
| </view> | |||||
| <view class="mt20 ml10 ml28"> | |||||
| <up-upload :fileList="fileList.waterB" @afterRead="afterRead" @delete="deletePic" name="waterB" | |||||
| multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead"> | |||||
| <image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}"> | |||||
| </image> | |||||
| </up-upload> | |||||
| <view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}">后</view> | |||||
| </view> | |||||
| </view> | |||||
| <view class="mt32 ml10" :style="{fontSize:'30rpx',fontWeight:'400'}">猫砂盆、尿垫前后对比 </view> | |||||
| <view class="level"> | |||||
| <view class="mt20 ml10"> | |||||
| <up-upload :fileList="fileList.urinalA" @afterRead="afterRead" @delete="deletePic" name="urinalA" | |||||
| multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead"> | |||||
| <image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}"> | |||||
| </image> | |||||
| </up-upload> | |||||
| <view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}">前</view> | |||||
| </view> | |||||
| <view class="mt20 ml10 ml28"> | |||||
| <up-upload :fileList="fileList.urinalB" @afterRead="afterRead" @delete="deletePic" name="urinalB" | |||||
| multiple :maxCount="1" width="131rpx" height="131rpx" :disabled="isRead"> | |||||
| <image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}"> | |||||
| </image> | |||||
| </up-upload> | |||||
| <view class="jus-center level" :style="{color:'#999999',fontSize:'22rpx',width:'131rpx'}">后</view> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| <view class="top mt24 box-size" :style="{borderRadius:'16rpx'}"> | |||||
| <view class="form-title"> | |||||
| 定制服务记录 | |||||
| </view> | |||||
| <view class="mt32 ml10"> | |||||
| <span :style="{fontSize:'30rpx',fontWeight:'400'}">遛狗</span> | |||||
| <span :style="{fontSize:'26rpx',color:'#C7C7C7'}">(2-3张)</span> | |||||
| </view> | |||||
| <view class="level"> | |||||
| <view class="mt20 ml10 mr20"> | |||||
| <up-upload :fileList="fileList.testa" @afterRead="afterRead" @delete="deletePic" name="testa" | |||||
| multiple :maxCount="3" width="131rpx" height="131rpx" :disabled="isRead"> | |||||
| <image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}"> | |||||
| </image> | |||||
| </up-upload> | |||||
| </view> | |||||
| </view> | |||||
| <view class="mt32 ml10"> | |||||
| <span :style="{fontSize:'30rpx',fontWeight:'400'}">陪玩</span> | |||||
| <span :style="{fontSize:'26rpx',color:'#C7C7C7'}">(2-3张)</span> | |||||
| </view> | |||||
| <view class="level"> | |||||
| <view class="mt20 ml10 mr20"> | |||||
| <up-upload :fileList="fileList.testb" @afterRead="afterRead" @delete="deletePic" name="testb" | |||||
| multiple :maxCount="3" width="131rpx" height="131rpx" :disabled="isRead"> | |||||
| <image src="/static/images/ydd/add_photo.png" mode="" :style="{width:'131rpx',height:'131rpx'}"> | |||||
| </image> | |||||
| </up-upload> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| <view class="top mt24 box-size" :style="{borderRadius:'16rpx'}"> | |||||
| <view class="form-title"> | |||||
| 其他补充信息(必填) | |||||
| </view> | |||||
| <view class="mt32 ml10" :style="{color:'#999999',fontSize:'30rpx'}"> | |||||
| 可记录一下今日趣事、宠物状况、提醒事项等 | |||||
| </view> | |||||
| <view class="mt24"> | |||||
| <textarea cols="30" rows="10" | |||||
| placeholder="请输入内容" | |||||
| v-model="content" | |||||
| :style="{color:'#999999',fontSize:'30rpx',backgroundColor:'#F5F5F5',borderRadius:'16rpx',width:'681rpx',height:'180rpx'}" | |||||
| class="pd20 box-size" :disabled="isRead"></textarea> | |||||
| </view> | |||||
| </view> | |||||
| <view class="buttom mt60 box-size" | |||||
| v-if="!isRead" | |||||
| style="display: flex;gap: 20rpx;" | |||||
| > | |||||
| <view class="buttom-item buttom-item-2 level size-30" | |||||
| @click="saveDraft(false)" | |||||
| :style="{borderRadius:'41rpx',color:'#ff842c'}"> | |||||
| 保存草稿 | |||||
| </view> | |||||
| <view class="buttom-item level size-30" | |||||
| @click="submit" | |||||
| :style="{borderRadius:'41rpx',color:'#fff'}"> | |||||
| 确定提交 | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| </template> | |||||
| <script setup> | |||||
| import { | |||||
| onMounted, | |||||
| reactive, | |||||
| ref, | |||||
| } from "vue"; | |||||
| import { | |||||
| onLoad | |||||
| } from '@dcloudio/uni-app' | |||||
| import { | |||||
| ossUpload | |||||
| } from '@/utils/oss-upload/oss/index.js' | |||||
| import { orderItemLogGetByOrderId } from '@/api/order/order.js' | |||||
| import { | |||||
| getOrderPetById, | |||||
| orderItemLogAddOrUpdate, | |||||
| } from "@/api/order/order.js"; | |||||
| onLoad((options) => { | |||||
| orderId.value = options.id || null; | |||||
| isRead.value = options.isRead || false; | |||||
| itemOrderID.value = options.itemID | |||||
| serviceId.value = options.serviceId; | |||||
| GetByOrderId() | |||||
| }); | |||||
| const orderId = ref(0) | |||||
| const itemOrderID = ref(0) | |||||
| const serviceId = ref(0) | |||||
| const isRead = ref(false) | |||||
| const content = ref('') | |||||
| const fileList = reactive({ | |||||
| // glove: [],//手套 | |||||
| // ShoeCover: [],//鞋套 | |||||
| // food: [],//粮碗 | |||||
| // water: [],//水碗 | |||||
| // urinal: [],//猫砂盆、尿盆 | |||||
| }) | |||||
| const form = ref({}) | |||||
| const petList = ref([ | |||||
| // { | |||||
| // title: '小汪' | |||||
| // }, | |||||
| // { | |||||
| // title: '大黄' | |||||
| // }, | |||||
| ]) | |||||
| // 删除图片 | |||||
| const deletePic = (event) => { | |||||
| if (fileList[event.name]) { | |||||
| fileList[event.name].splice(event.index, 1); | |||||
| } | |||||
| }; | |||||
| // 新增图片 | |||||
| const afterRead = (event) => { | |||||
| if (!fileList[event.name]) { | |||||
| fileList[event.name] = [] | |||||
| } | |||||
| event.file.forEach(n => { | |||||
| ossUpload(n.url) | |||||
| .then(url => { | |||||
| fileList[event.name].push({ | |||||
| url | |||||
| }) | |||||
| }) | |||||
| }) | |||||
| }; | |||||
| function submit() { | |||||
| // 参数效验 | |||||
| if (!fileList.glove || fileList.glove.length == 0) { | |||||
| return msg('请上传鞋套照片') | |||||
| } | |||||
| if (!fileList.ShoeCover || fileList.ShoeCover.length == 0) { | |||||
| return msg('请上传鞋套照片') | |||||
| } | |||||
| for (let i = 0; i < petList.value.length; i++) { | |||||
| if (!fileList['pet' + i] || fileList['pet' + i].length < 2) { | |||||
| return msg(`请上传${petList.value[i].title}照片`) | |||||
| } | |||||
| } | |||||
| if (!fileList.foodA || fileList.foodA.length < 1 || | |||||
| !fileList.foodB || fileList.foodB.length < 1) { | |||||
| return msg('请上传粮碗前后照片') | |||||
| } | |||||
| if (!fileList.waterA || fileList.waterA.length < 1 || | |||||
| !fileList.waterB || fileList.waterB.length < 1) { | |||||
| return msg('请上传水碗前后照片') | |||||
| } | |||||
| if (!fileList.urinalA || fileList.urinalA.length < 1 || | |||||
| !fileList.urinalB || fileList.urinalB.length < 1) { | |||||
| return msg('请上传猫砂盆、尿盆照片') | |||||
| } | |||||
| if(!content.value){ | |||||
| return msg('请填写补充信息') | |||||
| } | |||||
| saveDraft(true) | |||||
| } | |||||
| function GetByOrderId(){ | |||||
| orderItemLogGetByOrderId(serviceId.value) | |||||
| .then(res => { | |||||
| if(res.code == 200 && res.data) { | |||||
| const data = res.data; | |||||
| form.value = data | |||||
| // 回显手套照片 | |||||
| if(data.glovePhoto) { | |||||
| fileList.glove = data.glovePhoto.split(',').map(url => ({ url })); | |||||
| } | |||||
| // 回显鞋套照片 | |||||
| if(data.shoeCoverPhoto) { | |||||
| fileList.ShoeCover = data.shoeCoverPhoto.split(',').map(url => ({ url })); | |||||
| } | |||||
| // 回显宠物照片 | |||||
| if(data.petPhoto) { | |||||
| const pets = JSON.parse(data.petPhoto); | |||||
| petList.value = pets; | |||||
| pets.forEach((pet, index) => { | |||||
| if(pet.fileList) { | |||||
| fileList['pet' + index] = pet.fileList.split(',').map(url => ({ url })); | |||||
| } | |||||
| }); | |||||
| } | |||||
| // 回显粮碗照片 | |||||
| if(data.grainBowlFront) fileList.foodA = data.grainBowlFront.split(',').map(url => ({ url })); | |||||
| if(data.grainBowlAfter) fileList.foodB = data.grainBowlAfter.split(',').map(url => ({ url })); | |||||
| // 回显水碗照片 | |||||
| if(data.waterBowlFront) fileList.waterA = data.waterBowlFront.split(',').map(url => ({ url })); | |||||
| if(data.waterBowlAfter) fileList.waterB = data.waterBowlAfter.split(',').map(url => ({ url })); | |||||
| // 回显猫砂盆/尿垫照片 | |||||
| if(data.basinFront) fileList.urinalA = data.basinFront.split(',').map(url => ({ url })); | |||||
| if(data.basinAfter) fileList.urinalB = data.basinAfter.split(',').map(url => ({ url })); | |||||
| // 回显定制服务照片 | |||||
| if(data.workDogImage) fileList.testa = data.workDogImage.split(',').map(url => ({ url })); | |||||
| if(data.workPalyImage) fileList.testb = data.workPalyImage.split(',').map(url => ({ url })); | |||||
| }else{ | |||||
| getOrderPetByIdFN() | |||||
| } | |||||
| }) | |||||
| } | |||||
| function getOrderPetByIdFN(){ | |||||
| getOrderPetById(orderId.value) | |||||
| .then(res => { | |||||
| if(res.code == 200){ | |||||
| res.data.forEach((n, i) => { | |||||
| fileList['pet' + i] = [] | |||||
| }) | |||||
| res.data.forEach((n, i) => { | |||||
| petList.value.push({ | |||||
| title : n.name, | |||||
| id : n.id, | |||||
| }) | |||||
| }) | |||||
| } | |||||
| }) | |||||
| } | |||||
| function saveDraft(flag) { | |||||
| // 将所有图片数组转换为逗号分隔的字符串 | |||||
| const params = { | |||||
| orderId: orderId.value, | |||||
| itemOrderId : itemOrderID.value, | |||||
| itemDateId : serviceId.value, | |||||
| glovePhoto: fileList.glove?.map(item => item.url).join(',') || '', | |||||
| shoeCoverPhoto: fileList.ShoeCover?.map(item => item.url).join(',') || '', | |||||
| // 合并所有宠物照片 | |||||
| petPhoto: JSON.stringify( | |||||
| petList.value.map((pet, index) => { | |||||
| return { | |||||
| id : pet.id, | |||||
| title : pet.title, | |||||
| fileList: fileList['pet' + index]?.map(item => item.url).join(',') || '' | |||||
| }; | |||||
| }) | |||||
| ), | |||||
| // 粮碗照片 | |||||
| grainBowlFront: fileList.foodA?.map(item => item.url).join(',') || '', | |||||
| grainBowlAfter: fileList.foodB?.map(item => item.url).join(',') || '', | |||||
| // 水碗照片 | |||||
| waterBowlFront: fileList.waterA?.map(item => item.url).join(',') || '', | |||||
| waterBowlAfter: fileList.waterB?.map(item => item.url).join(',') || '', | |||||
| // 猫砂盆/尿垫照片 | |||||
| basinFront: fileList.urinalA?.map(item => item.url).join(',') || '', | |||||
| basinAfter: fileList.urinalB?.map(item => item.url).join(',') || '', | |||||
| // 定制服务照片 | |||||
| workDogImage: fileList.testa?.map(item => item.url).join(',') || '', | |||||
| workPalyImage: fileList.testb?.map(item => item.url).join(',') || '', | |||||
| // 备注信息 | |||||
| notes: form.value.notes || '', | |||||
| } | |||||
| if(form.value.id) { | |||||
| params.id = form.value.id | |||||
| } | |||||
| if(flag) { | |||||
| params.submitFlag = 2 | |||||
| } | |||||
| orderItemLogAddOrUpdate(params) | |||||
| .then(res => { | |||||
| if(res.code === 200) { | |||||
| uni.showToast({ | |||||
| title: '提交成功', | |||||
| icon: 'success' | |||||
| }) | |||||
| // 如果是确认提交(非草稿),则返回上一页 | |||||
| setTimeout(() => { | |||||
| uni.navigateBack() | |||||
| }, 1500) | |||||
| } else { | |||||
| uni.showToast({ | |||||
| title: res.msg || '提交失败', | |||||
| icon: 'none' | |||||
| }) | |||||
| } | |||||
| }) | |||||
| .catch(err => { | |||||
| uni.showToast({ | |||||
| title: '提交失败', | |||||
| icon: 'none' | |||||
| }) | |||||
| }) | |||||
| } | |||||
| function msg(content) { | |||||
| uni.showToast({ | |||||
| title: content, | |||||
| icon: 'none' | |||||
| }) | |||||
| } | |||||
| </script> | |||||
| <style scoped lang="scss"> | |||||
| @import"index.scss" | |||||
| </style> | |||||
| @ -0,0 +1,636 @@ | |||||
| <template> | |||||
| <view class="timeline-container"> | |||||
| <!-- 日期和状态标签 --> | |||||
| <view class="date-header"> | |||||
| <view class="date-box"> | |||||
| <view class="date-box-color" :style="{'background-color': getTopBgColor()}"></view> | |||||
| <view class="date-month-day">{{ formatDate(date).month }}-{{ formatDate(date).day }}</view> | |||||
| </view> | |||||
| <view class="status-tag" :class="{'status-tag-pending': status}"> | |||||
| <image src="/static/images/ydd/icon1.png" | |||||
| mode="aspectFit" | |||||
| v-if="status" | |||||
| class="status-icon"></image> | |||||
| <image src="/static/images/order/success.png" | |||||
| mode="aspectFit" | |||||
| v-else | |||||
| class="status-icon"></image> | |||||
| {{ status ? '待上门' : '已完成' }} | |||||
| </view> | |||||
| </view> | |||||
| <!-- 空状态显示 --> | |||||
| <view v-if="!processedList || processedList.length === 0" class="empty-state"> | |||||
| <text class="empty-text">暂无订单数据</text> | |||||
| </view> | |||||
| <!-- 时间线主体 --> | |||||
| <view v-else class="timeline-body" v-for="(item, index) in processedList" :key="index"> | |||||
| <view class="timeline-line"></view> | |||||
| <view class="time-point"> | |||||
| <view class="time-icon"> | |||||
| <image src="/static/images/order/address.png" mode="aspectFit" class="time-image"></image> | |||||
| </view> | |||||
| <view class="time-text">{{ item.address }}</view> | |||||
| <view class="collapse-icon" @click="toggleServiceCard(index)"> | |||||
| {{ serviceCardCollapsed[index] ? '展开' : '收起' }} <text class="arrow" :class="{'arrow-up': !serviceCardCollapsed[index]}">▼</text> | |||||
| </view> | |||||
| </view> | |||||
| <!-- 服务内容卡片 --> | |||||
| <view v-if="!serviceCardCollapsed[index]" class="service-card"> | |||||
| <!-- 服务日期 --> | |||||
| <view class="service-section"> | |||||
| <view class="section-title"> | |||||
| <view class="title-indicator"></view> | |||||
| <text>服务日期</text> | |||||
| <text style="margin-left: auto;font-weight: 500;font-size: 24rpx;">订单编号:{{ item.orderId }}</text> | |||||
| </view> | |||||
| <view class="section-content date-content" :class="{bgSuccessQ : item.status}"> | |||||
| {{ item.fullDate }} | |||||
| </view> | |||||
| </view> | |||||
| <!-- 陪伴对象 --> | |||||
| <view class="service-section"> | |||||
| <view class="section-title"> | |||||
| <view class="title-indicator"></view> | |||||
| <text>陪伴对象</text> | |||||
| <view class="collapse-icon" @click="togglePetList(index)"> | |||||
| {{ petListCollapsed[index] ? '展开' : '收起' }} <text class="arrow" :class="{'arrow-up': !petListCollapsed[index]}">▼</text> | |||||
| </view> | |||||
| </view> | |||||
| <view class="section-content pet-list" :class="{bgSuccessQ : item.status}" v-if="!petListCollapsed[index]"> | |||||
| <view v-for="(pet, i) in item.petList" :key="i" class="pet-item"> | |||||
| <view class="pet-avatar"> | |||||
| <image :src="pet.avatar" mode="aspectFill" class="avatar-image"></image> | |||||
| </view> | |||||
| <view class="pet-info"> | |||||
| <view class="pet-name"> | |||||
| {{ pet.name }} | |||||
| <text class="pet-gender" :class="{'pet-gender-male': pet.gender === 'male', 'pet-gender-female': pet.gender === 'female'}"> | |||||
| {{ pet.gender === 'male' ? '♂' : '♀' }} | |||||
| </text> | |||||
| </view> | |||||
| <view class="pet-description"> | |||||
| {{ pet.breed }}{{ pet.bodyType }} | {{ pet.services.join('+') }} | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| <!-- 上门地址 --> | |||||
| <view class="service-section"> | |||||
| <view class="section-title"> | |||||
| <view class="title-indicator"></view> | |||||
| <text>上门地址</text> | |||||
| </view> | |||||
| <view class="section-content address-content" :class="{bgSuccessQ : item.status}"> | |||||
| {{ item.addressDetail }} | |||||
| </view> | |||||
| </view> | |||||
| <!-- 操作按钮 --> | |||||
| <view class="action-buttons"> | |||||
| <view class="btn btn-clock" :class="{bgSuccess : item.status}" @click="handleClock(item)">{{ item.status ? '打卡记录' : '打卡' }}</view> | |||||
| <view class="btn btn-clock" :class="{bgSuccess : item.status}" @click="handlePetFile(item)">宠物档案</view> | |||||
| <view class="btn btn-clock" :class="{bgSuccess : item.status}" @click="handleServiceFile(item)">服务档案</view> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| </template> | |||||
| <script setup> | |||||
| import { ref, computed } from 'vue'; | |||||
| import { getOrderServiceText, getProductNameText } from '@/utils/serviceTime.js'; | |||||
| // 定义组件属性 | |||||
| const props = defineProps({ | |||||
| date: { | |||||
| type: String, | |||||
| default: '2024-12-08' | |||||
| }, | |||||
| orderCount: { | |||||
| type: Number, | |||||
| default: 2 | |||||
| }, | |||||
| status : { | |||||
| type: Boolean, | |||||
| default: true | |||||
| }, | |||||
| current: { | |||||
| type: Number, | |||||
| default: 0 | |||||
| }, | |||||
| list: { | |||||
| type: Array, | |||||
| default: () => [] | |||||
| } | |||||
| }); | |||||
| // 宠物列表折叠状态 - 使用数组来单独控制每个卡片中的宠物列表 | |||||
| const petListCollapsed = ref([]); | |||||
| // 服务卡片折叠状态 - 使用数组来单独控制每个卡片 | |||||
| const serviceCardCollapsed = ref([]); | |||||
| // 切换宠物列表显示状态 | |||||
| const togglePetList = (index) => { | |||||
| if (petListCollapsed.value[index] === undefined) { | |||||
| petListCollapsed.value[index] = true; | |||||
| } else { | |||||
| petListCollapsed.value[index] = !petListCollapsed.value[index]; | |||||
| } | |||||
| }; | |||||
| // 切换服务卡片显示状态 | |||||
| const toggleServiceCard = (index) => { | |||||
| if (serviceCardCollapsed.value[index] === undefined) { | |||||
| serviceCardCollapsed.value[index] = true; | |||||
| } else { | |||||
| serviceCardCollapsed.value[index] = !serviceCardCollapsed.value[index]; | |||||
| } | |||||
| }; | |||||
| // 格式化日期 | |||||
| const formatDate = (dateString) => { | |||||
| const date = new Date(dateString); | |||||
| return { | |||||
| day: date.getDate().toString().padStart(2, '0'), | |||||
| month: (date.getMonth() + 1).toString().padStart(2, '0') | |||||
| }; | |||||
| }; | |||||
| // 处理订单数据,转换为组件所需格式 | |||||
| const processedList = computed(() => { | |||||
| return props.list.map(order => { | |||||
| // 获取所有宠物信息 | |||||
| const petList = []; | |||||
| let orderId = 0 | |||||
| let serviceId = 0 | |||||
| let status = 1 | |||||
| if (order.appletOrderItemDate && order.appletOrderItemDate.length > 0) { | |||||
| order.appletOrderItemDate.forEach(item => { | |||||
| if (item.orderServiceList && item.orderServiceList.petVo) { | |||||
| const pet = item.orderServiceList.petVo; | |||||
| const services = []; | |||||
| // 获取服务名称 | |||||
| if (item.orderItemList && item.orderItemList.length > 0) { | |||||
| item.orderItemList.forEach(orderItem => { | |||||
| services.push(orderItem.productName); | |||||
| orderId = orderItem.orderId | |||||
| }); | |||||
| } | |||||
| serviceId = item.id | |||||
| if(item.status == 0){ | |||||
| status = 0 | |||||
| } | |||||
| petList.push({ | |||||
| name: pet.name, | |||||
| serviceId : item.id, | |||||
| gender: pet.gender === '男生' ? 'male' : 'female', | |||||
| breed: pet.breed, | |||||
| bodyType: `(${pet.bodyType})`, | |||||
| services: services, | |||||
| avatar: pet.photo || (pet.petType === 'dog' ? '/static/images/ydd/dog.png' : '/static/images/ydd/cat.png') | |||||
| }); | |||||
| } | |||||
| }); | |||||
| } | |||||
| return { | |||||
| id: order.orderId, | |||||
| orderId, | |||||
| serviceId, | |||||
| status, | |||||
| address: order.cityAddress, | |||||
| addressDetail: order.address, | |||||
| fullDate: props.date.replace(/-/g, '/'), | |||||
| petList: petList | |||||
| }; | |||||
| }); | |||||
| }); | |||||
| // 按钮事件处理函数 | |||||
| const handleClock = (item) => { | |||||
| console.log(item); | |||||
| // 根据订单状态确定跳转路径 | |||||
| const paths = [ | |||||
| `/otherPages/myOrdersManage/clock/index?id=${item.orderId}&itemID=${item.id}&serviceId=${item.serviceId}`, | |||||
| `/otherPages/myOrdersManage/clock/index?isRead=true&id=${item.orderId}&itemID=${item.id}&serviceId=${item.serviceId}`, | |||||
| ]; | |||||
| uni.navigateTo({ | |||||
| url: props.status ? paths[0] : paths[1] | |||||
| }); | |||||
| }; | |||||
| const handlePetFile = (item) => { | |||||
| uni.navigateTo({ | |||||
| url: "/otherPages/orderTakingManage/pet/index?id=" + item.orderId | |||||
| }); | |||||
| }; | |||||
| const handleServiceFile = (item) => { | |||||
| uni.navigateTo({ | |||||
| url: "/otherPages/myOrdersManage/service/index?id=" + item.orderId | |||||
| }); | |||||
| }; | |||||
| function getTopBgColor(){ | |||||
| return props.status ? '#FFAA48' : '#4CD964'; | |||||
| } | |||||
| </script> | |||||
| <style lang="scss" scoped> | |||||
| .bgSuccess{ | |||||
| background-color: #4CD964 !important; | |||||
| } | |||||
| .bgSuccessQ{ | |||||
| background-color: #4CD96422 !important; | |||||
| } | |||||
| .timeline-container { | |||||
| position: relative; | |||||
| padding: 20rpx; | |||||
| margin-bottom: 30rpx; | |||||
| .empty-state { | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| padding: 80rpx 40rpx; | |||||
| .empty-image { | |||||
| width: 200rpx; | |||||
| height: 200rpx; | |||||
| margin-bottom: 20rpx; | |||||
| } | |||||
| .empty-text { | |||||
| color: #999; | |||||
| font-size: 28rpx; | |||||
| } | |||||
| } | |||||
| .date-header { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| margin-bottom: 20rpx; | |||||
| .date-box { | |||||
| width: 80rpx; | |||||
| background-color: #ffffff; | |||||
| border: 2px solid #333; | |||||
| border-radius: 0; | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| justify-content: center; | |||||
| align-items: center; | |||||
| margin-right: 20rpx; | |||||
| box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05); | |||||
| border-radius: 14rpx; | |||||
| .date-box-color{ | |||||
| height: 20rpx; | |||||
| width: 100%; | |||||
| border-top-left-radius: 14rpx; | |||||
| border-top-right-radius: 14rpx; | |||||
| position: relative; | |||||
| &::before{ | |||||
| content: ''; | |||||
| display: block; | |||||
| background-color: #ddd; | |||||
| width: 100%; | |||||
| height: 26rpx; | |||||
| top: 100%; | |||||
| left: 0; | |||||
| position: absolute; | |||||
| } | |||||
| } | |||||
| .date-month-day { | |||||
| position: relative; | |||||
| font-size: 26rpx; | |||||
| font-weight: bold; | |||||
| color: #333; | |||||
| height: 50rpx; | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| justify-content: center; | |||||
| } | |||||
| } | |||||
| .status-tag { | |||||
| background-color: #4CD96422; | |||||
| color: #4CD964; | |||||
| border: 4rpx solid #4CD964; | |||||
| padding: 16rpx 26rpx; | |||||
| border-radius: 14rpx; | |||||
| font-size: 26rpx; | |||||
| display: flex; | |||||
| align-items: center; | |||||
| position: relative; | |||||
| margin-left: 20rpx; | |||||
| .status-icon { | |||||
| width: 32rpx; | |||||
| height: 32rpx; | |||||
| margin-right: 8rpx; | |||||
| } | |||||
| &::after{ | |||||
| content: ''; | |||||
| display: block; | |||||
| position: absolute; | |||||
| width: 0; | |||||
| height: 0; | |||||
| top: 50%; | |||||
| transform: translateY(-50%); | |||||
| left: -16rpx; | |||||
| border-top: 16rpx solid transparent; | |||||
| border-bottom: 16rpx solid transparent; | |||||
| border-right: 16rpx solid #4CD964; | |||||
| } | |||||
| &::before{ | |||||
| content: ''; | |||||
| display: block; | |||||
| position: absolute; | |||||
| width: 0; | |||||
| height: 0; | |||||
| top: 50%; | |||||
| transform: translateY(-50%); | |||||
| left: -12rpx; | |||||
| border-top: 12rpx solid transparent; | |||||
| border-bottom: 12rpx solid transparent; | |||||
| border-right: 12rpx solid #4CD96422; | |||||
| z-index: 1; | |||||
| } | |||||
| } | |||||
| .status-tag-pending { | |||||
| background-color: #FFAA4822; | |||||
| color: #FFAA48; | |||||
| border-color: #FFAA48; | |||||
| &::after{ | |||||
| border-right-color: #FFAA48; | |||||
| } | |||||
| &::before{ | |||||
| border-right-color: #FFAA4822; | |||||
| } | |||||
| } | |||||
| } | |||||
| .timeline-body { | |||||
| position: relative; | |||||
| padding-left: 40rpx; | |||||
| padding-bottom: 40rpx; | |||||
| .timeline-line { | |||||
| position: absolute; | |||||
| left: 40rpx; | |||||
| top: 0; | |||||
| height: 100%; | |||||
| width: 0; | |||||
| border-left: 2rpx dashed #707070; | |||||
| border-left-style: dashed; | |||||
| border-image: repeating-linear-gradient(to bottom, #707070 0, #707070 8rpx, transparent 8rpx, transparent 20rpx) 1; | |||||
| z-index: 0; | |||||
| &::after{ | |||||
| content: ''; | |||||
| display: block; | |||||
| position: absolute; | |||||
| width: 8rpx; | |||||
| height: 8rpx; | |||||
| background-color: #000; | |||||
| border: 2rpx solid #707070; | |||||
| border-radius: 50%; | |||||
| left: -7rpx; | |||||
| top: 30rpx; | |||||
| } | |||||
| } | |||||
| .time-point { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| margin-bottom: 20rpx; | |||||
| position: relative; | |||||
| z-index: 1; | |||||
| .time-icon { | |||||
| width: 60rpx; | |||||
| height: 60rpx; | |||||
| background-color: #fff; | |||||
| border-radius: 50%; | |||||
| display: flex; | |||||
| justify-content: center; | |||||
| align-items: center; | |||||
| margin-right: 20rpx; | |||||
| position: relative; | |||||
| left: 20rpx; | |||||
| .time-image { | |||||
| width: 40rpx; | |||||
| height: 40rpx; | |||||
| } | |||||
| } | |||||
| .time-text { | |||||
| font-size: 28rpx; | |||||
| color: #333; | |||||
| margin-left: 20rpx; | |||||
| flex: 1; | |||||
| } | |||||
| .collapse-icon { | |||||
| font-size: 24rpx; | |||||
| color: #999; | |||||
| padding: 0 20rpx; | |||||
| .arrow { | |||||
| transition: transform 0.3s; | |||||
| display: inline-block; | |||||
| } | |||||
| .arrow-up { | |||||
| transform: rotate(180deg); | |||||
| } | |||||
| } | |||||
| } | |||||
| .service-card { | |||||
| background-color: #fff; | |||||
| border-radius: 12rpx; | |||||
| padding: 30rpx; | |||||
| box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); | |||||
| margin-left: 20rpx; | |||||
| .service-section { | |||||
| margin-bottom: 30rpx; | |||||
| .section-title { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| margin-bottom: 15rpx; | |||||
| .title-indicator { | |||||
| width: 6rpx; | |||||
| height: 30rpx; | |||||
| background-color: #FFAA48; | |||||
| margin-right: 15rpx; | |||||
| } | |||||
| text { | |||||
| font-size: 28rpx; | |||||
| color: #333; | |||||
| font-weight: bold; | |||||
| } | |||||
| .collapse-icon { | |||||
| margin-left: auto; | |||||
| font-size: 24rpx; | |||||
| color: #999; | |||||
| .arrow { | |||||
| transition: transform 0.3s; | |||||
| display: inline-block; | |||||
| } | |||||
| .arrow-up { | |||||
| transform: rotate(180deg); | |||||
| } | |||||
| } | |||||
| } | |||||
| .section-content { | |||||
| padding: 0 15rpx; | |||||
| background-color: #FFF9F0; | |||||
| } | |||||
| .date-content { | |||||
| background-color: #FFF9F0; | |||||
| padding: 20rpx; | |||||
| border-radius: 8rpx; | |||||
| font-size: 28rpx; | |||||
| color: #333; | |||||
| } | |||||
| .pet-list { | |||||
| padding: 15rpx; | |||||
| .pet-item { | |||||
| display: flex; | |||||
| margin-bottom: 20rpx; | |||||
| &:last-child { | |||||
| margin-bottom: 0; | |||||
| } | |||||
| .pet-avatar { | |||||
| width: 80rpx; | |||||
| height: 80rpx; | |||||
| border-radius: 50%; | |||||
| overflow: hidden; | |||||
| margin-right: 20rpx; | |||||
| .avatar-image { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| } | |||||
| } | |||||
| .pet-info { | |||||
| flex: 1; | |||||
| .pet-name { | |||||
| font-size: 28rpx; | |||||
| color: #333; | |||||
| margin-bottom: 8rpx; | |||||
| .pet-gender { | |||||
| display: inline-block; | |||||
| width: 32rpx; | |||||
| height: 32rpx; | |||||
| line-height: 32rpx; | |||||
| text-align: center; | |||||
| border-radius: 50%; | |||||
| color: #fff; | |||||
| font-size: 20rpx; | |||||
| margin-left: 10rpx; | |||||
| } | |||||
| .pet-gender-male { | |||||
| background-color: #4A90E2; | |||||
| } | |||||
| .pet-gender-female { | |||||
| background-color: #FF6B9A; | |||||
| } | |||||
| } | |||||
| .pet-description { | |||||
| font-size: 24rpx; | |||||
| color: #7D8196; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| .address-content { | |||||
| padding: 20rpx; | |||||
| border-radius: 8rpx; | |||||
| font-size: 28rpx; | |||||
| color: #7D8196; | |||||
| } | |||||
| } | |||||
| .action-buttons { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| .btn { | |||||
| width: 30%; | |||||
| height: 80rpx; | |||||
| line-height: 80rpx; | |||||
| text-align: center; | |||||
| border-radius: 40rpx; | |||||
| font-size: 28rpx; | |||||
| } | |||||
| .btn-clock { | |||||
| background-color: #FFAA48; | |||||
| color: #fff; | |||||
| } | |||||
| .btn-pet-file, .btn-service-file { | |||||
| background-color: #F6F7FB; | |||||
| color: #333; | |||||
| border: 1px solid #E5E6EB; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| </style> | |||||