快递查询功能实现
在这一节中,我们将实现一个完整的快递查询功能。主要包括以下内容:
- 设计和实现快递查询页面
- 创建快递信息展示组件
- 集成聚合数据快递 API
- 实现查询历史记录功能
准备工作
-
注册聚合数据账号并获取 API Key:
- 访问 聚合数据官网
- 注册账号并实名认证
- 申请全国快递物流查询 API
- 获取 API Key
-
更新环境变量文件:
JUHE_EXPRESS_KEY=your_api_key_here
快递信息展示组件
首先,我们创建一个可复用的快递信息展示组件:
<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>
快递查询页面
接下来,创建快递查询页面:
<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 路由:
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: '服务器错误,请稍后重试' } }})
使用说明
-
查询快递信息:
- 从下拉列表选择快递公司
- 输入运单号
- 点击查询按钮
-
查询历史功能:
- 自动保存最近 10 条查询记录
- 支持从历史记录中重新查询
- 可以清空历史记录
-
快递信息展示:
- 快递公司名称
- 运单号
- 物流状态和图标
- 详细的物流信息时间线
优化建议
-
数据验证
- 添加运单号格式验证
- 支持扫描运单号二维码
-
用户体验
- 添加运单号自动识别快递公司功能
- 支持订阅物流更新提醒
- 添加常用运单号收藏功能
-
性能优化
- 实现查询结果缓存
- 优化历史记录存储方式
- 添加查询频率限制
下一步
接下来,我们将:
- 实现汇率换算功能
- 设计汇率计算器界面
- 集成汇率查询 API
- 添加货币选择功能