2 Commity a5cc7ecc77 ... 6f8b8770cd

Autor SHA1 Správa Dátum
  颜琼丽 6f8b8770cd Merge remote-tracking branch 'origin/master' 7 hodín pred
  颜琼丽 559b267904 feat:创建订单功能完成 7 hodín pred

+ 7 - 0
jd_logistics-app/api/order.js

@@ -17,6 +17,13 @@ export function dictList(data) {
   return request.get('system/dict/data/type/'+ data);
 }
 
+/**
+ * 物流轨迹查询
+ * @param {Object} data
+ */
+export function queryDeliverTrace(data) {
+  return request.post('system/kd100/wxmini/queryExpres',data);
+}
 
 /**
  * 支付结果

+ 61 - 64
jd_logistics-app/pages/index/index.vue

@@ -2,16 +2,9 @@
 	<view class="container">
 		<!-- 上半部分:轮播图区域 -->
 		<view class="top-section">
-			<swiper class="swiper" 
-				:indicator-dots="true" 
-				:autoplay="true" 
-				:interval="3000" 
-				:duration="1000" 
-				:circular="true"
-				indicator-active-color="#1B64F0"
-				indicator-color="rgba(255, 255, 255, 0.6)"
-				indicator-class="swiper-indicator"
-				active-class="swiper-indicator-active">
+			<swiper class="swiper" :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000"
+				:circular="true" indicator-active-color="#1B64F0" indicator-color="rgba(255, 255, 255, 0.6)"
+				indicator-class="swiper-indicator" active-class="swiper-indicator-active">
 				<swiper-item v-for="(item, index) in swiperList" :key="index">
 					<view class="swiper-item">
 						<image class="swiper-img" :src="item.imageUrl" mode="aspectFill" />
@@ -19,7 +12,7 @@
 				</swiper-item>
 			</swiper>
 		</view>
-		
+
 		<view class="btn-container">
 			<view class="btn-item" @click="handleExpress('2')">
 				<image class="button-icon" src="/static/img/index-time.png" />
@@ -27,7 +20,7 @@
 					<view class="button-title">瑞鲸速达(顺丰)</view>
 					<view class="button-desc">一小时下单取件</view>
 				</view>
-				
+
 			</view>
 			<view class="btn-item" @click="handleExpress('1')">
 				<image class="button-icon" src="/static/img/index-un-time.png" />
@@ -35,7 +28,7 @@
 					<view class="button-title">瑞鲸速达(京东)</view>
 					<view class="button-desc">下单当日取件</view>
 				</view>
-				
+
 			</view>
 			<view class="btn-item" @click="showExpressDialog">
 				<image class="button-icon" src="/static/img/index-personal.png" />
@@ -43,9 +36,9 @@
 					<view class="button-title">个人寄件</view>
 					<view class="button-desc">支持顺丰和京东</view>
 				</view>
-				
+
 			</view>
-			
+
 		</view>
 
 		<!-- 使用 u-popup 弹框组件 -->
@@ -56,28 +49,37 @@
 
 <script setup>
 	import {
-		ref,onMounted
+		ref,
+		onMounted
 	} from 'vue'
-	import {onShow} from '@dcloudio/uni-app'
+	import {
+		onShow
+	} from '@dcloudio/uni-app'
 	// 导入 u-popup 弹框组件
 	import PersonalExpressDialog from './components/PersonalExpressDialog.vue'
-	import { bannerList } from '../../api/user'
+	import {
+		bannerList
+	} from '../../api/user'
+	import {
+		checkLoginShowModal,
+		quickLogin
+	} from "@/utils/util.js";
 
 	const showDialog = ref(false)
 
 	// 轮播图数据
 	const swiperList = ref([])
-	
-	onShow(()=>{
+
+	onShow(() => {
 		getBannerList()
 	})
-	
-	const getBannerList = ()=>{
-		bannerList().then(res=>{
-			if(res.code == 200){
+
+	const getBannerList = () => {
+		bannerList().then(res => {
+			if (res.code == 200) {
 				swiperList.value = res.data
 			}
-		},{})
+		}, {})
 	}
 
 	const showExpressDialog = () => {
@@ -108,17 +110,11 @@
 		}, 500)
 	}
 
-	const handleExpress = (company) => {
-	  uni.navigateTo({ 
-		    url: `/pages/order/create_order?product=${company}` 
-		  })
-		
-		// 实际跳转代码
-		// setTimeout(() => {
-		//   uni.navigateTo({ 
-		//     url: `/pages/order/create?company=${company}` 
-		//   })
-		// }, 1500)
+	const handleExpress = async (company) => {
+		if (!await checkLoginShowModal()) return;
+		uni.navigateTo({
+			url: `/pages/order/create_order?product=${company}`
+		})
 	}
 </script>
 
@@ -158,15 +154,17 @@
 	/* 注意:在不同平台可能需要使用不同的选择器 */
 	/* 微信小程序 */
 	::v-deep .wx-swiper-dots {
-		bottom: 80rpx !important; /* 调整指示器容器的位置 */
+		bottom: 80rpx !important;
+		/* 调整指示器容器的位置 */
 		display: flex !important;
 		justify-content: center !important;
 		align-items: center !important;
 	}
-	
+
 	/* H5平台 */
 	::v-deep .uni-swiper-dots {
-		bottom: 80rpx !important; /* 调整指示器容器的位置 */
+		bottom: 80rpx !important;
+		/* 调整指示器容器的位置 */
 		display: flex !important;
 		justify-content: center !important;
 		align-items: center !important;
@@ -180,7 +178,7 @@
 		background: rgba(255, 255, 255, 0.6) !important;
 		margin: 0 4rpx !important;
 	}
-	
+
 	.swiper-indicator-active {
 		width: 32rpx !important;
 		height: 8rpx !important;
@@ -193,13 +191,14 @@
 		position: absolute;
 		left: 0;
 		right: 0;
-		bottom: 80rpx; /* 调整指示器的垂直位置 */
+		bottom: 80rpx;
+		/* 调整指示器的垂直位置 */
 		z-index: 10;
 		display: flex;
 		justify-content: center;
 		align-items: center;
 	}
-	
+
 	.custom-indicator-dot {
 		width: 8rpx;
 		height: 8rpx;
@@ -208,27 +207,27 @@
 		margin: 0 4rpx;
 		transition: all 0.3s ease;
 	}
