parent
dc5658513c
commit
55a7f3c79c
@ -0,0 +1,197 @@ |
|||||||
|
<template> |
||||||
|
<div class="scrolling-announcement"> |
||||||
|
<div class="announcement-header"> |
||||||
|
<div class="header-icon">📢</div> |
||||||
|
<div class="header-title">最新公告</div> |
||||||
|
</div> |
||||||
|
<div class="announcement-content"> |
||||||
|
<div class="scrolling-container" ref="scrollingContainer"> |
||||||
|
<div |
||||||
|
class="announcement-item current" |
||||||
|
:key="'current-' + currentIndex" |
||||||
|
> |
||||||
|
{{ announcements[currentIndex]?.content }} |
||||||
|
</div> |
||||||
|
<div |
||||||
|
class="announcement-item next" |
||||||
|
:key="'next-' + nextIndex" |
||||||
|
> |
||||||
|
{{ announcements[nextIndex]?.content }} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup> |
||||||
|
import { ref, onMounted, onUnmounted, reactive, computed } from 'vue' |
||||||
|
|
||||||
|
const scrollingContainer = ref(null) |
||||||
|
const currentIndex = ref(0) |
||||||
|
const isAnimating = ref(false) |
||||||
|
let scrollTimer = null |
||||||
|
|
||||||
|
const announcements = reactive([]) |
||||||
|
|
||||||
|
// 计算下一个索引 |
||||||
|
const nextIndex = computed(() => { |
||||||
|
return announcements.length > 0 ? (currentIndex.value + 1) % announcements.length : 0 |
||||||
|
}) |
||||||
|
|
||||||
|
// 加载公告数据 |
||||||
|
const loadAnnouncements = async () => { |
||||||
|
// 室外页面直接使用默认模拟数据,确保显示效果 |
||||||
|
console.log('室外页面使用专用公告数据') |
||||||
|
loadDefaultAnnouncements() |
||||||
|
} |
||||||
|
|
||||||
|
// 加载默认公告 |
||||||
|
const loadDefaultAnnouncements = () => { |
||||||
|
announcements.splice(0, announcements.length, |
||||||
|
{ content: '【重要通知】今日牦牛交易价格上涨2.5%,活牛32.8元/公斤,鲜肉78.5元/公斤,受春季需求增长影响,请商户关注行情变化,合理安排采购计划' }, |
||||||
|
{ content: '【招商信息】新增北京、上海、广州、深圳等一线城市优质采购商入驻,涵盖餐饮连锁、精品超市、生鲜电商等业态,欢迎牧民朋友联系洽谈合作' }, |
||||||
|
{ content: '【系统维护】本周末(3月18日22:00-19日08:00)系统升级维护,交易时间调整为09:00-17:00,支付功能暂停4小时,请提前安排交易事宜' }, |
||||||
|
{ content: '【市场资讯】受春季牧草丰茂、气候适宜等因素影响,预计下月牦牛供应量增加15%,达8500头左右,肉质品质提升,建议采购商提前规划库存' }, |
||||||
|
{ content: '【质量报告】西藏、青海、甘肃等主要产区肉品检测合格率达98.5%,创历史新高,蛋白质含量21.8%,重金属含量远低于国标,品质安全有保障' }, |
||||||
|
{ content: '【政策利好】国家三部委发布牦牛产业发展指导意见,三年内投入50亿元专项资金,支持养殖技术、品种改良、冷链物流等领域,并给予税收减免支持' }, |
||||||
|
{ content: '【安全提醒】近期发现不法分子冒充平台工作人员诈骗,以系统升级、账户异常为借口要求转账,请提高警惕,平台绝不私下要求转账,疑问请联系客服400-8888-666' }, |
||||||
|
{ content: '【物流升级】与顺丰、德邦、中通等企业合作开通专业冷链服务,覆盖全国主要城市,配备冷藏车辆恒温仓储,一线城市24小时送达,配送效率提升60%' }, |
||||||
|
{ content: '【技术创新】推出AI智能定价系统2.0,整合市场供需、季节变化、品质等级等数据,运用深度学习算法提供精准价格参考,预计节省交易成本15-20%' }, |
||||||
|
{ content: '【节日祝福】藏历新年洛萨节即将到来,平台全体员工向牧民朋友和合作伙伴致以节日祝福,祝大家新年健康幸福、生意兴隆,牦牛产业蒸蒸日上,扎西德勒!' } |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
// 执行滚动动画 |
||||||
|
const performScroll = () => { |
||||||
|
if (isAnimating.value || announcements.length <= 1) return |
||||||
|
|
||||||
|
isAnimating.value = true |
||||||
|
const container = scrollingContainer.value |
||||||
|
|
||||||
|
// 添加滚动类来触发动画 |
||||||
|
container.classList.add('scrolling') |
||||||
|
|
||||||
|
// 动画完成后更新索引并重置状态 |
||||||
|
setTimeout(() => { |
||||||
|
currentIndex.value = nextIndex.value |
||||||
|
container.classList.remove('scrolling') |
||||||
|
isAnimating.value = false |
||||||
|
}, 800) // 动画持续时间 |
||||||
|
} |
||||||
|
|
||||||
|
// 开始垂直滚动 |
||||||
|
const startVerticalScroll = () => { |
||||||
|
if (announcements.length <= 1) return |
||||||
|
|
||||||
|
scrollTimer = setInterval(() => { |
||||||
|
performScroll() |
||||||
|
}, 5000) // 每5秒切换一次 |
||||||
|
} |
||||||
|
|
||||||
|
// 停止滚动 |
||||||
|
const stopVerticalScroll = () => { |
||||||
|
if (scrollTimer) { |
||||||
|
clearInterval(scrollTimer) |
||||||
|
scrollTimer = null |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
onMounted(async () => { |
||||||
|
await loadAnnouncements() |
||||||
|
// 等待数据加载完成后开始滚动 |
||||||
|
if (announcements.length > 1) { |
||||||
|
startVerticalScroll() |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
onUnmounted(() => { |
||||||
|
stopVerticalScroll() |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.scrolling-announcement { |
||||||
|
position: absolute; |
||||||
|
top: 20px; |
||||||
|
left: 0; |
||||||
|
right: 0; |
||||||
|
height: 70px; |
||||||
|
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; |
||||||
|
overflow: hidden; |
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); |
||||||
|
} |
||||||
|
|
||||||
|
.announcement-header { |
||||||
|
position: absolute; |
||||||
|
left: 0; |
||||||
|
top: 0; |
||||||
|
bottom: 0; |
||||||
|
width: 140px; |
||||||
|
background: rgba(64, 158, 255, 0.2); |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
gap: 8px; |
||||||
|
border-right: 1px solid rgba(64, 158, 255, 0.3); |
||||||
|
} |
||||||
|
|
||||||
|
.header-icon { |
||||||
|
font-size: 20px; |
||||||
|
} |
||||||
|
|
||||||
|
.header-title { |
||||||
|
font-size: 20px; |
||||||
|
font-weight: bold; |
||||||
|
color: #409EFF; |
||||||
|
} |
||||||
|
|
||||||
|
.announcement-content { |
||||||
|
margin-left: 140px; |
||||||
|
height: 100%; |
||||||
|
overflow: hidden; |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
|
||||||
|
.scrolling-container { |
||||||
|
position: relative; |
||||||
|
height: 100%; |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.announcement-item { |
||||||
|
position: absolute; |
||||||
|
top: 0; |
||||||
|
left: 0; |
||||||
|
right: 0; |
||||||
|
height: 100%; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
font-size: 28px; |
||||||
|
color: #fff; |
||||||
|
padding: 0 30px; |
||||||
|
white-space: nowrap; |
||||||
|
transition: transform 0.8s ease-in-out; |
||||||
|
font-weight: 500; |
||||||
|
} |
||||||
|
|
||||||
|
.announcement-item.current { |
||||||
|
transform: translateY(0); |
||||||
|
} |
||||||
|
|
||||||
|
.announcement-item.next { |
||||||
|
transform: translateY(100%); |
||||||
|
} |
||||||
|
|
||||||
|
/* 滚动动画状态 */ |
||||||
|
.scrolling-container.scrolling .announcement-item.current { |
||||||
|
transform: translateY(-100%); |
||||||
|
} |
||||||
|
|
||||||
|
.scrolling-container.scrolling .announcement-item.next { |
||||||
|
transform: translateY(0); |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,303 @@ |
|||||||
|
<template> |
||||||
|
<div class="outdoor-viewport"> |
||||||
|
<div class="outdoor-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="announcement-section"> |
||||||
|
<ScrollingAnnouncement /> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- 主内容四象限布局 --> |
||||||
|
<div class="main-content"> |
||||||
|
<!-- 左上:牦牛实时交易概况 --> |
||||||
|
<div class="content-area top-left"> |
||||||
|
<YakTradingOverview /> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- 右上:活牛、鲜肉价格趋势 --> |
||||||
|
<div class="content-area top-right"> |
||||||
|
<PriceTrendChart /> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- 左下:待交易牦牛数据 --> |
||||||
|
<div class="content-area bottom-left"> |
||||||
|
<PendingYakData /> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- 右下:交易结算数据 --> |
||||||
|
<div class="content-area bottom-right"> |
||||||
|
<TransactionSettlement /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup> |
||||||
|
import { ref, onMounted, onUnmounted } from 'vue' |
||||||
|
import ScrollingAnnouncement from '../components/Outdoor/ScrollingAnnouncement.vue' |
||||||
|
import YakTradingOverview from '../components/Outdoor/YakTradingOverview.vue' |
||||||
|
import PriceTrendChart from '../components/Outdoor/PriceTrendChart.vue' |
||||||
|
import PendingYakData from '../components/Outdoor/PendingYakData.vue' |
||||||
|
import TransactionSettlement from '../components/Outdoor/TransactionSettlement.vue' |
||||||
|
import { Lunar, Solar } from 'lunar-javascript' |
||||||
|
|
||||||
|
// 基准分辨率 3000×1500 |
||||||
|
const BASE_WIDTH = 3000 |
||||||
|
const BASE_HEIGHT = 1500 |
||||||
|
|
||||||
|
// 响应式数据 |
||||||
|
const containerRef = ref(null) |
||||||
|
const currentDate = ref('') |
||||||
|
const currentWeekday = ref('') |
||||||
|
const lunarDate = ref('') |
||||||
|
const weather = ref('晴') |
||||||
|
const temperature = ref('18°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() |
||||||
|
|
||||||
|
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) // 每分钟更新一次 |
||||||
|
|
||||||
|
// 隐藏页面滚动条 |
||||||
|
document.body.style.overflow = 'hidden' |
||||||
|
document.documentElement.style.overflow = 'hidden' |
||||||
|
|
||||||
|
// 初始化缩放 |
||||||
|
updateScale() |
||||||
|
|
||||||
|
// 监听窗口大小变化 |
||||||
|
window.addEventListener('resize', updateScale) |
||||||
|
}) |
||||||
|
|
||||||
|
onUnmounted(() => { |
||||||
|
if (timer) { |
||||||
|
clearInterval(timer) |
||||||
|
} |
||||||
|
window.removeEventListener('resize', updateScale) |
||||||
|
|
||||||
|
// 恢复页面滚动条 |
||||||
|
document.body.style.overflow = 'auto' |
||||||
|
document.documentElement.style.overflow = 'auto' |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.outdoor-viewport { |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
background: #000000; |
||||||
|
overflow: hidden; |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
|
||||||
|
.outdoor-container { |
||||||
|
width: 3000px; |
||||||
|
height: 1500px; |
||||||
|
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: 30px; |
||||||
|
box-sizing: border-box; |
||||||
|
position: absolute; |
||||||
|
} |
||||||
|
|
||||||
|
/* 顶部时间日期天气区域 */ |
||||||
|
.header-datetime { |
||||||
|
height: 80px; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
margin-bottom: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.datetime-info { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
gap: 50px; |
||||||
|
font-size: 36px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
.date { |
||||||
|
color: #ffffff; |
||||||
|
font-size: 36px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
.weekday { |
||||||
|
color: #409EFF; |
||||||
|
font-size: 36px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
.lunar { |
||||||
|
color: #67C23A; |
||||||
|
font-size: 36px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
.weather { |
||||||
|
color: #E6A23C; |
||||||
|
font-size: 36px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
.temperature { |
||||||
|
color: #F56C6C; |
||||||
|
font-size: 36px; |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
/* 滚动公告区域 */ |
||||||
|
.announcement-section { |
||||||
|
height: 90px; |
||||||
|
margin-bottom: 25px; |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
|
||||||
|
/* 主内容四象限网格布局 */ |
||||||
|
.main-content { |
||||||
|
flex: 1; |
||||||
|
display: grid; |
||||||
|
grid-template-columns: 1fr 1fr; |
||||||
|
grid-template-rows: 1fr 1fr; |
||||||
|
gap: 25px; |
||||||
|
min-height: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.content-area { |
||||||
|
background: rgba(15, 25, 45, 0.4); |
||||||
|
backdrop-filter: blur(20px) saturate(180%); |
||||||
|
border-radius: 20px; |
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1); |
||||||
|
box-shadow: |
||||||
|
0 12px 48px rgba(0, 0, 0, 0.3), |
||||||
|
0 6px 24px rgba(64, 158, 255, 0.1), |
||||||
|
inset 0 2px 0 rgba(255, 255, 255, 0.1); |
||||||
|
overflow: hidden; |
||||||
|
transition: all 0.3s ease; |
||||||
|
} |
||||||
|
|
||||||
|
.content-area:hover { |
||||||
|
transform: translateY(-3px); |
||||||
|
box-shadow: |
||||||
|
0 18px 72px rgba(0, 0, 0, 0.4), |
||||||
|
0 9px 36px rgba(64, 158, 255, 0.15), |
||||||
|
inset 0 2px 0 rgba(255, 255, 255, 0.15); |
||||||
|
} |
||||||
|
|
||||||
|
.top-left { |
||||||
|
grid-area: 1 / 1 / 2 / 2; |
||||||
|
} |
||||||
|
|
||||||
|
.top-right { |
||||||
|
grid-area: 1 / 2 / 2 / 3; |
||||||
|
} |
||||||
|
|
||||||
|
.bottom-left { |
||||||
|
grid-area: 2 / 1 / 3 / 2; |
||||||
|
} |
||||||
|
|
||||||
|
.bottom-right { |
||||||
|
grid-area: 2 / 2 / 3 / 3; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
</style> |
||||||
Loading…
Reference in new issue