|
@@ -0,0 +1,500 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <view class="detail-container">
|
|
|
|
|
+ <!-- 加载动画 -->
|
|
|
|
|
+ <view class="loading-container" v-if="pageLoading">
|
|
|
|
|
+ <view class="skeleton-header">
|
|
|
|
|
+ <view class="skeleton-line skeleton-ani" style="width: 30%; height: 36rpx;"></view>
|
|
|
|
|
+ <view class="skeleton-line skeleton-ani" style="width: 20%; height: 36rpx;"></view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="skeleton-card" v-for="j in 3" :key="'c' + j">
|
|
|
|
|
+ <view class="skeleton-line skeleton-ani" style="width: 60%; height: 28rpx; margin-bottom: 20rpx;"></view>
|
|
|
|
|
+ <view class="skeleton-line skeleton-ani" style="width: 90%; height: 24rpx; margin-bottom: 14rpx;"></view>
|
|
|
|
|
+ <view class="skeleton-line skeleton-ani" style="width: 75%; height: 24rpx;"></view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <template v-else>
|
|
|
|
|
+ <!-- 顶部状态区 -->
|
|
|
|
|
+ <view class="detail-header pre-accept">
|
|
|
|
|
+ <view class="status-row">
|
|
|
|
|
+ <text class="status-title">待接单</text>
|
|
|
|
|
+ <text class="status-price">¥{{ orderDetail.fulfillmentCommission }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="status-desc">待接单订单,接单后可查看完整联系方式</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <scroll-view scroll-y class="detail-content">
|
|
|
|
|
+ <!-- 宠物档案卡片 -->
|
|
|
|
|
+ <view class="white-card pet-bar">
|
|
|
|
|
+ <image class="pb-avatar" :src="orderDetail.petAvatar" mode="aspectFill"></image>
|
|
|
|
|
+ <view class="pb-info">
|
|
|
|
|
+ <view class="pb-name-row">
|
|
|
|
|
+ <text class="pb-name">{{ orderDetail.petName }}</text>
|
|
|
|
|
+ <text class="pb-breed">{{ orderDetail.petBreed }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="pb-tags">
|
|
|
|
|
+ <text class="pb-tag">{{ orderDetail.serviceName }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="pb-actions">
|
|
|
|
|
+ <view class="view-profile-btn" @click="showPetProfile">
|
|
|
|
|
+ <text>宠物档案</text>
|
|
|
|
|
+ <text class="arrow">></text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 用户档案卡片 -->
|
|
|
|
|
+ <view class="white-card user-profile-card">
|
|
|
|
|
+ <view class="tl-title-row">
|
|
|
|
|
+ <view class="orange-bar"></view>
|
|
|
|
|
+ <text class="tl-title">用户档案</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="bi-row">
|
|
|
|
|
+ <image class="bi-icon" src="/static/icons/user.svg"></image>
|
|
|
|
|
+ <view class="bi-content">
|
|
|
|
|
+ <text class="bi-label">联系人</text>
|
|
|
|
|
+ <text class="bi-val">{{ orderDetail.ownerName || '匿名用户' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="bi-row">
|
|
|
|
|
+ <image class="bi-icon" src="/static/icons/phone.svg"></image>
|
|
|
|
|
+ <view class="bi-content">
|
|
|
|
|
+ <text class="bi-label">联系电话</text>
|
|
|
|
|
+ <text class="bi-val">{{ orderDetail.ownerPhone || '接单后可见' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="bi-row no-border">
|
|
|
|
|
+ <image class="bi-icon" src="/static/icons/location.svg"></image>
|
|
|
|
|
+ <view class="bi-content">
|
|
|
|
|
+ <text class="bi-label">详细地址</text>
|
|
|
|
|
+ <text class="bi-val">{{ orderDetail.endAddress || '暂无详细地址' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 路线及服务信息 -->
|
|
|
|
|
+ <view class="white-card service-info-card">
|
|
|
|
|
+ <view class="tl-title-row">
|
|
|
|
|
+ <view class="orange-bar"></view>
|
|
|
|
|
+ <text class="tl-title">服务详情</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="si-row time-row">
|
|
|
|
|
+ <image class="si-icon outline" src="/static/icons/clock.svg"></image>
|
|
|
|
|
+ <view class="si-content">
|
|
|
|
|
+ <text class="si-label">服务时间</text>
|
|
|
|
|
+ <text class="si-val">{{ orderDetail.time }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 接送类型的地址展现 -->
|
|
|
|
|
+ <template v-if="orderDetail.type === 1">
|
|
|
|
|
+ <view class="si-row addr-row start-addr">
|
|
|
|
|
+ <view class="icon-circle start">起</view>
|
|
|
|
|
+ <view class="route-line-vertical"></view>
|
|
|
|
|
+ <view class="si-content">
|
|
|
|
|
+ <text class="si-addr-title">{{ orderDetail.startLocation }}</text>
|
|
|
|
|
+ <text class="si-addr-desc">{{ orderDetail.startAddress }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="nav-btn-circle" @click="openNavigation('start')">
|
|
|
|
|
+ <image class="nav-arrow" src="/static/icons/nav_arrow.svg"></image>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="si-row addr-row end-addr">
|
|
|
|
|
+ <view class="icon-circle end">终</view>
|
|
|
|
|
+ <view class="si-content">
|
|
|
|
|
+ <text class="si-addr-title">{{ orderDetail.endLocation }}</text>
|
|
|
|
|
+ <text class="si-addr-desc">{{ orderDetail.endAddress }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="nav-btn-circle" @click="openNavigation('end')">
|
|
|
|
|
+ <image class="nav-arrow" src="/static/icons/nav_arrow.svg"></image>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <!-- 喂遛/洗护类型的地址展现 -->
|
|
|
|
|
+ <template v-else>
|
|
|
|
|
+ <view class="si-row addr-row end-addr">
|
|
|
|
|
+ <view class="icon-circle service">服</view>
|
|
|
|
|
+ <view class="si-content">
|
|
|
|
|
+ <text class="si-addr-title">{{ orderDetail.endLocation }}</text>
|
|
|
|
|
+ <text class="si-addr-desc">{{ orderDetail.endAddress }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="nav-btn-circle" @click="openNavigation('end')">
|
|
|
|
|
+ <image class="nav-arrow" src="/static/icons/nav_arrow.svg"></image>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="si-row no-border">
|
|
|
|
|
+ <image class="si-icon outline custom-icon-file" src="/static/icons/file.svg"></image>
|
|
|
|
|
+ <view class="si-content">
|
|
|
|
|
+ <text class="si-label">订单备注</text>
|
|
|
|
|
+ <text class="si-val">{{ orderDetail.remark || '-' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 订单日志 -->
|
|
|
|
|
+ <view class="white-card logs-card">
|
|
|
|
|
+ <view class="tl-title-row">
|
|
|
|
|
+ <view class="orange-bar"></view>
|
|
|
|
|
+ <text class="tl-title">订单日志</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="tl-list">
|
|
|
|
|
+ <view class="tl-item" v-for="(log, index) in orderLogs" :key="index">
|
|
|
|
|
+ <view class="tl-marker" :class="{ 'active': index === 0 }">
|
|
|
|
|
+ <view class="tl-dot-inner" v-if="index === 0"></view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="tl-content-row">
|
|
|
|
|
+ <view class="tl-header">
|
|
|
|
|
+ <text class="tl-status">{{ log.title || '系统记录' }}</text>
|
|
|
|
|
+ <text class="tl-time">{{ log.time }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="tl-remark" v-if="log.content">{{ log.content }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view style="height: 60rpx;"></view>
|
|
|
|
|
+ </scroll-view>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 宠物档案弹窗 (引用首页逻辑) -->
|
|
|
|
|
+ <view class="pet-modal-mask" v-if="showPetModal" @click="closePetProfile">
|
|
|
|
|
+ <view class="pet-modal-content" @click.stop>
|
|
|
|
|
+ <view class="pet-modal-header">
|
|
|
|
|
+ <text class="pet-modal-title">宠物档案</text>
|
|
|
|
|
+ <view class="close-icon-btn" @click="closePetProfile">×</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <scroll-view scroll-y class="pet-modal-scroll">
|
|
|
|
|
+ <view class="pet-base-info">
|
|
|
|
|
+ <image class="pm-avatar" :src="orderDetail.petAvatar" mode="aspectFill"></image>
|
|
|
|
|
+ <view class="pm-info-text">
|
|
|
|
|
+ <view class="pm-name-row">
|
|
|
|
|
+ <text class="pm-name">{{ orderDetail.petName }}</text>
|
|
|
|
|
+ <view class="pm-gender" v-if="orderDetail.petGender === 'M'">
|
|
|
|
|
+ <text class="gender-icon">♂</text><text>公</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="pm-gender female" v-else-if="orderDetail.petGender === 'F'">
|
|
|
|
|
+ <text class="gender-icon">♀</text><text>母</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="pm-breed">品种:{{ orderDetail.petBreed }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="pm-detail-grid">
|
|
|
|
|
+ <view class="pm-grid-item half">
|
|
|
|
|
+ <text class="pm-label">年龄</text>
|
|
|
|
|
+ <text class="pm-val">{{ orderDetail.petAge || '未知' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="pm-grid-item half">
|
|
|
|
|
+ <text class="pm-label">体重</text>
|
|
|
|
|
+ <text class="pm-val">{{ orderDetail.petWeight || '未知' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="pm-grid-item full">
|
|
|
|
|
+ <text class="pm-label">性格</text>
|
|
|
|
|
+ <text class="pm-val">{{ orderDetail.petPersonality || '暂无' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="pm-grid-item full">
|
|
|
|
|
+ <text class="pm-label">备注/禁忌</text>
|
|
|
|
|
+ <text class="pm-val">{{ orderDetail.petNotes || '暂无说明' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view style="height: 40rpx;"></view>
|
|
|
|
|
+ <button class="pm-bottom-close" @click="closePetProfile">关闭</button>
|
|
|
|
|
+ <view style="height: 20rpx;"></view>
|
|
|
|
|
+ </scroll-view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 导航地图选择 -->
|
|
|
|
|
+ <view class="nav-modal-mask" v-if="showNavModal" @click="closeNavModal">
|
|
|
|
|
+ <view class="nav-action-sheet" @click.stop>
|
|
|
|
|
+ <view class="nav-sheet-title">选择地图进行导航</view>
|
|
|
|
|
+ <view class="nav-sheet-item" @click="chooseMap('高德')">高德地图</view>
|
|
|
|
|
+ <view class="nav-sheet-item" @click="chooseMap('腾讯')">腾讯地图</view>
|
|
|
|
|
+ <view class="nav-sheet-item" @click="chooseMap('百度')">百度地图</view>
|
|
|
|
|
+ <view class="nav-sheet-gap"></view>
|
|
|
|
|
+ <view class="nav-sheet-item cancel" @click="closeNavModal">取消</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script>
|
|
|
|
|
+import { getOrderInfo } from '@/api/order/subOrder'
|
|
|
|
|
+import { getOrderLogs } from '@/api/order/subOrderLog'
|
|
|
|
|
+import { listAllService } from '@/api/service/list'
|
|
|
|
|
+import { getPetDetail } from '@/api/archieves/pet'
|
|
|
|
|
+import { reportGps } from '@/utils/gps'
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 订单大厅详情页 (数据修正版)
|
|
|
|
|
+ * @Author: Antigravity
|
|
|
|
|
+ */
|
|
|
|
|
+export default {
|
|
|
|
|
+ data() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ orderId: null,
|
|
|
|
|
+ pageLoading: true,
|
|
|
|
|
+ orderDetail: {
|
|
|
|
|
+ type: 1, fulfillmentCommission: '0.00', time: '',
|
|
|
|
|
+ petAvatar: '/static/dog.png', petName: '', petBreed: '',
|
|
|
|
|
+ serviceName: '', startLocation: '', startAddress: '', endLocation: '', endAddress: '',
|
|
|
|
|
+ remark: '', orderNo: '', createTime: '', fromLat: null, fromLng: null, toLat: null, toLng: null,
|
|
|
|
|
+ ownerName: '', ownerPhone: '', petAge: '', petWeight: '', petGender: 'M', petPersonality: '', petNotes: ''
|
|
|
|
|
+ },
|
|
|
|
|
+ orderLogs: [],
|
|
|
|
|
+ serviceList: [],
|
|
|
|
|
+ showNavModal: false,
|
|
|
|
|
+ navTargetPointType: '',
|
|
|
|
|
+ showPetModal: false
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ async onLoad(options) {
|
|
|
|
|
+ if (options.id) this.orderId = options.id
|
|
|
|
|
+ this.pageLoading = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.loadServiceList()
|
|
|
|
|
+ await this.loadOrderDetail()
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.pageLoading = false
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ async loadServiceList() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await listAllService()
|
|
|
|
|
+ this.serviceList = res.data || []
|
|
|
|
|
+ } catch (err) { console.error('获取服务类型失败:', err) }
|
|
|
|
|
+ },
|
|
|
|
|
+ async loadOrderDetail() {
|
|
|
|
|
+ if (!this.orderId) return
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getOrderInfo(this.orderId)
|
|
|
|
|
+ const order = res.data
|
|
|
|
|
+ if (!order) return
|
|
|
|
|
+
|
|
|
|
|
+ const serviceInfo = this.serviceList.find(s => s.id === order.service)
|
|
|
|
|
+ const mode = serviceInfo?.mode || 0
|
|
|
|
|
+ this.orderDetail = {
|
|
|
|
|
+ id: order.id,
|
|
|
|
|
+ type: mode === 1 ? 1 : 2,
|
|
|
|
|
+ fulfillmentCommission: (order.fulfillmentCommission / 100).toFixed(2),
|
|
|
|
|
+ time: order.serviceTime || '',
|
|
|
|
|
+ petAvatar: order.petAvatar || '/static/dog.png',
|
|
|
|
|
+ petName: order.petName || '宠物',
|
|
|
|
|
+ petBreed: order.breed || '未知品种',
|
|
|
|
|
+ serviceName: serviceInfo?.name || '未知服务',
|
|
|
|
|
+ startLocation: order.fromAddress || '暂无起点',
|
|
|
|
|
+ startAddress: order.fromAddress || '',
|
|
|
|
|
+ fromLat: order.fromLat, fromLng: order.fromLng,
|
|
|
|
|
+ endLocation: order.toAddress || '查看详情可见',
|
|
|
|
|
+ endAddress: order.toAddress || '',
|
|
|
|
|
+ toLat: order.toLat, toLng: order.toLng,
|
|
|
|
|
+ remark: order.remark || '',
|
|
|
|
|
+ orderNo: order.code || 'T' + order.id,
|
|
|
|
|
+ createTime: order.createDateTime || order.serviceTime,
|
|
|
|
|
+ ownerName: order.contact || '匿名用户',
|
|
|
|
|
+ ownerPhone: order.contactPhoneNumber || ''
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 加载宠物详情
|
|
|
|
|
+ if (order.usrPet) {
|
|
|
|
|
+ this.loadPetInfo(order.usrPet)
|
|
|
|
|
+ }
|
|
|
|
|
+ // 2. 加载时间轴日志
|
|
|
|
|
+ this.loadLogs()
|
|
|
|
|
+ } catch (err) { console.error('获取详情失败:', err) }
|
|
|
|
|
+ },
|
|
|
|
|
+ async loadPetInfo(petId) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getPetDetail(petId)
|
|
|
|
|
+ const pet = res.data
|
|
|
|
|
+ if (pet) {
|
|
|
|
|
+ this.orderDetail.petAge = pet.age || '未知'
|
|
|
|
|
+ this.orderDetail.petWeight = pet.weight || '未知'
|
|
|
|
|
+ this.orderDetail.petGender = pet.sex || 'M'
|
|
|
|
|
+ this.orderDetail.petPersonality = pet.personality || '暂无标签'
|
|
|
|
|
+ this.orderDetail.petNotes = pet.remark || '暂无说明'
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (err) { console.error('获取宠物详情失败:', err) }
|
|
|
|
|
+ },
|
|
|
|
|
+ async loadLogs() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getOrderLogs(this.orderId)
|
|
|
|
|
+ const list = Array.isArray(res.data) ? res.data : (res.rows || [])
|
|
|
|
|
+ this.orderLogs = list.map(item => ({
|
|
|
|
|
+ title: item.title || '状态更新',
|
|
|
|
|
+ time: item.createTime || '',
|
|
|
|
|
+ content: item.content || ''
|
|
|
|
|
+ }))
|
|
|
|
|
+ } catch (err) { console.error('获取日志失败:', err) }
|
|
|
|
|
+ },
|
|
|
|
|
+ showPetProfile() { this.showPetModal = true },
|
|
|
|
|
+ closePetProfile() { this.showPetModal = false },
|
|
|
|
|
+ openNavigation(type) { this.navTargetPointType = type; this.showNavModal = true },
|
|
|
|
|
+ closeNavModal() { this.showNavModal = false },
|
|
|
|
|
+ chooseMap(mapType) {
|
|
|
|
|
+ let pointType = this.navTargetPointType;
|
|
|
|
|
+ let name = pointType === 'start' ? (this.orderDetail.startAddress || '起点') : (this.orderDetail.endAddress || '终点');
|
|
|
|
|
+ let address = name;
|
|
|
|
|
+ let latitude = pointType === 'start' ? Number(this.orderDetail.fromLat) : Number(this.orderDetail.toLat);
|
|
|
|
|
+ let longitude = pointType === 'start' ? Number(this.orderDetail.fromLng) : Number(this.orderDetail.toLng);
|
|
|
|
|
+ this.showNavModal = false;
|
|
|
|
|
+
|
|
|
|
|
+ const navigateTo = (lat, lng) => {
|
|
|
|
|
+ uni.openLocation({ latitude: lat, longitude: lng, name: name, address: address });
|
|
|
|
|
+ };
|
|
|
|
|
+ if (latitude && longitude && !isNaN(latitude)) {
|
|
|
|
|
+ navigateTo(latitude, longitude);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showLoading({ title: '定位中...' });
|
|
|
|
|
+ reportGps(true).then(res => {
|
|
|
|
|
+ uni.hideLoading(); navigateTo(res.latitude, res.longitude);
|
|
|
|
|
+ }).catch(() => uni.hideLoading());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="scss">
|
|
|
|
|
+.detail-container {
|
|
|
|
|
+ min-height: 100vh;
|
|
|
|
|
+ background-color: #f8f8f8;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.detail-header {
|
|
|
|
|
+ padding: 40rpx 40rpx 80rpx;
|
|
|
|
|
+ background: linear-gradient(135deg, #FF9800 0%, #FFB74D 100%);
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ .status-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 20rpx;
|
|
|
|
|
+ .status-title { font-size: 44rpx; font-weight: bold; }
|
|
|
|
|
+ .status-price { font-size: 40rpx; font-weight: bold; }
|
|
|
|
|
+ }
|
|
|
|
|
+ .status-desc { font-size: 26rpx; opacity: 0.9; }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.detail-content {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ padding: 20rpx;
|
|
|
|
|
+ margin-top: -40rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.white-card {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-radius: 24rpx;
|
|
|
|
|
+ padding: 30rpx;
|
|
|
|
|
+ margin-bottom: 24rpx;
|
|
|
|
|
+ box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 宠物栏 */
|
|
|
|
|
+.pet-bar {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ .pb-avatar { width: 110rpx; height: 110rpx; border-radius: 50%; margin-right: 24rpx; background: #f0f0f0; border: 4rpx solid #fff; box-shadow: 0 4rpx 10rpx rgba(0,0,0,0.05); }
|
|
|
|
|
+ .pb-info {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ .pb-name { font-size: 34rpx; font-weight: bold; color: #333; margin-right: 12rpx; }
|
|
|
|
|
+ .pb-breed { font-size: 24rpx; color: #999; }
|
|
|
|
|
+ .pb-tags { margin-top: 10rpx; .pb-tag { font-size: 22rpx; color: #FF9800; background: rgba(255,152,0,0.1); padding: 4rpx 16rpx; border-radius: 6rpx; } }
|
|
|
|
|
+ }
|
|
|
|
|
+ .view-profile-btn {
|
|
|
|
|
+ display: flex; align-items: center; background: #FFF3E0; padding: 10rpx 20rpx; border-radius: 30rpx;
|
|
|
|
|
+ text { font-size: 24rpx; color: #FF9800; font-weight: bold; }
|
|
|
|
|
+ .arrow { margin-left: 6rpx; font-size: 20rpx; }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 用户档案 */
|
|
|
|
|
+.user-profile-card {
|
|
|
|
|
+ .bi-row {
|
|
|
|
|
+ display: flex; align-items: flex-start; padding: 20rpx 0; border-bottom: 1rpx solid #f5f5f5;
|
|
|
|
|
+ &.no-border { border-bottom: none; }
|
|
|
|
|
+ .bi-icon { width: 32rpx; height: 32rpx; margin-right: 20rpx; margin-top: 6rpx; opacity: 0.6; }
|
|
|
|
|
+ .bi-content {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ .bi-label { font-size: 24rpx; color: #999; margin-bottom: 4rpx; display: block; }
|
|
|
|
|
+ .bi-val { font-size: 28rpx; color: #333; font-weight: 500; }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 服务详情 */
|
|
|
|
|
+.service-info-card {
|
|
|
|
|
+ .si-row {
|
|
|
|
|
+ display: flex; padding: 24rpx 0; border-bottom: 1rpx solid #f5f5f5;
|
|
|
|
|
+ &.no-border { border-bottom: none; }
|
|
|
|
|
+ .si-icon { width: 36rpx; height: 36rpx; margin-right: 20rpx; margin-top: 4rpx; }
|
|
|
|
|
+ .si-content {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ .si-label { font-size: 24rpx; color: #999; display: block; margin-bottom: 6rpx; }
|
|
|
|
|
+ .si-val { font-size: 28rpx; color: #333; }
|
|
|
|
|
+ .si-addr-title { font-size: 30rpx; font-weight: bold; color: #333; display: block; margin-bottom: 8rpx; }
|
|
|
|
|
+ .si-addr-desc { font-size: 24rpx; color: #666; }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.addr-row {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ .icon-circle {
|
|
|
|
|
+ width: 44rpx; height: 44rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center;
|
|
|
|
|
+ font-size: 22rpx; color: #fff; margin-right: 20rpx; flex-shrink: 0; margin-top: 6rpx;
|
|
|
|
|
+ &.start { background: #FF9800; }
|
|
|
|
|
+ &.end { background: #4CAF50; }
|
|
|
|
|
+ &.service { background: #2196F3; }
|
|
|
|
|
+ }
|
|
|
|
|
+ .route-line-vertical { position: absolute; left: 22rpx; top: 56rpx; bottom: -10rpx; width: 2rpx; border-left: 2rpx dashed #ddd; }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.nav-btn-circle { width: 56rpx; height: 56rpx; border-radius: 50%; background: #FFF3E0; display: flex; align-items: center; justify-content: center; margin-left: 20rpx; .nav-arrow { width: 28rpx; height: 28rpx; } }
|
|
|
|
|
+
|
|
|
|
|
+/* 日志板块 */
|
|
|
|
|
+.tl-title-row { display: flex; align-items: center; margin-bottom: 30rpx; }
|
|
|
|
|
+.orange-bar { width: 8rpx; height: 32rpx; background-color: #FF9800; margin-right: 16rpx; border-radius: 4rpx; }
|
|
|
|
|
+.tl-title { font-size: 30rpx; font-weight: bold; color: #333; }
|
|
|
|
|
+.tl-list { padding-left: 10rpx; }
|
|
|
|
|
+.tl-item { display: flex; position: relative; padding-bottom: 40rpx; }
|
|
|
|
|
+.tl-item:last-child { padding-bottom: 0; }
|
|
|
|
|
+.tl-marker { width: 16rpx; height: 16rpx; border-radius: 50%; background-color: #E0E0E0; position: absolute; left: 0; top: 12rpx; z-index: 2; display: flex; justify-content: center; align-items: center; }
|
|
|
|
|
+.tl-marker.active { background-color: #fff; border: 3rpx solid #FF9800; width: 18rpx; height: 18rpx; left: -1rpx; }
|
|
|
|
|
+.tl-dot-inner { width: 10rpx; height: 10rpx; border-radius: 50%; background-color: #FF9800; }
|
|
|
|
|
+.tl-item:not(:last-child)::after { content: ''; position: absolute; left: 7rpx; top: 32rpx; bottom: -10rpx; width: 2rpx; background-color: #FFE0B2; z-index: 1; }
|
|
|
|
|
+.tl-content-row { margin-left: 44rpx; display: flex; flex-direction: column; width: 100%; }
|
|
|
|
|
+.tl-header { display: flex; justify-content: space-between; align-items: center; width: 100%; margin-bottom: 12rpx; }
|
|
|
|
|
+.tl-status { font-size: 28rpx; color: #333; font-weight: bold; }
|
|
|
|
|
+.tl-time { font-size: 24rpx; color: #999; }
|
|
|
|
|
+.tl-remark { font-size: 24rpx; color: #666; background-color: #F9F9F9; padding: 16rpx; border-radius: 12rpx; line-height: 1.5; }
|
|
|
|
|
+
|
|
|
|
|
+/* 弹窗通用 */
|
|
|
|
|
+.pet-modal-mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); z-index: 999; display: flex; align-items: center; justify-content: center; }
|
|
|
|
|
+.pet-modal-content { width: 680rpx; background: #fff; border-radius: 24rpx; overflow: hidden; }
|
|
|
|
|
+.pet-modal-header { padding: 30rpx; display: flex; justify-content: space-between; align-items: center; border-bottom: 1rpx solid #f5f5f5; .pet-modal-title { font-size: 34rpx; font-weight: bold; } .close-icon-btn { font-size: 40rpx; color: #999; } }
|
|
|
|
|
+.pet-modal-scroll { padding: 30rpx; height: 75vh; box-sizing: border-box; }
|
|
|
|
|
+.pet-base-info { display: flex; align-items: center; margin-bottom: 40rpx; .pm-avatar { width: 120rpx; height: 120rpx; border-radius: 50%; margin-right: 30rpx; } }
|
|
|
|
|
+.pm-name-row { display: flex; align-items: center; margin-bottom: 12rpx; .pm-name { font-size: 36rpx; font-weight: bold; margin-right: 20rpx; } .pm-gender { background: #E3F2FD; padding: 4rpx 16rpx; border-radius: 20rpx; text { font-size: 22rpx; color: #1E88E5; } &.female { background: #FCE4EC; text { color: #D81B60; } } } }
|
|
|
|
|
+.pm-breed { font-size: 26rpx; color: #999; }
|
|
|
|
|
+.pm-detail-grid { display: flex; flex-wrap: wrap; gap: 20rpx; .pm-grid-item { background: #f8f8f8; padding: 24rpx; border-radius: 16rpx; display: flex; flex-direction: column; &.half { width: calc(50% - 10rpx); box-sizing: border-box; } &.full { width: 100%; box-sizing: border-box; } .pm-label { font-size: 24rpx; color: #999; margin-bottom: 8rpx; } .pm-val { font-size: 28rpx; color: #333; font-weight: 500; } } }
|
|
|
|
|
+.pm-bottom-close { margin-top: 40rpx; background: #f5f5f5; color: #666; border-radius: 40rpx; font-size: 30rpx; }
|
|
|
|
|
+
|
|
|
|
|
+.nav-modal-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.4); z-index: 1000; }
|
|
|
|
|
+.nav-action-sheet { position: absolute; bottom: 0; left: 0; right: 0; background: #fff; border-radius: 24rpx 24rpx 0 0; padding-bottom: env(safe-area-inset-bottom);
|
|
|
|
|
+ .nav-sheet-title { padding: 30rpx; text-align: center; font-size: 28rpx; color: #999; border-bottom: 1rpx solid #eee; }
|
|
|
|
|
+ .nav-sheet-item { padding: 30rpx; text-align: center; font-size: 32rpx; color: #333; border-bottom: 1rpx solid #eee; &.cancel { color: #f26d6d; border-bottom: none; } }
|
|
|
|
|
+ .nav-sheet-gap { height: 12rpx; background: #f5f5f5; }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.loading-container { padding: 100rpx 40rpx; .skeleton-header { display: flex; justify-content: space-between; margin-bottom: 60rpx; } .skeleton-card { background: #fff; border-radius: 20rpx; padding: 30rpx; margin-bottom: 30rpx; } .skeleton-line { background: #f0f0f0; border-radius: 4rpx; } .skeleton-ani { animation: skeleton-loading 1.4s ease infinite; } }
|
|
|
|
|
+@keyframes skeleton-loading { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } }
|
|
|
|
|
+</style>
|