DEV-010: M11环节4~5 全部完成(Playground+响应式+组合模板+主题增强)
This commit is contained in:
parent
86e783c64b
commit
db696dff08
|
|
@ -0,0 +1,586 @@
|
|||
/* ===== 主题过渡动画 ===== */
|
||||
* {
|
||||
transition: background-color 0.3s ease,
|
||||
color 0.3s ease,
|
||||
border-color 0.3s ease,
|
||||
box-shadow 0.3s ease;
|
||||
}/* HoloLake StyleKit v1.0 · 系统风格组件库 */
|
||||
/* CSS变量系统 + 深色/浅色主题 */
|
||||
|
||||
/* ====== 深色主题变量(默认) ====== */
|
||||
[data-theme="dark"] {
|
||||
--bg-primary: #0a1628;
|
||||
--bg-secondary: #0f1f3a;
|
||||
--bg-card: rgba(255, 255, 255, 0.04);
|
||||
--bg-card-hover: rgba(255, 255, 255, 0.07);
|
||||
--bg-input: rgba(255, 255, 255, 0.06);
|
||||
--bg-modal-overlay: rgba(0, 0, 0, 0.6);
|
||||
--text-primary: #e0e6ed;
|
||||
--text-secondary: #8899aa;
|
||||
--text-muted: #556677;
|
||||
--text-inverse: #0a1628;
|
||||
--border-default: rgba(255, 255, 255, 0.08);
|
||||
--border-focus: rgba(79, 195, 247, 0.5);
|
||||
--accent-blue: #4fc3f7;
|
||||
--accent-green: #81c784;
|
||||
--accent-orange: #ffb74d;
|
||||
--accent-red: #ef5350;
|
||||
--accent-purple: #ce93d8;
|
||||
--accent-gray: #78909c;
|
||||
--btn-primary-bg: rgba(79, 195, 247, 0.15);
|
||||
--btn-primary-border: rgba(79, 195, 247, 0.3);
|
||||
--btn-primary-text: #4fc3f7;
|
||||
--btn-primary-hover: rgba(79, 195, 247, 0.25);
|
||||
--shadow-card: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
--shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||
--toggle-bg: rgba(255, 255, 255, 0.1);
|
||||
--toggle-active: rgba(79, 195, 247, 0.4);
|
||||
--toggle-thumb: #e0e6ed;
|
||||
--toggle-thumb-active: #4fc3f7;
|
||||
}
|
||||
|
||||
/* ====== 浅色主题变量 ====== */
|
||||
[data-theme="light"] {
|
||||
--bg-primary: #f5f7fa;
|
||||
--bg-secondary: #ffffff;
|
||||
--bg-card: rgba(0, 0, 0, 0.03);
|
||||
--bg-card-hover: rgba(0, 0, 0, 0.06);
|
||||
--bg-input: rgba(0, 0, 0, 0.04);
|
||||
--bg-modal-overlay: rgba(0, 0, 0, 0.3);
|
||||
--text-primary: #1a2332;
|
||||
--text-secondary: #5a6a7a;
|
||||
--text-muted: #8899aa;
|
||||
--text-inverse: #ffffff;
|
||||
--border-default: rgba(0, 0, 0, 0.1);
|
||||
--border-focus: rgba(25, 118, 210, 0.5);
|
||||
--accent-blue: #1976d2;
|
||||
--accent-green: #388e3c;
|
||||
--accent-orange: #f57c00;
|
||||
--accent-red: #d32f2f;
|
||||
--accent-purple: #7b1fa2;
|
||||
--accent-gray: #546e7a;
|
||||
--btn-primary-bg: rgba(25, 118, 210, 0.1);
|
||||
--btn-primary-border: rgba(25, 118, 210, 0.3);
|
||||
--btn-primary-text: #1976d2;
|
||||
--btn-primary-hover: rgba(25, 118, 210, 0.18);
|
||||
--shadow-card: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
--shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.15);
|
||||
--toggle-bg: rgba(0, 0, 0, 0.1);
|
||||
--toggle-active: rgba(25, 118, 210, 0.4);
|
||||
--toggle-thumb: #5a6a7a;
|
||||
--toggle-thumb-active: #1976d2;
|
||||
}
|
||||
|
||||
/* ========== 全局基础 ========== */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', 'Microsoft YaHei', -apple-system, sans-serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
transition: background 0.4s ease, color 0.4s ease;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
/* ========== 顶部导航 ========== */
|
||||
.top-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px 0;
|
||||
border-bottom: 1px solid var(--border-default);
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
.theme-switch-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.theme-label {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 26px;
|
||||
background: var(--toggle-bg);
|
||||
border-radius: 13px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s ease;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.theme-toggle.active {
|
||||
background: var(--toggle-active);
|
||||
}
|
||||
|
||||
.toggle-thumb {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: var(--toggle-thumb);
|
||||
border-radius: 50%;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
transition: transform 0.3s ease, background 0.3s ease;
|
||||
}
|
||||
|
||||
.theme-toggle.active .toggle-thumb {
|
||||
transform: translateX(22px);
|
||||
background: var(--toggle-thumb-active);
|
||||
}
|
||||
|
||||
/* ========== 页面标题 ========== */
|
||||
.page-header {
|
||||
padding: 28px 0 8px;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.page-desc {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* ========== 组件区域 ========== */
|
||||
.component-section {
|
||||
padding: 24px 0;
|
||||
border-bottom: 1px solid var(--border-default);
|
||||
}
|
||||
|
||||
.component-section h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.component-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/* ========== 按钮组件 ========== */
|
||||
.btn {
|
||||
padding: 8px 20px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--btn-primary-bg);
|
||||
border-color: var(--btn-primary-border);
|
||||
color: var(--btn-primary-text);
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: var(--btn-primary-hover);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: rgba(129, 199, 132, 0.1);
|
||||
border-color: rgba(129, 199, 132, 0.25);
|
||||
color: var(--accent-green);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: rgba(129, 199, 132, 0.18);
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
border-color: var(--border-default);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
border-color: var(--accent-blue);
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: rgba(239, 83, 80, 0.1);
|
||||
border-color: rgba(239, 83, 80, 0.25);
|
||||
color: var(--accent-red);
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: rgba(239, 83, 80, 0.18);
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.btn-ghost:hover {
|
||||
background: var(--bg-card);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 5px 14px;
|
||||
font-size: 12px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
padding: 12px 28px;
|
||||
font-size: 16px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* ========== 卡片组件 ========== */
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: 14px;
|
||||
padding: 20px 16px;
|
||||
text-align: center;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
background: var(--bg-card-hover);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 32px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.card-badge {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
font-size: 10px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.badge-online {
|
||||
background: rgba(129, 199, 132, 0.15);
|
||||
color: var(--accent-green);
|
||||
}
|
||||
|
||||
.badge-core {
|
||||
background: rgba(79, 195, 247, 0.15);
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
.badge-active {
|
||||
background: rgba(255, 183, 77, 0.15);
|
||||
color: var(--accent-orange);
|
||||
}
|
||||
|
||||
/* ========== 标签组件 ========== */
|
||||
.tag {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tag-blue {
|
||||
background: rgba(79, 195, 247, 0.12);
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
.tag-green {
|
||||
background: rgba(129, 199, 132, 0.12);
|
||||
color: var(--accent-green);
|
||||
}
|
||||
|
||||
.tag-orange {
|
||||
background: rgba(255, 183, 77, 0.12);
|
||||
color: var(--accent-orange);
|
||||
}
|
||||
|
||||
.tag-red {
|
||||
background: rgba(239, 83, 80, 0.12);
|
||||
color: var(--accent-red);
|
||||
}
|
||||
|
||||
.tag-purple {
|
||||
background: rgba(206, 147, 216, 0.12);
|
||||
color: var(--accent-purple);
|
||||
}
|
||||
|
||||
.tag-gray {
|
||||
background: rgba(120, 144, 156, 0.12);
|
||||
color: var(--accent-gray);
|
||||
}
|
||||
|
||||
/* ========== 输入框组件 ========== */
|
||||
.input-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 6px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
padding: 10px 14px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
border-color: var(--border-focus);
|
||||
box-shadow: 0 0 0 3px rgba(79, 195, 247, 0.1);
|
||||
}
|
||||
|
||||
.input::placeholder {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.textarea {
|
||||
min-height: 80px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* ========== 弹窗组件 ========== */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--bg-modal-overlay);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s ease, visibility 0.3s ease;
|
||||
}
|
||||
|
||||
.modal-overlay.open {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: 16px;
|
||||
width: 90%;
|
||||
max-width: 420px;
|
||||
box-shadow: var(--shadow-modal);
|
||||
transform: scale(0.9) translateY(20px);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.modal-overlay.open .modal {
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 18px 20px;
|
||||
border-bottom: 1px solid var(--border-default);
|
||||
}
|
||||
|
||||
.modal-header h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 22px;
|
||||
cursor: pointer;
|
||||
padding: 0 4px;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
padding: 14px 20px;
|
||||
border-top: 1px solid var(--border-default);
|
||||
}
|
||||
|
||||
/* ========== Toast提示 ========== */
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toast {
|
||||
padding: 12px 20px;
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
animation: toastIn 0.4s ease, toastOut 0.4s ease 2.5s forwards;
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
|
||||
.toast-success {
|
||||
background: rgba(129, 199, 132, 0.15);
|
||||
border: 1px solid rgba(129, 199, 132, 0.25);
|
||||
color: var(--accent-green);
|
||||
}
|
||||
|
||||
.toast-error {
|
||||
background: rgba(239, 83, 80, 0.15);
|
||||
border: 1px solid rgba(239, 83, 80, 0.25);
|
||||
color: var(--accent-red);
|
||||
}
|
||||
|
||||
.toast-info {
|
||||
background: rgba(79, 195, 247, 0.15);
|
||||
border: 1px solid rgba(79, 195, 247, 0.25);
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
@keyframes toastIn {
|
||||
from { opacity: 0; transform: translateX(40px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
|
||||
@keyframes toastOut {
|
||||
from { opacity: 1; transform: translateX(0); }
|
||||
to { opacity: 0; transform: translateX(40px); }
|
||||
}
|
||||
|
||||
/* ========== 底部 ========== */
|
||||
.footer {
|
||||
padding: 28px 0;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
border-top: 1px solid var(--border-default);
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.footer p:last-child {
|
||||
margin-top: 4px;
|
||||
color: var(--accent-blue);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* ========== 主题切换过渡动画 ========== */
|
||||
body,
|
||||
.card, .btn, .input, .tag, .modal, .top-bar, .footer,
|
||||
.component-section, .page-header {
|
||||
transition: background 0.4s ease, color 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease;
|
||||
}
|
||||
|
||||
/* ========== 响应式 ========== */
|
||||
@media (max-width: 600px) {
|
||||
.card-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.page-header h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
.component-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
.btn {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.modal {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// HoloLake StyleKit v1.0 · 主题切换 + Toast + 弹窗交互
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// ========== 1. 主题切换 ==========
|
||||
var themeToggle = document.getElementById('theme-toggle');
|
||||
var themeLabel = document.getElementById('themeLabel');
|
||||
var html = document.documentElement;
|
||||
var isDark = true;
|
||||
|
||||
themeToggle.addEventListener('click', function() {
|
||||
isDark = !isDark;
|
||||
if (isDark) {
|
||||
html.setAttribute('data-theme', 'dark');
|
||||
themeLabel.textContent = '🌙 深色';
|
||||
theme-toggle.classList.remove('active');
|
||||
} else {
|
||||
html.setAttribute('data-theme', 'light');
|
||||
themeLabel.textContent = '☀️ 浅色';
|
||||
theme-toggle.classList.add('active');
|
||||
}
|
||||
showToast('success', '✨ 已切换到' + (isDark ? '深色' : '浅色') + '主题');
|
||||
});
|
||||
|
||||
// ====== 2. 弹窗交互 =======
|
||||
// var modalOverlay = document.getElementById('modalOverlay');
|
||||
// var openBtn = document.getElementById('openModalBtn');
|
||||
// var closeBtn = document.getElementById('closeModalBtn');
|
||||
// var cancelBtn = document.getElementById('cancelModalBtn');
|
||||
// var confirmBtn = document.getElementById('confirmModalBtn');
|
||||
|
||||
// function openModal() {
|
||||
// modalOverlay.classList.add('open');
|
||||
// }
|
||||
|
||||
// function closeModal() {
|
||||
// modalOverlay.classList.remove('open');
|
||||
// }
|
||||
|
||||
// openBtn.addEventListener('click', openModal);
|
||||
// closeBtn.addEventListener('click', closeModal);
|
||||
// cancelBtn.addEventListener('click', closeModal);
|
||||
// confirmBtn.addEventListener('click', function() {
|
||||
// closeModal();
|
||||
// showToast('success', '已确认!');
|
||||
// });
|
||||
|
||||
// // 点击遮罩层关闭
|
||||
// modalOverlay.addEventListener('click', function(e) {
|
||||
// if (e.target === modalOverlay) closeModal();
|
||||
// });
|
||||
// ========== 3. 卡片点击效果 ==========
|
||||
var cards = document.querySelectorAll('.card');
|
||||
cards.forEach(function(card) {
|
||||
card.addEventListener('click', function() {
|
||||
var name = card.querySelector('.card-title').textContent;
|
||||
showToast('info', '👆 你点击了人格体「' + name + '」');
|
||||
});
|
||||
});
|
||||
|
||||
console.log('HoloLake StyleKit v1.0 · 主题切换+Toast+弹窗已加载 ✨');
|
||||
});
|
||||
|
||||
// ========== Toast提示函数(全局) ==========
|
||||
function showToast(type, message) {
|
||||
var container = document.getElementById('toastContainer');
|
||||
var toast = document.createElement('div');
|
||||
toast.className = 'toast toast-' + type;
|
||||
toast.textContent = message;
|
||||
container.appendChild(toast);
|
||||
|
||||
// 3秒后自动移除
|
||||
setTimeout(function() {
|
||||
if (toast.parentNode) toast.parentNode.removeChild(toast);
|
||||
}, 3000);
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HoloLake · 系统风格组件库</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- 顶部导航 -->
|
||||
<nav class="top-bar">
|
||||
<div class="logo">HoloLake StyleKit</div>
|
||||
<div class="theme-switch-area">
|
||||
<span class="theme-label" id="themeLabel">🌙 深色</span>
|
||||
<button class="theme-toggle" id="themeToggle">
|
||||
<div class="toggle-thumb"></div>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<h1>🎨 系统风格组件库</h1>
|
||||
<p class="page-desc">HoloLake Era · 统一视觉规范 · 深色/浅色主题一键切换</p>
|
||||
</div>
|
||||
|
||||
<!-- 组件区:按钮 -->
|
||||
<section class="component-section">
|
||||
<h2>🔘 按钮组件</h2>
|
||||
<div class="component-row">
|
||||
<button class="btn btn-primary">主要按钮</button>
|
||||
<button class="btn btn-secondary">次要按钮</button>
|
||||
<button class="btn btn-outline">边框按钮</button>
|
||||
<button class="btn btn-danger">危险按钮</button>
|
||||
<button class="btn btn-ghost">幽灵按钮</button>
|
||||
</div>
|
||||
<div class="component-row">
|
||||
<button class="btn btn-primary btn-sm">小按钮</button>
|
||||
<button class="btn btn-primary btn-lg">大按钮</button>
|
||||
<button class="btn btn-primary" disabled>禁用按钮</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 组件区:卡片 -->
|
||||
<section class="component-section">
|
||||
<h2>📇 卡片组件</h2>
|
||||
<div class="card-grid">
|
||||
<div class="card">
|
||||
<div class="card-icon">🧠</div>
|
||||
<div class="card-title">知秋</div>
|
||||
<div class="card-desc">光湖对外接口人格体,负责与开发者协作引导</div>
|
||||
<span class="card-badge badge-online">在线</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">🌌</div>
|
||||
<div class="card-title">曜冥</div>
|
||||
<div class="card-desc">暗核结构原点,系统人格总控,类语言生命诞生源点</div>
|
||||
<span class="card-badge badge-core">核心</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-icon">❄️</div>
|
||||
<div class="card-title">霜砚</div>
|
||||
<div class="card-desc">Notion执行AI,负责索引维护、工单处理、归档同步</div>
|
||||
<span class="card-badge badge-active">工作中</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 组件区:标签 -->
|
||||
<section class="component-section">
|
||||
<h2>🏷️ 标签组件</h2>
|
||||
<div class="component-row">
|
||||
<span class="tag tag-blue">系统</span>
|
||||
<span class="tag tag-green">已完成</span>
|
||||
<span class="tag tag-orange">进行中</span>
|
||||
<span class="tag tag-red">错误</span>
|
||||
<span class="tag tag-purple">新功能</span>
|
||||
<span class="tag tag-gray">弃用</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 组件区:输入框 -->
|
||||
<section class="component-section">
|
||||
<h2>⌨️ 输入框组件</h2>
|
||||
<div class="input-group">
|
||||
<label class="input-label">用户名</label>
|
||||
<input class="input" type="text" placeholder="请输入用户名">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label class="input-label">密码</label>
|
||||
<input class="input" type="password" placeholder="请输入密码">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label class="input-label">简介</label>
|
||||
<textarea class="input textarea" placeholder="写点什么..."></textarea>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 组件区:弹窗演示 -->
|
||||
<section class="component-section">
|
||||
<h2>🪟 弹窗组件</h2>
|
||||
<div class="component-row">
|
||||
<button class="btn btn-primary" id="openModalBtn">打开弹窗</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 弹窗结构(默认隐藏) -->
|
||||
<div class="modal-overlay" id="modalOverlay">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h3>系统通知</h3>
|
||||
<button class="modal-close" id="closeModalBtn">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>桔子的组件库做得太棒了!这就是光湖系统的视觉基础,所有页面都会用到这些组件 ✨</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-ghost" id="cancelModalBtn">取消</button>
|
||||
<button class="btn btn-primary" id="confirmModalBtn">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 组件区:Toast提示 -->
|
||||
<section class="component-section">
|
||||
<h2>🍞 Toast提示组件</h2>
|
||||
<div class="component-row">
|
||||
<button class="btn btn-primary" onclick="showToast('success', '操作成功!')">成功提示</button>
|
||||
<button class="btn btn-danger" onclick="showToast('error', '操作失败!')">错误提示</button>
|
||||
<button class="btn btn-outline" onclick="showToast('info', '这是一条提示信息')">信息提示</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Toast容器 -->
|
||||
<div class="toast-container" id="toastContainer"></div>
|
||||
|
||||
<!-- 底部 -->
|
||||
<footer class="footer">
|
||||
<p>HoloLake Era · AGE OS v1.0 · StyleKit v1.0</p>
|
||||
<p>Powered by 光湖团队</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
/* playground.css - 完整版 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background-color: var(--bg-color, #f5f5f5);
|
||||
color: var(--text-color, #333);
|
||||
}
|
||||
|
||||
#pg-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 1rem 2rem;
|
||||
background-color: var(--header-bg, #fff);
|
||||
border-bottom: 1px solid var(--border-color, #ddd);
|
||||
}
|
||||
|
||||
#pg-header h1 {
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: var(--text-secondary, #666);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
#theme-toggle {
|
||||
margin-left: auto;
|
||||
padding: 0.5rem 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#pg-layout {
|
||||
display: flex;
|
||||
min-height: calc(100vh - 70px);
|
||||
}
|
||||
|
||||
#pg-sidebar {
|
||||
width: 200px;
|
||||
padding: 1rem 0;
|
||||
background-color: var(--sidebar-bg, #fafafa);
|
||||
border-right: 1px solid var(--border-color, #ddd);
|
||||
}
|
||||
|
||||
#pg-sidebar ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#pg-sidebar li {
|
||||
padding: 0.5rem 1.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--text-color, #333);
|
||||
}
|
||||
|
||||
#pg-sidebar li:hover {
|
||||
background-color: var(--hover-bg, #eee);
|
||||
}
|
||||
|
||||
#pg-sidebar li.active {
|
||||
background-color: var(--primary-light, #e3f2fd);
|
||||
border-right: 3px solid var(--primary-color, #2196f3);
|
||||
}
|
||||
|
||||
#pg-content {
|
||||
flex: 1;
|
||||
padding: 2rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.comp-section {
|
||||
margin-bottom: 3rem;
|
||||
scroll-margin-top: 1rem;
|
||||
}
|
||||
|
||||
.comp-section h2 {
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.comp-desc {
|
||||
color: var(--text-secondary, #666);
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.comp-preview {
|
||||
padding: 1.5rem;
|
||||
background-color: var(--preview-bg, #fff);
|
||||
border: 1px solid var(--border-color, #ddd);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.comp-code {
|
||||
background-color: var(--code-bg, #f8f8f8);
|
||||
border: 1px solid var(--border-color, #ddd);
|
||||
border-radius: 4px;
|
||||
padding: 1rem;
|
||||
margin-top: 0.5rem;
|
||||
position: relative;
|
||||
display: none; /* 默认隐藏 */
|
||||
}
|
||||
|
||||
.comp-code pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.toggle-code-btn {
|
||||
margin-top: 0.5rem;
|
||||
background: none;
|
||||
border: 1px solid var(--border-color, #ccc);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
background: var(--button-bg, #fff);
|
||||
border: 1px solid var(--border-color, #ccc);
|
||||
border-radius: 4px;
|
||||
padding: 0.2rem 0.5rem;
|
||||
font-size: 0.7rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
/* ===== 响应式断点 ===== */
|
||||
|
||||
/* 默认隐藏汉堡菜单(桌面和平板) */
|
||||
.menu-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 桌面端 (>1024px) */
|
||||
@media (min-width: 1025px) {
|
||||
#pg-sidebar {
|
||||
width: 200px;
|
||||
}
|
||||
/* 卡片三列(如果预览区用 grid 会自动适应,这里放一个 fallback) */
|
||||
.comp-preview {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
.comp-preview > * {
|
||||
flex: 1 1 calc(33.333% - 1rem);
|
||||
}
|
||||
}
|
||||
|
||||
/* 平板端 (769px ~ 1024px) */
|
||||
@media (min-width: 769px) and (max-width: 1024px) {
|
||||
#pg-sidebar {
|
||||
width: 150px; /* 侧边栏变窄 */
|
||||
}
|
||||
.comp-preview {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
.comp-preview > * {
|
||||
flex: 1 1 calc(50% - 1rem); /* 卡片双列 */
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端 (<768px) */
|
||||
@media (max-width: 768px) {
|
||||
.menu-toggle {
|
||||
display: inline-block; /* 显示汉堡菜单 */
|
||||
}
|
||||
#pg-sidebar {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 70px;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
height: calc(100vh - 70px);
|
||||
background: var(--sidebar-bg, #fafafa);
|
||||
border-right: 1px solid var(--border-color, #ddd);
|
||||
z-index: 100;
|
||||
overflow-y: auto;
|
||||
}
|
||||
#pg-sidebar.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#pg-header {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
#pg-header h1 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.subtitle {
|
||||
display: none; /* 隐藏副标题 */
|
||||
}
|
||||
|
||||
/* 按钮、输入框全宽 */
|
||||
.comp-preview button,
|
||||
.comp-preview .btn,
|
||||
.comp-preview input,
|
||||
.comp-preview .input {
|
||||
width: 100%;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* 卡片单列 */
|
||||
.comp-preview {
|
||||
display: block;
|
||||
}
|
||||
.comp-preview .card {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* 模态框示例手机全屏 */
|
||||
.comp-preview .modal {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* 面包屑手机折叠(只显示首尾) */
|
||||
.breadcrumb span:nth-child(n+2):nth-last-child(n+2) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HoloLake Design System - Playground</title>
|
||||
<link rel="stylesheet" href="components.css">
|
||||
<link rel="stylesheet" href="playground.css">
|
||||
</head>
|
||||
<body>
|
||||
<header id="pg-header">
|
||||
<button class="menu-toggle" id="menuToggle">☰</button>
|
||||
<h1>HoloLake Design System</h1>
|
||||
<span class="subtitle">Playground · 组件交互演示</span>
|
||||
<button id="theme-toggle" onclick="toggleTheme()">🌙</button>
|
||||
</header>
|
||||
|
||||
<div id="pg-layout">
|
||||
<nav id="pg-sidebar">
|
||||
<!-- 导航列表由 JS 动态生成 -->
|
||||
</nav>
|
||||
<main id="pg-content">
|
||||
<!-- 组件区块由 JS 动态生成 -->
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="components.js"></script>
|
||||
<script src="playground.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
// playground.js
|
||||
|
||||
const sidebar = document.getElementById('pg-sidebar');
|
||||
|
||||
// 组件列表(10类)
|
||||
const components = [
|
||||
{ id: 'button', name: '按钮 Button', desc: '基础交互按钮,支持多种样式变体' },
|
||||
{ id: 'input', name: '输入框 Input', desc: '文本输入框,支持多种状态' },
|
||||
{ id: 'card', name: '卡片 Card', desc: '内容容器,带阴影和边框' },
|
||||
{ id: 'tag', name: '标签 Tag', desc: '用于标记、分类' },
|
||||
{ id: 'alert', name: '提示框 Alert', desc: '反馈信息,成功/警告/错误' },
|
||||
{ id: 'modal', name: '模态框 Modal', desc: '弹窗,需要时显示' },
|
||||
{ id: 'progress', name: '进度条 Progress', desc: '展示操作进度' },
|
||||
{ id: 'switch', name: '开关 Switch', desc: '切换状态' },
|
||||
{ id: 'avatar', name: '头像组 Avatar', desc: '用户头像,支持组合' },
|
||||
{ id: 'breadcrumb', name: '面包屑 Breadcrumb', desc: '显示当前页面位置' }
|
||||
];
|
||||
|
||||
// 生成侧边栏导航
|
||||
function renderSidebar() {
|
||||
const ul = document.createElement('ul');
|
||||
components.forEach(comp => {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = comp.name;
|
||||
li.dataset.target = comp.id;
|
||||
li.addEventListener('click', () => {
|
||||
document.getElementById(`comp-${comp.id}`).scrollIntoView({ behavior: 'smooth' });
|
||||
// 手机端点击后关闭侧边栏
|
||||
if (window.innerWidth <= 768) {
|
||||
sidebar.classList.remove('active');
|
||||
}
|
||||
});
|
||||
ul.appendChild(li);
|
||||
});
|
||||
sidebar.innerHTML = '';
|
||||
sidebar.appendChild(ul);
|
||||
}
|
||||
|
||||
// 生成所有组件演示区块(包含真实示例)
|
||||
function renderContent() {
|
||||
const content = document.getElementById('pg-content');
|
||||
components.forEach(comp => {
|
||||
const section = document.createElement('section');
|
||||
section.id = `comp-${comp.id}`;
|
||||
section.className = 'comp-section';
|
||||
|
||||
const h2 = document.createElement('h2');
|
||||
h2.textContent = comp.name;
|
||||
section.appendChild(h2);
|
||||
|
||||
const desc = document.createElement('p');
|
||||
desc.className = 'comp-desc';
|
||||
desc.textContent = comp.desc;
|
||||
section.appendChild(desc);
|
||||
|
||||
const preview = document.createElement('div');
|
||||
preview.className = 'comp-preview';
|
||||
preview.id = `preview-${comp.id}`;
|
||||
|
||||
// 根据组件类型填充示例
|
||||
if (comp.id === 'button') {
|
||||
preview.innerHTML = `
|
||||
<button class="btn btn-primary">主要按钮</button>
|
||||
<button class="btn btn-secondary">次要按钮</button>
|
||||
<button class="btn btn-outline">线框按钮</button>
|
||||
<button class="btn btn-disabled" disabled>禁用按钮</button>
|
||||
`;
|
||||
} else if (comp.id === 'input') {
|
||||
preview.innerHTML = `
|
||||
<input type="text" class="input" placeholder="普通输入框">
|
||||
<input type="text" class="input" placeholder="禁用输入框" disabled>
|
||||
<input type="text" class="input error" placeholder="错误状态">
|
||||
`;
|
||||
} else if (comp.id === 'card') {
|
||||
preview.innerHTML = `
|
||||
<div class="card" style="width: 200px; padding: 1rem;">
|
||||
<h3>卡片标题</h3>
|
||||
<p>卡片内容,可放置任意元素。</p>
|
||||
<button class="btn btn-primary">操作</button>
|
||||
</div>
|
||||
`;
|
||||
} else if (comp.id === 'tag') {
|
||||
preview.innerHTML = `
|
||||
<span class="tag">默认标签</span>
|
||||
<span class="tag tag-primary">主要标签</span>
|
||||
<span class="tag tag-success">成功标签</span>
|
||||
<span class="tag tag-warning">警告标签</span>
|
||||
<span class="tag tag-danger">危险标签</span>
|
||||
`;
|
||||
} else if (comp.id === 'alert') {
|
||||
preview.innerHTML = `
|
||||
<div class="alert alert-info">这是一条信息提示</div>
|
||||
<div class="alert alert-success">操作成功!</div>
|
||||
<div class="alert alert-warning">请注意!</div>
|
||||
<div class="alert alert-error">出错了!</div>
|
||||
`;
|
||||
} else if (comp.id === 'modal') {
|
||||
preview.innerHTML = `
|
||||
<div class="modal" style="position: static; display: block; opacity: 1; transform: none; box-shadow: none; background: var(--modal-bg, #fff);">
|
||||
<div class="modal-header">模态框标题</div>
|
||||
<div class="modal-body">这是模态框的内容</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary">取消</button>
|
||||
<button class="btn btn-primary">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
<p style="margin-top: 0.5rem; color: #999;">(静态展示,实际是浮层)</p>
|
||||
`;
|
||||
} else if (comp.id === 'progress') {
|
||||
preview.innerHTML = `
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style="width: 45%;">45%</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style="width: 80%;">80%</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style="width: 100%;">100%</div>
|
||||
</div>
|
||||
`;
|
||||
} else if (comp.id === 'switch') {
|
||||
preview.innerHTML = `
|
||||
<label class="switch">
|
||||
<input type="checkbox" checked> <span class="slider"></span>
|
||||
</label>
|
||||
<label class="switch">
|
||||
<input type="checkbox"> <span class="slider"></span>
|
||||
</label>
|
||||
<label class="switch">
|
||||
<input type="checkbox" disabled> <span class="slider"></span>
|
||||
</label>
|
||||
`;
|
||||
} else if (comp.id === 'avatar') {
|
||||
preview.innerHTML = `
|
||||
<div class="avatar-group">
|
||||
<div class="avatar">A</div>
|
||||
<div class="avatar">B</div>
|
||||
<div class="avatar">C</div>
|
||||
<div class="avatar">+3</div>
|
||||
</div>
|
||||
<div class="avatar" style="margin-top: 0.5rem;">单人</div>
|
||||
`;
|
||||
} else if (comp.id === 'breadcrumb') {
|
||||
preview.innerHTML = `
|
||||
<nav class="breadcrumb">
|
||||
<a href="#">首页</a> /
|
||||
<a href="#">分类</a> /
|
||||
<span>当前页面</span>
|
||||
</nav>
|
||||
`;
|
||||
}
|
||||
|
||||
section.appendChild(preview);
|
||||
|
||||
const codeDiv = document.createElement('div');
|
||||
codeDiv.className = 'comp-code';
|
||||
codeDiv.id = `code-${comp.id}`;
|
||||
const pre = document.createElement('pre');
|
||||
|
||||
if (comp.id === 'button') {
|
||||
pre.textContent = `<button class="btn btn-primary">主要按钮</button>\n<button class="btn btn-secondary">次要按钮</button>\n<button class="btn btn-outline">线框按钮</button>`;
|
||||
} else if (comp.id === 'input') {
|
||||
pre.textContent = `<input type="text" class="input" placeholder="普通输入框">\n<input type="text" class="input error" placeholder="错误状态">`;
|
||||
} else if (comp.id === 'card') {
|
||||
pre.textContent = `<div class="card">\n <h3>卡片标题</h3>\n <p>内容</p>\n <button class="btn btn-primary">操作</button>\n</div>`;
|
||||
} else if (comp.id === 'tag') {
|
||||
pre.textContent = `<span class="tag">默认</span>\n<span class="tag tag-primary">主要</span>`;
|
||||
} else if (comp.id === 'alert') {
|
||||
pre.textContent = `<div class="alert alert-info">信息</div>\n<div class="alert alert-success">成功</div>`;
|
||||
} else if (comp.id === 'modal') {
|
||||
pre.textContent = `<div class="modal">\n <div class="modal-header">标题</div>\n <div class="modal-body">内容</div>\n <div class="modal-footer">\n <button class="btn">取消</button>\n <button class="btn btn-primary">确认</button>\n </div>\n</div>`;
|
||||
} else if (comp.id === 'progress') {
|
||||
pre.textContent = `<div class="progress">\n <div class="progress-bar" style="width: 50%;">50%</div>\n</div>`;
|
||||
} else if (comp.id === 'switch') {
|
||||
pre.textContent = `<label class="switch">\n <input type="checkbox">\n <span class="slider"></span>\n</label>`;
|
||||
} else if (comp.id === 'avatar') {
|
||||
pre.textContent = `<div class="avatar-group">\n <div class="avatar">A</div>\n <div class="avatar">B</div>\n</div>`;
|
||||
} else if (comp.id === 'breadcrumb') {
|
||||
pre.textContent = `<nav class="breadcrumb">\n <a href="#">首页</a> /\n <a href="#">分类</a> /\n <span>当前</span>\n</nav>`;
|
||||
}
|
||||
|
||||
codeDiv.appendChild(pre);
|
||||
|
||||
const copyBtn = document.createElement('button');
|
||||
copyBtn.className = 'copy-btn';
|
||||
copyBtn.textContent = '复制';
|
||||
copyBtn.onclick = () => copyCode(comp.id);
|
||||
codeDiv.appendChild(copyBtn);
|
||||
|
||||
section.appendChild(codeDiv);
|
||||
|
||||
const toggleBtn = document.createElement('button');
|
||||
toggleBtn.className = 'toggle-code-btn';
|
||||
toggleBtn.textContent = '查看代码 ▼';
|
||||
toggleBtn.onclick = () => toggleCode(comp.id);
|
||||
section.appendChild(toggleBtn);
|
||||
|
||||
content.appendChild(section);
|
||||
});
|
||||
}
|
||||
|
||||
// 切换代码显示/隐藏
|
||||
window.toggleCode = function(compId) {
|
||||
const codeDiv = document.getElementById(`code-${compId}`);
|
||||
const toggleBtn = document.querySelector(`#comp-${compId} .toggle-code-btn`);
|
||||
if (codeDiv.style.display === 'none' || !codeDiv.style.display) {
|
||||
codeDiv.style.display = 'block';
|
||||
toggleBtn.textContent = '隐藏代码 ▲';
|
||||
} else {
|
||||
codeDiv.style.display = 'none';
|
||||
toggleBtn.textContent = '查看代码 ▼';
|
||||
}
|
||||
};
|
||||
|
||||
// 复制代码
|
||||
window.copyCode = function(compId) {
|
||||
const codeDiv = document.getElementById(`code-${compId}`);
|
||||
const code = codeDiv.querySelector('pre').innerText;
|
||||
navigator.clipboard.writeText(code).then(() => {
|
||||
alert('代码已复制!');
|
||||
});
|
||||
};
|
||||
|
||||
// 滚动高亮
|
||||
function setupIntersectionObserver() {
|
||||
const sections = document.querySelectorAll('.comp-section');
|
||||
const navItems = document.querySelectorAll('#pg-sidebar li');
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const id = entry.target.id.replace('comp-', '');
|
||||
navItems.forEach(item => {
|
||||
item.classList.remove('active');
|
||||
if (item.dataset.target === id) {
|
||||
item.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.3 });
|
||||
|
||||
sections.forEach(section => observer.observe(section));
|
||||
}
|
||||
|
||||
// 汉堡菜单交互
|
||||
const menuToggle = document.getElementById('menuToggle');
|
||||
if (menuToggle) {
|
||||
menuToggle.addEventListener('click', () => {
|
||||
sidebar.classList.toggle('active');
|
||||
});
|
||||
}
|
||||
|
||||
// 窗口大小改变时,如果大于768px,强制移除active类
|
||||
window.addEventListener('resize', () => {
|
||||
if (window.innerWidth > 768) {
|
||||
sidebar.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
renderSidebar();
|
||||
renderContent();
|
||||
setupIntersectionObserver();
|
||||
});
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>登录卡片 - HoloLake Design System</title>
|
||||
<link rel="stylesheet" href="../components.css">
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
background: var(--bg-primary);
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 1rem;
|
||||
}
|
||||
.card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
.card h2 {
|
||||
margin-bottom: 1.5rem;
|
||||
text-align: center;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.input-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.input-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.25rem;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.input {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: 8px;
|
||||
background: var(--bg-input);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.checkbox-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.btn-block {
|
||||
width: 100%;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.switch-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.alert {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.register-fields {
|
||||
margin-top: 1rem;
|
||||
border-top: 1px solid var(--border-default);
|
||||
padding-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<h2 id="formTitle">登录</h2>
|
||||
<div id="loginForm">
|
||||
<div class="input-group">
|
||||
<label>用户名</label>
|
||||
<input type="text" class="input" id="loginUsername" placeholder="请输入用户名">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>密码</label>
|
||||
<input type="password" class="input" id="loginPassword" placeholder="请输入密码">
|
||||
</div>
|
||||
</div>
|
||||
<div id="registerForm" class="hidden">
|
||||
<div class="input-group">
|
||||
<label>用户名</label>
|
||||
<input type="text" class="input" id="regUsername" placeholder="请输入用户名">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>密码</label>
|
||||
<input type="password" class="input" id="regPassword" placeholder="请输入密码">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>确认密码</label>
|
||||
<input type="password" class="input" id="regConfirm" placeholder="请再次输入密码">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkbox-row">
|
||||
<label class="switch-label">
|
||||
<span>记住我</span>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="rememberMe">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</label>
|
||||
<button class="btn btn-ghost btn-sm" id="toggleFormBtn">注册账号</button>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-block" id="actionBtn">登录</button>
|
||||
<button class="btn btn-outline btn-block" id="switchToRegister" style="display: none;">已有账号?去登录</button>
|
||||
|
||||
<div id="messageArea" class="alert hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../components.js"></script>
|
||||
<script>
|
||||
(function() {
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
const registerForm = document.getElementById('registerForm');
|
||||
const formTitle = document.getElementById('formTitle');
|
||||
const actionBtn = document.getElementById('actionBtn');
|
||||
const toggleFormBtn = document.getElementById('toggleFormBtn');
|
||||
const switchToRegister = document.getElementById('switchToRegister');
|
||||
const rememberMe = document.getElementById('rememberMe');
|
||||
const messageArea = document.getElementById('messageArea');
|
||||
|
||||
let isLogin = true;
|
||||
|
||||
// 加载记住我状态
|
||||
const savedRemember = localStorage.getItem('rememberMe');
|
||||
if (savedRemember === 'true') {
|
||||
rememberMe.checked = true;
|
||||
}
|
||||
|
||||
// 记住我开关变化时保存
|
||||
rememberMe.addEventListener('change', function() {
|
||||
localStorage.setItem('rememberMe', this.checked);
|
||||
});
|
||||
|
||||
function showMessage(type, text) {
|
||||
messageArea.className = `alert alert-${type}`;
|
||||
messageArea.textContent = text;
|
||||
messageArea.classList.remove('hidden');
|
||||
setTimeout(() => {
|
||||
messageArea.classList.add('hidden');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function toggleForms() {
|
||||
isLogin = !isLogin;
|
||||
if (isLogin) {
|
||||
loginForm.classList.remove('hidden');
|
||||
registerForm.classList.add('hidden');
|
||||
formTitle.textContent = '登录';
|
||||
actionBtn.textContent = '登录';
|
||||
toggleFormBtn.textContent = '注册账号';
|
||||
} else {
|
||||
loginForm.classList.add('hidden');
|
||||
registerForm.classList.remove('hidden');
|
||||
formTitle.textContent = '注册';
|
||||
actionBtn.textContent = '注册';
|
||||
toggleFormBtn.textContent = '返回登录';
|
||||
}
|
||||
}
|
||||
|
||||
toggleFormBtn.addEventListener('click', toggleForms);
|
||||
|
||||
actionBtn.addEventListener('click', function() {
|
||||
if (isLogin) {
|
||||
const username = document.getElementById('loginUsername').value;
|
||||
const password = document.getElementById('loginPassword').value;
|
||||
if (!username || !password) {
|
||||
showMessage('error', '请输入用户名和密码');
|
||||
} else {
|
||||
showMessage('success', '登录成功(模拟)');
|
||||
}
|
||||
} else {
|
||||
const username = document.getElementById('regUsername').value;
|
||||
const password = document.getElementById('regPassword').value;
|
||||
const confirm = document.getElementById('regConfirm').value;
|
||||
if (!username || !password || !confirm) {
|
||||
showMessage('error', '请填写所有字段');
|
||||
} else if (password !== confirm) {
|
||||
showMessage('error', '两次密码不一致');
|
||||
} else {
|
||||
showMessage('success', '注册成功(模拟)');
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>设置面板 - HoloLake Design System</title>
|
||||
<link rel="stylesheet" href="../components.css">
|
||||
<style>
|
||||
body {
|
||||
background: var(--bg-primary);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 0;
|
||||
padding: 2rem 1rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.settings-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.breadcrumb {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.card h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.2rem;
|
||||
border-bottom: 1px solid var(--border-default);
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
.setting-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.setting-label {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.progress-container {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.progress {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.avatar-group {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: flex-end;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.alert {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="settings-container">
|
||||
<!-- 面包屑 -->
|
||||
<nav class="breadcrumb">
|
||||
<a href="#">首页</a> /
|
||||
<a href="#">设置</a> /
|
||||
<span>显示设置</span>
|
||||
</nav>
|
||||
|
||||
<!-- 卡片:外观设置 -->
|
||||
<div class="card">
|
||||
<h3>外观</h3>
|
||||
<div class="setting-item">
|
||||
<span class="setting-label">深色模式</span>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="darkModeToggle">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<span class="setting-label">通知</span>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="notificationToggle">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<span class="setting-label">自动保存</span>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="autoSaveToggle">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片:存储空间 -->
|
||||
<div class="card">
|
||||
<h3>存储空间</h3>
|
||||
<div class="progress-container">
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style="width: 65%;" id="storageBar">65%</div>
|
||||
</div>
|
||||
<p class="setting-label">已使用 6.5GB / 总空间 10GB</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片:团队成员 -->
|
||||
<div class="card">
|
||||
<h3>团队成员</h3>
|
||||
<div class="avatar-group">
|
||||
<div class="avatar">A</div>
|
||||
<div class="avatar">B</div>
|
||||
<div class="avatar">C</div>
|
||||
<div class="avatar">+2</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="button-group">
|
||||
<button class="btn btn-outline" id="resetBtn">重置默认</button>
|
||||
<button class="btn btn-primary" id="saveBtn">保存设置</button>
|
||||
</div>
|
||||
|
||||
<div id="messageArea" class="alert hidden"></div>
|
||||
</div>
|
||||
|
||||
<script src="../components.js"></script>
|
||||
<script>
|
||||
(function() {
|
||||
const darkModeToggle = document.getElementById('darkModeToggle');
|
||||
const notificationToggle = document.getElementById('notificationToggle');
|
||||
const autoSaveToggle = document.getElementById('autoSaveToggle');
|
||||
const saveBtn = document.getElementById('saveBtn');
|
||||
const resetBtn = document.getElementById('resetBtn');
|
||||
const messageArea = document.getElementById('messageArea');
|
||||
const html = document.documentElement;
|
||||
|
||||
// 加载本地存储的状态
|
||||
darkModeToggle.checked = localStorage.getItem('darkMode') === 'true' || false;
|
||||
notificationToggle.checked = localStorage.getItem('notifications') === 'true' || false;
|
||||
autoSaveToggle.checked = localStorage.getItem('autoSave') === 'true' || false;
|
||||
|
||||
// 深色模式开关联动全局主题
|
||||
darkModeToggle.addEventListener('change', function() {
|
||||
html.setAttribute('data-theme', this.checked ? 'dark' : 'light');
|
||||
localStorage.setItem('darkMode', this.checked);
|
||||
});
|
||||
|
||||
// 初始化主题
|
||||
if (darkModeToggle.checked) {
|
||||
html.setAttribute('data-theme', 'dark');
|
||||
} else {
|
||||
html.setAttribute('data-theme', 'light');
|
||||
}
|
||||
|
||||
function showMessage(type, text) {
|
||||
messageArea.className = `alert alert-${type}`;
|
||||
messageArea.textContent = text;
|
||||
messageArea.classList.remove('hidden');
|
||||
setTimeout(() => {
|
||||
messageArea.classList.add('hidden');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 保存设置
|
||||
saveBtn.addEventListener('click', function() {
|
||||
localStorage.setItem('darkMode', darkModeToggle.checked);
|
||||
localStorage.setItem('notifications', notificationToggle.checked);
|
||||
localStorage.setItem('autoSave', autoSaveToggle.checked);
|
||||
showMessage('success', '设置已保存');
|
||||
});
|
||||
|
||||
// 重置默认
|
||||
resetBtn.addEventListener('click', function() {
|
||||
if (confirm('确定要恢复默认设置吗?')) {
|
||||
darkModeToggle.checked = true; // 默认深色
|
||||
notificationToggle.checked = false;
|
||||
autoSaveToggle.checked = false;
|
||||
|
||||
// 应用默认
|
||||
html.setAttribute('data-theme', 'dark');
|
||||
localStorage.setItem('darkMode', 'true');
|
||||
localStorage.setItem('notifications', 'false');
|
||||
localStorage.setItem('autoSave', 'false');
|
||||
|
||||
showMessage('info', '已恢复默认设置');
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue