2587 lines
98 KiB
HTML
2587 lines
98 KiB
HTML
|
|
<!DOCTYPE html>
|
|||
|
|
<html lang="zh-CN">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="UTF-8">
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
|
<title>NEON-OS | 赛博朋克未来操作系统</title>
|
|||
|
|
|
|||
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|||
|
|
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700;900&family=Roboto+Mono:wght@300;400;500&display=swap" rel="stylesheet">
|
|||
|
|
|
|||
|
|
<style>
|
|||
|
|
:root {
|
|||
|
|
--neon-pink: #ff2a6d;
|
|||
|
|
--neon-blue: #05d9e8;
|
|||
|
|
--neon-purple: #d100d1;
|
|||
|
|
--neon-cyan: #00ff9f;
|
|||
|
|
--dark-bg: #0d0d16;
|
|||
|
|
--glass-bg: rgba(13, 13, 22, 0.7);
|
|||
|
|
--glass-border: rgba(5, 217, 232, 0.3);
|
|||
|
|
--scan-line: rgba(0, 255, 159, 0.05);
|
|||
|
|
--grid-color: rgba(255, 42, 109, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
* {
|
|||
|
|
margin: 0;
|
|||
|
|
padding: 0;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
user-select: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body {
|
|||
|
|
font-family: 'Roboto Mono', monospace;
|
|||
|
|
background-color: var(--dark-bg);
|
|||
|
|
color: var(--neon-blue);
|
|||
|
|
overflow: hidden;
|
|||
|
|
height: 100vh;
|
|||
|
|
width: 100vw;
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 桌面背景 */
|
|||
|
|
.desktop-bg {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
background:
|
|||
|
|
linear-gradient(rgba(13, 13, 22, 0.9), rgba(13, 13, 22, 0.7)),
|
|||
|
|
repeating-linear-gradient(0deg, transparent, transparent 2px, var(--grid-color) 2px, var(--grid-color) 3px),
|
|||
|
|
repeating-linear-gradient(90deg, transparent, transparent 2px, var(--grid-color) 2px, var(--grid-color) 3px);
|
|||
|
|
z-index: -2;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.scan-lines {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
background: linear-gradient(to bottom, transparent 50%, var(--scan-line) 51%);
|
|||
|
|
background-size: 100% 4px;
|
|||
|
|
z-index: -1;
|
|||
|
|
animation: scanline 8s linear infinite;
|
|||
|
|
pointer-events: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes scanline {
|
|||
|
|
0% { background-position: 0 0; }
|
|||
|
|
100% { background-position: 0 100%; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 开机动画 */
|
|||
|
|
#boot-screen {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
background: var(--dark-bg);
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
justify-content: center;
|
|||
|
|
align-items: center;
|
|||
|
|
z-index: 10000;
|
|||
|
|
transition: opacity 0.5s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.boot-logo {
|
|||
|
|
font-family: 'Orbitron', sans-serif;
|
|||
|
|
font-size: 3rem;
|
|||
|
|
font-weight: 900;
|
|||
|
|
color: var(--neon-pink);
|
|||
|
|
text-shadow: 0 0 10px var(--neon-pink), 0 0 20px var(--neon-pink), 0 0 40px var(--neon-pink);
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
animation: flicker 2s infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.boot-text {
|
|||
|
|
color: var(--neon-cyan);
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
letter-spacing: 2px;
|
|||
|
|
margin-top: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.boot-progress {
|
|||
|
|
width: 300px;
|
|||
|
|
height: 4px;
|
|||
|
|
background: rgba(255, 255, 255, 0.1);
|
|||
|
|
margin-top: 30px;
|
|||
|
|
position: relative;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.boot-progress-bar {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
height: 100%;
|
|||
|
|
width: 0%;
|
|||
|
|
background: var(--neon-blue);
|
|||
|
|
box-shadow: 0 0 10px var(--neon-blue);
|
|||
|
|
animation: bootProgress 3s ease forwards;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes bootProgress {
|
|||
|
|
to { width: 100%; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes flicker {
|
|||
|
|
0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% { opacity: 1; }
|
|||
|
|
20%, 24%, 55% { opacity: 0.4; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 锁屏界面 */
|
|||
|
|
#lock-screen {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
background: rgba(13, 13, 22, 0.95);
|
|||
|
|
backdrop-filter: blur(10px);
|
|||
|
|
display: none;
|
|||
|
|
flex-direction: column;
|
|||
|
|
justify-content: center;
|
|||
|
|
align-items: center;
|
|||
|
|
z-index: 9000;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.lock-Time {
|
|||
|
|
font-family: 'Orbitron', sans-serif;
|
|||
|
|
font-size: 5rem;
|
|||
|
|
color: var(--neon-pink);
|
|||
|
|
text-shadow: 0 0 20px var(--neon-pink);
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.lock-Date {
|
|||
|
|
font-size: 1.5rem;
|
|||
|
|
color: var(--neon-cyan);
|
|||
|
|
margin-bottom: 30px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.lock-input {
|
|||
|
|
background: rgba(5, 217, 232, 0.1);
|
|||
|
|
border: 1px solid var(--neon-blue);
|
|||
|
|
color: var(--neon-blue);
|
|||
|
|
padding: 12px 20px;
|
|||
|
|
font-family: 'Roboto Mono', monospace;
|
|||
|
|
font-size: 1rem;
|
|||
|
|
width: 300px;
|
|||
|
|
text-align: center;
|
|||
|
|
outline: none;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.lock-input:focus {
|
|||
|
|
box-shadow: 0 0 15px var(--neon-blue);
|
|||
|
|
background: rgba(5, 217, 232, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.lock-buttons {
|
|||
|
|
margin-top: 20px;
|
|||
|
|
display: flex;
|
|||
|
|
gap: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.lock-btn {
|
|||
|
|
padding: 10px 20px;
|
|||
|
|
background: rgba(5, 217, 232, 0.1);
|
|||
|
|
border: 1px solid var(--neon-blue);
|
|||
|
|
color: var(--neon-blue);
|
|||
|
|
cursor: pointer;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
font-family: 'Roboto Mono', monospace;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.lock-btn:hover {
|
|||
|
|
background: var(--neon-blue);
|
|||
|
|
color: var(--dark-bg);
|
|||
|
|
box-shadow: 0 0 15px var(--neon-blue);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.lock-hint {
|
|||
|
|
margin-top: 15px;
|
|||
|
|
color: var(--neon-purple);
|
|||
|
|
font-size: 0.8rem;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 桌面 - 修复显示问题 */
|
|||
|
|
#desktop {
|
|||
|
|
display: none; /* 默认隐藏,通过JS控制显示 */
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
position: relative;
|
|||
|
|
background: var(--dark-bg);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 当桌面显示时,覆盖所有内容 */
|
|||
|
|
#desktop.active {
|
|||
|
|
display: block !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 桌面小部件 */
|
|||
|
|
.desktop-widgets {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 20px;
|
|||
|
|
right: 20px;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 10px;
|
|||
|
|
z-index: 100;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.widget {
|
|||
|
|
background: var(--glass-bg);
|
|||
|
|
border: 1px solid var(--glass-border);
|
|||
|
|
backdrop-filter: blur(10px);
|
|||
|
|
padding: 15px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
min-width: 200px;
|
|||
|
|
box-shadow: 0 0 20px rgba(5, 217, 232, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.widget-title {
|
|||
|
|
font-family: 'Orbitron', sans-serif;
|
|||
|
|
font-size: 0.8rem;
|
|||
|
|
color: var(--neon-cyan);
|
|||
|
|
margin-bottom: 10px;
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.widget-content {
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
color: var(--neon-blue);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.system-monitor-bars {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 5px;
|
|||
|
|
margin-top: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.monitor-bar {
|
|||
|
|
height: 6px;
|
|||
|
|
background: rgba(255, 255, 255, 0.1);
|
|||
|
|
border-radius: 3px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.monitor-bar-fill {
|
|||
|
|
height: 100%;
|
|||
|
|
background: var(--neon-pink);
|
|||
|
|
box-shadow: 0 0 5px var(--neon-pink);
|
|||
|
|
transition: width 0.5s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 桌面图标 */
|
|||
|
|
.desktop-icons {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 20px;
|
|||
|
|
left: 20px;
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(4, 1fr);
|
|||
|
|
gap: 20px;
|
|||
|
|
z-index: 50;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.desktop-icon {
|
|||
|
|
width: 80px;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
padding: 10px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.desktop-icon:hover {
|
|||
|
|
background: rgba(255, 42, 109, 0.2);
|
|||
|
|
box-shadow: 0 0 15px var(--neon-pink);
|
|||
|
|
transform: scale(1.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.desktop-icon i {
|
|||
|
|
font-size: 2rem;
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
color: var(--neon-blue);
|
|||
|
|
text-shadow: 0 0 10px var(--neon-blue);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.desktop-icon span {
|
|||
|
|
font-size: 0.7rem;
|
|||
|
|
color: var(--neon-cyan);
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 任务栏 */
|
|||
|
|
#taskbar {
|
|||
|
|
position: absolute;
|
|||
|
|
bottom: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 50px;
|
|||
|
|
background: var(--glass-bg);
|
|||
|
|
backdrop-filter: blur(10px);
|
|||
|
|
border-top: 1px solid var(--glass-border);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 0 20px;
|
|||
|
|
z-index: 8000;
|
|||
|
|
box-shadow: 0 -5px 20px rgba(0, 0, 0, 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.taskbar-start {
|
|||
|
|
font-family: 'Orbitron', sans-serif;
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: var(--neon-pink);
|
|||
|
|
cursor: pointer;
|
|||
|
|
padding: 8px 15px;
|
|||
|
|
border: 1px solid var(--neon-pink);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
margin-right: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.taskbar-start:hover {
|
|||
|
|
background: var(--neon-pink);
|
|||
|
|
color: var(--dark-bg);
|
|||
|
|
box-shadow: 0 0 15px var(--neon-pink);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.taskbar-apps {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 10px;
|
|||
|
|
flex-grow: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.taskbar-app {
|
|||
|
|
padding: 8px 15px;
|
|||
|
|
background: rgba(5, 217, 232, 0.1);
|
|||
|
|
border: 1px solid rgba(5, 217, 232, 0.3);
|
|||
|
|
color: var(--neon-blue);
|
|||
|
|
cursor: pointer;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
font-size: 0.8rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.taskbar-app:hover {
|
|||
|
|
background: rgba(5, 217, 232, 0.2);
|
|||
|
|
box-shadow: 0 0 10px rgba(5, 217, 232, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.taskbar-app.active {
|
|||
|
|
background: var(--neon-blue);
|
|||
|
|
color: var(--dark-bg);
|
|||
|
|
box-shadow: 0 0 15px var(--neon-blue);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.taskbar-system {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 15px;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.taskbar-icon {
|
|||
|
|
color: var(--neon-cyan);
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.taskbar-icon:hover {
|
|||
|
|
color: var(--neon-pink);
|
|||
|
|
text-shadow: 0 0 10px var(--neon-pink);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.taskbar-clock {
|
|||
|
|
font-family: 'Orbitron', sans-serif;
|
|||
|
|
color: var(--neon-purple);
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
min-width: 80px;
|
|||
|
|
text-align: right;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 开始菜单 */
|
|||
|
|
#start-menu {
|
|||
|
|
position: absolute;
|
|||
|
|
bottom: 50px;
|
|||
|
|
left: 20px;
|
|||
|
|
width: 300px;
|
|||
|
|
background: var(--glass-bg);
|
|||
|
|
backdrop-filter: blur(15px);
|
|||
|
|
border: 1px solid var(--glass-border);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 20px;
|
|||
|
|
display: none;
|
|||
|
|
z-index: 8500;
|
|||
|
|
box-shadow: 0 0 30px rgba(5, 217, 232, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.start-menu-title {
|
|||
|
|
font-family: 'Orbitron', sans-serif;
|
|||
|
|
color: var(--neon-pink);
|
|||
|
|
font-size: 1.2rem;
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.start-menu-apps {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 8px;
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.start-menu-app {
|
|||
|
|
padding: 10px;
|
|||
|
|
background: rgba(255, 255, 255, 0.05);
|
|||
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.start-menu-app:hover {
|
|||
|
|
background: rgba(5, 217, 232, 0.2);
|
|||
|
|
border-color: var(--neon-blue);
|
|||
|
|
box-shadow: 0 0 10px rgba(5, 217, 232, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.start-menu-app i {
|
|||
|
|
width: 20px;
|
|||
|
|
text-align: center;
|
|||
|
|
color: var(--neon-cyan);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.start-menu-options {
|
|||
|
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|||
|
|
padding-top: 15px;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.start-menu-option {
|
|||
|
|
padding: 8px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
color: var(--neon-purple);
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.start-menu-option:hover {
|
|||
|
|
background: rgba(255, 42, 109, 0.2);
|
|||
|
|
box-shadow: 0 0 10px rgba(255, 42, 109, 0.3);
|
|||
|
|
color: var(--neon-pink);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 窗口系统 */
|
|||
|
|
.window {
|
|||
|
|
position: absolute;
|
|||
|
|
background: var(--glass-bg);
|
|||
|
|
backdrop-filter: blur(15px);
|
|||
|
|
border: 1px solid var(--glass-border);
|
|||
|
|
border-radius: 8px 8px 0 0;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
min-width: 300px;
|
|||
|
|
min-height: 200px;
|
|||
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
|
|||
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.window.active {
|
|||
|
|
border-color: var(--neon-blue);
|
|||
|
|
box-shadow: 0 10px 30px rgba(5, 217, 232, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.window-header {
|
|||
|
|
background: rgba(5, 217, 232, 0.1);
|
|||
|
|
padding: 8px 12px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
cursor: move;
|
|||
|
|
border-bottom: 1px solid rgba(5, 217, 232, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.window-title {
|
|||
|
|
font-family: 'Orbitron', sans-serif;
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
color: var(--neon-blue);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.window-controls {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.window-control {
|
|||
|
|
width: 12px;
|
|||
|
|
height: 12px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.window-control.close {
|
|||
|
|
background: var(--neon-pink);
|
|||
|
|
box-shadow: 0 0 5px var(--neon-pink);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.window-control.minimize {
|
|||
|
|
background: var(--neon-cyan);
|
|||
|
|
box-shadow: 0 0 5px var(--neon-cyan);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.window-control.maximize {
|
|||
|
|
background: var(--neon-purple);
|
|||
|
|
box-shadow: 0 0 5px var(--neon-purple);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.window-control:hover {
|
|||
|
|
transform: scale(1.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.window-body {
|
|||
|
|
flex: 1;
|
|||
|
|
padding: 15px;
|
|||
|
|
overflow: auto;
|
|||
|
|
color: var(--neon-blue);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.window-resize {
|
|||
|
|
position: absolute;
|
|||
|
|
right: 0;
|
|||
|
|
bottom: 0;
|
|||
|
|
width: 20px;
|
|||
|
|
height: 20px;
|
|||
|
|
cursor: nwse-resize;
|
|||
|
|
background: linear-gradient(135deg, transparent 50%, var(--neon-blue) 50%);
|
|||
|
|
opacity: 0.5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 终端 */
|
|||
|
|
.terminal {
|
|||
|
|
font-family: 'Roboto Mono', monospace;
|
|||
|
|
background: rgba(0, 0, 0, 0.7);
|
|||
|
|
height: 100%;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.terminal-output {
|
|||
|
|
flex: 1;
|
|||
|
|
padding: 10px;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
white-space: pre-wrap;
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
line-height: 1.4;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.terminal-input-line {
|
|||
|
|
display: flex;
|
|||
|
|
padding: 10px;
|
|||
|
|
background: rgba(5, 217, 232, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.terminal-prompt {
|
|||
|
|
color: var(--neon-green);
|
|||
|
|
margin-right: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.terminal-input {
|
|||
|
|
background: transparent;
|
|||
|
|
border: none;
|
|||
|
|
outline: none;
|
|||
|
|
color: var(--neon-blue);
|
|||
|
|
flex: 1;
|
|||
|
|
font-family: 'Roboto Mono', monospace;
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.terminal .green { color: var(--neon-cyan); }
|
|||
|
|
.terminal .pink { color: var(--neon-pink); }
|
|||
|
|
.terminal .purple { color: var(--neon-purple); }
|
|||
|
|
.terminal .error { color: #ff5555; }
|
|||
|
|
|
|||
|
|
/* 通知系统 */
|
|||
|
|
#notification-area {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 20px;
|
|||
|
|
right: 20px;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 10px;
|
|||
|
|
width: 300px;
|
|||
|
|
z-index: 9500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.notification {
|
|||
|
|
background: var(--glass-bg);
|
|||
|
|
backdrop-filter: blur(15px);
|
|||
|
|
border: 1px solid var(--glass-border);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 15px;
|
|||
|
|
animation: slideIn 0.3s ease;
|
|||
|
|
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
|
|||
|
|
position: relative;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes slideIn {
|
|||
|
|
from {
|
|||
|
|
transform: translateX(100%);
|
|||
|
|
opacity: 0;
|
|||
|
|
}
|
|||
|
|
to {
|
|||
|
|
transform: translateX(0);
|
|||
|
|
opacity: 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.notification-header {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.notification-title {
|
|||
|
|
font-family: 'Orbitron', sans-serif;
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
color: var(--neon-pink);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.notification-close {
|
|||
|
|
cursor: pointer;
|
|||
|
|
color: var(--neon-cyan);
|
|||
|
|
opacity: 0.7;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.notification-close:hover {
|
|||
|
|
opacity: 1;
|
|||
|
|
color: var(--neon-pink);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.notification-body {
|
|||
|
|
font-size: 0.8rem;
|
|||
|
|
color: var(--neon-blue);
|
|||
|
|
line-height: 1.4;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.notification.success { border-left: 3px solid var(--neon-cyan); }
|
|||
|
|
.notification.error { border-left: 3px solid #ff5555; }
|
|||
|
|
.notification.warning { border-left: 3px solid #ffaa00; }
|
|||
|
|
.notification.info { border-left: 3px solid var(--neon-blue); }
|
|||
|
|
|
|||
|
|
/* 右键菜单 */
|
|||
|
|
#context-menu {
|
|||
|
|
position: absolute;
|
|||
|
|
background: var(--glass-bg);
|
|||
|
|
backdrop-filter: blur(15px);
|
|||
|
|
border: 1px solid var(--glass-border);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 8px 0;
|
|||
|
|
display: none;
|
|||
|
|
z-index: 10000;
|
|||
|
|
min-width: 180px;
|
|||
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.context-menu-item {
|
|||
|
|
padding: 10px 15px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 10px;
|
|||
|
|
color: var(--neon-blue);
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.context-menu-item:hover {
|
|||
|
|
background: rgba(5, 217, 232, 0.2);
|
|||
|
|
color: var(--neon-cyan);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.context-menu-item i {
|
|||
|
|
width: 20px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.context-menu-divider {
|
|||
|
|
height: 1px;
|
|||
|
|
background: rgba(255, 255, 255, 0.1);
|
|||
|
|
margin: 5px 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 矩阵雨画布 */
|
|||
|
|
#matrix-canvas {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
z-index: 9999;
|
|||
|
|
display: none;
|
|||
|
|
background: black;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 隐藏类 - 用于灵活控制 */
|
|||
|
|
.hidden {
|
|||
|
|
display: none !important;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<!-- 开机动画 -->
|
|||
|
|
<div id="boot-screen">
|
|||
|
|
<div class="boot-logo">NEON-OS</div>
|
|||
|
|
<div class="boot-text">初始化系统核心模块...</div>
|
|||
|
|
<div class="boot-progress">
|
|||
|
|
<div class="boot-progress-bar"></div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 锁屏界面 -->
|
|||
|
|
<div id="lock-screen">
|
|||
|
|
<div class="lock-Time" id="lock-time">00:00</div>
|
|||
|
|
<div class="lock-Date" id="lock-date">加载中...</div>
|
|||
|
|
<input type="password" class="lock-input" placeholder="输入密码或点击跳过" id="lock-password">
|
|||
|
|
<div class="lock-buttons">
|
|||
|
|
<button class="lock-btn" onclick="unlockSystem()">进入系统</button>
|
|||
|
|
<button class="lock-btn" onclick="skipLockScreen()">跳过</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="lock-hint">提示:直接点击"跳过"即可进入</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 桌面 -->
|
|||
|
|
<div id="desktop" style="display:none;">
|
|||
|
|
<div class="desktop-bg"></div>
|
|||
|
|
<div class="scan-lines"></div>
|
|||
|
|
|
|||
|
|
<!-- 桌面小部件 -->
|
|||
|
|
<div class="desktop-widgets">
|
|||
|
|
<div class="widget">
|
|||
|
|
<div class="widget-title">系统时钟</div>
|
|||
|
|
<div class="widget-content" id="widget-clock">00:00:00</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="widget">
|
|||
|
|
<div class="widget-title">系统状态</div>
|
|||
|
|
<div class="widget-content" id="widget-status">运行中</div>
|
|||
|
|
<div class="system-monitor-bars">
|
|||
|
|
<div class="monitor-bar">
|
|||
|
|
<div class="monitor-bar-fill" id="cpu-bar" style="width: 45%"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="monitor-bar">
|
|||
|
|
<div class="monitor-bar-fill" id="mem-bar" style="width: 62%"></div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="widget">
|
|||
|
|
<div class="widget-title">快速命令</div>
|
|||
|
|
<div class="widget-content" style="cursor: pointer; padding: 5px;">
|
|||
|
|
<div style="margin: 5px 0;" onclick="openTerminal()">打开终端</div>
|
|||
|
|
<div style="margin: 5px 0;" onclick="showNotification('info', '系统信息', 'NEON-OS 版本 1.0.0')">系统信息</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 桌面图标 -->
|
|||
|
|
<div class="desktop-icons">
|
|||
|
|
<div class="desktop-icon" onclick="openApp('file-manager')">
|
|||
|
|
<i class="fas fa-folder"></i>
|
|||
|
|
<span>文件管理器</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="desktop-icon" onclick="openTerminal()">
|
|||
|
|
<i class="fas fa-terminal"></i>
|
|||
|
|
<span>终端</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="desktop-icon" onclick="openApp('browser')">
|
|||
|
|
<i class="fas fa-globe"></i>
|
|||
|
|
<span>浏览器</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="desktop-icon" onclick="openApp('settings')">
|
|||
|
|
<i class="fas fa-cog"></i>
|
|||
|
|
<span>设置中心</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="desktop-icon" onclick="openApp('music-player')">
|
|||
|
|
<i class="fas fa-music"></i>
|
|||
|
|
<span>音乐播放器</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="desktop-icon" onclick="openApp('notepad')">
|
|||
|
|
<i class="fas fa-sticky-note"></i>
|
|||
|
|
<span>便签</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="desktop-icon" onclick="openApp('monitor')">
|
|||
|
|
<i class="fas fa-chart-line"></i>
|
|||
|
|
<span>系统监控</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="desktop-icon" onclick="openApp('about')">
|
|||
|
|
<i class="fas fa-info-circle"></i>
|
|||
|
|
<span>关于系统</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 任务栏 -->
|
|||
|
|
<div id="taskbar">
|
|||
|
|
<div class="taskbar-start" onclick="toggleStartMenu()">
|
|||
|
|
<i class="fas fa-power-off"></i> 开始
|
|||
|
|
</div>
|
|||
|
|
<div class="taskbar-apps" id="taskbar-apps"></div>
|
|||
|
|
<div class="taskbar-system">
|
|||
|
|
<i class="fas fa-bell taskbar-icon" onclick="showNotification('info', '通知中心', '暂无新通知')"></i>
|
|||
|
|
<i class="fas fa-volume-up taskbar-icon" onclick="toggleVolume()"></i>
|
|||
|
|
<div class="taskbar-clock" id="taskbar-clock">00:00</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 开始菜单 -->
|
|||
|
|
<div id="start-menu">
|
|||
|
|
<div class="user-avatar">NE</div>
|
|||
|
|
<div class="start-menu-title">NEON-OS</div>
|
|||
|
|
<div class="start-menu-apps">
|
|||
|
|
<div class="start-menu-app" onclick="openApp('file-manager')">
|
|||
|
|
<i class="fas fa-folder"></i> 文件管理器
|
|||
|
|
</div>
|
|||
|
|
<div class="start-menu-app" onclick="openTerminal()">
|
|||
|
|
<i class="fas fa-terminal"></i> 终端
|
|||
|
|
</div>
|
|||
|
|
<div class="start-menu-app" onclick="openApp('browser')">
|
|||
|
|
<i class="fas fa-globe"></i> 浏览器
|
|||
|
|
</div>
|
|||
|
|
<div class="start-menu-app" onclick="openApp('settings')">
|
|||
|
|
<i class="fas fa-cog"></i> 设置中心
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="start-menu-options">
|
|||
|
|
<div class="start-menu-option" onclick="rebootSystem()">重启系统</div>
|
|||
|
|
<div class="start-menu-option" onclick="logout()">切换用户</div>
|
|||
|
|
<div class="start-menu-option" onclick="shutdown()">关机</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 通知区域 -->
|
|||
|
|
<div id="notification-area"></div>
|
|||
|
|
|
|||
|
|
<!-- 右键菜单 -->
|
|||
|
|
<div id="context-menu">
|
|||
|
|
<div class="context-menu-item" onclick="refreshDesktop()">
|
|||
|
|
<i class="fas fa-sync-alt"></i> 刷新
|
|||
|
|
</div>
|
|||
|
|
<div class="context-menu-item" onclick="openApp('settings')">
|
|||
|
|
<i class="fas fa-cog"></i> 个性化设置
|
|||
|
|
</div>
|
|||
|
|
<div class="context-menu-divider"></div>
|
|||
|
|
<div class="context-menu-item" onclick="openApp('about')">
|
|||
|
|
<i class="fas fa-info-circle"></i> 关于 NEON-OS
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 矩阵雨画布 -->
|
|||
|
|
<canvas id="matrix-canvas"></canvas>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
// 全局状态管理
|
|||
|
|
const state = {
|
|||
|
|
apps: {},
|
|||
|
|
nextZIndex: 100,
|
|||
|
|
settings: {
|
|||
|
|
theme: 'cyberpunk',
|
|||
|
|
volume: 0.7,
|
|||
|
|
wallpaper: 'default',
|
|||
|
|
notifications: true
|
|||
|
|
},
|
|||
|
|
fileSystem: {
|
|||
|
|
root: {
|
|||
|
|
type: 'folder',
|
|||
|
|
children: {
|
|||
|
|
'Documents': {
|
|||
|
|
type: 'folder',
|
|||
|
|
children: {
|
|||
|
|
'readme.txt': { type: 'file', content: '欢迎使用 NEON-OS!\n这是一个赛博朋克风格的操作系统。\n尝试在终端输入 help 查看可用命令。' },
|
|||
|
|
'notes.txt': { type: 'file', content: '我的笔记:\n- 优化render循环\n- 修复拖拽bug\n- 添加更多命令' }
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
'Projects': {
|
|||
|
|
type: 'folder',
|
|||
|
|
children: {
|
|||
|
|
'neon-os.html': { type: 'file', content: '<!DOCTYPE html>\n<!-- 这是我们之前的工作 -->' },
|
|||
|
|
'design.fig': { type: 'file', content: '二进制文件预览:\n[FIGMA设计原型数据]' }
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
'Music': {
|
|||
|
|
type: 'folder',
|
|||
|
|
children: {
|
|||
|
|
'synthwave.mp3': { type: 'file', content: '音频文件' },
|
|||
|
|
'cyberpunk.flac': { type: 'file', content: '无损音频' }
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
'config.json': { type: 'file', content: '{\n "theme": "cyberpunk",\n "notifications": true\n}' }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
currentDirectory: [],
|
|||
|
|
musicPlayer: {
|
|||
|
|
playing: false,
|
|||
|
|
currentTrack: 0,
|
|||
|
|
tracks: [
|
|||
|
|
{ title: '霓虹之夜', artist: 'SynthWave', duration: 180 },
|
|||
|
|
{ title: '数字梦境', artist: 'CyberPunk', duration: 220 },
|
|||
|
|
{ title: '未来都市', artist: 'NeoTokyo', duration: 200 }
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
terminalHistory: [],
|
|||
|
|
matrixRunning: false
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 初始化系统
|
|||
|
|
function initSystem() {
|
|||
|
|
console.log('[SYSTEM] 正在初始化...');
|
|||
|
|
const savedSettings = localStorage.getItem('neon-os-settings');
|
|||
|
|
if (savedSettings) {
|
|||
|
|
try {
|
|||
|
|
const parsed = JSON.parse(savedSettings);
|
|||
|
|
state.settings = { ...state.settings, ...parsed };
|
|||
|
|
console.log('[SYSTEM] 设置已加载');
|
|||
|
|
} catch (e) {
|
|||
|
|
console.log('[SYSTEM] 设置加载失败,使用默认值');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
const bootScreen = document.getElementById('boot-screen');
|
|||
|
|
if (bootScreen) {
|
|||
|
|
bootScreen.style.opacity = '0';
|
|||
|
|
setTimeout(() => {
|
|||
|
|
bootScreen.style.display = 'none';
|
|||
|
|
showLockScreen();
|
|||
|
|
}, 500);
|
|||
|
|
}
|
|||
|
|
}, 2000); // 缩短开机动画时间,加快测试
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 显示锁屏
|
|||
|
|
function showLockScreen() {
|
|||
|
|
console.log('[LOCK] 显示锁屏界面');
|
|||
|
|
const lockScreen = document.getElementById('lock-screen');
|
|||
|
|
if (lockScreen) {
|
|||
|
|
lockScreen.style.display = 'flex';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
updateLockTime();
|
|||
|
|
setInterval(updateLockTime, 1000);
|
|||
|
|
|
|||
|
|
const passwordInput = document.getElementById('lock-password');
|
|||
|
|
const hint = document.querySelector('.lock-hint');
|
|||
|
|
|
|||
|
|
if (passwordInput) {
|
|||
|
|
passwordInput.addEventListener('keydown', (e) => {
|
|||
|
|
if (e.key === 'Enter') {
|
|||
|
|
unlockSystem();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (hint) {
|
|||
|
|
hint.addEventListener('click', skipLockScreen);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateLockTime() {
|
|||
|
|
const now = new Date();
|
|||
|
|
const hours = String(now.getHours()).padStart(2, '0');
|
|||
|
|
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|||
|
|
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|||
|
|
const timeStr = `${hours}:${minutes}:${seconds}`;
|
|||
|
|
|
|||
|
|
const dateStr = now.toLocaleDateString('zh-CN', {
|
|||
|
|
year: 'numeric',
|
|||
|
|
month: 'long',
|
|||
|
|
day: 'numeric',
|
|||
|
|
weekday: 'long'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const lockTime = document.getElementById('lock-time');
|
|||
|
|
const lockDate = document.getElementById('lock-date');
|
|||
|
|
|
|||
|
|
if (lockTime) lockTime.textContent = timeStr;
|
|||
|
|
if (lockDate) lockDate.textContent = dateStr;
|
|||
|
|
|
|||
|
|
const taskbarClock = document.getElementById('taskbar-clock');
|
|||
|
|
if (taskbarClock) {
|
|||
|
|
taskbarClock.textContent = `${hours}:${minutes}`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const widgetClock = document.getElementById('widget-clock');
|
|||
|
|
if (widgetClock) {
|
|||
|
|
widgetClock.textContent = timeStr;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳过锁屏(直接进入)
|
|||
|
|
function skipLockScreen() {
|
|||
|
|
console.log('[LOCK] 跳过锁屏,直接进入系统');
|
|||
|
|
unlockSystem();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解锁系统 - 修复显示问题
|
|||
|
|
function unlockSystem() {
|
|||
|
|
console.log('[UNLOCK] 开始解锁流程');
|
|||
|
|
const lockScreen = document.getElementById('lock-screen');
|
|||
|
|
const desktop = document.getElementById('desktop');
|
|||
|
|
|
|||
|
|
if (!lockScreen || !desktop) {
|
|||
|
|
console.error('[UNLOCK] 必要元素未找到');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 简化解锁流程
|
|||
|
|
lockScreen.style.opacity = '0';
|
|||
|
|
console.log('[UNLOCK] 锁屏透明度设置为0');
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
console.log('[UNLOCK] 隐藏锁屏,显示桌面');
|
|||
|
|
lockScreen.style.display = 'none';
|
|||
|
|
|
|||
|
|
// 显示桌面 - 使用多种方式确保显示
|
|||
|
|
desktop.style.display = 'block';
|
|||
|
|
desktop.classList.remove('hidden');
|
|||
|
|
desktop.classList.add('active');
|
|||
|
|
|
|||
|
|
console.log('[UNLOCK] 桌面display样式:', desktop.style.display);
|
|||
|
|
console.log('[UNLOCK] 桌面classList:', desktop.className);
|
|||
|
|
|
|||
|
|
// 启动桌面动画
|
|||
|
|
try {
|
|||
|
|
startDesktopAnimation();
|
|||
|
|
console.log('[UNLOCK] 桌面动画已启动');
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('[UNLOCK] 桌面动画启动失败:', e);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 显示欢迎通知
|
|||
|
|
setTimeout(() => {
|
|||
|
|
showNotification('success', '系统启动', '欢迎回来,用户');
|
|||
|
|
console.log('[UNLOCK] 已显示欢迎通知');
|
|||
|
|
}, 500);
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
showNotification('info', '提示', '右键点击桌面访问菜单,或使用开始菜单打开应用');
|
|||
|
|
}, 3000);
|
|||
|
|
|
|||
|
|
}, 500);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 桌面动画
|
|||
|
|
function startDesktopAnimation() {
|
|||
|
|
console.log('[ANIM] 开始桌面监控动画');
|
|||
|
|
|
|||
|
|
// CPU/内存监控
|
|||
|
|
setInterval(() => {
|
|||
|
|
if (!document.getElementById('desktop') || document.getElementById('desktop').style.display === 'none') return;
|
|||
|
|
|
|||
|
|
const cpu = Math.floor(Math.random() * 30) + 20;
|
|||
|
|
const mem = Math.floor(Math.random() * 30) + 40;
|
|||
|
|
|
|||
|
|
const cpuBar = document.getElementById('cpu-bar');
|
|||
|
|
const memBar = document.getElementById('mem-bar');
|
|||
|
|
|
|||
|
|
if (cpuBar) cpuBar.style.width = cpu + '%';
|
|||
|
|
if (memBar) memBar.style.width = mem + '%';
|
|||
|
|
}, 2000);
|
|||
|
|
|
|||
|
|
// 随机通知
|
|||
|
|
setInterval(() => {
|
|||
|
|
if (!document.getElementById('desktop') || document.getElementById('desktop').style.display === 'none') return;
|
|||
|
|
|
|||
|
|
if (Math.random() > 0.9 && state.settings.notifications) {
|
|||
|
|
const messages = [
|
|||
|
|
{ type: 'info', title: '系统更新', msg: '检查到可用的系统更新' },
|
|||
|
|
{ type: 'success', title: '备份完成', msg: '所有文件已自动备份' },
|
|||
|
|
{ type: 'warning', title: '磁盘空间', msg: 'C盘剩余空间不足20%' }
|
|||
|
|
];
|
|||
|
|
const msg = messages[Math.floor(Math.random() * messages.length)];
|
|||
|
|
showNotification(msg.type, msg.title, msg.msg);
|
|||
|
|
}
|
|||
|
|
}, 15000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 窗口管理系统(保持原有逻辑,略作简化)
|
|||
|
|
function createWindow(appId, title, icon, initialContent = '') {
|
|||
|
|
if (state.apps[appId]) {
|
|||
|
|
activateWindow(appId);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const window = document.createElement('div');
|
|||
|
|
window.className = 'window';
|
|||
|
|
window.id = `window-${appId}`;
|
|||
|
|
window.style.left = '100px';
|
|||
|
|
window.style.top = '100px';
|
|||
|
|
window.style.width = '600px';
|
|||
|
|
window.style.height = '400px';
|
|||
|
|
window.style.zIndex = state.nextZIndex++;
|
|||
|
|
|
|||
|
|
window.innerHTML = `
|
|||
|
|
<div class="window-header" data-app="${appId}">
|
|||
|
|
<div class="window-title">
|
|||
|
|
<i class="${icon}"></i>
|
|||
|
|
<span>${title}</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="window-controls">
|
|||
|
|
<div class="window-control minimize" onclick="minimizeWindow('${appId}')"></div>
|
|||
|
|
<div class="window-control maximize" onclick="maximizeWindow('${appId}')"></div>
|
|||
|
|
<div class="window-control close" onclick="closeWindow('${appId}')"></div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="window-body" id="body-${appId}">
|
|||
|
|
${initialContent}
|
|||
|
|
</div>
|
|||
|
|
<div class="window-resize" data-app="${appId}"></div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
document.getElementById('desktop').appendChild(window);
|
|||
|
|
|
|||
|
|
state.apps[appId] = {
|
|||
|
|
element: window,
|
|||
|
|
minimized: false,
|
|||
|
|
maximized: false,
|
|||
|
|
originalSize: { width: '600px', height: '400px', left: '100px', top: '100px' }
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
makeDraggable(window);
|
|||
|
|
makeResizable(window);
|
|||
|
|
activateWindow(appId);
|
|||
|
|
addToTaskbar(appId, title, icon);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function makeDraggable(element) {
|
|||
|
|
const header = element.querySelector('.window-header');
|
|||
|
|
let isDragging = false;
|
|||
|
|
let currentX, currentY, initialX, initialY;
|
|||
|
|
|
|||
|
|
header.addEventListener('mousedown', (e) => {
|
|||
|
|
if (e.target.classList.contains('window-control')) return;
|
|||
|
|
isDragging = true;
|
|||
|
|
initialX = e.clientX - element.offsetLeft;
|
|||
|
|
initialY = e.clientY - element.offsetTop;
|
|||
|
|
activateWindow(element.id.replace('window-', ''));
|
|||
|
|
e.preventDefault();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
document.addEventListener('mousemove', (e) => {
|
|||
|
|
if (!isDragging) return;
|
|||
|
|
currentX = e.clientX - initialX;
|
|||
|
|
currentY = e.clientY - initialY;
|
|||
|
|
currentX = Math.max(0, Math.min(currentX, window.innerWidth - element.offsetWidth));
|
|||
|
|
currentY = Math.max(0, Math.min(currentY, window.innerHeight - 50));
|
|||
|
|
element.style.left = currentX + 'px';
|
|||
|
|
element.style.top = currentY + 'px';
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
document.addEventListener('mouseup', () => { isDragging = false; });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function makeResizable(element) {
|
|||
|
|
const resizeHandle = element.querySelector('.window-resize');
|
|||
|
|
let isResizing = false;
|
|||
|
|
let startX, startY, startWidth, startHeight;
|
|||
|
|
|
|||
|
|
resizeHandle.addEventListener('mousedown', (e) => {
|
|||
|
|
isResizing = true;
|
|||
|
|
startX = e.clientX;
|
|||
|
|
startY = e.clientY;
|
|||
|
|
startWidth = element.offsetWidth;
|
|||
|
|
startHeight = element.offsetHeight;
|
|||
|
|
e.preventDefault();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
document.addEventListener('mousemove', (e) => {
|
|||
|
|
if (!isResizing) return;
|
|||
|
|
const newWidth = startWidth + (e.clientX - startX);
|
|||
|
|
const newHeight = startHeight + (e.clientY - startY);
|
|||
|
|
if (newWidth > 300) element.style.width = newWidth + 'px';
|
|||
|
|
if (newHeight > 200) element.style.height = newHeight + 'px';
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
document.addEventListener('mouseup', () => { isResizing = false; });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function activateWindow(appId) {
|
|||
|
|
const app = state.apps[appId];
|
|||
|
|
if (!app) return;
|
|||
|
|
|
|||
|
|
app.element.style.zIndex = state.nextZIndex++;
|
|||
|
|
Object.values(state.apps).forEach(appItem => appItem.element.classList.remove('active'));
|
|||
|
|
app.element.classList.add('active');
|
|||
|
|
|
|||
|
|
if (app.minimized) {
|
|||
|
|
app.element.style.display = 'flex';
|
|||
|
|
app.minimized = false;
|
|||
|
|
updateTaskbarActive(appId);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function minimizeWindow(appId) {
|
|||
|
|
const app = state.apps[appId];
|
|||
|
|
if (!app) return;
|
|||
|
|
app.element.style.display = 'none';
|
|||
|
|
app.minimized = true;
|
|||
|
|
updateTaskbarActive(appId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function maximizeWindow(appId) {
|
|||
|
|
const app = state.apps[appId];
|
|||
|
|
if (!app) return;
|
|||
|
|
|
|||
|
|
if (app.maximized) {
|
|||
|
|
app.element.style.width = app.originalSize.width;
|
|||
|
|
app.element.style.height = app.originalSize.height;
|
|||
|
|
app.element.style.left = app.originalSize.left;
|
|||
|
|
app.element.style.top = app.originalSize.top;
|
|||
|
|
app.maximized = false;
|
|||
|
|
} else {
|
|||
|
|
app.originalSize = {
|
|||
|
|
width: app.element.style.width,
|
|||
|
|
height: app.element.style.height,
|
|||
|
|
left: app.element.style.left,
|
|||
|
|
top: app.element.style.top
|
|||
|
|
};
|
|||
|
|
app.element.style.width = '100%';
|
|||
|
|
app.element.style.height = 'calc(100% - 50px)';
|
|||
|
|
app.element.style.left = '0';
|
|||
|
|
app.element.style.top = '0';
|
|||
|
|
app.maximized = true;
|
|||
|
|
const body = document.getElementById(`body-${appId}`);
|
|||
|
|
if (body) body.style.height = 'calc(100% - 41px)';
|
|||
|
|
}
|
|||
|
|
activateWindow(appId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function closeWindow(appId) {
|
|||
|
|
const app = state.apps[appId];
|
|||
|
|
if (!app) return;
|
|||
|
|
|
|||
|
|
if (appId === 'music-player' && state.musicPlayer.playing) {
|
|||
|
|
state.musicPlayer.playing = false;
|
|||
|
|
stopMusicProgress();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (appId === 'monitor') {
|
|||
|
|
stopMonitorCharts();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
app.element.remove();
|
|||
|
|
delete state.apps[appId];
|
|||
|
|
removeFromTaskbar(appId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 任务栏管理
|
|||
|
|
function addToTaskbar(appId, title, icon) {
|
|||
|
|
const taskbarApps = document.getElementById('taskbar-apps');
|
|||
|
|
const appButton = document.createElement('div');
|
|||
|
|
appButton.className = 'taskbar-app';
|
|||
|
|
appButton.id = `taskbar-${appId}`;
|
|||
|
|
appButton.innerHTML = `<i class="${icon}"></i> ${title}`;
|
|||
|
|
appButton.onclick = () => {
|
|||
|
|
if (state.apps[appId].minimized) {
|
|||
|
|
activateWindow(appId);
|
|||
|
|
} else {
|
|||
|
|
minimizeWindow(appId);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
taskbarApps.appendChild(appButton);
|
|||
|
|
updateTaskbarActive(appId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function removeFromTaskbar(appId) {
|
|||
|
|
const button = document.getElementById(`taskbar-${appId}`);
|
|||
|
|
if (button) button.remove();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateTaskbarActive(appId) {
|
|||
|
|
const buttons = document.querySelectorAll('.taskbar-app');
|
|||
|
|
buttons.forEach(btn => btn.classList.remove('active'));
|
|||
|
|
|
|||
|
|
if (state.apps[appId] && !state.apps[appId].minimized) {
|
|||
|
|
const activeButton = document.getElementById(`taskbar-${appId}`);
|
|||
|
|
if (activeButton) activeButton.classList.add('active');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 应用程序逻辑
|
|||
|
|
function openApp(appId) {
|
|||
|
|
switch(appId) {
|
|||
|
|
case 'file-manager': openFileManager(); break;
|
|||
|
|
case 'browser': openBrowser(); break;
|
|||
|
|
case 'settings': openSettings(); break;
|
|||
|
|
case 'music-player': openMusicPlayer(); break;
|
|||
|
|
case 'notepad': openNotepad(); break;
|
|||
|
|
case 'monitor': openMonitor(); break;
|
|||
|
|
case 'about': openAbout(); break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function openTerminal() {
|
|||
|
|
if (state.apps['terminal']) {
|
|||
|
|
activateWindow('terminal');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const initialContent = `
|
|||
|
|
<div class="terminal">
|
|||
|
|
<div class="terminal-output" id="terminal-output"></div>
|
|||
|
|
<div class="terminal-input-line">
|
|||
|
|
<span class="terminal-prompt">user@neon-os:~$</span>
|
|||
|
|
<input type="text" class="terminal-input" id="terminal-input" placeholder="输入 help 查看可用命令">
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
createWindow('terminal', '终端模拟器', 'fas fa-terminal', initialContent);
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
const input = document.getElementById('terminal-input');
|
|||
|
|
const output = document.getElementById('terminal-output');
|
|||
|
|
|
|||
|
|
if (output) {
|
|||
|
|
output.innerHTML = `
|
|||
|
|
<span class="green">NEON-OS 终端模拟器 v1.0</span>
|
|||
|
|
<br><span class="purple">输入 'help' 查看可用命令</span>
|
|||
|
|
<br>
|
|||
|
|
`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (input) {
|
|||
|
|
input.addEventListener('keydown', (e) => {
|
|||
|
|
if (e.key === 'Enter') {
|
|||
|
|
const command = input.value.trim();
|
|||
|
|
if (command) {
|
|||
|
|
state.terminalHistory.push(command);
|
|||
|
|
executeCommand(command, output);
|
|||
|
|
input.value = '';
|
|||
|
|
output.scrollTop = output.scrollHeight;
|
|||
|
|
}
|
|||
|
|
} else if (e.key === 'ArrowUp') {
|
|||
|
|
if (state.terminalHistory.length > 0) {
|
|||
|
|
input.value = state.terminalHistory[state.terminalHistory.length - 1];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
input.focus();
|
|||
|
|
}
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function executeCommand(command, output) {
|
|||
|
|
const parts = command.split(' ');
|
|||
|
|
const cmd = parts[0].toLowerCase();
|
|||
|
|
const args = parts.slice(1);
|
|||
|
|
|
|||
|
|
const prompt = document.createElement('div');
|
|||
|
|
prompt.innerHTML = `<span class="terminal-prompt">user@neon-os:~$</span> ${command}`;
|
|||
|
|
output.appendChild(prompt);
|
|||
|
|
|
|||
|
|
let response = '';
|
|||
|
|
|
|||
|
|
switch(cmd) {
|
|||
|
|
case 'help':
|
|||
|
|
response = '可用命令:\nhelp - 显示此帮助信息\nclear - 清屏\ndate - 显示当前日期时间\nwhoami - 显示当前用户\nls - 列出文件\ncd - 切换目录\ncat - 查看文件内容\necho - 输出文本\nneofetch - 显示系统信息\nmatrix - 启动矩阵雨特效\ntheme - 切换主题\nreboot - 重启系统\nexit - 关闭终端';
|
|||
|
|
break;
|
|||
|
|
case 'clear':
|
|||
|
|
output.innerHTML = '';
|
|||
|
|
return;
|
|||
|
|
case 'date':
|
|||
|
|
response = new Date().toString();
|
|||
|
|
break;
|
|||
|
|
case 'whoami':
|
|||
|
|
response = 'user@neon-os';
|
|||
|
|
break;
|
|||
|
|
case 'ls':
|
|||
|
|
const currentDir = getCurrentDirectory();
|
|||
|
|
if (typeof currentDir === 'object') {
|
|||
|
|
response = Object.keys(currentDir.children).map(name => {
|
|||
|
|
const item = currentDir.children[name];
|
|||
|
|
return item.type === 'folder' ? `<span class="green">${name}/</span>` : name;
|
|||
|
|
}).join(' ');
|
|||
|
|
} else {
|
|||
|
|
response = '<span class="error">错误:无法读取目录</span>';
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 'cd':
|
|||
|
|
if (args.length === 0) {
|
|||
|
|
state.currentDirectory = [];
|
|||
|
|
response = '已返回根目录';
|
|||
|
|
} else if (args[0] === '..') {
|
|||
|
|
if (state.currentDirectory.length > 0) {
|
|||
|
|
state.currentDirectory.pop();
|
|||
|
|
response = '已返回上级目录';
|
|||
|
|
} else {
|
|||
|
|
response = '已在根目录';
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
const currentDir = getCurrentDirectory();
|
|||
|
|
if (currentDir.children && currentDir.children[args[0]] && currentDir.children[args[0]].type === 'folder') {
|
|||
|
|
state.currentDirectory.push(args[0]);
|
|||
|
|
response = `已切换到 /${state.currentDirectory.join('/')}`;
|
|||
|
|
} else {
|
|||
|
|
response = `<span class="error">目录不存在: ${args[0]}</span>`;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 'cat':
|
|||
|
|
if (args.length === 0) {
|
|||
|
|
response = '<span class="error">用法: cat <文件名></span>';
|
|||
|
|
} else {
|
|||
|
|
const currentDir = getCurrentDirectory();
|
|||
|
|
if (currentDir.children && currentDir.children[args[0]] && currentDir.children[args[0]].type === 'file') {
|
|||
|
|
const content = currentDir.children[args[0]].content;
|
|||
|
|
response = `<pre>${content}</pre>`;
|
|||
|
|
} else {
|
|||
|
|
response = `<span class="error">文件不存在: ${args[0]}</span>`;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 'echo':
|
|||
|
|
response = args.join(' ');
|
|||
|
|
break;
|
|||
|
|
case 'neofetch':
|
|||
|
|
response = '<span style="color: #ff2a6d; font-weight: bold;">NEON-OS</span> 版本 1.0.0\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n<span style="color: #05d9e8;">内核</span>: CyberKernel 2.6.18\n<span style="color: #d100d1;">架构</span>: WebAssembly x86_64\n<span style="color: #00ff9f;">Shell</span>: NeonTerminal v1.0\n<span style="color: #ff2a6d;">主题</span>: ' + state.settings.theme + '\n<span style="color: #05d9e8;">内存占用</span>: ' + (Math.floor(Math.random() * 30 + 20)) + '% / 16GB\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
|
|||
|
|
break;
|
|||
|
|
case 'matrix':
|
|||
|
|
if (!state.matrixRunning) {
|
|||
|
|
startMatrixRain();
|
|||
|
|
response = '<span class="green">矩阵雨已启动... 输入任意命令或刷新页面退出</span>';
|
|||
|
|
} else {
|
|||
|
|
response = '<span class="error">矩阵雨已在运行</span>';
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case 'theme':
|
|||
|
|
const themes = ['cyberpunk', 'retro', 'minimal', 'dark', 'light'];
|
|||
|
|
const nextTheme = themes[(themes.indexOf(state.settings.theme) + 1) % themes.length];
|
|||
|
|
state.settings.theme = nextTheme;
|
|||
|
|
saveSettings();
|
|||
|
|
applyTheme();
|
|||
|
|
response = '<span class="green">主题已切换至: ' + nextTheme + '</span>';
|
|||
|
|
break;
|
|||
|
|
case 'reboot':
|
|||
|
|
response = '<span class="purple">系统将在3秒后重启...</span>';
|
|||
|
|
setTimeout(rebootSystem, 3000);
|
|||
|
|
break;
|
|||
|
|
case 'exit':
|
|||
|
|
closeWindow('terminal');
|
|||
|
|
return;
|
|||
|
|
case 'mkdir':
|
|||
|
|
case 'touch':
|
|||
|
|
case 'rm':
|
|||
|
|
response = '<span class="error">只读文件系统: ' + cmd + ' 命令已被禁用</span>';
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
if (command.trim() !== '') {
|
|||
|
|
response = '<span class="error">命令未找到: ' + cmd + '</span>';
|
|||
|
|
} else {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (response) {
|
|||
|
|
const responseDiv = document.createElement('div');
|
|||
|
|
responseDiv.style.marginTop = '5px';
|
|||
|
|
responseDiv.innerHTML = response;
|
|||
|
|
output.appendChild(responseDiv);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (response.length > 50) {
|
|||
|
|
const lastChild = output.lastChild;
|
|||
|
|
if (lastChild) {
|
|||
|
|
lastChild.classList.add('typewriter');
|
|||
|
|
setTimeout(() => lastChild.classList.remove('typewriter'), 2000);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function getCurrentDirectory() {
|
|||
|
|
let current = state.fileSystem.root;
|
|||
|
|
for (let dir of state.currentDirectory) {
|
|||
|
|
if (current.children && current.children[dir]) {
|
|||
|
|
current = current.children[dir];
|
|||
|
|
} else {
|
|||
|
|
return state.fileSystem.root;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return current;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function openFileManager() {
|
|||
|
|
if (state.apps['file-manager']) {
|
|||
|
|
activateWindow('file-manager');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const content = `
|
|||
|
|
<div class="file-manager">
|
|||
|
|
<div class="fm-sidebar">
|
|||
|
|
<div style="color: var(--neon-cyan); margin-bottom: 10px; font-family: 'Orbitron', sans-serif;">位置</div>
|
|||
|
|
<div style="cursor: pointer; margin-bottom: 8px;" onclick="resetFMPath()">🏠 根目录</div>
|
|||
|
|
<div style="cursor: pointer; margin-bottom: 8px;" onclick="setFMPath(['Documents'])">📁 Documents</div>
|
|||
|
|
<div style="cursor: pointer; margin-bottom: 8px;" onclick="setFMPath(['Projects'])">📁 Projects</div>
|
|||
|
|
<div style="cursor: pointer; margin-bottom: 8px;" onclick="setFMPath(['Music'])">📁 Music</div>
|
|||
|
|
<div style="margin-top: 20px; padding-top: 10px; border-top: 1px solid rgba(5, 217, 232, 0.2);">
|
|||
|
|
<div style="font-size: 0.7rem; color: var(--neon-purple);">
|
|||
|
|
当前: /${state.currentDirectory.join('/')}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="fm-main" id="fm-main"></div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
createWindow('file-manager', '文件管理器', 'fas fa-folder', content);
|
|||
|
|
|
|||
|
|
setTimeout(() => renderFileManager(), 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function renderFileManager() {
|
|||
|
|
const fmMain = document.getElementById('fm-main');
|
|||
|
|
if (!fmMain) return;
|
|||
|
|
|
|||
|
|
const current = getCurrentDirectory();
|
|||
|
|
let html = '';
|
|||
|
|
|
|||
|
|
if (state.currentDirectory.length > 0) {
|
|||
|
|
html += `
|
|||
|
|
<div class="fm-item folder" onclick="goUpDirectory()">
|
|||
|
|
<i class="fas fa-level-up-alt"></i>
|
|||
|
|
<span>上一级</span>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (current.children) {
|
|||
|
|
Object.entries(current.children).forEach(([name, item]) => {
|
|||
|
|
const iconClass = item.type === 'folder' ? 'fas fa-folder' : 'fas fa-file-alt';
|
|||
|
|
const itemClass = item.type === 'folder' ? 'folder' : 'file';
|
|||
|
|
html += `
|
|||
|
|
<div class="fm-item ${itemClass}" oncontextmenu="showFMContextMenu(event, '${name}', '${item.type}')" onclick="openFMItem('${name}', '${item.type}')">
|
|||
|
|
<i class="${iconClass}"></i>
|
|||
|
|
<span>${name}</span>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
fmMain.innerHTML = html;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function setFMPath(path) {
|
|||
|
|
state.currentDirectory = path;
|
|||
|
|
setTimeout(() => renderFileManager(), 50);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function resetFMPath() {
|
|||
|
|
state.currentDirectory = [];
|
|||
|
|
setTimeout(() => renderFileManager(), 50);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function goUpDirectory() {
|
|||
|
|
if (state.currentDirectory.length > 0) {
|
|||
|
|
state.currentDirectory.pop();
|
|||
|
|
setTimeout(() => renderFileManager(), 50);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function openFMItem(name, type) {
|
|||
|
|
if (type === 'folder') {
|
|||
|
|
state.currentDirectory.push(name);
|
|||
|
|
setTimeout(() => renderFileManager(), 50);
|
|||
|
|
} else {
|
|||
|
|
const current = getCurrentDirectory();
|
|||
|
|
if (current.children[name]) {
|
|||
|
|
showNotification('info', '文件预览', '文件: ' + name + ' \\n内容已显示在终端(使用 cat 命令)');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function showFMContextMenu(event, name, type) {
|
|||
|
|
event.preventDefault();
|
|||
|
|
event.stopPropagation();
|
|||
|
|
|
|||
|
|
const contextMenu = document.getElementById('context-menu');
|
|||
|
|
contextMenu.innerHTML = `
|
|||
|
|
<div class="context-menu-item" onclick="showNotification('info', '属性', '文件: ${name} \\n类型: ${type} \\n大小: ' + (Math.floor(Math.random() * 1000)) + 'KB')">
|
|||
|
|
<i class="fas fa-info-circle"></i> 属性
|
|||
|
|
</div>
|
|||
|
|
<div class="context-menu-item" onclick="showNotification('warning', '权限', '该操作需要管理员权限')">
|
|||
|
|
<i class="fas fa-lock"></i> 权限管理
|
|||
|
|
</div>
|
|||
|
|
${type === 'file' ? `
|
|||
|
|
<div class="context-menu-item" onclick="showNotification('info', '编辑', '在终端使用 cat 查看内容')">
|
|||
|
|
<i class="fas fa-edit"></i> 编辑
|
|||
|
|
</div>
|
|||
|
|
` : ''}
|
|||
|
|
<div class="context-menu-divider"></div>
|
|||
|
|
<div class="context-menu-item" onclick="showNotification('error', '错误', '只读文件系统:无法删除')">
|
|||
|
|
<i class="fas fa-trash"></i> 删除
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
contextMenu.style.display = 'block';
|
|||
|
|
contextMenu.style.left = event.pageX + 'px';
|
|||
|
|
contextMenu.style.top = event.pageY + 'px';
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
document.addEventListener('click', closeContextMenuOnce);
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function openBrowser() {
|
|||
|
|
if (state.apps['browser']) {
|
|||
|
|
activateWindow('browser');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const content = `
|
|||
|
|
<div class="browser">
|
|||
|
|
<div class="browser-toolbar">
|
|||
|
|
<input type="text" class="browser-url" id="browser-url" value="https://example.com" placeholder="输入URL...">
|
|||
|
|
<button class="browser-btn" onclick="navigateBrowser()">前往</button>
|
|||
|
|
<button class="browser-btn" onclick="refreshBrowser()">刷新</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="browser-content" id="browser-content">
|
|||
|
|
<iframe src="https://example.com"></iframe>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
createWindow('browser', 'Web浏览器', 'fas fa-globe', content);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function navigateBrowser() {
|
|||
|
|
const urlInput = document.getElementById('browser-url');
|
|||
|
|
const content = document.getElementById('browser-content');
|
|||
|
|
if (urlInput && content) {
|
|||
|
|
let url = urlInput.value;
|
|||
|
|
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|||
|
|
url = 'https://' + url;
|
|||
|
|
}
|
|||
|
|
content.innerHTML = '<iframe src="' + url + '"></iframe>';
|
|||
|
|
showNotification('info', '浏览器', '正在访问: ' + url);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function refreshBrowser() {
|
|||
|
|
const content = document.getElementById('browser-content');
|
|||
|
|
if (content) {
|
|||
|
|
const iframe = content.querySelector('iframe');
|
|||
|
|
if (iframe) {
|
|||
|
|
iframe.src = iframe.src;
|
|||
|
|
showNotification('info', '浏览器', '页面已刷新');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function openSettings() {
|
|||
|
|
if (state.apps['settings']) {
|
|||
|
|
activateWindow('settings');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const content = `
|
|||
|
|
<div class="settings">
|
|||
|
|
<div class="setting-group">
|
|||
|
|
<h3>主题设置</h3>
|
|||
|
|
<div class="setting-item">
|
|||
|
|
<span class="setting-label">当前主题: ${state.settings.theme}</span>
|
|||
|
|
<div class="setting-control">
|
|||
|
|
<button onclick="changeTheme()">切换主题</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-item">
|
|||
|
|
<span class="setting-label">壁纸风格</span>
|
|||
|
|
<div class="setting-control">
|
|||
|
|
<button onclick="changeWallpaper()">更换壁纸</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-group">
|
|||
|
|
<h3>系统设置</h3>
|
|||
|
|
<div class="setting-item">
|
|||
|
|
<span class="setting-label">音量: ${Math.round(state.settings.volume * 100)}%</span>
|
|||
|
|
<div class="setting-control">
|
|||
|
|
<input type="range" min="0" max="100" value="${state.settings.volume * 100}"
|
|||
|
|
oninput="setVolume(this.value)">
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-item">
|
|||
|
|
<span class="setting-label">启用通知</span>
|
|||
|
|
<div class="setting-control">
|
|||
|
|
<button onclick="toggleNotifications()">${state.settings.notifications ? '禁用' : '启用'}</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-group">
|
|||
|
|
<h3>隐私与安全</h3>
|
|||
|
|
<div class="setting-item">
|
|||
|
|
<span class="setting-label">清除本地数据</span>
|
|||
|
|
<div class="setting-control">
|
|||
|
|
<button onclick="clearData()">清除</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-item">
|
|||
|
|
<span class="setting-label">导出配置</span>
|
|||
|
|
<div class="setting-control">
|
|||
|
|
<button onclick="exportConfig()">导出</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
createWindow('settings', '设置中心', 'fas fa-cog', content);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function changeTheme() {
|
|||
|
|
const themes = ['cyberpunk', 'retro', 'minimal', 'dark', 'light'];
|
|||
|
|
const currentIndex = themes.indexOf(state.settings.theme);
|
|||
|
|
const nextIndex = (currentIndex + 1) % themes.length;
|
|||
|
|
state.settings.theme = themes[nextIndex];
|
|||
|
|
saveSettings();
|
|||
|
|
applyTheme();
|
|||
|
|
showNotification('success', '主题切换', '已切换至: ' + themes[nextIndex]);
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
if (state.apps['settings']) {
|
|||
|
|
closeWindow('settings');
|
|||
|
|
openSettings();
|
|||
|
|
}
|
|||
|
|
}, 500);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function changeWallpaper() {
|
|||
|
|
const wallpapers = ['default', 'neon', 'grid', 'city', 'matrix'];
|
|||
|
|
const currentWallpaper = state.settings.wallpaper;
|
|||
|
|
const nextIndex = (wallpapers.indexOf(currentWallpaper) + 1) % wallpapers.length;
|
|||
|
|
state.settings.wallpaper = wallpapers[nextIndex];
|
|||
|
|
saveSettings();
|
|||
|
|
applyWallpaper();
|
|||
|
|
showNotification('success', '壁纸更换', '已更换为: ' + wallpapers[nextIndex]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function setVolume(value) {
|
|||
|
|
state.settings.volume = value / 100;
|
|||
|
|
saveSettings();
|
|||
|
|
if (state.apps['settings']) {
|
|||
|
|
const label = document.querySelector('.setting-item span');
|
|||
|
|
if (label) {
|
|||
|
|
label.textContent = '音量: ' + value + '%';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function toggleNotifications() {
|
|||
|
|
state.settings.notifications = !state.settings.notifications;
|
|||
|
|
saveSettings();
|
|||
|
|
showNotification('info', '通知设置', '通知已' + (state.settings.notifications ? '启用' : '禁用'));
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
if (state.apps['settings']) {
|
|||
|
|
closeWindow('settings');
|
|||
|
|
openSettings();
|
|||
|
|
}
|
|||
|
|
}, 500);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function clearData() {
|
|||
|
|
if (confirm('确定要清除所有本地数据吗?这将重置所有设置。')) {
|
|||
|
|
localStorage.removeItem('neon-os-settings');
|
|||
|
|
showNotification('success', '数据清除', '本地数据已清除');
|
|||
|
|
setTimeout(rebootSystem, 1000);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function exportConfig() {
|
|||
|
|
const config = JSON.stringify(state.settings, null, 2);
|
|||
|
|
showNotification('info', '导出配置', '配置信息已复制到终端(模拟操作)\\n\\n' + config);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function openMusicPlayer() {
|
|||
|
|
if (state.apps['music-player']) {
|
|||
|
|
activateWindow('music-player');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const content = `
|
|||
|
|
<div class="music-player">
|
|||
|
|
<div class="album-cover">
|
|||
|
|
<i class="fas fa-music"></i>
|
|||
|
|
</div>
|
|||
|
|
<div class="music-info">
|
|||
|
|
<div class="music-title" id="music-title">${state.musicPlayer.tracks[state.musicPlayer.currentTrack].title}</div>
|
|||
|
|
<div class="music-artist" id="music-artist">${state.musicPlayer.tracks[state.musicPlayer.currentTrack].artist}</div>
|
|||
|
|
<div class="progress-bar" onclick="seekMusic(event)">
|
|||
|
|
<div class="progress-fill" id="music-progress"></div>
|
|||
|
|
</div>
|
|||
|
|
<div style="display: flex; justify-content: space-between; margin-top: 5px; font-size: 0.7rem; color: var(--neon-purple);">
|
|||
|
|
<span id="current-time">0:00</span>
|
|||
|
|
<span id="total-time">${formatTime(state.musicPlayer.tracks[state.musicPlayer.currentTrack].duration)}</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="audio-visualizer" id="audio-visualizer"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="music-controls">
|
|||
|
|
<div class="music-control" onclick="prevTrack()"><i class="fas fa-step-backward"></i></div>
|
|||
|
|
<div class="music-control" id="play-pause-btn" onclick="togglePlay()"><i class="fas fa-play"></i></div>
|
|||
|
|
<div class="music-control" onclick="nextTrack()"><i class="fas fa-step-forward"></i></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="volume-control">
|
|||
|
|
<i class="fas fa-volume-up"></i>
|
|||
|
|
<input type="range" min="0" max="100" value="${state.settings.volume * 100}" oninput="setTrackVolume(this.value)">
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
createWindow('music-player', '音乐播放器', 'fas fa-music', content);
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
if (!state.musicPlayer.playing) {
|
|||
|
|
createAudioVisualizer();
|
|||
|
|
}
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function togglePlay() {
|
|||
|
|
state.musicPlayer.playing = !state.musicPlayer.playing;
|
|||
|
|
const btn = document.getElementById('play-pause-btn');
|
|||
|
|
|
|||
|
|
if (btn) {
|
|||
|
|
btn.innerHTML = state.musicPlayer.playing ? '<i class="fas fa-pause"></i>' : '<i class="fas fa-play"></i>';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (state.musicPlayer.playing) {
|
|||
|
|
showNotification('info', '音乐播放器', '正在播放: ' + state.musicPlayer.tracks[state.musicPlayer.currentTrack].title);
|
|||
|
|
startMusicProgress();
|
|||
|
|
} else {
|
|||
|
|
showNotification('info', '音乐播放器', '已暂停');
|
|||
|
|
stopMusicProgress();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function prevTrack() {
|
|||
|
|
const currentIndex = state.musicPlayer.currentTrack;
|
|||
|
|
state.musicPlayer.currentTrack = currentIndex > 0 ? currentIndex - 1 : state.musicPlayer.tracks.length - 1;
|
|||
|
|
updateTrackInfo();
|
|||
|
|
showNotification('info', '音乐切换', '播放: ' + state.musicPlayer.tracks[state.musicPlayer.currentTrack].title);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function nextTrack() {
|
|||
|
|
const currentIndex = state.musicPlayer.currentTrack;
|
|||
|
|
state.musicPlayer.currentTrack = (currentIndex + 1) % state.musicPlayer.tracks.length;
|
|||
|
|
updateTrackInfo();
|
|||
|
|
showNotification('info', '音乐切换', '播放: ' + state.musicPlayer.tracks[state.musicPlayer.currentTrack].title);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateTrackInfo() {
|
|||
|
|
const track = state.musicPlayer.tracks[state.musicPlayer.currentTrack];
|
|||
|
|
const titleEl = document.getElementById('music-title');
|
|||
|
|
const artistEl = document.getElementById('music-artist');
|
|||
|
|
const totalTimeEl = document.getElementById('total-time');
|
|||
|
|
|
|||
|
|
if (titleEl) titleEl.textContent = track.title;
|
|||
|
|
if (artistEl) artistEl.textContent = track.artist;
|
|||
|
|
if (totalTimeEl) totalTimeEl.textContent = formatTime(track.duration);
|
|||
|
|
|
|||
|
|
const progress = document.getElementById('music-progress');
|
|||
|
|
if (progress) progress.style.width = '0%';
|
|||
|
|
|
|||
|
|
if (state.musicPlayer.playing) {
|
|||
|
|
stopMusicProgress();
|
|||
|
|
setTimeout(startMusicProgress, 100);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function startMusicProgress() {
|
|||
|
|
stopMusicProgress();
|
|||
|
|
const track = state.musicPlayer.tracks[state.musicPlayer.currentTrack];
|
|||
|
|
const progress = document.getElementById('music-progress');
|
|||
|
|
const currentTimeEl = document.getElementById('current-time');
|
|||
|
|
|
|||
|
|
if (!progress || !currentTimeEl) return;
|
|||
|
|
|
|||
|
|
let currentSeconds = 0;
|
|||
|
|
|
|||
|
|
state.musicPlayer.progressInterval = setInterval(() => {
|
|||
|
|
currentSeconds++;
|
|||
|
|
if (currentSeconds >= track.duration) {
|
|||
|
|
nextTrack();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const percentage = (currentSeconds / track.duration) * 100;
|
|||
|
|
progress.style.width = percentage + '%';
|
|||
|
|
currentTimeEl.textContent = formatTime(currentSeconds);
|
|||
|
|
|
|||
|
|
updateVisualizer();
|
|||
|
|
}, 1000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function stopMusicProgress() {
|
|||
|
|
if (state.musicPlayer.progressInterval) {
|
|||
|
|
clearInterval(state.musicPlayer.progressInterval);
|
|||
|
|
state.musicPlayer.progressInterval = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function seekMusic(event) {
|
|||
|
|
if (state.musicPlayer.playing) {
|
|||
|
|
const bar = event.currentTarget;
|
|||
|
|
const rect = bar.getBoundingClientRect();
|
|||
|
|
const clickX = event.clientX - rect.left;
|
|||
|
|
const percentage = clickX / rect.width;
|
|||
|
|
const track = state.musicPlayer.tracks[state.musicPlayer.currentTrack];
|
|||
|
|
const newSeconds = Math.floor(percentage * track.duration);
|
|||
|
|
|
|||
|
|
const progress = document.getElementById('music-progress');
|
|||
|
|
const currentTimeEl = document.getElementById('current-time');
|
|||
|
|
|
|||
|
|
if (progress && currentTimeEl) {
|
|||
|
|
progress.style.width = (percentage * 100) + '%';
|
|||
|
|
currentTimeEl.textContent = formatTime(newSeconds);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function setTrackVolume(value) {
|
|||
|
|
state.settings.volume = value / 100;
|
|||
|
|
saveSettings();
|
|||
|
|
showNotification('info', '音量调整', '当前音量: ' + value + '%');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function createAudioVisualizer() {
|
|||
|
|
const visualizer = document.getElementById('audio-visualizer');
|
|||
|
|
if (!visualizer) return;
|
|||
|
|
|
|||
|
|
visualizer.innerHTML = '';
|
|||
|
|
for (let i = 0; i < 20; i++) {
|
|||
|
|
const bar = document.createElement('div');
|
|||
|
|
bar.className = 'audio-bar';
|
|||
|
|
bar.style.height = Math.random() * 20 + 5 + 'px';
|
|||
|
|
visualizer.appendChild(bar);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateVisualizer() {
|
|||
|
|
const visualizer = document.getElementById('audio-visualizer');
|
|||
|
|
if (!visualizer) return;
|
|||
|
|
|
|||
|
|
if (state.musicPlayer.playing) {
|
|||
|
|
const bars = visualizer.children;
|
|||
|
|
for (let bar of bars) {
|
|||
|
|
bar.style.height = Math.random() * 30 + 10 + 'px';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function formatTime(seconds) {
|
|||
|
|
const mins = Math.floor(seconds / 60);
|
|||
|
|
const secs = seconds % 60;
|
|||
|
|
return mins + ':' + (secs < 10 ? '0' : '') + secs;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function openNotepad() {
|
|||
|
|
if (state.apps['notepad']) {
|
|||
|
|
activateWindow('notepad');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const savedContent = localStorage.getItem('neon-os-notepad') || '';
|
|||
|
|
|
|||
|
|
const content = `
|
|||
|
|
<div class="notepad">
|
|||
|
|
<div class="notepad-toolbar">
|
|||
|
|
<button class="notepad-btn" onclick="saveNotepad()">保存</button>
|
|||
|
|
<button class="notepad-btn" onclick="clearNotepad()">清空</button>
|
|||
|
|
<button class="notepad-btn" onclick="exportNotepad()">导出</button>
|
|||
|
|
</div>
|
|||
|
|
<textarea class="notepad-editor" id="notepad-editor" placeholder="输入您的笔记...">${savedContent}</textarea>
|
|||
|
|
<div class="notepad-status" id="notepad-status">已保存至本地存储</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
createWindow('notepad', '便签', 'fas fa-sticky-note', content);
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
const editor = document.getElementById('notepad-editor');
|
|||
|
|
if (editor) {
|
|||
|
|
editor.addEventListener('input', () => {
|
|||
|
|
localStorage.setItem('neon-os-notepad', editor.value);
|
|||
|
|
updateNotepadStatus('已自动保存');
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function saveNotepad() {
|
|||
|
|
const editor = document.getElementById('notepad-editor');
|
|||
|
|
if (editor) {
|
|||
|
|
localStorage.setItem('neon-os-notepad', editor.value);
|
|||
|
|
updateNotepadStatus('保存成功!');
|
|||
|
|
showNotification('success', '便签', '笔记已保存');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function clearNotepad() {
|
|||
|
|
const editor = document.getElementById('notepad-editor');
|
|||
|
|
if (editor && confirm('确定要清空所有内容吗?')) {
|
|||
|
|
editor.value = '';
|
|||
|
|
localStorage.removeItem('neon-os-notepad');
|
|||
|
|
updateNotepadStatus('已清空');
|
|||
|
|
showNotification('success', '便签', '内容已清空');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function exportNotepad() {
|
|||
|
|
const editor = document.getElementById('notepad-editor');
|
|||
|
|
if (editor) {
|
|||
|
|
const content = editor.value;
|
|||
|
|
showNotification('info', '导出', '笔记内容已复制到剪贴板(模拟操作)');
|
|||
|
|
navigator.clipboard.writeText(content).catch(() => {});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateNotepadStatus(message) {
|
|||
|
|
const status = document.getElementById('notepad-status');
|
|||
|
|
if (status) {
|
|||
|
|
status.textContent = message;
|
|||
|
|
setTimeout(() => {
|
|||
|
|
status.textContent = '已保存至本地存储';
|
|||
|
|
}, 2000);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function openMonitor() {
|
|||
|
|
if (state.apps['monitor']) {
|
|||
|
|
activateWindow('monitor');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const content = `
|
|||
|
|
<div class="monitor" id="monitor-content">
|
|||
|
|
<div class="monitor-item">
|
|||
|
|
<div class="monitor-item-title">CPU 使用率</div>
|
|||
|
|
<div class="monitor-chart" id="cpu-chart"></div>
|
|||
|
|
<div class="monitor-stats" id="cpu-stats">核心: 4 | 频率: 3.2GHz | 负载: 42%</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="monitor-item">
|
|||
|
|
<div class="monitor-item-title">内存使用</div>
|
|||
|
|
<div class="monitor-chart" id="mem-chart"></div>
|
|||
|
|
<div class="monitor-stats" id="mem-stats">总共: 16GB | 已用: 9.8GB | 可用: 6.2GB</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="monitor-item">
|
|||
|
|
<div class="monitor-item-title">磁盘 I/O</div>
|
|||
|
|
<div class="monitor-chart" id="disk-chart"></div>
|
|||
|
|
<div class="monitor-stats" id="disk-stats">读取: 12MB/s | 写入: 8MB/s</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="monitor-item">
|
|||
|
|
<div class="monitor-item-title">网络状态</div>
|
|||
|
|
<div class="monitor-chart" id="network-chart"></div>
|
|||
|
|
<div class="monitor-stats" id="network-stats">下载: 45Mbps | 上传: 12Mbps</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
createWindow('monitor', '系统监控', 'fas fa-chart-line', content);
|
|||
|
|
|
|||
|
|
setTimeout(() => startMonitorUpdate(), 500);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function startMonitorUpdate() {
|
|||
|
|
stopMonitorCharts();
|
|||
|
|
|
|||
|
|
if (!state.apps['monitor']) return;
|
|||
|
|
|
|||
|
|
state.monitorInterval = setInterval(() => {
|
|||
|
|
if (!state.apps['monitor']) {
|
|||
|
|
stopMonitorCharts();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
updateMonitorChart('cpu-chart', Math.floor(Math.random() * 40) + 30, 'cpu-stats',
|
|||
|
|
'核心: 4 | 频率: 3.2GHz | 负载: ' + (Math.floor(Math.random() * 20) + 30) + '%');
|
|||
|
|
|
|||
|
|
updateMonitorChart('mem-chart', Math.floor(Math.random() * 30) + 40, 'mem-stats',
|
|||
|
|
'总共: 16GB | 已用: ' + (Math.random() * 4 + 9).toFixed(1) + 'GB | 可用: ' + (Math.random() * 2 + 4).toFixed(1) + 'GB');
|
|||
|
|
|
|||
|
|
updateMonitorChart('disk-chart', Math.floor(Math.random() * 30) + 20, 'disk-stats',
|
|||
|
|
'读取: ' + (Math.floor(Math.random() * 20) + 5) + 'MB/s | 写入: ' + (Math.floor(Math.random() * 15) + 3) + 'MB/s');
|
|||
|
|
|
|||
|
|
updateMonitorChart('network-chart', Math.floor(Math.random() * 30) + 20, 'network-stats',
|
|||
|
|
'下载: ' + (Math.floor(Math.random() * 50) + 10) + 'Mbps | 上传: ' + (Math.floor(Math.random() * 20) + 5) + 'Mbps');
|
|||
|
|
}, 2000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateMonitorChart(chartId, value, statsId, statsText) {
|
|||
|
|
const chart = document.getElementById(chartId);
|
|||
|
|
const stats = document.getElementById(statsId);
|
|||
|
|
|
|||
|
|
if (chart && stats) {
|
|||
|
|
chart.innerHTML = '';
|
|||
|
|
const barContainer = document.createElement('div');
|
|||
|
|
barContainer.className = 'monitor-bar-chart';
|
|||
|
|
|
|||
|
|
for (let i = 0; i < 8; i++) {
|
|||
|
|
const barItem = document.createElement('div');
|
|||
|
|
barItem.className = 'monitor-bar-item';
|
|||
|
|
const randomValue = Math.random() * 30 + value - 15;
|
|||
|
|
barItem.style.height = Math.max(5, randomValue) + '%';
|
|||
|
|
barContainer.appendChild(barItem);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
chart.appendChild(barContainer);
|
|||
|
|
|
|||
|
|
if (stats) {
|
|||
|
|
stats.textContent = statsText;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function stopMonitorCharts() {
|
|||
|
|
if (state.monitorInterval) {
|
|||
|
|
clearInterval(state.monitorInterval);
|
|||
|
|
state.monitorInterval = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function openAbout() {
|
|||
|
|
if (state.apps['about']) {
|
|||
|
|
activateWindow('about');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const content = `
|
|||
|
|
<div style="padding: 20px; line-height: 1.6; color: var(--neon-blue);">
|
|||
|
|
<div style="text-align: center; margin-bottom: 20px;">
|
|||
|
|
<h2 style="color: var(--neon-pink); font-family: 'Orbitron', sans-serif; margin-bottom: 10px;">NEON-OS</h2>
|
|||
|
|
<p style="color: var(--neon-cyan);">赛博朋克未来操作系统</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div style="background: rgba(0,0,0,0.3); padding: 15px; border-radius: 8px; margin-bottom: 15px;">
|
|||
|
|
<h3 style="color: var(--neon-pink); margin-bottom: 8px;">系统规格</h3>
|
|||
|
|
<p>版本: <span class="highlight">1.0.0</span></p>
|
|||
|
|
<p>架构: WebAssembly/JS</p>
|
|||
|
|
<p>构建日期: 2024</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div style="background: rgba(0,0,0,0.3); padding: 15px; border-radius: 8px; margin-bottom: 15px;">
|
|||
|
|
<h3 style="color: var(--neon-pink); margin-bottom: 8px;">功能特性</h3>
|
|||
|
|
<ul style="margin-left: 20px;">
|
|||
|
|
<li>完整的窗口管理系统</li>
|
|||
|
|
<li>终端模拟器(支持Linux命令)</li>
|
|||
|
|
<li>多媒体音乐播放器</li>
|
|||
|
|
<li>系统监控与性能分析</li>
|
|||
|
|
<li>本地化数据持久化</li>
|
|||
|
|
<li>赛博朋克主题UI</li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div style="text-align: center; margin-top: 20px; padding-top: 20px; border-top: 1px solid rgba(5, 217, 232, 0.2);">
|
|||
|
|
<p style="color: var(--neon-purple);">© 2024 NEON-OS Project | 全栈工程示例</p>
|
|||
|
|
<p style="color: var(--neon-cyan); font-size: 0.8rem; margin-top: 8px;">右键桌面探索更多功能</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
createWindow('about', '关于系统', 'fas fa-info-circle', content);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 系统函数
|
|||
|
|
function toggleStartMenu() {
|
|||
|
|
const menu = document.getElementById('start-menu');
|
|||
|
|
if (menu.style.display === 'block') {
|
|||
|
|
menu.style.display = 'none';
|
|||
|
|
} else {
|
|||
|
|
menu.style.display = 'block';
|
|||
|
|
setTimeout(() => {
|
|||
|
|
document.addEventListener('click', closeStartMenuOnce);
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function closeStartMenuOnce(e) {
|
|||
|
|
const menu = document.getElementById('start-menu');
|
|||
|
|
const startBtn = document.querySelector('.taskbar-start');
|
|||
|
|
|
|||
|
|
if (menu && menu.style.display === 'block' &&
|
|||
|
|
!menu.contains(e.target) && !startBtn.contains(e.target)) {
|
|||
|
|
menu.style.display = 'none';
|
|||
|
|
document.removeEventListener('click', closeStartMenuOnce);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function toggleVolume() {
|
|||
|
|
showNotification('info', '音量', '当前音量: ' + Math.round(state.settings.volume * 100) + '%');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function showNotification(type, title, message) {
|
|||
|
|
if (!state.settings.notifications) return;
|
|||
|
|
|
|||
|
|
const area = document.getElementById('notification-area');
|
|||
|
|
const notification = document.createElement('div');
|
|||
|
|
notification.className = 'notification ' + type;
|
|||
|
|
|
|||
|
|
const icons = {
|
|||
|
|
success: 'fa-check-circle',
|
|||
|
|
error: 'fa-times-circle',
|
|||
|
|
warning: 'fa-exclamation-triangle',
|
|||
|
|
info: 'fa-info-circle'
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
notification.innerHTML = `
|
|||
|
|
<div class="notification-header">
|
|||
|
|
<div class="notification-title"><i class="fas ${icons[type]}"></i> ${title}</div>
|
|||
|
|
<div class="notification-close" onclick="this.parentElement.parentElement.remove()">
|
|||
|
|
<i class="fas fa-times"></i>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="notification-body">${message}</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
area.appendChild(notification);
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
if (notification.parentElement) {
|
|||
|
|
notification.style.opacity = '0';
|
|||
|
|
notification.style.transform = 'translateX(100%)';
|
|||
|
|
setTimeout(() => notification.remove(), 300);
|
|||
|
|
}
|
|||
|
|
}, 4000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function closeContextMenuOnce(e) {
|
|||
|
|
const contextMenu = document.getElementById('context-menu');
|
|||
|
|
if (contextMenu && contextMenu.style.display === 'block' && !contextMenu.contains(e.target)) {
|
|||
|
|
contextMenu.style.display = 'none';
|
|||
|
|
document.removeEventListener('click', closeContextMenuOnce);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 桌面右键菜单
|
|||
|
|
document.addEventListener('contextmenu', (e) => {
|
|||
|
|
const desktop = document.getElementById('desktop');
|
|||
|
|
const contextMenu = document.getElementById('context-menu');
|
|||
|
|
|
|||
|
|
// 确保是在桌面上的右键
|
|||
|
|
if (!desktop || desktop.style.display === 'none') return;
|
|||
|
|
if (desktop.contains(e.target)) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
contextMenu.style.display = 'block';
|
|||
|
|
contextMenu.style.left = e.pageX + 'px';
|
|||
|
|
contextMenu.style.top = e.pageY + 'px';
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
document.addEventListener('click', closeContextMenuOnce);
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
function refreshDesktop() {
|
|||
|
|
if (state.apps['file-manager']) {
|
|||
|
|
renderFileManager();
|
|||
|
|
}
|
|||
|
|
showNotification('success', '刷新', '桌面已刷新');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 主题和壁纸应用
|
|||
|
|
function applyTheme() {
|
|||
|
|
const root = document.documentElement;
|
|||
|
|
const theme = state.settings.theme;
|
|||
|
|
|
|||
|
|
const themes = {
|
|||
|
|
cyberpunk: {
|
|||
|
|
neonPink: '#ff2a6d',
|
|||
|
|
neonBlue: '#05d9e8',
|
|||
|
|
neonPurple: '#d100d1',
|
|||
|
|
neonCyan: '#00ff9f',
|
|||
|
|
darkBg: '#0d0d16'
|
|||
|
|
},
|
|||
|
|
retro: {
|
|||
|
|
neonPink: '#ff7700',
|
|||
|
|
neonBlue: '#00ff88',
|
|||
|
|
neonPurple: '#ff00aa',
|
|||
|
|
neonCyan: '#ffff00',
|
|||
|
|
darkBg: '#1a1a2e'
|
|||
|
|
},
|
|||
|
|
minimal: {
|
|||
|
|
neonPink: '#00ff88',
|
|||
|
|
neonBlue: '#00ccff',
|
|||
|
|
neonPurple: '#aa88ff',
|
|||
|
|
neonCyan: '#ffffff',
|
|||
|
|
darkBg: '#0a0a0a'
|
|||
|
|
},
|
|||
|
|
dark: {
|
|||
|
|
neonPink: '#bb0044',
|
|||
|
|
neonBlue: '#0088cc',
|
|||
|
|
neonPurple: '#6600cc',
|
|||
|
|
neonCyan: '#00cc88',
|
|||
|
|
darkBg: '#000000'
|
|||
|
|
},
|
|||
|
|
light: {
|
|||
|
|
neonPink: '#cc0066',
|
|||
|
|
neonBlue: '#0066cc',
|
|||
|
|
neonPurple: '#6600cc',
|
|||
|
|
neonCyan: '#009966',
|
|||
|
|
darkBg: '#f0f0f0'
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const selectedTheme = themes[theme] || themes.cyberpunk;
|
|||
|
|
|
|||
|
|
root.style.setProperty('--neon-pink', selectedTheme.neonPink);
|
|||
|
|
root.style.setProperty('--neon-blue', selectedTheme.neonBlue);
|
|||
|
|
root.style.setProperty('--neon-purple', selectedTheme.neonPurple);
|
|||
|
|
root.style.setProperty('--neon-cyan', selectedTheme.neonCyan);
|
|||
|
|
root.style.setProperty('--dark-bg', selectedTheme.darkBg);
|
|||
|
|
|
|||
|
|
if (theme === 'light') {
|
|||
|
|
root.style.setProperty('--glass-bg', 'rgba(255, 255, 255, 0.8)');
|
|||
|
|
root.style.setProperty('--glass-border', 'rgba(0, 102, 204, 0.3)');
|
|||
|
|
} else {
|
|||
|
|
root.style.setProperty('--glass-bg', 'rgba(13, 13, 22, 0.7)');
|
|||
|
|
root.style.setProperty('--glass-border', 'rgba(5, 217, 232, 0.3)');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function applyWallpaper() {
|
|||
|
|
const bg = document.querySelector('.desktop-bg');
|
|||
|
|
const wallpaper = state.settings.wallpaper;
|
|||
|
|
|
|||
|
|
if (!bg) return;
|
|||
|
|
|
|||
|
|
let background = '';
|
|||
|
|
|
|||
|
|
switch(wallpaper) {
|
|||
|
|
case 'neon':
|
|||
|
|
background = `linear-gradient(135deg, rgba(255, 42, 109, 0.1), rgba(5, 217, 232, 0.1)), radial-gradient(circle at 20% 50%, rgba(255, 42, 109, 0.1), transparent 50%), radial-gradient(circle at 80% 80%, rgba(5, 217, 232, 0.1), transparent 50%)`;
|
|||
|
|
break;
|
|||
|
|
case 'grid':
|
|||
|
|
background = `linear-gradient(rgba(0, 255, 159, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 159, 0.05) 1px, transparent 1px), repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0, 255, 159, 0.03) 2px, rgba(0, 255, 159, 0.03) 3px), repeating-linear-gradient(90deg, transparent, transparent 2px, rgba(0, 255, 159, 0.03) 2px, rgba(0, 255, 159, 0.03) 3px)`;
|
|||
|
|
break;
|
|||
|
|
case 'city':
|
|||
|
|
background = `linear-gradient(180deg, rgba(13, 13, 22, 0.7), rgba(13, 13, 22, 0.3)), repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(255, 42, 109, 0.1) 2px, rgba(255, 42, 109, 0.1) 3px)`;
|
|||
|
|
break;
|
|||
|
|
case 'matrix':
|
|||
|
|
background = `linear-gradient(rgba(0, 255, 159, 0.02) 2px, transparent 2px), linear-gradient(90deg, rgba(0, 255, 159, 0.02) 2px, transparent 2px)`;
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
background = `linear-gradient(rgba(13, 13, 22, 0.9), rgba(13, 13, 22, 0.7)), repeating-linear-gradient(0deg, transparent, transparent 2px, var(--grid-color) 2px, var(--grid-color) 3px), repeating-linear-gradient(90deg, transparent, transparent 2px, var(--grid-color) 2px, var(--grid-color) 3px)`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bg.style.background = background;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function saveSettings() {
|
|||
|
|
localStorage.setItem('neon-os-settings', JSON.stringify(state.settings));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 矩阵雨特效
|
|||
|
|
function startMatrixRain() {
|
|||
|
|
const canvas = document.getElementById('matrix-canvas');
|
|||
|
|
const ctx = canvas.getContext('2d');
|
|||
|
|
|
|||
|
|
canvas.style.display = 'block';
|
|||
|
|
state.matrixRunning = true;
|
|||
|
|
|
|||
|
|
canvas.width = window.innerWidth;
|
|||
|
|
canvas.height = window.innerHeight;
|
|||
|
|
|
|||
|
|
const cols = Math.floor(canvas.width / 20);
|
|||
|
|
const drops = [];
|
|||
|
|
const chars = '01abcdef0123456789@$%&*';
|
|||
|
|
|
|||
|
|
for (let i = 0; i < cols; i++) {
|
|||
|
|
drops[i] = 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function draw() {
|
|||
|
|
if (!state.matrixRunning) {
|
|||
|
|
canvas.style.display = 'none';
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
|
|||
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|||
|
|
|
|||
|
|
ctx.fillStyle = '#00ff9f';
|
|||
|
|
ctx.font = '16px monospace';
|
|||
|
|
|
|||
|
|
for (let i = 0; i < drops.length; i++) {
|
|||
|
|
const text = chars.charAt(Math.floor(Math.random() * chars.length));
|
|||
|
|
ctx.fillText(text, i * 20, drops[i] * 20);
|
|||
|
|
|
|||
|
|
if (drops[i] * 20 > canvas.height && Math.random() > 0.975) {
|
|||
|
|
drops[i] = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
drops[i]++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
requestAnimationFrame(draw);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
draw();
|
|||
|
|
|
|||
|
|
const exitHandler = () => {
|
|||
|
|
state.matrixRunning = false;
|
|||
|
|
document.removeEventListener('keydown', exitHandler);
|
|||
|
|
document.removeEventListener('click', exitHandler);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
document.addEventListener('keydown', exitHandler);
|
|||
|
|
document.addEventListener('click', exitHandler);
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 系统操作
|
|||
|
|
function rebootSystem() {
|
|||
|
|
showNotification('warning', '系统重启', '正在重启系统...');
|
|||
|
|
setTimeout(() => {
|
|||
|
|
location.reload();
|
|||
|
|
}, 2000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function logout() {
|
|||
|
|
showNotification('info', '切换用户', '正在切换用户...');
|
|||
|
|
|
|||
|
|
Object.keys(state.apps).forEach(appId => closeWindow(appId));
|
|||
|
|
|
|||
|
|
state.currentDirectory = [];
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
document.getElementById('desktop').style.display = 'none';
|
|||
|
|
document.getElementById('start-menu').style.display = 'none';
|
|||
|
|
showLockScreen();
|
|||
|
|
}, 1000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function shutdown() {
|
|||
|
|
showNotification('error', '关机', '系统正在关闭...');
|
|||
|
|
const desktop = document.getElementById('desktop');
|
|||
|
|
if (desktop) desktop.style.opacity = '0';
|
|||
|
|
setTimeout(() => {
|
|||
|
|
document.body.innerHTML = `
|
|||
|
|
<div style="display: flex; justify-content: center; align-items: center; height: 100vh; background: #000; color: var(--neon-pink); font-family: 'Orbitron', sans-serif; font-size: 2rem;">
|
|||
|
|
系统已关机
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
}, 2000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 键盘快捷键
|
|||
|
|
document.addEventListener('keydown', (e) => {
|
|||
|
|
if (e.ctrlKey && e.altKey && e.key === 't') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
openTerminal();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (e.ctrlKey && e.altKey && e.key === 'w') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
let topZIndex = 0;
|
|||
|
|
let topAppId = null;
|
|||
|
|
|
|||
|
|
Object.entries(state.apps).forEach(([appId, app]) => {
|
|||
|
|
const zIndex = parseInt(app.element.style.zIndex);
|
|||
|
|
if (zIndex > topZIndex && !app.minimized) {
|
|||
|
|
topZIndex = zIndex;
|
|||
|
|
topAppId = appId;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (topAppId) {
|
|||
|
|
closeWindow(topAppId);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (e.ctrlKey && e.altKey && e.key === 'm') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
let topZIndex = 0;
|
|||
|
|
let topAppId = null;
|
|||
|
|
|
|||
|
|
Object.entries(state.apps).forEach(([appId, app]) => {
|
|||
|
|
const zIndex = parseInt(app.element.style.zIndex);
|
|||
|
|
if (zIndex > topZIndex && !app.minimized) {
|
|||
|
|
topZIndex = zIndex;
|
|||
|
|
topAppId = appId;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (topAppId) {
|
|||
|
|
minimizeWindow(topAppId);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (e.ctrlKey && e.altKey && e.key === 'e') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
openApp('file-manager');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 窗口点击事件(激活窗口)
|
|||
|
|
document.addEventListener('click', (e) => {
|
|||
|
|
const windowElement = e.target.closest('.window');
|
|||
|
|
if (windowElement && !e.target.closest('.window-control')) {
|
|||
|
|
const appId = windowElement.id.replace('window-', '');
|
|||
|
|
activateWindow(appId);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 响应式处理
|
|||
|
|
window.addEventListener('resize', () => {
|
|||
|
|
Object.values(state.apps).forEach(app => {
|
|||
|
|
if (!app.maximized && !app.minimized) {
|
|||
|
|
const rect = app.element.getBoundingClientRect();
|
|||
|
|
if (rect.left > window.innerWidth || rect.top > window.innerHeight) {
|
|||
|
|
app.element.style.left = '50px';
|
|||
|
|
app.element.style.top = '50px';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const matrixCanvas = document.getElementById('matrix-canvas');
|
|||
|
|
if (matrixCanvas && matrixCanvas.style.display !== 'none') {
|
|||
|
|
matrixCanvas.width = window.innerWidth;
|
|||
|
|
matrixCanvas.height = window.innerHeight;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 系统初始化
|
|||
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|||
|
|
console.log('[INIT] DOM加载完成,开始初始化系统');
|
|||
|
|
initSystem();
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
applyTheme();
|
|||
|
|
applyWallpaper();
|
|||
|
|
}, 100);
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|