Spaces:
Runtime error
Runtime error
File size: 11,112 Bytes
3834ddb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | <!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="交易日期" width="100" align="center">
<template slot-scope="scope">{{ formatDate(scope.row.trade_date) }}</template>
</el-table-column>
<el-table-column label="方向/国家" width="110" 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="220">
<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="200">
<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="金额/重量" width="160" 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="航线信息" width="160">
<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> |