Spaces:
Running
Running
| <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> |