Huanyi 1 долоо хоног өмнө
parent
commit
fd4b7900ac

+ 2 - 2
manifest.json

@@ -2,8 +2,8 @@
     "name" : "好萌友",
     "appid" : "__UNI__F19BBAD",
     "description" : "宠物服务商家端",
-    "versionName" : "1.1.4",
-    "versionCode" : 42,
+    "versionName" : "1.2.0t",
+    "versionCode" : 43,
     "transformPx" : false,
     "app-plus" : {
         "usingComponents" : true,

+ 50 - 47
pages/my/complaint/list/index.vue

@@ -8,35 +8,32 @@
 			<view v-else class="history-card" v-for="item in historyList" :key="item.id">
 				<view class="card-header">
 					<view class="left-box">
-						<text class="status-tag" :class="item.praiseFlag ? 'praise' : 'complaint'">{{ item.praiseFlag ? '赞' : '不赞' }}</text>
 						<text class="order-no">单号:{{ item.orderCode || '-' }}</text>
+						<text class="submitted-tag">已提交</text>
+					</view>
+					<view :class="['praise-badge', item.praiseFlag ? 'agree' : 'disagree']">
+						<image class="badge-icon"
+							:src="item.praiseFlag ? '/static/images/complaint-agree.png' : '/static/images/complaint-disagree.png'"
+							mode="aspectFit"></image>
+						<text class="badge-text">{{ item.praiseFlag ? '赞' : '不赞' }}</text>
 					</view>
-					<text class="status-text">已提交</text>
 				</view>
+				<view class="card-time">{{ item.createTime || '-' }}</view>
 				<view class="card-body">
 					<view class="reason-row">
-						<text class="label">{{ item.praiseFlag ? '理由:' : '不赞原因:' }}</text>
 						<text class="reason-content">{{ item.reason || '未填写内容' }}</text>
 					</view>
-					
+
 					<!-- 凭证图片展示 @Author: Antigravity -->
-					<view class="photo-grid" v-if="item.photos">
-						<image 
-							v-for="(url, index) in item.photos.split(',')" 
-							:key="index" 
-							:src="url" 
-							mode="aspectFill"
-							class="photo-item"
-							@click="previewImage(item.photos.split(','), index)"
-						></image>
+					<view class="photo-grid" v-if="item.photoUrls">
+						<image v-for="(url, index) in item.photoUrls.split(',')" :key="index" :src="url"
+							mode="aspectFill" class="photo-item"
+							@click="previewImage(item.photoUrls.split(','), index)"></image>
 					</view>
 				</view>
-				<view class="card-footer">
-					<text class="time">{{ item.createTime || '-' }}</text>
-				</view>
 			</view>
 
-			
+
 			<view v-if="historyList.length > 0 && !hasMore" class="no-more">没有更多了</view>
 		</view>
 	</view>
@@ -62,7 +59,7 @@ const loadData = async (isLoadMore = false) => {
 			pageNum: pageNum.value,
 			pageSize: pageSize.value
 		})
