logo
GitHub

快递查询功能实现

在这一节中,我们将实现一个完整的快递查询功能。主要包括以下内容:

  1. 设计和实现快递查询页面
  2. 创建快递信息展示组件
  3. 集成聚合数据快递 API
  4. 实现查询历史记录功能

准备工作

  1. 注册聚合数据账号并获取 API Key:

    • 访问 聚合数据官网
    • 注册账号并实名认证
    • 申请全国快递物流查询 API
    • 获取 API Key
  2. 更新环境变量文件:

.env
JUHE_EXPRESS_KEY=your_api_key_here

快递信息展示组件

首先,我们创建一个可复用的快递信息展示组件:

components/ExpressCard.vue
<script setup lang="ts">
import { computed } from 'vue'
// 定义组件属性
interface Props {
info: {
company: string
number: string
list: Array<{
datetime: string
remark: string
zone: string
}>
status: string
isComplete: boolean
}
}
const props = defineProps<Props>()
// 根据快递状态选择图标和颜色
const statusInfo = computed(() => {
const status = props.info.status
if (status === '已签收') {
return {
icon: '✅',
color: 'text-success'
}
}
if (status === '运输中') {
return {
icon: '🚚',
color: 'text-info'
}
}
return {
icon: '📦',
color: 'text-warning'
}
})
</script>
<template>
<div class="card w-full bg-base-200 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl flex justify-between">
<span>{{ info.company }}</span>
<span :class="statusInfo.color">
{{ statusInfo.icon }} {{ info.status }}
</span>
</h2>
<p class="text-lg mb-4">运单号:{{ info.number }}</p>
<!-- 物流信息时间线 -->
<ul class="timeline timeline-vertical">
<li v-for="(item, index) in info.list" :key="index" class="timeline-item">
<div class="timeline-middle">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm.75-13a.75.75 0 00-1.5 0v5c0 .414.336.75.75.75h4a.75.75 0 000-1.5h-3.25V5z" clip-rule="evenodd" />
</svg>
</div>
<div class="timeline-start md:text-end mb-10">
<time class="font-mono">{{ item.datetime }}</time>
<div class="text-lg">{{ item.remark }}</div>
<div class="text-sm text-base-content/60">{{ item.zone }}</div>
</div>
<hr/>
</li>
</ul>
</div>
</div>
</template>

快递查询页面

接下来,创建快递查询页面:

