Add server components
build-winui / winui (push) Has been cancelled

This commit is contained in:
QWQLwToo
2026-06-26 13:28:09 +08:00
parent 7ecc6a8923
commit 079ee4eaeb
168 changed files with 37475 additions and 0 deletions
+851
View File
@@ -0,0 +1,851 @@
/* 简约风格的后台管理样式 */
:root {
--primary: #0071e3;
--success: #34c759;
--danger: #ef4444;
--warning: #ff9500;
--bg: #f5f5f7;
--card: #ffffff;
--text: #1d1d1f;
--text-secondary: #86868b;
--border: rgba(0, 0, 0, 0.1);
--shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
--shadow-hover: 0 4px 16px rgba(0, 0, 0, 0.12);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
}
/* 认证页面 */
.auth-container {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 1rem;
}
.auth-box {
background: var(--card);
border-radius: 16px;
padding: 2.5rem;
width: 100%;
max-width: 420px;
box-shadow: var(--shadow-hover);
animation: fadeInUp 0.4s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.auth-header {
text-align: center;
margin-bottom: 2rem;
}
.auth-logo {
width: 64px;
height: 64px;
margin: 0 auto 1rem;
color: var(--primary);
}
.auth-header h1 {
font-size: 1.75rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.auth-header p {
color: var(--text-secondary);
font-size: 0.9rem;
}
.auth-form {
margin-top: 1.5rem;
}
.form-group {
margin-bottom: 1.25rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
font-size: 0.9rem;
color: var(--text);
}
.form-group input,
.form-group select {
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 1rem;
transition: all 0.2s;
background: var(--card);
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.1);
}
.form-group small {
display: block;
margin-top: 0.25rem;
color: var(--text-secondary);
font-size: 0.8rem;
}
.password-strength {
margin-top: 0.5rem;
height: 3px;
background: #e0e0e0;
border-radius: 2px;
overflow: hidden;
transition: all 0.3s;
}
.password-strength::after {
content: '';
display: block;
height: 100%;
width: 0%;
background: var(--danger);
transition: width 0.3s, background 0.3s;
}
.password-strength.weak::after {
width: 33%;
background: var(--danger);
}
.password-strength.medium::after {
width: 66%;
background: var(--warning);
}
.password-strength.strong::after {
width: 100%;
background: var(--success);
}
.btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
}
.btn-primary {
background: var(--primary);
color: white;
width: 100%;
justify-content: center;
}
.btn-primary:hover {
background: #0051a5;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 113, 227, 0.3);
}
.btn-secondary {
background: #e0e0e0;
color: var(--text);
}
.btn-secondary:hover {
background: #d0d0d0;
}
.btn-success {
background: var(--success);
color: white;
}
.btn-danger {
background: var(--danger);
color: white;
}
.btn-icon {
padding: 0.5rem;
background: transparent;
border: none;
color: var(--text-secondary);
cursor: pointer;
border-radius: 6px;
transition: all 0.2s;
}
.btn-icon:hover {
background: var(--bg);
color: var(--text);
}
.error-message {
margin-top: 1rem;
padding: 0.75rem 1rem;
background: #fee;
color: var(--danger);
border-radius: 8px;
display: none;
animation: fadeIn 0.3s;
}
.error-message.show {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.auth-footer {
margin-top: 1.5rem;
text-align: center;
}
.auth-link {
color: var(--primary);
text-decoration: none;
font-size: 0.9rem;
transition: color 0.2s;
}
.auth-link:hover {
color: #0051a5;
text-decoration: underline;
}
/* 管理界面 */
.admin-container {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.admin-header {
background: var(--card);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--border);
box-shadow: var(--shadow);
}
.header-left {
display: flex;
align-items: center;
gap: 1rem;
}
.back-link {
display: flex;
align-items: center;
color: var(--text-secondary);
text-decoration: none;
padding: 0.5rem;
border-radius: 6px;
transition: all 0.2s;
}
.back-link:hover {
background: var(--bg);
color: var(--text);
}
.admin-header h1 {
font-size: 1.5rem;
font-weight: 600;
}
.header-right {
display: flex;
align-items: center;
gap: 1rem;
}
#current-user {
color: var(--text-secondary);
font-size: 0.9rem;
}
.admin-layout {
display: flex;
flex: 1;
}
.admin-sidebar {
width: 260px;
background: var(--card);
border-right: 1px solid var(--border);
padding: 1rem 0;
}
.sidebar-nav {
display: flex;
flex-direction: column;
}
.nav-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1.5rem;
color: var(--text);
text-decoration: none;
transition: all 0.2s;
border-left: 3px solid transparent;
}
.nav-item:hover {
background: var(--bg);
color: var(--primary);
}
.nav-item.active {
background: rgba(0, 113, 227, 0.08);
color: var(--primary);
border-left-color: var(--primary);
font-weight: 500;
}
.admin-content {
flex: 1;
padding: 2rem;
overflow-y: auto;
}
.page {
display: none;
animation: fadeIn 0.3s;
}
.page.active {
display: block;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.page-header h2 {
font-size: 1.75rem;
font-weight: 600;
}
.button-group {
display: flex;
gap: 0.75rem;
}
/* 统计卡片 */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background: var(--card);
border-radius: 12px;
padding: 1.5rem;
display: flex;
align-items: center;
gap: 1rem;
box-shadow: var(--shadow);
transition: all 0.2s;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-hover);
}
.stat-icon {
font-size: 2.5rem;
}
.stat-info {
flex: 1;
}
.stat-value {
font-size: 2rem;
font-weight: 600;
color: var(--primary);
line-height: 1.2;
}
.stat-label {
color: var(--text-secondary);
font-size: 0.9rem;
margin-top: 0.25rem;
}
/* 表格 */
.table-container {
background: var(--card);
border-radius: 12px;
overflow: hidden;
box-shadow: var(--shadow);
}
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table thead {
background: var(--bg);
}
.data-table th {
padding: 1rem;
text-align: left;
font-weight: 600;
font-size: 0.9rem;
color: var(--text-secondary);
}
.data-table td {
padding: 1rem;
border-top: 1px solid var(--border);
}
.data-table tbody tr {
transition: background 0.2s;
}
.data-table tbody tr:hover {
background: var(--bg);
}
.badge {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 500;
background: var(--bg);
color: var(--text);
}
.empty-state {
padding: 2rem;
text-align: center;
color: var(--text-secondary);
}
/* 模态框 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
align-items: center;
justify-content: center;
animation: fadeIn 0.2s;
}
.modal.show {
display: flex;
}
.modal-content {
background: var(--card);
border-radius: 12px;
width: 90%;
max-width: 600px;
max-height: 90vh;
overflow-y: auto;
box-shadow: var(--shadow-hover);
animation: fadeInUp 0.3s;
}
.modal-header {
padding: 1.5rem;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h3 {
font-size: 1.25rem;
font-weight: 600;
}
.modal-close {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--text-secondary);
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
transition: all 0.2s;
}
.modal-close:hover {
background: var(--bg);
color: var(--text);
}
.modal-body {
padding: 1.5rem;
}
.modal-footer {
padding: 1.5rem;
border-top: 1px solid var(--border);
display: flex;
justify-content: flex-end;
gap: 1rem;
}
/* 文件浏览器 */
.file-browser {
background: var(--card);
border-radius: 12px;
padding: 1rem;
box-shadow: var(--shadow);
}
.file-item {
padding: 1rem;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
transition: background 0.2s;
}
.file-item:last-child {
border-bottom: none;
}
.file-item:hover {
background: var(--bg);
}
/* 编辑器 */
.editor-container {
background: var(--card);
border-radius: 12px;
overflow: hidden;
box-shadow: var(--shadow);
}
.code-editor {
width: 100%;
min-height: 500px;
padding: 1rem;
border: 1px solid var(--border);
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
resize: vertical;
background: var(--card);
color: var(--text);
}
.code-editor:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.1);
}
.config-controls {
display: flex;
gap: 0.75rem;
margin-bottom: 1rem;
align-items: center;
}
.config-controls select {
flex: 1;
padding: 0.75rem;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 1rem;
}
/* 日志 */
.logs-container {
background: var(--card);
border-radius: 12px;
padding: 1rem;
max-height: 600px;
overflow-y: auto;
box-shadow: var(--shadow);
}
.logs-content {
font-family: 'Courier New', monospace;
font-size: 0.85rem;
}
.log-entry {
padding: 0.75rem;
border-bottom: 1px solid var(--border);
display: flex;
gap: 1rem;
transition: background 0.2s;
}
.log-entry:last-child {
border-bottom: none;
}
.log-entry:hover {
background: var(--bg);
}
.log-time {
color: var(--text-secondary);
min-width: 150px;
}
.log-level {
font-weight: 600;
min-width: 60px;
}
.log-level.INFO {
color: var(--success);
}
.log-level.WARN {
color: var(--warning);
}
.log-level.ERROR {
color: var(--danger);
}
.log-message {
flex: 1;
}
/* 设置页面 */
.settings-container {
max-width: 1000px;
}
.settings-section {
margin-bottom: 2rem;
}
.section-header {
margin-bottom: 1rem;
}
.section-header h2 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 0.25rem;
}
.section-description {
color: var(--text-secondary);
font-size: 0.9rem;
}
.settings-card {
background: var(--card);
border-radius: 12px;
padding: 1.5rem;
box-shadow: var(--shadow);
}
.database-info,
.system-stats,
.os-info {
display: grid;
gap: 1rem;
}
.info-item,
.stat-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 0;
border-bottom: 1px solid var(--border);
}
.info-item:last-child,
.stat-item:last-child {
border-bottom: none;
}
.info-label,
.stat-label {
color: var(--text-secondary);
font-size: 0.9rem;
}
.info-value,
.stat-value {
font-weight: 500;
color: var(--text);
}
.status-badge {
padding: 0.25rem 0.75rem;
border-radius: 12px;
font-size: 0.85rem;
font-weight: 500;
}
.status-connected {
background: rgba(52, 199, 89, 0.1);
color: var(--success);
}
.database-password,
.database-convert {
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 1px solid var(--border);
}
.password-warning,
.convert-warning {
background: #fff3cd;
border: 1px solid var(--warning);
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
color: #856404;
font-size: 0.9rem;
}
.password-form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.password-form .form-group {
margin-bottom: 0;
}
#password-result,
#convert-result {
margin-top: 1rem;
padding: 1rem;
border-radius: 8px;
display: none;
}
#password-result.success,
#convert-result.success {
background: rgba(52, 199, 89, 0.1);
border: 1px solid var(--success);
color: var(--success);
display: block;
}
#password-result.error,
#convert-result.error {
background: rgba(239, 68, 68, 0.1);
border: 1px solid var(--danger);
color: var(--danger);
display: block;
}
.convert-form {
display: flex;
gap: 0.75rem;
align-items: center;
}
.convert-form select {
flex: 1;
padding: 0.75rem;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 1rem;
}
/* 响应式 */
@media (max-width: 768px) {
.admin-layout {
flex-direction: column;
}
.admin-sidebar {
width: 100%;
border-right: none;
border-bottom: 1px solid var(--border);
}
.sidebar-nav {
flex-direction: row;
overflow-x: auto;
}
.nav-item {
white-space: nowrap;
}
.stats-grid {
grid-template-columns: 1fr;
}
.page-header {
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
}
+523
View File
@@ -0,0 +1,523 @@
:root {
--background: #fbf7ef;
--foreground: #1c1917;
--card: rgba(255, 255, 255, 0.9);
--card-foreground: #1c1917;
--popover: #ffffff;
--popover-foreground: #1c1917;
--primary: #5f6f45;
--primary-foreground: #ffffff;
--secondary: #f5f5f4;
--secondary-foreground: #1c1917;
--muted: #f5f5f4;
--muted-foreground: #57534e;
--accent: #b56e45;
--accent-foreground: #ffffff;
--destructive: #b91c1c;
--destructive-foreground: #ffffff;
--border: #d6d3d1;
--input: #d6d3d1;
--ring: #7a8a67;
--radius: 0.75rem;
--shadow: 0 18px 40px rgba(28, 25, 23, 0.08);
}
* {
box-sizing: border-box;
}
body.shad-app {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
color: var(--foreground);
background:
radial-gradient(circle at top left, rgba(181, 110, 69, 0.1), transparent 20rem),
radial-gradient(circle at top right, rgba(122, 138, 103, 0.1), transparent 20rem),
var(--background);
}
.site-header {
border-bottom: 1px solid rgba(214, 211, 209, 0.8);
padding: 34px 0 30px;
}
.header-inner {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 24px;
}
.header-inner h1 {
margin: 10px 0 0;
max-width: 760px;
font-size: clamp(2rem, 4vw, 3rem);
line-height: 1.08;
letter-spacing: 0;
}
.header-inner p {
margin: 14px 0 0;
max-width: 760px;
color: var(--muted-foreground);
line-height: 1.8;
}
.header-actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 10px;
}
.eyebrow {
color: var(--primary);
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.summary-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14px;
margin-bottom: 26px;
}
.summary-card {
border: 1px solid var(--border);
border-radius: var(--radius);
background: rgba(255, 255, 255, 0.72);
padding: 16px;
}
.summary-card span {
color: var(--muted-foreground);
font-size: 0.86rem;
}
.summary-card strong {
display: block;
margin-top: 6px;
font-size: 1.25rem;
}
.summary-card p {
margin: 8px 0 0;
color: var(--muted-foreground);
line-height: 1.6;
}
.section-title {
display: flex;
align-items: end;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
}
.section-title h2 {
margin: 6px 0 0;
}
.section-title p {
margin: 0;
color: var(--muted-foreground);
}
a {
color: inherit;
text-decoration: none;
}
button {
font: inherit;
}
code {
border: 1px solid var(--border);
border-radius: 0.375rem;
padding: 0.125rem 0.35rem;
background: var(--muted);
font-family: "Cascadia Code", "JetBrains Mono", monospace;
}
.shad-container {
width: min(1180px, calc(100% - 32px));
margin: 0 auto;
}
.shad-hero {
border-bottom: 1px solid rgba(214, 211, 209, 0.8);
}
.shad-hero__inner {
min-height: 280px;
display: flex;
align-items: center;
padding: 48px 0 40px;
}
.shad-hero__copy {
max-width: 780px;
}
.shad-hero__title {
margin: 16px 0 0;
font-size: clamp(2rem, 4vw, 3.25rem);
line-height: 1.05;
letter-spacing: 0;
}
.shad-hero__text {
margin: 16px 0 0;
color: var(--muted-foreground);
line-height: 1.8;
}
.shad-main {
padding: 28px 0 56px;
}
.shad-section__head {
margin-bottom: 18px;
}
.shad-section__head h2 {
margin: 0;
font-size: 1.35rem;
}
.shad-section__head p {
margin: 6px 0 0;
color: var(--muted-foreground);
}
.shad-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 16px;
}
.shad-card,
.shad-dialog__content {
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--card);
color: var(--card-foreground);
box-shadow: var(--shadow);
backdrop-filter: blur(12px);
}
.shad-card {
padding: 20px;
}
.shad-card--state {
padding: 24px;
}
.shad-card--danger {
border-color: rgba(185, 28, 28, 0.24);
}
.shad-card--state h3,
.shad-product__title h3 {
margin: 0;
}
.shad-card--state p,
.shad-product__title p,
.shad-history__item p {
margin: 8px 0 0;
color: var(--muted-foreground);
line-height: 1.7;
}
.shad-product {
display: flex;
flex-direction: column;
gap: 16px;
}
.shad-product__head {
display: flex;
gap: 14px;
align-items: flex-start;
}
.shad-product__icon {
width: 52px;
height: 52px;
flex: 0 0 52px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 0.875rem;
background: rgba(22, 101, 52, 0.08);
color: var(--primary);
}
.shad-product__icon svg {
width: 24px;
height: 24px;
}
.shad-stack {
display: flex;
gap: 8px;
}
.shad-stack--row {
flex-direction: row;
}
.shad-stack--wrap {
flex-wrap: wrap;
}
.shad-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 28px;
padding: 0 10px;
border-radius: 999px;
border: 1px solid transparent;
background: rgba(22, 101, 52, 0.1);
color: var(--primary);
font-size: 0.82rem;
white-space: nowrap;
}
.shad-badge--outline {
background: transparent;
border-color: var(--border);
color: var(--muted-foreground);
}
.shad-badge--muted {
background: var(--muted);
color: var(--muted-foreground);
}
.shad-badge--subtle {
background: rgba(217, 119, 6, 0.12);
color: #92400e;
}
.shad-panel {
border: 1px solid rgba(214, 211, 209, 0.8);
border-radius: calc(var(--radius) - 0.15rem);
padding: 16px;
background: linear-gradient(180deg, rgba(28, 25, 23, 0.02), rgba(28, 25, 23, 0.04));
}
.shad-panel__main {
display: flex;
align-items: end;
justify-content: space-between;
gap: 12px;
}
.shad-panel__main strong {
display: block;
margin-top: 4px;
font-size: 1.4rem;
color: var(--primary);
}
.shad-meta-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
margin-top: 14px;
}
.shad-meta {
display: flex;
flex-direction: column;
gap: 4px;
}
.shad-muted {
color: var(--muted-foreground);
font-size: 0.86rem;
}
.shad-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.shad-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.shad-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 40px;
padding: 0 16px;
border-radius: calc(var(--radius) - 0.2rem);
border: 1px solid transparent;
cursor: pointer;
transition: background 140ms ease, border-color 140ms ease, transform 140ms ease;
}
.shad-button:hover {
transform: translateY(-1px);
}
.shad-button--primary {
background: var(--primary);
color: var(--primary-foreground);
}
.shad-button--primary:hover {
background: #14532d;
}
.shad-button--secondary {
background: var(--secondary);
color: var(--secondary-foreground);
border-color: var(--border);
}
.shad-button--secondary:hover {
background: #ede9e7;
}
.shad-dialog {
position: fixed;
inset: 0;
display: none;
align-items: center;
justify-content: center;
padding: 16px;
}
.shad-dialog.is-open {
display: flex;
}
.shad-dialog__overlay {
position: absolute;
inset: 0;
background: rgba(28, 25, 23, 0.45);
}
.shad-dialog__content {
position: relative;
width: min(680px, 100%);
max-height: min(80vh, 720px);
overflow: hidden;
}
.shad-dialog__header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 18px 20px;
border-bottom: 1px solid var(--border);
}
.shad-dialog__header h3 {
margin: 0;
}
.shad-dialog__body {
padding: 8px 20px 20px;
overflow: auto;
}
.shad-icon-button {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--border);
background: var(--popover);
color: var(--popover-foreground);
cursor: pointer;
}
.shad-history {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
}
.shad-history__item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 14px 0;
border-bottom: 1px solid rgba(214, 211, 209, 0.8);
}
.shad-history__item:last-child {
border-bottom: 0;
}
.shad-footer {
border-top: 1px solid rgba(214, 211, 209, 0.8);
background: rgba(255, 255, 255, 0.7);
}
.shad-footer__inner {
display: flex;
justify-content: space-between;
gap: 12px;
padding: 18px 0 24px;
color: var(--muted-foreground);
font-size: 0.9rem;
}
@media (max-width: 760px) {
.header-inner,
.section-title {
align-items: flex-start;
flex-direction: column;
}
.header-actions,
.summary-grid {
width: 100%;
}
.summary-grid {
grid-template-columns: 1fr;
}
.shad-hero__inner {
min-height: auto;
padding: 40px 0 32px;
}
.shad-meta-grid {
grid-template-columns: 1fr;
}
.shad-actions,
.shad-footer__inner,
.shad-panel__main,
.shad-history__item,
.shad-product__head {
flex-direction: column;
align-items: stretch;
}
.shad-button {
width: 100%;
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 MiB

+532
View File
@@ -0,0 +1,532 @@
// 后台管理 JavaScript
// 获取 Cookie
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
return null;
}
// API 请求封装
async function apiRequest(url, options = {}) {
const token = getCookie('token');
const headers = {
'Content-Type': 'application/json',
...options.headers,
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
try {
const response = await fetch(url, {
...options,
headers,
credentials: 'include',
});
const data = await response.json();
if (!response.ok) {
// 如果是401,跳转到登录页
if (response.status === 401) {
window.location.href = '/admin/login';
return;
}
throw new Error(data.error || '请求失败');
}
return data;
} catch (error) {
console.error('API Error:', error);
throw error;
}
}
// 加载当前用户信息
async function loadCurrentUser() {
try {
const data = await apiRequest('/admin/me');
const userEl = document.getElementById('current-user');
if (userEl && data.user) {
userEl.textContent = `${data.user.username}${data.user.is_admin ? ' (管理员)' : ''}`;
}
} catch (error) {
console.error('Load user error:', error);
}
}
// 登出
document.getElementById('logout-btn')?.addEventListener('click', async () => {
try {
await apiRequest('/admin/logout', { method: 'POST' });
document.cookie = 'token=; path=/; max-age=0';
window.location.href = '/admin/login';
} catch (error) {
console.error('Logout error:', error);
// 即使失败也跳转
document.cookie = 'token=; path=/; max-age=0';
window.location.href = '/admin/login';
}
});
// 页面导航
document.querySelectorAll('.nav-item').forEach(item => {
item.addEventListener('click', (e) => {
e.preventDefault();
const page = item.dataset.page;
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
item.classList.add('active');
document.getElementById(`page-${page}`).classList.add('active');
// 加载对应页面数据
if (page === 'routes') {
loadRoutes();
} else if (page === 'logs') {
loadLogs();
} else if (page === 'files') {
loadFiles();
} else if (page === 'config') {
loadConfig();
} else if (page === 'database') {
loadDatabaseInfo();
}
});
});
// 加载数据库信息
async function loadDatabaseInfo() {
try {
const data = await apiRequest('/admin/api/database');
const dbDetails = document.getElementById('db-details');
const osDetails = document.getElementById('os-details');
const dbConfig = document.getElementById('db-config');
const passwordSection = document.getElementById('database-password-section');
dbDetails.innerHTML = `
<p><strong>类型:</strong> ${data.database.type}</p>
<p><strong>状态:</strong> ${data.database.status}</p>
${data.database.file ? `<p><strong>文件:</strong> ${data.database.file}</p>` : ''}
${data.database.cgo_support !== undefined ? `<p><strong>CGO 支持:</strong> ${data.database.cgo_support ? '是' : '否'}</p>` : ''}
`;
osDetails.innerHTML = `
<p><strong>操作系统:</strong> ${data.os.os}</p>
<p><strong>架构:</strong> ${data.os.arch}</p>
`;
// 加载数据库配置
try {
const configData = await apiRequest('/admin/api/database/config');
dbConfig.innerHTML = `
<p><strong>类型:</strong> ${configData.config.type}</p>
<p><strong>主机:</strong> ${configData.config.host}</p>
<p><strong>端口:</strong> ${configData.config.port}</p>
<p><strong>用户:</strong> ${configData.config.user}</p>
<p><strong>数据库:</strong> ${configData.config.database}</p>
<p><strong>已设置密码:</strong> ${configData.config.has_password ? '是' : '否'}</p>
`;
// 如果是 MySQL,显示密码修改界面
if (configData.config.type === 'mysql') {
passwordSection.style.display = 'block';
} else {
passwordSection.style.display = 'none';
}
} catch (error) {
console.error('Load database config error:', error);
dbConfig.innerHTML = '<p>无法加载配置信息</p>';
}
} catch (error) {
console.error('Load database info error:', error);
}
}
// 刷新数据库信息
document.getElementById('refresh-db-btn')?.addEventListener('click', loadDatabaseInfo);
// 转换数据库
document.getElementById('convert-db-btn')?.addEventListener('click', async () => {
const targetType = document.getElementById('target-db-type').value;
if (!confirm(`确定要转换数据库类型吗?\n\n目标类型: ${targetType.toUpperCase()}\n\n此操作会导出当前数据并导入到新数据库。请确保已备份数据!`)) {
return;
}
const resultEl = document.getElementById('convert-result');
resultEl.className = '';
resultEl.textContent = '正在转换...';
resultEl.style.display = 'block';
try {
const result = await apiRequest('/admin/api/database/convert', {
method: 'POST',
body: JSON.stringify({
target_type: targetType,
}),
});
resultEl.className = 'success';
resultEl.textContent = result.message || '数据库转换成功!';
loadDatabaseInfo();
loadLogs();
} catch (error) {
resultEl.className = 'error';
resultEl.textContent = '转换失败: ' + error.message;
}
});
// 更新数据库密码
document.getElementById('password-form')?.addEventListener('submit', async (e) => {
e.preventDefault();
const currentPassword = document.getElementById('current-password').value;
const newPassword = document.getElementById('new-password').value;
const confirmPassword = document.getElementById('confirm-password').value;
if (newPassword !== confirmPassword) {
alert('新密码和确认密码不一致!');
return;
}
if (!confirm('确定要更新数据库 root 密码吗?\n\n更新后需要修改环境变量 DB_PASSWORD 并重启服务器!')) {
return;
}
const resultEl = document.getElementById('password-result');
resultEl.className = '';
resultEl.textContent = '正在更新密码...';
resultEl.style.display = 'block';
try {
const result = await apiRequest('/admin/api/database/password', {
method: 'POST',
body: JSON.stringify({
current_password: currentPassword,
new_password: newPassword,
confirm_password: confirmPassword,
}),
});
resultEl.className = 'success';
resultEl.textContent = result.message || '密码更新成功!';
// 清空表单
document.getElementById('password-form').reset();
loadLogs();
} catch (error) {
resultEl.className = 'error';
resultEl.textContent = '更新失败: ' + error.message;
}
});
// 加载系统信息
async function loadSystemInfo() {
try {
const data = await apiRequest('/admin/api/system');
document.getElementById('stat-users').textContent = data.users;
document.getElementById('stat-routes').textContent = data.routes;
document.getElementById('stat-logs').textContent = data.logs;
document.getElementById('stat-time').textContent = data.server_time;
} catch (error) {
console.error('Load system info error:', error);
}
}
// 加载路由
async function loadRoutes() {
try {
const data = await apiRequest('/admin/api/routes');
const tbody = document.getElementById('routes-table-body');
if (!tbody) return;
tbody.innerHTML = '';
if (data.routes && data.routes.length > 0) {
data.routes.forEach(route => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${route.id}</td>
<td><span class="badge">${route.method}</span></td>
<td>${route.path}</td>
<td>${route.type}</td>
<td>${route.description || '-'}</td>
<td>${route.is_active ? '✅' : '❌'}</td>
<td>
<button class="btn btn-secondary" onclick="editRoute(${route.id})">编辑</button>
<button class="btn btn-danger" onclick="deleteRoute(${route.id})">删除</button>
</td>
`;
tbody.appendChild(tr);
});
} else {
tbody.innerHTML = '<tr><td colspan="7" class="empty-state">暂无路由</td></tr>';
}
} catch (error) {
console.error('Load routes error:', error);
const tbody = document.getElementById('routes-table-body');
if (tbody) {
tbody.innerHTML = '<tr><td colspan="7" class="empty-state">加载失败: ' + error.message + '</td></tr>';
}
}
}
// 添加路由
document.getElementById('add-route-btn')?.addEventListener('click', () => {
document.getElementById('route-modal-title').textContent = '添加路由';
document.getElementById('route-form').reset();
document.getElementById('route-id').value = '';
document.getElementById('route-modal').classList.add('show');
});
// 编辑路由
window.editRoute = async function(id) {
try {
const data = await apiRequest('/admin/api/routes');
const route = data.routes.find(r => r.id === id);
if (route) {
document.getElementById('route-modal-title').textContent = '编辑路由';
document.getElementById('route-id').value = route.id;
document.getElementById('route-method').value = route.method;
document.getElementById('route-path').value = route.path;
document.getElementById('route-type').value = route.type;
document.getElementById('route-handler').value = route.handler;
document.getElementById('route-description').value = route.description || '';
document.getElementById('route-active').checked = route.is_active;
document.getElementById('route-order').value = route.order;
document.getElementById('route-modal').classList.add('show');
}
} catch (error) {
console.error('Edit route error:', error);
}
};
// 删除路由
window.deleteRoute = async function(id) {
if (!confirm('确定要删除这个路由吗?')) return;
try {
await apiRequest(`/admin/api/routes/${id}`, { method: 'DELETE' });
loadRoutes();
} catch (error) {
alert('删除失败: ' + error.message);
}
};
// 保存路由
document.getElementById('route-save-btn')?.addEventListener('click', async () => {
const id = document.getElementById('route-id').value;
const routeData = {
method: document.getElementById('route-method').value,
path: document.getElementById('route-path').value,
type: document.getElementById('route-type').value,
handler: document.getElementById('route-handler').value,
description: document.getElementById('route-description').value,
is_active: document.getElementById('route-active').checked,
order: parseInt(document.getElementById('route-order').value) || 0,
};
try {
if (id) {
await apiRequest(`/admin/api/routes/${id}`, {
method: 'PUT',
body: JSON.stringify(routeData),
});
} else {
await apiRequest('/admin/api/routes', {
method: 'POST',
body: JSON.stringify(routeData),
});
}
document.getElementById('route-modal').classList.remove('show');
loadRoutes();
} catch (error) {
alert('保存失败: ' + error.message);
}
});
// 关闭模态框
document.querySelectorAll('.modal-close, #route-cancel-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.getElementById('route-modal').classList.remove('show');
});
});
// 加载日志
async function loadLogs() {
try {
const data = await apiRequest('/admin/api/logs?limit=100');
const logsEl = document.getElementById('logs-content');
if (!logsEl) return;
logsEl.innerHTML = '';
if (data.logs && data.logs.length > 0) {
data.logs.forEach(log => {
const entry = document.createElement('div');
entry.className = 'log-entry';
entry.innerHTML = `
<span class="log-time">${log.time || ''}</span>
<span class="log-level ${log.level || 'INFO'}">${log.level || 'INFO'}</span>
<span class="log-message">${log.message || ''}</span>
`;
logsEl.appendChild(entry);
});
logsEl.scrollTop = logsEl.scrollHeight;
} else {
logsEl.innerHTML = '<div class="empty-state">暂无日志</div>';
}
} catch (error) {
console.error('Load logs error:', error);
const logsEl = document.getElementById('logs-content');
if (logsEl) {
logsEl.innerHTML = '<div class="empty-state">加载失败: ' + error.message + '</div>';
}
}
}
// 刷新日志
document.getElementById('refresh-logs-btn')?.addEventListener('click', loadLogs);
// 清空日志
document.getElementById('clear-logs-btn')?.addEventListener('click', () => {
const logsEl = document.getElementById('logs-content');
if (logsEl) {
logsEl.innerHTML = '<div class="empty-state">日志已清空</div>';
}
});
// 加载文件
async function loadFiles() {
const fileBrowser = document.getElementById('file-browser');
if (!fileBrowser) return;
try {
const data = await apiRequest('/admin/api/files?dir=public/downloads');
fileBrowser.innerHTML = '';
if (data.files && data.files.length > 0) {
data.files.forEach(file => {
const item = document.createElement('div');
item.className = 'file-item';
item.innerHTML = `
<div>
<strong>${file.name}</strong>
<small style="display: block; color: #86868b;">
${file.is_dir ? '📁 目录' : `📄 ${formatBytes(file.size)}`}${file.mod_time || ''}
</small>
</div>
${!file.is_dir ? `<button class="btn btn-secondary" onclick="readFile('${file.name}')">查看</button>` : ''}
`;
fileBrowser.appendChild(item);
});
} else {
fileBrowser.innerHTML = '<div class="empty-state">暂无文件</div>';
}
} catch (error) {
console.error('Load files error:', error);
fileBrowser.innerHTML = '<div class="empty-state">加载失败: ' + error.message + '</div>';
}
}
document.getElementById('refresh-files-btn')?.addEventListener('click', loadFiles);
// 读取文件
window.readFile = async function(filename) {
const fullPath = `public/downloads/${filename}`;
try {
const data = await apiRequest(`/admin/api/file?path=${encodeURIComponent(fullPath)}`);
// 显示文件内容在模态框中或新窗口
const content = data.content.substring(0, 5000) + (data.content.length > 5000 ? '\n\n... (内容过长,已截断)' : '');
alert('文件内容:\n\n' + content);
} catch (error) {
alert('读取文件失败: ' + error.message);
}
};
// 加载配置
async function loadConfig() {
const file = document.getElementById('config-select').value;
try {
const response = await fetch(`/public/${file}`);
const data = await response.json();
document.getElementById('config-editor').value = JSON.stringify(data, null, 2);
} catch (error) {
console.error('Load config error:', error);
}
}
document.getElementById('load-config-btn')?.addEventListener('click', loadConfig);
// 保存配置
document.getElementById('save-config-btn')?.addEventListener('click', async () => {
const file = document.getElementById('config-select').value;
const content = document.getElementById('config-editor').value;
try {
const jsonData = JSON.parse(content);
const result = await apiRequest('/admin/api/config', {
method: 'PUT',
body: JSON.stringify({
file: file,
content: jsonData,
reload: false, // 仅保存
}),
});
alert(result.message || '配置保存成功!');
} catch (error) {
alert('保存失败: ' + error.message);
}
});
// 保存并立即加载配置
document.getElementById('save-reload-config-btn')?.addEventListener('click', async () => {
const file = document.getElementById('config-select').value;
const content = document.getElementById('config-editor').value;
try {
const jsonData = JSON.parse(content);
const result = await apiRequest('/admin/api/config', {
method: 'PUT',
body: JSON.stringify({
file: file,
content: jsonData,
reload: true, // 保存并立即加载
}),
});
alert(result.message || '配置已保存并立即生效!');
// 刷新日志以显示加载信息
if (document.getElementById('page-logs')?.classList.contains('active')) {
loadLogs();
}
} catch (error) {
alert('保存失败: ' + error.message);
}
});
// 工具函数
function formatBytes(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', () => {
loadCurrentUser();
loadSystemInfo();
// 定期更新系统信息
setInterval(loadSystemInfo, 30000); // 每30秒更新一次
});
+193
View File
@@ -0,0 +1,193 @@
// 认证相关 JavaScript
// 检查是否已登录
async function checkAuth() {
try {
const token = getCookie('token');
if (!token) {
return false;
}
const response = await fetch('/admin/me', {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (response.ok) {
const data = await response.json();
// 已登录,跳转到管理页面
window.location.href = '/admin';
return true;
}
} catch (error) {
console.error('Auth check error:', error);
}
return false;
}
// 获取 Cookie
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
return null;
}
// API 请求封装
async function apiRequest(url, options = {}) {
const token = getCookie('token');
const headers = {
'Content-Type': 'application/json',
...options.headers
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(url, {
...options,
headers
});
if (!response.ok) {
const error = await response.json().catch(() => ({ error: '请求失败' }));
throw new Error(error.error || '请求失败');
}
return await response.json();
}
// 显示错误消息
function showError(message) {
const errorEl = document.getElementById('auth-error');
if (errorEl) {
errorEl.textContent = message;
errorEl.classList.add('show');
setTimeout(() => {
errorEl.classList.remove('show');
}, 5000);
}
}
// 登录表单处理
const loginForm = document.getElementById('login-form');
if (loginForm) {
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(loginForm);
const data = {
username: formData.get('username'),
password: formData.get('password')
};
const submitBtn = loginForm.querySelector('button[type="submit"]');
const originalText = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = '<span>登录中...</span>';
try {
const result = await apiRequest('/admin/login', {
method: 'POST',
body: JSON.stringify(data)
});
// 设置 token cookie
document.cookie = `token=${result.token}; path=/; max-age=86400; SameSite=Lax`;
// 跳转到管理页面
window.location.href = '/admin';
} catch (error) {
showError(error.message || '登录失败');
submitBtn.disabled = false;
submitBtn.innerHTML = originalText;
}
});
}
// 注册表单处理
const registerForm = document.getElementById('register-form');
if (registerForm) {
// 密码强度检测
const passwordInput = document.getElementById('password');
const passwordStrength = document.getElementById('password-strength');
if (passwordInput && passwordStrength) {
passwordInput.addEventListener('input', (e) => {
const password = e.target.value;
let strength = 'weak';
if (password.length >= 8) {
const hasUpper = /[A-Z]/.test(password);
const hasLower = /[a-z]/.test(password);
const hasNumber = /[0-9]/.test(password);
const hasSpecial = /[^A-Za-z0-9]/.test(password);
const score = [hasUpper, hasLower, hasNumber, hasSpecial].filter(Boolean).length;
if (score >= 3) {
strength = 'strong';
} else if (score >= 2) {
strength = 'medium';
}
}
passwordStrength.className = `password-strength ${strength}`;
});
}
registerForm.addEventListener('submit', async (e) => {
e.preventDefault();
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirm-password').value;
if (password !== confirmPassword) {
showError('两次输入的密码不一致');
return;
}
const formData = new FormData(registerForm);
const data = {
username: formData.get('username'),
email: formData.get('email'),
password: password
};
const submitBtn = registerForm.querySelector('button[type="submit"]');
const originalText = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = '<span>注册中...</span>';
try {
const result = await apiRequest('/admin/register', {
method: 'POST',
body: JSON.stringify(data)
});
// 设置 token cookie
document.cookie = `token=${result.token}; path=/; max-age=86400; SameSite=Lax`;
// 跳转到管理页面
window.location.href = '/admin';
} catch (error) {
showError(error.message || '注册失败');
submitBtn.disabled = false;
submitBtn.innerHTML = originalText;
}
});
}
// 页面加载时检查认证状态
document.addEventListener('DOMContentLoaded', () => {
// 如果已经在登录/注册页面,不需要检查
if (window.location.pathname.includes('/admin/login') ||
window.location.pathname.includes('/admin/register')) {
return;
}
// 检查是否已登录
checkAuth();
});
+128
View File
@@ -0,0 +1,128 @@
// 设置页面 JavaScript
// 加载数据库信息
async function loadDatabaseInfo() {
try {
const data = await apiRequest('/admin/api/database');
const configData = await apiRequest('/admin/api/database/config');
document.getElementById('db-type').textContent = data.database.type;
document.getElementById('db-status').textContent = data.database.status;
document.getElementById('db-status').className = 'info-value status-badge status-connected';
if (data.database.file) {
document.getElementById('db-file-item').style.display = 'flex';
document.getElementById('db-file').textContent = data.database.file;
}
// 如果是 MySQL,显示密码修改界面
if (configData.config.type === 'mysql') {
document.getElementById('database-password-section').style.display = 'block';
}
// 操作系统信息
document.getElementById('os-type').textContent = data.os.os;
document.getElementById('os-arch').textContent = data.os.arch;
} catch (error) {
console.error('Load database info error:', error);
}
}
// 加载系统信息
async function loadSystemInfo() {
try {
const data = await apiRequest('/admin/api/system');
document.getElementById('stat-users').textContent = data.users;
document.getElementById('stat-routes').textContent = data.routes;
document.getElementById('stat-logs').textContent = data.logs;
document.getElementById('stat-time').textContent = data.server_time;
} catch (error) {
console.error('Load system info error:', error);
}
}
// 更新数据库密码
const passwordForm = document.getElementById('password-form');
if (passwordForm) {
passwordForm.addEventListener('submit', async (e) => {
e.preventDefault();
const currentPassword = document.getElementById('current-password').value;
const newPassword = document.getElementById('new-password').value;
const confirmPassword = document.getElementById('confirm-password').value;
if (newPassword !== confirmPassword) {
alert('新密码和确认密码不一致!');
return;
}
if (!confirm('确定要更新数据库 root 密码吗?\n\n更新后需要修改环境变量 DB_PASSWORD 并重启服务器!')) {
return;
}
const resultEl = document.getElementById('password-result');
resultEl.className = '';
resultEl.textContent = '正在更新密码...';
resultEl.style.display = 'block';
try {
const result = await apiRequest('/admin/api/database/password', {
method: 'POST',
body: JSON.stringify({
current_password: currentPassword,
new_password: newPassword,
confirm_password: confirmPassword,
}),
});
resultEl.className = 'success';
resultEl.textContent = result.message || '密码更新成功!';
passwordForm.reset();
} catch (error) {
resultEl.className = 'error';
resultEl.textContent = '更新失败: ' + error.message;
}
});
}
// 转换数据库
const convertDbBtn = document.getElementById('convert-db-btn');
if (convertDbBtn) {
convertDbBtn.addEventListener('click', async () => {
const targetType = document.getElementById('target-db-type').value;
if (!confirm(`确定要转换数据库类型吗?\n\n目标类型: ${targetType.toUpperCase()}\n\n此操作会导出当前数据并导入到新数据库。请确保已备份数据!`)) {
return;
}
const resultEl = document.getElementById('convert-result');
resultEl.className = '';
resultEl.textContent = '正在转换...';
resultEl.style.display = 'block';
try {
const result = await apiRequest('/admin/api/database/convert', {
method: 'POST',
body: JSON.stringify({
target_type: targetType,
}),
});
resultEl.className = 'success';
resultEl.textContent = result.message || '数据库转换成功!';
loadDatabaseInfo();
} catch (error) {
resultEl.className = 'error';
resultEl.textContent = '转换失败: ' + error.message;
}
});
}
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', () => {
loadDatabaseInfo();
loadSystemInfo();
// 定期更新系统信息
setInterval(loadSystemInfo, 30000); // 每30秒更新一次
});
+71
View File
@@ -0,0 +1,71 @@
{
"comment": "English Language Pack",
"nav.home": "Home",
"nav.toolbox": "Toolbox",
"nav.logs": "Logs",
"nav.settings": "Settings",
"home.greeting.morning": "Good morning, have a wonderful day.",
"home.greeting.noon": "Good afternoon, time for a break.",
"home.greeting.afternoon": "Good afternoon, keep up the good work.",
"home.greeting.evening": "Good evening, time to relax.",
"home.greeting.night": "It's late, please rest.",
"home.welcome": "Welcome to YMhut Box, wish you a great day.",
"home.announcement": "Announcement",
"home.announcement.failed": "Failed to load announcement...",
"home.announcement.viewFull": "View Full Announcement",
"home.search.placeholder": "Smart Search: Enter query...",
"home.search.button": "Search",
"home.search.disabled": "Smart Search is currently disabled",
"home.search.results.stats": "Found about {count} results (in {time}ms)",
"home.search.results.viewFull": "View Full Results (incl. Advanced Options)",
"home.search.results.empty.title": "No results found for \"{query}\"",
"home.search.results.empty.sub": "Please try different keywords.",
"home.search.failed.title": "Search Failed",
"home.updates": "Update Log",
"home.updates.close": "Close",
"home.updates.modal.title": "Full Announcement",
"tool.smartSearch.name": "Smart Search",
"tool.smartSearch.desc": "AI aggregated search for high-quality results",
"common.loading": "Loading...",
"common.search": "Search",
"common.backToToolbox": "Back to Toolbox",
"common.options": "Advanced Options",
"common.error": "Error",
"common.loading.tool": "Loading tool module...",
"common.notification.title.success": "Success",
"common.notification.title.error": "Error",
"common.notification.title.info": "Info",
"settings.appearance": "Appearance",
"settings.updates": "Update Management",
"settings.about": "About",
"settings.status.monitor": "Status Monitor",
"settings.status.cpu": "CPU",
"settings.status.mem": "Memory",
"settings.status.gpu": "GPU",
"settings.status.uptime": "Uptime",
"settings.appearance.title": "Appearance Settings",
"settings.appearance.theme": "Theme",
"settings.appearance.language": "Language",
"settings.appearance.language.auto": "Auto (Default)",
"settings.appearance.language.zh-CN": "简体中文",
"settings.appearance.language.en-US": "English",
"settings.appearance.language.restartMsg": "Language change will take effect after restart.",
"settings.appearance.bg": "Custom Background",
"settings.appearance.bg.select": "Select",
"settings.appearance.bg.clear": "Clear",
"settings.appearance.bg.opacity": "Background Opacity",
"settings.appearance.card.opacity": "Card Opacity",
"settings.traffic.title": "Traffic Statistics",
"settings.traffic.total": "Total Usage",
"settings.traffic.chart.empty.title": "No traffic history",
"settings.traffic.chart.empty.sub": "Data will be recorded starting today.",
"settings.update.title": "Update Management",
"settings.update.checkBtn": "Check for Updates",
"settings.update.checking": "Checking...",
"settings.update.checkDefault": "Click button to check for new version",
"settings.about.title": "About & Environment",
"settings.about.version": "Current Version",
"settings.about.developer": "Developer",
"settings.about.moreInfo": "More Info & Credits",
"settings.about.env.title": "Installed Environments"
}
+71
View File
@@ -0,0 +1,71 @@
{
"comment": "简体中文语言包",
"nav.home": "主页",
"nav.toolbox": "工具箱",
"nav.logs": "日志",
"nav.settings": "设置",
"home.greeting.morning": "早上好, 新的一天元气满满",
"home.greeting.noon": "中午好, 午休时间到了",
"home.greeting.afternoon": "下午好, 继续努力吧",
"home.greeting.evening": "晚上好, 放松一下吧",
"home.greeting.night": "凌晨了, 注意休息哦",
"home.welcome": "欢迎使用 YMhut Box, 愿你拥有美好的一天。",
"home.announcement": "公告",
"home.announcement.failed": "公告加载失败...",
"home.announcement.viewFull": "查看完整公告",
"home.search.placeholder": "智能搜索:输入查询内容...",
"home.search.button": "搜索",
"home.search.disabled": "智能搜索工具当前不可用",
"home.search.results.stats": "找到约 {count} 条结果 (耗时 {time}ms)",
"home.search.results.viewFull": "查看完整结果 (含高级选项)",
"home.search.results.empty.title": "未找到关于 \"{query}\" 的结果",
"home.search.results.empty.sub": "请尝试更换关键词。",
"home.search.failed.title": "搜索失败",
"home.updates": "更新日志",
"home.updates.close": "关闭",
"home.updates.modal.title": "完整公告",
"tool.smartSearch.name": "智能搜索",
"tool.smartSearch.desc": "AI 聚合搜索,获取高质量结果",
"common.loading": "加载中...",
"common.search": "搜索",
"common.backToToolbox": "返回工具箱",
"common.options": "高级选项",
"common.error": "错误",
"common.loading.tool": "正在初始化工具模块...",
"common.notification.title.success": "成功",
"common.notification.title.error": "错误",
"common.notification.title.info": "提示",
"settings.appearance": "外观",
"settings.updates": "更新管理",
"settings.about": "关于",
"settings.status.monitor": "状态监控",
"settings.status.cpu": "CPU",
"settings.status.mem": "内存",
"settings.status.gpu": "GPU",
"settings.status.uptime": "运行时长",
"settings.appearance.title": "外观设置",
"settings.appearance.theme": "界面主题",
"settings.appearance.language": "语言 (Language)",
"settings.appearance.language.auto": "自动 (Auto)",
"settings.appearance.language.zh-CN": "简体中文",
"settings.appearance.language.en-US": "English",
"settings.appearance.language.restartMsg": "语言设置将在重启后生效。",
"settings.appearance.bg": "自定义背景",
"settings.appearance.bg.select": "选择",
"settings.appearance.bg.clear": "清除",
"settings.appearance.bg.opacity": "背景透明度",
"settings.appearance.card.opacity": "卡片透明度",
"settings.traffic.title": "流量统计",
"settings.traffic.total": "累计使用流量",
"settings.traffic.chart.empty.title": "暂无历史流量数据",
"settings.traffic.chart.empty.sub": "数据将从今天开始记录",
"settings.update.title": "更新管理",
"settings.update.checkBtn": "检查更新",
"settings.update.checking": "正在检查...",
"settings.update.checkDefault": "点击按钮检查新版本",
"settings.about.title": "关于与软件环境",
"settings.about.version": "当前版本",
"settings.about.developer": "开发者",
"settings.about.moreInfo": "更多信息与鸣谢",
"settings.about.env.title": "已安装的开发环境"
}
+175
View File
@@ -0,0 +1,175 @@
{
"categories": [
{
"enabled": true,
"icon": "fas fa-image",
"id": "image",
"layout": {
"aspect_ratio": "16:9",
"columns": 1,
"show_preview": true,
"transition_effect": "fade"
},
"name": "随机图片",
"subcategories": [
{
"api_url": "https://xjj.ymhut.bid/xjj",
"description": "精选小姐姐图片",
"downloadable": true,
"id": "xjj",
"name": "小姐姐",
"refresh_interval": 30,
"supported_formats": [
"jpg",
"jpeg",
"png",
"webp"
],
"thumbnail_url": "https://pic2.zhimg.com/v2-379be37e0b4d372aa60046f9ce771f12_r.jpg"
},
{
"api_url": "https://v2.xxapi.cn/api/baisi?return=302",
"description": "随机白丝图片",
"downloadable": true,
"id": "baisi",
"name": "白丝",
"refresh_interval": 30,
"supported_formats": [
"jpg",
"jpeg",
"png",
"webp"
],
"thumbnail_url": "https://n.sinaimg.cn/sinacn10112/760/w640h920/20200126/4b00-innckcf8208822.jpg"
},
{
"api_url": "https://v2.xxapi.cn/api/heisi?return=302",
"description": "随机黑丝图片",
"downloadable": true,
"id": "heisi",
"name": "黑丝",
"refresh_interval": 30,
"supported_formats": [
"jpg",
"jpeg",
"png",
"webp"
],
"thumbnail_url": "https://img-baofun.zhhainiao.com/pcwallpaper_ugc_mobile/static/6902725194a8c081767ee82373d3b017.jpeg"
},
{
"api_url": "https://api.pearapi.ai/api/beautifulgirl?type=image",
"description": "三坑少女图(包含动漫、漫画、游戏)",
"downloadable": true,
"id": "third_girl",
"name": "三坑少女(4K)",
"refresh_interval": 30,
"supported_formats": [
"jpg",
"jpeg",
"png",
"webp"
],
"thumbnail_url": "https://www.sgpjbg.com/FileUpload/News/c358f121-6683-490b-beed-6debb44e4824.jpg"
},
{
"api_url": "https://apii.ctose.cn/api/cy/api/",
"description": "miku的随机图",
"downloadable": true,
"id": "miku",
"name": "初音未来",
"refresh_interval": 30,
"supported_formats": [
"jpg",
"jpeg",
"png",
"webp"
],
"thumbnail_url": "https://apii.ctose.cn/api/cy/api/"
},
{
"api_url": "https://api.suyanw.cn/api/mao.php",
"description": "猫羽雫的随机图",
"downloadable": true,
"id": "猫羽雫",
"name": "猫羽雫",
"refresh_interval": 30,
"supported_formats": [
"jpg",
"jpeg",
"png",
"webp"
],
"thumbnail_url": "https://api.suyanw.cn/api/mao.php"
},
{
"api_url": "https://api.suyanw.cn/api/scenery.php",
"description": "随机高清壁纸",
"downloadable": true,
"id": "wappller",
"name": "高清壁纸",
"refresh_interval": 30,
"supported_formats": [
"jpg",
"jpeg",
"png",
"webp"
],
"thumbnail_url": "https://api.suyanw.cn/api/scenery.php"
}
]
},
{
"enabled": true,
"icon": "fas fa-video",
"id": "video",
"layout": {
"aspect_ratio": "16:9",
"auto_play": false,
"columns": 1,
"show_preview": true,
"transition_effect": "slide"
},
"name": "随机视频",
"subcategories": [
{
"api_url": "https://dh.lt6.ltd/xjj/video.php",
"description": "随机风格类型视频",
"downloadable": true,
"id": "radom_xjj_leixing",
"name": "小姐姐不同风格视频",
"refresh_interval": 60,
"supported_formats": [
"mp4",
"webm"
],
"thumbnail_url": "https://n.sinaimg.cn/sinacn19/176/w888h888/20181119/0c26-hmhhnqt1050818.jpg"
},
{
"api_url": "https://api.mmp.cc/api/miss?type=mp4",
"description": "随机风格小姐姐的视频",
"downloadable": true,
"id": "radom_xjj_short",
"name": "短视频",
"refresh_interval": 60,
"supported_formats": [
"mp4",
"webm"
],
"thumbnail_url": "https://weather-real.oss-cn-shanghai.aliyuncs.com/weather/2025-06-17/1750091559255t7FNOX.jpg"
}
]
}
],
"last_updated": "2025-09-9T17:45:00Z",
"layout_version": "1.0.6",
"ui_config": {
"animations": {
"duration": 300,
"transition_effect": "fade"
},
"dark_mode": false,
"default_view": "grid",
"show_thumbnails": false
}
}
+4
View File
@@ -0,0 +1,4 @@
{
"manifest_version": 1,
"modules": []
}
+56
View File
@@ -0,0 +1,56 @@
{
"ai-translation": {
"enabled": true,
"message": ""
},
"baidu-hot": {
"enabled": true,
"message": "百度热榜接口正在维护,预计短时间内不会恢复。"
},
"bili-hot-ranking": {
"enabled": true,
"message": "B站热搜接口正在维护,预计短时间内不会恢复。"
},
"comment": "工具状态控制文件。 'enabled: false' 将禁用该工具。",
"comment_screening_room": "随机放映室使用 '分类ID.子分类ID' 作为键",
"dns-query": {
"enabled": true,
"message": ""
},
"image.baisi": {
"enabled": true,
"message": ""
},
"image.heisi": {
"enabled": true,
"message": ""
},
"image.wappller": {
"enabled": true,
"message": "高清壁纸 暂时下线,请先浏览其他分类。"
},
"image.xjj": {
"enabled": true,
"message": ""
},
"image.猫羽雫": {
"enabled": true,
"message": "猫羽雫 暂时下线,请先浏览其他分类。"
},
"ip-info": {
"enabled": true,
"message": ""
},
"ip-query": {
"enabled": true,
"message": ""
},
"smart-search": {
"enabled": true,
"message": ""
},
"video.radom_xjj_leixing": {
"enabled": true,
"message": ""
}
}
+95
View File
@@ -0,0 +1,95 @@
{
"api_keys": {
"uapipro": ""
},
"app_version": "2.0.6.2",
"build": "2",
"channel": "stable",
"title": "YMhut Box 2.0.6.2",
"message": "本版本重点修复覆盖安装后白屏退出、用户目录 runtime 占用、语言包膨胀和设置页初始化问题,并继续完善 WinUI 3 工具型工作台体验。",
"message_md": "# YMhut Box 2.0.6.2\n\n本版本继续收尾 WinUI 3 工具型工作台:修复覆盖安装/直启稳定性、用户目录 runtime 残留、自检结果页卡顿、排行榜/资讯显示、中文日志和设置页初始化问题,并新增安装器输出框、Markdown 公告、媒体播放器与随机放映室增强、价格/指标图表化展示。",
"release_notes": "修复 EXE/latest 直启和覆盖安装后因语言资源布局导致的白屏退出;发布布局改为纯 lang\\zh-CN 与 lang\\en-US,移除多余语言包和旧 resources\\lang 压缩依赖;启动与自检链路禁止在用户数据目录保存 Runtime/runtime/Runtimes/runtimes 等运行时副本,旧残留会在启动和安装时清理;完善启动自检、安装完整性检查、服务状态结果页、工具箱与工具详情布局、结果/原始输出渲染、排行榜/资讯结构化显示、设置页控制中心和系统概况实时图表;修复设置页初始化失败、中文模式英文漏出、部分日志英文展示、天气胶囊图标缺失以及关闭确认记住选择等问题。",
"release_notes_md": "## Bug 修复\n\n- 修复 EXE/latest 直启和覆盖安装后因语言资源布局导致的白屏退出,失败时写入清晰日志并显示可读错误。\n- 杜绝用户数据目录生成 Runtime/runtime/Runtimes/runtimes 或完整程序 payload 副本,旧残留会在启动和安装时清理。\n- 修复自检结果页加载大量历史明细时卡顿或短暂无响应的问题,改为确认后摘要优先、明细按需加载。\n- 修复中文模式下部分日志仍显示英文的问题,错误码、HTTP 状态和反馈状态仍保留必要原文。\n- 修复排行榜、热榜和资讯类工具被“已隐藏远程地址,仅展示脱敏来源名称”提示干扰后无法生成卡片的问题,同时继续隐藏远程 URL。\n- 修复设置页初始化失败、关闭确认记住选择、天气胶囊部分状态缺少动画图标等问题。\n\n## 支持增强\n\n- 发布布局统一为纯 `lang\\zh-CN` / `lang\\en-US`,移除多余语言包、根目录 culture 目录和旧 `resources\\lang` 压缩依赖。\n- 客户端公告、首页公告、关于页更新弹窗和更新日志弹窗支持 Markdown 标题、列表、表格、代码与链接,解析失败时回退纯文本。\n- 随机放映室支持远程媒体重定向解析,并为图片、视频、音频加载提供进度提示。\n- 数据类工具增强价格/指标结果展示,黄金价格等结果可同时显示摘要、表格和趋势折线图。\n\n## 新增能力\n\n- 安装引导程序在提取文件阶段新增只读输出框,展示旧版本清理、payload 提取、依赖检查和安装收尾进度。\n- 新增启动/安装完整性自检结果入口,服务状态页可确认后查看结果、复制摘要和导出 JSON。\n- 媒体播放器补齐播放列表、常用播放控制、倍速、音量、全屏、图片/视频/音频混播和常见系统解码格式入口。\n- 随机放映室迁移旧版展示思路,改为图片、视频、音频三段式远程媒体浏览。\n\n## 体验重构\n\n- 工具箱与工具详情页继续向高密度 WinUI 工具工作台收束,结果区固定提供“结果”和“原始输出”。\n- 设置控制中心增加分页滚动提示,系统概况图表补齐网格、坐标和实时信息。\n- 主题继续参考 Microsoft Store 与 Windows 媒体播放器的中性 Fluent 风格,不使用渐变,蓝色为主强调,橙/红仅用于警示。\n- 随机放映室和播放器 UI 更接近 Win11 媒体体验,加载、失败、保存、全屏等状态更清晰。",
"category_list": [
{
"icon": "monitor",
"id": "system",
"name": "系统工具"
},
{
"icon": "code",
"id": "developer",
"name": "开发工具"
},
{
"icon": "image",
"id": "image",
"name": "图像工具"
}
],
"detected_product": "YMhut Box",
"detected_packages": {
"YMhut Box": [
{
"version": "2.0.6.2",
"extension": "exe",
"fileName": "YMhut_Box_WinUI_Setup_2.0.6.2.exe",
"downloadPath": "/downloads/YMhut_Box_WinUI_Setup_2.0.6.2.exe",
"size": "待发布",
"sizeBytes": 0,
"updateDate": "2026-06-14",
"updateTime": "2026-06-14 00:00:00"
},
{
"version": "2.0.6.2",
"extension": "exe",
"fileName": "YMhut_Box_WinUI_Setup_2.0.6.2_Light.exe",
"downloadPath": "/downloads/YMhut_Box_WinUI_Setup_2.0.6.2_Light.exe",
"size": "待发布",
"sizeBytes": 0,
"updateDate": "2026-06-14",
"updateTime": "2026-06-14 00:00:00"
}
]
},
"download_mirrors": [
{
"enabled": true,
"id": "primary",
"name": "官方直连",
"sha256": "",
"type": "direct",
"url": "https://update.ymhut.cn/downloads/YMhut_Box_WinUI_Setup_2.0.6.2.exe"
},
{
"enabled": true,
"id": "light",
"name": "轻量安装包",
"sha256": "",
"type": "direct",
"url": "https://update.ymhut.cn/downloads/YMhut_Box_WinUI_Setup_2.0.6.2_Light.exe"
}
],
"download_url": "https://update.ymhut.cn/downloads/YMhut_Box_WinUI_Setup_2.0.6.2.exe",
"home_notes": "v2.0.6.2 聚焦安装布局、启动稳定性和 WinUI 工作台体验:覆盖安装会清理旧程序布局和 runtime 残留,用户目录只保留设置、日志、缓存和轻量状态;语言资源只保留中英双语并归入 lang;工具箱、工具详情、结果渲染、排行榜/资讯卡片、设置控制中心、系统概况和服务状态页继续向原生 Fluent 工具软件风格收束。",
"last_update_notes": {
"v2.0.5.3 稳定性": "延续启动首页、插件扫描、反馈中心和日志展示修复;本版本进一步处理覆盖安装、语言资源和用户目录 runtime 残留。",
"v2.0.5.3 安装包体积": "上一版新增轻量安装包通道;本版本继续收束语言包和旧资源布局,避免无关文件混入发布目录。",
"v2.0.5.3 设置外观": "上一版加入窗口材质设置;本版本继续完善设置页控制中心、系统概况实时图表和中文本地化。"
},
"last_updated": "2026-06-14T00:00:00Z",
"tool_metadata": {},
"update_notes": {
"启动与覆盖安装": "修复覆盖安装后启动白屏或应用自行退出的问题;旧压缩语言布局不再回退复制到用户目录,失败时写入清晰日志并显示可读错误。",
"用户目录瘦身": "用户数据目录不再生成 Runtime、runtime、Runtimes 或 runtimes 文件夹;启动、自检和安装器都会清理旧 runtime 残留,避免大型运行时副本占用本机存储。",
"语言资源布局": "发布布局统一为纯 lang,保留 lang\\zh-CN 与 lang\\en-US;移除其他语言包、根目录 culture 目录和 resources\\lang 压缩残留。",
"启动自检": "迁移并改造旧版启动初始化思路:快速预检负责用户目录、设置、数据库、下载队列、安装根和关键资源;月度完整性检查在窗口可用后后台运行。",
"自检结果页": "服务状态页新增启动自检与安装完整性入口;查看结果前增加确认提示和加载进度,历史先加载摘要,用户选择后再读取完整明细,降低大量结果渲染造成的卡顿。",
"排行榜与资讯": "修复部分排行榜、热榜和资讯类工具因“已隐藏远程地址,仅展示脱敏来源名称”提示混入结构化解析而无法显示卡片内容的问题,同时继续隐藏远程地址。",
"工具箱与工具详情": "工具箱改为更高密度的原生 WinUI 工作台布局;工具内容区域按功能类型优化输入、结果、原始输出、复制、搜索和导出体验。",
"设置页": "修复设置页初始化失败;控制中心分页限制为 5 项可视并增加动态上下箭头提示;系统概况加入网格坐标、暗色绘图区和更多实时系统信息。",
"主题与视觉": "主题改为微软商店/媒体播放器式中性 Fluent 风格,移除渐变主视觉,蓝色作为主强调色,橙红仅用于更新、警告和风险行为。",
"天气胶囊": "补齐阴天、未知、离线等状态的基础图标和轻量动效,遵守关闭动画与高对比度设置。",
"本地化与日志": "修复工具箱与安全、风险确认、默认工具范围、设置弹窗和高频日志的中文模式英文漏出;反馈码和原始错误信息仍保留必要英文。"
}
}