301 lines
7.3 KiB
Plaintext
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>
|