-	
+
 	.custom-indicator-dot.active {
 		width: 32rpx;
 		height: 8rpx;
 		background: #1B64F0;
 		border-radius: 4rpx;
 	}
-	
-	.btn-container{
-			display: flex;
-			flex-wrap: wrap;
-			justify-content: space-between;
-			position: relative;
-			top: -40rpx;
-			padding: 0rpx 20rpx;
-		
-		.btn-item{
+
+	.btn-container {
+		display: flex;
+		flex-wrap: wrap;
+		justify-content: space-between;
+		position: relative;
+		top: -40rpx;
+		padding: 0rpx 20rpx;
+
+		.btn-item {
 			width: 339rpx;
 			height: 172rpx;
 			background: #FFFFFF;
-			box-shadow: 0rpx 4rpx 16rpx 0rpx rgba(27,100,240,0.08), 0rpx 0rpx 8rpx 0rpx rgba(27,100,240,0.08);
+			box-shadow: 0rpx 4rpx 16rpx 0rpx rgba(27, 100, 240, 0.08), 0rpx 0rpx 8rpx 0rpx rgba(27, 100, 240, 0.08);
 			border-radius: 32rpx;
 			border: 2rpx solid #FFFFFF;
 			display: flex;
@@ -236,18 +235,18 @@
 			align-items: center;
 			padding: 20rpx 0rpx 20rpx 20rpx;
 			margin-bottom: 32rpx;
-			
-			.button-icon{
+
+			.button-icon {
 				width: 88rpx;
 				height: 88rpx;
 			}
-			
-			.button-right{
+
+			.button-right {
 				display: flex;
 				flex-direction: column;
 				flex: 1;
 				margin-left: 16rpx;
-				
+
 				.button-title {
 					height: 48rpx;
 					line-height: 48rpx;
@@ -256,7 +255,7 @@
 					color: #333;
 					flex-shrink: 0;
 				}
-				
+
 				.button-desc {
 					height: 44rpx;
 					line-height: 44rpx;
@@ -266,9 +265,7 @@
 				}
 			}
 		}
-		
-		
-	}
 
-	
+
+	}
 </style>

+ 274 - 270
jd_logistics-app/pages/logistics/index.vue

@@ -6,364 +6,368 @@
 			<view class="company-info">
 				<text class="company-name">京东物流</text>
 				<view class="tracking-number">
-					<text class="number">JDVA40141037033</text>
-					<text class="copy-btn" @click="copyTrackingNumber">复制</text>
+					<text class="number">{{ orderNo }}</text>
+					<text class="copy-btn" @click="copyTrackingNumber(orderNo)">复制</text>
 				</view>
 			</view>
 		</view>
 
-		<!-- 物流轨迹时间轴 -->
-		<view class="timeline-container">
-			<TimelineItem v-for="(item, index) in trackList" :key="index" :title="item.title" :desc="item.desc"
-				:time="item.time" :is-active="item.isActive" :show-line="index < trackList.length - 1"
-				:show-dot-bg="index === 0" />
+		<!-- 当前最新物流状态 -->
+		<view v-if="currentTrace" class="current-status">
+			<view class="status-icon">
+				<text class="icon">{{ getStatusIcon(currentTrace.title) }}</text>
+			</view>
+			<view class="status-info">
+				<text class="status-text">{{ currentTrace.title }}</text>
+				<text class="status-desc">{{ currentTrace.desc }}</text>
+				<view class="courier-info" v-if="currentTrace.courier">
+					<text class="courier-text">快递员:{{ currentTrace.courier.name }}</text>
+					<text class="contact-text" @click="contactCourier(currentTrace.courier.phone)">联系</text>
+				</view>
+				<text class="status-time">{{ currentTrace.time }}</text>
+			</view>
 		</view>
 
-	
+		<!-- 物流轨迹时间轴(滚动区域) -->
+		<scroll-view class="timeline-scroll" :style="{ height: scrollHeight + 'px' }" scroll-y>
+			<view class="timeline-container">
+				<TimelineItem 
+					v-for="(item, index) in deliverTraceList" 
+					:key="index"
+					:title="item.title"
+					:desc="item.desc"
+					:time="item.time"
+					:is-active="item.isActive"
+					:show-line="index < deliverTraceList.length - 1"
+					:show-dot-bg="index === 0"
+				/>
+				<view v-if="deliverTraceList.length === 0 && !loading" class="empty-trace">
+					暂无物流轨迹
+				</view>
+			</view>
+		</scroll-view>
+
+		<!-- 底部操作栏 -->
+		<view class="bottom-actions">
+			<button class="action-btn" @click="viewDetails">查看详情</button>
+			<button class="action-btn" @click="copyAllInfo">复制全部信息</button>
+		</view>
 	</view>
 </template>
 
 <script setup>
-	import {
-		ref,
-		onMounted
-	} from 'vue'
+	import { ref, onMounted, computed } from 'vue'
+	import { onLoad } from '@dcloudio/uni-app'
 	import TimelineItem from '@/components/TimelineItem.vue'
+	import { queryDeliverTrace } from '@/api/order'
+
+	// 响应式数据
+	const orderNo = ref('')
+	const scrollHeight = ref(600)
+	const deliverTraceList = ref([])
+	const loading = ref(false)
+
+	// 当前最新一条物流(用于头部状态展示)
+	const currentTrace = computed(() => {
+		return deliverTraceList.value.length > 0 ? deliverTraceList.value[0] : null
+	})
 
-	const scrollHeight = ref(400)
-
-	// 模拟物流轨迹数据
-	const trackList = ref([{
-			title: '已代收',
-			desc: '您的快件已由[门卫]代收,快递员:马新宽、联系...',
-			time: '2026-01-11 17:32:32',
-			isActive: true
-		},
-		{
-			title: '派送中',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '运输中',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '运输中',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '运输中',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
-		},
-		{
-			title: '已揽件',
-			desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...',
-			time: '2026-01-11 17:32:32',
-			isActive: false
+	// 从路由参数获取运单号
+	onLoad((options) => {
+		if (options.orderNo) {
+			orderNo.value = options.orderNo
+			getDeliverTrace()
+		} else {
+			// 无参数时使用模拟数据(仅用于演示)
+			loadMockData()
 		}
-	])
+	})
 
+	// 计算滚动区域高度(减去头部、当前状态、底部等固定高度)
 	onMounted(() => {
-		// 计算滚动区域高度
 		uni.getSystemInfo({
 			success: (res) => {
-				scrollHeight.value = res.windowHeight - 420 // 减去头部、当前状态、底部等固定高度
+				// 64px 约等于 128rpx,根据实际布局调整
+				const headerHeight = 140   // 头部高度(rpx)
+				const statusHeight = 200   // 当前状态高度(rpx)
+				const bottomHeight = 120   // 底部按钮高度(rpx)
+				const pxRatio = res.windowWidth / 750
+				scrollHeight.value = res.windowHeight - (headerHeight + statusHeight + bottomHeight) * pxRatio - 40
 			}
 		})
 	})
 
-	const copyTrackingNumber = () => {
-		uni.setClipboardData({
-			data: 'JDVA40141037033',
-			success: () => {
-				uni.showToast({
-					title: '已复制运单号',
-					icon: 'success'
-				})
+	// 获取真实物流轨迹
+	const getDeliverTrace = () => {
+		if (!orderNo.value) return
+		loading.value = true
+		const params = {
+			number: orderNo.value,
+			company: ''  // 可传入快递公司编码
+		}
+		queryDeliverTrace(params).then(response => {
+			if (response.code === 200 && response.data) {
+				// 按时间倒序排列(最新的在前)
+				const list = response.data.sort((a, b) => 
+					new Date(b.time) - new Date(a.time)
+				).map((item, index) => ({
+					...item,
+					isActive: index === 0
+				}))
+				deliverTraceList.value = list
+			} else {
+				uni.showToast({ title: '未查询到物流信息', icon: 'none' })
+				deliverTraceList.value = []
 			}
+		}).catch(error => {
+			console.error('查询物流轨迹失败:', error)
+			uni.showToast({ title: '查询物流信息失败', icon: 'error' })
+			deliverTraceList.value = []
+		}).finally(() => {
+			loading.value = false
+		})
+	}
+
+	// 模拟数据(用于无参数时演示)
+	const loadMockData = () => {
+		orderNo.value = 'JDVA40141037033'
+		deliverTraceList.value = [
+			{ title: '已代收', desc: '您的快件已由[门卫]代收,快递员:马新宽、联系...', time: '2026-01-11 17:32:32', isActive: true, courier: { name: '马新宽', phone: '13800138000' } },
+			{ title: '派送中', desc: '快件已投递至【北京海淀区北京大学菜鸟驿站】...', time: '2026-01-11 15:20:10', isActive: false },
+			{ title: '运输中', desc: '快件已到达【北京转运中心】', time: '2026-01-10 08:45:22', isActive: false },
+			{ title: '运输中', desc: '快件已从【上海转运中心】发出', time: '2026-01-09 20:12:05', isActive: false },
+			{ title: '已揽件', desc: '快件已被【上海浦东营业部】揽收', time: '2026-01-09 14:30:00', isActive: false }
+		]
+	}
+
+	// 复制运单号
+	const copyTrackingNumber = (text) => {
+		uni.setClipboardData({
+			data: text,
+			success: () => uni.showToast({ title: '已复制', icon: 'success' })
 		})
 	}
 
-	const contactCourier = () => {
+	// 联系快递员
+	const contactCourier = (phone) => {
 		uni.showModal({
 			title: '联系快递员',
-			content: '拨打快递员电话?',
+			content: `拨打 ${phone} ?`,
 			success: (res) => {
-				if (res.confirm) {
-					uni.makePhoneCall({
-						phoneNumber: '请填写快递员电话'
-					})
-				}
+				if (res.confirm) uni.makePhoneCall({ phoneNumber: phone })
 			}
 		})
 	}
 
+	// 查看详情(示例)
 	const viewDetails = () => {
-		uni.showToast({
-			title: '查看详情',
-			icon: 'none'
-		})
+		uni.showToast({ title: '功能开发中', icon: 'none' })
 	}
 
+	// 复制全部物流信息
 	const copyAllInfo = () => {
-		const allInfo =
-			`京东物流\n运单号:JDVA40141037033\n状态:已代收\n快递员:马新宽\n\n物流轨迹:\n2026-01-11 17:32:32 已代收\n2026-01-11 17:32:32 派送中\n2026-01-11 17:32:32 运输中\n2026-01-11 17:32:32 已揽件`
-
+		let info = `京东物流\n运单号:${orderNo.value}\n`
+		if (currentTrace.value) {
+			info += `状态:${currentTrace.value.title}\n`
+			if (currentTrace.value.courier) info += `快递员:${currentTrace.value.courier.name}\n`
+			info += `\n物流轨迹:\n`
+		}
+		deliverTraceList.value.forEach(item => {
+			info += `${item.time} ${item.title} ${item.desc}\n`
+		})
 		uni.setClipboardData({
-			data: allInfo,
-			success: () => {
-				uni.showToast({
-					title: '已复制全部信息',
-					icon: 'success'
-				})
-			}
+			data: info,
+			success: () => uni.showToast({ title: '已复制全部信息', icon: 'success' })
 		})
 	}
+
+	// 根据状态显示图标(可根据实际需求扩展)
+	const getStatusIcon = (title) => {
+		const map = {
+			'已代收': '📦',
+			'派送中': '🚚',
+			'运输中': '🚛',
+			'已揽件': '📦',
+			'已签收': '✅'
+		}
+		return map[title] || '●'
+	}
 </script>
 
 <style lang="scss" scoped>
 	.logistics-container {
+		display: flex;
+		flex-direction: column;
 		padding: 20rpx;
 		background-color: #f5f5f5;
 		min-height: 100vh;
+		box-sizing: border-box;
 	}
 
-	// 头部样式
+	/* 头部快递信息 */
 	.logistics-header {
 		height: 140rpx;
-		background: #FFFFFF;
+		background: #fff;
 		border-radius: 32rpx;
 		padding: 20rpx;
 		display: flex;
+		align-items: center;
+		margin-bottom: 20rpx;
 
 		.logo {
 			width: 100rpx;
 			height: 100rpx;
+			flex-shrink: 0;
 		}
-	}
 
-	.company-info {
-		margin-left: 20rpx;
+		.company-info {
+			margin-left: 20rpx;
+			flex: 1;
 
-		.company-name {
-			font-size: 32rpx;
-			height: 48rpx;
-			line-height: 48rpx;
-			font-weight: bold;
-			display: block;
-			margin-bottom: 8rpx;
-		}
+			.company-name {
+				font-size: 32rpx;
+				font-weight: bold;
+				display: block;
+				margin-bottom: 8rpx;
+			}
 
-		.tracking-number {
-			height: 44rpx;
-			display: flex;
-			align-items: center;
+			.tracking-number {
+				display: flex;
+				align-items: center;
 
-			.number {
-				height: 44rpx;
-				font-weight: 400;
-				line-height: 44rpx;
-				font-size: 28rpx;
-				color: #666;
-			}
+				.number {
+					font-size: 28rpx;
+					color: #666;
+				}
 
-			.copy-btn {
-				height: 44rpx;
-				font-weight: 400;
-				font-size: 28rpx;
-				color: #1B64F0;
-				margin-left: 8rpx;
-				line-height: 44rpx;
-				text-align: center;
+				.copy-btn {
+					font-size: 28rpx;
+					color: #1B64F0;
+					margin-left: 16rpx;
+					padding: 0 8rpx;
+					background: #f0f6ff;
+					border-radius: 8rpx;
+				}
 			}
 		}
 	}
 
-
-	// 当前状态样式
+	/* 当前状态卡片 */
 	.current-status {
-		background-color: #fff;
-		border-radius: 16rpx;
+		background: #fff;
+		border-radius: 24rpx;
 		padding: 30rpx;
 		margin-bottom: 20rpx;
 		display: flex;
-		align-items: flex-start;
-		box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
-	}
+		box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
 
-	.status-icon {
-		width: 80rpx;
-		height: 80rpx;
-		background: #1aad19;
-		border-radius: 50%;
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		margin-right: 20rpx;
+		.status-icon {
+			width: 80rpx;
+			height: 80rpx;
+			background: #1aad19;
+			border-radius: 50%;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			margin-right: 20rpx;
+			flex-shrink: 0;
 
-		.icon {
-			color: #fff;
-			font-size: 40rpx;
-			font-weight: bold;
+			.icon {
+				color: #fff;
+				font-size: 44rpx;
+			}
 		}
-	}
 
-	.status-info {
-		flex: 1;
+		.status-info {
+			flex: 1;
 
-		.status-text {
-			font-size: 32rpx;
-			font-weight: bold;
-			color: #333;
-			display: block;
-			margin-bottom: 10rpx;
-		}
+			.status-text {
+				font-size: 32rpx;
+				font-weight: bold;
+				color: #333;
+				display: block;
+				margin-bottom: 8rpx;
+			}
 
-		.status-desc {
-			font-size: 26rpx;
-			color: #666;
-			display: block;
-			margin-bottom: 15rpx;
-			line-height: 1.4;
-		}
+			.status-desc {
+				font-size: 26rpx;
+				color: #666;
+				line-height: 1.4;
+				margin-bottom: 12rpx;
+			}
 
-		.courier-info {
-			background: #f8f8f8;
-			border-radius: 12rpx;
-			padding: 15rpx;
-			margin-bottom: 15rpx;
+			.courier-info {
+				background: #f8f8f8;
+				border-radius: 12rpx;
+				padding: 16rpx;
+				margin-bottom: 12rpx;
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+
+				.courier-text {
+					font-size: 26rpx;
+					color: #333;
+				}
 
-			.courier-text {
-				font-size: 26rpx;
-				color: #333;
-				display: block;
-				margin-bottom: 5rpx;
+				.contact-text {
+					font-size: 26rpx;
+					color: #1aad19;
+					padding: 0 12rpx;
+				}
 			}
 
-			.contact-text {
+			.status-time {
 				font-size: 24rpx;
-				color: #1aad19;
+				color: #999;
 			}
 		}
-
-		.status-time {
-			font-size: 24rpx;
-			color: #999;
-			display: block;
-		}
 	}
 
-	// 时间轴容器
-	.timeline-container {
-		margin-top: 20rpx;
-		background-color: #fff;
+	/* 时间轴滚动区域 */
+	.timeline-scroll {
+		background: #fff;
 		border-radius: 32rpx;
 		margin-bottom: 20rpx;
-		padding: 20rpx;
+		overflow: hidden;
 	}
 
+	.timeline-container {
+		padding: 20rpx 30rpx;
+		box-sizing: border-box;
+	}
+
+	.empty-trace {
+		text-align: center;
+		padding: 60rpx 0;
+		color: #999;
+		font-size: 28rpx;
+	}
+
+	/* 底部操作栏 */
+	.bottom-actions {
+		display: flex;
+		justify-content: space-around;
+		padding: 20rpx 0;
+		background: #fff;
+		border-radius: 32rpx;
+		margin-top: auto;
+
+		.action-btn {
+			width: 300rpx;
+			height: 80rpx;
+			line-height: 80rpx;
+			background: #f0f6ff;
+			color: #1B64F0;
+			border-radius: 40rpx;
+			font-size: 28rpx;
+			border: none;
+			padding: 0;
+			margin: 0;
+
+			&::after {
+				border: none;
+			}
+		}
+	}
 </style>

+ 169 - 333
jd_logistics-app/pages/order/components/TimePopup.vue

@@ -1,5 +1,4 @@
 <template>
-  <!-- 使用 u-popup 作为弹窗容器 -->
   <u-popup 
     :show="visible" 
     mode="bottom" 
@@ -31,7 +30,11 @@
             @tap="selectDate(date.value)"
           >
             <view class="date-content">
-              <text class="date-name">{{ date.label }}</text>
+              <text class="date-name" 
+				  :class="{ 
+					active: selectedDate === date.value,
+					today: date.isToday
+					}">{{ date.label }}</text>
               <text class="date-info">{{ date.month }}月{{ date.day }}日</text>
               <text class="date-week">{{ date.week }}</text>
             </view>
@@ -44,21 +47,20 @@
           <view v-if="selectedDate === todayDate && withinOneHourSlot" class="recommend-section">
             <view class="recommend-header">
               <text class="recommend-title">一小时内</text>
-              <!-- <text class="recommend-desc">推荐时间:{{ withinOneHourSlot.label }}</text> -->
             </view>
-            <view class="recommend-time"
-			 @tap="selectTime(withinOneHourSlot.value, true)">
+            <view 
+              class="recommend-time"
+              :class="{ active: selectedTime === withinOneHourSlot.value }"
+              @tap="selectTime(withinOneHourSlot.value, true)"
+            >
               <text class="time-value">{{ withinOneHourSlot.label }}</text>
-              <!-- <text class="time-tag">推荐</text> -->
             </view>
           </view>
           
-          <!-- 时间段列表 -->
+          <!-- 普通时间段列表 -->
           <view class="time-section">
             <view class="section-title">可选时间段</view>
-            
             <view v-if="loading" class="loading">加载中...</view>
-            
             <view v-else class="time-list">
               <view 
                 v-for="timeSlot in timeSlots" 
@@ -80,7 +82,7 @@
           <!-- 提示信息 -->
           <view class="notice-section">
             <text class="notice-text">
-              部分区域支持夜间上门,具体以快递员联系为准
+              <!-- 部分区域支持夜间上门,具体以快递员联系为准 -->
             </text>
           </view>
         </scroll-view>
@@ -104,245 +106,155 @@
 import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
 
 const props = defineProps({
-  visible: {
-    type: Boolean,
-    default: false
-  },
-  // 显示天数
-  days: {
-    type: Number,
-    default: 3
-  },
-  // 开始时间
-  startTime: {
-    type: Number,
-    default: 9
-  },
-  // 结束时间
-  endTime: {
-    type: Number,
-    default: 21
-  },
-  // 是否自动选择推荐时间
-  autoSelect: {
-    type: Boolean,
-    default: true
-  }
+  visible: { type: Boolean, default: false },
+  days: { type: Number, default: 3 },
+  startTime: { type: Number, default: 8 },
+  endTime: { type: Number, default: 20 },
+  autoSelect: { type: Boolean, default: true }
 })
 
 const emit = defineEmits(['update:visible', 'close', 'confirm'])
 
-// 当前时间
+// 当前时间(每分钟更新)
 const currentTime = ref(new Date())
 const selectedDate = ref('')
 const selectedTime = ref('')
 const loading = ref(false)
 const isWithinOneHour = ref(false)
 
-// 计算当前时间20分钟后的时间
+// ----- 精确时间计算 -----
+const oneHourLater = computed(() => {
+  const now = currentTime.value
+  return now.getHours() + now.getMinutes() / 60 + 1
+})
+
 const twentyMinutesLater = computed(() => {
-  const now = new Date()
+  const now = new Date(currentTime.value)
   now.setMinutes(now.getMinutes() + 10)
   return now
 })
 
-// 计算一小时后的小时(向上取整)
-const oneHourLaterHour = computed(() => {
-  const now = new Date()
-  now.setHours(now.getHours() + 1)
-  return Math.ceil(now.getHours())
-})
+// ----- 推荐时段(唯一 value 前缀,不与普通时段冲突)-----
+const withinOneHourSlot = computed(() => {
+  if (!dateList.value.length || !dateList.value[0].isToday) return null
+  
+  const later = twentyMinutesLater.value
+  let startHour = later.getHours()
+  let startMinute = Math.ceil(later.getMinutes() / 10) * 10
 
-// 获取今天日期字符串
-const todayDate = computed(() => {
-  const today = new Date()
-  return formatDate(today)
+  if (startMinute >= 60) {
+    startHour += 1
+    startMinute = 0
+  }
+  if (startHour >= props.endTime) return null
+
+  let endHour = startHour + 1
+  let endMinute = startMinute
+  if (endHour > props.endTime) {
+    endHour = props.endTime
+    endMinute = 0
+  }
+  const duration = (endHour - startHour) * 60 + (endMinute - startMinute)
+  if (duration < 30) return null
+
+  const startStr = formatTime(startHour, startMinute)
+  const endStr = formatTime(endHour, endMinute)
+  return {
+    start: startHour + startMinute / 60,
+    end: endHour + endMinute / 60,
+    label: `(${startStr}-${endStr})`,
+    value: `immediate:${startStr}-${endStr}`, // ✅ 唯一标识
+    startHour, startMinute, endHour, endMinute
+  }
 })
 
-// 生成日期列表
+// ----- 日期列表 -----
+const todayDate = computed(() => formatDate(new Date()))
 const dateList = computed(() => {
   const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
   const list = []
   const today = new Date()
-  
   for (let i = 0; i < props.days; i++) {
     const date = new Date(today)
     date.setDate(date.getDate() + i)
-    
     const month = date.getMonth() + 1
     const day = date.getDate()
     const week = days[date.getDay()]
     const dateStr = formatDate(date)
-    
-    let label = ''
-    if (i === 0) {
-      label = '今天'
-    } else if (i === 1) {
-      label = '明天'
-    } else {
-      label = '后天'
-    }
-    
     list.push({
       value: dateStr,
-      label: label,
-      week: week,
-      month: month,
-      day: day,
-      fullLabel: `${month}月${day}日 ${week}`,
+      label: i === 0 ? '今天' : i === 1 ? '明天' : '后天',
+      week, month, day,
       isToday: i === 0,
-      isTomorrow: i === 1,
-      isDayAfterTomorrow: i === 2,
-      displayDate: `${date.getFullYear()}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`
+      displayDate: dateStr
     })
   }
-  
   return list
 })
 
-// 基础时间段配置
+// ----- 基础整点时段 -----
 const baseTimeSlots = computed(() => {
   const slots = []
-  const startHour = props.startTime
-  const endHour = props.endTime
-  const interval = 1 // 小时间隔
-  
-  for (let hour = startHour; hour < endHour; hour += interval) {
-    const start = hour
-    const end = hour + interval
-    const startStr = start.toString().padStart(2, '0')
-    const endStr = end.toString().padStart(2, '0')
-    
+  for (let hour = props.startTime; hour < props.endTime; hour++) {
     slots.push({
-      start: start,
-      end: end,
-      label: `${startStr}:00-${endStr}:00`,
-      value: `${startStr}:00-${endStr}:00`,
-      startHour: start,
-      endHour: end
+      start: hour,
+      end: hour + 1,
+      label: `${padZero(hour)}:00-${padZero(hour + 1)}:00`,
+      value: `${padZero(hour)}:00-${padZero(hour + 1)}:00`,
+      startHour: hour,
+      endHour: hour + 1
     })
   }
-  
   return slots
 })
 
-// 计算一小时内时间段
-const withinOneHourSlot = computed(() => {
-  if (!dateList.value.length || !dateList.value[0].isToday) return null
-  
-  const now = currentTime.value
-  const later20min = twentyMinutesLater.value
-  
-  // 计算开始时间:当前时间 + 20分钟,分钟向上取整到10的倍数
-  const startMinutes = later20min.getMinutes()
-  const roundedMinutes = Math.ceil(startMinutes / 10) * 10
-  let startHour = later20min.getHours()
-  let startMinute = roundedMinutes
-  
-  // 如果分钟超过60,小时加1,分钟归零
-  if (startMinute >= 60) {
-    startHour += 1
-    startMinute = 0
-  }
-  
-  // 计算结束时间:开始时间 + 1小时
-  let endHour = startHour + 1
-  let endMinute = startMinute
-  
-  // 如果开始时间已经超过结束时间限制,则不显示
-  if (startHour >= props.endTime) return null
-  
-  // 如果结束时间超过结束时间限制,则调整
-  if (endHour > props.endTime) {
-    endHour = props.endTime
-    endMinute = 0
-  }
-  
-  // 如果开始时间小于开始时间限制,则调整
-  if (startHour < props.startTime) {
-    startHour = props.startTime
-    startMinute = 0
-  }
-  
-  // 检查时间段是否有效(至少30分钟)
-  const slotDuration = (endHour - startHour) * 60 + (endMinute - startMinute)
-  if (slotDuration < 30) return null
-  
-  const startStr = formatTime(startHour, startMinute)
-  const endStr = formatTime(endHour, endMinute)
-  
-  return {
-    start: startHour + startMinute / 60,
-    end: endHour + endMinute / 60,
-    label: `(${startStr}-${endStr})`,
-    value: `${startStr}-${endStr}`,
-    isWithinOneHour: true,
-    startHour: startHour,
-    startMinute: startMinute,
-    endHour: endHour,
-    endMinute: endMinute
-  }
-})
-
-// 是否是夜间时间段
-const isNightTime = (hour) => {
-  return hour >= 18
-}
-
-// 根据选择的日期获取时间段
+// ----- 根据所选日期过滤可用时段(精确到小时+分钟)-----
 const timeSlots = computed(() => {
   const slots = []
   const isToday = selectedDate.value === todayDate.value
-  
+
   if (isToday) {
-    const oneHourLater = oneHourLaterHour.value
-    
-    // 添加普通时间段(开始时间从一小时后开始)
+    const threshold = oneHourLater.value
     baseTimeSlots.value.forEach(slot => {
-      // 时间段开始时间必须在当前时间+1小时之后
-      const isDisabled = slot.start < oneHourLater
-      
-      if (!isDisabled) {
+      if (slot.start >= threshold) {
         slots.push({
           ...slot,
           disabled: false,
-          night: false,
+          night: slot.start >= 22,
           tag: ''
-        })		
-		// night: isNightTime(slot.start),
+        })
       }
     })
   } else {
-    // 明天及以后显示所有时间段
     baseTimeSlots.value.forEach(slot => {
       slots.push({
         ...slot,
         disabled: false,
-        night: isNightTime(slot.start),
+        night: slot.start >= 22,
         tag: ''
       })
     })
   }
-  
   return slots
 })
 
-// 按钮文本
-const buttonText = computed(() => {
-  if (!selectedTime.value) return '请选择上门时间'
-  return '确定'
-})
+// ----- 按钮文字 -----
+const buttonText = computed(() => selectedTime.value ? '确定' : '请选择上门时间')
 
-// 初始化选择
-const initSelection = () => {
-  if (dateList.value.length > 0) {
-    selectedDate.value = dateList.value[0].value
-    
-    // 自动选择推荐时间或第一个可用时间
-    if (withinOneHourSlot.value && props.autoSelect) {
+// ----- 核心:切换日期(自动根据 autoSelect 选中第一个可用时段)-----
+const selectDate = (date) => {
+  selectedDate.value = date
+  selectedTime.value = ''
+  isWithinOneHour.value = false
+
+  // 若开启自动选择,立即为该日期选择一个合适的时间
+  if (props.autoSelect) {
+    // 1. 如果是今天且有推荐时段,优先选中推荐
+    if (date === todayDate.value && withinOneHourSlot.value) {
       selectedTime.value = withinOneHourSlot.value.value
       isWithinOneHour.value = true
     } else {
+      // 2. 否则选中该日期下第一个可用普通时段
       const firstSlot = timeSlots.value[0]
       if (firstSlot) {
         selectedTime.value = firstSlot.value
@@ -352,121 +264,98 @@ const initSelection = () => {
   }
 }
 
-// 选择日期
-const selectDate = (date) => {
-  selectedDate.value = date
-  selectedTime.value = '' // 重置时间选择
-  isWithinOneHour.value = false
-}
-
-// 选择时间
-const selectTime = (time, isWithinOneHourFlag = false) => {
+// ----- 选择时间(普通时段 / 推荐时段)-----
+const selectTime = (time, isImmediate = false) => {
   selectedTime.value = time
-  isWithinOneHour.value = isWithinOneHourFlag
+  isWithinOneHour.value = isImmediate
 }
 
-// 格式化日期时间
-const formatDateTime = (dateStr, hour) => {
-  const date = new Date(dateStr)
-  const wholeHour = Math.floor(hour)
-  const minutes = Math.round((hour - wholeHour) * 60)
-  date.setHours(wholeHour, minutes, 0, 0)
-  
-  const year = date.getFullYear()
-  const month = String(date.getMonth() + 1).padStart(2, '0')
-  const day = String(date.getDate()).padStart(2, '0')
-  const hours = String(date.getHours()).padStart(2, '0')
-  const mins = String(date.getMinutes()).padStart(2, '0')
-  const seconds = String(date.getSeconds()).padStart(2, '0')
-  
-  return `${year}-${month}-${day} ${hours}:${mins}:${seconds}`
-}
-
-// 获取开始小时
-const getStartHour = (timeLabel) => {
-  if (isWithinOneHour.value) {
-    return withinOneHourSlot.value.startHour + withinOneHourSlot.value.startMinute / 60
-  }
-  
-  const match = timeLabel.match(/(\d{2}):/)
-  return match ? parseInt(match[1]) : 0
-}
-
-// 获取结束小时
-const getEndHour = (timeLabel) => {
-  if (isWithinOneHour.value) {
-    return withinOneHourSlot.value.endHour + withinOneHourSlot.value.endMinute / 60
+// ----- 初始化弹窗(打开时)-----
+const initSelection = () => {
+  if (dateList.value.length) {
+    // 直接调用 selectDate,复用自动选择逻辑
+    selectDate(dateList.value[0].value)
   }
-  
-  const match = timeLabel.match(/-(\d{2}):/)
-  return match ? parseInt(match[1]) : 0
 }
 
-// 确认选择
+// ----- 确认选择 -----
 const confirm = () => {
   if (!selectedTime.value) return
-  
+
   const selectedDateObj = dateList.value.find(d => d.value === selectedDate.value)
-  const selectedTimeSlot = isWithinOneHour.value ? withinOneHourSlot.value : 
-                          baseTimeSlots.value.find(s => s.value === selectedTime.value)
-  
-  if (!selectedDateObj || !selectedTimeSlot) return
-  
-  // 计算开始和结束时间
-  const startHour = getStartHour(selectedTime.value)
-  const endHour = getEndHour(selectedTime.value)
-  
-  // 计算具体的开始和结束时间字符串
-  const startTimeStr = formatDateTime(selectedDate.value, startHour)
-  const endTimeStr = formatDateTime(selectedDate.value, endHour)
-  
-  // 生成显示文本
-  let displayText = ''
+  let selectedTimeSlot
+
   if (isWithinOneHour.value) {
-    displayText = `${selectedDateObj.label} 一小时内`
+    selectedTimeSlot = withinOneHourSlot.value
   } else {
-    displayText = `${selectedDateObj.label} ${selectedTime.value}`
+    selectedTimeSlot = baseTimeSlots.value.find(s => s.value === selectedTime.value)
   }
-  
-  const timeData = {
+
+  if (!selectedDateObj || !selectedTimeSlot) return
+
+  const startHour = isWithinOneHour.value
+    ? withinOneHourSlot.value.start
+    : selectedTimeSlot.start
+  const endHour = isWithinOneHour.value
+    ? withinOneHourSlot.value.end
+    : selectedTimeSlot.end
+
+  const startTimeStr = formatDateTime(selectedDate.value, startHour)
+  const endTimeStr = formatDateTime(selectedDate.value, endHour)
+
+  const displayText = isWithinOneHour.value
+    ? `${selectedDateObj.label} 一小时内`
+    : `${selectedDateObj.label} ${selectedTime.value}`
+
+  emit('confirm', {
     date: selectedDate.value,
     time: selectedTime.value,
     dateLabel: selectedDateObj.label,
     timeLabel: selectedTime.value,
     fullLabel: displayText,
-    displayText: displayText,
+    displayText,
     startTime: startTimeStr,
     endTime: endTimeStr,
     isToday: selectedDateObj.isToday,
     isImmediate: isWithinOneHour.value,
-    isNight: isNightTime(selectedTimeSlot.startHour),
-    startHour: startHour,
-    endHour: endHour
-  }
-  
-  emit('confirm', timeData)
+    isNight: selectedTimeSlot.start >= 18,
+    startHour,
+    endHour
+  })
   close()
 }
 
-// 关闭弹框
+// ----- 关闭弹窗 -----
 const close = () => {
   emit('close')
   emit('update:visible', false)
 }
 
-// 工具函数
+// ----- 工具函数 -----
 const formatDate = (date) => {
-  const year = date.getFullYear()
-  const month = String(date.getMonth() + 1).padStart(2, '0')
-  const day = String(date.getDate()).padStart(2, '0')
-  return `${year}-${month}-${day}`
+  const y = date.getFullYear()
+  const m = String(date.getMonth() + 1).padStart(2, '0')
+  const d = String(date.getDate()).padStart(2, '0')
+  return `${y}-${m}-${d}`
 }
-
 const formatTime = (hour, minute) => {
-  return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
+  return `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`
+}
+const padZero = (num) => String(num).padStart(2, '0')
+const formatDateTime = (dateStr, hourDecimal) => {
+  const date = new Date(dateStr)
+  const wholeHour = Math.floor(hourDecimal)
+  const minutes = Math.round((hourDecimal - wholeHour) * 60)
+  date.setHours(wholeHour, minutes, 0, 0)
+  const y = date.getFullYear()
+  const m = String(date.getMonth() + 1).padStart(2, '0')
+  const d = String(date.getDate()).padStart(2, '0')
+  const hh = String(date.getHours()).padStart(2, '0')
+  const mm = String(date.getMinutes()).padStart(2, '0')
+  return `${y}-${m}-${d} ${hh}:${mm}:00`
 }
 
-// 监听可见性变化
+// ----- 监听弹窗显示,初始化 -----
 watch(() => props.visible, (newVal) => {
   if (newVal) {
     currentTime.value = new Date()
@@ -474,32 +363,26 @@ watch(() => props.visible, (newVal) => {
   }
 })
 
-// 定时更新当前时间
+// ----- 每分钟刷新当前时间(仅弹窗可见时)-----
 let timer = null
 onMounted(() => {
   timer = setInterval(() => {
     if (props.visible) {
       currentTime.value = new Date()
     }
-  }, 60000) // 每分钟更新一次
+  }, 60000)
 })
-
-// 组件销毁时清除定时器
 onUnmounted(() => {
-  if (timer) {
-    clearInterval(timer)
-  }
+  if (timer) clearInterval(timer)
 })
 </script>
 
 <style scoped lang="less">
-/* 弹窗内部容器 */
 .time-picker-container {
   height: 80vh;
   display: flex;
   flex-direction: column;
 }
-
 .picker-header {
   display: flex;
   justify-content: space-between;
@@ -508,44 +391,35 @@ onUnmounted(() => {
   border-bottom: 1rpx solid #f0f0f0;
   flex-shrink: 0;
 }
-
 .picker-title {
   font-size: 36rpx;
   font-weight: 600;
   color: #333;
 }
-
 .close-btn {
   font-size: 50rpx;
   color: #999;
   line-height: 40rpx;
   padding: 10rpx;
 }
-
-/* 主要内容区域 */
 .main-content {
   flex: 1;
   display: flex;
   overflow: hidden;
 }
-
-/* 左侧日期选择 */
 .date-sidebar {
   width: 220rpx;
   background-color: #f8f8f8;
   border-right: 1rpx solid #f0f0f0;
 }
-
 .date-item {
   padding: 32rpx 24rpx;
   border-bottom: 1rpx solid #f0f0f0;
 }
-
 .date-item.active {
   background-color: #fff;
   position: relative;
 }
-
 .date-item.active::before {
   content: '';
   position: absolute;
@@ -557,114 +431,96 @@ onUnmounted(() => {
   background-color: #1B64F0;
   border-radius: 0 4rpx 4rpx 0;
 }
-
 .date-item.today .date-name {
-  color: #1B64F0;
+  // color: #1B64F0;
+  color: #333;
 }
-
 .date-item.active .date-content {
   color: #1B64F0;
+  font-weight: 600;
 }
-
 .date-content {
   display: flex;
   flex-direction: column;
   align-items: center;
   text-align: center;
 }
-
 .date-name {
   font-size: 32rpx;
-  font-weight: 600;
   color: #333;
   margin-bottom: 8rpx;
+  &.active{  
+	color: #1B64F0;
+  }
 }
-
 .date-info {
   font-size: 24rpx;
   color: #666;
   margin-bottom: 4rpx;
 }
-
 .date-week {
   font-size: 22rpx;
   color: #999;
 }
-
-/* 右侧时间选择 */
 .time-content {
   flex: 1;
   padding: 0 32rpx;
   overflow-y: auto;
 }
-
 .recommend-section {
   margin-top: 32rpx;
   padding-bottom: 24rpx;
   border-bottom: 1rpx solid #f0f0f0;
 }
-
 .recommend-header {
   display: flex;
   justify-content: space-between;
   align-items: center;
   margin-bottom: 20rpx;
 }
-
 .recommend-title {
   font-size: 28rpx;
   font-weight: 500;
   color: #333;
 }
-
-.recommend-desc {
-  font-size: 24rpx;
-  color: #1B64F0;
-}
-
+/* 推荐时段:未选中时灰色边框,无背景 */
 .recommend-time {
   display: flex;
   align-items: center;
   justify-content: space-between;
   padding: 28rpx 24rpx;
-  // background: linear-gradient(135deg, #fff2f0 0%, #ffe6e6 100%);
   border-radius: 16rpx;
-  border: 2rpx solid #1B64F0;
+  border: 2rpx solid #e8e8e8;
+  transition: all 0.2s;
 }
-
-.time-value {
+.recommend-time .time-value {
   font-size: 32rpx;
   font-weight: 500;
-  color: #1B64F0;
+  color: #333;
 }
-
-.recommend-time .time-tag {
-  font-size: 24rpx;
-  padding: 8rpx 16rpx;
-  background: #1B64F0;
-  color: white;
-  border-radius: 20rpx;
+/* 推荐时段:选中时蓝色背景 + 白色文字 */
+.recommend-time.active {
+  // background-color: #1B64F0;
+  border-color: #1B64F0;
+}
+.recommend-time.active .time-value {
+  // color: white;
 }
-
-/* 时间段列表 */
 .time-section {
   margin-top: 32rpx;
   margin-bottom: 32rpx;
 }
-
 .section-title {
   font-size: 28rpx;
   font-weight: 500;
   color: #333;
   margin-bottom: 24rpx;
 }
-
 .time-list {
   display: flex;
   flex-direction: column;
   gap: 20rpx;
 }
-
 .time-item {
   padding: 28rpx 24rpx;
   border: 2rpx solid #e8e8e8;
@@ -672,42 +528,33 @@ onUnmounted(() => {
   display: flex;
   align-items: center;
   justify-content: space-between;
-  position: relative;
   transition: all 0.2s ease;
 }
-
 .time-item.active {
   border-color: #1B64F0;
-  // background: #fff2f0;
+  background-color: #fff;
 }
-
 .time-item.night {
   border-color: #4a5cff;
 }
-
 .time-item:not(.disabled):active {
   transform: scale(0.98);
   opacity: 0.8;
 }
-
 .time-item.disabled {
   opacity: 0.5;
 }
-
 .time-text {
   font-size: 28rpx;
   color: #333;
 }
-
 .time-item.active .time-text {
   color: #1B64F0;
   font-weight: 500;
 }
-
 .time-item.night .time-text {
   color: #4a5cff;
 }
-
 .time-item .time-tag {
   font-size: 20rpx;
   padding: 4rpx 12rpx;
@@ -715,32 +562,25 @@ onUnmounted(() => {
   background: #f5f5f5;
   color: #666;
 }
-
 .time-item.active .time-tag {
   background: #1B64F0;
   color: white;
 }
-
 .time-item.night .time-tag {
   background: #4a5cff;
   color: white;
 }
-
-/* 提示信息 */
 .notice-section {
   padding: 24rpx;
   margin-bottom: 32rpx;
   background: #f8f8f8;
   border-radius: 12rpx;
 }
-
 .notice-text {
   font-size: 24rpx;
   color: #666;
   line-height: 1.4;
 }
-
-/* 操作按钮 */
 .action-bar {
   padding: 24rpx 32rpx;
   padding-top: 0;
@@ -748,7 +588,6 @@ onUnmounted(() => {
   border-top: 1rpx solid #f0f0f0;
   flex-shrink: 0;
 }
-
 .confirm-btn {
   width: 100%;
   height: 88rpx;
@@ -763,17 +602,14 @@ onUnmounted(() => {
   border: none;
   transition: all 0.2s ease;
 }
-
 .confirm-btn.active {
   background: #1B64F0;
   color: white;
 }
-
 .confirm-btn.active:active {
   opacity: 0.9;
   transform: scale(0.99);
 }
-
 .loading {
   text-align: center;
   padding: 60rpx;

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 421 - 363
jd_logistics-app/pages/order/create_order.vue


+ 3 - 18
jd_logistics-app/pages/search/search.vue

@@ -52,10 +52,6 @@
 	}
 
 	const handleSearch = () => {
-		uni.navigateTo({
-			url:'/pages/logistics/index'
-		})
-		return
 		if (!trackingNumber.value.trim()) {
 			uni.showToast({
 				title: '请输入运单号',
@@ -63,21 +59,10 @@
 			})
 			return
 		}
-
-		// 这里处理搜索逻辑
-		console.log('搜索运单号:', trackingNumber.value)
-		uni.showLoading({
-			title: '查询中...'
+		uni.navigateTo({
+			url:'/pages/logistics/index?orderNo=' + trackingNumber
 		})
-
-		// 模拟API调用
-		setTimeout(() => {
-			uni.hideLoading()
-			uni.showToast({
-				title: '查询成功',
-				icon: 'success'
-			})
-		}, 1500)
+		return
 	}
 </script>
 

+ 1 - 1
jd_logistics-app/utils/util.js

@@ -330,7 +330,7 @@ export function checkLoginShowModal() {
     if (!checkLogin()) {
       uni.showModal({
         title: '温馨提示',
-        content: '登录后将享受更多优质权益,请先登录',
+        content: '登录后使用该功能,请先登录',
         // showCancel:false,
         success: function (res) {
           if (res.confirm) {