-		
+
 		const rows = res.rows || []
 		if (isLoadMore) {
 			historyList.value = [...historyList.value, ...rows]
@@ -129,20 +126,19 @@ const previewImage = (urls, index) => {
 	border-radius: 20rpx;
 	padding: 28rpx;
 	margin-bottom: 24rpx;
-	box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03);
+	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
 }
 
 .card-header {
 	display: flex;
 	justify-content: space-between;
 	align-items: center;
-	border-bottom: 2rpx solid #EEEEEE;
 }
 
 .left-box {
 	display: flex;
 	align-items: center;
-	gap: 16rpx;
+	gap: 12rpx;
 }
 
 .order-no {
@@ -151,26 +147,52 @@ const previewImage = (urls, index) => {
 	font-weight: 500;
 }
 
-.status-tag {
+.submitted-tag {
 	font-size: 20rpx;
+	color: #999;
+	background: #f0f0f0;
 	padding: 2rpx 12rpx;
 	border-radius: 6rpx;
-	font-weight: 700;
-	&.praise {
+	font-weight: 500;
+}
+
+.praise-badge {
+	display: flex;
+	align-items: center;
+	gap: 6rpx;
+	padding: 4rpx 14rpx;
+	border-radius: 8rpx;
+	font-size: 22rpx;
+	font-weight: 600;
+	flex-shrink: 0;
+
+	&.agree {
 		color: #52c41a;
 		background: #f6ffed;
-		border: 1rpx solid #b7eb8f;
 	}
-	&.complaint {
+
+	&.disagree {
 		color: #ff4d4f;
 		background: #fff1f0;
-		border: 1rpx solid #ffa39e;
 	}
 }
 
-.status-text {
+.badge-icon {
+	width: 28rpx;
+	height: 28rpx;
+	flex-shrink: 0;
+}
+
+.badge-text {
+	line-height: 1;
+}
+
+.card-time {
 	font-size: 24rpx;
 	color: #999;
+	margin-top: 12rpx;
+	padding-bottom: 16rpx;
+	border-bottom: 2rpx solid #EEEEEE;
 }
 
 .reason-row {
@@ -179,11 +201,6 @@ const previewImage = (urls, index) => {
 	gap: 12rpx;
 }
 
-.label {
-	font-size: 24rpx;
-	color: #999;
-}
-
 .reason-content {
 	font-size: 28rpx;
 	color: #333;
@@ -204,19 +221,6 @@ const previewImage = (urls, index) => {
 	background: #f5f5f5;
 }
 
-.card-footer {
-	margin-top: 24rpx;
-	padding-top: 20rpx;
-	border-top: 2rpx solid #EEEEEE;
-	display: flex;
-	justify-content: flex-end;
-}
-
-.time {
-	font-size: 24rpx;
-	color: #999;
-}
-
 .no-more {
 	text-align: center;
 	font-size: 24rpx;
@@ -224,4 +228,3 @@ const previewImage = (urls, index) => {
 	padding: 20rpx 0;
 }
 </style>
-

+ 1 - 1
pages/my/pet/detail/index.vue

@@ -126,7 +126,7 @@ import { getPet } from '@/api/archieves/pet'
 import navBar from '@/components/nav-bar/index.vue'
 import customerEnums from '@/json/customer.json'
 
-const defaultAvatar = 'https://images.unsplash.com/photo-1552053831-71594a27632d?q=80&w=600&auto=format&fit=crop'
+const defaultAvatar = '/static/icon/pet.svg'
 const petId = ref(null)
 const petInfo = ref({})
 

+ 3 - 2
pages/my/pet/list/index.vue

@@ -5,7 +5,8 @@
 		<view class="action-bar">
 			<view class="search-box">
 				<text class="search-icon">🔍</text>
-				<input type="text" v-model="searchKeyword" placeholder="搜索宠物名/主人" class="search-input" confirm-type="search" @confirm="onSearch" />
+				<input type="text" v-model="searchKeyword" placeholder="搜索宠物名/主人" class="search-input"
+					confirm-type="search" @confirm="onSearch" />
 			</view>
 			<view class="add-btn" @click="goToAdd">+ 新增档案</view>
 		</view>
@@ -13,7 +14,7 @@
 		<!-- 宠物档案卡片列表 -->
 		<view class="list-container">
 			<view class="pet-card" v-for="pet in pets" :key="pet.id" @click="handleCardClick(pet)">
-				<image :src="pet.avatarUrl || '/static/default-avatar.png'" class="pet-photo" mode="aspectFill"></image>
+				<image :src="pet.avatarUrl || '/static/icon/pet.svg'" class="pet-photo" mode="aspectFill"></image>
 				<view class="card-info">
 					<view class="info-top">
 						<text class="pet-name">{{ pet.name }}</text>

+ 19 - 1
pages/my/settings/index.vue

@@ -16,6 +16,10 @@
 					<text class="cell-value">{{ cacheSize }}</text>
 					<text class="arrow">›</text>
 				</view>
+				<view class="cell-item">
+					<text class="cell-title">版本号</text>
+					<text class="cell-value">v{{ appVersion }}</text>
+				</view>
 			</view>
 
 			<view class="cell-group danger-group">
@@ -39,6 +43,17 @@ import navBar from '@/components/nav-bar/index.vue'
 
 const cacheSize = ref('0 KB')
 
+const appVersion = ref('1.0.0')
+// #ifdef APP-PLUS
+plus.runtime.getProperty(plus.runtime.appid, (widgetInfo) => {
+	appVersion.value = widgetInfo.version || '1.0.0'
+})
+// #endif
+// #ifndef APP-PLUS
+const systemInfo = uni.getSystemInfoSync()
+appVersion.value = systemInfo.appVersion || '1.0.0'
+// #endif
+
 const calculateCacheSize = () => {
 	try {
 		const res = uni.getStorageInfoSync()
@@ -168,6 +183,9 @@ const onLogout = () => {
 	font-size: 30rpx;
 	font-weight: 500;
 	line-height: 84rpx;
-	&::after { border: none; }
+
+	&::after {
+		border: none;
+	}
 }
 </style>

+ 1 - 1
pages/my/user/detail/index.vue

@@ -2,7 +2,7 @@
 	<view class="user-detail-page">
 		<NavBar title="用户详情" bgColor="#ffd53f" color="#5c4314"></NavBar>
 		<view class="user-hero">
-			<image :src="user.avatarUrl || 'https://img.icons8.com/?size=256&id=23235&format=png'" class="avatar" mode="aspectFill"></image>
+			<image :src="user.avatarUrl || (user.gender === 1 ? '/static/icon/remale.svg' : '/static/icon/male.svg')" class="avatar" mode="aspectFill"></image>
 			<view class="hero-info">
 				<text class="user-name">{{ user.name || '-' }}</text>
 				<text class="gender-text" v-if="user.gender !== undefined">({{ user.gender === 1 ? '女' : '男' }})</text>

+ 65 - 61
pages/my/user/list/index.vue

@@ -5,7 +5,8 @@
 		<view class="action-bar">
 			<view class="search-box">
 				<uni-icons type="search" size="14" color="#999"></uni-icons>
-				<input type="text" v-model="searchValue" placeholder="搜索姓名/手机号" class="search-input" confirm-type="search" @confirm="onSearch" />
+				<input type="text" v-model="searchValue" placeholder="搜索姓名/手机号" class="search-input"
+					confirm-type="search" @confirm="onSearch" />
 			</view>
 			<picker :range="statusOptions" range-key="label" @change="onStatusFilterChange">
 				<view class="filter-btn">
@@ -20,7 +21,9 @@
 		<view class="list-container">
 			<view class="user-card" v-for="user in users" :key="user.id">
 				<view class="user-header">
-					<image :src="user.avatarUrl || '/static/default-avatar.png'" class="user-avatar" mode="aspectFill"></image>
+					<image
+						:src="user.avatarUrl || (user.gender === 1 ? '/static/icon/remale.svg' : '/static/icon/male.svg')"
+						class="user-avatar" mode="aspectFill"></image>
 					<view class="user-info-main">
 						<text class="user-name">{{ user.name }}</text>
 						<text class="phone-row">{{ user.phone }}</text>
@@ -59,7 +62,8 @@
 				</view>
 			</view>
 
-			<view v-if="users.length === 0" style="text-align: center; color: #999; padding: 50rpx 0; font-size: 28rpx;">
+			<view v-if="users.length === 0"
+				style="text-align: center; color: #999; padding: 50rpx 0; font-size: 28rpx;">
 				暂无用户数据
 			</view>
 		</view>
@@ -79,8 +83,8 @@ const statusOptions = [{ label: '状态', value: undefined }, { label: '正常',
 const currentStatusOption = ref(0) // Default to 全部状态
 
 const onStatusFilterChange = (e) => {
-    currentStatusOption.value = e.detail.value
-    onSearch()
+	currentStatusOption.value = e.detail.value
+	onSearch()
 }
 
 const pageNum = ref(1)
@@ -89,49 +93,49 @@ const hasMore = ref(true)
 
 const loadUsers = async (isLoadMore = false) => {
 	try {
-        uni.showNavigationBarLoading()
-        const res = await listCustomer({
-            pageNum: pageNum.value,
-            pageSize: pageSize.value,
-            keyword: searchValue.value,
-            status: statusOptions[currentStatusOption.value].value
-        })
-        const rows = res?.rows || []
-        if (isLoadMore) {
-            users.value = [...users.value, ...rows]
-        } else {
-            users.value = rows
-        }
-        hasMore.value = users.value.length < (res?.total || 0)
+		uni.showNavigationBarLoading()
+		const res = await listCustomer({
+			pageNum: pageNum.value,
+			pageSize: pageSize.value,
+			keyword: searchValue.value,
+			status: statusOptions[currentStatusOption.value].value
+		})
+		const rows = res?.rows || []
+		if (isLoadMore) {
+			users.value = [...users.value, ...rows]
+		} else {
+			users.value = rows
+		}
+		hasMore.value = users.value.length < (res?.total || 0)
 	} catch (error) {
 		console.error('获取用户列表失败', error)
 		uni.showToast({ title: typeof error === 'string' ? error : '获取用户列表失败', icon: 'none' })
 	} finally {
-        uni.hideNavigationBarLoading()
-        uni.stopPullDownRefresh()
-    }
+		uni.hideNavigationBarLoading()
+		uni.stopPullDownRefresh()
+	}
 }
 
 onShow(() => {
-    pageNum.value = 1
-    loadUsers()
+	pageNum.value = 1
+	loadUsers()
 })
 
 onPullDownRefresh(() => {
-    pageNum.value = 1
-    loadUsers()
+	pageNum.value = 1
+	loadUsers()
 })
 
 onReachBottom(() => {
-    if (hasMore.value) {
-        pageNum.value++
-        loadUsers(true)
-    }
+	if (hasMore.value) {
+		pageNum.value++
+		loadUsers(true)
+	}
 })
 
 const onSearch = () => {
-    pageNum.value = 1
-    loadUsers()
+	pageNum.value = 1
+	loadUsers()
 }
 
 const goToAdd = () => uni.navigateTo({ url: '/pages/my/user/add/index' })
@@ -141,35 +145,35 @@ const goToPetList = (user) => uni.navigateTo({ url: `/pages/my/pet/list/index?us
 const goToOrderList = (user) => uni.reLaunch({ url: '/pages/order/list/index' })
 
 const onStatusChange = (e, user) => {
-    const originalStatus = user.status
-    const targetStatus = e.detail.value ? 0 : 1
-    const text = targetStatus === 0 ? '启用' : '停用'
-
-    uni.showModal({
-        title: '提示',
-        content: `确认要${text}该用户吗?`,
-        success: async (res) => {
-            if (res.confirm) {
-                try {
-                    uni.showLoading({ title: '处理中...' })
-                    await changeCustomerStatus(user.id, targetStatus)
-                    uni.hideLoading()
-                    user.status = targetStatus
-                    uni.showToast({ title: `已${text}`, icon: 'success' })
-                } catch(err) {
-                    uni.hideLoading()
-                    uni.showToast({ title: typeof err === 'string' ? err : '操作失败', icon: 'none' })
-                    // 恢复源数据渲染,利用 setTimeout 强制触发 vue 的重算
-                    user.status = targetStatus
-                    setTimeout(() => { user.status = originalStatus }, 50)
-                }
-            } else {
-                // 用户取消,恢复 switch
-                user.status = targetStatus
-                setTimeout(() => { user.status = originalStatus }, 50)
-            }
-        }
-    })
+	const originalStatus = user.status
+	const targetStatus = e.detail.value ? 0 : 1
+	const text = targetStatus === 0 ? '启用' : '停用'
+
+	uni.showModal({
+		title: '提示',
+		content: `确认要${text}该用户吗?`,
+		success: async (res) => {
+			if (res.confirm) {
+				try {
+					uni.showLoading({ title: '处理中...' })
+					await changeCustomerStatus(user.id, targetStatus)
+					uni.hideLoading()
+					user.status = targetStatus
+					uni.showToast({ title: `已${text}`, icon: 'success' })
+				} catch (err) {
+					uni.hideLoading()
+					uni.showToast({ title: typeof err === 'string' ? err : '操作失败', icon: 'none' })
+					// 恢复源数据渲染,利用 setTimeout 强制触发 vue 的重算
+					user.status = targetStatus
+					setTimeout(() => { user.status = originalStatus }, 50)
+				}
+			} else {
+				// 用户取消,恢复 switch
+				user.status = targetStatus
+				setTimeout(() => { user.status = originalStatus }, 50)
+			}
+		}
+	})
 }
 </script>
 

+ 909 - 141
pages/order/apply/index.vue

@@ -1,7 +1,7 @@
 <template>
 	<view class="order-apply-page">
 		<nav-bar title="下单预约"></nav-bar>
-		
+
 		<view class="apply-content">
 			<!-- 01 服务类型 -->
 			<text class="section-title">01 服务类型</text>
@@ -23,7 +23,8 @@
 			<view class="card basic-info-card">
 				<view class="field-item" @click="showShopSelect = true">
 					<text class="field-label require">服务门店</text>
-					<text :class="['field-value', !formData.shopName ? 'placeholder' : '']">{{ formData.shopName || '请选择商户门店' }}</text>
+					<text :class="['field-value', !formData.shopName ? 'placeholder' : '']">{{ formData.shopName ||
+						'请选择商户门店' }}</text>
 					<view class="right-arrow"></view>
 				</view>
 				<view class="field-item" @click="showUserSelect = true">
@@ -39,7 +40,8 @@
 				</view>
 				<view class="field-item" @click="openPetPicker">
 					<text class="field-label require">选择宠物</text>
-					<text :class="['field-value', !formData.petName ? 'placeholder' : '']">{{ formData.petName || '选择宠物档案' }}</text>
+					<text :class="['field-value', !formData.petName ? 'placeholder' : '']">{{ formData.petName ||
+						'选择宠物档案' }}</text>
 					<view class="right-arrow"></view>
 				</view>
 			</view>
@@ -52,7 +54,7 @@
 						<text class="field-label">团购套餐</text>
 						<input class="field-input" v-model="formData.packageName" placeholder="请输入套餐名称(选填)" />
 					</view>
-					
+
 					<text class="form-item-label require">接送模式</text>
 					<view class="mode-select">
 						<view v-for="mode in transportModes" :key="mode.value"
@@ -68,24 +70,28 @@
 						<view class="route-fields">
 							<text class="addr-label require">起点 (用户家)</text>
 							<view class="route-picker-trigger" @click="openRegionSelect('pick')">
-								<text :class="['display-text', !formData.pickArea ? 'placeholder' : '']">{{ pickAreaLabel || '选择省/市/区' }}</text>
+								<text :class="['display-text', !formData.pickArea ? 'placeholder' : '']">{{
+									pickAreaLabel || '选择省/市/区' }}</text>
 								<view class="right-arrow"></view>
 							</view>
 							<input class="route-input" v-model="formData.pickAddress" placeholder="详细地址" />
-							
+
 							<text class="addr-label require">终点 (门店)</text>
 							<view class="route-picker-trigger" @click="openRegionSelect('pickEnd')">
-								<text :class="['display-text', !formData.pickEndArea ? 'placeholder' : '']">{{ pickEndAreaLabel || '选择省/市/区' }}</text>
+								<text :class="['display-text', !formData.pickEndArea ? 'placeholder' : '']">{{
+									pickEndAreaLabel || '选择省/市/区' }}</text>
 								<view class="right-arrow"></view>
 							</view>
 							<input class="route-input" v-model="formData.pickEndAddress" placeholder="详细地址" />
-							
+
 							<view class="contact-row">
 								<input class="route-input half" v-model="formData.pickContact" placeholder="联系人" />
-								<input class="route-input half" v-model="formData.pickPhone" placeholder="电话" type="tel" />
+								<input class="route-input half" v-model="formData.pickPhone" placeholder="电话"
+									type="tel" />
 							</view>
 							<view class="route-time-trigger" @click="openTimeModal('pick')">
-								<text :class="!formData.pickTime ? 'placeholder' : ''">{{ pickTimeDisplay || '设置接宠时间' }}</text>
+								<text :class="!formData.pickStartTime ? 'placeholder' : ''">{{
+									truncateTime(formData.pickStartTime) || '设置接宠时间' }}</text>
 							</view>
 						</view>
 					</view>
@@ -96,31 +102,36 @@
 						<view class="route-fields">
 							<text class="addr-label require">起点 (门店)</text>
 							<view class="route-picker-trigger" @click="openRegionSelect('sendStart')">
-								<text :class="['display-text', !formData.sendStartArea ? 'placeholder' : '']">{{ sendStartAreaLabel || '选择省/市/区' }}</text>
+								<text :class="['display-text', !formData.sendStartArea ? 'placeholder' : '']">{{
+									sendStartAreaLabel || '选择省/市/区' }}</text>
 								<view class="right-arrow"></view>
 							</view>
 							<input class="route-input" v-model="formData.sendStartAddress" placeholder="详细地址" />
-							
+
 							<text class="addr-label require">终点 (用户家)</text>
 							<view class="route-picker-trigger" @click="openRegionSelect('send')">
-								<text :class="['display-text', !formData.sendArea ? 'placeholder' : '']">{{ sendAreaLabel || '选择省/市/区' }}</text>
+								<text :class="['display-text', !formData.sendArea ? 'placeholder' : '']">{{
+									sendAreaLabel || '选择省/市/区' }}</text>
 								<view class="right-arrow"></view>
 							</view>
 							<input class="route-input" v-model="formData.sendAddress" placeholder="详细地址" />
-							
+
 							<view class="contact-row">
 								<input class="route-input half" v-model="formData.sendContact" placeholder="联系人" />
-								<input class="route-input half" v-model="formData.sendPhone" placeholder="电话" type="tel" />
+								<input class="route-input half" v-model="formData.sendPhone" placeholder="电话"
+									type="tel" />
 							</view>
 							<view class="route-time-trigger" @click="openTimeModal('send')">
-								<text :class="!formData.sendTime ? 'placeholder' : ''">{{ sendTimeDisplay || '设置送宠时间' }}</text>
+								<text :class="!formData.sendStartTime ? 'placeholder' : ''">{{
+									truncateTime(formData.sendStartTime) || '设置送宠时间' }}</text>
 							</view>
 						</view>
 					</view>
 
 					<!-- 接送备注 -->
 					<text class="remarks-title">备注信息</text>
-					<textarea class="remarks-textarea" v-model="formData.transportNote" placeholder="请添加接送备注 (如宠物性格、接送要求等)"></textarea>
+					<textarea class="remarks-textarea" v-model="formData.transportNote"
+						placeholder="请添加接送备注 (如宠物性格、接送要求等)"></textarea>
 				</view>
 			</template>
 
@@ -137,35 +148,36 @@
 						<view class="route-fields">
 							<text class="addr-label require">上门服务地址</text>
 							<view class="route-picker-trigger" @click="openRegionSelect('service')">
-								<text :class="['display-text', !formData.serviceArea ? 'placeholder' : '']">{{ serviceAreaLabel || '请选择省/市/区' }}</text>
+								<text :class="['display-text', !formData.serviceArea ? 'placeholder' : '']">{{
+									serviceAreaLabel || '请选择省/市/区' }}</text>
 								<view class="right-arrow"></view>
 							</view>
-							<input class="route-input" v-model="formData.serviceAddress" placeholder="详细地址 (街道/路名/门牌号)" />
+							<input class="route-input" v-model="formData.serviceAddress"
+								placeholder="详细地址 (街道/路名/门牌号)" />
 						</view>
 					</view>
 
 					<view class="booking-section">
 						<view class="booking-header">
 							<text class="label require">预约服务时间</text>
-							<view class="count-tag">共 {{ formData.feedTimes.length }} 次</view>
+							<text class="count-tag">共 {{ formData.feedTimes.length }} 次</text>
 						</view>
 						<view class="time-item-row" v-for="(time, index) in formData.feedTimes" :key="index">
-							<text class="index">{{ index + 1 }}.</text>
-							<view class="flex-time-box" @click="openTimeModal('feed', index, 'start')">
-								<text :class="['time-text', !time.start ? 'placeholder' : '']">{{ truncateTime(time.start) || '开始' }}</text>
-							</view>
-							<text class="to-line">~</text>
-							<view class="flex-time-box" @click="openTimeModal('feed', index, 'end')">
-								<text :class="['time-text', !time.end ? 'placeholder' : '']">{{ truncateTime(time.end) || '结束' }}</text>
+							<view class="flex-time-range" @click="openTimeModal('feed', index)">
+								<text :class="['time-text', !time.start && !time.end ? 'placeholder' : '']">{{
+									feedTimeDisplay(time) || '开始 ~ 结束' }}</text>
 							</view>
 							<view class="action-buttons">
-								<view class="circle-btn add" v-if="index === formData.feedTimes.length - 1" @click="addFeedTime">+</view>
-								<view class="circle-btn remove" v-if="formData.feedTimes.length > 1" @click="removeFeedTime(index)">-</view>
+								<view class="circle-btn add" v-if="index === formData.feedTimes.length - 1"
+									@click="addFeedTime">+</view>
+								<view class="circle-btn remove" v-if="formData.feedTimes.length > 1"
+									@click="removeFeedTime(index)">-</view>
 							</view>
 						</view>
 					</view>
 					<text class="remarks-title">备注信息</text>
-					<textarea class="remarks-textarea" v-model="formData.otherNote" placeholder="如有其他注意事项请备注"></textarea>
+					<textarea class="remarks-textarea" v-model="formData.otherNote"
+						placeholder="如有其他注意事项请备注"></textarea>
 				</view>
 			</template>
 
@@ -174,7 +186,8 @@
 			<view class="card quote-card">
 				<view class="field-item">
 					<text class="field-label require">报价金额</text>
-					<input class="field-input quote-input" v-model="formData.quoteAmount" type="digit" placeholder="填入数字" />
+					<input class="field-input quote-input" v-model="formData.quoteAmount" type="digit"
+						placeholder="填入数字" />
 					<text class="unit-text">元</text>
 				</view>
 				<text class="quote-tips">注:此价格将作为订单最终结算金额。</text>
@@ -192,9 +205,12 @@
 		</view>
 
 		<!-- 居中联动选择弹窗群 @Author: Antigravity -->
-		
+
 		<!-- 宠主搜索弹窗 -->
-		<page-select v-model="showUserSelect" title="选择宠主用户" searchable :searchKey="userSearchKey" searchPlaceholder="搜索宠主姓名/手机号" :options="userList" labelKey="name" valueKey="id" :value="formData.customerId" :loading="userPage.loading" :finished="userPage.finished" emptyText="未找到相关宠主" @select="onUserSelect" @loadMore="fetchUsers(false)" @search="onUserSearch">
+		<page-select v-model="showUserSelect" title="选择宠主用户" searchable :searchKey="userSearchKey"
+			searchPlaceholder="搜索宠主姓名/手机号" :options="userList" labelKey="name" valueKey="id"
+			:value="formData.customerId" :loading="userPage.loading" :finished="userPage.finished" emptyText="未找到相关宠主"
+			@select="onUserSelect" @loadMore="fetchUsers(false)" @search="onUserSearch">
 			<template #item="{ item }">
 				<view class="user-info">
 					<text class="name">{{ item.name }}</text>
@@ -202,17 +218,22 @@
 				</view>
 			</template>
 		</page-select>
-		
+
 		<!-- 区域选择器 (Cascader) @Author: Antigravity -->
 		<view class="center-modal-mask" v-if="showRegionModal" @click="showRegionModal = false">
 			<view class="center-modal-content region-modal" @click.stop>
-				<view class="modal-header"><text class="modal-title">选择区域</text><view class="close-btn" @click="showRegionModal = false"></view></view>
+				<view class="modal-header"><text class="modal-title">选择区域</text>
+					<view class="close-btn" @click="showRegionModal = false"></view>
+				</view>
 				<view class="cascade-indicator">
-					<text v-for="(node, idx) in regionPath" :key="idx" class="path-node" @click="backToLevel(idx)">{{ node.name }}</text>
+					<text v-for="(node, idx) in regionPath" :key="idx" class="path-node" @click="backToLevel(idx)">{{
+						node.name
+					}}</text>
 					<text class="path-node active" v-if="regionPath.length < 3">请选择</text>
 				</view>
 				<scroll-view scroll-y class="modal-list-scroll">
-					<view class="list-item" v-for="item in currentRegionList" :key="item.code" @click="onRegionStepSelect(item)">
+					<view class="list-item" v-for="item in currentRegionList" :key="item.code"
+						@click="onRegionStepSelect(item)">
 						<text class="item-text">{{ item.name }}</text>
 						<view class="checkmark" v-if="isRegionSelected(item)"></view>
 					</view>
@@ -220,26 +241,65 @@
 			</view>
 		</view>
 
-	<!-- 门店选择 -->
-	<page-select v-model="showShopSelect" title="选择服务门店" searchable :searchKey="shopSearchKey" searchPlaceholder="搜索门店名称" :options="shopList" labelKey="name" valueKey="id" :value="formData.merchantId" :loading="shopPage.loading" :finished="shopPage.finished" @select="onShopSelect" @loadMore="fetchShops(true)" @search="onShopSearch" />
-		
+		<!-- 门店选择 -->
+		<page-select v-model="showShopSelect" title="选择服务门店" searchable :searchKey="shopSearchKey"
+			searchPlaceholder="搜索门店名称" :options="shopList" labelKey="name" valueKey="id" :value="formData.merchantId"
+			:loading="shopPage.loading" :finished="shopPage.finished" @select="onShopSelect"
+			@loadMore="fetchShops(true)" @search="onShopSearch" />
+
 		<!-- 宠物选择 -->
-		<center-select v-model="showPetPopup" title="选择指定宠物" :options="petOptions" labelKey="_label" valueKey="id" :value="formData.petId" @select="onPetSelect" />
+		<center-select v-model="showPetPopup" title="选择指定宠物" :options="petOptions" labelKey="_label" valueKey="id"
+			:value="formData.petId" @select="onPetSelect" />
 
 		<!-- 日期时间选择弹窗 @Author: Antigravity -->
 		<view class="center-modal-mask" v-if="showTimeModal" @click="showTimeModal = false">
 			<view class="center-modal-content time-modal" @click.stop>
 				<view class="modal-header"><text class="modal-title">选择预约时间</text></view>
 				<view class="datetime-picker-body">
-					<picker-view class="picker-view" :value="tempTimeIdx" @change="onTempTimeChange">
-						<picker-view-column><view class="picker-item" v-for="d in timeRanges[0]" :key="d">{{ d }}</view></picker-view-column>
-						<picker-view-column><view class="picker-item" v-for="h in timeRanges[1]" :key="h">{{ h }}时</view></picker-view-column>
-						<picker-view-column><view class="picker-item" v-for="m in timeRanges[2]" :key="m">{{ m }}分</view></picker-view-column>
-					</picker-view>
+					<template v-if="isDualTimePicker">
+						<view class="time-slot-row">
+							<view :class="['time-slot', { active: activeSlot === 'start' }]"
+								@click="activeSlot = 'start'">
+								<text class="slot-label">开始时间</text>
+								<text :class="['slot-value', !tempStartDisplay ? 'placeholder' : '']">{{
+									tempStartDisplay || '请选择' }}</text>
+							</view>
+							<view :class="['time-slot', { active: activeSlot === 'end' }]" @click="activeSlot = 'end'">
+								<text class="slot-label">结束时间</text>
+								<text :class="['slot-value', !tempEndDisplay ? 'placeholder' : '']">{{ tempEndDisplay ||
+									'请选择' }}</text>
+							</view>
+						</view>
+						<picker-view class="picker-view" :value="tempTimeIdx" @change="onTempTimeChange">
+							<picker-view-column>
+								<view class="picker-item" v-for="d in timeRanges[0]" :key="d">{{ d }}</view>
+							</picker-view-column>
+							<picker-view-column>
+								<view class="picker-item" v-for="h in timeRanges[1]" :key="h">{{ h }}时</view>
+							</picker-view-column>
+							<picker-view-column>
+								<view class="picker-item" v-for="m in timeRanges[2]" :key="m">{{ m }}分</view>
+							</picker-view-column>
+						</picker-view>
+					</template>
+					<template v-else>
+						<picker-view class="picker-view" :value="tempSingleIdx"
+							@change="(e) => tempSingleIdx = e.detail.value">
+							<picker-view-column>
+								<view class="picker-item" v-for="d in timeRanges[0]" :key="d">{{ d }}</view>
+							</picker-view-column>
+							<picker-view-column>
+								<view class="picker-item" v-for="h in timeRanges[1]" :key="h">{{ h }}时</view>
+							</picker-view-column>
+							<picker-view-column>
+								<view class="picker-item" v-for="m in timeRanges[2]" :key="m">{{ m }}分</view>
+							</picker-view-column>
+						</picker-view>
+					</template>
 				</view>
 				<view class="modal-footer">
 					<view class="modal-cancel" @click="showTimeModal = false">取消</view>
-					<view class="modal-confirm" @click="confirmTime">确定</view>
+					<view class="modal-confirm" @click="confirmTime">确</view>
 				</view>
 			</view>
 		</view>
@@ -295,16 +355,24 @@ const serviceAreaLabel = ref('')
 const formData = reactive({
 	merchantId: '', shopName: '', customerId: '', customerName: '', petId: '', petName: '',
 	packageName: '', transportMode: 'round_trip',
-	pickArea: '', pickAddress: '', pickEndArea: '', pickEndAddress: '', pickContact: '', pickPhone: '', pickTime: '',
-	sendStartArea: '', sendStartAddress: '', sendArea: '', sendAddress: '', sendContact: '', sendPhone: '', sendTime: '',
+	pickArea: '', pickAddress: '', pickEndArea: '', pickEndAddress: '', pickContact: '', pickPhone: '',
+	pickStartTime: '', pickEndTime: '',
+	sendStartArea: '', sendStartAddress: '', sendArea: '', sendAddress: '', sendContact: '', sendPhone: '',
+	sendStartTime: '', sendEndTime: '',
 	serviceArea: '', serviceAddress: '', feedTimes: [{ start: '', end: '' }],
 	otherNote: '', transportNote: '', quoteAmount: ''
 })
 
 // 时间选择器逻辑 (5分钟一个间隔 @Author: Antigravity)
 const timeRanges = ref([[], [], []])
+const activeSlot = ref('start')
+const tempStartIdx = ref([0, 0, 0])
+const tempEndIdx = ref([0, 0, 0])
 const tempTimeIdx = ref([0, 0, 0])
-const timeCtx = reactive({ type: '', index: 0, field: '' })
+const tempSingleIdx = ref([0, 0, 0])
+const timeCtx = reactive({ type: '', index: 0 })
+
+const isDualTimePicker = computed(() => timeCtx.type === 'feed')
 
 onLoad((options) => {
 	if (options.service) activeService.value = options.service
@@ -329,29 +397,100 @@ const initTimeRanges = () => {
 	]
 }
 
-const openTimeModal = (type, index = 0, field = '') => {
-	timeCtx.type = type; timeCtx.index = index; timeCtx.field = field
-	tempTimeIdx.value = [0, 0, 0]
+const openTimeModal = (type, index = 0) => {
+	timeCtx.type = type; timeCtx.index = index
+	activeSlot.value = 'start'
+	const readTime = (t) => {
+		if (!t) return null
+		const match = t.match(/(\d{2})-(\d{2}) (\d{2}):(\d{2})/)
+		if (!match) return null
+		const monthDay = `${match[1]}-${match[2]}`
+		const di = timeRanges.value[0].findIndex(d => d === monthDay)
+		const hi = parseInt(match[3])
+		const mi = timeRanges.value[2].findIndex(m => parseInt(m) === parseInt(match[4]))
+		return [di < 0 ? 0 : di, hi < 0 ? 0 : hi, mi < 0 ? 0 : mi]
+	}
+	const nowIdx = () => {
+		const now = new Date()
+		const monthDay = `${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
+		const di = timeRanges.value[0].findIndex(d => d === monthDay)
+		const hi = now.getHours()
+		const mi = timeRanges.value[2].findIndex(m => parseInt(m) >= now.getMinutes())
+		return [di < 0 ? 0 : di, hi < 0 ? 0 : hi, mi < 0 ? 0 : mi]
+	}
+	if (type === 'feed') {
+		tempStartIdx.value = readTime(formData.feedTimes[index].start) || nowIdx()
+		tempEndIdx.value = readTime(formData.feedTimes[index].end) || nowIdx()
+		tempTimeIdx.value = [...tempStartIdx.value]
+	} else if (type === 'pick') {
+		tempSingleIdx.value = readTime(formData.pickStartTime) || nowIdx()
+	} else if (type === 'send') {
+		tempSingleIdx.value = readTime(formData.sendStartTime) || nowIdx()
+	}
 	showTimeModal.value = true
 }
 
-const onTempTimeChange = (e) => { tempTimeIdx.value = e.detail.value }
+const onTempTimeChange = (e) => {
+	tempTimeIdx.value = e.detail.value
+	if (activeSlot.value === 'start') {
+		tempStartIdx.value = [...e.detail.value]
+	} else {
+		tempEndIdx.value = [...e.detail.value]
+	}
+}
+
+const buildTimeLabel = (idx) => {
+	const d = timeRanges.value[0][idx[0]]
+	const h = timeRanges.value[1][idx[1]]
+	const m = timeRanges.value[2][idx[2]]
+	return d && h !== undefined && m !== undefined ? `${d} ${h}:${m}` : ''
+}
+
+const tempStartDisplay = computed(() => buildTimeLabel(tempStartIdx.value))
+const tempEndDisplay = computed(() => buildTimeLabel(tempEndIdx.value))
+
+watch(activeSlot, (val) => {
+	tempTimeIdx.value = val === 'start' ? [...tempStartIdx.value] : [...tempEndIdx.value]
+})
 
 const confirmTime = () => {
-	const [di, hi, mi] = tempTimeIdx.value
-	const val = `${new Date().getFullYear()}-${timeRanges.value[0][di]} ${timeRanges.value[1][hi]}:${timeRanges.value[2][mi]}:00`
-	if (timeCtx.type === 'pick') formData.pickTime = val
-	else if (timeCtx.type === 'send') formData.sendTime = val
-	else if (timeCtx.type === 'feed') {
-		if (timeCtx.field === 'start') formData.feedTimes[timeCtx.index].start = val
-		else formData.feedTimes[timeCtx.index].end = val
+	const buildTime = (idx) => {
+		const [di, hi, mi] = idx
+		return `${new Date().getFullYear()}-${timeRanges.value[0][di]} ${timeRanges.value[1][hi]}:${timeRanges.value[2][mi]}:00`
+	}
+	if (timeCtx.type === 'feed') {
+		const startVal = buildTime(tempStartIdx.value)
+		const endVal = buildTime(tempEndIdx.value)
+		formData.feedTimes[timeCtx.index].start = startVal
+		formData.feedTimes[timeCtx.index].end = endVal
+	} else if (timeCtx.type === 'pick') {
+		const val = buildTime(tempSingleIdx.value)
+		formData.pickStartTime = val
+		formData.pickEndTime = val
+	} else if (timeCtx.type === 'send') {
+		const val = buildTime(tempSingleIdx.value)
+		formData.sendStartTime = val
+		formData.sendEndTime = val
 	}
 	showTimeModal.value = false
 }
 
 const truncateTime = (t) => t ? t.substring(5, 16) : ''
-const pickTimeDisplay = computed(() => truncateTime(formData.pickTime))
-const sendTimeDisplay = computed(() => truncateTime(formData.sendTime))
+const feedTimeDisplay = (time) => {
+	const s = truncateTime(time.start)
+	const e = truncateTime(time.end)
+	return s && e ? `${s} ~ ${e}` : (s || e || '')
+}
+const pickTimeDisplay = computed(() => {
+	const s = truncateTime(formData.pickStartTime)
+	const e = truncateTime(formData.pickEndTime)
+	return s && e ? `${s} ~ ${e}` : (s || e || '')
+})
+const sendTimeDisplay = computed(() => {
+	const s = truncateTime(formData.sendStartTime)
+	const e = truncateTime(formData.sendEndTime)
+	return s && e ? `${s} ~ ${e}` : (s || e || '')
+})
 
 // 区域选择逻辑
 const regionPath = ref([])
@@ -366,10 +505,10 @@ const currentRegionList = computed(() => {
 	return list
 })
 
-const openRegionSelect = (type) => { 
+const openRegionSelect = (type) => {
 	activeRegionType.value = type
 	regionPath.value = []
-	showRegionModal.value = true 
+	showRegionModal.value = true
 }
 
 const backToLevel = (idx) => { regionPath.value = regionPath.value.slice(0, idx) }
@@ -396,7 +535,7 @@ const isRegionSelected = (item) => {
 // 核心回填逻辑修正 @Author: Antigravity
 watch([selectedShop, selectedUser, regionTree], ([shop, user, tree]) => {
 	if (!shop && !user) return
-	
+
 	// 处理门店信息
 	const storeAreaCode = (shop?.areaCode || '').replace(/,/g, '/')
 	const storeLeaf = storeAreaCode.split('/').pop() || ''
@@ -406,7 +545,7 @@ watch([selectedShop, selectedUser, regionTree], ([shop, user, tree]) => {
 	const userAreaCode = (user?.regionCode || '')
 	const userLeaf = userAreaCode.split('/').pop() || ''
 	const userPath = findRegionLabel(userAreaCode, tree)
-	
+
 	if (shop) {
 		formData.merchantId = shop.id; formData.shopName = shop.name
 		// 接送单终点 = 门店
@@ -499,7 +638,7 @@ const onShopSelect = (shop) => { selectedShop.value = shop; showShopSelect.value
 const onShopSearch = (keyword) => { shopSearchKey.value = keyword; fetchShops(false) }
 const onUserSearch = (keyword) => { userSearchKey.value = keyword; fetchUsers(true) }
 const onUserSelect = (user) => {
-	selectedUser.value = user; formData.customerId = user.id; 
+	selectedUser.value = user; formData.customerId = user.id;
 	formData.petId = ''; formData.petName = ''; petList.value = []; fetchPets(user.id)
 	showUserSelect.value = false
 }
@@ -523,8 +662,8 @@ const onSubmit = async () => {
 		const baseMode = serviceInfo.value?.mode || 0
 		const defC = selectedUser.value?.name; const defP = selectedUser.value?.phone || selectedUser.value?.phoneNumber
 		if (activeService.value === 'transport') {
-			if (formData.transportMode !== 'return_home') subOrders.push({ mode: baseMode, type: formData.transportMode === 'round_trip' ? 0 : 2, contact: formData.pickContact || defC, contactPhoneNumber: formData.pickPhone || defP, serviceTime: formData.pickTime, endServiceTime: formData.pickTime, fromCode: formData.pickArea, fromAddress: formData.pickAddress, toCode: formData.pickEndArea, toAddress: formData.pickEndAddress })
-			if (formData.transportMode !== 'pick_up') subOrders.push({ mode: baseMode, type: formData.transportMode === 'round_trip' ? 1 : 3, contact: formData.sendContact || defC, contactPhoneNumber: formData.sendPhone || defP, serviceTime: formData.sendTime, endServiceTime: formData.sendTime, fromCode: formData.sendStartArea, fromAddress: formData.sendStartAddress, toCode: formData.sendArea, toAddress: formData.sendAddress })
+			if (formData.transportMode !== 'return_home') subOrders.push({ mode: baseMode, type: formData.transportMode === 'round_trip' ? 0 : 2, contact: formData.pickContact || defC, contactPhoneNumber: formData.pickPhone || defP, serviceTime: formData.pickStartTime, endServiceTime: formData.pickEndTime, fromCode: formData.pickArea, fromAddress: formData.pickAddress, toCode: formData.pickEndArea, toAddress: formData.pickEndAddress })
+			if (formData.transportMode !== 'pick_up') subOrders.push({ mode: baseMode, type: formData.transportMode === 'round_trip' ? 1 : 3, contact: formData.sendContact || defC, contactPhoneNumber: formData.sendPhone || defP, serviceTime: formData.sendStartTime, endServiceTime: formData.sendEndTime, fromCode: formData.sendStartArea, fromAddress: formData.sendStartAddress, toCode: formData.sendArea, toAddress: formData.sendAddress })
 		} else {
 			formData.feedTimes.forEach(t => subOrders.push({ mode: baseMode, contact: defC, contactPhoneNumber: defP, serviceTime: t.start, endServiceTime: t.end || t.start, fromCode: formData.serviceArea, fromAddress: formData.serviceAddress, toCode: formData.serviceArea, toAddress: formData.serviceAddress }))
 		}
@@ -542,21 +681,68 @@ const onSubmit = async () => {
 
 <style lang="scss" scoped>
 /* 统一页面字体栈 @Author: Antigravity */
-.order-apply-page { 
-	background: #f7f8fa; 
-	min-height: 100vh; 
-	padding-bottom: 220rpx; 
+.order-apply-page {
+	background: #f7f8fa;
+	min-height: 100vh;
+	padding-bottom: 220rpx;
 	font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, 'STHeitiSTXihei', 'Microsoft YaHei', Arial, sans-serif;
 }
-.apply-content { padding: 0 28rpx; }
-.section-title { display: flex; align-items: center; font-size: 28rpx; font-weight: bold; color: #333; margin: 32rpx 0 20rpx; &::before { content: ''; width: 8rpx; height: 26rpx; background: #f7ca3e; margin-right: 16rpx; border-radius: 4rpx; } }
-.card { background: #fff; border-radius: 24rpx; padding: 24rpx; margin-bottom: 24rpx; }
 
-.service-type-display { display: flex; align-items: center; gap: 24rpx; }
-.service-icon-box { width: 88rpx; height: 88rpx; border-radius: 20rpx; display: flex; align-items: center; justify-content: center; }
-.service-icon-box.transport { background: linear-gradient(135deg, #64b5f6, #2196f3); }
-.service-icon-box.feed { background: linear-gradient(135deg, #ffb74d, #ff9800); }
-.service-icon-box.wash { background: linear-gradient(135deg, #81c784, #4caf50); }
+.apply-content {
+	padding: 0 28rpx;
+}
+
+.section-title {
+	display: flex;
+	align-items: center;
+	font-size: 28rpx;
+	font-weight: bold;
+	color: #333;
+	margin: 32rpx 0 20rpx;
+
+	&::before {
+		content: '';
+		width: 8rpx;
+		height: 26rpx;
+		background: #f7ca3e;
+		margin-right: 16rpx;
+		border-radius: 4rpx;
+	}
+}
+
+.card {
+	background: #fff;
+	border-radius: 24rpx;
+	padding: 24rpx;
+	margin-bottom: 24rpx;
+}
+
+.service-type-display {
+	display: flex;
+	align-items: center;
+	gap: 24rpx;
+}
+
+.service-icon-box {
+	width: 88rpx;
+	height: 88rpx;
+	border-radius: 20rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+}
+
+.service-icon-box.transport {
+	background: linear-gradient(135deg, #64b5f6, #2196f3);
+}
+
+.service-icon-box.feed {
+	background: linear-gradient(135deg, #ffb74d, #ff9800);
+}
+
+.service-icon-box.wash {
+	background: linear-gradient(135deg, #81c784, #4caf50);
+}
 
 .main-name {
 	display: block;
@@ -573,70 +759,652 @@ const onSubmit = async () => {
 }
 
 /* CSS 手绘图标 @Author: Antigravity */
-.pure-css-icon { width: 40rpx; height: 40rpx; border: 4rpx solid #fff; border-radius: 8rpx; position: relative; &::after { content: ''; position: absolute; top: 10rpx; left: 10rpx; width: 12rpx; height: 12rpx; background: #fff; border-radius: 50%; } }
-
-.field-item { display: flex; align-items: center; padding: 28rpx 0; border-bottom: 2rpx solid #f5f5f5; height: 44rpx; &:last-child { border-bottom: none; } }
-.field-label { width: 180rpx; font-size: 28rpx; color: #333; flex-shrink: 0; }
-.require::before { content: '*'; color: #f56c6c; margin-right: 4rpx; }
-.field-value { flex: 1; font-size: 28rpx; color: #333; text-align: right; margin-right: 16rpx; }
-.field-value.placeholder { color: #ccc; }
-.field-value-wrap { flex: 1; display: flex; flex-direction: column; align-items: flex-end; margin-right: 16rpx; .selected-name { font-size: 28rpx; font-weight: bold; color: #333; } .selected-phone { font-size: 22rpx; color: #999; } }
-.placeholder { color: #ccc; font-size: 28rpx; }
-
-.mode-select { display: flex; gap: 16rpx; margin: 20rpx 0 32rpx; }
-.mode-btn { flex: 1; height: 60rpx; display: flex; align-items: center; justify-content: center; border: 2rpx solid #f0f0f0; border-radius: 30rpx; font-size: 24rpx; color: #666; &.active { background: #fef8e5; border-color: #f7ca3e; color: #f7ca3e; font-weight: bold; } }
-
-.route-box { display: flex; gap: 20rpx; margin-bottom: 30rpx; }
-.route-icon { width: 44rpx; height: 44rpx; border-radius: 8rpx; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 22rpx; font-weight: bold; flex-shrink: 0; margin-top: 10rpx; }
-.route-icon.pick { background: #5bb7ff; }
-.route-icon.send { background: #64cf5c; }
-.route-icon.service { background: #ff9500; }
-.route-fields { flex: 1; display: flex; flex-direction: column; gap: 6rpx; }
-.addr-label { font-size: 22rpx; color: #999; margin-top: 10rpx; }
-.route-picker-trigger { height: 64rpx; border-bottom: 2rpx solid #f5f5f5; display: flex; align-items: center; justify-content: space-between; .display-text { font-size: 26rpx; color: #333; &.placeholder { color: #ccc; } } }
-.route-input { height: 72rpx; font-size: 26rpx; border-bottom: 2rpx solid #f5f5f5; &.half { flex: 1; } }
-.contact-row { display: flex; gap: 16rpx; }
-.route-time-trigger { height: 72rpx; background: #f9f9f9; border-radius: 12rpx; display: flex; align-items: center; padding: 0 20rpx; font-size: 26rpx; color: #333; margin-top: 10rpx; .placeholder { color: #ccc; } }
-
-.address-title, .form-item-label, .booking-header .label, .remarks-title { display: block; font-size: 26rpx; color: #666; margin: 20rpx 0 10rpx; }
-.booking-section { margin-top: 24rpx; }
-.count-tag { font-size: 20rpx; color: #ff9500; background: #fff3e0; padding: 4rpx 12rpx; border-radius: 6rpx; }
-.time-item-row { display: flex; align-items: center; gap: 12rpx; margin-bottom: 16rpx; }
-.index { font-size: 24rpx; color: #999; width: 30rpx; }
-.flex-time-box { flex: 1; height: 64rpx; background: #fcfcfc; border: 2rpx solid #eee; border-radius: 10rpx; display: flex; align-items: center; justify-content: center; .time-text { font-size: 24rpx; color: #333; &.placeholder { color: #ccc; } } }
-.action-buttons { display: flex; gap: 12rpx; margin-left: 8rpx; }
-.circle-btn { width: 44rpx; height: 44rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; font-weight: bold; &.add { background: #e3f2fd; color: #2196f3; } &.remove { background: #fde2e2; color: #f56c6c; } }
-.remarks-textarea { width: 100%; height: 140rpx; font-size: 26rpx; background: #f9f9f9; border-radius: 16rpx; padding: 16rpx; box-sizing: border-box; }
-
-.quote-input { flex: 1; font-size: 36rpx; color: #f44336; font-weight: bold; text-align: right; }
-.unit-text { font-size: 28rpx; color: #333; margin-left: 8rpx; }
-.quote-tips { display: block; font-size: 22rpx; color: #999; margin-top: 20rpx; }
-
-.footer-bar { position: fixed; bottom: 0; left: 0; right: 0; background: #fff; padding: 24rpx 32rpx; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05); z-index: 100; }
-.quotation-box { display: flex; align-items: baseline; .p-label { font-size: 24rpx; color: #333; } .p-symbol { font-size: 28rpx; color: #f44336; font-weight: bold; margin-left: 8rpx; } .p-amount { font-size: 40rpx; font-weight: 900; color: #f44336; } }
-.submit-btn { width: 280rpx; height: 84rpx; background: linear-gradient(90deg, #ffd53f, #ff9500); color: #fff; border-radius: 42rpx; font-size: 28rpx; font-weight: bold; line-height: 84rpx; &::after { border: none; } }
+.pure-css-icon {
+	width: 40rpx;
+	height: 40rpx;
+	border: 4rpx solid #fff;
+	border-radius: 8rpx;
+	position: relative;
+
+	&::after {
+		content: '';
+		position: absolute;
+		top: 10rpx;
+		left: 10rpx;
+		width: 12rpx;
+		height: 12rpx;
+		background: #fff;
+		border-radius: 50%;
+	}
+}
+
+.field-item {
+	display: flex;
+	align-items: center;
+	padding: 28rpx 0;
+	border-bottom: 2rpx solid #f5f5f5;
+	height: 44rpx;
+
+	&:last-child {
+		border-bottom: none;
+	}
+}
+
+.field-label {
+	width: 180rpx;
+	font-size: 28rpx;
+	color: #333;
+	flex-shrink: 0;
+}
+
+.require::before {
+	content: '*';
+	color: #f56c6c;
+	margin-right: 4rpx;
+}
+
+.field-value {
+	flex: 1;
+	font-size: 28rpx;
+	color: #333;
+	text-align: right;
+	margin-right: 16rpx;
+}
+
+.field-value.placeholder {
+	color: #ccc;
+}
+
+.field-value-wrap {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	align-items: flex-end;
+	margin-right: 16rpx;
+
+	.selected-name {
+		font-size: 28rpx;
+		font-weight: bold;
+		color: #333;
+	}
+
+	.selected-phone {
+		font-size: 22rpx;
+		color: #999;
+	}
+}
+
+.placeholder {
+	color: #ccc;
+	font-size: 28rpx;
+}
+
+.mode-select {
+	display: flex;
+	gap: 16rpx;
+	margin: 20rpx 0 32rpx;
+}
+
+.mode-btn {
+	flex: 1;
+	height: 60rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	border: 2rpx solid #f0f0f0;
+	border-radius: 30rpx;
+	font-size: 24rpx;
+	color: #666;
+
+	&.active {
+		background: #fef8e5;
+		border-color: #f7ca3e;
+		color: #f7ca3e;
+		font-weight: bold;
+	}
+}
+
+.route-box {
+	display: flex;
+	gap: 20rpx;
+	margin-bottom: 30rpx;
+}
+
+.route-icon {
+	width: 44rpx;
+	height: 44rpx;
+	border-radius: 8rpx;
+	color: #fff;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	font-size: 22rpx;
+	font-weight: bold;
+	flex-shrink: 0;
+	margin-top: 10rpx;
+}
+
+.route-icon.pick {
+	background: #5bb7ff;
+}
+
+.route-icon.send {
+	background: #64cf5c;
+}
+
+.route-icon.service {
+	background: #ff9500;
+}
+
+.route-fields {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	gap: 6rpx;
+}
+
+.addr-label {
+	font-size: 22rpx;
+	color: #999;
+	margin-top: 10rpx;
+}
+
+.route-picker-trigger {
+	height: 64rpx;
+	border-bottom: 2rpx solid #f5f5f5;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+
+	.display-text {
+		font-size: 26rpx;
+		color: #333;
+
+		&.placeholder {
+			color: #ccc;
+		}
+	}
+}
+
+.route-input {
+	height: 72rpx;
+	font-size: 26rpx;
+	border-bottom: 2rpx solid #f5f5f5;
+
+	&.half {
+		flex: 1;
+	}
+}
+
+.contact-row {
+	display: flex;
+	gap: 16rpx;
+}
+
+.route-time-trigger {
+	height: 72rpx;
+	background: #f9f9f9;
+	border-radius: 12rpx;
+	display: flex;
+	align-items: center;
+	padding: 0 20rpx;
+	font-size: 26rpx;
+	color: #333;
+	margin-top: 10rpx;
+
+	.placeholder {
+		color: #ccc;
+	}
+}
+
+.address-title,
+.form-item-label,
+.remarks-title {
+	display: block;
+	font-size: 26rpx;
+	color: #666;
+	margin: 20rpx 0 10rpx;
+}
+
+.booking-header {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	margin: 20rpx 0 10rpx;
+}
+
+.booking-header .label {
+	display: block;
+	font-size: 26rpx;
+	color: #666;
+}
+
+.booking-section {
+	margin-top: 24rpx;
+}
+
+.count-tag {
+	font-size: 20rpx;
+	color: #f44336;
+	padding: 4rpx 0;
+}
+
+.time-item-row {
+	display: flex;
+	align-items: center;
+	gap: 12rpx;
+	margin-bottom: 16rpx;
+}
+
+.flex-time-range {
+	flex: 1;
+	height: 64rpx;
+	background: #fcfcfc;
+	border: 2rpx solid #eee;
+	border-radius: 10rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+
+	.time-text {
+		font-size: 24rpx;
+		color: #333;
+
+		&.placeholder {
+			color: #ccc;
+		}
+	}
+}
+
+.action-buttons {
+	display: flex;
+	gap: 12rpx;
+	margin-left: 8rpx;
+}
+
+.circle-btn {
+	width: 44rpx;
+	height: 44rpx;
+	border-radius: 50%;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	font-size: 32rpx;
+	font-weight: bold;
+
+	&.add {
+		background: #e3f2fd;
+		color: #2196f3;
+	}
+
+	&.remove {
+		background: #fde2e2;
+		color: #f56c6c;
+	}
+}
+
+.remarks-textarea {
+	width: 100%;
+	height: 140rpx;
+	font-size: 26rpx;
+	background: #f9f9f9;
+	border-radius: 16rpx;
+	padding: 16rpx;
+	box-sizing: border-box;
+}
+
+.quote-input {
+	flex: 1;
+	font-size: 36rpx;
+	color: #f44336;
+	font-weight: bold;
+	text-align: right;
+}
+
+.unit-text {
+	font-size: 28rpx;
+	color: #333;
+	margin-left: 8rpx;
+}
+
+.quote-tips {
+	display: block;
+	font-size: 22rpx;
+	color: #999;
+	margin-top: 20rpx;
+}
+
+.footer-bar {
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	background: #fff;
+	padding: 24rpx 32rpx;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
+	z-index: 100;
+}
+
+.quotation-box {
+	display: flex;
+	align-items: baseline;
+
+	.p-label {
+		font-size: 24rpx;
+		color: #333;
+	}
+
+	.p-symbol {
+		font-size: 28rpx;
+		color: #f44336;
+		font-weight: bold;
+		margin-left: 8rpx;
+	}
+
+	.p-amount {
+		font-size: 40rpx;
+		font-weight: 900;
+		color: #f44336;
+	}
+}
+
+.submit-btn {
+	width: 280rpx;
+	height: 84rpx;
+	background: linear-gradient(90deg, #ffd53f, #ff9500);
+	color: #fff;
+	border-radius: 42rpx;
+	font-size: 28rpx;
+	font-weight: bold;
+	line-height: 84rpx;
+
+	&::after {
+		border: none;
+	}
+}
 
 /* CSS Common Modal UI @Author: Antigravity */
-.center-modal-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); z-index: 10000; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(4rpx); }
-.center-modal-content { width: 620rpx; background: #fff; border-radius: 32rpx; display: flex; flex-direction: column; overflow: hidden; animation: popIn 0.3s ease-out; }
-@keyframes popIn { from { transform: scale(0.9); opacity: 0; } to { transform: scale(1); opacity: 1; } }
+.center-modal-mask {
+	position: fixed;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	background: rgba(0, 0, 0, 0.6);
+	z-index: 10000;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	backdrop-filter: blur(4rpx);
+}
+
+.center-modal-content {
+	width: 620rpx;
+	background: #fff;
+	border-radius: 32rpx;
+	display: flex;
+	flex-direction: column;
+	overflow: hidden;
+	animation: popIn 0.3s ease-out;
+}
+
+@keyframes popIn {
+	from {
+		transform: scale(0.9);
+		opacity: 0;
+	}
+
+	to {
+		transform: scale(1);
+		opacity: 1;
+	}
+}
+
+.modal-header {
+	padding: 32rpx;
+	border-bottom: 2rpx solid #f2f2f2;
+	position: relative;
+	text-align: center;
+
+	.modal-title {
+		font-size: 30rpx;
+		font-weight: bold;
+		color: #333;
+	}
+}
+
+.close-btn {
+	position: absolute;
+	right: 24rpx;
+	top: 24rpx;
+	width: 44rpx;
+	height: 44rpx;
+
+	&::before,
+	&::after {
+		content: '';
+		position: absolute;
+		top: 20rpx;
+		left: 8rpx;
+		width: 28rpx;
+		height: 4rpx;
+		background: #999;
+		transform: rotate(45deg);
+		border-radius: 4rpx;
+	}
+
+	&::after {
+		transform: rotate(-45deg);
+	}
+}
+
+.search-box {
+	display: flex;
+	align-items: center;
+	background: #f5f5f5;
+	border-radius: 36rpx;
+	padding: 0 24rpx;
+	height: 72rpx;
+	margin: 0 4rpx;
+
+	.search-icon {
+		width: 20rpx;
+		height: 20rpx;
+		border: 3rpx solid #999;
+		border-radius: 50%;
+		margin-right: 12rpx;
+		position: relative;
+
+		&::after {
+			content: '';
+			width: 10rpx;
+			height: 3rpx;
+			background: #999;
+			position: absolute;
+			bottom: -4rpx;
+			right: -4rpx;
+			transform: rotate(45deg);
+		}
+	}
+
+	.search-input {
+		flex: 1;
+		font-size: 26rpx;
+	}
+
+	.search-btn {
+		font-size: 26rpx;
+		color: #ff9500;
+		font-weight: bold;
+		margin-left: 20rpx;
+	}
+}
+
+.modal-list-scroll {
+	flex: 1;
+	max-height: 55vh;
+	padding: 0 32rpx;
+}
+
+.list-item {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	padding: 30rpx 0;
+	border-bottom: 2rpx solid #f9f9f9;
+
+	.user-info {
+		display: flex;
+		flex-direction: column;
+
+		.name {
+			font-size: 28rpx;
+			font-weight: bold;
+			color: #333;
+		}
+
+		.phone {
+			font-size: 22rpx;
+			color: #999;
+			margin-top: 4rpx;
+		}
+	}
+}
+
+.checkmark {
+	width: 12rpx;
+	height: 22rpx;
+	border-right: 4rpx solid #ff9500;
+	border-bottom: 4rpx solid #ff9500;
+	transform: rotate(45deg);
+}
+
+.cascade-indicator {
+	display: flex;
+	padding: 20rpx 32rpx;
+	background: #fafafa;
+	border-bottom: 2rpx solid #f2f2f2;
+	flex-wrap: wrap;
+	gap: 12rpx;
 
-.modal-header { padding: 32rpx; border-bottom: 2rpx solid #f2f2f2; position: relative; text-align: center; .modal-title { font-size: 30rpx; font-weight: bold; color: #333; } }
-.close-btn { position: absolute; right: 24rpx; top: 24rpx; width: 44rpx; height: 44rpx; &::before, &::after { content: ''; position: absolute; top: 20rpx; left: 8rpx; width: 28rpx; height: 4rpx; background: #999; transform: rotate(45deg); border-radius: 4rpx; } &::after { transform: rotate(-45deg); } }
+	.path-node {
+		font-size: 24rpx;
+		color: #666;
 
-.search-box { display: flex; align-items: center; background: #f5f5f5; border-radius: 36rpx; padding: 0 24rpx; height: 72rpx; margin: 0 4rpx; .search-icon { width: 20rpx; height: 20rpx; border: 3rpx solid #999; border-radius: 50%; margin-right: 12rpx; position: relative; &::after { content: ''; width: 10rpx; height: 3rpx; background: #999; position: absolute; bottom: -4rpx; right: -4rpx; transform: rotate(45deg); } } .search-input { flex: 1; font-size: 26rpx; } .search-btn { font-size: 26rpx; color: #ff9500; font-weight: bold; margin-left: 20rpx; } }
-.modal-list-scroll { flex: 1; max-height: 55vh; padding: 0 32rpx; }
-.list-item { display: flex; align-items: center; justify-content: space-between; padding: 30rpx 0; border-bottom: 2rpx solid #f9f9f9; .user-info { display: flex; flex-direction: column; .name { font-size: 28rpx; font-weight: bold; color: #333; } .phone { font-size: 22rpx; color: #999; margin-top: 4rpx; } } }
-.checkmark { width: 12rpx; height: 22rpx; border-right: 4rpx solid #ff9500; border-bottom: 4rpx solid #ff9500; transform: rotate(45deg); }
+		&.active {
+			color: #ff9500;
+			font-weight: bold;
+		}
+	}
+}
+
+.datetime-picker-body {
+	padding: 20rpx 0;
+}
 
-.cascade-indicator { display: flex; padding: 20rpx 32rpx; background: #fafafa; border-bottom: 2rpx solid #f2f2f2; flex-wrap: wrap; gap: 12rpx; .path-node { font-size: 24rpx; color: #666; &.active { color: #ff9500; font-weight: bold; } } }
+.time-slot-row {
+	display: flex;
+	padding: 0 20rpx;
+	margin-bottom: 20rpx;
+	gap: 16rpx;
+}
 
-.datetime-picker-body { height: 400rpx; padding: 20rpx 0; }
-.picker-view { width: 100%; height: 100%; }
-.picker-item { line-height: 80rpx; text-align: center; font-size: 28rpx; }
-.modal-footer { display: flex; border-top: 2rpx solid #f2f2f2; .modal-cancel, .modal-confirm { flex: 1; height: 96rpx; line-height: 96rpx; text-align: center; font-size: 28rpx; } .modal-confirm { color: #ff9500; font-weight: bold; border-left: 2rpx solid #f2f2f2; } }
+.time-slot {
+	flex: 1;
+	border: 2rpx solid #eee;
+	border-radius: 12rpx;
+	padding: 16rpx;
+	text-align: center;
 
-.right-arrow { width: 12rpx; height: 12rpx; border-right: 3rpx solid #ccc; border-top: 3rpx solid #ccc; transform: rotate(45deg); flex-shrink: 0; }
-.empty-tip { padding: 80rpx 0; text-align: center; color: #ccc; font-size: 24rpx; }
-.user-info { display: flex; flex-direction: column; flex: 1; .name { font-size: 28rpx; font-weight: bold; color: #333; } .phone { font-size: 22rpx; color: #999; margin-top: 4rpx; } }
+	&.active {
+		border-color: #ff9500;
+		background: #fff8f0;
+	}
+}
+
+.slot-label {
+	display: block;
+	font-size: 22rpx;
+	color: #999;
+	margin-bottom: 6rpx;
+}
+
+.slot-value {
+	display: block;
+	font-size: 26rpx;
+	color: #333;
+	font-weight: 500;
+
+	&.placeholder {
+		color: #ccc;
+	}
+}
+
+.picker-view {
+	width: 100%;
+	height: 360rpx;
+}
+
+.picker-item {
+	line-height: 80rpx;
+	text-align: center;
+	font-size: 28rpx;
+}
+
+.modal-footer {
+	display: flex;
+	border-top: 2rpx solid #f2f2f2;
+
+	.modal-cancel,
+	.modal-confirm {
+		flex: 1;
+		height: 96rpx;
+		line-height: 96rpx;
+		text-align: center;
+		font-size: 28rpx;
+	}
+
+	.modal-confirm {
+		color: #ff9500;
+		font-weight: bold;
+		border-left: 2rpx solid #f2f2f2;
+	}
+}
+
+.right-arrow {
+	width: 12rpx;
+	height: 12rpx;
+	border-right: 3rpx solid #ccc;
+	border-top: 3rpx solid #ccc;
+	transform: rotate(45deg);
+	flex-shrink: 0;
+}
+
+.empty-tip {
+	padding: 80rpx 0;
+	text-align: center;
+	color: #ccc;
+	font-size: 24rpx;
+}
+
+.user-info {
+	display: flex;
+	flex-direction: column;
+	flex: 1;
+
+	.name {
+		font-size: 28rpx;
+		font-weight: bold;
+		color: #333;
+	}
+
+	.phone {
+		font-size: 22rpx;
+		color: #999;
+		margin-top: 4rpx;
+	}
+}
 </style>

+ 3 - 0
pages/order/detail/index.vue

@@ -377,6 +377,7 @@ const order = reactive({
 	remark: '',
 	assigneeName: '',
 	cancelTime: '',
+	orderCommission: 0,
 	pickAddress: '',
 	pickTime: '',
 	sendAddress: '',
@@ -431,6 +432,7 @@ const loadOrderDetail = async (id) => {
 			order.fulfiller = res.fulfiller
 			order.packageName = res.groupPurchasePackageName || res.packageName || '-'
 			order.createTime = res.createTime || '-'
+			order.orderCommission = res.orderCommission || 0
 
 			// 并行加载关联信息,提升效率并减少串行触发的重演
 			const tasks = []
@@ -674,6 +676,7 @@ const baseInfoList = computed(() => {
 	const list = [
 		{ label: '系统单号', value: order.code || order.id },
 		{ label: '服务类型', value: currentServiceName.value },
+		{ label: '订单金额', value: `¥${(order.orderCommission / 100).toFixed(2)}`, highlight: true },
 		{ label: '归属门店', value: order.shopName },
 		{ label: '宠主信息', value: `${order.userName} / ${order.userPhone}` },
 		{ label: '预约时间', value: order.bookTime },

+ 27 - 3
pages/order/list/index.vue

@@ -111,6 +111,7 @@
 
 			<!-- 空状态 -->
 			<view class="empty-state" v-if="!loading && orders.length === 0">
+				<image class="empty-image" src="/static/images/order-noorders.png" mode="aspectFit"></image>
 				<text class="empty-text">暂无相关订单</text>
 			</view>
 
@@ -126,8 +127,11 @@
 			<view class="modal-content">
 				<view class="modal-title">提示</view>
 				<view class="modal-body">
-					<view style="margin-bottom: 20rpx; font-size: 28rpx; color: #666;">确定要取消订单 [{{ currentCancelOrder?.id }}] 吗?</view>
-					<textarea class="cancel-input" v-model="cancelReason" placeholder="必填,请输入取消原因" placeholder-class="ph-color" :show-confirm-bar="false"></textarea>
+					<view style="margin-bottom: 20rpx; font-size: 28rpx; color: #666;">确定要取消订单 [{{
+						currentCancelOrder?.id }}] 吗?
+					</view>
+					<textarea class="cancel-input" v-model="cancelReason" placeholder="必填,请输入取消原因"
+						placeholder-class="ph-color" :show-confirm-bar="false"></textarea>
 				</view>
 				<view class="modal-footer">
 					<view class="modal-btn btn-cancel" @click="closeCancelModal">取消</view>
@@ -726,7 +730,10 @@ const onComplaint = (order) => {
 	display: inline-flex;
 	align-items: center;
 	justify-content: center;
-	&::after { border: none; }
+
+	&::after {
+		border: none;
+	}
 }
 
 .btn-cancel {
@@ -747,6 +754,13 @@ const onComplaint = (order) => {
 	padding: 100rpx 0;
 }
 
+.empty-image {
+	width: 300rpx;
+	height: 300rpx;
+	display: block;
+	margin: 0 auto 30rpx;
+}
+
 .empty-text {
 	font-size: 28rpx;
 	color: #999;
@@ -774,6 +788,7 @@ const onComplaint = (order) => {
 	align-items: center;
 	justify-content: center;
 }
+
 .modal-mask {
 	position: absolute;
 	top: 0;
@@ -782,6 +797,7 @@ const onComplaint = (order) => {
 	height: 100%;
 	background-color: rgba(0, 0, 0, 0.5);
 }
+
 .modal-content {
 	position: relative;
 	width: 80%;
@@ -790,6 +806,7 @@ const onComplaint = (order) => {
 	overflow: hidden;
 	z-index: 1000;
 }
+
 .modal-title {
 	padding: 30rpx 0 20rpx;
 	text-align: center;
@@ -797,9 +814,11 @@ const onComplaint = (order) => {
 	font-weight: bold;
 	color: #333;
 }
+
 .modal-body {
 	padding: 10rpx 40rpx 30rpx;
 }
+
 .cancel-input {
 	width: 100%;
 	height: 160rpx;
@@ -810,13 +829,16 @@ const onComplaint = (order) => {
 	box-sizing: border-box;
 	color: #333;
 }
+
 .ph-color {
 	color: #999;
 }
+
 .modal-footer {
 	display: flex;
 	border-top: 1rpx solid #CCCCCC;
 }
+
 .modal-btn {
 	flex: 1;
 	height: 90rpx;
@@ -825,10 +847,12 @@ const onComplaint = (order) => {
 	font-size: 30rpx;
 	font-weight: 500;
 }
+
 .btn-cancel {
 	color: #666;
 	border-right: 1rpx solid #eee;
 }
+
 .btn-confirm {
 	color: #2196f3;
 }

BIN
static/images/complaint-agree.png


BIN
static/images/complaint-disagree.png


BIN
static/images/order-noorders.png


+ 2 - 2
utils/config.js

@@ -2,8 +2,8 @@
 
 // 接口基础地址
 // export const BASE_URL = 'http://192.168.1.118:8080'
-export const BASE_URL = 'http://www.hoomeng.pet/api'
-// export const BASE_URL = 'http://111.228.46.254/api'
+// export const BASE_URL = 'http://www.hoomeng.pet/api'
+export const BASE_URL = 'http://111.228.46.254/api'
 // export const BASE_URL = 'http://192.168.1.205:8080'
 
 // 请求超时时间(毫秒)