pages/express/index.vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'
// 查询表单
const company = ref('')
const number = ref('')
const loading = ref(false)
const error = ref('')
// 快递信息
const expressInfo = ref<any>(null)
// 查询历史
const history = ref<Array<{
company: string
number: string
time: string
}>>([])
// 快递公司列表
const companies = [
{ name: '顺丰速运', code: 'SF' },
{ name: '中通快递', code: 'ZTO' },
{ name: '圆通速递', code: 'YTO' },
{ name: '韵达速递', code: 'YD' },
{ name: '申通快递', code: 'STO' },
{ name: '百世快递', code: 'HTKY' },
{ name: 'EMS', code: 'EMS' },
{ name: '京东物流', code: 'JD' }
]
// 加载查询历史
onMounted(() => {
const savedHistory = localStorage.getItem('express_history')
if (savedHistory) {
history.value = JSON.parse(savedHistory)
}
})
// 保存查询历史
function saveHistory(companyName: string, expressNumber: string) {
const newRecord = {
company: companyName,
number: expressNumber,
time: new Date().toLocaleString('zh-CN')
}
history.value = [newRecord, ...history.value.slice(0, 9)]
localStorage.setItem('express_history', JSON.stringify(history.value))
}
// 查询快递信息
async function queryExpress() {
if (!company.value || !number.value) {
error.value = '请选择快递公司并输入运单号'
return
}
try {
loading.value = true
error.value = ''
expressInfo.value = null
// 调用快递查询 API
const response = await fetch('/api/express/query', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
company: company.value,
number: number.value
})
})
const data = await response.json()
if (data.error) {
error.value = data.error
return
}
expressInfo.value = data
const companyName = companies.find(c => c.code === company.value)?.name || company.value
saveHistory(companyName, number.value)
} catch (e) {
error.value = '查询失败,请稍后重试'
} finally {
loading.value = false
}
}
// 从历史记录中查询
function queryFromHistory(record: typeof history.value[0]) {
company.value = companies.find(c => c.name === record.company)?.code || ''
number.value = record.number
queryExpress()
}
// 清空历史记录
function clearHistory() {
history.value = []
localStorage.removeItem('express_history')
}
</script>
<template>
<div class="max-w-4xl mx-auto">
<h1 class="text-3xl font-bold mb-8">快递查询</h1>
<!-- 查询表单 -->
<div class="form-control w-full max-w-lg mb-8">
<div class="flex gap-4 mb-4">
<select
v-model="company"
class="select select-bordered w-full max-w-xs"
>
<option value="">选择快递公司</option>
<option
v-for="item in companies"
:key="item.code"
:value="item.code"
>
{{ item.name }}
</option>
</select>
<input
v-model="number"
type="text"
placeholder="输入运单号"
class="input input-bordered w-full"
/>
</div>
<button
class="btn btn-primary w-full"
:disabled="loading"
@click="queryExpress"
>
查询
</button>
<!-- 错误提示 -->
<div v-if="error" class="alert alert-error mt-4">
{{ error }}
</div>
</div>
<!-- 查询历史 -->
<div v-if="history.length > 0" class="mb-8">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold">查询历史</h2>
<button
class="btn btn-sm btn-ghost"
@click="clearHistory"
>
清空
</button>
</div>
<div class="overflow-x-auto">
<table class="table table-zebra">
<thead>
<tr>
<th>快递公司</th>
<th>运单号</th>
<th>查询时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="record in history" :key="record.number">
<td>{{ record.company }}</td>
<td>{{ record.number }}</td>
<td>{{ record.time }}</td>
<td>
<button
class="btn btn-sm btn-ghost"
@click="queryFromHistory(record)"
>
重新查询
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 快递信息 -->
<div v-if="expressInfo">
<ExpressCard :info="expressInfo" />
</div>
<!-- 加载状态 -->
<div
v-if="loading"
class="flex justify-center items-center h-40"
>
<span class="loading loading-spinner loading-lg"></span>
</div>
</div>
</template>

API 路由

创建快递查询相关的 API 路由:

server/api/express/query.ts
import { defineEventHandler, readBody } from 'h3'
export default defineEventHandler(async (event) => {
try {
const body = await readBody(event)
const { company, number } = body
if (!company || !number) {
return {
error: '请提供快递公司和运单号'
}
}
// 调用聚合数据快递 API
const apiKey = process.env.JUHE_EXPRESS_KEY
const response = await fetch(
`http://apis.juhe.cn/exp/index?com=${company}&no=${number}&key=${apiKey}`
)
const data = await response.json()
if (data.error_code !== 0) {
return {
error: data.reason || '获取快递信息失败'
}
}
// 格式化返回数据
return {
company: data.result.company,
number: data.result.no,
list: data.result.list,
status: data.result.status,
isComplete: data.result.isComplete
}
} catch (error) {
console.error('Express API Error:', error)
return {
error: '服务器错误,请稍后重试'
}
}
})

使用说明

  1. 查询快递信息:

    • 从下拉列表选择快递公司
    • 输入运单号
    • 点击查询按钮
  2. 查询历史功能:

    • 自动保存最近 10 条查询记录
    • 支持从历史记录中重新查询
    • 可以清空历史记录
  3. 快递信息展示:

    • 快递公司名称
    • 运单号
    • 物流状态和图标
    • 详细的物流信息时间线

优化建议

  1. 数据验证

    • 添加运单号格式验证
    • 支持扫描运单号二维码
  2. 用户体验

    • 添加运单号自动识别快递公司功能
    • 支持订阅物流更新提醒
    • 添加常用运单号收藏功能
  3. 性能优化

    • 实现查询结果缓存
    • 优化历史记录存储方式
    • 添加查询频率限制

下一步

接下来,我们将:

  1. 实现汇率换算功能
  2. 设计汇率计算器界面
  3. 集成汇率查询 API
  4. 添加货币选择功能