| @ -0,0 +1,45 @@ | |||
| <template> | |||
| <button plain class="customer-service-btn" open-type="contact"> | |||
| <image src="@/static/images/ydd/chat.png" mode="widthFix"></image> | |||
| </button> | |||
| </template> | |||
| <script setup> | |||
| // 客服按钮组件 - 可以接收props来自定义位置等属性 | |||
| const props = defineProps({ | |||
| // 距离右边的距离 | |||
| right: { | |||
| type: String, | |||
| default: '20rpx' | |||
| }, | |||
| // 距离底部的距离 | |||
| bottom: { | |||
| type: String, | |||
| default: '400rpx' | |||
| }, | |||
| // 按钮宽度 | |||
| width: { | |||
| type: String, | |||
| default: '97rpx' | |||
| } | |||
| }) | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .customer-service-btn { | |||
| position: fixed; | |||
| right: v-bind(right); | |||
| bottom: v-bind(bottom); | |||
| z-index: 9999; | |||
| border: none; | |||
| padding: 0; | |||
| width: v-bind(width); | |||
| height: auto; | |||
| background: transparent; | |||
| image { | |||
| width: 100%; | |||
| height: auto; | |||
| } | |||
| } | |||
| </style> | |||
| @ -1,206 +0,0 @@ | |||
| <template> | |||
| <!-- <view>伴宠师认证</view> --> | |||
| <view class="page"> | |||
| <view class="box"> | |||
| <view class="content bg-fff"> | |||
| <view class="group" v-for="(group, gIdx) in list" :key="`group-${gIdx}`"> | |||
| <view class="label size-22 level" :style="{borderRadius:'10rpx'}"> | |||
| {{ group.title }} | |||
| </view> | |||
| <view > | |||
| <questionCard | |||
| v-for="(item, qIdx) in group.children" | |||
| :key="`${gIdx}-question-${qIdx}`" | |||
| :index="qIdx" | |||
| :data="item" | |||
| :type="item.type" | |||
| mode="display" | |||
| ></questionCard> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| <view class="footer-btn flex-colc"> | |||
| <view class="size-22 color-777 tips-rest"> | |||
| <!-- todo --> | |||
| 剩余考试机会:<text class="highlight">{{ restTimes }}</text>次 | |||
| </view> | |||
| <view class="btn" @click="onClick"> | |||
| 重新考试 | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script setup> | |||
| import { ref, computed } from 'vue' | |||
| import { onShow } from '@dcloudio/uni-app' | |||
| import { getQuestionList, getQuestionOptions, answeBaseByQuestionId, answeTrainByQuestionId, retakeExam } from '@/api/examination' | |||
| import { useStore } from 'vuex' | |||
| import questionCard from '../components/questionCard.vue'; | |||
| const store = useStore() | |||
| const configList = computed(() => { | |||
| return store.getters.configList | |||
| }) | |||
| const userInfo = computed(() => { | |||
| return store.state.user.userInfo | |||
| }) | |||
| const list = ref([ | |||
| { | |||
| title: '选择题', | |||
| children: [], | |||
| }, | |||
| { | |||
| title: '主观题', | |||
| children: [], | |||
| }, | |||
| ]) | |||
| const initList = async () => { | |||
| try { | |||
| let groups = [ | |||
| { title: '选择题', children: [], }, | |||
| { title: '主观题', children: [], }, | |||
| ] | |||
| let questions = await getQuestionList() | |||
| for (let i = 0; i < questions.length; i++) { | |||
| const { id, title, type } = questions[i] | |||
| let data = { id, title, type } | |||
| if (type === '基本') { | |||
| const options = (await getQuestionOptions({ questionId: id })).map(item => ({ id: item.id, title: item.title})) | |||
| data.options = options | |||
| // todo: 替换成批量查询接口 | |||
| const { answer, answerId } = await answeBaseByQuestionId({ questionId: id }) | |||
| if (answer.isTrue) { | |||
| continue | |||
| } | |||
| data.answer = answer.id | |||
| data.value = answerId | |||
| data.isTrue = answer.isTrue | |||
| groups[0].children.push(data) | |||
| } else { | |||
| // todo: 替换成批量查询接口 | |||
| const { answer, remark } = await answeTrainByQuestionId({ questionId: id }) | |||
| if (!remark) { | |||
| continue | |||
| } | |||
| data.answer = remark | |||
| data.value = answer | |||
| groups[1].children.push(data) | |||
| } | |||
| } | |||
| list.value = groups.filter(group => group.children.length > 0) | |||
| console.log('--list', list.value) | |||
| } catch (err) { | |||
| console.log('--initList--err', err) | |||
| } | |||
| } | |||
| const restTimes = ref() | |||
| onShow(() => { | |||
| // todo: fetch | |||
| restTimes.value = parseInt(configList.value.pet_work_num.paramValueText || 0) | |||
| initList() | |||
| }) | |||
| const onClick = () => { | |||
| retakeExam({ | |||
| userId : userInfo.value.userId | |||
| }).then(res => { | |||
| uni.reLaunch({ | |||
| url: "/otherPages/authentication/examination/start" | |||
| // todo: check | |||
| // url: "/otherPages/authentication/list/index" | |||
| }) | |||
| }) | |||
| } | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| $bar-height: 163rpx; | |||
| .page { | |||
| padding-bottom: $bar-height; | |||
| } | |||
| .box { | |||
| // background-image: linear-gradient(to bottom, #ffbf60, #f2f2f2); | |||
| padding: 16rpx; | |||
| .step { | |||
| width: 720rpx; | |||
| height: 32rpx; | |||
| border-radius: 32rpx; | |||
| background-color: #D9D9D9; | |||
| .in { | |||
| width: 50%; | |||
| height: 32rpx; | |||
| background-color: #ffbf60; | |||
| border-radius: 32rpx; | |||
| } | |||
| } | |||
| } | |||
| .content { | |||
| border-radius: 20rpx; | |||
| padding: 15rpx 20rpx; | |||
| .label { | |||
| width: 86rpx; | |||
| padding: 5rpx 15rpx; | |||
| color: #fff; | |||
| background-color: #FFBF60; | |||
| justify-content: center; | |||
| } | |||
| } | |||
| .group + .group { | |||
| margin-top: 68rpx; | |||
| } | |||
| .level { | |||
| display: flex; | |||
| } | |||
| .footer-btn { | |||
| height: $bar-height; | |||
| } | |||
| .tips { | |||
| &-rest { | |||
| color: #707070; | |||
| font-size: 22rpx; | |||
| margin: 9rpx 0 13rpx 0; | |||
| .highlight { | |||
| color: #FF8DC6; | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @ -1,8 +0,0 @@ | |||
| <template> | |||
| </template> | |||
| <script> | |||
| </script> | |||
| <style> | |||
| </style> | |||
| @ -1,462 +0,0 @@ | |||
| <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> | |||
| @ -1,66 +0,0 @@ | |||
| <template> | |||
| <view class="container"> | |||
| <view v-for="(item,index) in questions" :key="index"> | |||
| <view>{{ item.name }}</view> | |||
| <up-radio-group | |||
| v-model="radioValue[index]" | |||
| placement="column" | |||
| iconPlacement="right" | |||
| > | |||
| <up-radio | |||
| :customStyle="{marginBottom: '8px'}" | |||
| v-for="(item, index) in item.option" | |||
| :key="index" | |||
| :label="item.name" | |||
| :name="item.value" | |||
| @change="radioChange" | |||
| > | |||
| </up-radio> | |||
| </up-radio-group> | |||
| </view> | |||
| <up-button text="提交1" open-type="getPhoneNumber" | |||
| @getphonenumber="handleSubmit"></up-button> | |||
| </view> | |||
| </template> | |||
| <script setup> | |||
| import {useMixin} from "@/utils/useMixin"; | |||
| import {ref} from "vue"; | |||
| const {questions} = useMixin() | |||
| const radioValue = ref([]) | |||
| const handleSubmit = (e) => { | |||
| wx.login({ | |||
| success: res => { | |||
| if (res.code) { | |||
| // 发起网络请求,将 code 发送到服务器进行登录 | |||
| wx.request({ | |||
| url: 'https://yourserver.com/getPhoneNumber', // 替换为你的服务器地址 | |||
| method: 'POST', | |||
| data: { | |||
| code: res.code, | |||
| encryptedData: e.detail.encryptedData, | |||
| iv: e.detail.iv | |||
| }, | |||
| success: function(response) { | |||
| // 服务器返回解密后的手机号 | |||
| console.log(response.data.phoneNumber); | |||
| } | |||
| }); | |||
| } else { | |||
| console.log('登录失败!' + res.errMsg); | |||
| } | |||
| } | |||
| }); | |||
| } | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| .container { | |||
| padding: 20px; | |||
| font-size: 14px; | |||
| line-height: 24px; | |||
| background: #FFFFFF; | |||
| } | |||
| </style> | |||
| @ -1,754 +0,0 @@ | |||
| <template> | |||
| <!-- <div>服务档案</div> --> | |||
| <view class="box box-size"> | |||
| <view style="margin: 32rpx 0;"> | |||
| <up-steps :current="currentStep" activeColor="#FFA042" inactiveColor="#E5E5E5" class="up-steps-custom"> | |||
| <up-steps-item title="喂养要求" :titleStyle="{fontSize: '32rpx', fontWeight: 'bold', color: currentStep === 0 ? '#FFA042' : '#999999'}"></up-steps-item> | |||
| <up-steps-item title="清洁要求" :titleStyle="{fontSize: '32rpx', fontWeight: 'bold', color: currentStep === 1 ? '#FFA042' : '#999999'}"></up-steps-item> | |||
| <up-steps-item title="附加服务" :titleStyle="{fontSize: '32rpx', fontWeight: 'bold', color: currentStep === 2 ? '#FFA042' : '#999999'}"></up-steps-item> | |||
| </up-steps> | |||
| </view> | |||
| <!-- v-if="detail.includeDryFood | |||
| || detail.includeWetFood | |||
| || detail.includeHomemadeFood | |||
| || detail.includeRawMeat | |||
| || detail.includeHealthSupplements | |||
| || detail.includeSnacks | |||
| " --> | |||
| <!-- 喂养要求内容 --> | |||
| <view v-if="currentStep === 0"> | |||
| <view class="top box-size" :style="{borderRadius:'16rpx'}"> | |||
| <view class="form-title"> | |||
| 本次喂养过程中,您需要喂养的食品包括 | |||
| </view> | |||
| <view v-if="!detail.includeDryFood && !detail.includeWetFood && !detail.includeHomemadeFood && !detail.includeRawMeat && !detail.includeHealthSupplements && !detail.includeSnacks" class="empty-state-full"> | |||
| <up-icon name="info-circle" size="60rpx" color="#999999"></up-icon> | |||
| <text class="empty-text-full">暂无喂养食品</text> | |||
| </view> | |||
| <view class=" mt32 ml10" v-if="detail.includeDryFood"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 干粮(主粮、冻干等) | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 干粮摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.dryFoodLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.dryFoodLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24 flex-between"> | |||
| <view :style="{color:'#999999'}">干粮喂养重量</view> | |||
| <view class="level"> | |||
| <view class="input_ mr10"> | |||
| <input type="text" v-model="detail.dryFoodFeedWeight" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| <view>/次(单位:克)</view> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24 flex-between"> | |||
| <view :style="{color:'#999999'}">干粮喂养频率</view> | |||
| <view class="level"> | |||
| <view class="input_ mr10"> | |||
| <input type="text" v-model="detail.dryFoodFeedFrequencyDays" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| <view>天</view> | |||
| <view class="input_ mr10"> | |||
| <input type="text" v-model="detail.dryFoodFeedFrequencyTimes" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| <view>次</view> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 备注信息 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" disabled v-model="detail.dryFoodRemarks" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeDryFood"></view> | |||
| <view class=" mt32 ml10" v-if="detail.includeWetFood"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 湿粮(罐头等) | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 湿粮摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.wetFoodLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.wetFoodLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24 flex-between"> | |||
| <view :style="{color:'#999999'}">湿粮喂养重量</view> | |||
| <view class="level"> | |||
| <view class="input_ mr10"> | |||
| <input type="text" disabled v-model="detail.wetFoodFeedWeight" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| <view>/次(单位:罐)</view> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24 flex-between"> | |||
| <view :style="{color:'#999999'}">湿粮喂养频率</view> | |||
| <view class="level"> | |||
| <view class="input_ mr10"> | |||
| <input type="text" disabled v-model="detail.wetFoodFeedFrequencyDays" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| <view>天</view> | |||
| <view class="input_ mr10"> | |||
| <input type="text" disabled v-model="detail.wetFoodFeedFrequencyTimes" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| <view>次</view> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 备注信息 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.wetFoodRemarks" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeWetFood"></view> | |||
| <view class=" mt32 ml10" v-if="detail.includeHomemadeFood"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 自制食品 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 自制食品摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.homemadeFoodLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.homemadeFoodLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 自制饭制作和喂食说明 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.homemadeFoodInstructions" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeHomemadeFood"></view> | |||
| <view class=" mt32 ml10" v-if="detail.includeRawMeat"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 生骨肉 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 生骨肉摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.rawMeatLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.rawMeatLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 生骨肉喂食说明 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" disabled v-model="detail.rawMeatFeedingInstructions" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeRawMeat"></view> | |||
| <view class=" mt32 ml10" v-if="detail.includeHealthSupplements"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 保健品 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 保健品摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.healthSupplementsLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.healthSupplementsLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 保健品喂养方式 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" disabled v-model="detail.healthSupplementsFeedingInstructions" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeHealthSupplements"></view> | |||
| <view class=" mt32 ml10" v-if="detail.includeSnacks"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 零食 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 零食摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.snacksLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.snacksLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 零食喂养方式 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" disabled v-model="detail.snacksFeedingMethod" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeSnacks"></view> | |||
| </view> | |||
| <view class="top mt24" :style="{borderRadius:'16rpx'}"> | |||
| <view class="form-title"> | |||
| 食盆/自动喂食器 | |||
| </view> | |||
| <view class=" mt32 ml10"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 喂食器具摆放位置 | |||
| </view> | |||
| <view class="mt20"> | |||
| <template v-if="handleImageUrl(detail.feedingTrayAutomaticFeederLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.feedingTrayAutomaticFeederLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 备注信息 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" disabled v-model="detail.feedingTrayAutomaticFeederRemarks" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| <view class="top mt24" :style="{borderRadius:'16rpx'}"> | |||
| <view class="form-title"> | |||
| 饮用水 | |||
| </view> | |||
| <view class=" mt32 ml10"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 饮用水摆放位置 | |||
| </view> | |||
| <view class="mt20"> | |||
| <template v-if="handleImageUrl(detail.waterBowlLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.waterBowlLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 饮用水添加要求 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" disabled v-model="detail.waterBowlRemarks" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| <view class="top mt24" :style="{borderRadius:'16rpx'}"> | |||
| <view class="form-title"> | |||
| 其他补充信息(非必填) | |||
| </view> | |||
| <view class=" mt32 ml10"> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 备注信息 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" disabled v-model="detail.otherRemark" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| <!-- 清洁要求内容 --> | |||
| <view v-if="currentStep === 1"> | |||
| <view class="top box-size" :style="{borderRadius:'16rpx'}"> | |||
| <view class="form-title"> | |||
| 您需要的清洁内容 | |||
| </view> | |||
| <view v-if="!detail.includeCatLitter && !detail.includeUrinePad" class="empty-state-full"> | |||
| <up-icon name="info-circle" size="60rpx" color="#999999"></up-icon> | |||
| <text class="empty-text-full">暂无清洁内容</text> | |||
| </view> | |||
| <view class="mt32 ml10" v-if="detail.includeCatLitter"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 猫砂 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 猫砂摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.catLitterLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.catLitterLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24 flex-between"> | |||
| <view :style="{color:'#999999'}">猫砂更换频率</view> | |||
| <view class="level"> | |||
| <view class="input_ mr10"> | |||
| <input type="text" disabled v-model="detail.catLitterChangeFrequencyDays" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| <view>天</view> | |||
| <view class="input_ mr10"> | |||
| <input type="text" disabled v-model="detail.catLitterChangeFrequencyTimes" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| <view>次</view> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 备注信息 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.catLitterRemarks" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.includeCatLitter"></view> | |||
| <view class="mt32 ml10" v-if="detail.includeUrinePad"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 尿垫 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 尿垫摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.urinePadLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.urinePadLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24 flex-between"> | |||
| <view :style="{color:'#999999'}">更换尿垫</view> | |||
| <view class="level"> | |||
| <view class="input_ mr10"> | |||
| <input type="text" v-model="detail.urinePadChangeFrequency" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| <view>是</view> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24 flex-between"> | |||
| <view :style="{color:'#999999'}">尿垫更换频率</view> | |||
| <view class="level"> | |||
| <view class="input_ mr10"> | |||
| <input type="text" v-model="detail.urinePadChangeFrequencyDays" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| <view>天</view> | |||
| <view class="input_ mr10"> | |||
| <input type="text" v-model="detail.urinePadChangeFrequencyTimes" :style="{width:'80rpx',height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| <view>次</view> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 备注信息 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" disabled v-model="detail.urinePadRemarks" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| <!-- 附加服务内容 --> | |||
| <view v-if="currentStep === 2"> | |||
| <view class="top box-size" :style="{borderRadius:'16rpx'}"> | |||
| <view class="form-title"> | |||
| 您需要的附加服务 | |||
| </view> | |||
| <view v-if="!detail.additionalService || (!detail.playtimeService && !detail.furCleaningService && !detail.groomingService && !detail.deepCleaningService)" class="empty-state-full"> | |||
| <up-icon name="info-circle" size="60rpx" color="#999999"></up-icon> | |||
| <text class="empty-text-full">暂无附加服务</text> | |||
| </view> | |||
| <!-- 陪玩服务 --> | |||
| <view class="mt32 ml10" v-if="detail.playtimeService"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 陪玩 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 陪玩用具摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.playtimeToolsLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.playtimeToolsLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 您希望的互动方式和要求 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.playtimeRequirements" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.playtimeService"></view> | |||
| <!-- 活动区吸毛 --> | |||
| <view class="mt32 ml10" v-if="detail.furCleaningService"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 活动区吸毛 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 吸毛用品摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.furCleaningToolsLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.furCleaningToolsLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 请说明吸毛区域及要求 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.furCleaningRequirements" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.furCleaningService"></view> | |||
| <!-- 毛发梳理 --> | |||
| <view class="mt32 ml10" v-if="detail.groomingService"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 毛发梳理 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 毛发梳理用品摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.groomingToolsLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.groomingToolsLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 请说明毛发梳理要求(如: 天数,次数等) | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.groomingRequirements" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.groomingService"></view> | |||
| <!-- 食具深度清洁 --> | |||
| <view class="mt32 ml10" v-if="detail.deepCleaningService"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 食具深度清洁 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 需深度清洁的用具及消毒剂摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <template v-if="handleImageUrl(detail.deepCleaningToolsLocation).length > 0"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.deepCleaningToolsLocation)" :key="index"></u-image> | |||
| </template> | |||
| <view v-else class="empty-state"> | |||
| <up-icon name="info-circle" size="40rpx" color="#999999"></up-icon> | |||
| <text class="empty-text">暂无图片</text> | |||
| </view> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 请说明食具/饮水机深度清洁要求(如清洁方式、消毒剂使用方式、频率等) | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.deepCleaningRemarks" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.deepCleaningService"></view> | |||
| <!-- 喂药上药 --> | |||
| <view class="mt32 ml10" v-if="detail.administerMedication"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 喂药上药 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 药品摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.administerMedicationLocation)" :key="index"></u-image> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 请说明药品使用方式 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" disabled v-model="detail.administerMedicationRemarks" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <view class="line" :style="{borderRadius:'1rpx'}" v-if="detail.administerMedication"></view> | |||
| <!-- 遛狗服务 --> | |||
| <view class="mt32 ml10" v-if="detail.walkDog"> | |||
| <view class="color-A55 size-30 fw400"> | |||
| 遛狗服务 | |||
| </view> | |||
| <view class="size-30 mt32 fw400"> | |||
| 遛狗用品摆放位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.walkDogToolsLocation)" :key="index"></u-image> | |||
| </view> | |||
| <!-- 雨天遛狗 --> | |||
| <view v-if="detail.walkDogRainyDay"> | |||
| <view class="size-30 mt32 fw400"> | |||
| 狗狗雨具位置 | |||
| </view> | |||
| <view class="img mt20"> | |||
| <u-image class="img" :src="item" mode="aspectFill" width="173rpx" height="230rpx" @click="previewImage(item)" v-for="(item, index) in handleImageUrl(detail.dogRainGearLocation)" :key="index"></u-image> | |||
| </view> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 下雨天遛狗要求 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.walkDogRainRemark" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <!-- 遛狗时长 --> | |||
| <view v-if="detail.isWalkDogDuration"> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 遛狗时长 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.walkDogDuration" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <!-- 狗狗行为 --> | |||
| <view v-if="detail.isDogBehavior"> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 狗狗行为 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.dogBehavior" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <!-- 偏好区域 --> | |||
| <view v-if="detail.isFavoriteRegion"> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 偏好区域 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.favoriteRegion" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| <!-- 偏好路线 --> | |||
| <view v-if="detail.isFavoriteRoute"> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 偏好路线 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.favoriteRoute" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| <!-- 附加服务备注 --> | |||
| <view class="mt32 ml10"> | |||
| <view class="size-30 mt24" :style="{color:'#999999'}"> | |||
| 附加服务备注 | |||
| </view> | |||
| <view class="notes mt16 box-size ml--15" :style="{borderRadius:'16rpx'}"> | |||
| <input type="text" v-model="detail.additionalServicesRemarks" :style="{height:'50rpx',fontSize:'30rpx'}"> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| <!-- 底部按钮 --> | |||
| <view class="buttom_ mt60 box-size level" v-if="currentStep > 0"> | |||
| <view class="buttom_item level size-30" :style="{borderRadius:'41rpx',color:'#fff'}" @click="prevStep"> | |||
| 上一页 | |||
| </view> | |||
| <view class="buttom_item level size-30" :style="{borderRadius:'41rpx',color:'#fff'}" @click="nextStep"> | |||
| {{ currentStep === 2 ? '完成' : '下一页' }} | |||
| </view> | |||
| </view> | |||
| <view class="buttom mt60 box-size" v-else> | |||
| <view class="buttom-item level size-30" :style="{borderRadius:'41rpx',color:'#fff'}" @click="nextStep"> | |||
| 下一页 | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import { | |||
| getpetList | |||
| } from "@/api/pet/index.js" | |||
| import { | |||
| getDictList | |||
| } from "@/api/system/user.js" | |||
| import { | |||
| getPetCareByPetId | |||
| } from "@/api/order/order.js"; | |||
| export default { | |||
| data() { | |||
| return { | |||
| detail: {}, | |||
| show: false, | |||
| petTypes: [], | |||
| petType: '', | |||
| orderId: 0, | |||
| currentStep: 0, // 当前步骤,0:喂养要求,1:清洁要求,2:附加服务 | |||
| } | |||
| }, | |||
| onLoad(options) { | |||
| this.orderId = options.petId | |||
| this.getPetCareByPetId() | |||
| // 如果有指定步骤,则切换到对应步骤 | |||
| if (options.step) { | |||
| this.currentStep = parseInt(options.step) | |||
| } | |||
| }, | |||
| methods: { | |||
| getPetCareByPetId() { | |||
| getPetCareByPetId(this.orderId).then(res => { | |||
| this.detail = res.data || {} | |||
| }) | |||
| }, | |||
| handleImageUrl(url) { | |||
| if (!url) return [] | |||
| return url.split(',') | |||
| }, | |||
| previewImage(url) { | |||
| uni.previewImage({ | |||
| urls: [url] | |||
| }) | |||
| }, | |||
| // 切换到上一步 | |||
| prevStep() { | |||
| if (this.currentStep > 0) { | |||
| this.currentStep--; | |||
| } | |||
| }, | |||
| // 切换到下一步 | |||
| nextStep() { | |||
| if (this.currentStep < 2) { | |||
| this.currentStep++; | |||
| } else { | |||
| // 如果是最后一步,可以提交表单或返回上一页 | |||
| uni.navigateBack(); | |||
| } | |||
| }, | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| @import"detail.scss"; | |||
| @import"steps-custom.scss"; | |||
| .empty-state { | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| justify-content: center; | |||
| width: 173rpx; | |||
| height: 230rpx; | |||
| background-color: #f5f5f5; | |||
| border-radius: 8rpx; | |||
| } | |||
| .empty-text { | |||
| margin-top: 10rpx; | |||
| font-size: 24rpx; | |||
| color: #999999; | |||
| } | |||
| .empty-state-full { | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| justify-content: center; | |||
| width: 100%; | |||
| height: 200rpx; | |||
| background-color: #f9f9f9; | |||
| border-radius: 8rpx; | |||
| margin-top: 20rpx; | |||
| } | |||
| .empty-text-full { | |||
| margin-top: 20rpx; | |||
| font-size: 28rpx; | |||
| color: #999999; | |||
| } | |||
| /* 自定义步骤条样式 */ | |||
| :deep(.up-steps-custom) { | |||
| .u-steps-item__wrapper { | |||
| width: 40rpx !important; | |||
| height: 40rpx !important; | |||
| &__circle { | |||
| width: 40rpx !important; | |||
| height: 40rpx !important; | |||
| background: #E5E5E5; | |||
| &__text { | |||
| font-size: 24rpx; | |||
| } | |||
| } | |||
| } | |||
| .u-steps-item__line { | |||
| top: 20rpx !important; | |||
| height: 4rpx !important; | |||
| background: #E5E5E5 !important; | |||
| } | |||
| /* 激活状态样式 */ | |||
| .u-steps-item--active { | |||
| .u-steps-item__wrapper__circle { | |||
| background: linear-gradient(90deg, #FFA042, #FFD591) !important; | |||
| } | |||
| } | |||
| /* 已完成步骤前的线条样式 */ | |||
| .u-steps-item--process { | |||
| .u-steps-item__line { | |||
| background: linear-gradient(90deg, #FFA042, #FFD591) !important; | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @ -1,80 +0,0 @@ | |||
| <template> | |||
| <!-- <div>服务档案</div> --> | |||
| <view class="box box-size"> | |||
| <view class="container box-size level" | |||
| @click="toDetail" :style="{borderRadius:'16rpx'}"> | |||
| <view class="level"> | |||
| <view class="img"> | |||
| <image src="" mode=""></image> | |||
| </view> | |||
| <view class="text"> | |||
| 查看猫猫“小咪”的服务信息 | |||
| </view> | |||
| </view> | |||
| <view class="img_"> | |||
| <image src="/static/images/ydd/right.png" mode=""></image> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script setup> | |||
| function toDetail() { | |||
| uni.navigateTo({ | |||
| url: "/otherPages/myOrdersManage/service/detail" | |||
| }) | |||
| } | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| .box { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| padding: 2% 3%; | |||
| } | |||
| .box-size { | |||
| box-sizing: border-box; | |||
| } | |||
| .level { | |||
| display: flex; | |||
| } | |||
| .container { | |||
| width: 710rpx; | |||
| height: 121rpx; | |||
| background-color: #fff; | |||
| padding: 1% 4% 3% 2%; | |||
| justify-content: space-between; | |||
| margin-bottom: 20rpx; | |||
| } | |||
| .img image { | |||
| width: 104rpx; | |||
| height: 104rpx; | |||
| border-radius: 100rpx; | |||
| background-color: red; | |||
| margin-right: 20rpx; | |||
| } | |||
| .text { | |||
| font-size: 30rpx; | |||
| display: grid; | |||
| place-items: center; | |||
| } | |||
| .img_ { | |||
| display: grid; | |||
| place-items: center; | |||
| } | |||
| .img_ image { | |||
| width: 40rpx; | |||
| height: 40rpx; | |||
| /* background-color: red; */ | |||
| } | |||
| </style> | |||
| @ -1,66 +0,0 @@ | |||
| <template> | |||
| <view class="container"> | |||
| <view v-for="(item,index) in questions" :key="index"> | |||
| <view>{{ item.name }}</view> | |||
| <up-radio-group | |||
| v-model="radioValue[index]" | |||
| placement="column" | |||
| iconPlacement="right" | |||
| > | |||
| <up-radio | |||
| :customStyle="{marginBottom: '8px'}" | |||
| v-for="(item, index) in item.option" | |||
| :key="index" | |||
| :label="item.name" | |||
| :name="item.value" | |||
| @change="radioChange" | |||
| > | |||
| </up-radio> | |||
| </up-radio-group> | |||
| </view> | |||
| <up-button text="提交1" open-type="getPhoneNumber" | |||
| @getphonenumber="handleSubmit"></up-button> | |||
| </view> | |||
| </template> | |||
| <script setup> | |||
| import {useMixin} from "@/utils/useMixin"; | |||
| import {ref} from "vue"; | |||
| const {questions} = useMixin() | |||
| const radioValue = ref([]) | |||
| const handleSubmit = (e) => { | |||
| wx.login({ | |||
| success: res => { | |||
| if (res.code) { | |||
| // 发起网络请求,将 code 发送到服务器进行登录 | |||
| wx.request({ | |||
| url: 'https://yourserver.com/getPhoneNumber', // 替换为你的服务器地址 | |||
| method: 'POST', | |||
| data: { | |||
| code: res.code, | |||
| encryptedData: e.detail.encryptedData, | |||
| iv: e.detail.iv | |||
| }, | |||
| success: function(response) { | |||
| // 服务器返回解密后的手机号 | |||
| console.log(response.data.phoneNumber); | |||
| } | |||
| }); | |||
| } else { | |||
| console.log('登录失败!' + res.errMsg); | |||
| } | |||
| } | |||
| }); | |||
| } | |||
| </script> | |||
| <style> | |||
| .container { | |||
| padding: 20px; | |||
| font-size: 14px; | |||
| line-height: 24px; | |||
| background: #FFFFFF; | |||
| } | |||
| </style> | |||
| @ -1,9 +0,0 @@ | |||
| <template> | |||
| <div>保证金明细</div> | |||
| </template> | |||
| <script> | |||
| </script> | |||
| <style> | |||
| </style> | |||
| @ -1,9 +0,0 @@ | |||
| <template> | |||
| <div>保证金/保证金退还</div> | |||
| </template> | |||
| <script> | |||
| </script> | |||
| <style> | |||
| </style> | |||
| @ -1,9 +0,0 @@ | |||
| <template> | |||
| <div>交易明细</div> | |||
| </template> | |||
| <script> | |||
| </script> | |||
| <style> | |||
| </style> | |||
| @ -1,9 +0,0 @@ | |||
| <template> | |||
| <div>提现</div> | |||
| </template> | |||
| <script> | |||
| </script> | |||
| <style> | |||
| </style> | |||
| @ -1,66 +0,0 @@ | |||
| <template> | |||
| <view class="container"> | |||
| <view v-for="(item,index) in questions" :key="index"> | |||
| <view>{{ item.name }}</view> | |||
| <up-radio-group | |||
| v-model="radioValue[index]" | |||
| placement="column" | |||
| iconPlacement="right" | |||
| > | |||
| <up-radio | |||
| :customStyle="{marginBottom: '8px'}" | |||
| v-for="(item, index) in item.option" | |||
| :key="index" | |||
| :label="item.name" | |||
| :name="item.value" | |||
| @change="radioChange" | |||
| > | |||
| </up-radio> | |||
| </up-radio-group> | |||
| </view> | |||
| <up-button text="提交1" open-type="getPhoneNumber" | |||
| @getphonenumber="handleSubmit"></up-button> | |||
| </view> | |||
| </template> | |||
| <script setup> | |||
| import {useMixin} from "@/utils/useMixin"; | |||
| import {ref} from "vue"; | |||
| const {questions} = useMixin() | |||
| const radioValue = ref([]) | |||
| const handleSubmit = (e) => { | |||
| wx.login({ | |||
| success: res => { | |||
| if (res.code) { | |||
| // 发起网络请求,将 code 发送到服务器进行登录 | |||
| wx.request({ | |||
| url: 'https://yourserver.com/getPhoneNumber', // 替换为你的服务器地址 | |||
| method: 'POST', | |||
| data: { | |||
| code: res.code, | |||
| encryptedData: e.detail.encryptedData, | |||
| iv: e.detail.iv | |||
| }, | |||
| success: function(response) { | |||
| // 服务器返回解密后的手机号 | |||
| console.log(response.data.phoneNumber); | |||
| } | |||
| }); | |||
| } else { | |||
| console.log('登录失败!' + res.errMsg); | |||
| } | |||
| } | |||
| }); | |||
| } | |||
| </script> | |||
| <style> | |||
| .container { | |||
| padding: 20px; | |||
| font-size: 14px; | |||
| line-height: 24px; | |||
| background: #FFFFFF; | |||
| } | |||
| </style> | |||
| @ -1,316 +0,0 @@ | |||
| <template> | |||
| <view> | |||
| <view class="personal-pet-basic-info"> | |||
| <view class="personal-pet-info-title border-bottom"> | |||
| 宠物基本信息 | |||
| </view> | |||
| <u--form labelPosition="left" :model="model" ref="uForm"> | |||
| <u-form-item required label="昵称" :prop="`petBaseInfo.nickName`" labelWidth="80" borderBottom> | |||
| <u--input v-model="petBaseInfo.nickName" placeholder="请输入宠物昵称" @change="nameChange" | |||
| placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input> | |||
| </u-form-item> | |||
| <u-form-item label="性别" :prop="`petBaseInfo.sex`" labelWidth="80" borderBottom @click="showSex = true"> | |||
| <u--input v-model="sexText" disabled disabledColor="#ffffff" placeholder="请选择" | |||
| placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input> | |||
| <u-icon slot="right" name="arrow-right" color="#AAA"></u-icon> | |||
| </u-form-item> | |||
| <!-- 后台获取品种数据 赶进度这里先注释 --> | |||
| <!-- <u-form-item required label="品种" :prop="`petBaseInfo.breed`" labelWidth="80" borderBottom | |||
| @click="breedSelectOpen"> --> | |||
| <u-form-item required label="品种" :prop="`petBaseInfo.type`" labelWidth="80" borderBottom> | |||
| <u--input v-model="petBaseInfo.type" disabledColor="#ffffff" placeholder="请输入" | |||
| placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input> | |||
| <u-icon slot="right" name="arrow-right" color="#AAA"></u-icon> | |||
| </u-form-item> | |||
| <!-- 赶进度,先注释 --> | |||
| <!-- <u-form-item required label="体重" :prop="`petBaseInfo.weight`" labelWidth="80" borderBottom | |||
| @click="showWeight = true" ref="item1"> --> | |||
| <u-form-item required label="体重" :prop="`petBaseInfo.weight`" labelWidth="80" borderBottom ref="item1"> | |||
| <u--input v-model="petBaseInfo.weight" disabledColor="#ffffff" placeholder="请输入(单位kg)" | |||
| placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right" type="number"></u--input> | |||
| <u-icon slot="right" name="arrow-right" color="#AAA"></u-icon> | |||
| </u-form-item> | |||
| <u-form-item label="出生年月" :prop="`petBaseInfo.birthday`" labelWidth="80" borderBottom | |||
| @click="yearMonthOpen"> | |||
| <u--input v-model="petBaseInfo.birthday" disabled disabledColor="#ffffff" placeholder="请选择" | |||
| placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input> | |||
| <u-icon slot="right" name="arrow-right" color="#AAA"></u-icon> | |||
| </u-form-item> | |||
| <!-- 后台获取性格选择数据 感进度这里先注释 --> | |||
| <!-- <u-form-item required label="性格" :prop="`petBaseInfo.personality`" labelWidth="80" borderBottom | |||
| @click="dispositionSelectOpen"> --> | |||
| <u-form-item required label="性格" :prop="`petBaseInfo.personality`" labelWidth="80" borderBottom> | |||
| <u--input v-model="petBaseInfo.personality" disabledColor="#ffffff" placeholder="请输入" | |||
| placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input> | |||
| <u-icon slot="right" name="arrow-right" color="#AAA"></u-icon> | |||
| </u-form-item> | |||
| </u--form> | |||
| </view> | |||
| <!-- 后台获取性格选择数据 感进度这里先注释 --> | |||
| <!-- <view style="background-color: #fffcf2;padding: 10px 20px;" v-show="showDisposition"> | |||
| <view style="height: 85%;"> | |||
| <u-checkbox-group v-model="petBaseInfo.personality" @change="checkboxChange" iconPlacement="left" | |||
| placement="row" style="flex-wrap: wrap;" activeColor="#ffbf60"> | |||
| <u-checkbox :customStyle="{margin: '8px'}" v-for="(item, index) in dispositionActions" :key="item" | |||
| :label="item" :name="item"> | |||
| </u-checkbox> | |||
| </u-checkbox-group> | |||
| </view> | |||
| </view> --> | |||
| <u-picker :show="showSex" :showToolbar='false' :columns="sexActions" @cancel="showSex = false" | |||
| :immediateChange="true" @change="sexSelect"></u-picker> | |||
| <u-picker :show="showWeight" :showToolbar='false' :columns="weightActions" @cancel="showWeight = false" | |||
| :immediateChange="true" @change="weightSelect"></u-picker> | |||
| <u-overlay :show="showBreed"> | |||
| <view class="breed-select" style="height: 60%;"> | |||
| <u-search shape="round" :show-action="false" v-model="searchValue" @change="searchBreed"></u-search> | |||
| <view style="padding: 10px;height: 80%;overflow: auto;"> | |||
| <u-radio-group v-model="tempBreed" placement="column"> | |||
| <u-radio v-for="(item,index) in searchBreedData" :customStyle="{marginBottom: '8px'}" | |||
| activeColor="#ffbf60" :label="item" :name="item" :key="item"></u-radio> | |||
| </u-radio-group> | |||
| </view> | |||
| <view class="personal-pet-breed-btns"> | |||
| <view class="personal-pet-breed-btn"> | |||
| <u-button color="#FFF4E4" @click="breedSelectClose"> | |||
| <view style="color: #A9A9A9;"> | |||
| 取消 | |||
| </view> | |||
| </u-button> | |||
| </view> | |||
| <view class="personal-pet-breed-btn" @click="breedSelectConfirm"> | |||
| <u-button color="#FFBF60"> | |||
| <view style="color: #fff;"> | |||
| 确定 | |||
| </view> | |||
| </u-button> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </u-overlay> | |||
| <u-datetime-picker :maxDate='getMaxDate()' :minDate="getMinDate()" :show="showBirthday" v-model="tempBirthday" | |||
| mode="year-month" @confirm="yearMonthConfirm" @cancel="yearMonthClose"></u-datetime-picker> | |||
| </view> | |||
| </template> | |||
| <script setup> | |||
| import { | |||
| ref, | |||
| onMounted, | |||
| watch, | |||
| computed | |||
| } from 'vue'; | |||
| // 定义 props | |||
| const props = defineProps({ | |||
| petType: { | |||
| type: String, | |||
| default: 'dog' | |||
| }, | |||
| petBaseInfo: { | |||
| type: Object, | |||
| default: () => ({ | |||
| nickName: '', | |||
| sex: '', | |||
| type: '', | |||
| weight: '', | |||
| birthday: '', | |||
| personality: '' | |||
| }) | |||
| } | |||
| }); | |||
| // 定义 emits | |||
| const emits = defineEmits(['update:petBaseInfo']); | |||
| // 定义响应式数据 | |||
| const showSex = ref(false); | |||
| const showBreed = ref(false); | |||
| const showWeight = ref(false); | |||
| const showBirthday = ref(false); | |||
| const showDisposition = ref(false); | |||
| const sexActions = ref([ | |||
| ['男生', '女生'] | |||
| ]); | |||
| const weightActions = ref([ | |||
| ['小型(<10 KG)', '中型(10~20KG)', '大型(20KG以上)'] | |||
| ]); | |||
| const dispositionActions = ref([]); | |||
| const breedData = ref([]); | |||
| const searchBreedData = ref([]); | |||
| const tempBreed = ref(''); | |||
| const searchValue = ref(''); | |||
| const tempBirthday = ref(''); | |||
| const tempDisposition = ref(''); | |||
| const uForm = ref(null); | |||
| const item1 = ref(null); | |||
| const sexText = computed(() => { | |||
| const sexMap = { | |||
| 0: '男生', | |||
| 1: '女生' | |||
| }; | |||
| return sexMap[props.petBaseInfo.sex] || ''; | |||
| }); | |||
| // 定义方法 | |||
| const nameChange = () => { | |||
| updatePetBaseInfo(); | |||
| }; | |||
| const sexSelect = (e) => { | |||
| props.petBaseInfo.sex = e.index; | |||
| showSex.value = false; | |||
| updatePetBaseInfo(); | |||
| }; | |||
| const weightSelect = (e) => { | |||
| props.petBaseInfo.weight = e.value[0]; | |||
| showWeight.value = false; | |||
| updatePetBaseInfo(); | |||
| }; | |||
| const breedSelectOpen = () => { | |||
| searchBreedData.value = breedData.value; | |||
| searchValue.value = ''; | |||
| tempBreed.value = props.petBaseInfo.breed; | |||
| showBreed.value = true; | |||
| }; | |||
| const searchBreed = () => { | |||
| if (searchValue.value && searchValue.value !== '') { | |||
| searchBreedData.value = breedData.value.filter(e => e.includes(searchValue.value)); | |||
| } else { | |||
| searchBreedData.value = breedData.value; | |||
| } | |||
| }; | |||
| const breedSelectClose = () => { | |||
| tempBreed.value = ''; | |||
| showBreed.value = false; | |||
| }; | |||
| const breedSelectConfirm = () => { | |||
| props.petBaseInfo.type = tempBreed.value; | |||
| showBreed.value = false; | |||
| updatePetBaseInfo(); | |||
| }; | |||
| const yearMonthOpen = () => { | |||
| if (props.petBaseInfo.birthday) { | |||
| tempBirthday.value = Number(new Date(props.petBaseInfo.birthday)); | |||
| } else { | |||
| tempBirthday.value = Number(new Date()); | |||
| } | |||
| showBirthday.value = true; | |||
| }; | |||
| const yearMonthClose = () => { | |||
| showBirthday.value = false; | |||
| }; | |||
| const yearMonthConfirm = (e) => { | |||
| const timeFormat = uni.$u.timeFormat; | |||
| props.petBaseInfo.birthday = timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss'); | |||
| showBirthday.value = false; | |||
| updatePetBaseInfo(); | |||
| }; | |||
| const dispositionSelectOpen = () => { | |||
| tempDisposition.value = props.petBaseInfo.personality; | |||
| showDisposition.value = true; | |||
| }; | |||
| const dispositionSelectClose = () => { | |||
| tempDisposition.value = ''; | |||
| showDisposition.value = false; | |||
| }; | |||
| const dispositionSelectConfirm = () => { | |||
| props.petBaseInfo.personality = tempDisposition.value; | |||
| showDisposition.value = false; | |||
| updatePetBaseInfo(); | |||
| }; | |||
| const checkboxChange = (n) => { | |||
| updatePetBaseInfo(); | |||
| }; | |||
| const updatePetBaseInfo = () => { | |||
| // 触发 update 事件,将修改后的 petBaseInfo 对象发送给父组件 | |||
| emits('update:petBaseInfo', props.petBaseInfo); | |||
| }; | |||
| const getPersonalityDataList = () => { | |||
| // getDictList('pet_personality').then(res => { | |||
| // if (res.code === 200) { | |||
| // dispositionActions.value = Array.from(new Set(res.data.map(e => e.dictLabel))); | |||
| // } else { | |||
| // uni.showToast({ | |||
| // title: '获取性格失败', | |||
| // icon: 'none' | |||
| // }); | |||
| // } | |||
| // }); | |||
| }; | |||
| const getPetBreed = () => { | |||
| let petBreedType = props.petType === 'cat' ? 'pet_brand_cat' : 'pet_brand_dog'; | |||
| // getDictList(petBreedType).then(res => { | |||
| // if (res.code === 200) { | |||
| // breedData.value = Array.from(new Set(res.data.map(e => e.dictLabel))); | |||
| // } else { | |||
| // uni.showToast({ | |||
| // title: '获取种类失败', | |||
| // icon: 'none' | |||
| // }); | |||
| // } | |||
| // }); | |||
| }; | |||
| const getMaxDate = () => { | |||
| return Date.now(); | |||
| }; | |||
| const getMinDate = () => { | |||
| let dt = new Date(); | |||
| dt.setFullYear(dt.getFullYear() - 30); | |||
| return Date.parse(dt); | |||
| }; | |||
| // 生命周期钩子 | |||
| onMounted(() => { | |||
| // getPersonalityDataList(); | |||
| // getPetBreed(); | |||
| }); | |||
| </script> | |||
| <style> | |||
| .personal-pet-basic-info { | |||
| background-color: #fff; | |||
| margin-top: 10px; | |||
| padding: 10px 20px; | |||
| } | |||
| .personal-pet-breed-btns { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| box-sizing: border-box; | |||
| background-color: #FFFFFF; | |||
| padding: 20rpx 20rpx 40rpx 20rpx; | |||
| left: 0; | |||
| width: 100%; | |||
| height: 160rpx; | |||
| position: fixed; | |||
| bottom: 0; | |||
| z-index: 100; | |||
| } | |||
| .personal-pet-breed-btn { | |||
| width: 40%; | |||
| } | |||
| </style> | |||
| @ -1,253 +0,0 @@ | |||
| <template> | |||
| <view> | |||
| <view class="personal-pet-health-info"> | |||
| <view class="personal-pet-info-title border-bottom"> | |||
| 宠物健康情况 | |||
| </view> | |||
| <u--form labelPosition="left" :model="petHealthInfo" ref="uForm"> | |||
| <u-form-item required label="疫苗" :prop="`petHealthInfo.vaccine`" labelWidth="80" borderBottom | |||
| @click="showVaccine = true"> | |||
| <u--input v-model="vaccineText" disabled disabledColor="#ffffff" placeholder="请选择" | |||
| placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input> | |||
| <u-icon slot="right" name="arrow-right" color="#AAA"></u-icon> | |||
| </u-form-item> | |||
| <u-form-item required label="驱虫" :prop="`petHealthInfo.deworm`" labelWidth="80" borderBottom | |||
| @click="showExpelling = true"> | |||
| <u--input v-model="dewormText" disabled disabledColor="#ffffff" placeholder="请选择" | |||
| placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input> | |||
| <u-icon slot="right" name="arrow-right" color="#AAA"></u-icon> | |||
| </u-form-item> | |||
| <u-form-item label="绝育" :prop="`petHealthInfo.neutered`" labelWidth="80" borderBottom | |||
| @click="showSterilization = true" ref="item1"> | |||
| <u--input v-model="neuteredText" disabled disabledColor="#ffffff" placeholder="请选择" | |||
| placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input> | |||
| <u-icon slot="right" name="arrow-right" color="#AAA"></u-icon> | |||
| </u-form-item> | |||
| <u-form-item v-if="petType === '1' || petType === '狗狗' " label="狗证" :prop="`petHealthInfo.petCard`" | |||
| labelWidth="80" borderBottom @click="showDog = true" ref="item1"> | |||
| <u--input v-model="petCartText" disabled disabledColor="#ffffff" placeholder="请选择" | |||
| placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input> | |||
| <u-icon slot="right" name="arrow-right" color="#AAA"></u-icon> | |||
| </u-form-item> | |||
| <view class="dog-tips" v-if="petType === 'dog' || petType === '狗狗'"> | |||
| <u-icon name="info-circle" color="#A94F20" size="12"></u-icon> | |||
| <view style="margin-left: 3px;"> | |||
| 未办理养犬许可证且需要外出遛狗, 犬只存在被相关单位收缴 | |||
| 甚至捕杀的可能,请您遵守当地养犬规范,合法文明养犬。具体 | |||
| 请您查看当地养犬条例 | |||
| </view> | |||
| </view> | |||
| <!-- 赶进度,先注释 --> | |||
| <!-- <u-form-item required label="健康" :prop="`petHealthInfo.healths`" labelWidth="80" @click="healthsSelect"> --> | |||
| <u-form-item required label="健康" :prop="`petHealthInfo.health`" labelWidth="80"> | |||
| <u--input disabledColor="#ffffff" placeholder="请输入" v-model="petHealthInfo.health" | |||
| placeholderStyle="text-align:right;color:#AAA" border="none" inputAlign="right"></u--input> | |||
| <view slot="right"> | |||
| <u-icon v-if="showHealths" name="arrow-down" color="#AAA"></u-icon> | |||
| <u-icon v-else name="arrow-right" color="#AAA"></u-icon> | |||
| </view> | |||
| </u-form-item> | |||
| </u--form> | |||
| </view> | |||
| <!-- 赶进度,先注释 --> | |||
| <!-- <view class="health-select" v-show="showHealths"> | |||
| <view style="padding: 10px;height: 85%;"> | |||
| <u-checkbox-group v-model="petHealthInfo.healthStatus" @change="checkboxChange" placement="column" | |||
| activeColor="#ffbf60"> | |||
| <u-checkbox :customStyle="{marginBottom: '8px'}" v-for="(item, index) in healthData" :key="index" | |||
| :label="item" :name="item"> | |||
| </u-checkbox> | |||
| </u-checkbox-group> | |||
| <u--input placeholder="请输入其他健康特征" | |||
| :disabled="!(petHealthInfo.healthStatus && petHealthInfo.healthStatus.includes('其他'))" | |||
| border="surround" maxlength='20' :customStyle="{backgroundColor: '#fff'}" | |||
| @change="updatePetHealthInfo()" v-model="petHealthInfo.remark"></u--input> | |||
| </view> | |||
| </view> --> | |||
| <u-picker :show="showVaccine" :columns="vaccineActions" @cancel="showVaccine = false" | |||
| :immediateChange="true" @confirm="vaccineSelect"></u-picker> | |||
| <u-picker :show="showExpelling" :columns="expellingActions" @cancel="showExpelling = false" | |||
| :immediateChange="true" @confirm="expellingSelect"></u-picker> | |||
| <u-picker :show="showSterilization" :columns="sterilizationActions" | |||
| @cancel="showSterilization = false" :immediateChange="true" @confirm="sterilizationSelect"></u-picker> | |||
| <u-picker :show="showDog" :columns="dogActions" @cancel="showDog = false" | |||
| :immediateChange="true" @confirm="dogSelect"></u-picker> | |||
| </view> | |||
| </template> | |||
| <script setup> | |||
| import { | |||
| ref, | |||
| onMounted, | |||
| watch, | |||
| computed | |||
| } from 'vue'; | |||
| // 定义 props | |||
| const props = defineProps({ | |||
| petType: { | |||
| type: String, | |||
| default: 'dog' | |||
| }, | |||
| petHealthInfo: { | |||
| type: Object, | |||
| default: () => ({ | |||
| vaccine: '', | |||
| deworm: '', | |||
| neutered: '', | |||
| petCard: '', | |||
| health: [], | |||
| remark: '' | |||
| }) | |||
| } | |||
| }); | |||
| // 定义 emits | |||
| const emits = defineEmits(['update:petHealthInfo', 'updatePetHealthInfo']); | |||
| // 定义响应式数据 | |||
| const showVaccine = ref(false); | |||
| const showExpelling = ref(false); | |||
| const showSterilization = ref(false); | |||
| const showDog = ref(false); | |||
| const showHealths = ref(false); | |||
| const vaccineActions = ref([ | |||
| ['每年都免疫', '有免疫史', '未免疫'] | |||
| ]); | |||
| const expellingActions = ref([ | |||
| ['未驱虫', '定期驱虫', '有驱虫史'] | |||
| ]); | |||
| const sterilizationActions = ref([ | |||
| ['已绝育', '未绝育'] | |||
| ]); | |||
| const dogActions = ref([ | |||
| ['是', '否'] | |||
| ]); | |||
| const healthData = ref([]); | |||
| const tempHealths = ref([]); | |||
| const uForm = ref(null); | |||
| const item1 = ref(null); | |||
| const vaccineText = computed(() => { | |||
| const vaccineMap = { | |||
| 0: '每年都免疫', | |||
| 1: '有免疫史', | |||
| 2: '未免疫' | |||
| }; | |||
| return vaccineMap[props.petHealthInfo.vaccine] || '' | |||
| }); | |||
| const dewormText = computed(() => { | |||
| const dewormMap = { | |||
| 0: '未驱虫', | |||
| 1: '定期驱虫', | |||
| 2: '有驱虫史' | |||
| }; | |||
| return dewormMap[props.petHealthInfo.deworm] || '' | |||
| }); | |||
| const neuteredText = computed(() => { | |||
| const neuteredMap = { | |||
| 0: '已绝育', | |||
| 1: '未绝育' | |||
| }; | |||
| return neuteredMap[props.petHealthInfo.neutered] || '' | |||
| }); | |||
| const petCartText = computed(() => { | |||
| const petCartMap = { | |||
| 0: '是', | |||
| 1: '否' | |||
| }; | |||
| return petCartMap[props.petHealthInfo.petCard] || '' | |||
| }); | |||
| // 定义方法 | |||
| const getHealthDataList = () => { | |||
| // getDictList('pet_health_status').then(res => { | |||
| // if (res.code === 200) { | |||
| // healthData.value = res.data.map(e => e.dictLabel); | |||
| // } else { | |||
| // uni.showToast({ | |||
| // title: '获取健康类型失败', | |||
| // icon: 'none' | |||
| // }); | |||
| // } | |||
| // }); | |||
| }; | |||
| const vaccineSelect = (e) => { | |||
| props.petHealthInfo.vaccine = e.indexs[0]; | |||
| showVaccine.value = false; | |||
| updatePetHealthInfo(); | |||
| }; | |||
| const expellingSelect = (e) => { | |||
| props.petHealthInfo.deworm = e.indexs[0]; | |||
| showExpelling.value = false; | |||
| updatePetHealthInfo(); | |||
| }; | |||
| const sterilizationSelect = (e) => { | |||
| props.petHealthInfo.neutered = e.indexs[0]; | |||
| showSterilization.value = false; | |||
| updatePetHealthInfo(); | |||
| }; | |||
| const dogSelect = (e) => { | |||
| props.petHealthInfo.petCard = e.indexs[0]; | |||
| showDog.value = false; | |||
| updatePetHealthInfo(); | |||
| }; | |||
| const healthsSelect = () => { | |||
| showHealths.value = !showHealths.value; | |||
| uni.pageScrollTo({ | |||
| scrollTop: 2000, | |||
| duration: 300 | |||
| }); | |||
| }; | |||
| const checkboxChange = (n) => { | |||
| updatePetHealthInfo(); | |||
| }; | |||
| const changeRemark = (e) => { | |||
| props.petHealthInfo.remark = e; | |||
| updatePetHealthInfo(); | |||
| }; | |||
| const updatePetHealthInfo = (map) => { | |||
| // 触发 update 事件,将修改后的 petHealthInfo 对象发送给父组件 | |||
| emits('update:petHealthInfo', props.petHealthInfo); | |||
| }; | |||
| // 生命周期钩子 | |||
| onMounted(() => { | |||
| // getHealthDataList(); | |||
| // defaultVaccine.value = vaccineActions.value[0].indexOf(props.petHealthInfo.vaccineStatus); | |||
| // defaultDeworming.value = expellingActions.value[0].indexOf(props.petHealthInfo.dewormingStatus); | |||
| // defaultSterilization.value = sterilizationActions.value[0].indexOf(props.petHealthInfo.sterilization); | |||
| // defaultDoglicense.value = dogActions.value[0].indexOf(props.petHealthInfo.doglicenseStatus); | |||
| }); | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .personal-pet-health-info { | |||
| background-color: #fff; | |||
| padding: 10px 20px 0 20px; | |||
| margin-top: 10px; | |||
| } | |||
| .health-select { | |||
| background-color: #fffcf2; | |||
| padding: 10px 20px; | |||
| } | |||
| .dog-tips { | |||
| color: #A94F20; | |||
| font-size: 12px; | |||
| display: flex; | |||
| align-items: baseline; | |||
| margin-top: 5px; | |||
| } | |||
| </style> | |||
| @ -1,36 +0,0 @@ | |||
| .box{ | |||
| width: 100vw; | |||
| height: 100vh; | |||
| .top{ | |||
| width: 100vw; | |||
| height: 100rpx; | |||
| padding: 3% 4%; | |||
| background-color: #FFF; | |||
| color: #FFBF60; | |||
| font-size: 30rpx; | |||
| .line{ | |||
| width: 106rpx; | |||
| height: 6rpx; | |||
| background-color: #FFBF60; | |||
| margin: 10rpx 0 0 8rpx; | |||
| } | |||
| } | |||
| .center{ | |||
| width: 100vw; | |||
| height: auto; | |||
| background-color: #fff; | |||
| } | |||
| } | |||
| .box-size{ | |||
| box-sizing: border-box; | |||
| } | |||
| .level{ | |||
| display: flex; | |||
| } | |||
| @ -1,52 +0,0 @@ | |||
| <template> | |||
| <!-- <div>服务记录</div> --> | |||
| <view class="box"> | |||
| <view class="top box-size"> | |||
| 全部记录 | |||
| <view class="line" :style="{borderRadius:'3rpx'}"></view> | |||
| </view> | |||
| <view class="center mt10 pl10 pr20 pb10 box-size" v-for="item in items"> | |||
| <view class="flex-b"> | |||
| <view class="level"> | |||
| <view> | |||
| <image src="" mode="" | |||
| :style="{width:'126rpx',height:'126rpx',borderRadius:'100%',backgroundColor:'red'}" | |||
| class="mt22 ml10"></image> | |||
| </view> | |||
| <view class="mt22 ml10"> | |||
| <view class="size-30 fw700">猫小姐</view> | |||
| <view class="size-28 color-777">服务时间:2023-12-10 </view> | |||
| <view class="size-28 color-777">宠物:猫咪</view> | |||
| </view> | |||
| </view> | |||
| <view class="size-28 mr10 mt20" :style="{color:'#FFBF60'}">重庆市</view> | |||
| </view> | |||
| <view class="ml30 size-30 mt10"> | |||
| 猫咪正常饮食,无异常,排尿较少,可能天气较热。服务全程与主人视频,愉快的一天。 | |||
| </view> | |||
| <view class="level"> | |||
| <view class="ml20 mt20"> | |||
| <image src="" mode="" | |||
| :style="{width:'154rpx',height:'164rpx',backgroundColor:'red',borderRadius:'16rpx'}"></image> | |||
| </view> | |||
| </view> | |||
| <view class="flex-rowr"> | |||
| <view > | |||
| <image src="" mode="" :style="{width:'17rpx',height:'19rpx'}"></image> | |||
| </view> | |||
| <view class="ml10 mr20" :style="{color:'#707070',fontSize:'22rpx'}">编辑</view> | |||
| <view > | |||
| <up-icon name="edit-pen" color="#2979ff" size="58"></up-icon> | |||
| </view> | |||
| <view class="ml10" :style="{color:'#707070',fontSize:'22rpx'}">删除</view> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| @import"index.scss"; | |||
| </style> | |||
| @ -1,193 +0,0 @@ | |||
| <template> | |||
| <view class="page"> | |||
| <navbar title="分享好友" leftClick @leftClick="$utils.navigateBack" /> | |||
| <view class="flex content"> | |||
| <view style="width: 598rpx; height: 1063rpx;"> | |||
| <canvas id="myCanvas" canvas-id="firstCanvas1" type="2d" style="width: 100%; height: 100%;"></canvas> | |||
| </view> | |||
| <view class="flex btns"> | |||
| <button class="flex btn btn-back" @click="$utils.navigateBack">返回</button> | |||
| <button plain class="flex btn btn-save" @click="saveImg" >保存到相册</button> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import { mapState } from 'vuex' | |||
| export default { | |||
| data() { | |||
| return { | |||
| wxCodeImage: null, | |||
| canvas: {}, | |||
| } | |||
| }, | |||
| computed : { | |||
| ...mapState(['configList']) | |||
| }, | |||
| onLoad() { | |||
| }, | |||
| onReady() { | |||
| this.fetchQrCode() | |||
| }, | |||
| methods: { | |||
| async fetchQrCode() { | |||
| try { | |||
| const url = (await this.$fetch('getQrCode'))?.url | |||
| this.wxCodeImage = this.$config.aliOss.url + url | |||
| console.log('--wxCodeImage', this.wxCodeImage) | |||
| this.draw() | |||
| } catch (err) { | |||
| } | |||
| }, | |||
| draw() { | |||
| uni.showLoading({ | |||
| title: "拼命绘画中..." | |||
| }) | |||
| wx.createSelectorQuery() | |||
| .select('#myCanvas') // 绘制的canvas的id | |||
| .fields({ | |||
| node: true, | |||
| size: true | |||
| }) | |||
| .exec(async (res) => { | |||
| const canvas = res[0].node | |||
| // 渲染上下文 | |||
| const ctx = canvas.getContext('2d') | |||
| // Canvas 画布的实际绘制宽高 | |||
| const width = res[0].width | |||
| const height = res[0].height | |||
| // 初始化画布大小 | |||
| const dpr = wx.getWindowInfo().pixelRatio | |||
| //根据dpr调整 | |||
| // dpr 2 4 | |||
| // 3 6 | |||
| console.log("--dpr", dpr) | |||
| canvas.width = width * dpr | |||
| canvas.height = height * dpr | |||
| let Ratio = canvas.width / 598 | |||
| this.canvas = canvas | |||
| ctx.scale(dpr, dpr) | |||
| ctx.clearRect(0, 0, width, height) | |||
| //背景图片 | |||
| const bgImage = canvas.createImage() | |||
| bgImage.src = this.configList.index_lvxing || 'https://image.hhlm1688.com//upload/组3833x_1742803627396.png' | |||
| bgImage.onload = () => { | |||
| ctx.drawImage(bgImage, 0, 0, width, height) | |||
| //二维码图片 | |||
| const coderImage = canvas.createImage() | |||
| coderImage.src = this.wxCodeImage | |||
| coderImage.onload = () => { | |||
| const x = 197 * Ratio / dpr | |||
| const y = 562 * Ratio / dpr | |||
| const size = 206 * Ratio / dpr | |||
| ctx.drawImage(coderImage, x, y, size, size) | |||
| uni.hideLoading() | |||
| } | |||
| } | |||
| }) | |||
| }, | |||
| saveImg(){ | |||
| this.$authorize('scope.writePhotosAlbum').then((res) => { | |||
| this.imgApi() | |||
| }) | |||
| }, | |||
| imgApi() { | |||
| wx.canvasToTempFilePath({ | |||
| x: 0, | |||
| y: 0, | |||
| width: this.canvas.width, | |||
| height: this.canvas.height, | |||
| canvas: this.canvas, | |||
| success: (res) => { | |||
| let tempFilePath = res.tempFilePath; | |||
| this.saveImgToPhone(tempFilePath) | |||
| }, | |||
| fail: (err) => { | |||
| console.log('--canvasToTempFilePath--fail', err) | |||
| } | |||
| }, this); | |||
| }, | |||
| saveImgToPhone(image) { | |||
| /* 获取图片的信息 */ | |||
| uni.getImageInfo({ | |||
| src: image, | |||
| success: function(image) { | |||
| /* 保存图片到手机相册 */ | |||
| uni.saveImageToPhotosAlbum({ | |||
| filePath: image.path, | |||
| success: function() { | |||
| uni.showModal({ | |||
| title: '保存成功', | |||
| content: '图片已成功保存到相册', | |||
| showCancel: false | |||
| }); | |||
| }, | |||
| complete(res) { | |||
| console.log(res); | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| } | |||
| }, | |||
| } | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| .page { | |||
| background-color: #111317; | |||
| height: 100vh; | |||
| } | |||
| .content { | |||
| margin-top: 79rpx; | |||
| flex-direction: column; | |||
| } | |||
| .btns { | |||
| justify-content: space-between; | |||
| margin-top: 53rpx; | |||
| width: 598rpx; | |||
| } | |||
| .btn { | |||
| display: inline-flex; | |||
| width: 280rpx; | |||
| height: 90rpx; | |||
| font-size: 36rpx; | |||
| line-height: 1; | |||
| color: #FFFFFF; | |||
| border-radius: 45rpx; | |||
| margin: 0; | |||
| &-back { | |||
| background-color: #4E5053; | |||
| } | |||
| &-save { | |||
| background-image: linear-gradient(to right, #02DED6, #05D9A2); | |||
| } | |||
| } | |||
| </style> | |||
| @ -1,66 +0,0 @@ | |||
| <template> | |||
| <view class="container"> | |||
| <view v-for="(item,index) in questions" :key="index"> | |||
| <view>{{ item.name }}</view> | |||
| <up-radio-group | |||
| v-model="radioValue[index]" | |||
| placement="column" | |||
| iconPlacement="right" | |||
| > | |||
| <up-radio | |||
| :customStyle="{marginBottom: '8px'}" | |||
| v-for="(item, index) in item.option" | |||
| :key="index" | |||
| :label="item.name" | |||
| :name="item.value" | |||
| @change="radioChange" | |||
| > | |||
| </up-radio> | |||
| </up-radio-group> | |||
| </view> | |||
| <up-button text="提交1" open-type="getPhoneNumber" | |||
| @getphonenumber="handleSubmit"></up-button> | |||
| </view> | |||
| </template> | |||
| <script setup> | |||
| import {useMixin} from "@/utils/useMixin"; | |||
| import {ref} from "vue"; | |||
| const {questions} = useMixin() | |||
| const radioValue = ref([]) | |||
| const handleSubmit = (e) => { | |||
| wx.login({ | |||
| success: res => { | |||
| if (res.code) { | |||
| // 发起网络请求,将 code 发送到服务器进行登录 | |||
| wx.request({ | |||
| url: 'https://yourserver.com/getPhoneNumber', // 替换为你的服务器地址 | |||
| method: 'POST', | |||
| data: { | |||
| code: res.code, | |||
| encryptedData: e.detail.encryptedData, | |||
| iv: e.detail.iv | |||
| }, | |||
| success: function(response) { | |||
| // 服务器返回解密后的手机号 | |||
| console.log(response.data.phoneNumber); | |||
| } | |||
| }); | |||
| } else { | |||
| console.log('登录失败!' + res.errMsg); | |||
| } | |||
| } | |||
| }); | |||
| } | |||
| </script> | |||
| <style> | |||
| .container { | |||
| padding: 20px; | |||
| font-size: 14px; | |||
| line-height: 24px; | |||
| background: #FFFFFF; | |||
| } | |||
| </style> | |||
| @ -1,277 +0,0 @@ | |||
| <template> | |||
| <up-popup :show="show" mode="bottom" @close="close" :round="10" :closeable="true"> | |||
| <view class="popup-container"> | |||
| <view class="popup-title">宠物服务时间列表</view> | |||
| <view class="pet-list"> | |||
| <!-- 宠物列表循环 --> | |||
| <view class="pet-item" v-for="(pet, petIndex) in petList" :key="pet.id"> | |||
| <view class="pet-info"> | |||
| <up-image class="pet-avatar" width="50px" height="50px" :src="pet.photo" shape="circle"></up-image> | |||
| <view class="pet-details"> | |||
| <view class="pet-name">{{ pet.name }}</view> | |||
| <view class="pet-breed">{{ pet.breed }} {{ pet.bodyType }}</view> | |||
| </view> | |||
| </view> | |||
| <!-- 该宠物的服务时间列表 --> | |||
| <view class="service-list"> | |||
| <view class="service-item" v-for="(service, index) in pet.services" :key="index"> | |||
| <view class="service-header"> | |||
| <view class="service-date">{{ service.serviceDate }}</view> | |||
| <view class="service-time">{{ getServiceTimeText(service.expectServiceTime) }}</view> | |||
| </view> | |||
| <view class="service-products"> | |||
| <view class="product-item" v-for="(product, pIndex) in service.products" :key="pIndex"> | |||
| <view class="product-name">{{ product.productName }}</view> | |||
| <!-- <view class="product-price">¥{{ product.salePrice }}</view> --> | |||
| </view> | |||
| </view> | |||
| <view class="service-footer"> | |||
| <up-button | |||
| type="primary" | |||
| text="查看记录" | |||
| @click="handleClock(service)" | |||
| shape="circle" | |||
| size="mini" | |||
| style="height: 60rpx;" | |||
| color="#FFAA48"></up-button> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </up-popup> | |||
| </template> | |||
| <script setup> | |||
| import { ref, defineProps, defineEmits } from 'vue'; | |||
| const props = defineProps({ | |||
| show: { | |||
| type: Boolean, | |||
| default: false | |||
| }, | |||
| orderData: { | |||
| type: Object, | |||
| default: () => ({}) | |||
| } | |||
| }); | |||
| const emit = defineEmits(['close']); | |||
| const petList = ref([]); | |||
| // 关闭弹窗 | |||
| const close = () => { | |||
| emit('close'); | |||
| }; | |||
| // 获取服务时间段文本 | |||
| const getServiceTimeText = (timeCode) => { | |||
| const timeMap = { | |||
| 'MORNING': '早上', | |||
| 'AFTERNOON': '下午', | |||
| 'EVENING': '晚上' | |||
| }; | |||
| return timeMap[timeCode] || '未指定'; | |||
| }; | |||
| // 处理打卡事件 | |||
| const handleClock = (service) => { | |||
| if (!props.orderData || !service) return; | |||
| let url = `/otherPages/myOrdersManage/clock/index?id=${props.orderData.orderId}&itemID=${props.orderData.id}&serviceId=${service.id}&isRead=true` | |||
| if(service){ | |||
| } | |||
| uni.navigateTo({ | |||
| url, | |||
| }); | |||
| }; | |||
| // 处理数据,将订单数据转换为按宠物分组的服务时间列表格式 | |||
| const processOrderData = (orderData) => { | |||
| if (!orderData || !orderData.h5OrderVO) return []; | |||
| const { orderServiceList, orderItemList, petVOList } = orderData.h5OrderVO; | |||
| if (!orderServiceList || !orderItemList || !petVOList) return []; | |||
| // 按宠物ID分组 | |||
| const petMap = {}; | |||
| // 先将宠物信息添加到映射中 | |||
| petVOList.forEach(pet => { | |||
| petMap[pet.id] = { | |||
| ...pet, | |||
| services: [] | |||
| }; | |||
| }); | |||
| // 按服务日期和宠物ID组织数据 | |||
| const serviceMap = {}; | |||
| // 遍历服务列表,创建日期和宠物ID的映射 | |||
| orderServiceList.forEach(service => { | |||
| const serviceId = service.id; | |||
| const petId = service.petId; | |||
| const serviceDate = service.serviceDate; | |||
| const expectServiceTime = service.expectServiceTime; | |||
| if (!serviceMap[serviceId]) { | |||
| serviceMap[serviceId] = { | |||
| id: serviceId, | |||
| petId, | |||
| serviceDate, | |||
| expectServiceTime, | |||
| products: [] | |||
| }; | |||
| } | |||
| }); | |||
| // 遍历订单项,将产品添加到对应的服务日期中 | |||
| orderItemList.forEach(item => { | |||
| const serviceId = item.orderServiceId; | |||
| if (serviceMap[serviceId]) { | |||
| serviceMap[serviceId].products.push({ | |||
| productName: item.productName, | |||
| salePrice: item.salePrice, | |||
| pic: item.pic, | |||
| quantity: item.quantity, | |||
| spData: item.spData ? JSON.parse(item.spData) : {} | |||
| }); | |||
| } | |||
| }); | |||
| // 将服务添加到对应的宠物中 | |||
| Object.values(serviceMap).forEach(service => { | |||
| if (petMap[service.petId]) { | |||
| petMap[service.petId].services.push(service); | |||
| } | |||
| }); | |||
| // 转换为数组格式并按日期排序每个宠物的服务 | |||
| const result = Object.values(petMap); | |||
| result.forEach(pet => { | |||
| pet.services.sort((a, b) => new Date(a.serviceDate) - new Date(b.serviceDate)); | |||
| }); | |||
| return result; | |||
| }; | |||
| // 更新服务列表数据 | |||
| const updateServiceList = () => { | |||
| if (props.orderData && props.orderData.h5OrderVO) { | |||
| petList.value = processOrderData(props.orderData); | |||
| } | |||
| }; | |||
| // 暴露方法给父组件 | |||
| defineExpose({ | |||
| updateServiceList | |||
| }); | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| .popup-container { | |||
| padding: 30rpx; | |||
| max-height: 70vh; | |||
| overflow-y: auto; | |||
| } | |||
| .popup-title { | |||
| font-size: 32rpx; | |||
| font-weight: bold; | |||
| text-align: center; | |||
| margin-bottom: 30rpx; | |||
| } | |||
| .pet-list { | |||
| .pet-item { | |||
| margin-bottom: 40rpx; | |||
| .pet-info { | |||
| display: flex; | |||
| align-items: center; | |||
| margin-bottom: 20rpx; | |||
| .pet-avatar { | |||
| flex-shrink: 0; | |||
| margin-right: 20rpx; | |||
| } | |||
| .pet-details { | |||
| .pet-name { | |||
| font-size: 28rpx; | |||
| font-weight: bold; | |||
| color: #333; | |||
| } | |||
| .pet-breed { | |||
| font-size: 24rpx; | |||
| color: #666; | |||
| margin-top: 6rpx; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .service-list { | |||
| .service-item { | |||
| margin-bottom: 20rpx; | |||
| background-color: #f8f8f8; | |||
| border-radius: 12rpx; | |||
| padding: 20rpx; | |||
| .service-header { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| margin-bottom: 20rpx; | |||
| padding-bottom: 10rpx; | |||
| border-bottom: 1px solid #eee; | |||
| .service-date { | |||
| font-size: 28rpx; | |||
| font-weight: bold; | |||
| color: #333; | |||
| } | |||
| .service-time { | |||
| font-size: 24rpx; | |||
| color: #666; | |||
| background-color: #f0f0f0; | |||
| padding: 4rpx 12rpx; | |||
| border-radius: 20rpx; | |||
| } | |||
| } | |||
| .service-products { | |||
| margin-bottom: 20rpx; | |||
| .product-item { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| padding: 10rpx 0; | |||
| .product-name { | |||
| font-size: 26rpx; | |||
| color: #666; | |||
| } | |||
| .product-price { | |||
| font-size: 26rpx; | |||
| color: #FF530A; | |||
| } | |||
| } | |||
| } | |||
| .service-footer { | |||
| display: flex; | |||
| justify-content: flex-end; | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @ -1,636 +0,0 @@ | |||
| <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> | |||
| @ -1,79 +0,0 @@ | |||
| <template> | |||
| <view class="base-info"> | |||
| <view class="form-title">基本信息</view> | |||
| <view class="box"> | |||
| <DForm :list="state.list" @submit="handleSubmit" /> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script setup> | |||
| import { | |||
| reactive | |||
| } from "vue"; | |||
| import DForm from "@/components/dForm/index.vue" | |||
| const state = reactive({ | |||
| list: [{ | |||
| type: "input", | |||
| label: "姓名", | |||
| key: "name", | |||
| placeholder: "请输入您的真实姓名", | |||
| }, | |||
| { | |||
| type: "input", | |||
| label: "身份证号", | |||
| key: "idCard", | |||
| placeholder: "请输入您的真实身份证号", | |||
| }, | |||
| { | |||
| type: "radio", | |||
| label: "性别", | |||
| key: "sex", | |||
| options: [{ | |||
| name: "男" | |||
| }, | |||
| { | |||
| name: "女" | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| type: "input", | |||
| label: "年龄", | |||
| key: "gender", | |||
| placeholder: "请输入您的年龄", | |||
| }, | |||
| { | |||
| type: "input", | |||
| label: "养宠经验", | |||
| key: "shij", | |||
| placeholder: "请输入您的养宠年限", | |||
| unit: "年" | |||
| }, | |||
| ] | |||
| }) | |||
| const handleSubmit = (val) => { | |||
| console.log("获取参数", val) | |||
| } | |||
| </script> | |||
| <style scoped> | |||
| .base-info { | |||
| width: 718rpx; | |||
| margin: 0 auto; | |||
| background-color: #fff; | |||
| border-radius: 28rpx; | |||
| } | |||
| .box { | |||
| width: 680rpx; | |||
| margin: 0 auto; | |||
| background-color: #fff; | |||
| padding: 0 36rpx; | |||
| box-sizing: border-box; | |||
| } | |||
| </style> | |||
| @ -0,0 +1,29 @@ | |||
| <template> | |||
| <view class="page"> | |||
| <up-parse :content="configList.home_agreement.paramValueArea"></up-parse> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import { mapState, mapGetters } from 'vuex' | |||
| export default { | |||
| data() { | |||
| return { | |||
| } | |||
| }, | |||
| computed : { | |||
| ...mapGetters(['configList']) | |||
| }, | |||
| methods: { | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| .page{ | |||
| padding: 30rpx; | |||
| background-color: #fff; | |||
| } | |||
| </style> | |||
| @ -0,0 +1,29 @@ | |||
| <template> | |||
| <view class="page"> | |||
| <up-parse :content="configList.pet_platform_introduction.paramValueArea"></up-parse> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import { mapState, mapGetters } from 'vuex' | |||
| export default { | |||
| data() { | |||
| return { | |||
| } | |||
| }, | |||
| computed : { | |||
| ...mapGetters(['configList']) | |||
| }, | |||
| methods: { | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| .page{ | |||
| padding: 30rpx; | |||
| background-color: #fff; | |||
| } | |||
| </style> | |||
| @ -1,256 +0,0 @@ | |||
| /** | |||
| * 此为wxs模块,只支持APP-VUE,微信和QQ小程序以及H5平台 | |||
| * wxs内部不支持es6语法,变量只能使用var定义,无法使用解构,箭头函数等特性 | |||
| */ | |||
| // 开始触摸 | |||
| function touchstart(event, ownerInstance) { | |||
| // 触发事件的组件的ComponentDescriptor实例 | |||
| var instance = event.instance | |||
| // wxs内的局部变量快照,此快照是属于整个组件的,在touchstart和touchmove事件中都能获取到相同的结果 | |||
| var state = instance.getState() | |||
| if (state.disable) return | |||
| var touches = event.touches | |||
| // 如果进行的是多指触控,不允许进行操作 | |||
| if (touches && touches.length > 1) return | |||
| // 标识当前为滑动中状态 | |||
| state.moving = true | |||
| // 记录触摸开始点的坐标值 | |||
| state.startX = touches[0].pageX | |||
| state.startY = touches[0].pageY | |||
| } | |||
| // 触摸滑动 | |||
| function touchmove(event, ownerInstance) { | |||
| // 触发事件的组件的ComponentDescriptor实例 | |||
| var instance = event.instance | |||
| // wxs内的局部变量快照 | |||
| var state = instance.getState() | |||
| if (state.disabled || !state.moving) return | |||
| var touches = event.touches | |||
| var pageX = touches[0].pageX | |||
| var pageY = touches[0].pageY | |||
| var moveX = pageX - state.startX | |||
| var moveY = pageY - state.startY | |||
| var buttonsWidth = state.buttonsWidth | |||
| // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动 | |||
| if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) { | |||
| event.preventDefault() | |||
| event.stopPropagation() | |||
| } | |||
| // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格 | |||
| if (Math.abs(moveX) < Math.abs(moveY)) return | |||
| // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断 | |||
| // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是 | |||
| // 在超出后,设置为0 | |||
| if (state.status === 'open') { | |||
| // 在开启状态下,向左滑动,需忽略 | |||
| if (moveX < 0) moveX = 0 | |||
| // 想要收起菜单,最大能移动的距离为按钮的总宽度 | |||
| if (moveX > buttonsWidth) moveX = buttonsWidth | |||
| // 如果是已经打开了的状态,向左滑动时,移动收起菜单 | |||
| moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance) | |||
| } else { | |||
| // 关闭状态下,右滑动需忽略 | |||
| if (moveX > 0) moveX = 0 | |||
| // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数 | |||
| if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth | |||
| // 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来 | |||
| moveSwipeAction(moveX, instance, ownerInstance) | |||
| } | |||
| } | |||
| // 触摸结束 | |||
| function touchend(event, ownerInstance) { | |||
| // 触发事件的组件的ComponentDescriptor实例 | |||
| var instance = event.instance | |||
| // wxs内的局部变量快照 | |||
| var state = instance.getState() | |||
| if (!state.moving || state.disabled) return | |||
| var touches = event.changedTouches ? event.changedTouches[0] : {} | |||
| var pageX = touches.pageX | |||
| var pageY = touches.pageY | |||
| var moveX = pageX - state.startX | |||
| if (state.status === 'open') { | |||
| // 在展开的状态下,继续左滑,无需操作 | |||
| if (moveX < 0) return | |||
| // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑 | |||
| if (moveX === 0) { | |||
| return closeSwipeAction(instance, ownerInstance) | |||
| } | |||
| // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态 | |||
| if (Math.abs(moveX) < state.threshold) { | |||
| openSwipeAction(instance, ownerInstance) | |||
| } else { | |||
| // 如果滑动距离大于阈值,则执行收起逻辑 | |||
| closeSwipeAction(instance, ownerInstance) | |||
| } | |||
| } else { | |||
| // 在关闭的状态下,右滑,无需操作 | |||
| if (moveX > 0) return | |||
| // 理由同上 | |||
| if (Math.abs(moveX) < state.threshold) { | |||
| closeSwipeAction(instance, ownerInstance) | |||
| } else { | |||
| openSwipeAction(instance, ownerInstance) | |||
| } | |||
| } | |||
| } | |||
| // 获取过渡时间 | |||
| function getDuration(value) { | |||
| if (value.toString().indexOf('s') >= 0) return value | |||
| return value > 30 ? value + 'ms' : value + 's' | |||
| } | |||
| // 滑动结束时判断滑动的方向 | |||
| function getMoveDirection(instance, ownerInstance) { | |||
| var state = instance.getState() | |||
| } | |||
| // 移动滑动选择器内容区域,同时显示出其隐藏的菜单 | |||
| function moveSwipeAction(moveX, instance, ownerInstance) { | |||
| var state = instance.getState() | |||
| // 获取所有按钮的实例,需要通过它去设置按钮的位移 | |||
| var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button') | |||
| var len = buttons.length | |||
| var previewButtonsMoveX = 0 | |||
| // 设置菜单内容部分的偏移 | |||
| instance.requestAnimationFrame(function() { | |||
| instance.setStyle({ | |||
| // 设置translateX的值 | |||
| 'transition': 'none', | |||
| transform: 'translateX(' + moveX + 'px)', | |||
| '-webkit-transform': 'translateX(' + moveX + 'px)' | |||
| }) | |||
| // 折叠按钮动画 | |||
| for (var i = len - 1; i >= 0; i--) { | |||
| // 通过比例,得出元素自身该移动的距离 | |||
| var translateX = state.buttons[i].width / state.buttonsWidth * moveX | |||
| // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和 | |||
| var realTranslateX = translateX + previewButtonsMoveX | |||
| buttons[i].setStyle({ | |||
| // 在移动期间,不能使用过渡效果,否则会造成卡顿,本质原因是每次移动一点,就要花一定时间去过渡这个过程 | |||
| 'transition': 'none', | |||
| 'transform': 'translateX(' + realTranslateX + 'px)', | |||
| '-webkit-transform': 'translateX(' + realTranslateX + 'px)' | |||
| }) | |||
| // 记录本按钮之前的所有按钮的移动距离之和 | |||
| previewButtonsMoveX += translateX | |||
| } | |||
| }) | |||
| } | |||
| // 一次性展开滑动菜单 | |||
| function openSwipeAction(instance, ownerInstance) { | |||
| var state = instance.getState() | |||
| // 获取所有按钮的实例,需要通过它去设置按钮的位移 | |||
| var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button') | |||
| var len = buttons.length | |||
| // 处理duration单位问题 | |||
| const duration = getDuration(state.duration) | |||
| // 展开过程中,是向左移动,所以X的偏移应该为负值 | |||
| var buttonsWidth = -state.buttonsWidth | |||
| var previewButtonsMoveX = 0 | |||
| instance.requestAnimationFrame(function() { | |||
| // 设置菜单主体内容 | |||
| instance.setStyle({ | |||
| 'transition': 'transform ' + duration, | |||
| 'transform': 'translateX(' + buttonsWidth + 'px)', | |||
| '-webkit-transform': 'translateX(' + buttonsWidth + 'px)', | |||
| }) | |||
| // 设置各个隐藏的按钮为展开的状态 | |||
| for (var i = len - 1; i >= 0; i--) { | |||
| // 通过比例,得出元素自身该移动的距离 | |||
| var translateX = state.buttons[i].width / state.buttonsWidth * buttonsWidth | |||
| // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和 | |||
| var realTranslateX = translateX + previewButtonsMoveX | |||
| buttons[i].setStyle({ | |||
| // 在移动期间,需要加上动画效果 | |||
| 'transition': 'transform ' + duration, | |||
| 'transform': 'translateX(' + realTranslateX + 'px)', | |||
| '-webkit-transform': 'translateX(' + realTranslateX + 'px)' | |||
| }) | |||
| // 记录本按钮之前的所有按钮的移动距离之和 | |||
| previewButtonsMoveX += translateX | |||
| } | |||
| }) | |||
| setStatus('open', instance) | |||
| } | |||
| // 标记菜单的当前状态,open-已经打开,close-已经关闭 | |||
| function setStatus(status, instance) { | |||
| var state = instance.getState() | |||
| state.status = status | |||
| } | |||
| // 一次性收起滑动菜单 | |||
| function closeSwipeAction(instance, ownerInstance) { | |||
| var state = instance.getState() | |||
| // 获取所有按钮的实例,需要通过它去设置按钮的位移 | |||
| var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button') | |||
| var len = buttons.length | |||
| // 处理duration单位问题 | |||
| const duration = getDuration(state.duration) | |||
| instance.requestAnimationFrame(function() { | |||
| // 设置菜单主体内容 | |||
| instance.setStyle({ | |||
| 'transition': 'transform ' + duration, | |||
| 'transform': 'translateX(0px)', | |||
| '-webkit-transform': 'translateX(0px)' | |||
| }) | |||
| // 设置各个隐藏的按钮为收起的状态 | |||
| for (var i = len - 1; i >= 0; i--) { | |||
| buttons[i].setStyle({ | |||
| 'transition': 'transform ' + duration, | |||
| 'transform': 'translateX(0px)', | |||
| '-webkit-transform': 'translateX(0px)' | |||
| }) | |||
| } | |||
| }) | |||
| setStatus('close', instance) | |||
| } | |||
| // show的状态发生变化 | |||
| function showChange(newValue, oldValue, ownerInstance, instance) { | |||
| var state = instance.getState() | |||
| if (state.disabled) return | |||
| // 打开或关闭单元格 | |||
| if (newValue) { | |||
| openSwipeAction(instance, ownerInstance) | |||
| } else { | |||
| closeSwipeAction(instance, ownerInstance) | |||
| } | |||
| } | |||
| // 菜单尺寸发生变化 | |||
| function sizeChange(newValue, oldValue, ownerInstance, instance) { | |||
| // wxs内的局部变量快照 | |||
| var state = instance.getState() | |||
| state.disabled = newValue.disabled | |||
| state.duration = newValue.duration | |||
| state.show = newValue.show | |||
| state.threshold = newValue.threshold | |||
| state.buttons = newValue.buttons | |||
| var len = state.buttons.length | |||
| if (len) { | |||
| var buttonsWidth = 0 | |||
| var buttons = newValue.buttons | |||
| for (var i = 0; i < len; i++) { | |||
| buttonsWidth += buttons[i].width | |||
| } | |||
| } | |||
| state.buttonsWidth = buttonsWidth | |||
| } | |||
| module.exports = { | |||
| touchstart: touchstart, | |||
| touchmove: touchmove, | |||
| touchend: touchend, | |||
| sizeChange: sizeChange | |||
| } | |||
| @ -1,270 +0,0 @@ | |||
| // nvue操作dom的库,用于获取dom的尺寸信息 | |||
| const dom = uni.requireNativePlugin('dom') | |||
| // nvue中用于操作元素动画的库,类似于uni.animation,只不过uni.animation不能用于nvue | |||
| const animation = uni.requireNativePlugin('animation') | |||
| import { sleep } from '../../libs/function/index'; | |||
| export default { | |||
| data() { | |||
| return { | |||
| // 是否滑动中 | |||
| moving: false, | |||
| // 状态,open-打开状态,close-关闭状态 | |||
| status: 'close', | |||
| // 开始触摸点的X和Y轴坐标 | |||
| startX: 0, | |||
| startY: 0, | |||
| // 所有隐藏按钮的尺寸信息数组 | |||
| buttons: [], | |||
| // 所有按钮的总宽度 | |||
| buttonsWidth: 0, | |||
| // 记录上一次移动的位置值 | |||
| moveX: 0, | |||
| // 记录上一次滑动的位置,用于前后两次做对比,如果移动的距离小于某一阈值,则认为前后之间没有移动,为了解决可能存在的通信阻塞问题 | |||
| lastX: 0 | |||
| } | |||
| }, | |||
| computed: { | |||
| // 获取过渡时间 | |||
| getDuratin() { | |||
| let duration = String(this.duration) | |||
| // 如果ms为单位,返回ms的数值部分 | |||
| if (duration.indexOf('ms') >= 0) return parseInt(duration) | |||
| // 如果s为单位,为了得到ms的数值,需要乘以1000 | |||
| if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000 | |||
| // 如果值传了数值,且小于30,认为是s单位 | |||
| duration = Number(duration) | |||
| return duration < 30 ? duration * 1000 : duration | |||
| } | |||
| }, | |||
| watch: { | |||
| show: { | |||
| immediate: true, | |||
| handler(n) { | |||
| // if(n === true) { | |||
| // sleep(50).then(() => { | |||
| // this.openSwipeAction() | |||
| // }) | |||
| // } else { | |||
| // this.closeSwipeAction() | |||
| // } | |||
| } | |||
| } | |||
| }, | |||
| mounted() { | |||
| sleep(20).then(() => { | |||
| this.queryRect() | |||
| }) | |||
| }, | |||
| methods: { | |||
| close() { | |||
| this.closeSwipeAction() | |||
| }, | |||
| // 触摸单元格 | |||
| touchstart(event) { | |||
| if (this.disabled) return | |||
| this.closeOther() | |||
| const { touches } = event | |||
| // 记录触摸开始点的坐标值 | |||
| this.startX = touches[0].pageX | |||
| this.startY = touches[0].pageY | |||
| }, | |||
| // // 触摸滑动 | |||
| touchmove(event) { | |||
| if (this.disabled) return | |||
| const { touches } = event | |||
| const { pageX } = touches[0] | |||
| const { pageY } = touches[0] | |||
| let moveX = pageX - this.startX | |||
| const moveY = pageY - this.startY | |||
| const { buttonsWidth } = this | |||
| const len = this.buttons.length | |||
| // 判断前后两次的移动距离,如果小于一定值,则不进行移动处理 | |||
| if (Math.abs(pageX - this.lastX) < 0.3) return | |||
| this.lastX = pageX | |||
| // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动 | |||
| if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > this.threshold) { | |||
| event.stopPropagation() | |||
| } | |||
| // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格 | |||
| if (Math.abs(moveX) < Math.abs(moveY)) return | |||
| // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断 | |||
| // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是 | |||
| // 在超出后,设置为0 | |||
| if (this.status === 'open') { | |||
| // 在开启状态下,向左滑动,需忽略 | |||
| if (moveX < 0) moveX = 0 | |||
| // 想要收起菜单,最大能移动的距离为按钮的总宽度 | |||
| if (moveX > buttonsWidth) moveX = buttonsWidth | |||
| // 如果是已经打开了的状态,向左滑动时,移动收起菜单 | |||
| this.moveSwipeAction(-buttonsWidth + moveX) | |||
| } else { | |||
| // 关闭状态下,右滑动需忽略 | |||
| if (moveX > 0) moveX = 0 | |||
| // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数 | |||
| if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth | |||
| // 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来 | |||
| this.moveSwipeAction(moveX) | |||
| } | |||
| }, | |||
| // 单元格结束触摸 | |||
| touchend(event) { | |||
| if (this.disabled) return | |||
| const touches = event.changedTouches ? event.changedTouches[0] : {} | |||
| const { pageX } = touches | |||
| const { pageY } = touches | |||
| const { buttonsWidth } = this | |||
| this.moveX = pageX - this.startX | |||
| if (this.status === 'open') { | |||
| // 在展开的状态下,继续左滑,无需操作 | |||
| if (this.moveX < 0) this.moveX = 0 | |||
| if (this.moveX > buttonsWidth) this.moveX = buttonsWidth | |||
| // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑 | |||
| if (this.moveX === 0) { | |||
| return this.closeSwipeAction() | |||
| } | |||
| // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态 | |||
| if (Math.abs(this.moveX) < this.threshold) { | |||
| this.openSwipeAction() | |||
| } else { | |||
| // 如果滑动距离大于阈值,则执行收起逻辑 | |||
| this.closeSwipeAction() | |||
| } | |||
| } else { | |||
| // 在关闭的状态下,右滑,无需操作 | |||
| if (this.moveX >= 0) this.moveX = 0 | |||
| if (this.moveX <= -buttonsWidth) this.moveX = -buttonsWidth | |||
| // 理由同上 | |||
| if (Math.abs(this.moveX) < this.threshold) { | |||
| this.closeSwipeAction() | |||
| } else { | |||
| this.openSwipeAction() | |||
| } | |||
| } | |||
| }, | |||
| // 移动滑动选择器内容区域,同时显示出其隐藏的菜单 | |||
| moveSwipeAction(moveX) { | |||
| if (this.moving) return | |||
| this.moving = true | |||
| let previewButtonsMoveX = 0 | |||
| const len = this.buttons.length | |||
| animation.transition(this.$refs['u-swipe-action-item__content'].ref, { | |||
| styles: { | |||
| transform: `translateX(${moveX}px)` | |||
| }, | |||
| timingFunction: 'linear' | |||
| }, () => { | |||
| this.moving = false | |||
| }) | |||
| // 按钮的组的长度 | |||
| for (let i = len - 1; i >= 0; i--) { | |||
| const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref | |||
| // 通过比例,得出元素自身该移动的距离 | |||
| const translateX = this.buttons[i].width / this.buttonsWidth * moveX | |||
| // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和 | |||
| const realTranslateX = translateX + previewButtonsMoveX | |||
| animation.transition(buttonRef, { | |||
| styles: { | |||
| transform: `translateX(${realTranslateX}px)` | |||
| }, | |||
| duration: 0, | |||
| delay: 0, | |||
| timingFunction: 'linear' | |||
| }, () => {}) | |||
| // 记录本按钮之前的所有按钮的移动距离之和 | |||
| previewButtonsMoveX += translateX | |||
| } | |||
| }, | |||
| // 关闭菜单 | |||
| closeSwipeAction() { | |||
| if (this.status === 'close') return | |||
| this.moving = true | |||
| const { buttonsWidth } = this | |||
| animation.transition(this.$refs['u-swipe-action-item__content'].ref, { | |||
| styles: { | |||
| transform: 'translateX(0px)' | |||
| }, | |||
| duration: this.getDuratin, | |||
| timingFunction: 'ease-in-out' | |||
| }, () => { | |||
| this.status = 'close' | |||
| this.moving = false | |||
| this.closeHandler() | |||
| }) | |||
| // 按钮的组的长度 | |||
| const len = this.buttons.length | |||
| for (let i = len - 1; i >= 0; i--) { | |||
| const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref | |||
| // 如果不满足边界条件,返回 | |||
| if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return | |||
| animation.transition(buttonRef, { | |||
| styles: { | |||
| transform: 'translateX(0px)' | |||
| }, | |||
| duration: this.getDuratin, | |||
| timingFunction: 'ease-in-out' | |||
| }, () => {}) | |||
| } | |||
| }, | |||
| // 打开菜单 | |||
| openSwipeAction() { | |||
| if (this.status === 'open') return | |||
| this.moving = true | |||
| const buttonsWidth = -this.buttonsWidth | |||
| let previewButtonsMoveX = 0 | |||
| animation.transition(this.$refs['u-swipe-action-item__content'].ref, { | |||
| styles: { | |||
| transform: `translateX(${buttonsWidth}px)` | |||
| }, | |||
| duration: this.getDuratin, | |||
| timingFunction: 'ease-in-out' | |||
| }, () => { | |||
| this.status = 'open' | |||
| this.moving = false | |||
| this.openHandler() | |||
| }) | |||
| // 按钮的组的长度 | |||
| const len = this.buttons.length | |||
| for (let i = len - 1; i >= 0; i--) { | |||
| const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref | |||
| // 如果不满足边界条件,返回 | |||
| if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return | |||
| // 通过比例,得出元素自身该移动的距离 | |||
| const translateX = this.buttons[i].width / this.buttonsWidth * buttonsWidth | |||
| // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和 | |||
| const realTranslateX = translateX + previewButtonsMoveX | |||
| animation.transition(buttonRef, { | |||
| styles: { | |||
| transform: `translateX(${realTranslateX}px)` | |||
| }, | |||
| duration: this.getDuratin, | |||
| timingFunction: 'ease-in-out' | |||
| }, () => {}) | |||
| previewButtonsMoveX += translateX | |||
| } | |||
| }, | |||
| // 查询按钮节点信息 | |||
| queryRect() { | |||
| // 历遍所有按钮数组,通过getRectByDom返回一个promise | |||
| const promiseAll = this.rightOptions.map((item, index) => this.getRectByDom(this.$refs[`u-swipe-action-item__right__button-${index}`][0])) | |||
| // 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式 | |||
| Promise.all(promiseAll).then((sizes) => { | |||
| this.buttons = sizes | |||
| // 计算所有按钮总宽度 | |||
| this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0) | |||
| }) | |||
| }, | |||
| // 通过nvue的dom模块,查询节点信息 | |||
| getRectByDom(ref) { | |||
| return new Promise((resolve) => { | |||
| dom.getComponentRect(ref, (res) => { | |||
| resolve(res.size) | |||
| }) | |||
| }) | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,101 @@ | |||
| import { useStore } from 'vuex' | |||
| import { computed } from 'vue' | |||
| export default { | |||
| data() { | |||
| return { | |||
| shareConfig: { | |||
| // 默认分享配置 | |||
| title: '', | |||
| desc: '', | |||
| imageUrl: '', | |||
| path: '', | |||
| query: '' | |||
| } | |||
| } | |||
| }, | |||
| async onLoad(options) { | |||
| // 构建当前页面路径,包含参数 | |||
| const pages = getCurrentPages() | |||
| const currentPage = pages[pages.length - 1] | |||
| let path = currentPage.route | |||
| // 构建查询参数 | |||
| if (options && Object.keys(options).length > 0) { | |||
| const queryString = Object.keys(options) | |||
| .map(key => `${key}=${options[key]}`) | |||
| .join('&') | |||
| path = `${path}?${queryString}` | |||
| } | |||
| this.shareConfig.path = path | |||
| this.shareConfig.query = options || {} | |||
| // 设置默认分享内容 | |||
| this.setDefaultShareConfig() | |||
| }, | |||
| methods: { | |||
| // 设置默认分享配置 | |||
| setDefaultShareConfig() { | |||
| try { | |||
| const store = useStore() | |||
| const configList = computed(() => store.getters.configList) | |||
| // 从全局配置中获取默认分享信息 | |||
| this.shareConfig.title = configList.value?.applet_info?.paramValueText || '猫妈狗爸伴宠师' | |||
| this.shareConfig.imageUrl = configList.value?.applet_info?.paramValueImage || '' | |||
| } catch (error) { | |||
| console.log('获取分享配置失败:', error) | |||
| // 设置默认值 | |||
| this.shareConfig.title = '猫妈狗爸伴宠师' | |||
| this.shareConfig.imageUrl = '' | |||
| } | |||
| }, | |||
| // 自定义分享配置 | |||
| setShareConfig(config = {}) { | |||
| this.shareConfig = { | |||
| ...this.shareConfig, | |||
| ...config | |||
| } | |||
| }, | |||
| // 获取分享配置 | |||
| getShareConfig() { | |||
| return { | |||
| title: this.shareConfig.title, | |||
| desc: this.shareConfig.desc, | |||
| imageUrl: this.shareConfig.imageUrl, | |||
| path: this.shareConfig.path, | |||
| query: this.shareConfig.query | |||
| } | |||
| }, | |||
| // 显示分享菜单 | |||
| showShareMenu() { | |||
| // #ifdef MP-WEIXIN | |||
| uni.showShareMenu({ | |||
| withShareTicket: true, | |||
| success: () => { | |||
| console.log('显示分享菜单成功') | |||
| }, | |||
| fail: (error) => { | |||
| console.error('显示分享菜单失败:', error) | |||
| } | |||
| }) | |||
| // #endif | |||
| }, | |||
| }, | |||
| // 微信小程序分享给朋友 | |||
| onShareAppMessage(res) { | |||
| return this.getShareConfig() | |||
| }, | |||
| // 微信小程序分享到朋友圈 | |||
| onShareTimeline(res) { | |||
| return this.getShareConfig() | |||
| }, | |||
| } | |||