641 lines
22 KiB
JavaScript
641 lines
22 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');
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// 添加Q&A和帮助链接的事件监听
|
||
const qaLink = this.shadowRoot.querySelector('#qa-link');
|
||
const helpLink = this.shadowRoot.querySelector('#help-link');
|
||
|
||
if (qaLink) {
|
||
qaLink.addEventListener('click', (e) => {
|
||
e.preventDefault();
|
||
// 获取子工具栏
|
||
const subToolbar = document.querySelector('sub-toolbar');
|
||
if (subToolbar) {
|
||
// 触发与常见问题按钮相同的事件
|
||
const event = new CustomEvent('sub-item-selected', {
|
||
detail: {
|
||
parent: 'home',
|
||
text: 'Q&A',
|
||
icon: 'question'
|
||
},
|
||
bubbles: true,
|
||
composed: true
|
||
});
|
||
subToolbar.dispatchEvent(event);
|
||
}
|
||
});
|
||
}
|
||
|
||
if (helpLink) {
|
||
helpLink.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: 'help'
|
||
},
|
||
bubbles: true,
|
||
composed: true
|
||
});
|
||
subToolbar.dispatchEvent(event);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
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);
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.about-section .section-title {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.about-section p {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.about-section .version-list {
|
||
margin-bottom: auto;
|
||
}
|
||
|
||
.help-links {
|
||
margin-top: auto;
|
||
display: flex;
|
||
gap: 16px;
|
||
justify-content: center;
|
||
padding-top: 20px;
|
||
border-top: 1px solid #eee;
|
||
}
|
||
|
||
.help-link {
|
||
position: relative;
|
||
color: #667eea;
|
||
text-decoration: none;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 15px;
|
||
padding: 4px 2px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.help-link:hover {
|
||
color: #764ba2;
|
||
}
|
||
|
||
.help-link::before {
|
||
content: '';
|
||
display: inline-block;
|
||
width: 16px;
|
||
height: 16px;
|
||
background-size: 16px;
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
transition: all 0.3s ease;
|
||
/* 将黑色图标转换为蓝色 (#667eea) */
|
||
filter: brightness(0) saturate(100%) invert(53%) sepia(95%) saturate(1731%) hue-rotate(213deg) brightness(99%) contrast(94%) !important;
|
||
}
|
||
|
||
#qa-link::before {
|
||
background-image: url('assets/icons/png/question_b.png');
|
||
}
|
||
|
||
#help-link::before {
|
||
background-image: url('assets/icons/png/help_b.png');
|
||
}
|
||
|
||
.help-link:hover::before {
|
||
/* 将黑色图标转换为紫色 (#764ba2) */
|
||
filter: brightness(0) saturate(100%) invert(36%) sepia(24%) saturate(1674%) hue-rotate(233deg) brightness(94%) contrast(92%) !important;
|
||
}
|
||
|
||
.help-link::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 1px;
|
||
background: currentColor;
|
||
transform: scaleX(0);
|
||
transform-origin: right;
|
||
transition: transform 0.3s ease;
|
||
}
|
||
|
||
.help-link:hover::after {
|
||
transform: scaleX(1);
|
||
transform-origin: left;
|
||
}
|
||
|
||
.help-link::before {
|
||
content: '';
|
||
display: inline-block;
|
||
width: 18px;
|
||
height: 18px;
|
||
background-size: 18px;
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.1));
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.help-link:hover::before {
|
||
transform: scale(1.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 class="help-links">
|
||
<a href="#" class="help-link" id="qa-link">Q&A</a>
|
||
<a href="#" class="help-link" id="help-link">帮助</a>
|
||
</div>
|
||
</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);
|