main
parent
5e40a11ce1
commit
dc5658513c
@ -0,0 +1,12 @@ |
|||||||
|
<template> |
||||||
|
<router-view /> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup> |
||||||
|
// 路由入口组件,用于管理不同页面的切换 |
||||||
|
</script> |
||||||
|
|
||||||
|
<style> |
||||||
|
/* 路由入口样式重置 */ |
||||||
|
/* 注意:不要在这里设置overflow,让各页面自己控制 */ |
||||||
|
</style> |
||||||
@ -1,5 +1,6 @@ |
|||||||
import { createApp } from 'vue' |
import { createApp } from 'vue' |
||||||
import App from './App.vue' |
import RouterApp from './RouterApp.vue' |
||||||
|
import router from './router' |
||||||
import './styles/index.scss' |
import './styles/index.scss' |
||||||
|
|
||||||
createApp(App).mount('#app')
|
createApp(RouterApp).use(router).mount('#app')
|
||||||
@ -0,0 +1,23 @@ |
|||||||
|
import { createRouter, createWebHistory } from 'vue-router' |
||||||
|
import Dashboard from '../views/Dashboard.vue' |
||||||
|
import TV86Display from '../views/TV86Display.vue' |
||||||
|
|
||||||
|
const routes = [ |
||||||
|
{ |
||||||
|
path: '/', |
||||||
|
name: 'Dashboard', |
||||||
|
component: Dashboard |
||||||
|
}, |
||||||
|
{ |
||||||
|
path: '/tv86', |
||||||
|
name: 'TV86Display', |
||||||
|
component: TV86Display |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
const router = createRouter({ |
||||||
|
history: createWebHistory(), |
||||||
|
routes |
||||||
|
}) |
||||||
|
|
||||||
|
export default router
|
||||||
@ -0,0 +1,469 @@ |
|||||||
|
<template> |
||||||
|
<div class="dashboard-container"> |
||||||
|
<!-- 标题区域 --> |
||||||
|
<div class="header-section"> |
||||||
|
<div class="header-info left"> |
||||||
|
<!-- <div class="info-item"> |
||||||
|
<span class="status-indicator" :class="isLayoutReady ? 'online' : 'loading'"></span> |
||||||
|
<span>{{ isLayoutReady ? '系统运行正常' : '系统初始化中' }}</span> |
||||||
|
</div> --> |
||||||
|
</div> |
||||||
|
<h1 class="main-title">{{ config?.screen?.title || '智慧活畜交易大数据中心' }}</h1> |
||||||
|
<div class="header-info right"> |
||||||
|
<div class="info-item"> |
||||||
|
<span class="current-time">{{ currentTime || '--:--:--' }}</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- 主要内容区域 --> |
||||||
|
<div v-if="isLayoutReady" class="main-content"> |
||||||
|
<!-- 左侧区域 - 分为左和左中两列 --> |
||||||
|
<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 class="center-section"> |
||||||
|
<div class="map-container"> |
||||||
|
<ChinaMap /> |
||||||
|
<!-- <ScrollingAnnouncement /> --> |
||||||
|
<!-- <div class="floating-table"> |
||||||
|
<TransactionDetails /> |
||||||
|
</div> --> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- 右侧区域 - 分为右中和右两列 --> |
||||||
|
<div class="right-section"> |
||||||
|
<!-- 当供应信息展开时,整个右侧区域显示供应信息 --> |
||||||
|
<div v-if="isSupplyExpanded" class="expanded-supply-container"> |
||||||
|
<SupplyDemandData :force-expanded="true" @expand-change="handleSupplyExpand" /> |
||||||
|
</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> |
||||||
|
|
||||||
|
<!-- 加载状态 --> |
||||||
|
<div v-else class="loading-container"> |
||||||
|
<div class="loading-content"> |
||||||
|
<div class="loading-spinner"></div> |
||||||
|
<div class="loading-text">正在初始化布局...</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup> |
||||||
|
import { ref, onMounted, onUnmounted, nextTick } from 'vue' |
||||||
|
import { configManager } from '../utils/config.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' |
||||||
|
|
||||||
|
// 响应式数据 |
||||||
|
const config = ref(null) |
||||||
|
const currentTime = ref('') |
||||||
|
const isLayoutReady = ref(false) // 控制页面渲染时机 |
||||||
|
const isSupplyExpanded = ref(false) // 控制供应信息组件是否展开 |
||||||
|
|
||||||
|
// 更新时间 |
||||||
|
const updateTime = () => { |
||||||
|
const now = new Date() |
||||||
|
currentTime.value = now.toLocaleString('zh-CN', { |
||||||
|
year: 'numeric', |
||||||
|
month: '2-digit', |
||||||
|
day: '2-digit', |
||||||
|
hour: '2-digit', |
||||||
|
minute: '2-digit', |
||||||
|
second: '2-digit' |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// 处理供应信息组件展开/收缩 |
||||||
|
const handleSupplyExpand = (expanded) => { |
||||||
|
isSupplyExpanded.value = expanded |
||||||
|
} |
||||||
|
|
||||||
|
// 初始化配置和时间 |
||||||
|
let timeTimer = null |
||||||
|
|
||||||
|
onMounted(async () => { |
||||||
|
// 立即开始更新时间 |
||||||
|
updateTime() |
||||||
|
timeTimer = setInterval(updateTime, 1000) |
||||||
|
|
||||||
|
try { |
||||||
|
// 加载配置 |
||||||
|
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) |
||||||
|
// 使用默认配置 |
||||||
|
config.value = configManager.getDefaultConfig() |
||||||
|
configManager.setCSSVariables() |
||||||
|
|
||||||
|
// 等待DOM更新后显示页面 |
||||||
|
await nextTick() |
||||||
|
await new Promise(resolve => requestAnimationFrame(resolve)) |
||||||
|
isLayoutReady.value = true |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
onUnmounted(() => { |
||||||
|
if (timeTimer) { |
||||||
|
clearInterval(timeTimer) |
||||||
|
} |
||||||
|
}) |
||||||
|
</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); |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
padding: 20px; |
||||||
|
background: |
||||||
|
radial-gradient(ellipse at 30% 40%, rgba(64, 158, 255, 0.06) 0%, transparent 60%), |
||||||
|
radial-gradient(ellipse at 70% 60%, rgba(139, 92, 246, 0.05) 0%, transparent 60%), |
||||||
|
radial-gradient(ellipse at 50% 80%, rgba(0, 212, 170, 0.03) 0%, transparent 50%), |
||||||
|
linear-gradient(135deg, #0a0e1a 0%, #0f1419 25%, #131825 50%, #0f1419 75%, #0a0e1a 100%); |
||||||
|
overflow: hidden; |
||||||
|
box-sizing: border-box; |
||||||
|
gap: 20px; |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
|
||||||
|
.dashboard-container::before { |
||||||
|
content: ''; |
||||||
|
position: absolute; |
||||||
|
top: 0; |
||||||
|
left: 0; |
||||||
|
right: 0; |
||||||
|
bottom: 0; |
||||||
|
background: |
||||||
|
linear-gradient(90deg, transparent 0%, rgba(64, 158, 255, 0.03) 50%, transparent 100%), |
||||||
|
linear-gradient(0deg, transparent 0%, rgba(139, 92, 246, 0.03) 50%, transparent 100%), |
||||||
|
radial-gradient(circle at 25% 25%, rgba(64, 158, 255, 0.05) 0%, transparent 50%), |
||||||
|
radial-gradient(circle at 75% 75%, rgba(139, 92, 246, 0.05) 0%, transparent 50%); |
||||||
|
pointer-events: none; |
||||||
|
z-index: 1; |
||||||
|
animation: backgroundShift 15s ease-in-out infinite; |
||||||
|
} |
||||||
|
|
||||||
|
@keyframes backgroundShift { |
||||||
|
0%, 100% { |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
50% { |
||||||
|
opacity: 0.7; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* 响应式适配 - 针对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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* 标题区域 */ |
||||||
|
.header-section { |
||||||
|
height: 84px; |
||||||
|
display: flex; |
||||||
|
justify-content: space-between; |
||||||
|
align-items: center; |
||||||
|
padding: 0 40px; |
||||||
|
background: url('../images/大标题背景.png') center/contain no-repeat; |
||||||
|
background-size: auto 84px; |
||||||
|
border-radius: 12px; |
||||||
|
backdrop-filter: blur(10px); |
||||||
|
position: relative; |
||||||
|
z-index: 10; |
||||||
|
flex-shrink: 0; |
||||||
|
background-size: 100% 84px; |
||||||
|
} |
||||||
|
|
||||||
|
.main-title { |
||||||
|
font-size: 36px; |
||||||
|
font-weight: 700; |
||||||
|
background: linear-gradient(180deg, #fff 50%, #44c1ff); |
||||||
|
-webkit-background-clip: text; |
||||||
|
-webkit-text-fill-color: transparent; |
||||||
|
background-clip: text; |
||||||
|
text-align: center; |
||||||
|
letter-spacing: 3px; |
||||||
|
text-shadow: 0 0 20px rgba(64, 158, 255, 0.5); |
||||||
|
flex: 2; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
margin-bottom: 15px; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.header-info { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
gap: 24px; |
||||||
|
flex: 1; |
||||||
|
|
||||||
|
&.left { |
||||||
|
justify-content: flex-start; |
||||||
|
} |
||||||
|
|
||||||
|
&.right { |
||||||
|
justify-content: flex-end; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.info-item { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
gap: 8px; |
||||||
|
font-size: 14px; |
||||||
|
color: #a0a8b8; |
||||||
|
} |
||||||
|
|
||||||
|
.current-time { |
||||||
|
font-family: 'Courier New', monospace; |
||||||
|
font-weight: bold; |
||||||
|
color: #409EFF; |
||||||
|
} |
||||||
|
|
||||||
|
/* 主要内容区域 */ |
||||||
|
.main-content { |
||||||
|
flex: 1; |
||||||
|
display: flex; |
||||||
|
gap: 16px; |
||||||
|
min-height: 0; |
||||||
|
padding-bottom: 20px; |
||||||
|
} |
||||||
|
|
||||||
|
/* 左侧区域布局 */ |
||||||
|
.left-section { |
||||||
|
flex: 1; |
||||||
|
display: flex; |
||||||
|
gap: 12px; |
||||||
|
min-width: 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; |
||||||
|
} |
||||||
|
|
||||||
|
.map-container { |
||||||
|
position: relative; |
||||||
|
flex: 1; |
||||||
|
min-height: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.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; |
||||||
|
} |
||||||
|
|
||||||
|
.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; |
||||||
|
} |
||||||
|
|
||||||
|
.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> |
||||||
@ -0,0 +1,287 @@ |
|||||||
|
<template> |
||||||
|
<div class="tv86-viewport"> |
||||||
|
<div class="tv86-container" ref="containerRef"> |
||||||
|
<!-- 顶部时间日期天气区域 --> |
||||||
|
<div class="header-datetime"> |
||||||
|
<div class="datetime-info"> |
||||||
|
<span class="date">{{ currentDate }}</span> |
||||||
|
<span class="weekday">{{ currentWeekday }}</span> |
||||||
|
<span class="lunar">{{ lunarDate }}</span> |
||||||
|
<span class="weather">{{ weather }}</span> |
||||||
|
<span class="temperature">{{ temperature }}</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- 主内容区域 --> |
||||||
|
<div class="main-content"> |
||||||
|
<!-- 左侧区域 --> |
||||||
|
<div class="left-section"> |
||||||
|
<!-- 左上:牦牛实时交易概况 --> |
||||||
|
<div class="left-top"> |
||||||
|
<YakTradingOverview /> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- 左下:活牛、鲜肉价格趋势 --> |
||||||
|
<div class="left-bottom"> |
||||||
|
<PriceTrendChart /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- 右侧:市场公告 --> |
||||||
|
<div class="right-section"> |
||||||
|
<MarketAnnouncements /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup> |
||||||
|
import { ref, onMounted, onUnmounted } from 'vue' |
||||||
|
import YakTradingOverview from '../components/TV86/YakTradingOverview.vue' |
||||||
|
import PriceTrendChart from '../components/TV86/PriceTrendChart.vue' |
||||||
|
import MarketAnnouncements from '../components/TV86/MarketAnnouncements.vue' |
||||||
|
import { Lunar, Solar } from 'lunar-javascript' |
||||||
|
|
||||||
|
// 基准分辨率 |
||||||
|
const BASE_WIDTH = 3840 |
||||||
|
const BASE_HEIGHT = 2160 |
||||||
|
|
||||||
|
// 响应式数据 |
||||||
|
const containerRef = ref(null) |
||||||
|
const currentDate = ref('') |
||||||
|
const currentWeekday = ref('') |
||||||
|
const lunarDate = ref('') |
||||||
|
const weather = ref('晴') |
||||||
|
const temperature = ref('36°C') |
||||||
|
|
||||||
|
// 更新时间日期 |
||||||
|
const updateDateTime = () => { |
||||||
|
const now = new Date() |
||||||
|
|
||||||
|
// 格式化日期 |
||||||
|
const year = now.getFullYear() |
||||||
|
const month = now.getMonth() + 1 |
||||||
|
const day = now.getDate() |
||||||
|
currentDate.value = `${year}年${month}月${day}日` |
||||||
|
|
||||||
|
// 星期 |
||||||
|
const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'] |
||||||
|
currentWeekday.value = weekdays[now.getDay()] |
||||||
|
|
||||||
|
// 真实农历日期计算 |
||||||
|
try { |
||||||
|
const solar = Solar.fromDate(now) |
||||||
|
const lunar = solar.getLunar() |
||||||
|
|
||||||
|
// 获取农历信息 |
||||||
|
const lunarMonth = lunar.getMonthInChinese() |
||||||
|
const lunarDay = lunar.getDayInChinese() |
||||||
|
|
||||||
|
// 使用toString方法获取完整信息来检查闰月 |
||||||
|
const lunarString = lunar.toString() |
||||||
|
const isLeapMonth = lunarString.includes('闰') |
||||||
|
|
||||||
|
// 处理农历月份格式 |
||||||
|
let monthName = lunarMonth |
||||||
|
|
||||||
|
// 标准化月份名称 |
||||||
|
const monthMap = { |
||||||
|
'正': '正月', |
||||||
|
'一': '正月', |
||||||
|
'二': '二月', |
||||||
|
'三': '三月', |
||||||
|
'四': '四月', |
||||||
|
'五': '五月', |
||||||
|
'六': '六月', |
||||||
|
'七': '七月', |
||||||
|
'八': '八月', |
||||||
|
'九': '九月', |
||||||
|
'十': '十月', |
||||||
|
'冬': '冬月', |
||||||
|
'腊': '腊月' |
||||||
|
} |
||||||
|
|
||||||
|
if (monthMap[monthName]) { |
||||||
|
monthName = monthMap[monthName] |
||||||
|
} else if (!monthName.includes('月')) { |
||||||
|
monthName = monthName + '月' |
||||||
|
} |
||||||
|
|
||||||
|
// 处理闰月情况 |
||||||
|
if (isLeapMonth && !monthName.startsWith('闰')) { |
||||||
|
monthName = '闰' + monthName |
||||||
|
} |
||||||
|
|
||||||
|
lunarDate.value = `农历${monthName}${lunarDay}` |
||||||
|
} catch (error) { |
||||||
|
console.error('农历计算错误:', error) |
||||||
|
lunarDate.value = '农历计算中...' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 页面缩放适配 |
||||||
|
const updateScale = () => { |
||||||
|
if (!containerRef.value) return |
||||||
|
|
||||||
|
const windowWidth = window.innerWidth |
||||||
|
const windowHeight = window.innerHeight |
||||||
|
|
||||||
|
// 计算缩放比例 |
||||||
|
const scaleX = windowWidth / BASE_WIDTH |
||||||
|
const scaleY = windowHeight / BASE_HEIGHT |
||||||
|
|
||||||
|
// 取较小的缩放比例,确保内容完全可见 |
||||||
|
const scale = Math.min(scaleX, scaleY) |
||||||
|
|
||||||
|
// 应用缩放 |
||||||
|
containerRef.value.style.transform = `scale(${scale})` |
||||||
|
containerRef.value.style.transformOrigin = 'top left' |
||||||
|
|
||||||
|
// 调整容器位置,使其居中 |
||||||
|
const scaledWidth = BASE_WIDTH * scale |
||||||
|
const scaledHeight = BASE_HEIGHT * scale |
||||||
|
|
||||||
|
const offsetX = (windowWidth - scaledWidth) / 2 |
||||||
|
const offsetY = (windowHeight - scaledHeight) / 2 |
||||||
|
|
||||||
|
containerRef.value.style.left = `${offsetX}px` |
||||||
|
containerRef.value.style.top = `${offsetY}px` |
||||||
|
} |
||||||
|
|
||||||
|
let timer = null |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
updateDateTime() |
||||||
|
timer = setInterval(updateDateTime, 60000) // 每分钟更新一次 |
||||||
|
|
||||||
|
// 86寸电视页面专用:禁用页面滚动条 |
||||||
|
document.body.style.overflow = 'hidden' |
||||||
|
document.documentElement.style.overflow = 'hidden' |
||||||
|
|
||||||
|
// 初始化缩放 |
||||||
|
updateScale() |
||||||
|
|
||||||
|
// 监听窗口大小变化 |
||||||
|
window.addEventListener('resize', updateScale) |
||||||
|
}) |
||||||
|
|
||||||
|
onUnmounted(() => { |
||||||
|
if (timer) { |
||||||
|
clearInterval(timer) |
||||||
|
} |
||||||
|
window.removeEventListener('resize', updateScale) |
||||||
|
|
||||||
|
// 恢复页面滚动条(当离开86寸电视页面时) |
||||||
|
document.body.style.overflow = 'auto' |
||||||
|
document.documentElement.style.overflow = 'auto' |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.tv86-viewport { |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
background: #000000; |
||||||
|
overflow: hidden; |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
|
||||||
|
.tv86-container { |
||||||
|
width: 3840px; |
||||||
|
height: 2160px; |
||||||
|
background: |
||||||
|
radial-gradient(ellipse at 25% 25%, rgba(64, 158, 255, 0.08) 0%, transparent 50%), |
||||||
|
radial-gradient(ellipse at 75% 75%, rgba(139, 92, 246, 0.06) 0%, transparent 50%), |
||||||
|
linear-gradient(135deg, #0a0e1a 0%, #0f1419 25%, #131825 50%, #0f1419 75%, #0a0e1a 100%); |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif; |
||||||
|
color: #ffffff; |
||||||
|
overflow: hidden; |
||||||
|
padding: 40px; |
||||||
|
box-sizing: border-box; |
||||||
|
position: absolute; |
||||||
|
} |
||||||
|
|
||||||
|
/* 顶部时间日期天气区域 */ |
||||||
|
.header-datetime { |
||||||
|
height: 200px; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
margin-bottom: 40px; |
||||||
|
} |
||||||
|
|
||||||
|
.datetime-info { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
gap: 80px; |
||||||
|
font-size: 56px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
.date { |
||||||
|
color: #ffffff; |
||||||
|
font-size: 56px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
.weekday { |
||||||
|
color: #409EFF; |
||||||
|
font-size: 56px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
.lunar { |
||||||
|
color: #67C23A; |
||||||
|
font-size: 56px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
.weather { |
||||||
|
color: #E6A23C; |
||||||
|
font-size: 56px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
.temperature { |
||||||
|
color: #F56C6C; |
||||||
|
font-size: 56px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
/* 主内容区域 */ |
||||||
|
.main-content { |
||||||
|
flex: 1; |
||||||
|
display: flex; |
||||||
|
gap: 60px; |
||||||
|
min-height: 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* 左侧区域 */ |
||||||
|
.left-section { |
||||||
|
flex: 2; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
gap: 40px; |
||||||
|
} |
||||||
|
|
||||||
|
.left-top { |
||||||
|
/* height: 400px; */ |
||||||
|
flex-shrink: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.left-bottom { |
||||||
|
flex: 1; |
||||||
|
min-height: 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* 右侧区域 */ |
||||||
|
.right-section { |
||||||
|
flex: 1; |
||||||
|
} |
||||||
|
|
||||||
|
/* 确保内容始终适配,移除响应式媒体查询 */ |
||||||
|
/* 页面会通过JavaScript自动缩放来适配不同屏幕尺寸 */ |
||||||
|
</style> |
||||||
Loading…
Reference in new issue