You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
livestock-trading/src/views/Dashboard.vue

472 lines
11 KiB

<template>
<div class="dashboard-container">
<img
class="dashboard-bg"
src="/images/世界地图背景.png"
alt=""
/>
<!-- 标题区域 -->
<HeaderBar
:title="systemConfig.title"
:title-background="systemConfig.titleBackground"
/>
<div class="content-area">
<!-- 主要内容区域 -->
<div v-if="isLayoutReady" class="main-content">
<!-- 左侧:背景 + 装饰 + 统计模块 -->
<div class="side-panel side-panel-left">
<img class="side-panel-bg" src="/images/左边bg.png" alt="" />
<img class="side-decor side-decor-left" src="/images/左部.png" alt="" />
<div class="side-panel-body">
<div class="left-section">
<div class="left-column">
<div class="left-top">
<RealTimeStats />
</div>
<div class="left-bottom">
<YakTradingData />
</div>
</div>
<div class="left-center-column">
<div class="left-center-top">
<ComprehensiveSalesStats />
</div>
<div class="left-center-bottom">
<YakPriceTrend />
</div>
</div>
</div>
</div>
</div>
<!-- 中央地图 -->
<div class="center-section">
<div class="map-container">
<ChinaMap />
</div>
</div>
<!-- 右侧:背景 + 统计模块 + 装饰 -->
<div class="side-panel side-panel-right">
<img class="side-panel-bg" src="/images/右边bg.png" alt="" />
<div class="side-panel-body">
<div class="right-section">
<div v-if="isSupplyExpanded" class="expanded-supply-container">
<SupplyDemandData
:force-expanded="true"
:initial-detail-id="supplyDetailId"
@expand-change="handleSupplyExpand"
@initial-detail-applied="clearSupplyDetailId"
/>
</div>
<template v-else>
<div class="right-center-column">
<div class="right-center-top">
<ExchangeMonitor />
</div>
<div class="right-center-middle">
<YakSalesTypeStats />
</div>
<div class="right-center-bottom">
<PurchaserAnalysis />
</div>
</div>
<div class="right-column">
<div class="right-bottom">
<SupplyDemandData :force-expanded="false" @expand-change="handleSupplyExpand" />
</div>
<div class="right-middle">
<MarketRealtimeMonitor />
</div>
<div class="right-top">
<MarketEnvironmentMonitor />
</div>
</div>
</template>
</div>
</div>
<img class="side-decor side-decor-right" src="/images/右部.png" alt="" />
</div>
</div>
<!-- 加载状态 -->
<div v-else class="loading-container">
<div class="loading-content">
<div class="loading-spinner"></div>
<div class="loading-text">正在初始化布局...</div>
</div>
</div>
</div>
<ScreenDecorations />
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue'
import { configManager } from '../utils/config.js'
import { loadSystemConfig } from '../utils/systemConfig.js'
import { dataManager } from '../utils/dataManager.js'
import { dataUpdater } from '../utils/dataUpdater.js'
// 导入组件
import RealTimeStats from '../components/RealTimeStats.vue'
import YakTradingData from '../components/YakTradingData.vue'
import ComprehensiveSalesStats from '../components/ComprehensiveSalesStats.vue'
import YakPriceTrend from '../components/YakPriceTrend.vue'
import ChinaMap from '../components/ChinaMap.vue'
import ScrollingAnnouncement from '../components/ScrollingAnnouncement.vue'
import TransactionDetails from '../components/TransactionDetails.vue'
import ExchangeMonitor from '../components/ExchangeMonitor.vue'
import YakSalesTypeStats from '../components/YakSalesTypeStats.vue'
import PurchaserAnalysis from '../components/PurchaserAnalysis.vue'
import MarketEnvironmentMonitor from '../components/MarketEnvironmentMonitor.vue'
import MarketRealtimeMonitor from '../components/MarketRealtimeMonitor.vue'
import SupplyDemandData from '../components/SupplyDemandData.vue'
import HeaderBar from '../components/HeaderBar.vue'
import ScreenDecorations from '../components/ScreenDecorations.vue'
// 响应式数据
const config = ref(null)
const systemConfig = ref({
title: '/标题.png',
titleBackground: '/images/标题背景.png'
})
const isLayoutReady = ref(false) // 控制页面渲染时机
const isSupplyExpanded = ref(false) // 控制供应信息组件是否展开
const supplyDetailId = ref('')
// 处理供应信息组件展开/收缩
const handleSupplyExpand = (payload) => {
if (typeof payload === 'boolean') {
isSupplyExpanded.value = payload
if (!payload) {
supplyDetailId.value = ''
}
return
}
isSupplyExpanded.value = !!payload.expanded
if (payload.detailId) {
supplyDetailId.value = payload.detailId
}
}
const clearSupplyDetailId = () => {
supplyDetailId.value = ''
}
// 初始化配置
onMounted(async () => {
try {
// 加载系统配置(标题栏)
systemConfig.value = await loadSystemConfig()
// 加载配置
console.log('Dashboard: 开始加载配置')
config.value = await configManager.loadConfig()
console.log('Dashboard: 配置加载完成', config.value)
// 设置CSS变量
console.log('Dashboard: 设置CSS变量')
configManager.setCSSVariables()
// 等待CSS变量生效和DOM更新
await nextTick()
// 再等待一帧确保样式完全应用
await new Promise(resolve => requestAnimationFrame(resolve))
console.log('Dashboard: CSS变量已生效')
// 加载数据配置
console.log('Dashboard: 开始加载数据配置')
const loadedData = await dataManager.loadData()
console.log('Dashboard: 数据配置加载完成', loadedData)
// 标记布局准备完成,开始渲染组件
console.log('Dashboard: 布局准备完成,开始渲染组件')
isLayoutReady.value = true
console.log('Dashboard 完全初始化完成')
} catch (error) {
console.error('Failed to initialize dashboard:', error)
systemConfig.value = await loadSystemConfig()
// 使用默认配置
config.value = configManager.getDefaultConfig()
configManager.setCSSVariables()
// 等待DOM更新后显示页面
await nextTick()
await new Promise(resolve => requestAnimationFrame(resolve))
isLayoutReady.value = true
}
})
</script>
<style scoped>
/* Dashboard专用样式 - 4:1大屏布局 */
.dashboard-container {
width: var(--screen-width, 5120px);
height: var(--screen-height, 1440px);
min-width: var(--screen-width, 5120px);
min-height: var(--screen-height, 1440px);
--decor-offset-x: 16px;
display: flex;
flex-direction: column;
padding: 20px;
background: #0a0e1a;
overflow: hidden;
box-sizing: border-box;
gap: 20px;
position: relative;
}
.dashboard-bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: fill;
pointer-events: none;
user-select: none;
z-index: 0;
}
.dashboard-container > :not(.dashboard-bg) {
position: relative;
z-index: 1;
}
/* 响应式适配 - 针对4:1宽高比优化 */
@media (max-width: 1919px) and (min-width: 1600px) {
.dashboard-container {
padding: 6px;
gap: 6px;
}
}
@media (max-width: 1599px) {
.dashboard-container {
padding: 5px;
gap: 5px;
}
}
/* 正文区域 */
.content-area {
flex: 1;
display: flex;
flex-direction: column;
padding: 0 var(--decor-offset-x);
min-height: 0;
position: relative;
z-index: 2;
box-sizing: border-box;
}
/* 主要内容区域 */
.main-content {
flex: 1;
display: flex;
gap: 16px;
min-height: 0;
min-width: 0;
padding-bottom: 20px;
position: relative;
box-sizing: border-box;
}
/* 左右侧背景面板(含装饰 + 统计模块) */
.side-panel {
flex: 1;
position: relative;
display: flex;
align-items: stretch;
min-width: 0;
min-height: 0;
overflow: hidden;
}
.side-panel-bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: fill;
pointer-events: none;
user-select: none;
z-index: 0;
}
.side-panel-body {
flex: 1;
position: relative;
z-index: 1;
display: flex;
min-width: 0;
min-height: 0;
padding: 12px;
box-sizing: border-box;
}
.side-panel-left {
gap: var(--decor-offset-x);
padding-left: var(--decor-offset-x);
}
.side-panel-right {
gap: var(--decor-offset-x);
padding-right: var(--decor-offset-x);
}
.side-decor {
position: relative;
z-index: 2;
height: 100%;
width: auto;
flex-shrink: 0;
display: block;
object-fit: fill;
pointer-events: none;
user-select: none;
}
/* 左侧区域布局 */
.left-section {
flex: 1;
display: flex;
gap: 12px;
min-width: 0;
min-height: 0;
}
.left-column {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
min-width: 0;
}
.left-center-column {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
min-width: 0;
}
.left-top,
.left-bottom,
.left-center-top,
.left-center-bottom {
flex: 1;
min-height: 0;
}
/* 中央区域布局 */
.center-section {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
background: transparent;
}
.map-container {
position: relative;
flex: 1;
min-height: 0;
background: transparent;
}
.floating-table {
position: absolute;
bottom: 20px;
left: 20px;
right: 20px;
height: 200px;
background: rgba(26, 31, 46, 0.95);
border: 1px solid rgba(64, 158, 255, 0.4);
border-radius: 8px;
backdrop-filter: blur(10px);
z-index: 100;
}
/* 右侧区域布局 */
.right-section {
flex: 1;
display: flex;
gap: 12px;
min-width: 0;
min-height: 0;
}
.right-center-column {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
min-width: 0;
}
.right-column {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
min-width: 0;
}
.right-center-top,
.right-center-middle,
.right-center-bottom,
.right-top,
.right-middle,
.right-bottom {
flex: 1;
min-height: 0;
}
/* 展开供应信息的容器 */
.expanded-supply-container {
flex: 1;
min-height: 0;
}
/* 加载状态 */
.loading-container {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
min-height: 0;
min-width: 0;
}
.loading-content {
text-align: center;
color: #a0a8b8;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(64, 158, 255, 0.3);
border-top: 3px solid #409EFF;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 16px;
}
.loading-text {
font-size: 14px;
color: #a0a8b8;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>