Files
medical-mall/pages/mall/delivery/test/api-simulator.uvue
2026-02-06 15:10:18 +08:00

292 lines
6.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="container">
<view class="header">
<text class="title">第三方物流 API 模拟发送器</text>
<text class="subtitle">模拟外部物流平台向后端推送 Webhook 轨迹数据</text>
</view>
<view class="section">
<text class="section-title">1. 选择目标订单 (已发货)</text>
<scroll-view class="order-list" direction="horizontal">
<view v-for="(item, index) in shippedOrders" :key="index"
:class="['order-card', selectedOrderIndex == index ? 'active' : '']"
@click="selectOrder(index)">
<text class="order-no">{{ item.order_no }}</text>
<text class="tracking-no">{{ item.carrier }}: {{ item.tracking_no }}</text>
</view>
</scroll-view>
</view>
<view class="section">
<text class="section-title">2. 构造回调数据 (JSON Payload)</text>
<view class="form-group">
<text class="label">运单号:</text>
<input class="input" v-model="form.tracking_no" placeholder="请输入运单号" />
</view>
<view class="form-group">
<text class="label">快递公司:</text>
<input class="input" v-model="form.carrier" placeholder="请输入快递公司" />
</view>
<view class="form-group">
<text class="label">物流状态:</text>
<picker :range="statusOptions" range-key="label" @change="onStatusChange">
<view class="picker-val">{{ currentStatusLabel }}</view>
</picker>
</view>
<view class="form-group">
<text class="label">轨迹描述文字:</text>
<textarea class="textarea" v-model="form.event_text" placeholder="描述当前的物流位置或状态..." />
</view>
</view>
<view class="payload-preview">
<text class="preview-title">接口发送原始数据预览:</text>
<view class="code-block">
<text class="code-text">{{ jsonString }}</text>
</view>
</view>
<button class="btn-send" type="primary" @click="sendWebhook">立即推送 API 数据</button>
<view class="footer-links">
<text class="link" @click="goToLogs">查看接收日志 (Webhook Logs)</text>
</view>
</view>
</template>
<script setup lang="uts">
import { mockService, MockOrder } from './mock-service.uts'
const shippedOrders = computed((): MockOrder[] => {
return mockService.getMockOrders().filter((o: MockOrder): boolean => o.status !== 'PENDING' && o.tracking_no !== '')
})
const selectedOrderIndex = ref(-1)
const form = reactive({
tracking_no: '',
carrier: '',
status_code: 'IN_TRANSIT',
event_text: '快件已到达【XX转运中心】准备发往下一站'
})
const statusOptions = [
{ label: '在途中 (IN_TRANSIT)', value: 'IN_TRANSIT' },
{ label: '派送中 (OUT_FOR_DELIVERY)', value: 'OUT_FOR_DELIVERY' },
{ label: '已签收 (DELIVERED)', value: 'DELIVERED' },
{ label: '异常 (EXCEPTION)', value: 'EXCEPTION' }
]
const currentStatusLabel = computed(() => {
const opt = statusOptions.find(o => o.value === form.status_code)
return opt ? opt.label : '请选择'
})
const jsonString = computed(() => {
return JSON.stringify(form, null, 2)
})
function selectOrder(index: number) {
selectedOrderIndex.value = index
const order = shippedOrders.value[index]
form.tracking_no = order.tracking_no
form.carrier = order.carrier
// 根据订单当前状态智能预设
if (order.status === 'SHIPPED') {
form.status_code = 'IN_TRANSIT'
form.event_text = '快件已揽收,正发往城市中心'
} else if (order.status === 'IN_TRANSIT') {
form.status_code = 'OUT_FOR_DELIVERY'
form.event_text = '派送员王师傅(13700008888)正在派件'
}
}
function onStatusChange(e: UniPickerChangeEvent) {
const idx = e.detail.value as number
form.status_code = statusOptions[idx].value as string
}
function sendWebhook() {
if (!form.tracking_no) {
uni.showToast({ title: '请先填写运单号', icon: 'none' })
return
}
// 执行模拟推送 (转换为普通对象以兼容 UTS)
const payload = {
tracking_no: form.tracking_no,
carrier: form.carrier,
status_code: form.status_code,
event_text: form.event_text
} as UTSJSONObject
const success = mockService.pushWebhookData(payload)
if (success) {
uni.showToast({ title: 'API 发送成功!', icon: 'success' })
// 可选:跳转到详情或日志预览
} else {
uni.showModal({
title: '发送失败',
content: '系统未找到该运单号,后端拒绝接收该数据。',
showCancel: false
})
}
}
function goToLogs() {
uni.navigateTo({ url: '/pages/mall/delivery/test/webhook-logs' })
}
</script>
<style scoped>
.container {
padding: 20px;
background-color: #f8f9fa;
min-height: 100vh;
}
.header {
margin-bottom: 25px;
}
.title {
font-size: 20px;
font-weight: bold;
color: #333;
display: block;
}
.subtitle {
font-size: 13px;
color: #666;
margin-top: 5px;
display: block;
}
.section {
margin-bottom: 25px;
background: #fff;
padding: 15px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.section-title {
font-size: 15px;
font-weight: 600;
color: #444;
margin-bottom: 12px;
display: block;
}
.order-list {
white-space: nowrap;
display: flex;
flex-direction: row;
}
.order-card {
display: inline-block;
width: 160px;
padding: 12px;
background: #f0f4f8;
border: 1.5px solid transparent;
border-radius: 8px;
margin-right: 12px;
}
.order-card.active {
border-color: #007aff;
background: #eef6ff;
}
.order-no {
font-size: 13px;
font-weight: bold;
display: block;
}
.tracking-no {
font-size: 11px;
color: #888;
margin-top: 4px;
display: block;
}
.form-group {
margin-bottom: 15px;
border-bottom: 1px solid #f0f0f0;
padding-bottom: 10px;
}
.label {
font-size: 13px;
color: #666;
margin-bottom: 5px;
display: block;
}
.input {
font-size: 14px;
color: #333;
height: 35px;
}
.picker-val {
font-size: 14px;
color: #007aff;
padding: 5px 0;
}
.textarea {
font-size: 14px;
width: 100%;
height: 80px;
background: #fafafa;
padding: 8px;
border-radius: 4px;
}
.payload-preview {
background: #2d2d2d;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.preview-title {
color: #aaa;
font-size: 12px;
margin-bottom: 10px;
display: block;
}
.code-block {
font-family: monospace;
}
.code-text {
color: #69f0ae;
font-size: 12px;
line-height: 1.5;
}
.btn-send {
margin-top: 10px;
border-radius: 25px;
}
.footer-links {
text-align: center;
margin-top: 25px;
}
.link {
color: #007aff;
font-size: 14px;
text-decoration: underline;
}
</style>