customs-data / apps /api /static /index.html
3v324v23's picture
Deploy to Hugging Face Spaces - SQLite version with 10k sample data
0e8f624
Raw
History Blame Contribute Delete
11.1 kB
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>海关数据查询系统 - MVP</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<style>
body { margin: 0; padding: 20px; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; background-color: #f5f7fa; }
.app-container { max-width: 1400px; margin: 0 auto; }
.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
.header h1 { margin: 0; color: #303133; font-size: 24px; }
.search-card { margin-bottom: 20px; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0,0,0,.1); }
.result-card { border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0,0,0,.1); }
.el-table th { background-color: #f5f7fa; color: #606266; }
.pagination-container { margin-top: 20px; text-align: right; }
.tag-country { font-weight: bold; }
.amount-text { color: #f56c6c; font-weight: bold; }
</style>
</head>
<body>
<div id="app" class="app-container">
<div class="header">
<h1>🌐 海关数据查询系统 - 实时看板</h1>
<el-tag type="success">实时数据抓取运行中</el-tag>
</div>
<!-- 搜索条件区 -->
<el-card class="search-card">
<el-form :inline="true" :model="searchForm" size="small" @submit.native.prevent="handleSearch">
<el-form-item label="国家/地区">
<el-select v-model="searchForm.source_country" placeholder="选择国家" clearable style="width: 120px;">
<el-option label="全部" value=""></el-option>
<el-option label="美国 (US)" value="US"></el-option>
<el-option label="巴西 (BR)" value="BR"></el-option>
<el-option label="印度尼西亚 (ID)" value="ID"></el-option>
<el-option label="泰国 (TH)" value="TH"></el-option>
<el-option label="越南 (VN)" value="VN"></el-option>
<el-option label="马来西亚 (MY)" value="MY"></el-option>
<el-option label="新西兰 (NZ)" value="NZ"></el-option>
<el-option label="阿联酋 (AE)" value="AE"></el-option>
<el-option label="德国 (DE)" value="DE"></el-option>
</el-select>
</el-form-item>
<el-form-item label="进口商">
<el-input v-model="searchForm.importer_name" placeholder="支持模糊搜索" clearable></el-input>
</el-form-item>
<el-form-item label="出口商">
<el-input v-model="searchForm.exporter_name" placeholder="支持模糊搜索" clearable></el-input>
</el-form-item>
<el-form-item label="HS编码">
<el-input v-model="searchForm.hs_code" placeholder="如: 8471" clearable style="width: 120px;"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleSearch" :loading="loading">查询</el-button>
<el-button icon="el-icon-refresh" @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 数据表格区 -->
<el-card class="result-card">
<div slot="header" class="clearfix">
<span>共找到 <b>{{ total }}</b> 条清洗后的标准提单记录</span>
<el-button style="float: right; padding: 3px 0" type="text" icon="el-icon-download">导出当前数据</el-button>
</div>
<el-table :data="tableData" v-loading="loading" style="width: 100%" border size="small" stripe>
<el-table-column prop="trade_date" label="交易日期" min-width="110" align="center">
<template slot-scope="scope">{{ formatDate(scope.row.trade_date) }}</template>
</el-table-column>
<el-table-column label="方向/国家" min-width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.trade_direction === 'import' ? 'primary' : 'warning'" size="mini" effect="dark" style="margin-bottom: 4px;">
{{ scope.row.trade_direction === 'import' ? '进口' : '出口' }}
</el-tag><br>
<span class="tag-country">{{ scope.row.source_country }}</span>
</template>
</el-table-column>
<el-table-column label="交易双方" min-width="150">
<template slot-scope="scope">
<div style="font-size: 12px; color: #909399;">进口商:</div>
<div style="font-weight: bold; margin-bottom: 8px;">{{ scope.row.importer_name || '-' }}</div>
<div style="font-size: 12px; color: #909399;">出口商:</div>
<div style="font-weight: bold;">{{ scope.row.exporter_name || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="商品信息" min-width="180">
<template slot-scope="scope">
<el-tag size="mini" type="info" style="margin-bottom: 4px;">HS: {{ scope.row.hs_code || '-' }}</el-tag>
<div style="font-size: 12px; line-height: 1.4; color: #606266;">
{{ scope.row.product_name || '-' }}
</div>
</template>
</el-table-column>
<el-table-column label="金额/重量" min-width="140" align="right">
<template slot-scope="scope">
<div class="amount-text" v-if="scope.row.amount">
{{ formatNumber(scope.row.amount) }} {{ scope.row.currency }}
</div>
<div v-else>-</div>
<div style="font-size: 12px; color: #909399; margin-top: 4px;" v-if="scope.row.weight">
{{ formatNumber(scope.row.weight) }} {{ scope.row.weight_unit }}
</div>
</template>
</el-table-column>
<el-table-column label="航线信息" min-width="150">
<template slot-scope="scope">
<div style="font-size: 12px;"><i class="el-icon-location-outline"></i> 原产: {{ scope.row.origin_country || '-' }}</div>
<div style="font-size: 12px;"><i class="el-icon-place"></i> 目的: {{ scope.row.destination_country || '-' }}</div>
<div style="font-size: 12px; margin-top: 4px; color: #909399;">
<i class="el-icon-ship"></i> 方式: {{ scope.row.transport_mode || '-' }}
</div>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</el-card>
</div>
<script>
new Vue({
el: '#app',
data: function() {
return {
loading: false,
tableData: [],
total: 0,
searchForm: {
source_country: '',
importer_name: '',
exporter_name: '',
hs_code: '',
page: 1,
limit: 10
}
}
},
mounted() {
this.fetchData();
},
methods: {
async fetchData() {
this.loading = true;
try {
// 清理空参数
const payload = {};
for (const key in this.searchForm) {
if (this.searchForm[key] !== '' && this.searchForm[key] !== null) {
payload[key] = this.searchForm[key];
}
}
const response = await axios.post('/api/v1/trade/search', payload);
this.tableData = response.data.items;
this.total = response.data.total;
} catch (error) {
this.$message.error('获取数据失败,请检查网络或刷新重试');
console.error(error);
} finally {
this.loading = false;
}
},
handleSearch() {
this.searchForm.page = 1;
this.fetchData();
},
resetSearch() {
this.searchForm = {
source_country: '',
importer_name: '',
exporter_name: '',
hs_code: '',
page: 1,
limit: 10
};
this.fetchData();
},
handleSizeChange(val) {
this.searchForm.limit = val;
this.fetchData();
},
handleCurrentChange(val) {
this.searchForm.page = val;
this.fetchData();
},
formatDate(dateString) {
if (!dateString) return '-';
const date = new Date(dateString);
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
},
formatNumber(num) {
if (num === null || num === undefined) return '-';
return Number(num).toLocaleString('en-US');
}
}
})
</script>
</body>
</html>