diff --git a/src/App.vue b/src/App.vue index 00bfd6c..6a99540 100644 --- a/src/App.vue +++ b/src/App.vue @@ -153,835 +153,3 @@ async function searchNode(provider: "google" | "perplexity") { - diff --git a/src/main.ts b/src/main.ts index b670de8..4492964 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,7 @@ import { createApp } from "vue"; import App from "./App.vue"; +import "./styles/base.css"; +import "./styles/layout.css"; +import "./styles/common.css"; createApp(App).mount("#app"); diff --git a/src/pages/AdvancedCleanPage.vue b/src/pages/AdvancedCleanPage.vue index 2a2fe61..4d36344 100644 --- a/src/pages/AdvancedCleanPage.vue +++ b/src/pages/AdvancedCleanPage.vue @@ -110,3 +110,164 @@ const { expandedAdvanced, loading, runTask } = useAdvancedClean(props.showAlert) + + diff --git a/src/pages/DiskAnalysisPage.vue b/src/pages/DiskAnalysisPage.vue index 329a868..8419fd6 100644 --- a/src/pages/DiskAnalysisPage.vue +++ b/src/pages/DiskAnalysisPage.vue @@ -79,3 +79,135 @@ const { isFullScanning, fullScanProgress, treeData, startFullDiskScan, toggleNod + + diff --git a/src/pages/MemoryCleanPage.vue b/src/pages/MemoryCleanPage.vue index 76c4bb7..b208df3 100644 --- a/src/pages/MemoryCleanPage.vue +++ b/src/pages/MemoryCleanPage.vue @@ -76,3 +76,172 @@ const { state, startClean } = useMemoryClean(props.showAlert); + + diff --git a/src/styles/base.css b/src/styles/base.css new file mode 100644 index 0000000..aaa8dce --- /dev/null +++ b/src/styles/base.css @@ -0,0 +1,87 @@ +:root { + --primary-color: #007aff; + --primary-hover: #0063cc; + --bg-light: #fbfbfd; + --sidebar-bg: #ffffff; + --text-main: #1d1d1f; + --text-sec: #86868b; + --border-color: #e5e5e7; + --card-shadow: 0 12px 30px rgba(0, 0, 0, 0.04); + --btn-shadow: 0 4px 12px rgba(0, 122, 255, 0.25); +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", sans-serif; + color: var(--text-main); + background-color: var(--bg-light); + height: 100vh; + overflow: hidden; + -webkit-font-smoothing: antialiased; +} + +#app { + height: 100%; +} + +.app-container { + display: flex; + height: 100%; +} + +.svg-icon { + display: inline-flex; + align-items: center; + justify-content: center; + vertical-align: middle; +} + +.svg-icon svg { + width: 1.25em; + height: 1.25em; + stroke: currentColor; + stroke-width: 2; + fill: none; + stroke-linecap: round; + stroke-linejoin: round; +} + +.svg-icon.big svg { + width: 2.5em; + height: 2.5em; +} + +.icon.svg-icon { + margin-right: 12px; + color: inherit; +} + +.icon.svg-icon svg { + width: 18px; + height: 18px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes modalIn { + from { opacity: 0; transform: scale(0.9) translateY(20px); } + to { opacity: 1; transform: scale(1) translateY(0); } +} + +@keyframes slideUp { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} diff --git a/src/styles/common.css b/src/styles/common.css new file mode 100644 index 0000000..e41879d --- /dev/null +++ b/src/styles/common.css @@ -0,0 +1,508 @@ +.btn-primary { + background-color: var(--primary-color); + color: white; + border: none; + padding: 12px 40px; + border-radius: 12px; + font-size: 15px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: var(--btn-shadow); + flex-shrink: 0; +} + +.btn-sm { + padding: 8px 24px; + font-size: 14px; + border-radius: 10px; +} + +.btn-primary:hover { + background-color: var(--primary-hover); + transform: translateY(-1.5px); + box-shadow: 0 6px 16px rgba(0, 122, 255, 0.35); +} + +.btn-primary:active { + transform: translateY(0); +} + +.btn-primary:disabled { + background-color: #d1d1d6; + box-shadow: none; + cursor: not-allowed; + transform: none; +} + +.btn-secondary { + background-color: white; + color: var(--text-main); + border: 1px solid var(--border-color); + padding: 10px 28px; + border-radius: 10px; + font-size: 15px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; +} + +.btn-secondary:hover { + background-color: #f5f5f7; +} + +.main-action { + margin: 24px 0; + display: flex; + justify-content: center; +} + +.result-card { + background: white; + border-radius: 24px; + padding: 32px 40px; + width: 100%; + box-shadow: var(--card-shadow); + text-align: center; + border: 1px solid rgba(0, 0, 0, 0.02); +} + +.result-header { + margin-bottom: 24px; +} + +.result-icon { + font-size: 28px; + display: block; + margin-bottom: 8px; +} + +.result-header h2 { + font-size: 20px; + font-weight: 700; + color: var(--text-main); +} + +.result-stats { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 32px; +} + +.stat-item { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; +} + +.stat-value { + display: flex; + align-items: baseline; + justify-content: center; + font-size: 38px; + font-weight: 800; + color: var(--primary-color); + letter-spacing: -1.2px; + line-height: 1; + margin-bottom: 6px; + white-space: nowrap; +} + +.stat-value .unit { + font-size: 14px; + font-weight: 700; + margin-left: 3px; + letter-spacing: 0; + opacity: 0.9; +} + +.stat-label { + font-size: 13px; + color: var(--text-sec); + font-weight: 500; +} + +.stat-divider { + width: 1px; + height: 40px; + background-color: #f2f2f7; + margin: 0 16px; + flex-shrink: 0; +} + +.main-btn { + width: 180px; +} + +.scan-circle-container { + width: 200px; + height: 200px; + margin: 0 auto; +} + +.scan-circle { + width: 100%; + height: 100%; + border-radius: 50%; + border: 2px solid #f2f2f7; + display: flex; + align-items: center; + justify-content: center; + position: relative; +} + +.scan-circle.scanning { + border-color: transparent; +} + +.scan-circle.scanning::before { + content: ""; + position: absolute; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + border-radius: 50%; + border: 3px solid var(--primary-color); + border-top-color: transparent; + animation: spin 1s linear infinite; +} + +.scan-inner { + width: 168px; + height: 168px; + border-radius: 50%; + background: white; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05); + transition: transform 0.2s ease; +} + +.scan-inner:hover { + transform: scale(1.03); +} + +.scan-btn-text { + font-size: 18px; + font-weight: 700; + color: var(--primary-color); +} + +.scan-percent { + font-size: 36px; + font-weight: 800; + color: var(--primary-color); + letter-spacing: -1px; +} + +.stat-value.highlight-gray { + color: #8e8e93; +} + +.done-card { + border: 2px solid #e8f5e9; +} + +.result-icon.success { + color: #34c759; + font-size: 48px; + margin-bottom: 12px; + display: block; +} + +.scanning-loader, +.scanning-overlay { + padding: 100px 40px; + text-align: center; + color: var(--text-sec); +} + +.scanning-status { + margin-top: 16px; +} + +.scanning-main-text { + font-size: 16px; + font-weight: 600; + color: var(--text-main); + margin-bottom: 12px; +} + +.scanning-stats-row { + margin-bottom: 16px; +} + +.stat-badge { + background: #ebf4ff; + color: var(--primary-color); + padding: 6px 16px; + border-radius: 20px; + font-size: 13px; + font-weight: 700; +} + +.scanning-current-path { + font-size: 12px; + color: var(--text-sec); + max-width: 500px; + margin: 0 auto; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + opacity: 0.8; +} + +.spinner { + width: 44px; + height: 44px; + border: 3px solid #f2f2f7; + border-top: 3px solid var(--primary-color); + border-radius: 50%; + animation: spin 0.8s linear infinite; + margin: 0 auto 24px; +} + +.detail-list { + background: white; + border-radius: 20px; + padding: 32px; + box-shadow: var(--card-shadow); + margin-top: 40px; + border: 1px solid rgba(0, 0, 0, 0.02); +} + +.list-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.list-header h3 { + font-size: 18px; + margin-bottom: 0; + font-weight: 700; + color: var(--text-main); +} + +.list-actions { + display: flex; + gap: 8px; +} + +.btn-text { + background: none; + border: none; + color: var(--primary-color); + font-size: 12px; + font-weight: 600; + cursor: pointer; + padding: 6px 10px; + border-radius: 8px; + transition: all 0.2s; +} + +.btn-text:hover { + background-color: #f0f7ff; +} + +.detail-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 0; + border-bottom: 1px solid #f5f5f7; + font-size: 14px; + color: #424245; + transition: all 0.2s ease; + cursor: pointer; +} + +.detail-item:hover { + background-color: #fafafb; + padding-left: 8px; + padding-right: 8px; + border-radius: 8px; +} + +.detail-item.disabled { + opacity: 0.5; +} + +.detail-item:last-child { + border-bottom: none; +} + +.item-info { + display: flex; + align-items: center; + gap: 12px; +} + +.checkbox-container { + display: block; + position: relative; + width: 20px; + height: 20px; + cursor: pointer; + user-select: none; +} + +.checkbox-container input { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; +} + +.checkmark { + position: absolute; + top: 0; + left: 0; + height: 20px; + width: 20px; + background-color: #f2f2f7; + border-radius: 6px; + transition: all 0.2s; + border: 1px solid #e5e5e7; +} + +.checkbox-container:hover input ~ .checkmark { + background-color: #e5e5e7; +} + +.checkbox-container input:checked ~ .checkmark { + background-color: var(--primary-color); + border-color: var(--primary-color); +} + +.checkmark:after { + content: ""; + position: absolute; + display: none; + left: 6px; + top: 1px; + width: 5px; + height: 10px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} + +.checkbox-container input:checked ~ .checkmark:after { + display: block; +} + +.item-size { + font-weight: 600; + color: var(--primary-color); +} + +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.2); + backdrop-filter: blur(8px); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + animation: fadeIn 0.2s ease; +} + +.modal-card { + background: white; + width: 400px; + border-radius: 24px; + padding: 32px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15); + text-align: center; + animation: modalIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.modal-header { + margin-bottom: 20px; +} + +.modal-icon { + font-size: 40px; + display: block; + margin-bottom: 12px; +} + +.modal-header h3 { + font-size: 20px; + font-weight: 700; + color: var(--text-main); +} + +.modal-card.success .modal-header h3 { + color: #34c759; +} + +.modal-card.error .modal-header h3 { + color: #ff3b30; +} + +.modal-body { + margin-bottom: 32px; +} + +.modal-body p { + color: var(--text-sec); + font-size: 15px; + line-height: 1.6; +} + +.modal-footer { + display: flex; + justify-content: center; +} + +.context-menu { + position: fixed; + background: white; + min-width: 180px; + border-radius: 12px; + padding: 6px; + z-index: 2000; + border: 1px solid rgba(0, 0, 0, 0.08); + animation: fadeIn 0.1s ease; +} + +.menu-item { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 14px; + font-size: 13px; + color: var(--text-main); + cursor: pointer; + border-radius: 8px; + transition: background 0.15s; +} + +.menu-item:hover { + background-color: #f2f2f7; + color: var(--primary-color); +} + +.menu-icon { + font-size: 16px; +} + +.menu-divider { + height: 1px; + background: #f5f5f7; + margin: 4px 0; +} diff --git a/src/styles/layout.css b/src/styles/layout.css new file mode 100644 index 0000000..e902505 --- /dev/null +++ b/src/styles/layout.css @@ -0,0 +1,183 @@ +.sidebar { + width: 240px; + background-color: #f8fafd; + border-right: 1px solid #e9eff6; + display: flex; + flex-direction: column; + padding: 40px 0 20px; + z-index: 10; +} + +.sidebar-header { + padding: 0 28px 36px; +} + +.brand { + font-size: 20px; + font-weight: 700; + color: var(--text-main); + letter-spacing: -0.3px; +} + +.sidebar-nav { + flex: 1; +} + +.nav-item, +.nav-item-header { + padding: 12px 20px; + margin: 4px 12px; + display: flex; + align-items: center; + cursor: pointer; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + color: #4a5568; + font-size: 14px; + font-weight: 500; + border-radius: 12px; +} + +.nav-item:hover, +.nav-item-header:hover { + background-color: #edf2f7; + color: #2d3748; +} + +.nav-item.active { + background-color: #ebf4ff; + color: #04448a; + font-weight: 600; +} + +.icon { + margin-right: 12px; + font-size: 18px; + width: 24px; + text-align: center; +} + +.arrow { + margin-left: auto; + transition: transform 0.3s; + font-size: 10px; + color: #a0aec0; +} + +.arrow.open { + transform: rotate(180deg); + color: #4a5568; +} + +.nav-sub-items { + margin-bottom: 8px; +} + +.nav-sub-item { + padding: 10px 20px 10px 52px; + margin: 2px 12px; + cursor: pointer; + font-size: 13px; + color: #718096; + transition: all 0.2s; + border-radius: 10px; +} + +.nav-sub-item:hover { + background-color: #edf2f7; + color: #2d3748; +} + +.nav-sub-item.active { + color: #007aff; + font-weight: 600; + background-color: #ebf4ff; +} + +.sidebar-footer { + padding: 0 20px; + text-align: center; +} + +.version { + font-size: 12px; + color: #cbd5e0; + font-weight: 500; + letter-spacing: 0.2px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + +.content { + flex: 1; + padding: 40px 60px; + overflow-y: auto; + height: 100%; + scrollbar-gutter: stable; +} + +.content:has(.page-container.full-width) { + overflow-y: hidden; + padding-bottom: 24px; +} + +.page-container { + max-width: 800px; + margin: 0 auto; + padding-bottom: 0; + transition: max-width 0.4s ease; +} + +.page-container.full-width { + max-width: 1400px; + height: calc(100vh - 64px); + display: flex; + flex-direction: column; +} + +.page-header { + display: flex; + align-items: flex-end; + justify-content: space-between; + margin-bottom: 16px; + padding-bottom: 16px; + border-bottom: 1px solid var(--border-color); + flex-shrink: 0; +} + +.header-info { + display: flex; + flex-direction: column; + gap: 4px; +} + +.page-header h1 { + font-size: 22px; + font-weight: 700; + margin-bottom: 0; + color: var(--text-main); + line-height: 1.2; +} + +.page-header p { + color: var(--text-sec); + font-size: 13px; + margin-bottom: 0; + line-height: 1.2; +} + +.header-actions { + display: flex; + align-items: center; +} + +.placeholder-page { + padding-top: 120px; + text-align: center; + color: var(--text-sec); +} + +.empty-icon { + font-size: 64px; + display: block; + margin-bottom: 24px; + opacity: 0.5; +}