492 lines
16 KiB
JavaScript
492 lines
16 KiB
JavaScript
|
class OverviewPage extends HTMLElement {
|
|||
|
constructor() {
|
|||
|
super();
|
|||
|
this.attachShadow({ mode: 'open' });
|
|||
|
this.versions = [];
|
|||
|
}
|
|||
|
|
|||
|
connectedCallback() {
|
|||
|
this.loadVersions().then(() => {
|
|||
|
this.render();
|
|||
|
this.startClock();
|
|||
|
this.setupEventListeners();
|
|||
|
}).catch(error => {
|
|||
|
console.error('加载版本信息失败:', error);
|
|||
|
this.render();
|
|||
|
this.startClock();
|
|||
|
this.setupEventListeners();
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
disconnectedCallback() {
|
|||
|
if (this.clockInterval) {
|
|||
|
clearInterval(this.clockInterval);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
async loadVersions() {
|
|||
|
try {
|
|||
|
const response = await fetch('/api/versions');
|
|||
|
if (!response.ok) {
|
|||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|||
|
}
|
|||
|
this.versions = await response.json();
|
|||
|
} catch (error) {
|
|||
|
console.error('加载版本信息失败:', error);
|
|||
|
this.versions = [];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
formatVersionRecord(version) {
|
|||
|
const date = version.date;
|
|||
|
const time = version.time;
|
|||
|
const verNum = version.verNum;
|
|||
|
const title = version.title;
|
|||
|
|
|||
|
return `【${date} ${time}】v${verNum} ${title}`;
|
|||
|
}
|
|||
|
|
|||
|
startClock() {
|
|||
|
const updateClock = () => {
|
|||
|
const dateElement = this.shadowRoot.querySelector('.current-time .date');
|
|||
|
const timeElement = this.shadowRoot.querySelector('.current-time .time');
|
|||
|
const now = new Date();
|
|||
|
|
|||
|
dateElement.textContent = now.toLocaleString('zh-CN', {
|
|||
|
year: 'numeric',
|
|||
|
month: '2-digit',
|
|||
|
day: '2-digit'
|
|||
|
});
|
|||
|
|
|||
|
timeElement.textContent = now.toLocaleString('zh-CN', {
|
|||
|
hour: '2-digit',
|
|||
|
minute: '2-digit',
|
|||
|
second: '2-digit',
|
|||
|
hour12: false
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
updateClock();
|
|||
|
this.clockInterval = setInterval(updateClock, 1000);
|
|||
|
}
|
|||
|
|
|||
|
setupEventListeners() {
|
|||
|
const detailsLink = this.shadowRoot.querySelector('.details-link');
|
|||
|
if (detailsLink) {
|
|||
|
detailsLink.addEventListener('click', (e) => {
|
|||
|
e.preventDefault();
|
|||
|
// 获取子工具栏
|
|||
|
const subToolbar = document.querySelector('sub-toolbar');
|
|||
|
if (subToolbar) {
|
|||
|
// 触发与更新记录按钮相同的事件
|
|||
|
const event = new CustomEvent('sub-item-selected', {
|
|||
|
detail: {
|
|||
|
parent: 'home',
|
|||
|
text: '更新记录',
|
|||
|
icon: 'clock'
|
|||
|
},
|
|||
|
bubbles: true,
|
|||
|
composed: true
|
|||
|
});
|
|||
|
subToolbar.dispatchEvent(event);
|
|||
|
|
|||
|
// 激活更新记录按钮
|
|||
|
const updateRecordButton = subToolbar.shadowRoot.querySelector('.sub-item[data-icon="clock"]');
|
|||
|
if (updateRecordButton) {
|
|||
|
// 移除其他按钮的激活状态
|
|||
|
const siblings = updateRecordButton.parentElement.querySelectorAll('.sub-item');
|
|||
|
siblings.forEach(si => si.classList.remove('active'));
|
|||
|
// 激活更新记录按钮
|
|||
|
updateRecordButton.classList.add('active');
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
render() {
|
|||
|
// 添加加载状态的样式
|
|||
|
const style = `
|
|||
|
.update-list {
|
|||
|
min-height: 100px;
|
|||
|
position: relative;
|
|||
|
}
|
|||
|
|
|||
|
.update-list.loading::after {
|
|||
|
content: '加载中...';
|
|||
|
position: absolute;
|
|||
|
top: 50%;
|
|||
|
left: 50%;
|
|||
|
transform: translate(-50%, -50%);
|
|||
|
color: #666;
|
|||
|
}
|
|||
|
|
|||
|
.update-list.error::after {
|
|||
|
content: '加载失败';
|
|||
|
position: absolute;
|
|||
|
top: 50%;
|
|||
|
left: 50%;
|
|||
|
transform: translate(-50%, -50%);
|
|||
|
color: #ff4444;
|
|||
|
}
|
|||
|
`;
|
|||
|
|
|||
|
this.shadowRoot.innerHTML = `
|
|||
|
<style>
|
|||
|
${style}
|
|||
|
:host {
|
|||
|
display: block;
|
|||
|
height: 100%;
|
|||
|
}
|
|||
|
|
|||
|
.overview-container {
|
|||
|
max-width: 1200px;
|
|||
|
margin: 0 auto;
|
|||
|
display: grid;
|
|||
|
grid-template-columns: repeat(12, 1fr);
|
|||
|
gap: 20px;
|
|||
|
padding: 20px;
|
|||
|
}
|
|||
|
|
|||
|
.welcome-card {
|
|||
|
grid-column: span 8;
|
|||
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|||
|
color: white;
|
|||
|
border-radius: 12px;
|
|||
|
padding: 30px;
|
|||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|||
|
}
|
|||
|
|
|||
|
.welcome-card h1 {
|
|||
|
font-size: 32px;
|
|||
|
margin: 0 0 10px 0;
|
|||
|
}
|
|||
|
|
|||
|
.welcome-card p {
|
|||
|
font-size: 16px;
|
|||
|
opacity: 0.9;
|
|||
|
}
|
|||
|
|
|||
|
.current-time {
|
|||
|
grid-column: span 4;
|
|||
|
background: white;
|
|||
|
border-radius: 12px;
|
|||
|
padding: 20px;
|
|||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|||
|
}
|
|||
|
|
|||
|
.current-time h2 {
|
|||
|
font-size: 18px;
|
|||
|
color: #2c3e50;
|
|||
|
margin: 0 0 15px 0;
|
|||
|
}
|
|||
|
|
|||
|
.current-time .value {
|
|||
|
font-size: 24px;
|
|||
|
color: #3498db;
|
|||
|
font-weight: bold;
|
|||
|
}
|
|||
|
|
|||
|
.current-time .date{
|
|||
|
display: block;
|
|||
|
font-size: 20px;
|
|||
|
color: #764ba2;
|
|||
|
font-weight: bold;
|
|||
|
}
|
|||
|
|
|||
|
.current-time .time {
|
|||
|
display: block;
|
|||
|
font-size: 40px;
|
|||
|
color: #667eea;
|
|||
|
font-weight: bold;
|
|||
|
}
|
|||
|
|
|||
|
.version-info {
|
|||
|
grid-column: span 4;
|
|||
|
background: white;
|
|||
|
border-radius: 12px;
|
|||
|
padding: 20px;
|
|||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|||
|
}
|
|||
|
|
|||
|
.about-section {
|
|||
|
grid-column: span 4;
|
|||
|
background: white;
|
|||
|
border-radius: 12px;
|
|||
|
padding: 20px;
|
|||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|||
|
}
|
|||
|
|
|||
|
.calendar-widget {
|
|||
|
grid-column: span 4;
|
|||
|
background: white;
|
|||
|
border-radius: 12px;
|
|||
|
padding: 20px;
|
|||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|||
|
}
|
|||
|
|
|||
|
.updates-log {
|
|||
|
grid-column: span 12;
|
|||
|
background: white;
|
|||
|
border-radius: 12px;
|
|||
|
padding: 20px;
|
|||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|||
|
position: relative;
|
|||
|
}
|
|||
|
|
|||
|
.section-title {
|
|||
|
font-size: 18px;
|
|||
|
color: #2c3e50;
|
|||
|
margin: 0 0 15px 0;
|
|||
|
}
|
|||
|
|
|||
|
.version-list, .update-list {
|
|||
|
list-style: none;
|
|||
|
padding: 0;
|
|||
|
margin: 0;
|
|||
|
}
|
|||
|
|
|||
|
.version-list li, .update-list li {
|
|||
|
padding: 8px 0;
|
|||
|
border-bottom: 1px solid #eee;
|
|||
|
color: #2c3e50;
|
|||
|
}
|
|||
|
|
|||
|
.version-list li:last-child, .update-list li:last-child {
|
|||
|
border-bottom: none;
|
|||
|
}
|
|||
|
|
|||
|
.calendar {
|
|||
|
text-align: center;
|
|||
|
}
|
|||
|
|
|||
|
.calendar .month {
|
|||
|
font-size: 20px;
|
|||
|
font-weight: bold;
|
|||
|
color: #2c3e50;
|
|||
|
margin-bottom: 10px;
|
|||
|
}
|
|||
|
|
|||
|
.calendar-grid {
|
|||
|
display: grid;
|
|||
|
grid-template-columns: repeat(7, 1fr);
|
|||
|
gap: 5px;
|
|||
|
margin-top: 10px;
|
|||
|
}
|
|||
|
|
|||
|
.calendar-header {
|
|||
|
display: grid;
|
|||
|
grid-template-columns: repeat(7, 1fr);
|
|||
|
gap: 5px;
|
|||
|
margin-bottom: 5px;
|
|||
|
}
|
|||
|
|
|||
|
.calendar-header span {
|
|||
|
font-size: 14px;
|
|||
|
color: #666;
|
|||
|
text-align: center;
|
|||
|
}
|
|||
|
|
|||
|
.calendar-day {
|
|||
|
padding: 5px;
|
|||
|
text-align: center;
|
|||
|
font-size: 14px;
|
|||
|
border-radius: 4px;
|
|||
|
cursor: pointer;
|
|||
|
}
|
|||
|
|
|||
|
.calendar-day.today {
|
|||
|
background-color: #667eea;
|
|||
|
color: white;
|
|||
|
}
|
|||
|
|
|||
|
.calendar-day.other-month {
|
|||
|
color: #ccc;
|
|||
|
}
|
|||
|
|
|||
|
.version-list {
|
|||
|
list-style: none;
|
|||
|
padding: 0;
|
|||
|
margin: 0;
|
|||
|
}
|
|||
|
|
|||
|
.version-list li {
|
|||
|
padding: 8px 0;
|
|||
|
border-bottom: 1px solid #eee;
|
|||
|
color: #2c3e50;
|
|||
|
display: flex;
|
|||
|
justify-content: space-between;
|
|||
|
}
|
|||
|
|
|||
|
.version-list li .label {
|
|||
|
color: #666;
|
|||
|
font-size: 0.9em;
|
|||
|
min-width: 80px;
|
|||
|
}
|
|||
|
|
|||
|
.version-list li .value {
|
|||
|
color: #2c3e50;
|
|||
|
font-weight: 500;
|
|||
|
text-align: right;
|
|||
|
}
|
|||
|
|
|||
|
.version-list li:last-child {
|
|||
|
border-bottom: none;
|
|||
|
}
|
|||
|
|
|||
|
.version-note {
|
|||
|
color: #666;
|
|||
|
font-size: 0.9em;
|
|||
|
line-height: 1.4;
|
|||
|
padding: 8px 0;
|
|||
|
background: #f8f9fa;
|
|||
|
border-radius: 4px;
|
|||
|
margin-top: 8px;
|
|||
|
padding: 10px;
|
|||
|
}
|
|||
|
|
|||
|
.version-loading {
|
|||
|
text-align: center;
|
|||
|
color: #666;
|
|||
|
padding: 20px 0;
|
|||
|
}
|
|||
|
|
|||
|
.details-link {
|
|||
|
position: absolute;
|
|||
|
top: 20px;
|
|||
|
right: 20px;
|
|||
|
color: #667eea;
|
|||
|
text-decoration: none;
|
|||
|
font-size: 14px;
|
|||
|
display: flex;
|
|||
|
align-items: center;
|
|||
|
gap: 4px;
|
|||
|
transition: color 0.3s;
|
|||
|
}
|
|||
|
|
|||
|
.details-link:hover {
|
|||
|
color: #764ba2;
|
|||
|
}
|
|||
|
|
|||
|
.details-link::after {
|
|||
|
content: '›';
|
|||
|
font-size: 18px;
|
|||
|
line-height: 1;
|
|||
|
margin-top: -2px;
|
|||
|
}
|
|||
|
</style>
|
|||
|
<div class="overview-container">
|
|||
|
<div class="welcome-card">
|
|||
|
<h1>欢迎使用 XNSim</h1>
|
|||
|
<p>XNSim 是一个功能强大的仿真系统,为您提供专业的飞行模拟仿真分析工具和直观的操作界面。</p>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="current-time">
|
|||
|
<h2>当前时间</h2>
|
|||
|
<div class="value">
|
|||
|
<span class="date"></span>
|
|||
|
<span class="time"></span>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="version-info">
|
|||
|
<h2 class="section-title">版本信息</h2>
|
|||
|
${this.versions.length > 0 ? `
|
|||
|
<ul class="version-list">
|
|||
|
<li>
|
|||
|
<span class="label">当前版本</span>
|
|||
|
<span class="value">v${this.versions[0].verNum}</span>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<span class="label">发布日期</span>
|
|||
|
<span class="value">${this.versions[0].date}</span>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<span class="label">发布时间</span>
|
|||
|
<span class="value">${this.versions[0].time}</span>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
` : `
|
|||
|
<div class="version-loading">版本信息加载中...</div>
|
|||
|
`}
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="about-section">
|
|||
|
<h2 class="section-title">关于系统</h2>
|
|||
|
<p>XNSim 是新一代飞行模拟数据包仿真分析平台,致力于提供高效、准确的飞行模拟仿真解决方案。</p>
|
|||
|
<ul class="version-list">
|
|||
|
<li>开发团队:XNSim Team</li>
|
|||
|
<li>技术支持:无</li>
|
|||
|
<li>官方网站:无</li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="calendar-widget">
|
|||
|
<h2 class="section-title">日历</h2>
|
|||
|
<div class="calendar">
|
|||
|
<div class="month"></div>
|
|||
|
<div class="calendar-header">
|
|||
|
<span>日</span>
|
|||
|
<span>一</span>
|
|||
|
<span>二</span>
|
|||
|
<span>三</span>
|
|||
|
<span>四</span>
|
|||
|
<span>五</span>
|
|||
|
<span>六</span>
|
|||
|
</div>
|
|||
|
<div class="calendar-grid"></div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="updates-log">
|
|||
|
<h2 class="section-title">更新记录</h2>
|
|||
|
<a href="#" class="details-link">详细信息</a>
|
|||
|
<ul class="update-list ${this.versions.length === 0 ? 'error' : ''}">
|
|||
|
${this.versions.slice(0, 3).map(version => `
|
|||
|
<li>${this.formatVersionRecord(version)}</li>
|
|||
|
`).join('')}
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
`;
|
|||
|
|
|||
|
// 初始化日历显示
|
|||
|
const now = new Date();
|
|||
|
const calendarElement = this.shadowRoot.querySelector('.calendar');
|
|||
|
calendarElement.querySelector('.month').textContent = now.toLocaleString('zh-CN', { month: 'long' });
|
|||
|
calendarElement.querySelector('.calendar-grid').innerHTML = this.generateCalendarGrid(now);
|
|||
|
}
|
|||
|
|
|||
|
generateCalendarGrid(date) {
|
|||
|
const year = date.getFullYear();
|
|||
|
const month = date.getMonth();
|
|||
|
const today = new Date();
|
|||
|
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|||
|
const firstDay = new Date(year, month, 1).getDay();
|
|||
|
const lastDayPrevMonth = new Date(year, month, 0).getDate();
|
|||
|
let gridHtml = '';
|
|||
|
|
|||
|
// 上个月的日期
|
|||
|
for (let i = firstDay - 1; i >= 0; i--) {
|
|||
|
gridHtml += `<div class="calendar-day other-month">${lastDayPrevMonth - i}</div>`;
|
|||
|
}
|
|||
|
|
|||
|
// 当月的日期
|
|||
|
for (let day = 1; day <= daysInMonth; day++) {
|
|||
|
const isToday = day === today.getDate() &&
|
|||
|
month === today.getMonth() &&
|
|||
|
year === today.getFullYear();
|
|||
|
gridHtml += `<div class="calendar-day${isToday ? ' today' : ''}">${day}</div>`;
|
|||
|
}
|
|||
|
|
|||
|
// 下个月的日期
|
|||
|
const remainingDays = 42 - (firstDay + daysInMonth); // 保持6行格式
|
|||
|
for (let day = 1; day <= remainingDays; day++) {
|
|||
|
gridHtml += `<div class="calendar-day other-month">${day}</div>`;
|
|||
|
}
|
|||
|
|
|||
|
return gridHtml;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
customElements.define('overview-page', OverviewPage);
|