Files
medical-mall/pages/mall/delivery/test/platform-tracking-query.uvue
not-like-juvenile a5e7afacec 创建数据库表格
2026-02-06 16:56:24 +08:00

301 lines
7.3 KiB
Plaintext

<template>
<view class="container">
<view class="header">
<text class="title">运单/轨迹查询 (运维排障)</text>
</view>
<!-- 搜索栏 -->
<view class="search-section">
<input v-model="searchQuery" placeholder="请输入运单号或订单号" class="search-input" />
<button class="search-btn" @click="doSearch">查询</button>
</view>
<view v-if="waybillInfo" class="result-section">
<!-- 运单摘要 -->
<view class="card summary-card">
<view class="card-title">基本信息</view>
<view class="grid">
<view class="info-item">
<text class="label">运单号:</text>
<text class="value highlight">{{ waybillInfo.tracking_no }}</text>
</view>
<view class="info-item">
<text class="label">承运商:</text>
<text class="value">{{ waybillInfo.carrier }}</text>
</view>
<view class="info-item">
<text class="label">订单号:</text>
<text class="value">{{ waybillInfo.order_no }}</text>
</view>
<view class="info-item">
<text class="label">当前状态:</text>
<text class="value">{{ getStatusText(waybillInfo.status) }}</text>
</view>
</view>
</view>
<!-- 轨迹列表 -->
<view class="card events-card">
<view class="card-title">轨迹列表 (内网入库)</view>
<view v-for="(event, index) in events" :key="index" class="event-row">
<view class="event-time-col">
<text class="event-time">{{ formatTime(event.event_time) }}</text>
</view>
<view class="event-main-col">
<text class="event-text">{{ event.event_text }}</text>
<view class="event-meta">
<text class="meta-tag blue">代码: {{ event.event_code }}</text>
<text class="meta-tag orange">映射状态: {{ getStatusMapping(event.status_code) }}</text>
</view>
<view class="raw-box" v-if="showRaw[index]">
<text class="raw-text">{{ JSON.stringify(event.raw_payload, null, 2) }}</text>
</view>
<text class="toggle-raw" @click="toggleRaw(index)">{{ showRaw[index] ? '折叠原始报文' : '查看原始报文' }}</text>
</view>
</view>
</view>
<!-- Mock 控制板 (仅测试环境) -->
<view v-if="isTestEnv" class="card mock-card">
<view class="card-title">Mock 控制台 (测试辅助)</view>
<view class="action-grid">
<button class="mock-btn" @click="runScenario('full')">生成全流程轨迹</button>
<button class="mock-btn" @click="runScenario('exception')">注入异常节点</button>
<button class="mock-btn" @click="runScenario('standard')">步进下一节点</button>
</view>
</view>
</view>
<view v-else-if="hasSearched" class="empty-section">
<text>未找到相关运单记录</text>
</view>
</view>
</template>
<script uts>
import { mockService, MockOrder, MockTrackingEvent } from './mock-service.uts'
export default {
data() {
return {
searchQuery: 'YD987654321',
hasSearched: false,
waybillInfo: null as MockOrder | null,
events: [] as MockTrackingEvent[],
showRaw: [] as boolean[],
isTestEnv: true
}
},
methods: {
async doSearch() {
if (!this.searchQuery) return
uni.showLoading({ title: '查询中...' })
setTimeout(() => {
this.hasSearched = true
const tracking = mockService.getMockTracking(this.searchQuery)
const orders = mockService.getMockOrders()
const order = orders.find((o: MockOrder): boolean => o.tracking_no === this.searchQuery || o.order_no === this.searchQuery)
if (order != null) {
this.waybillInfo = order
this.events = tracking
} else {
this.waybillInfo = null
this.events = []
}
this.showRaw = new Array(this.events.length).fill(false)
uni.hideLoading()
}, 600)
},
formatTime(time: string) : string {
return time
},
toggleRaw(index: number) {
const current = this.showRaw[index]
this.showRaw[index] = !current
},
getStatusText(status: string) : string {
const maps = {
'PENDING': '待发货',
'SHIPPED': '已发货',
'IN_TRANSIT': '运输中',
'DELIVERED': '已签收',
'OUT_FOR_DELIVERY': '派送中',
'EXCEPTION': '异常'
}
return (maps[status] != null) ? maps[status] : status
},
getStatusMapping(code: string) : string {
const maps = {
'SHIPPED': '已揽收',
'IN_TRANSIT': '运输中',
'ARRIVED_HUB': '到达分拨中心',
'OUT_FOR_DELIVERY': '正在派送',
'DELIVERED': '已签收',
'EXCEPTION': '包裹异常'
}
return (maps[code] != null) ? maps[code] : code
},
runScenario(type: string) {
if (this.waybillInfo == null) return
uni.showModal({
title: '确认模拟',
content: `是否对 ${this.waybillInfo!.tracking_no} 运行【${type}】场景模拟?`,
success: (res) => {
if (res.confirm) {
mockService.runScenario(this.waybillInfo!.tracking_no, type)
uni.showToast({ title: '已触发' })
setTimeout(() => this.doSearch(), 1000)
}
}
})
}
}
}
</script>
<style scoped>
.container {
padding: 24rpx;
background-color: #f0f2f5;
min-height: 100vh;
}
.header {
margin-bottom: 30rpx;
}
.title {
font-size: 34rpx;
font-weight: bold;
color: #1a1a1a;
}
.search-section {
display: flex;
gap: 20rpx;
margin-bottom: 30rpx;
}
.search-input {
flex: 1;
background-color: #fff;
border: 1rpx solid #dcdfe6;
padding: 20rpx;
border-radius: 8rpx;
font-size: 28rpx;
}
.search-btn {
background-color: #409eff;
color: #fff;
padding: 0 40rpx;
border-radius: 8rpx;
font-size: 28rpx;
}
.card {
background-color: #fff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.05);
}
.card-title {
font-size: 28rpx;
font-weight: bold;
color: #606266;
margin-bottom: 20rpx;
border-bottom: 1rpx solid #ebeef5;
padding-bottom: 16rpx;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20rpx;
}
.info-item {
display: flex;
font-size: 26rpx;
}
.label {
color: #909399;
margin-right: 12rpx;
}
.value {
color: #303133;
}
.value.highlight {
color: #409eff;
font-weight: bold;
}
.event-row {
display: flex;
border-bottom: 1rpx solid #f2f6fc;
padding: 20rpx 0;
}
.event-time-col {
width: 200rpx;
}
.event-time {
font-size: 24rpx;
color: #909399;
}
.event-main-col {
flex: 1;
}
.event-text {
font-size: 28rpx;
color: #303133;
margin-bottom: 10rpx;
display: block;
}
.event-meta {
display: flex;
gap: 12rpx;
margin-bottom: 12rpx;
}
.meta-tag {
font-size: 20rpx;
padding: 2rpx 10rpx;
border-radius: 4rpx;
}
.meta-tag.blue {
background-color: #ecf5ff;
color: #409eff;
}
.meta-tag.orange {
background-color: #fdf6ec;
color: #e6a23c;
}
.toggle-raw {
font-size: 22rpx;
color: #409eff;
cursor: pointer;
}
.raw-box {
background-color: #f5f7fa;
padding: 16rpx;
border-radius: 4rpx;
margin: 10rpx 0;
}
.raw-text {
font-size: 20rpx;
color: #606266;
font-family: monospace;
}
.action-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 16rpx;
}
.mock-btn {
font-size: 24rpx;
padding: 16rpx 0;
background-color: #f4f4f5;
color: #909399;
border: 1rpx solid #dcdfe6;
}
.empty-section {
text-align: center;
padding: 100rpx 0;
color: #909399;
}
</style>