修改首页模块
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 64 64">
|
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
||||||
<stop offset="0" stop-color="#0c1426"/>
|
<stop offset="0" stop-color="#0c1426"/>
|
||||||
@@ -13,10 +13,10 @@
|
|||||||
</filter>
|
</filter>
|
||||||
</defs>
|
</defs>
|
||||||
<!-- 背景圆 -->
|
<!-- 背景圆 -->
|
||||||
<circle cx="32" cy="32" r="30" fill="url(#bg)" stroke="#00d4ff" stroke-width="2"/>
|
<circle cx="32" cy="32" r="30" fill="#ffffff" stroke="#00d4ff" stroke-width="3"/>
|
||||||
<!-- 牛字标识 -->
|
<!-- 牛字标识 -->
|
||||||
<g fill="#ffffff" font-family="'Microsoft YaHei','Arial'" font-weight="800" text-anchor="middle" filter="url(#glow)">
|
<g fill="#0C2435" font-family="'Microsoft YaHei','Arial'" font-weight="800" text-anchor="middle" filter="url(#glow)">
|
||||||
<text x="32" y="40" font-size="34">牛</text>
|
<text x="32" y="44" font-size="40">牛</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- 亮边描边 -->
|
<!-- 亮边描边 -->
|
||||||
<circle cx="32" cy="32" r="30" fill="none" stroke="rgba(0,212,255,0.35)" stroke-width="2"/>
|
<circle cx="32" cy="32" r="30" fill="none" stroke="rgba(0,212,255,0.35)" stroke-width="2"/>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 950 B After Width: | Height: | Size: 950 B |
@@ -11,19 +11,18 @@
|
|||||||
<div class="price-table-header">
|
<div class="price-table-header">
|
||||||
<div>序号</div>
|
<div>序号</div>
|
||||||
<div>省份</div>
|
<div>省份</div>
|
||||||
|
<div>地区</div>
|
||||||
<div>品种</div>
|
<div>品种</div>
|
||||||
<div>单价(元/斤)</div>
|
<div>单价</div>
|
||||||
|
<div>时间</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="(row, idx) in nationalPriceTableSortedRows" :key="row.id" class="price-table-row">
|
<div v-for="(row, idx) in nationalPriceTableSortedRows" :key="row.id" class="price-table-row">
|
||||||
<div>{{ idx + 1 }}</div>
|
<div>{{ idx + 1 }}</div>
|
||||||
<div>{{ row.province }}</div>
|
<div>{{ row.province }}</div>
|
||||||
|
<div>{{ row.location }}</div>
|
||||||
<div>{{ row.breed }}</div>
|
<div>{{ row.breed }}</div>
|
||||||
<div class="price-bar-cell">
|
<div class="price-value after-bar">{{ formatPrice(row.price) }}</div>
|
||||||
<div class="price-bar-track">
|
<div>{{ formatDate(row.time) }}</div>
|
||||||
<div class="price-bar" :style="getPriceBarStyle(row.price)"></div>
|
|
||||||
</div>
|
|
||||||
<span class="price-value after-bar">{{ formatPrice(row.price) }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -237,6 +236,7 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
height: 250px; /* 设置固定高度 */
|
height: 250px; /* 设置固定高度 */
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.species-price-table-header,
|
.species-price-table-header,
|
||||||
.species-price-table-row {
|
.species-price-table-row {
|
||||||
@@ -249,8 +249,13 @@
|
|||||||
.species-price-table-header {
|
.species-price-table-header {
|
||||||
color: #84acf0;
|
color: #84acf0;
|
||||||
border-bottom: 1px solid rgba(132, 172, 240, 0.2);
|
border-bottom: 1px solid rgba(132, 172, 240, 0.2);
|
||||||
padding-bottom: 6px;
|
padding: 10px 0;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
|
background: #0C2435;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
|
||||||
}
|
}
|
||||||
.species-price-table-row {
|
.species-price-table-row {
|
||||||
color: #eaf7ff;
|
color: #eaf7ff;
|
||||||
@@ -267,24 +272,47 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.price-table-header,
|
.price-table-header,
|
||||||
.price-table-row {
|
.price-table-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 0.6fr 1fr 1.2fr 2fr;
|
grid-template-columns: 0.6fr 1fr 1.2fr 1.2fr 1.4fr 0.9fr;
|
||||||
gap: 12px;
|
gap: 14px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-table-header > div:nth-child(5),
|
||||||
|
.price-table-row > div:nth-child(5) {
|
||||||
|
padding-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-table-header > div:nth-child(4),
|
||||||
|
.price-table-row > div:nth-child(4) {
|
||||||
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
.price-table-header {
|
.price-table-header {
|
||||||
color: #84acf0;
|
color: #84acf0;
|
||||||
border-bottom: 1px solid rgba(132, 172, 240, 0.2);
|
border-bottom: 1px solid rgba(132, 172, 240, 0.2);
|
||||||
padding-bottom: 6px;
|
padding: 8px 0;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
|
background: #0C2435;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
|
||||||
}
|
}
|
||||||
.price-table-row {
|
.price-table-row {
|
||||||
color: #eaf7ff;
|
color: #eaf7ff;
|
||||||
}
|
}
|
||||||
|
/* 奇偶行颜色区分 */
|
||||||
|
/* .price-table-row:nth-child(odd) {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
.price-table-row:nth-child(even) {
|
||||||
|
background: rgba(0, 212, 255, 0.06);
|
||||||
|
} */
|
||||||
/* 单价列条形图样式 */
|
/* 单价列条形图样式 */
|
||||||
.price-bar-cell {
|
.price-bar-cell {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -309,13 +337,13 @@
|
|||||||
}
|
}
|
||||||
.price-value {
|
.price-value {
|
||||||
min-width: 86px;
|
min-width: 86px;
|
||||||
text-align: right;
|
text-align: left;
|
||||||
color: #eaf7ff;
|
color: #eaf7ff;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
.price-value.after-bar {
|
.price-value.after-bar {
|
||||||
min-width: 86px;
|
min-width: 86px;
|
||||||
text-align: right;
|
text-align: left;
|
||||||
color: #eaf7ff;
|
color: #eaf7ff;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
@@ -386,6 +414,15 @@
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
.price-ranking-panel .panel-header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.price-ranking-panel .price-table {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
.price-ranking-panel .species-price-table {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.panel-header h3 {
|
.panel-header h3 {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
@@ -478,7 +515,7 @@ export default {
|
|||||||
'新疆维吾尔自治区': '新疆',
|
'新疆维吾尔自治区': '新疆',
|
||||||
'西藏自治区': '西藏',
|
'西藏自治区': '西藏',
|
||||||
'宁夏回族自治区': '宁夏',
|
'宁夏回族自治区': '宁夏',
|
||||||
'广西自治区': '广西',
|
'广西壮族自治区': '广西',
|
||||||
'河北省': '河北',
|
'河北省': '河北',
|
||||||
'山东省': '山东',
|
'山东省': '山东',
|
||||||
'黑龙江省': '黑龙江',
|
'黑龙江省': '黑龙江',
|
||||||
@@ -776,16 +813,16 @@ export default {
|
|||||||
|
|
||||||
// 全国牛单价排行榜表格数据
|
// 全国牛单价排行榜表格数据
|
||||||
nationalPriceTableRows: [
|
nationalPriceTableRows: [
|
||||||
{ id: 1, province: '河北省', breed: '安格斯牛', price: 14200 },
|
{ id: 1, province: '河北省', location: '石家庄市', breed: '安格斯牛', price: 14200, time: '2025-12-03' },
|
||||||
{ id: 2, province: '山东省', breed: '荷斯坦牛', price: 17000 },
|
{ id: 2, province: '山东省', location: '济南市', breed: '荷斯坦牛', price: 17000, time: '2025-12-03' },
|
||||||
{ id: 3, province: '江苏省', breed: '复洲黄牛', price: 14500 },
|
{ id: 3, province: '江苏省', location: '南京市', breed: '复洲黄牛', price: 14500, time: '2025-12-03' },
|
||||||
{ id: 4, province: '浙江省', breed: '西门塔尔牛', price: 16800 },
|
{ id: 4, province: '浙江省', location: '杭州市', breed: '西门塔尔牛', price: 16800, time: '2025-12-03' },
|
||||||
{ id: 5, province: '新疆维吾尔自治区', breed: '夏洛莱牛', price: 16500 },
|
{ id: 5, province: '新疆维吾尔自治区', location: '乌鲁木齐市', breed: '夏洛莱牛', price: 16500, time: '2025-12-03' },
|
||||||
{ id: 6, province: '甘肃省', breed: '水牛', price: 17387 },
|
{ id: 6, province: '甘肃省', location: '兰州市', breed: '水牛', price: 17387, time: '2025-12-03' },
|
||||||
{ id: 7, province: '广东省', breed: '安格斯牛', price: 14200 },
|
{ id: 7, province: '广东省', location: '广州市', breed: '安格斯牛', price: 14200, time: '2025-12-03' },
|
||||||
{ id: 8, province: '广西壮族自治区', breed: '荷斯坦牛', price: 17000 },
|
{ id: 8, province: '广西壮族自治区', location: '南宁市', breed: '荷斯坦牛', price: 17000, time: '2025-12-03' },
|
||||||
{ id: 9, province: '湖南省', breed: '复洲黄牛', price: 14500 },
|
{ id: 9, province: '湖南省', location: '长沙市', breed: '复洲黄牛', price: 14500, time: '2025-12-03' },
|
||||||
{ id: 10, province: '河南省', breed: '西门塔尔牛', price: 16800 }
|
{ id: 10, province: '河南省', location: '郑州市', breed: '西门塔尔牛', price: 16800, time: '2025-12-03' }
|
||||||
],
|
],
|
||||||
|
|
||||||
// 全国省份平均单价数据源(专用于右侧省份排行图表)
|
// 全国省份平均单价数据源(专用于右侧省份排行图表)
|
||||||
@@ -846,10 +883,11 @@ export default {
|
|||||||
],
|
],
|
||||||
|
|
||||||
// 耳标统计数据
|
// 耳标统计数据
|
||||||
earTagStats: {
|
earTagStats: {
|
||||||
completed: 45678,
|
completed: 45678,
|
||||||
planned: 52000
|
planned: 52000
|
||||||
},
|
},
|
||||||
|
_provinceDailyTimer: null,
|
||||||
|
|
||||||
// 耳标佩戴统计堆叠柱状图配置
|
// 耳标佩戴统计堆叠柱状图配置
|
||||||
earTagChartOption: {
|
earTagChartOption: {
|
||||||
@@ -1079,9 +1117,9 @@ export default {
|
|||||||
nameTextStyle: { color: '#00ffff', fontSize: 11 },
|
nameTextStyle: { color: '#00ffff', fontSize: 11 },
|
||||||
axisLine: { lineStyle: { color: '#00ffff' } },
|
axisLine: { lineStyle: { color: '#00ffff' } },
|
||||||
axisLabel: { color: '#ffffff', fontSize: 10 },
|
axisLabel: { color: '#ffffff', fontSize: 10 },
|
||||||
min: 12,
|
min: 5,
|
||||||
max: 15,
|
max: 25,
|
||||||
interval: 1,
|
interval: 5,
|
||||||
splitLine: { lineStyle: { color: 'rgba(0,255,255,0.2)' } }
|
splitLine: { lineStyle: { color: 'rgba(0,255,255,0.2)' } }
|
||||||
},
|
},
|
||||||
series: [{
|
series: [{
|
||||||
@@ -1147,6 +1185,19 @@ export default {
|
|||||||
return String(value || 0)
|
return String(value || 0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
formatDate(value) {
|
||||||
|
if (!value) {
|
||||||
|
const t = new Date()
|
||||||
|
const m = String(t.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(t.getDate()).padStart(2, '0')
|
||||||
|
return `${m}-${day}`
|
||||||
|
}
|
||||||
|
const d = new Date(value)
|
||||||
|
if (Number.isNaN(d.getTime())) return String(value)
|
||||||
|
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(d.getDate()).padStart(2, '0')
|
||||||
|
return `${m}-${day}`
|
||||||
|
},
|
||||||
// 计算条形宽度样式
|
// 计算条形宽度样式
|
||||||
getPriceBarStyle(price) {
|
getPriceBarStyle(price) {
|
||||||
const max = this.priceBarMax || 1
|
const max = this.priceBarMax || 1
|
||||||
@@ -1165,8 +1216,10 @@ export default {
|
|||||||
const rows = list.map((item, idx) => ({
|
const rows = list.map((item, idx) => ({
|
||||||
id: item.id ?? idx + 1,
|
id: item.id ?? idx + 1,
|
||||||
province: item.province ?? item.provinceName ?? '',
|
province: item.province ?? item.provinceName ?? '',
|
||||||
|
location: item.location ?? '',
|
||||||
breed: item.type ?? item.breed ?? '',
|
breed: item.type ?? item.breed ?? '',
|
||||||
price: Number(item.price)
|
price: Number(item.price),
|
||||||
|
time: item.time ?? item.priceDate ?? item.date ?? ''
|
||||||
})).filter(r => r.province && r.breed && Number.isFinite(r.price))
|
})).filter(r => r.province && r.breed && Number.isFinite(r.price))
|
||||||
// 更新数据源(驱动左侧表格与右侧省份排行图表)
|
// 更新数据源(驱动左侧表格与右侧省份排行图表)
|
||||||
this.nationalPriceTableRows = rows
|
this.nationalPriceTableRows = rows
|
||||||
@@ -1197,19 +1250,29 @@ export default {
|
|||||||
|
|
||||||
// 拉取全国省份平均单价排行榜数据(字段:province/provincePrice)
|
// 拉取全国省份平均单价排行榜数据(字段:province/provincePrice)
|
||||||
async fetchNationalProvinceAverages() {
|
async fetchNationalProvinceAverages() {
|
||||||
const url = '/api/cattle-data/provinces'
|
const fmt = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
|
||||||
try {
|
const base = new Date()
|
||||||
const res = await fetch(url)
|
let tryDate = fmt(base)
|
||||||
const raw = await res.json()
|
for (let i = 0; i < 7; i++) {
|
||||||
const list = Array.isArray(raw) ? raw : (Array.isArray(raw?.data) ? raw.data : [])
|
const url = `/api/cattle-data/province-daily-prices?priceDate=${encodeURIComponent(tryDate)}`
|
||||||
const rows = list.map((item, idx) => ({
|
|
||||||
id: item.id ?? idx + 1,
|
try {
|
||||||
province: item.province ?? item.provinceName ?? '',
|
const res = await fetch(url)
|
||||||
price: Number(item.provincePrice)
|
const raw = await res.json()
|
||||||
})).filter(r => r.province && Number.isFinite(r.price))
|
const list = Array.isArray(raw) ? raw : (Array.isArray(raw?.data) ? raw.data : [])
|
||||||
this.nationalProvinceAverageRows = rows
|
const rows = list.map((item, idx) => ({
|
||||||
} catch (e) {
|
id: item.id ?? idx + 1,
|
||||||
console.warn('获取全国省份平均单价失败:', e)
|
province: item.province ?? item.provinceName ?? '',
|
||||||
|
price: Number(item.price ?? item.provincePrice)
|
||||||
|
})).filter(r => r.province && Number.isFinite(r.price))
|
||||||
|
if (rows.length > 0) {
|
||||||
|
this.nationalProvinceAverageRows = rows
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
const d = new Date(tryDate)
|
||||||
|
d.setDate(d.getDate() - 1)
|
||||||
|
tryDate = fmt(d)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1563,6 +1626,25 @@ export default {
|
|||||||
this.fetchNationalPriceRanking()
|
this.fetchNationalPriceRanking()
|
||||||
// 拉取全国省份平均单价排行榜数据
|
// 拉取全国省份平均单价排行榜数据
|
||||||
this.fetchNationalProvinceAverages()
|
this.fetchNationalProvinceAverages()
|
||||||
|
if (this._provinceDailyTimer) clearTimeout(this._provinceDailyTimer)
|
||||||
|
const schedule = () => {
|
||||||
|
const now = new Date()
|
||||||
|
const next = new Date(now)
|
||||||
|
next.setHours(12, 0, 0, 0)
|
||||||
|
if (now.getTime() >= next.getTime()) next.setDate(next.getDate() + 1)
|
||||||
|
const delay = next.getTime() - now.getTime()
|
||||||
|
this._provinceDailyTimer = setTimeout(async () => {
|
||||||
|
await this.fetchNationalProvinceAverages()
|
||||||
|
schedule()
|
||||||
|
}, Math.max(1000, delay))
|
||||||
|
}
|
||||||
|
schedule()
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
if (this._provinceDailyTimer) {
|
||||||
|
clearTimeout(this._provinceDailyTimer)
|
||||||
|
this._provinceDailyTimer = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -65,6 +65,29 @@ export default {
|
|||||||
{ province: '四川', count: 811 },
|
{ province: '四川', count: 811 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const fetchCattleSourceTop3 = async () => {
|
||||||
|
try {
|
||||||
|
const url = '/api/cattle-data/provinces'
|
||||||
|
const res = await fetch(url)
|
||||||
|
const raw = await res.json()
|
||||||
|
const list = Array.isArray(raw) ? raw : (Array.isArray(raw?.data) ? raw.data : [])
|
||||||
|
const rows = list.map((item) => {
|
||||||
|
const province = item.province ?? item.provinceName ?? ''
|
||||||
|
// 接口字段单位为“万头”,无需再除以10000
|
||||||
|
const invRaw = item.inventory25th ?? item.inventory_25 ?? item.inventory2025 ?? item.inventory
|
||||||
|
const inv = Number(invRaw)
|
||||||
|
return { province, inv }
|
||||||
|
}).filter(x => x.province && Number.isFinite(x.inv) && x.inv > 0)
|
||||||
|
if (rows.length === 0) return
|
||||||
|
rows.sort((a, b) => b.inv - a.inv)
|
||||||
|
const top3 = rows.slice(0, 3).map(x => ({
|
||||||
|
province: x.province,
|
||||||
|
count: Math.round(x.inv)
|
||||||
|
}))
|
||||||
|
cattleSourceTop5.value = top3
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
const resize = () => {
|
const resize = () => {
|
||||||
baseEarth.resize();
|
baseEarth.resize();
|
||||||
@@ -713,6 +736,7 @@ export default {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
fetchCattleSourceTop3()
|
||||||
console.log('=== Map3D组件已挂载 ===');
|
console.log('=== Map3D组件已挂载 ===');
|
||||||
// console.log('farmData:', farmData); // 移除farmData引用
|
// console.log('farmData:', farmData); // 移除farmData引用
|
||||||
// 等待DOM完全渲染
|
// 等待DOM完全渲染
|
||||||
|
|||||||
@@ -544,7 +544,7 @@ baseForOwn(LazyWrapper.prototype, function(func, methodName) {
|
|||||||
var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
|
var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
|
||||||
isTaker = /^(?:head|last)$/.test(methodName),
|
isTaker = /^(?:head|last)$/.test(methodName),
|
||||||
lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
|
lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
|
||||||
retUnwrapped = isTaker || /^find/.test(methodName);
|
retUnwrapped = isTaker || methodName.startsWith('find');
|
||||||
|
|
||||||
if (!lodashFunc) {
|
if (!lodashFunc) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user